]> git.openstreetmap.org Git - rails.git/blob - vendor/assets/iD/iD.js
Merge remote-tracking branch 'upstream/pull/2742'
[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 && obj.version.toString(),
40313                     changeset: obj.changeset && obj.changeset.toString(),
40314                     timestamp: obj.timestamp,
40315                     user: obj.user,
40316                     uid: obj.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 && obj.version.toString(),
40327                     changeset: obj.changeset && obj.changeset.toString(),
40328                     timestamp: obj.timestamp,
40329                     user: obj.user,
40330                     uid: obj.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 && obj.version.toString(),
40341                     changeset: obj.changeset && obj.changeset.toString(),
40342                     timestamp: obj.timestamp,
40343                     user: obj.user,
40344                     uid: obj.uid && obj.uid.toString(),
40345                     tags: obj.tags,
40346                     members: getMembersJSON(obj)
40347                 });
40348             }
40349         };
40350
40351         function parseJSON(payload, callback, options) {
40352             options = Object.assign({ skipSeen: true }, options);
40353             if (!payload)  {
40354                 return callback({ message: 'No JSON', status: -1 });
40355             }
40356
40357             var json = payload;
40358             if (typeof json !== 'object')
40359                { json = JSON.parse(payload); }
40360
40361             if (!json.elements)
40362                 { return callback({ message: 'No JSON', status: -1 }); }
40363
40364             var children = json.elements;
40365
40366             var handle = window.requestIdleCallback(function() {
40367                 var results = [];
40368                 var result;
40369                 for (var i = 0; i < children.length; i++) {
40370                     result = parseChild(children[i]);
40371                     if (result) { results.push(result); }
40372                 }
40373                 callback(null, results);
40374             });
40375
40376             _deferred.add(handle);
40377
40378             function parseChild(child) {
40379                 var parser = jsonparsers[child.type];
40380                 if (!parser) { return null; }
40381
40382                 var uid;
40383
40384                 uid = osmEntity.id.fromOSM(child.type, child.id);
40385                 if (options.skipSeen) {
40386                     if (_tileCache.seen[uid]) { return null; }  // avoid reparsing a "seen" entity
40387                     _tileCache.seen[uid] = true;
40388                 }
40389
40390                 return parser(child, uid);
40391             }
40392         }
40393
40394         var parsers = {
40395             node: function nodeData(obj, uid) {
40396                 var attrs = obj.attributes;
40397                 return new osmNode({
40398                     id: uid,
40399                     visible: getVisible(attrs),
40400                     version: attrs.version.value,
40401                     changeset: attrs.changeset && attrs.changeset.value,
40402                     timestamp: attrs.timestamp && attrs.timestamp.value,
40403                     user: attrs.user && attrs.user.value,
40404                     uid: attrs.uid && attrs.uid.value,
40405                     loc: getLoc(attrs),
40406                     tags: getTags(obj)
40407                 });
40408             },
40409
40410             way: function wayData(obj, uid) {
40411                 var attrs = obj.attributes;
40412                 return new osmWay({
40413                     id: uid,
40414                     visible: getVisible(attrs),
40415                     version: attrs.version.value,
40416                     changeset: attrs.changeset && attrs.changeset.value,
40417                     timestamp: attrs.timestamp && attrs.timestamp.value,
40418                     user: attrs.user && attrs.user.value,
40419                     uid: attrs.uid && attrs.uid.value,
40420                     tags: getTags(obj),
40421                     nodes: getNodes(obj),
40422                 });
40423             },
40424
40425             relation: function relationData(obj, uid) {
40426                 var attrs = obj.attributes;
40427                 return new osmRelation({
40428                     id: uid,
40429                     visible: getVisible(attrs),
40430                     version: attrs.version.value,
40431                     changeset: attrs.changeset && attrs.changeset.value,
40432                     timestamp: attrs.timestamp && attrs.timestamp.value,
40433                     user: attrs.user && attrs.user.value,
40434                     uid: attrs.uid && attrs.uid.value,
40435                     tags: getTags(obj),
40436                     members: getMembers(obj)
40437                 });
40438             },
40439
40440             note: function parseNote(obj, uid) {
40441                 var attrs = obj.attributes;
40442                 var childNodes = obj.childNodes;
40443                 var props = {};
40444
40445                 props.id = uid;
40446                 props.loc = getLoc(attrs);
40447
40448                 // if notes are coincident, move them apart slightly
40449                 var coincident = false;
40450                 var epsilon = 0.00001;
40451                 do {
40452                     if (coincident) {
40453                         props.loc = geoVecAdd(props.loc, [epsilon, epsilon]);
40454                     }
40455                     var bbox = geoExtent(props.loc).bbox();
40456                     coincident = _noteCache.rtree.search(bbox).length;
40457                 } while (coincident);
40458
40459                 // parse note contents
40460                 for (var i = 0; i < childNodes.length; i++) {
40461                     var node = childNodes[i];
40462                     var nodeName = node.nodeName;
40463                     if (nodeName === '#text') { continue; }
40464
40465                     // if the element is comments, parse the comments
40466                     if (nodeName === 'comments') {
40467                         props[nodeName] = parseComments(node.childNodes);
40468                     } else {
40469                         props[nodeName] = node.textContent;
40470                     }
40471                 }
40472
40473                 var note = new osmNote(props);
40474                 var item = encodeNoteRtree(note);
40475                 _noteCache.note[note.id] = note;
40476                 _noteCache.rtree.insert(item);
40477
40478                 return note;
40479             },
40480
40481             user: function parseUser(obj, uid) {
40482                 var attrs = obj.attributes;
40483                 var user = {
40484                     id: uid,
40485                     display_name: attrs.display_name && attrs.display_name.value,
40486                     account_created: attrs.account_created && attrs.account_created.value,
40487                     changesets_count: '0',
40488                     active_blocks: '0'
40489                 };
40490
40491                 var img = obj.getElementsByTagName('img');
40492                 if (img && img[0] && img[0].getAttribute('href')) {
40493                     user.image_url = img[0].getAttribute('href');
40494                 }
40495
40496                 var changesets = obj.getElementsByTagName('changesets');
40497                 if (changesets && changesets[0] && changesets[0].getAttribute('count')) {
40498                     user.changesets_count = changesets[0].getAttribute('count');
40499                 }
40500
40501                 var blocks = obj.getElementsByTagName('blocks');
40502                 if (blocks && blocks[0]) {
40503                     var received = blocks[0].getElementsByTagName('received');
40504                     if (received && received[0] && received[0].getAttribute('active')) {
40505                         user.active_blocks = received[0].getAttribute('active');
40506                     }
40507                 }
40508
40509                 _userCache.user[uid] = user;
40510                 delete _userCache.toLoad[uid];
40511                 return user;
40512             }
40513         };
40514
40515
40516         function parseXML(xml, callback, options) {
40517             options = Object.assign({ skipSeen: true }, options);
40518             if (!xml || !xml.childNodes) {
40519                 return callback({ message: 'No XML', status: -1 });
40520             }
40521
40522             var root = xml.childNodes[0];
40523             var children = root.childNodes;
40524
40525             var handle = window.requestIdleCallback(function() {
40526                 var results = [];
40527                 var result;
40528                 for (var i = 0; i < children.length; i++) {
40529                     result = parseChild(children[i]);
40530                     if (result) { results.push(result); }
40531                 }
40532                 callback(null, results);
40533             });
40534
40535             _deferred.add(handle);
40536
40537
40538             function parseChild(child) {
40539                 var parser = parsers[child.nodeName];
40540                 if (!parser) { return null; }
40541
40542                 var uid;
40543                 if (child.nodeName === 'user') {
40544                     uid = child.attributes.id.value;
40545                     if (options.skipSeen && _userCache.user[uid]) {
40546                         delete _userCache.toLoad[uid];
40547                         return null;
40548                     }
40549
40550                 } else if (child.nodeName === 'note') {
40551                     uid = child.getElementsByTagName('id')[0].textContent;
40552
40553                 } else {
40554                     uid = osmEntity.id.fromOSM(child.nodeName, child.attributes.id.value);
40555                     if (options.skipSeen) {
40556                         if (_tileCache.seen[uid]) { return null; }  // avoid reparsing a "seen" entity
40557                         _tileCache.seen[uid] = true;
40558                     }
40559                 }
40560
40561                 return parser(child, uid);
40562             }
40563         }
40564
40565
40566         // replace or remove note from rtree
40567         function updateRtree$3(item, replace) {
40568             _noteCache.rtree.remove(item, function isEql(a, b) { return a.data.id === b.data.id; });
40569
40570             if (replace) {
40571                 _noteCache.rtree.insert(item);
40572             }
40573         }
40574
40575
40576         function wrapcb(thisArg, callback, cid) {
40577             return function(err, result) {
40578                 if (err) {
40579                     // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
40580                     if (err.status === 400 || err.status === 401 || err.status === 403) {
40581                         thisArg.logout();
40582                     }
40583                     return callback.call(thisArg, err);
40584
40585                 } else if (thisArg.getConnectionId() !== cid) {
40586                     return callback.call(thisArg, { message: 'Connection Switched', status: -1 });
40587
40588                 } else {
40589                     return callback.call(thisArg, err, result);
40590                 }
40591             };
40592         }
40593
40594
40595         var serviceOsm = {
40596
40597             init: function() {
40598                 utilRebind(this, dispatch$6, 'on');
40599             },
40600
40601
40602             reset: function() {
40603                 Array.from(_deferred).forEach(function(handle) {
40604                     window.cancelIdleCallback(handle);
40605                     _deferred.delete(handle);
40606                 });
40607
40608                 _connectionID++;
40609                 _userChangesets = undefined;
40610                 _userDetails = undefined;
40611                 _rateLimitError = undefined;
40612
40613                 Object.values(_tileCache.inflight).forEach(abortRequest$5);
40614                 Object.values(_noteCache.inflight).forEach(abortRequest$5);
40615                 Object.values(_noteCache.inflightPost).forEach(abortRequest$5);
40616                 if (_changeset.inflight) { abortRequest$5(_changeset.inflight); }
40617
40618                 _tileCache = { toLoad: {}, loaded: {}, inflight: {}, seen: {}, rtree: new RBush() };
40619                 _noteCache = { toLoad: {}, loaded: {}, inflight: {}, inflightPost: {}, note: {}, closed: {}, rtree: new RBush() };
40620                 _userCache = { toLoad: {}, user: {} };
40621                 _cachedApiStatus = undefined;
40622                 _changeset = {};
40623
40624                 return this;
40625             },
40626
40627
40628             getConnectionId: function() {
40629                 return _connectionID;
40630             },
40631
40632
40633             changesetURL: function(changesetID) {
40634                 return urlroot + '/changeset/' + changesetID;
40635             },
40636
40637
40638             changesetsURL: function(center, zoom) {
40639                 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
40640                 return urlroot + '/history#map=' +
40641                     Math.floor(zoom) + '/' +
40642                     center[1].toFixed(precision) + '/' +
40643                     center[0].toFixed(precision);
40644             },
40645
40646
40647             entityURL: function(entity) {
40648                 return urlroot + '/' + entity.type + '/' + entity.osmId();
40649             },
40650
40651
40652             historyURL: function(entity) {
40653                 return urlroot + '/' + entity.type + '/' + entity.osmId() + '/history';
40654             },
40655
40656
40657             userURL: function(username) {
40658                 return urlroot + '/user/' + username;
40659             },
40660
40661
40662             noteURL: function(note) {
40663                 return urlroot + '/note/' + note.id;
40664             },
40665
40666
40667             noteReportURL: function(note) {
40668                 return urlroot + '/reports/new?reportable_type=Note&reportable_id=' + note.id;
40669             },
40670
40671
40672             // Generic method to load data from the OSM API
40673             // Can handle either auth or unauth calls.
40674             loadFromAPI: function(path, callback, options) {
40675                 options = Object.assign({ skipSeen: true }, options);
40676                 var that = this;
40677                 var cid = _connectionID;
40678
40679                 function done(err, payload) {
40680                     if (that.getConnectionId() !== cid) {
40681                         if (callback) { callback({ message: 'Connection Switched', status: -1 }); }
40682                         return;
40683                     }
40684
40685                     var isAuthenticated = that.authenticated();
40686
40687                     // 400 Bad Request, 401 Unauthorized, 403 Forbidden
40688                     // Logout and retry the request..
40689                     if (isAuthenticated && err && err.status &&
40690                             (err.status === 400 || err.status === 401 || err.status === 403)) {
40691                         that.logout();
40692                         that.loadFromAPI(path, callback, options);
40693
40694                     // else, no retry..
40695                     } else {
40696                         // 509 Bandwidth Limit Exceeded, 429 Too Many Requests
40697                         // Set the rateLimitError flag and trigger a warning..
40698                         if (!isAuthenticated && !_rateLimitError && err && err.status &&
40699                                 (err.status === 509 || err.status === 429)) {
40700                             _rateLimitError = err;
40701                             dispatch$6.call('change');
40702                             that.reloadApiStatus();
40703
40704                         } else if ((err && _cachedApiStatus === 'online') ||
40705                             (!err && _cachedApiStatus !== 'online')) {
40706                             // If the response's error state doesn't match the status,
40707                             // it's likely we lost or gained the connection so reload the status
40708                             that.reloadApiStatus();
40709                         }
40710
40711                         if (callback) {
40712                             if (err) {
40713                                 return callback(err);
40714                             } else {
40715                                 if (path.indexOf('.json') !== -1) {
40716                                     return parseJSON(payload, callback, options);
40717                                 } else {
40718                                     return parseXML(payload, callback, options);
40719                                 }
40720                             }
40721                         }
40722                     }
40723                 }
40724
40725                 if (this.authenticated()) {
40726                     return oauth.xhr({ method: 'GET', path: path }, done);
40727                 } else {
40728                     var url = urlroot + path;
40729                     var controller = new AbortController();
40730                     d3_json(url, { signal: controller.signal })
40731                         .then(function(data) {
40732                             done(null, data);
40733                         })
40734                         .catch(function(err) {
40735                             if (err.name === 'AbortError') { return; }
40736                             // d3-fetch includes status in the error message,
40737                             // but we can't access the response itself
40738                             // https://github.com/d3/d3-fetch/issues/27
40739                             var match = err.message.match(/^\d{3}/);
40740                             if (match) {
40741                                 done({ status: +match[0], statusText: err.message });
40742                             } else {
40743                                 done(err.message);
40744                             }
40745                         });
40746                     return controller;
40747                 }
40748             },
40749
40750
40751             // Load a single entity by id (ways and relations use the `/full` call)
40752             // GET /api/0.6/node/#id
40753             // GET /api/0.6/[way|relation]/#id/full
40754             loadEntity: function(id, callback) {
40755                 var type = osmEntity.id.type(id);
40756                 var osmID = osmEntity.id.toOSM(id);
40757                 var options = { skipSeen: false };
40758
40759                 this.loadFromAPI(
40760                     '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : '') + '.json',
40761                     function(err, entities) {
40762                         if (callback) { callback(err, { data: entities }); }
40763                     },
40764                     options
40765                 );
40766             },
40767
40768
40769             // Load a single entity with a specific version
40770             // GET /api/0.6/[node|way|relation]/#id/#version
40771             loadEntityVersion: function(id, version, callback) {
40772                 var type = osmEntity.id.type(id);
40773                 var osmID = osmEntity.id.toOSM(id);
40774                 var options = { skipSeen: false };
40775
40776                 this.loadFromAPI(
40777                     '/api/0.6/' + type + '/' + osmID + '/' + version + '.json',
40778                     function(err, entities) {
40779                         if (callback) { callback(err, { data: entities }); }
40780                     },
40781                     options
40782                 );
40783             },
40784
40785
40786             // Load multiple entities in chunks
40787             // (note: callback may be called multiple times)
40788             // Unlike `loadEntity`, child nodes and members are not fetched
40789             // GET /api/0.6/[nodes|ways|relations]?#parameters
40790             loadMultiple: function(ids, callback) {
40791                 var that = this;
40792                 var groups = utilArrayGroupBy(utilArrayUniq(ids), osmEntity.id.type);
40793
40794                 Object.keys(groups).forEach(function(k) {
40795                     var type = k + 's';   // nodes, ways, relations
40796                     var osmIDs = groups[k].map(function(id) { return osmEntity.id.toOSM(id); });
40797                     var options = { skipSeen: false };
40798
40799                     utilArrayChunk(osmIDs, 150).forEach(function(arr) {
40800                         that.loadFromAPI(
40801                             '/api/0.6/' + type + '.json?' + type + '=' + arr.join(),
40802                             function(err, entities) {
40803                                 if (callback) { callback(err, { data: entities }); }
40804                             },
40805                             options
40806                         );
40807                     });
40808                 });
40809             },
40810
40811
40812             // Create, upload, and close a changeset
40813             // PUT /api/0.6/changeset/create
40814             // POST /api/0.6/changeset/#id/upload
40815             // PUT /api/0.6/changeset/#id/close
40816             putChangeset: function(changeset, changes, callback) {
40817                 var cid = _connectionID;
40818
40819                 if (_changeset.inflight) {
40820                     return callback({ message: 'Changeset already inflight', status: -2 }, changeset);
40821
40822                 } else if (_changeset.open) {   // reuse existing open changeset..
40823                     return createdChangeset.call(this, null, _changeset.open);
40824
40825                 } else {   // Open a new changeset..
40826                     var options = {
40827                         method: 'PUT',
40828                         path: '/api/0.6/changeset/create',
40829                         options: { header: { 'Content-Type': 'text/xml' } },
40830                         content: JXON.stringify(changeset.asJXON())
40831                     };
40832                     _changeset.inflight = oauth.xhr(
40833                         options,
40834                         wrapcb(this, createdChangeset, cid)
40835                     );
40836                 }
40837
40838
40839                 function createdChangeset(err, changesetID) {
40840                     _changeset.inflight = null;
40841                     if (err) { return callback(err, changeset); }
40842
40843                     _changeset.open = changesetID;
40844                     changeset = changeset.update({ id: changesetID });
40845
40846                     // Upload the changeset..
40847                     var options = {
40848                         method: 'POST',
40849                         path: '/api/0.6/changeset/' + changesetID + '/upload',
40850                         options: { header: { 'Content-Type': 'text/xml' } },
40851                         content: JXON.stringify(changeset.osmChangeJXON(changes))
40852                     };
40853                     _changeset.inflight = oauth.xhr(
40854                         options,
40855                         wrapcb(this, uploadedChangeset, cid)
40856                     );
40857                 }
40858
40859
40860                 function uploadedChangeset(err) {
40861                     _changeset.inflight = null;
40862                     if (err) { return callback(err, changeset); }
40863
40864                     // Upload was successful, safe to call the callback.
40865                     // Add delay to allow for postgres replication #1646 #2678
40866                     window.setTimeout(function() { callback(null, changeset); }, 2500);
40867                     _changeset.open = null;
40868
40869                     // At this point, we don't really care if the connection was switched..
40870                     // Only try to close the changeset if we're still talking to the same server.
40871                     if (this.getConnectionId() === cid) {
40872                         // Still attempt to close changeset, but ignore response because #2667
40873                         oauth.xhr({
40874                             method: 'PUT',
40875                             path: '/api/0.6/changeset/' + changeset.id + '/close',
40876                             options: { header: { 'Content-Type': 'text/xml' } }
40877                         }, function() { return true; });
40878                     }
40879                 }
40880             },
40881
40882
40883             // Load multiple users in chunks
40884             // (note: callback may be called multiple times)
40885             // GET /api/0.6/users?users=#id1,#id2,...,#idn
40886             loadUsers: function(uids, callback) {
40887                 var toLoad = [];
40888                 var cached = [];
40889
40890                 utilArrayUniq(uids).forEach(function(uid) {
40891                     if (_userCache.user[uid]) {
40892                         delete _userCache.toLoad[uid];
40893                         cached.push(_userCache.user[uid]);
40894                     } else {
40895                         toLoad.push(uid);
40896                     }
40897                 });
40898
40899                 if (cached.length || !this.authenticated()) {
40900                     callback(undefined, cached);
40901                     if (!this.authenticated()) { return; }  // require auth
40902                 }
40903
40904                 utilArrayChunk(toLoad, 150).forEach(function(arr) {
40905                     oauth.xhr(
40906                         { method: 'GET', path: '/api/0.6/users?users=' + arr.join() },
40907                         wrapcb(this, done, _connectionID)
40908                     );
40909                 }.bind(this));
40910
40911                 function done(err, xml) {
40912                     if (err) { return callback(err); }
40913
40914                     var options = { skipSeen: true };
40915                     return parseXML(xml, function(err, results) {
40916                         if (err) {
40917                             return callback(err);
40918                         } else {
40919                             return callback(undefined, results);
40920                         }
40921                     }, options);
40922                 }
40923             },
40924
40925
40926             // Load a given user by id
40927             // GET /api/0.6/user/#id
40928             loadUser: function(uid, callback) {
40929                 if (_userCache.user[uid] || !this.authenticated()) {   // require auth
40930                     delete _userCache.toLoad[uid];
40931                     return callback(undefined, _userCache.user[uid]);
40932                 }
40933
40934                 oauth.xhr(
40935                     { method: 'GET', path: '/api/0.6/user/' + uid },
40936                     wrapcb(this, done, _connectionID)
40937                 );
40938
40939                 function done(err, xml) {
40940                     if (err) { return callback(err); }
40941
40942                     var options = { skipSeen: true };
40943                     return parseXML(xml, function(err, results) {
40944                         if (err) {
40945                             return callback(err);
40946                         } else {
40947                             return callback(undefined, results[0]);
40948                         }
40949                     }, options);
40950                 }
40951             },
40952
40953
40954             // Load the details of the logged-in user
40955             // GET /api/0.6/user/details
40956             userDetails: function(callback) {
40957                 if (_userDetails) {    // retrieve cached
40958                     return callback(undefined, _userDetails);
40959                 }
40960
40961                 oauth.xhr(
40962                     { method: 'GET', path: '/api/0.6/user/details' },
40963                     wrapcb(this, done, _connectionID)
40964                 );
40965
40966                 function done(err, xml) {
40967                     if (err) { return callback(err); }
40968
40969                     var options = { skipSeen: false };
40970                     return parseXML(xml, function(err, results) {
40971                         if (err) {
40972                             return callback(err);
40973                         } else {
40974                             _userDetails = results[0];
40975                             return callback(undefined, _userDetails);
40976                         }
40977                     }, options);
40978                 }
40979             },
40980
40981
40982             // Load previous changesets for the logged in user
40983             // GET /api/0.6/changesets?user=#id
40984             userChangesets: function(callback) {
40985                 if (_userChangesets) {    // retrieve cached
40986                     return callback(undefined, _userChangesets);
40987                 }
40988
40989                 this.userDetails(
40990                     wrapcb(this, gotDetails, _connectionID)
40991                 );
40992
40993
40994                 function gotDetails(err, user) {
40995                     if (err) { return callback(err); }
40996
40997                     oauth.xhr(
40998                         { method: 'GET', path: '/api/0.6/changesets?user=' + user.id },
40999                         wrapcb(this, done, _connectionID)
41000                     );
41001                 }
41002
41003                 function done(err, xml) {
41004                     if (err) { return callback(err); }
41005
41006                     _userChangesets = Array.prototype.map.call(
41007                         xml.getElementsByTagName('changeset'),
41008                         function (changeset) { return { tags: getTags(changeset) }; }
41009                     ).filter(function (changeset) {
41010                         var comment = changeset.tags.comment;
41011                         return comment && comment !== '';
41012                     });
41013
41014                     return callback(undefined, _userChangesets);
41015                 }
41016             },
41017
41018
41019             // Fetch the status of the OSM API
41020             // GET /api/capabilities
41021             status: function(callback) {
41022                 var url = urlroot + '/api/capabilities';
41023                 var errback = wrapcb(this, done, _connectionID);
41024                 d3_xml(url)
41025                     .then(function(data) { errback(null, data); })
41026                     .catch(function(err) { errback(err.message); });
41027
41028                 function done(err, xml) {
41029                     if (err) {
41030                         // the status is null if no response could be retrieved
41031                         return callback(err, null);
41032                     }
41033
41034                     // update blacklists
41035                     var elements = xml.getElementsByTagName('blacklist');
41036                     var regexes = [];
41037                     for (var i = 0; i < elements.length; i++) {
41038                         var regex = elements[i].getAttribute('regex');  // needs unencode?
41039                         if (regex) {
41040                             regexes.push(regex);
41041                         }
41042                     }
41043                     if (regexes.length) {
41044                         _blacklists = regexes;
41045                     }
41046
41047                     if (_rateLimitError) {
41048                         return callback(_rateLimitError, 'rateLimited');
41049                     } else {
41050                         var waynodes = xml.getElementsByTagName('waynodes');
41051                         var maxWayNodes = waynodes.length && parseInt(waynodes[0].getAttribute('maximum'), 10);
41052                         if (maxWayNodes && isFinite(maxWayNodes)) { _maxWayNodes = maxWayNodes; }
41053
41054                         var apiStatus = xml.getElementsByTagName('status');
41055                         var val = apiStatus[0].getAttribute('api');
41056                         return callback(undefined, val);
41057                     }
41058                 }
41059             },
41060
41061             // Calls `status` and dispatches an `apiStatusChange` event if the returned
41062             // status differs from the cached status.
41063             reloadApiStatus: function() {
41064                 // throttle to avoid unncessary API calls
41065                 if (!this.throttledReloadApiStatus) {
41066                     var that = this;
41067                     this.throttledReloadApiStatus = throttle(function() {
41068                         that.status(function(err, status) {
41069                             if (status !== _cachedApiStatus) {
41070                                 _cachedApiStatus = status;
41071                                 dispatch$6.call('apiStatusChange', that, err, status);
41072                             }
41073                         });
41074                     }, 500);
41075                 }
41076                 this.throttledReloadApiStatus();
41077             },
41078
41079
41080             // Returns the maximum number of nodes a single way can have
41081             maxWayNodes: function() {
41082                 return _maxWayNodes;
41083             },
41084
41085
41086             // Load data (entities) from the API in tiles
41087             // GET /api/0.6/map?bbox=
41088             loadTiles: function(projection, callback) {
41089                 if (_off) { return; }
41090
41091                 // determine the needed tiles to cover the view
41092                 var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection);
41093
41094                 // abort inflight requests that are no longer needed
41095                 var hadRequests = hasInflightRequests(_tileCache);
41096                 abortUnwantedRequests$3(_tileCache, tiles);
41097                 if (hadRequests && !hasInflightRequests(_tileCache)) {
41098                     dispatch$6.call('loaded');    // stop the spinner
41099                 }
41100
41101                 // issue new requests..
41102                 tiles.forEach(function(tile) {
41103                     this.loadTile(tile, callback);
41104                 }, this);
41105             },
41106
41107
41108             // Load a single data tile
41109             // GET /api/0.6/map?bbox=
41110             loadTile: function(tile, callback) {
41111                 if (_off) { return; }
41112                 if (_tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) { return; }
41113
41114                 if (!hasInflightRequests(_tileCache)) {
41115                     dispatch$6.call('loading');   // start the spinner
41116                 }
41117
41118                 var path = '/api/0.6/map.json?bbox=';
41119                 var options = { skipSeen: true };
41120
41121                 _tileCache.inflight[tile.id] = this.loadFromAPI(
41122                     path + tile.extent.toParam(),
41123                     tileCallback,
41124                     options
41125                 );
41126
41127                 function tileCallback(err, parsed) {
41128                     delete _tileCache.inflight[tile.id];
41129                     if (!err) {
41130                         delete _tileCache.toLoad[tile.id];
41131                         _tileCache.loaded[tile.id] = true;
41132                         var bbox = tile.extent.bbox();
41133                         bbox.id = tile.id;
41134                         _tileCache.rtree.insert(bbox);
41135                     }
41136                     if (callback) {
41137                         callback(err, Object.assign({ data: parsed }, tile));
41138                     }
41139                     if (!hasInflightRequests(_tileCache)) {
41140                         dispatch$6.call('loaded');     // stop the spinner
41141                     }
41142                 }
41143             },
41144
41145
41146             isDataLoaded: function(loc) {
41147                 var bbox = { minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1] };
41148                 return _tileCache.rtree.collides(bbox);
41149             },
41150
41151
41152             // load the tile that covers the given `loc`
41153             loadTileAtLoc: function(loc, callback) {
41154                 // Back off if the toLoad queue is filling up.. re #6417
41155                 // (Currently `loadTileAtLoc` requests are considered low priority - used by operations to
41156                 // let users safely edit geometries which extend to unloaded tiles.  We can drop some.)
41157                 if (Object.keys(_tileCache.toLoad).length > 50) { return; }
41158
41159                 var k = geoZoomToScale(_tileZoom$3 + 1);
41160                 var offset = geoRawMercator().scale(k)(loc);
41161                 var projection = geoRawMercator().transform({ k: k, x: -offset[0], y: -offset[1] });
41162                 var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection);
41163
41164                 tiles.forEach(function(tile) {
41165                     if (_tileCache.toLoad[tile.id] || _tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) { return; }
41166
41167                     _tileCache.toLoad[tile.id] = true;
41168                     this.loadTile(tile, callback);
41169                 }, this);
41170             },
41171
41172
41173             // Load notes from the API in tiles
41174             // GET /api/0.6/notes?bbox=
41175             loadNotes: function(projection, noteOptions) {
41176                 noteOptions = Object.assign({ limit: 10000, closed: 7 }, noteOptions);
41177                 if (_off) { return; }
41178
41179                 var that = this;
41180                 var path = '/api/0.6/notes?limit=' + noteOptions.limit + '&closed=' + noteOptions.closed + '&bbox=';
41181                 var throttleLoadUsers = throttle(function() {
41182                     var uids = Object.keys(_userCache.toLoad);
41183                     if (!uids.length) { return; }
41184                     that.loadUsers(uids, function() {});  // eagerly load user details
41185                 }, 750);
41186
41187                 // determine the needed tiles to cover the view
41188                 var tiles = tiler$5.zoomExtent([_noteZoom, _noteZoom]).getTiles(projection);
41189
41190                 // abort inflight requests that are no longer needed
41191                 abortUnwantedRequests$3(_noteCache, tiles);
41192
41193                 // issue new requests..
41194                 tiles.forEach(function(tile) {
41195                     if (_noteCache.loaded[tile.id] || _noteCache.inflight[tile.id]) { return; }
41196
41197                     var options = { skipSeen: false };
41198                     _noteCache.inflight[tile.id] = that.loadFromAPI(
41199                         path + tile.extent.toParam(),
41200                         function(err) {
41201                             delete _noteCache.inflight[tile.id];
41202                             if (!err) {
41203                                 _noteCache.loaded[tile.id] = true;
41204                             }
41205                             throttleLoadUsers();
41206                             dispatch$6.call('loadedNotes');
41207                         },
41208                         options
41209                     );
41210                 });
41211             },
41212
41213
41214             // Create a note
41215             // POST /api/0.6/notes?params
41216             postNoteCreate: function(note, callback) {
41217                 if (!this.authenticated()) {
41218                     return callback({ message: 'Not Authenticated', status: -3 }, note);
41219                 }
41220                 if (_noteCache.inflightPost[note.id]) {
41221                     return callback({ message: 'Note update already inflight', status: -2 }, note);
41222                 }
41223
41224                 if (!note.loc[0] || !note.loc[1] || !note.newComment) { return; } // location & description required
41225
41226                 var comment = note.newComment;
41227                 if (note.newCategory && note.newCategory !== 'None') { comment += ' #' + note.newCategory; }
41228
41229                 var path = '/api/0.6/notes?' + utilQsString({ lon: note.loc[0], lat: note.loc[1], text: comment });
41230
41231                 _noteCache.inflightPost[note.id] = oauth.xhr(
41232                     { method: 'POST', path: path },
41233                     wrapcb(this, done, _connectionID)
41234                 );
41235
41236
41237                 function done(err, xml) {
41238                     delete _noteCache.inflightPost[note.id];
41239                     if (err) { return callback(err); }
41240
41241                     // we get the updated note back, remove from caches and reparse..
41242                     this.removeNote(note);
41243
41244                     var options = { skipSeen: false };
41245                     return parseXML(xml, function(err, results) {
41246                         if (err) {
41247                             return callback(err);
41248                         } else {
41249                             return callback(undefined, results[0]);
41250                         }
41251                     }, options);
41252                 }
41253             },
41254
41255
41256             // Update a note
41257             // POST /api/0.6/notes/#id/comment?text=comment
41258             // POST /api/0.6/notes/#id/close?text=comment
41259             // POST /api/0.6/notes/#id/reopen?text=comment
41260             postNoteUpdate: function(note, newStatus, callback) {
41261                 if (!this.authenticated()) {
41262                     return callback({ message: 'Not Authenticated', status: -3 }, note);
41263                 }
41264                 if (_noteCache.inflightPost[note.id]) {
41265                     return callback({ message: 'Note update already inflight', status: -2 }, note);
41266                 }
41267
41268                 var action;
41269                 if (note.status !== 'closed' && newStatus === 'closed') {
41270                     action = 'close';
41271                 } else if (note.status !== 'open' && newStatus === 'open') {
41272                     action = 'reopen';
41273                 } else {
41274                     action = 'comment';
41275                     if (!note.newComment) { return; } // when commenting, comment required
41276                 }
41277
41278                 var path = '/api/0.6/notes/' + note.id + '/' + action;
41279                 if (note.newComment) {
41280                     path += '?' + utilQsString({ text: note.newComment });
41281                 }
41282
41283                 _noteCache.inflightPost[note.id] = oauth.xhr(
41284                     { method: 'POST', path: path },
41285                     wrapcb(this, done, _connectionID)
41286                 );
41287
41288
41289                 function done(err, xml) {
41290                     delete _noteCache.inflightPost[note.id];
41291                     if (err) { return callback(err); }
41292
41293                     // we get the updated note back, remove from caches and reparse..
41294                     this.removeNote(note);
41295
41296                     // update closed note cache - used to populate `closed:note` changeset tag
41297                     if (action === 'close') {
41298                         _noteCache.closed[note.id] = true;
41299                     } else if (action === 'reopen') {
41300                         delete _noteCache.closed[note.id];
41301                     }
41302
41303                     var options = { skipSeen: false };
41304                     return parseXML(xml, function(err, results) {
41305                         if (err) {
41306                             return callback(err);
41307                         } else {
41308                             return callback(undefined, results[0]);
41309                         }
41310                     }, options);
41311                 }
41312             },
41313
41314
41315             switch: function(options) {
41316                 urlroot = options.urlroot;
41317
41318                 oauth.options(Object.assign({
41319                     url: urlroot,
41320                     loading: authLoading,
41321                     done: authDone
41322                 }, options));
41323
41324                 this.reset();
41325                 this.userChangesets(function() {});  // eagerly load user details/changesets
41326                 dispatch$6.call('change');
41327                 return this;
41328             },
41329
41330
41331             toggle: function(val) {
41332                 _off = !val;
41333                 return this;
41334             },
41335
41336
41337             isChangesetInflight: function() {
41338                 return !!_changeset.inflight;
41339             },
41340
41341
41342             // get/set cached data
41343             // This is used to save/restore the state when entering/exiting the walkthrough
41344             // Also used for testing purposes.
41345             caches: function(obj) {
41346                 function cloneCache(source) {
41347                     var target = {};
41348                     Object.keys(source).forEach(function(k) {
41349                         if (k === 'rtree') {
41350                             target.rtree = new RBush().fromJSON(source.rtree.toJSON());  // clone rbush
41351                         } else if (k === 'note') {
41352                             target.note = {};
41353                             Object.keys(source.note).forEach(function(id) {
41354                                 target.note[id] = osmNote(source.note[id]);   // copy notes
41355                             });
41356                         } else {
41357                             target[k] = JSON.parse(JSON.stringify(source[k]));   // clone deep
41358                         }
41359                     });
41360                     return target;
41361                 }
41362
41363                 if (!arguments.length) {
41364                     return {
41365                         tile: cloneCache(_tileCache),
41366                         note: cloneCache(_noteCache),
41367                         user: cloneCache(_userCache)
41368                     };
41369                 }
41370
41371                 // access caches directly for testing (e.g., loading notes rtree)
41372                 if (obj === 'get') {
41373                     return {
41374                         tile: _tileCache,
41375                         note: _noteCache,
41376                         user: _userCache
41377                     };
41378                 }
41379
41380                 if (obj.tile) {
41381                     _tileCache = obj.tile;
41382                     _tileCache.inflight = {};
41383                 }
41384                 if (obj.note) {
41385                     _noteCache = obj.note;
41386                     _noteCache.inflight = {};
41387                     _noteCache.inflightPost = {};
41388                 }
41389                 if (obj.user) {
41390                     _userCache = obj.user;
41391                 }
41392
41393                 return this;
41394             },
41395
41396
41397             logout: function() {
41398                 _userChangesets = undefined;
41399                 _userDetails = undefined;
41400                 oauth.logout();
41401                 dispatch$6.call('change');
41402                 return this;
41403             },
41404
41405
41406             authenticated: function() {
41407                 return oauth.authenticated();
41408             },
41409
41410
41411             authenticate: function(callback) {
41412                 var that = this;
41413                 var cid = _connectionID;
41414                 _userChangesets = undefined;
41415                 _userDetails = undefined;
41416
41417                 function done(err, res) {
41418                     if (err) {
41419                         if (callback) { callback(err); }
41420                         return;
41421                     }
41422                     if (that.getConnectionId() !== cid) {
41423                         if (callback) { callback({ message: 'Connection Switched', status: -1 }); }
41424                         return;
41425                     }
41426                     _rateLimitError = undefined;
41427                     dispatch$6.call('change');
41428                     if (callback) { callback(err, res); }
41429                     that.userChangesets(function() {});  // eagerly load user details/changesets
41430                 }
41431
41432                 return oauth.authenticate(done);
41433             },
41434
41435
41436             imageryBlacklists: function() {
41437                 return _blacklists;
41438             },
41439
41440
41441             tileZoom: function(val) {
41442                 if (!arguments.length) { return _tileZoom$3; }
41443                 _tileZoom$3 = val;
41444                 return this;
41445             },
41446
41447
41448             // get all cached notes covering the viewport
41449             notes: function(projection) {
41450                 var viewport = projection.clipExtent();
41451                 var min = [viewport[0][0], viewport[1][1]];
41452                 var max = [viewport[1][0], viewport[0][1]];
41453                 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
41454
41455                 return _noteCache.rtree.search(bbox)
41456                     .map(function(d) { return d.data; });
41457             },
41458
41459
41460             // get a single note from the cache
41461             getNote: function(id) {
41462                 return _noteCache.note[id];
41463             },
41464
41465
41466             // remove a single note from the cache
41467             removeNote: function(note) {
41468                 if (!(note instanceof osmNote) || !note.id) { return; }
41469
41470                 delete _noteCache.note[note.id];
41471                 updateRtree$3(encodeNoteRtree(note), false);  // false = remove
41472             },
41473
41474
41475             // replace a single note in the cache
41476             replaceNote: function(note) {
41477                 if (!(note instanceof osmNote) || !note.id) { return; }
41478
41479                 _noteCache.note[note.id] = note;
41480                 updateRtree$3(encodeNoteRtree(note), true);  // true = replace
41481                 return note;
41482             },
41483
41484
41485             // Get an array of note IDs closed during this session.
41486             // Used to populate `closed:note` changeset tag
41487             getClosedIDs: function() {
41488                 return Object.keys(_noteCache.closed).sort();
41489             }
41490
41491         };
41492
41493         var apibase$3 = 'https://wiki.openstreetmap.org/w/api.php';
41494         var _inflight$1 = {};
41495         var _wikibaseCache = {};
41496         var _localeIDs = { en: false };
41497
41498
41499         var debouncedRequest = debounce(request, 500, { leading: false });
41500
41501         function request(url, callback) {
41502             if (_inflight$1[url]) { return; }
41503             var controller = new AbortController();
41504             _inflight$1[url] = controller;
41505
41506             d3_json(url, { signal: controller.signal })
41507                 .then(function(result) {
41508                     delete _inflight$1[url];
41509                     if (callback) { callback(null, result); }
41510                 })
41511                 .catch(function(err) {
41512                     delete _inflight$1[url];
41513                     if (err.name === 'AbortError') { return; }
41514                     if (callback) { callback(err.message); }
41515                 });
41516         }
41517
41518
41519         /**
41520          * Get the best string value from the descriptions/labels result
41521          * Note that if mediawiki doesn't recognize language code, it will return all values.
41522          * In that case, fallback to use English.
41523          * @param values object - either descriptions or labels
41524          * @param langCode String
41525          * @returns localized string
41526          */
41527         function localizedToString(values, langCode) {
41528             if (values) {
41529                 values = values[langCode] || values.en;
41530             }
41531             return values ? values.value : '';
41532         }
41533
41534
41535         var serviceOsmWikibase = {
41536
41537             init: function() {
41538                 _inflight$1 = {};
41539                 _wikibaseCache = {};
41540                 _localeIDs = {};
41541             },
41542
41543
41544             reset: function() {
41545                 Object.values(_inflight$1).forEach(function(controller) { controller.abort(); });
41546                 _inflight$1 = {};
41547             },
41548
41549
41550             /**
41551              * Get the best value for the property, or undefined if not found
41552              * @param entity object from wikibase
41553              * @param property string e.g. 'P4' for image
41554              * @param langCode string e.g. 'fr' for French
41555              */
41556             claimToValue: function(entity, property, langCode) {
41557                 if (!entity.claims[property]) { return undefined; }
41558                 var locale = _localeIDs[langCode];
41559                 var preferredPick, localePick;
41560
41561                 entity.claims[property].forEach(function(stmt) {
41562                     // If exists, use value limited to the needed language (has a qualifier P26 = locale)
41563                     // Or if not found, use the first value with the "preferred" rank
41564                     if (!preferredPick && stmt.rank === 'preferred') {
41565                         preferredPick = stmt;
41566                     }
41567                     if (locale && stmt.qualifiers && stmt.qualifiers.P26 &&
41568                         stmt.qualifiers.P26[0].datavalue.value.id === locale
41569                     ) {
41570                         localePick = stmt;
41571                     }
41572                 });
41573
41574                 var result = localePick || preferredPick;
41575                 if (result) {
41576                     var datavalue = result.mainsnak.datavalue;
41577                     return datavalue.type === 'wikibase-entityid' ? datavalue.value.id : datavalue.value;
41578                 } else {
41579                     return undefined;
41580                 }
41581             },
41582
41583
41584             /**
41585              * Convert monolingual property into a key-value object (language -> value)
41586              * @param entity object from wikibase
41587              * @param property string e.g. 'P31' for monolingual wiki page title
41588              */
41589             monolingualClaimToValueObj: function(entity, property) {
41590                 if (!entity || !entity.claims[property]) { return undefined; }
41591
41592                 return entity.claims[property].reduce(function(acc, obj) {
41593                     var value = obj.mainsnak.datavalue.value;
41594                     acc[value.language] = value.text;
41595                     return acc;
41596                 }, {});
41597             },
41598
41599
41600             toSitelink: function(key, value) {
41601                 var result = value ? ('Tag:' + key + '=' + value) : 'Key:' + key;
41602                 return result.replace(/_/g, ' ').trim();
41603             },
41604
41605
41606             //
41607             // Pass params object of the form:
41608             // {
41609             //   key: 'string',
41610             //   value: 'string',
41611             //   rtype: 'string',
41612             //   langCode: 'string'
41613             // }
41614             //
41615             getEntity: function(params, callback) {
41616                 var doRequest = params.debounce ? debouncedRequest : request;
41617                 var that = this;
41618                 var titles = [];
41619                 var result = {};
41620                 var rtypeSitelink = params.rtype ? ('Relation:' + params.rtype).replace(/_/g, ' ').trim() : false;
41621                 var keySitelink = params.key ? this.toSitelink(params.key) : false;
41622                 var tagSitelink = (params.key && params.value) ? this.toSitelink(params.key, params.value) : false;
41623                 var localeSitelink;
41624
41625                 if (params.langCode && _localeIDs[params.langCode] === undefined) {
41626                     // If this is the first time we are asking about this locale,
41627                     // fetch corresponding entity (if it exists), and cache it.
41628                     // If there is no such entry, cache `false` value to avoid re-requesting it.
41629                     localeSitelink = ('Locale:' + params.langCode).replace(/_/g, ' ').trim();
41630                     titles.push(localeSitelink);
41631                 }
41632
41633                 if (rtypeSitelink) {
41634                     if (_wikibaseCache[rtypeSitelink]) {
41635                         result.rtype = _wikibaseCache[rtypeSitelink];
41636                     } else {
41637                         titles.push(rtypeSitelink);
41638                     }
41639                 }
41640
41641                 if (keySitelink) {
41642                     if (_wikibaseCache[keySitelink]) {
41643                         result.key = _wikibaseCache[keySitelink];
41644                     } else {
41645                         titles.push(keySitelink);
41646                     }
41647                 }
41648
41649                 if (tagSitelink) {
41650                     if (_wikibaseCache[tagSitelink]) {
41651                         result.tag = _wikibaseCache[tagSitelink];
41652                     } else {
41653                         titles.push(tagSitelink);
41654                     }
41655                 }
41656
41657                 if (!titles.length) {
41658                     // Nothing to do, we already had everything in the cache
41659                     return callback(null, result);
41660                 }
41661
41662                 // Requesting just the user language code
41663                 // If backend recognizes the code, it will perform proper fallbacks,
41664                 // and the result will contain the requested code. If not, all values are returned:
41665                 // {"zh-tw":{"value":"...","language":"zh-tw","source-language":"zh-hant"}
41666                 // {"pt-br":{"value":"...","language":"pt","for-language":"pt-br"}}
41667                 var obj = {
41668                     action: 'wbgetentities',
41669                     sites: 'wiki',
41670                     titles: titles.join('|'),
41671                     languages: params.langCode,
41672                     languagefallback: 1,
41673                     origin: '*',
41674                     format: 'json',
41675                     // There is an MW Wikibase API bug https://phabricator.wikimedia.org/T212069
41676                     // We shouldn't use v1 until it gets fixed, but should switch to it afterwards
41677                     // formatversion: 2,
41678                 };
41679
41680                 var url = apibase$3 + '?' + utilQsString(obj);
41681                 doRequest(url, function(err, d) {
41682                     if (err) {
41683                         callback(err);
41684                     } else if (!d.success || d.error) {
41685                         callback(d.error.messages.map(function(v) { return v.html['*']; }).join('<br>'));
41686                     } else {
41687                         var localeID = false;
41688                         Object.values(d.entities).forEach(function(res) {
41689                             if (res.missing !== '') {
41690                                 // Simplify access to the localized values
41691                                 res.description = localizedToString(res.descriptions, params.langCode);
41692                                 res.label = localizedToString(res.labels, params.langCode);
41693
41694                                 var title = res.sitelinks.wiki.title;
41695                                 if (title === rtypeSitelink) {
41696                                     _wikibaseCache[rtypeSitelink] = res;
41697                                     result.rtype = res;
41698                                 } else if (title === keySitelink) {
41699                                     _wikibaseCache[keySitelink] = res;
41700                                     result.key = res;
41701                                 } else if (title === tagSitelink) {
41702                                     _wikibaseCache[tagSitelink] = res;
41703                                     result.tag = res;
41704                                 } else if (title === localeSitelink) {
41705                                     localeID = res.id;
41706                                 } else {
41707                                     console.log('Unexpected title ' + title);  // eslint-disable-line no-console
41708                                 }
41709                             }
41710                         });
41711
41712                         if (localeSitelink) {
41713                             // If locale ID is not found, store false to prevent repeated queries
41714                             that.addLocale(params.langCode, localeID);
41715                         }
41716
41717                         callback(null, result);
41718                     }
41719                 });
41720             },
41721
41722
41723             //
41724             // Pass params object of the form:
41725             // {
41726             //   key: 'string',     // required
41727             //   value: 'string'    // optional
41728             // }
41729             //   -or-
41730             // {
41731             //   rtype: 'rtype'     // relation type  (e.g. 'multipolygon')
41732             // }
41733             //
41734             // Get an result object used to display tag documentation
41735             // {
41736             //   title:        'string',
41737             //   description:  'string',
41738             //   editURL:      'string',
41739             //   imageURL:     'string',
41740             //   wiki:         { title: 'string', text: 'string', url: 'string' }
41741             // }
41742             //
41743             getDocs: function(params, callback) {
41744                 var that = this;
41745                 var langCode = _mainLocalizer.localeCode().toLowerCase();
41746                 params.langCode = langCode;
41747
41748                 this.getEntity(params, function(err, data) {
41749                     if (err) {
41750                         callback(err);
41751                         return;
41752                     }
41753
41754                     var entity = data.rtype || data.tag || data.key;
41755                     if (!entity) {
41756                         callback('No entity');
41757                         return;
41758                     }
41759
41760                     // prepare result
41761                     var result = {
41762                         title: entity.title,
41763                         description: entity.description,
41764                         editURL: 'https://wiki.openstreetmap.org/wiki/' + entity.title
41765                     };
41766
41767                     // add image
41768                     if (entity.claims) {
41769                         var imageroot;
41770                         var image = that.claimToValue(entity, 'P4', langCode);
41771                         if (image) {
41772                             imageroot = 'https://commons.wikimedia.org/w/index.php';
41773                         } else {
41774                             image = that.claimToValue(entity, 'P28', langCode);
41775                             if (image) {
41776                                 imageroot = 'https://wiki.openstreetmap.org/w/index.php';
41777                             }
41778                         }
41779                         if (imageroot && image) {
41780                             result.imageURL = imageroot + '?' + utilQsString({
41781                                 title: 'Special:Redirect/file/' + image,
41782                                 width: 400
41783                             });
41784                         }
41785                     }
41786
41787                     // Try to get a wiki page from tag data item first, followed by the corresponding key data item.
41788                     // If neither tag nor key data item contain a wiki page in the needed language nor English,
41789                     // get the first found wiki page from either the tag or the key item.
41790                     var rtypeWiki = that.monolingualClaimToValueObj(data.rtype, 'P31');
41791                     var tagWiki = that.monolingualClaimToValueObj(data.tag, 'P31');
41792                     var keyWiki = that.monolingualClaimToValueObj(data.key, 'P31');
41793
41794                     // If exact language code does not exist, try to find the first part before the '-'
41795                     // BUG: in some cases, a more elaborate fallback logic might be needed
41796                     var langPrefix = langCode.split('-', 2)[0];
41797
41798                     // use the first acceptable wiki page
41799                     result.wiki =
41800                         getWikiInfo(rtypeWiki, langCode, 'inspector.wiki_reference') ||
41801                         getWikiInfo(rtypeWiki, langPrefix, 'inspector.wiki_reference') ||
41802                         getWikiInfo(rtypeWiki, 'en', 'inspector.wiki_en_reference') ||
41803                         getWikiInfo(tagWiki, langCode, 'inspector.wiki_reference') ||
41804                         getWikiInfo(tagWiki, langPrefix, 'inspector.wiki_reference') ||
41805                         getWikiInfo(tagWiki, 'en', 'inspector.wiki_en_reference') ||
41806                         getWikiInfo(keyWiki, langCode, 'inspector.wiki_reference') ||
41807                         getWikiInfo(keyWiki, langPrefix, 'inspector.wiki_reference') ||
41808                         getWikiInfo(keyWiki, 'en', 'inspector.wiki_en_reference');
41809
41810                     callback(null, result);
41811
41812
41813                     // Helper method to get wiki info if a given language exists
41814                     function getWikiInfo(wiki, langCode, tKey) {
41815                         if (wiki && wiki[langCode]) {
41816                             return {
41817                                 title: wiki[langCode],
41818                                 text: tKey,
41819                                 url: 'https://wiki.openstreetmap.org/wiki/' + wiki[langCode]
41820                             };
41821                         }
41822                     }
41823                 });
41824             },
41825
41826
41827             addLocale: function(langCode, qid) {
41828                 // Makes it easier to unit test
41829                 _localeIDs[langCode] = qid;
41830             },
41831
41832
41833             apibase: function(val) {
41834                 if (!arguments.length) { return apibase$3; }
41835                 apibase$3 = val;
41836                 return this;
41837             }
41838
41839         };
41840
41841         var jsonpCache = {};
41842         window.jsonpCache = jsonpCache;
41843
41844         function jsonpRequest(url, callback) {
41845             var request = {
41846                 abort: function() {}
41847             };
41848
41849             if (window.JSONP_FIX) {
41850                 if (window.JSONP_DELAY === 0) {
41851                     callback(window.JSONP_FIX);
41852                 } else {
41853                     var t = window.setTimeout(function() {
41854                         callback(window.JSONP_FIX);
41855                     }, window.JSONP_DELAY || 0);
41856
41857                     request.abort = function() { window.clearTimeout(t); };
41858                 }
41859
41860                 return request;
41861             }
41862
41863             function rand() {
41864                 var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
41865                 var c = '';
41866                 var i = -1;
41867                 while (++i < 15) { c += chars.charAt(Math.floor(Math.random() * 52)); }
41868                 return c;
41869             }
41870
41871             function create(url) {
41872                 var e = url.match(/callback=(\w+)/);
41873                 var c = e ? e[1] : rand();
41874
41875                 jsonpCache[c] = function(data) {
41876                     if (jsonpCache[c]) {
41877                         callback(data);
41878                     }
41879                     finalize();
41880                 };
41881
41882                 function finalize() {
41883                     delete jsonpCache[c];
41884                     script.remove();
41885                 }
41886
41887                 request.abort = finalize;
41888                 return 'jsonpCache.' + c;
41889             }
41890
41891             var cb = create(url);
41892
41893             var script = select('head')
41894                 .append('script')
41895                 .attr('type', 'text/javascript')
41896                 .attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
41897
41898             return request;
41899         }
41900
41901         var bubbleApi = 'https://dev.virtualearth.net/mapcontrol/HumanScaleServices/GetBubbles.ashx?';
41902         var streetsideImagesApi = 'https://t.ssl.ak.tiles.virtualearth.net/tiles/';
41903         var bubbleAppKey = 'AuftgJsO0Xs8Ts4M1xZUQJQXJNsvmh3IV8DkNieCiy3tCwCUMq76-WpkrBtNAuEm';
41904         var pannellumViewerCSS = 'pannellum-streetside/pannellum.css';
41905         var pannellumViewerJS = 'pannellum-streetside/pannellum.js';
41906         var maxResults$2 = 2000;
41907         var tileZoom$2 = 16.5;
41908         var tiler$6 = utilTiler().zoomExtent([tileZoom$2, tileZoom$2]).skipNullIsland(true);
41909         var dispatch$7 = dispatch('loadedBubbles', 'viewerChanged');
41910         var minHfov = 10;         // zoom in degrees:  20, 10, 5
41911         var maxHfov = 90;         // zoom out degrees
41912         var defaultHfov = 45;
41913
41914         var _hires = false;
41915         var _resolution = 512;    // higher numbers are slower - 512, 1024, 2048, 4096
41916         var _currScene = 0;
41917         var _ssCache;
41918         var _pannellumViewer;
41919         var _sceneOptions;
41920         var _dataUrlArray = [];
41921
41922
41923         /**
41924          * abortRequest().
41925          */
41926         function abortRequest$6(i) {
41927           i.abort();
41928         }
41929
41930
41931         /**
41932          * localeTimeStamp().
41933          */
41934         function localeTimestamp(s) {
41935           if (!s) { return null; }
41936           var options = { day: 'numeric', month: 'short', year: 'numeric' };
41937           var d = new Date(s);
41938           if (isNaN(d.getTime())) { return null; }
41939           return d.toLocaleString(_mainLocalizer.localeCode(), options);
41940         }
41941
41942
41943         /**
41944          * loadTiles() wraps the process of generating tiles and then fetching image points for each tile.
41945          */
41946         function loadTiles$2(which, url, projection, margin) {
41947           var tiles = tiler$6.margin(margin).getTiles(projection);
41948
41949           // abort inflight requests that are no longer needed
41950           var cache = _ssCache[which];
41951           Object.keys(cache.inflight).forEach(function (k) {
41952             var wanted = tiles.find(function (tile) { return k.indexOf(tile.id + ',') === 0; });
41953             if (!wanted) {
41954               abortRequest$6(cache.inflight[k]);
41955               delete cache.inflight[k];
41956             }
41957           });
41958
41959           tiles.forEach(function (tile) { return loadNextTilePage$2(which, url, tile); });
41960         }
41961
41962
41963         /**
41964          * loadNextTilePage() load data for the next tile page in line.
41965          */
41966         function loadNextTilePage$2(which, url, tile) {
41967           var cache = _ssCache[which];
41968           var nextPage = cache.nextPage[tile.id] || 0;
41969           var id = tile.id + ',' + String(nextPage);
41970           if (cache.loaded[id] || cache.inflight[id]) { return; }
41971
41972           cache.inflight[id] = getBubbles(url, tile, function (bubbles) {
41973             cache.loaded[id] = true;
41974             delete cache.inflight[id];
41975             if (!bubbles) { return; }
41976
41977             // [].shift() removes the first element, some statistics info, not a bubble point
41978             bubbles.shift();
41979
41980             var features = bubbles.map(function (bubble) {
41981               if (cache.points[bubble.id]) { return null; }  // skip duplicates
41982
41983               var loc = [bubble.lo, bubble.la];
41984               var d = {
41985                 loc: loc,
41986                 key: bubble.id,
41987                 ca: bubble.he,
41988                 captured_at: bubble.cd,
41989                 captured_by: 'microsoft',
41990                 // nbn: bubble.nbn,
41991                 // pbn: bubble.pbn,
41992                 // ad: bubble.ad,
41993                 // rn: bubble.rn,
41994                 pr: bubble.pr,  // previous
41995                 ne: bubble.ne,  // next
41996                 pano: true,
41997                 sequenceKey: null
41998               };
41999
42000               cache.points[bubble.id] = d;
42001
42002               // a sequence starts here
42003               if (bubble.pr === undefined) {
42004                 cache.leaders.push(bubble.id);
42005               }
42006
42007               return {
42008                 minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d
42009               };
42010
42011             }).filter(Boolean);
42012
42013             cache.rtree.load(features);
42014
42015             connectSequences();
42016
42017             if (which === 'bubbles') {
42018               dispatch$7.call('loadedBubbles');
42019             }
42020           });
42021         }
42022
42023
42024         // call this sometimes to connect the bubbles into sequences
42025         function connectSequences() {
42026           var cache = _ssCache.bubbles;
42027           var keepLeaders = [];
42028
42029           for (var i = 0; i < cache.leaders.length; i++) {
42030             var bubble = cache.points[cache.leaders[i]];
42031             var seen = {};
42032
42033             // try to make a sequence.. use the key of the leader bubble.
42034             var sequence = { key: bubble.key, bubbles: [] };
42035             var complete = false;
42036
42037             do {
42038               sequence.bubbles.push(bubble);
42039               seen[bubble.key] = true;
42040
42041               if (bubble.ne === undefined) {
42042                 complete = true;
42043               } else {
42044                 bubble = cache.points[bubble.ne];  // advance to next
42045               }
42046             } while (bubble && !seen[bubble.key] && !complete);
42047
42048
42049             if (complete) {
42050               _ssCache.sequences[sequence.key] = sequence;
42051
42052               // assign bubbles to the sequence
42053               for (var j = 0; j < sequence.bubbles.length; j++) {
42054                 sequence.bubbles[j].sequenceKey = sequence.key;
42055               }
42056
42057               // create a GeoJSON LineString
42058               sequence.geojson = {
42059                 type: 'LineString',
42060                 properties: { key: sequence.key },
42061                 coordinates: sequence.bubbles.map(function (d) { return d.loc; })
42062               };
42063
42064             } else {
42065               keepLeaders.push(cache.leaders[i]);
42066             }
42067           }
42068
42069           // couldn't complete these, save for later
42070           cache.leaders = keepLeaders;
42071         }
42072
42073
42074         /**
42075          * getBubbles() handles the request to the server for a tile extent of 'bubbles' (streetside image locations).
42076          */
42077         function getBubbles(url, tile, callback) {
42078           var rect = tile.extent.rectangle();
42079           var urlForRequest = url + utilQsString({
42080             n: rect[3],
42081             s: rect[1],
42082             e: rect[2],
42083             w: rect[0],
42084             c: maxResults$2,
42085             appkey: bubbleAppKey,
42086             jsCallback: '{callback}'
42087           });
42088
42089           return jsonpRequest(urlForRequest, function (data) {
42090             if (!data || data.error) {
42091               callback(null);
42092             } else {
42093               callback(data);
42094             }
42095           });
42096         }
42097
42098
42099         // partition viewport into higher zoom tiles
42100         function partitionViewport$2(projection) {
42101           var z = geoScaleToZoom(projection.scale());
42102           var z2 = (Math.ceil(z * 2) / 2) + 2.5;   // round to next 0.5 and add 2.5
42103           var tiler = utilTiler().zoomExtent([z2, z2]);
42104
42105           return tiler.getTiles(projection)
42106             .map(function (tile) { return tile.extent; });
42107         }
42108
42109
42110         // no more than `limit` results per partition.
42111         function searchLimited$2(limit, projection, rtree) {
42112           limit = limit || 5;
42113
42114           return partitionViewport$2(projection)
42115             .reduce(function (result, extent) {
42116               var found = rtree.search(extent.bbox())
42117                 .slice(0, limit)
42118                 .map(function (d) { return d.data; });
42119
42120               return (found.length ? result.concat(found) : result);
42121             }, []);
42122         }
42123
42124
42125         /**
42126          * loadImage()
42127          */
42128         function loadImage(imgInfo) {
42129           return new Promise(function (resolve) {
42130             var img = new Image();
42131             img.onload = function () {
42132               var canvas = document.getElementById('ideditor-canvas' + imgInfo.face);
42133               var ctx = canvas.getContext('2d');
42134               ctx.drawImage(img, imgInfo.x, imgInfo.y);
42135               resolve({ imgInfo: imgInfo, status: 'ok' });
42136             };
42137             img.onerror = function () {
42138               resolve({ data: imgInfo, status: 'error' });
42139             };
42140             img.setAttribute('crossorigin', '');
42141             img.src = imgInfo.url;
42142           });
42143         }
42144
42145
42146         /**
42147          * loadCanvas()
42148          */
42149         function loadCanvas(imageGroup) {
42150           return Promise.all(imageGroup.map(loadImage))
42151             .then(function (data) {
42152               var canvas = document.getElementById('ideditor-canvas' + data[0].imgInfo.face);
42153               var which = { '01': 0, '02': 1, '03': 2, '10': 3, '11': 4, '12': 5 };
42154               var face = data[0].imgInfo.face;
42155               _dataUrlArray[which[face]] = canvas.toDataURL('image/jpeg', 1.0);
42156               return { status: 'loadCanvas for face ' + data[0].imgInfo.face + 'ok'};
42157             });
42158         }
42159
42160
42161         /**
42162          * loadFaces()
42163          */
42164         function loadFaces(faceGroup) {
42165           return Promise.all(faceGroup.map(loadCanvas))
42166             .then(function () { return { status: 'loadFaces done' }; });
42167         }
42168
42169
42170         function setupCanvas(selection, reset) {
42171           if (reset) {
42172             selection.selectAll('#ideditor-stitcher-canvases')
42173               .remove();
42174           }
42175
42176           // Add the Streetside working canvases. These are used for 'stitching', or combining,
42177           // multiple images for each of the six faces, before passing to the Pannellum control as DataUrls
42178           selection.selectAll('#ideditor-stitcher-canvases')
42179             .data([0])
42180             .enter()
42181             .append('div')
42182             .attr('id', 'ideditor-stitcher-canvases')
42183             .attr('display', 'none')
42184             .selectAll('canvas')
42185             .data(['canvas01', 'canvas02', 'canvas03', 'canvas10', 'canvas11', 'canvas12'])
42186             .enter()
42187             .append('canvas')
42188             .attr('id', function (d) { return 'ideditor-' + d; })
42189             .attr('width', _resolution)
42190             .attr('height', _resolution);
42191         }
42192
42193
42194         function qkToXY(qk) {
42195           var x = 0;
42196           var y = 0;
42197           var scale = 256;
42198           for (var i = qk.length; i > 0; i--) {
42199             var key = qk[i-1];
42200             x += (+(key === '1' || key === '3')) * scale;
42201             y += (+(key === '2' || key === '3')) * scale;
42202             scale *= 2;
42203           }
42204           return [x, y];
42205         }
42206
42207
42208         function getQuadKeys() {
42209           var dim = _resolution / 256;
42210           var quadKeys;
42211
42212           if (dim === 16) {
42213             quadKeys = [
42214               '0000','0001','0010','0011','0100','0101','0110','0111',  '1000','1001','1010','1011','1100','1101','1110','1111',
42215               '0002','0003','0012','0013','0102','0103','0112','0113',  '1002','1003','1012','1013','1102','1103','1112','1113',
42216               '0020','0021','0030','0031','0120','0121','0130','0131',  '1020','1021','1030','1031','1120','1121','1130','1131',
42217               '0022','0023','0032','0033','0122','0123','0132','0133',  '1022','1023','1032','1033','1122','1123','1132','1133',
42218               '0200','0201','0210','0211','0300','0301','0310','0311',  '1200','1201','1210','1211','1300','1301','1310','1311',
42219               '0202','0203','0212','0213','0302','0303','0312','0313',  '1202','1203','1212','1213','1302','1303','1312','1313',
42220               '0220','0221','0230','0231','0320','0321','0330','0331',  '1220','1221','1230','1231','1320','1321','1330','1331',
42221               '0222','0223','0232','0233','0322','0323','0332','0333',  '1222','1223','1232','1233','1322','1323','1332','1333',
42222
42223               '2000','2001','2010','2011','2100','2101','2110','2111',  '3000','3001','3010','3011','3100','3101','3110','3111',
42224               '2002','2003','2012','2013','2102','2103','2112','2113',  '3002','3003','3012','3013','3102','3103','3112','3113',
42225               '2020','2021','2030','2031','2120','2121','2130','2131',  '3020','3021','3030','3031','3120','3121','3130','3131',
42226               '2022','2023','2032','2033','2122','2123','2132','2133',  '3022','3023','3032','3033','3122','3123','3132','3133',
42227               '2200','2201','2210','2211','2300','2301','2310','2311',  '3200','3201','3210','3211','3300','3301','3310','3311',
42228               '2202','2203','2212','2213','2302','2303','2312','2313',  '3202','3203','3212','3213','3302','3303','3312','3313',
42229               '2220','2221','2230','2231','2320','2321','2330','2331',  '3220','3221','3230','3231','3320','3321','3330','3331',
42230               '2222','2223','2232','2233','2322','2323','2332','2333',  '3222','3223','3232','3233','3322','3323','3332','3333'
42231             ];
42232
42233           } else if (dim === 8) {
42234             quadKeys = [
42235               '000','001','010','011',  '100','101','110','111',
42236               '002','003','012','013',  '102','103','112','113',
42237               '020','021','030','031',  '120','121','130','131',
42238               '022','023','032','033',  '122','123','132','133',
42239
42240               '200','201','210','211',  '300','301','310','311',
42241               '202','203','212','213',  '302','303','312','313',
42242               '220','221','230','231',  '320','321','330','331',
42243               '222','223','232','233',  '322','323','332','333'
42244             ];
42245
42246           } else if (dim === 4) {
42247             quadKeys = [
42248               '00','01',  '10','11',
42249               '02','03',  '12','13',
42250
42251               '20','21',  '30','31',
42252               '22','23',  '32','33'
42253             ];
42254
42255           } else {  // dim === 2
42256             quadKeys = [
42257               '0', '1',
42258               '2', '3'
42259             ];
42260           }
42261
42262           return quadKeys;
42263         }
42264
42265
42266
42267         var serviceStreetside = {
42268           /**
42269            * init() initialize streetside.
42270            */
42271           init: function() {
42272             if (!_ssCache) {
42273               this.reset();
42274             }
42275
42276             this.event = utilRebind(this, dispatch$7, 'on');
42277           },
42278
42279           /**
42280            * reset() reset the cache.
42281            */
42282           reset: function() {
42283             if (_ssCache) {
42284               Object.values(_ssCache.bubbles.inflight).forEach(abortRequest$6);
42285             }
42286
42287             _ssCache = {
42288               bubbles: { inflight: {}, loaded: {}, nextPage: {}, rtree: new RBush(), points: {}, leaders: [] },
42289               sequences: {}
42290             };
42291           },
42292
42293           /**
42294            * bubbles()
42295            */
42296           bubbles: function(projection) {
42297             var limit = 5;
42298             return searchLimited$2(limit, projection, _ssCache.bubbles.rtree);
42299           },
42300
42301
42302           sequences: function(projection) {
42303             var viewport = projection.clipExtent();
42304             var min = [viewport[0][0], viewport[1][1]];
42305             var max = [viewport[1][0], viewport[0][1]];
42306             var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
42307             var seen = {};
42308             var results = [];
42309
42310             // all sequences for bubbles in viewport
42311             _ssCache.bubbles.rtree.search(bbox)
42312               .forEach(function (d) {
42313                 var key = d.data.sequenceKey;
42314                 if (key && !seen[key]) {
42315                     seen[key] = true;
42316                     results.push(_ssCache.sequences[key].geojson);
42317                 }
42318               });
42319
42320             return results;
42321           },
42322
42323
42324           /**
42325            * loadBubbles()
42326            */
42327           loadBubbles: function(projection, margin) {
42328             // by default: request 2 nearby tiles so we can connect sequences.
42329             if (margin === undefined) { margin = 2; }
42330
42331             loadTiles$2('bubbles', bubbleApi, projection, margin);
42332           },
42333
42334
42335           viewer: function() {
42336             return _pannellumViewer;
42337           },
42338
42339
42340           initViewer: function () {
42341             if (!window.pannellum) { return; }
42342             if (_pannellumViewer) { return; }
42343
42344             var sceneID = ++_currScene + '';
42345             var options = {
42346               'default': { firstScene: sceneID },
42347               scenes: {}
42348             };
42349             options.scenes[sceneID] = _sceneOptions;
42350
42351             _pannellumViewer = window.pannellum.viewer('ideditor-viewer-streetside', options);
42352           },
42353
42354
42355           /**
42356            * loadViewer() create the streeside viewer.
42357            */
42358           loadViewer: function(context) {
42359             var that = this;
42360
42361             var pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
42362
42363             // create ms-wrapper, a photo wrapper class
42364             var wrap = context.container().select('.photoviewer').selectAll('.ms-wrapper')
42365               .data([0]);
42366
42367             // inject ms-wrapper into the photoviewer div
42368             // (used by all to house each custom photo viewer)
42369             var wrapEnter = wrap.enter()
42370               .append('div')
42371               .attr('class', 'photo-wrapper ms-wrapper')
42372               .classed('hide', true);
42373
42374             // inject div to support streetside viewer (pannellum) and attribution line
42375             wrapEnter
42376               .append('div')
42377               .attr('id', 'ideditor-viewer-streetside')
42378               .on(pointerPrefix + 'down.streetside', function () {
42379                 select(window)
42380                   .on(pointerPrefix + 'move.streetside', function () {
42381                     dispatch$7.call('viewerChanged');
42382                   }, true);
42383               })
42384               .on(pointerPrefix + 'up.streetside pointercancel.streetside', function () {
42385                 select(window)
42386                   .on(pointerPrefix + 'move.streetside', null);
42387
42388                 // continue dispatching events for a few seconds, in case viewer has inertia.
42389                 var t = timer(function (elapsed) {
42390                   dispatch$7.call('viewerChanged');
42391                   if (elapsed > 2000) {
42392                     t.stop();
42393                   }
42394                 });
42395               })
42396               .append('div')
42397               .attr('class', 'photo-attribution fillD');
42398
42399             var controlsEnter = wrapEnter
42400               .append('div')
42401               .attr('class', 'photo-controls-wrap')
42402               .append('div')
42403               .attr('class', 'photo-controls');
42404
42405             controlsEnter
42406               .append('button')
42407               .on('click.back', step(-1))
42408               .text('◄');
42409
42410             controlsEnter
42411               .append('button')
42412               .on('click.forward', step(1))
42413               .text('►');
42414
42415
42416             // create working canvas for stitching together images
42417             wrap = wrap
42418               .merge(wrapEnter)
42419               .call(setupCanvas, true);
42420
42421             // load streetside pannellum viewer css
42422             select('head').selectAll('#ideditor-streetside-viewercss')
42423               .data([0])
42424               .enter()
42425               .append('link')
42426               .attr('id', 'ideditor-streetside-viewercss')
42427               .attr('rel', 'stylesheet')
42428               .attr('href', context.asset(pannellumViewerCSS));
42429
42430             // load streetside pannellum viewer js
42431             select('head').selectAll('#ideditor-streetside-viewerjs')
42432               .data([0])
42433               .enter()
42434               .append('script')
42435               .attr('id', 'ideditor-streetside-viewerjs')
42436               .attr('src', context.asset(pannellumViewerJS));
42437
42438
42439             // Register viewer resize handler
42440             context.ui().photoviewer.on('resize.streetside', function () {
42441               if (_pannellumViewer) {
42442                 _pannellumViewer.resize();
42443               }
42444             });
42445
42446
42447             function step(stepBy) {
42448               return function () {
42449                 var viewer = context.container().select('.photoviewer');
42450                 var selected = viewer.empty() ? undefined : viewer.datum();
42451                 if (!selected) { return; }
42452
42453                 var nextID = (stepBy === 1 ? selected.ne : selected.pr);
42454                 var yaw = _pannellumViewer.getYaw();
42455                 var ca = selected.ca + yaw;
42456                 var origin = selected.loc;
42457
42458                 // construct a search trapezoid pointing out from current bubble
42459                 var meters = 35;
42460                 var p1 = [
42461                   origin[0] + geoMetersToLon(meters / 5, origin[1]),
42462                   origin[1]
42463                 ];
42464                 var p2 = [
42465                   origin[0] + geoMetersToLon(meters / 2, origin[1]),
42466                   origin[1] + geoMetersToLat(meters)
42467                 ];
42468                 var p3 = [
42469                   origin[0] - geoMetersToLon(meters / 2, origin[1]),
42470                   origin[1] + geoMetersToLat(meters)
42471                 ];
42472                 var p4 = [
42473                   origin[0] - geoMetersToLon(meters / 5, origin[1]),
42474                   origin[1]
42475                 ];
42476
42477                 var poly = [p1, p2, p3, p4, p1];
42478
42479                 // rotate it to face forward/backward
42480                 var angle = (stepBy === 1 ? ca : ca + 180) * (Math.PI / 180);
42481                 poly = geoRotate(poly, -angle, origin);
42482
42483                 var extent = poly.reduce(function (extent, point) {
42484                   return extent.extend(geoExtent(point));
42485                 }, geoExtent());
42486
42487                 // find nearest other bubble in the search polygon
42488                 var minDist = Infinity;
42489                 _ssCache.bubbles.rtree.search(extent.bbox())
42490                   .forEach(function (d) {
42491                     if (d.data.key === selected.key) { return; }
42492                     if (!geoPointInPolygon(d.data.loc, poly)) { return; }
42493
42494                     var dist = geoVecLength(d.data.loc, selected.loc);
42495                     var theta = selected.ca - d.data.ca;
42496                     var minTheta = Math.min(Math.abs(theta), 360 - Math.abs(theta));
42497                     if (minTheta > 20) {
42498                       dist += 5;  // penalize distance if camera angles don't match
42499                     }
42500
42501                     if (dist < minDist) {
42502                       nextID = d.data.key;
42503                       minDist = dist;
42504                     }
42505                   });
42506
42507                 var nextBubble = nextID && _ssCache.bubbles.points[nextID];
42508                 if (!nextBubble) { return; }
42509
42510                 context.map().centerEase(nextBubble.loc);
42511
42512                 that.selectImage(context, nextBubble)
42513                   .then(function (response) {
42514                     if (response.status === 'ok') {
42515                       _sceneOptions.yaw = yaw;
42516                       that.showViewer(context);
42517                     }
42518                   });
42519               };
42520             }
42521           },
42522
42523
42524           /**
42525            * showViewer()
42526            */
42527           showViewer: function(context, yaw) {
42528             if (!_sceneOptions) { return; }
42529
42530             if (yaw !== undefined) {
42531               _sceneOptions.yaw = yaw;
42532             }
42533
42534             if (!_pannellumViewer) {
42535               this.initViewer();
42536             } else {
42537               // make a new scene
42538               var sceneID = ++_currScene + '';
42539               _pannellumViewer
42540                 .addScene(sceneID, _sceneOptions)
42541                 .loadScene(sceneID);
42542
42543               // remove previous scene
42544               if (_currScene > 2) {
42545                 sceneID = (_currScene - 1) + '';
42546                 _pannellumViewer
42547                   .removeScene(sceneID);
42548               }
42549             }
42550
42551             var wrap = context.container().select('.photoviewer')
42552               .classed('hide', false);
42553
42554             var isHidden = wrap.selectAll('.photo-wrapper.ms-wrapper.hide').size();
42555
42556             if (isHidden) {
42557               wrap
42558                 .selectAll('.photo-wrapper:not(.ms-wrapper)')
42559                 .classed('hide', true);
42560
42561               wrap
42562                 .selectAll('.photo-wrapper.ms-wrapper')
42563                 .classed('hide', false);
42564             }
42565
42566             return this;
42567           },
42568
42569
42570           /**
42571            * hideViewer()
42572            */
42573           hideViewer: function (context) {
42574             var viewer = context.container().select('.photoviewer');
42575             if (!viewer.empty()) { viewer.datum(null); }
42576
42577             viewer
42578               .classed('hide', true)
42579               .selectAll('.photo-wrapper')
42580               .classed('hide', true);
42581
42582             context.container().selectAll('.viewfield-group, .sequence, .icon-sign')
42583               .classed('currentView', false);
42584
42585             return this.setStyles(context, null, true);
42586           },
42587
42588
42589           /**
42590            * selectImage().
42591            */
42592           selectImage: function (context, d) {
42593             var that = this;
42594             var viewer = context.container().select('.photoviewer');
42595             if (!viewer.empty()) { viewer.datum(d); }
42596
42597             this.setStyles(context, null, true);
42598
42599             var wrap = context.container().select('.photoviewer .ms-wrapper');
42600             var attribution = wrap.selectAll('.photo-attribution').html('');
42601
42602             wrap.selectAll('.pnlm-load-box')   // display "loading.."
42603               .style('display', 'block');
42604
42605             if (!d) {
42606               return Promise.resolve({ status: 'ok' });
42607             }
42608
42609             var line1 = attribution
42610               .append('div')
42611               .attr('class', 'attribution-row');
42612
42613             var hiresDomId = utilUniqueDomId('streetside-hires');
42614
42615             // Add hires checkbox
42616             var label = line1
42617               .append('label')
42618               .attr('for', hiresDomId)
42619               .attr('class', 'streetside-hires');
42620
42621             label
42622               .append('input')
42623               .attr('type', 'checkbox')
42624               .attr('id', hiresDomId)
42625               .property('checked', _hires)
42626               .on('click', function () {
42627                 event.stopPropagation();
42628
42629                 _hires = !_hires;
42630                 _resolution = _hires ? 1024 : 512;
42631                 wrap.call(setupCanvas, true);
42632
42633                 var viewstate = {
42634                   yaw: _pannellumViewer.getYaw(),
42635                   pitch: _pannellumViewer.getPitch(),
42636                   hfov: _pannellumViewer.getHfov()
42637                 };
42638
42639                 that.selectImage(context, d)
42640                   .then(function (response) {
42641                     if (response.status === 'ok') {
42642                       _sceneOptions = Object.assign(_sceneOptions, viewstate);
42643                       that.showViewer(context);
42644                     }
42645                   });
42646               });
42647
42648             label
42649               .append('span')
42650               .text(_t('streetside.hires'));
42651
42652
42653             var captureInfo = line1
42654               .append('div')
42655               .attr('class', 'attribution-capture-info');
42656
42657             // Add capture date
42658             if (d.captured_by) {
42659               var yyyy = (new Date()).getFullYear();
42660
42661               captureInfo
42662                 .append('a')
42663                 .attr('class', 'captured_by')
42664                 .attr('target', '_blank')
42665                 .attr('href', 'https://www.microsoft.com/en-us/maps/streetside')
42666                 .text('©' + yyyy + ' Microsoft');
42667
42668               captureInfo
42669                 .append('span')
42670                 .text('|');
42671             }
42672
42673             if (d.captured_at) {
42674               captureInfo
42675                 .append('span')
42676                 .attr('class', 'captured_at')
42677                 .text(localeTimestamp(d.captured_at));
42678             }
42679
42680             // Add image links
42681             var line2 = attribution
42682               .append('div')
42683               .attr('class', 'attribution-row');
42684
42685             line2
42686               .append('a')
42687               .attr('class', 'image-view-link')
42688               .attr('target', '_blank')
42689               .attr('href', 'https://www.bing.com/maps?cp=' + d.loc[1] + '~' + d.loc[0] +
42690                 '&lvl=17&dir=' + d.ca + '&style=x&v=2&sV=1')
42691               .text(_t('streetside.view_on_bing'));
42692
42693             line2
42694               .append('a')
42695               .attr('class', 'image-report-link')
42696               .attr('target', '_blank')
42697               .attr('href', 'https://www.bing.com/maps/privacyreport/streetsideprivacyreport?bubbleid=' +
42698                 encodeURIComponent(d.key) + '&focus=photo&lat=' + d.loc[1] + '&lng=' + d.loc[0] + '&z=17')
42699               .text(_t('streetside.report'));
42700
42701
42702             var bubbleIdQuadKey = d.key.toString(4);
42703             var paddingNeeded = 16 - bubbleIdQuadKey.length;
42704             for (var i = 0; i < paddingNeeded; i++) {
42705               bubbleIdQuadKey = '0' + bubbleIdQuadKey;
42706             }
42707             var imgUrlPrefix = streetsideImagesApi + 'hs' + bubbleIdQuadKey;
42708             var imgUrlSuffix = '.jpg?g=6338&n=z';
42709
42710             // Cubemap face code order matters here: front=01, right=02, back=03, left=10, up=11, down=12
42711             var faceKeys = ['01','02','03','10','11','12'];
42712
42713             // Map images to cube faces
42714             var quadKeys = getQuadKeys();
42715             var faces = faceKeys.map(function (faceKey) {
42716               return quadKeys.map(function (quadKey) {
42717                 var xy = qkToXY(quadKey);
42718                 return {
42719                   face: faceKey,
42720                   url: imgUrlPrefix + faceKey + quadKey + imgUrlSuffix,
42721                   x: xy[0],
42722                   y: xy[1]
42723                 };
42724               });
42725             });
42726
42727             return loadFaces(faces)
42728               .then(function () {
42729                 _sceneOptions = {
42730                   showFullscreenCtrl: false,
42731                   autoLoad: true,
42732                   compass: true,
42733                   northOffset: d.ca,
42734                   yaw: 0,
42735                   minHfov: minHfov,
42736                   maxHfov: maxHfov,
42737                   hfov: defaultHfov,
42738                   type: 'cubemap',
42739                   cubeMap: [
42740                     _dataUrlArray[0],
42741                     _dataUrlArray[1],
42742                     _dataUrlArray[2],
42743                     _dataUrlArray[3],
42744                     _dataUrlArray[4],
42745                     _dataUrlArray[5]
42746                   ]
42747                 };
42748                 return { status: 'ok' };
42749               });
42750           },
42751
42752
42753           getSequenceKeyForBubble: function(d) {
42754             return d && d.sequenceKey;
42755           },
42756
42757
42758           // Updates the currently highlighted sequence and selected bubble.
42759           // Reset is only necessary when interacting with the viewport because
42760           // this implicitly changes the currently selected bubble/sequence
42761           setStyles: function (context, hovered, reset) {
42762             if (reset) {  // reset all layers
42763               context.container().selectAll('.viewfield-group')
42764                 .classed('highlighted', false)
42765                 .classed('hovered', false)
42766                 .classed('currentView', false);
42767
42768               context.container().selectAll('.sequence')
42769                 .classed('highlighted', false)
42770                 .classed('currentView', false);
42771             }
42772
42773             var hoveredBubbleKey = hovered && hovered.key;
42774             var hoveredSequenceKey = this.getSequenceKeyForBubble(hovered);
42775             var hoveredSequence = hoveredSequenceKey && _ssCache.sequences[hoveredSequenceKey];
42776             var hoveredBubbleKeys =  (hoveredSequence && hoveredSequence.bubbles.map(function (d) { return d.key; })) || [];
42777
42778             var viewer = context.container().select('.photoviewer');
42779             var selected = viewer.empty() ? undefined : viewer.datum();
42780             var selectedBubbleKey = selected && selected.key;
42781             var selectedSequenceKey = this.getSequenceKeyForBubble(selected);
42782             var selectedSequence = selectedSequenceKey && _ssCache.sequences[selectedSequenceKey];
42783             var selectedBubbleKeys = (selectedSequence && selectedSequence.bubbles.map(function (d) { return d.key; })) || [];
42784
42785             // highlight sibling viewfields on either the selected or the hovered sequences
42786             var highlightedBubbleKeys = utilArrayUnion(hoveredBubbleKeys, selectedBubbleKeys);
42787
42788             context.container().selectAll('.layer-streetside-images .viewfield-group')
42789               .classed('highlighted', function (d) { return highlightedBubbleKeys.indexOf(d.key) !== -1; })
42790               .classed('hovered',     function (d) { return d.key === hoveredBubbleKey; })
42791               .classed('currentView', function (d) { return d.key === selectedBubbleKey; });
42792
42793             context.container().selectAll('.layer-streetside-images .sequence')
42794               .classed('highlighted', function (d) { return d.properties.key === hoveredSequenceKey; })
42795               .classed('currentView', function (d) { return d.properties.key === selectedSequenceKey; });
42796
42797             // update viewfields if needed
42798             context.container().selectAll('.viewfield-group .viewfield')
42799               .attr('d', viewfieldPath);
42800
42801             function viewfieldPath() {
42802               var d = this.parentNode.__data__;
42803               if (d.pano && d.key !== selectedBubbleKey) {
42804                 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
42805               } else {
42806                 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
42807               }
42808             }
42809
42810             return this;
42811           },
42812
42813
42814           /**
42815            * cache().
42816            */
42817           cache: function () {
42818             return _ssCache;
42819           }
42820         };
42821
42822         var apibase$4 = 'https://taginfo.openstreetmap.org/api/4/';
42823         var _inflight$2 = {};
42824         var _popularKeys = {};
42825         var _taginfoCache = {};
42826
42827         var tag_sorts = {
42828             point: 'count_nodes',
42829             vertex: 'count_nodes',
42830             area: 'count_ways',
42831             line: 'count_ways'
42832         };
42833         var tag_sort_members = {
42834             point: 'count_node_members',
42835             vertex: 'count_node_members',
42836             area: 'count_way_members',
42837             line: 'count_way_members',
42838             relation: 'count_relation_members'
42839         };
42840         var tag_filters = {
42841             point: 'nodes',
42842             vertex: 'nodes',
42843             area: 'ways',
42844             line: 'ways'
42845         };
42846         var tag_members_fractions = {
42847             point: 'count_node_members_fraction',
42848             vertex: 'count_node_members_fraction',
42849             area: 'count_way_members_fraction',
42850             line: 'count_way_members_fraction',
42851             relation: 'count_relation_members_fraction'
42852         };
42853
42854
42855         function sets(params, n, o) {
42856             if (params.geometry && o[params.geometry]) {
42857                 params[n] = o[params.geometry];
42858             }
42859             return params;
42860         }
42861
42862
42863         function setFilter(params) {
42864             return sets(params, 'filter', tag_filters);
42865         }
42866
42867
42868         function setSort(params) {
42869             return sets(params, 'sortname', tag_sorts);
42870         }
42871
42872
42873         function setSortMembers(params) {
42874             return sets(params, 'sortname', tag_sort_members);
42875         }
42876
42877
42878         function clean(params) {
42879             return utilObjectOmit(params, ['geometry', 'debounce']);
42880         }
42881
42882
42883         function filterKeys(type) {
42884             var count_type = type ? 'count_' + type : 'count_all';
42885             return function(d) {
42886                 return parseFloat(d[count_type]) > 2500 || d.in_wiki;
42887             };
42888         }
42889
42890
42891         function filterMultikeys(prefix) {
42892             return function(d) {
42893                 // d.key begins with prefix, and d.key contains no additional ':'s
42894                 var re = new RegExp('^' + prefix + '(.*)$');
42895                 var matches = d.key.match(re) || [];
42896                 return (matches.length === 2 && matches[1].indexOf(':') === -1);
42897             };
42898         }
42899
42900
42901         function filterValues(allowUpperCase) {
42902             return function(d) {
42903                 if (d.value.match(/[;,]/) !== null) { return false; }  // exclude some punctuation
42904                 if (!allowUpperCase && d.value.match(/[A-Z*]/) !== null) { return false; }  // exclude uppercase letters
42905                 return parseFloat(d.fraction) > 0.0;
42906             };
42907         }
42908
42909
42910         function filterRoles(geometry) {
42911             return function(d) {
42912                 if (d.role === '') { return false; } // exclude empty role
42913                 if (d.role.match(/[A-Z*;,]/) !== null) { return false; }  // exclude uppercase letters and some punctuation
42914                 return parseFloat(d[tag_members_fractions[geometry]]) > 0.0;
42915             };
42916         }
42917
42918
42919         function valKey(d) {
42920             return {
42921                 value: d.key,
42922                 title: d.key
42923             };
42924         }
42925
42926
42927         function valKeyDescription(d) {
42928             var obj = {
42929                 value: d.value,
42930                 title: d.description || d.value
42931             };
42932             if (d.count) {
42933                 obj.count = d.count;
42934             }
42935             return obj;
42936         }
42937
42938
42939         function roleKey(d) {
42940             return {
42941                 value: d.role,
42942                 title: d.role
42943             };
42944         }
42945
42946
42947         // sort keys with ':' lower than keys without ':'
42948         function sortKeys(a, b) {
42949             return (a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1) ? -1
42950                 : (a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1) ? 1
42951                 : 0;
42952         }
42953
42954
42955         var debouncedRequest$1 = debounce(request$1, 300, { leading: false });
42956
42957         function request$1(url, params, exactMatch, callback, loaded) {
42958             if (_inflight$2[url]) { return; }
42959
42960             if (checkCache(url, params, exactMatch, callback)) { return; }
42961
42962             var controller = new AbortController();
42963             _inflight$2[url] = controller;
42964
42965             d3_json(url, { signal: controller.signal })
42966                 .then(function(result) {
42967                     delete _inflight$2[url];
42968                     if (loaded) { loaded(null, result); }
42969                 })
42970                 .catch(function(err) {
42971                     delete _inflight$2[url];
42972                     if (err.name === 'AbortError') { return; }
42973                     if (loaded) { loaded(err.message); }
42974                 });
42975         }
42976
42977
42978         function checkCache(url, params, exactMatch, callback) {
42979             var rp = params.rp || 25;
42980             var testQuery = params.query || '';
42981             var testUrl = url;
42982
42983             do {
42984                 var hit = _taginfoCache[testUrl];
42985
42986                 // exact match, or shorter match yielding fewer than max results (rp)
42987                 if (hit && (url === testUrl || hit.length < rp)) {
42988                     callback(null, hit);
42989                     return true;
42990                 }
42991
42992                 // don't try to shorten the query
42993                 if (exactMatch || !testQuery.length) { return false; }
42994
42995                 // do shorten the query to see if we already have a cached result
42996                 // that has returned fewer than max results (rp)
42997                 testQuery = testQuery.slice(0, -1);
42998                 testUrl = url.replace(/&query=(.*?)&/, '&query=' + testQuery + '&');
42999             } while (testQuery.length >= 0);
43000
43001             return false;
43002         }
43003
43004
43005         var serviceTaginfo = {
43006
43007             init: function() {
43008                 _inflight$2 = {};
43009                 _taginfoCache = {};
43010                 _popularKeys = {
43011                     // manually exclude some keys – #5377, #7485
43012                     postal_code: true,
43013                     full_name: true,
43014                     loc_name: true,
43015                     reg_name: true,
43016                     short_name: true,
43017                     sorting_name: true,
43018                     artist_name: true,
43019                     nat_name: true,
43020                     long_name: true,
43021                     'bridge:name': true
43022                 };
43023
43024                 // Fetch popular keys.  We'll exclude these from `values`
43025                 // lookups because they stress taginfo, and they aren't likely
43026                 // to yield meaningful autocomplete results.. see #3955
43027                 var params = {
43028                     rp: 100,
43029                     sortname: 'values_all',
43030                     sortorder: 'desc',
43031                     page: 1,
43032                     debounce: false,
43033                     lang: _mainLocalizer.languageCode()
43034                 };
43035                 this.keys(params, function(err, data) {
43036                     if (err) { return; }
43037                     data.forEach(function(d) {
43038                         if (d.value === 'opening_hours') { return; }  // exception
43039                         _popularKeys[d.value] = true;
43040                     });
43041                 });
43042             },
43043
43044
43045             reset: function() {
43046                 Object.values(_inflight$2).forEach(function(controller) { controller.abort(); });
43047                 _inflight$2 = {};
43048             },
43049
43050
43051             keys: function(params, callback) {
43052                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43053                 params = clean(setSort(params));
43054                 params = Object.assign({
43055                     rp: 10,
43056                     sortname: 'count_all',
43057                     sortorder: 'desc',
43058                     page: 1,
43059                     lang: _mainLocalizer.languageCode()
43060                 }, params);
43061
43062                 var url = apibase$4 + 'keys/all?' + utilQsString(params);
43063                 doRequest(url, params, false, callback, function(err, d) {
43064                     if (err) {
43065                         callback(err);
43066                     } else {
43067                         var f = filterKeys(params.filter);
43068                         var result = d.data.filter(f).sort(sortKeys).map(valKey);
43069                         _taginfoCache[url] = result;
43070                         callback(null, result);
43071                     }
43072                 });
43073             },
43074
43075
43076             multikeys: function(params, callback) {
43077                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43078                 params = clean(setSort(params));
43079                 params = Object.assign({
43080                     rp: 25,
43081                     sortname: 'count_all',
43082                     sortorder: 'desc',
43083                     page: 1,
43084                     lang: _mainLocalizer.languageCode()
43085                 }, params);
43086
43087                 var prefix = params.query;
43088                 var url = apibase$4 + 'keys/all?' + utilQsString(params);
43089                 doRequest(url, params, true, callback, function(err, d) {
43090                     if (err) {
43091                         callback(err);
43092                     } else {
43093                         var f = filterMultikeys(prefix);
43094                         var result = d.data.filter(f).map(valKey);
43095                         _taginfoCache[url] = result;
43096                         callback(null, result);
43097                     }
43098                 });
43099             },
43100
43101
43102             values: function(params, callback) {
43103                 // Exclude popular keys from values lookups.. see #3955
43104                 var key = params.key;
43105                 if (key && _popularKeys[key]) {
43106                     callback(null, []);
43107                     return;
43108                 }
43109
43110                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43111                 params = clean(setSort(setFilter(params)));
43112                 params = Object.assign({
43113                     rp: 25,
43114                     sortname: 'count_all',
43115                     sortorder: 'desc',
43116                     page: 1,
43117                     lang: _mainLocalizer.languageCode()
43118                 }, params);
43119
43120                 var url = apibase$4 + 'key/values?' + utilQsString(params);
43121                 doRequest(url, params, false, callback, function(err, d) {
43122                     if (err) {
43123                         callback(err);
43124                     } else {
43125                         // In most cases we prefer taginfo value results with lowercase letters.
43126                         // A few OSM keys expect values to contain uppercase values (see #3377).
43127                         // This is not an exhaustive list (e.g. `name` also has uppercase values)
43128                         // but these are the fields where taginfo value lookup is most useful.
43129                         var re = /network|taxon|genus|species|brand|grape_variety|royal_cypher|listed_status|booth|rating|stars|:output|_hours|_times|_ref|manufacturer|country|target|brewery/;
43130                         var allowUpperCase = re.test(params.key);
43131                         var f = filterValues(allowUpperCase);
43132
43133                         var result = d.data.filter(f).map(valKeyDescription);
43134                         _taginfoCache[url] = result;
43135                         callback(null, result);
43136                     }
43137                 });
43138             },
43139
43140
43141             roles: function(params, callback) {
43142                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43143                 var geometry = params.geometry;
43144                 params = clean(setSortMembers(params));
43145                 params = Object.assign({
43146                     rp: 25,
43147                     sortname: 'count_all_members',
43148                     sortorder: 'desc',
43149                     page: 1,
43150                     lang: _mainLocalizer.languageCode()
43151                 }, params);
43152
43153                 var url = apibase$4 + 'relation/roles?' + utilQsString(params);
43154                 doRequest(url, params, true, callback, function(err, d) {
43155                     if (err) {
43156                         callback(err);
43157                     } else {
43158                         var f = filterRoles(geometry);
43159                         var result = d.data.filter(f).map(roleKey);
43160                         _taginfoCache[url] = result;
43161                         callback(null, result);
43162                     }
43163                 });
43164             },
43165
43166
43167             docs: function(params, callback) {
43168                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43169                 params = clean(setSort(params));
43170
43171                 var path = 'key/wiki_pages?';
43172                 if (params.value) {
43173                     path = 'tag/wiki_pages?';
43174                 } else if (params.rtype) {
43175                     path = 'relation/wiki_pages?';
43176                 }
43177
43178                 var url = apibase$4 + path + utilQsString(params);
43179                 doRequest(url, params, true, callback, function(err, d) {
43180                     if (err) {
43181                         callback(err);
43182                     } else {
43183                         _taginfoCache[url] = d.data;
43184                         callback(null, d.data);
43185                     }
43186                 });
43187             },
43188
43189
43190             apibase: function(_) {
43191                 if (!arguments.length) { return apibase$4; }
43192                 apibase$4 = _;
43193                 return this;
43194             }
43195
43196         };
43197
43198         var helpers$1 = createCommonjsModule(function (module, exports) {
43199         Object.defineProperty(exports, "__esModule", { value: true });
43200         /**
43201          * @module helpers
43202          */
43203         /**
43204          * Earth Radius used with the Harvesine formula and approximates using a spherical (non-ellipsoid) Earth.
43205          *
43206          * @memberof helpers
43207          * @type {number}
43208          */
43209         exports.earthRadius = 6371008.8;
43210         /**
43211          * Unit of measurement factors using a spherical (non-ellipsoid) earth radius.
43212          *
43213          * @memberof helpers
43214          * @type {Object}
43215          */
43216         exports.factors = {
43217             centimeters: exports.earthRadius * 100,
43218             centimetres: exports.earthRadius * 100,
43219             degrees: exports.earthRadius / 111325,
43220             feet: exports.earthRadius * 3.28084,
43221             inches: exports.earthRadius * 39.370,
43222             kilometers: exports.earthRadius / 1000,
43223             kilometres: exports.earthRadius / 1000,
43224             meters: exports.earthRadius,
43225             metres: exports.earthRadius,
43226             miles: exports.earthRadius / 1609.344,
43227             millimeters: exports.earthRadius * 1000,
43228             millimetres: exports.earthRadius * 1000,
43229             nauticalmiles: exports.earthRadius / 1852,
43230             radians: 1,
43231             yards: exports.earthRadius / 1.0936,
43232         };
43233         /**
43234          * Units of measurement factors based on 1 meter.
43235          *
43236          * @memberof helpers
43237          * @type {Object}
43238          */
43239         exports.unitsFactors = {
43240             centimeters: 100,
43241             centimetres: 100,
43242             degrees: 1 / 111325,
43243             feet: 3.28084,
43244             inches: 39.370,
43245             kilometers: 1 / 1000,
43246             kilometres: 1 / 1000,
43247             meters: 1,
43248             metres: 1,
43249             miles: 1 / 1609.344,
43250             millimeters: 1000,
43251             millimetres: 1000,
43252             nauticalmiles: 1 / 1852,
43253             radians: 1 / exports.earthRadius,
43254             yards: 1 / 1.0936,
43255         };
43256         /**
43257          * Area of measurement factors based on 1 square meter.
43258          *
43259          * @memberof helpers
43260          * @type {Object}
43261          */
43262         exports.areaFactors = {
43263             acres: 0.000247105,
43264             centimeters: 10000,
43265             centimetres: 10000,
43266             feet: 10.763910417,
43267             inches: 1550.003100006,
43268             kilometers: 0.000001,
43269             kilometres: 0.000001,
43270             meters: 1,
43271             metres: 1,
43272             miles: 3.86e-7,
43273             millimeters: 1000000,
43274             millimetres: 1000000,
43275             yards: 1.195990046,
43276         };
43277         /**
43278          * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}.
43279          *
43280          * @name feature
43281          * @param {Geometry} geometry input geometry
43282          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43283          * @param {Object} [options={}] Optional Parameters
43284          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43285          * @param {string|number} [options.id] Identifier associated with the Feature
43286          * @returns {Feature} a GeoJSON Feature
43287          * @example
43288          * var geometry = {
43289          *   "type": "Point",
43290          *   "coordinates": [110, 50]
43291          * };
43292          *
43293          * var feature = turf.feature(geometry);
43294          *
43295          * //=feature
43296          */
43297         function feature(geom, properties, options) {
43298             if (options === void 0) { options = {}; }
43299             var feat = { type: "Feature" };
43300             if (options.id === 0 || options.id) {
43301                 feat.id = options.id;
43302             }
43303             if (options.bbox) {
43304                 feat.bbox = options.bbox;
43305             }
43306             feat.properties = properties || {};
43307             feat.geometry = geom;
43308             return feat;
43309         }
43310         exports.feature = feature;
43311         /**
43312          * Creates a GeoJSON {@link Geometry} from a Geometry string type & coordinates.
43313          * For GeometryCollection type use `helpers.geometryCollection`
43314          *
43315          * @name geometry
43316          * @param {string} type Geometry Type
43317          * @param {Array<any>} coordinates Coordinates
43318          * @param {Object} [options={}] Optional Parameters
43319          * @returns {Geometry} a GeoJSON Geometry
43320          * @example
43321          * var type = "Point";
43322          * var coordinates = [110, 50];
43323          * var geometry = turf.geometry(type, coordinates);
43324          * // => geometry
43325          */
43326         function geometry(type, coordinates, options) {
43327             switch (type) {
43328                 case "Point": return point(coordinates).geometry;
43329                 case "LineString": return lineString(coordinates).geometry;
43330                 case "Polygon": return polygon(coordinates).geometry;
43331                 case "MultiPoint": return multiPoint(coordinates).geometry;
43332                 case "MultiLineString": return multiLineString(coordinates).geometry;
43333                 case "MultiPolygon": return multiPolygon(coordinates).geometry;
43334                 default: throw new Error(type + " is invalid");
43335             }
43336         }
43337         exports.geometry = geometry;
43338         /**
43339          * Creates a {@link Point} {@link Feature} from a Position.
43340          *
43341          * @name point
43342          * @param {Array<number>} coordinates longitude, latitude position (each in decimal degrees)
43343          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43344          * @param {Object} [options={}] Optional Parameters
43345          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43346          * @param {string|number} [options.id] Identifier associated with the Feature
43347          * @returns {Feature<Point>} a Point feature
43348          * @example
43349          * var point = turf.point([-75.343, 39.984]);
43350          *
43351          * //=point
43352          */
43353         function point(coordinates, properties, options) {
43354             if (options === void 0) { options = {}; }
43355             var geom = {
43356                 type: "Point",
43357                 coordinates: coordinates,
43358             };
43359             return feature(geom, properties, options);
43360         }
43361         exports.point = point;
43362         /**
43363          * Creates a {@link Point} {@link FeatureCollection} from an Array of Point coordinates.
43364          *
43365          * @name points
43366          * @param {Array<Array<number>>} coordinates an array of Points
43367          * @param {Object} [properties={}] Translate these properties to each Feature
43368          * @param {Object} [options={}] Optional Parameters
43369          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north]
43370          * associated with the FeatureCollection
43371          * @param {string|number} [options.id] Identifier associated with the FeatureCollection
43372          * @returns {FeatureCollection<Point>} Point Feature
43373          * @example
43374          * var points = turf.points([
43375          *   [-75, 39],
43376          *   [-80, 45],
43377          *   [-78, 50]
43378          * ]);
43379          *
43380          * //=points
43381          */
43382         function points(coordinates, properties, options) {
43383             if (options === void 0) { options = {}; }
43384             return featureCollection(coordinates.map(function (coords) {
43385                 return point(coords, properties);
43386             }), options);
43387         }
43388         exports.points = points;
43389         /**
43390          * Creates a {@link Polygon} {@link Feature} from an Array of LinearRings.
43391          *
43392          * @name polygon
43393          * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
43394          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43395          * @param {Object} [options={}] Optional Parameters
43396          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43397          * @param {string|number} [options.id] Identifier associated with the Feature
43398          * @returns {Feature<Polygon>} Polygon Feature
43399          * @example
43400          * var polygon = turf.polygon([[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]], { name: 'poly1' });
43401          *
43402          * //=polygon
43403          */
43404         function polygon(coordinates, properties, options) {
43405             if (options === void 0) { options = {}; }
43406             for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) {
43407                 var ring = coordinates_1[_i];
43408                 if (ring.length < 4) {
43409                     throw new Error("Each LinearRing of a Polygon must have 4 or more Positions.");
43410                 }
43411                 for (var j = 0; j < ring[ring.length - 1].length; j++) {
43412                     // Check if first point of Polygon contains two numbers
43413                     if (ring[ring.length - 1][j] !== ring[0][j]) {
43414                         throw new Error("First and last Position are not equivalent.");
43415                     }
43416                 }
43417             }
43418             var geom = {
43419                 type: "Polygon",
43420                 coordinates: coordinates,
43421             };
43422             return feature(geom, properties, options);
43423         }
43424         exports.polygon = polygon;
43425         /**
43426          * Creates a {@link Polygon} {@link FeatureCollection} from an Array of Polygon coordinates.
43427          *
43428          * @name polygons
43429          * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygon coordinates
43430          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43431          * @param {Object} [options={}] Optional Parameters
43432          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43433          * @param {string|number} [options.id] Identifier associated with the FeatureCollection
43434          * @returns {FeatureCollection<Polygon>} Polygon FeatureCollection
43435          * @example
43436          * var polygons = turf.polygons([
43437          *   [[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]],
43438          *   [[[-15, 42], [-14, 46], [-12, 41], [-17, 44], [-15, 42]]],
43439          * ]);
43440          *
43441          * //=polygons
43442          */
43443         function polygons(coordinates, properties, options) {
43444             if (options === void 0) { options = {}; }
43445             return featureCollection(coordinates.map(function (coords) {
43446                 return polygon(coords, properties);
43447             }), options);
43448         }
43449         exports.polygons = polygons;
43450         /**
43451          * Creates a {@link LineString} {@link Feature} from an Array of Positions.
43452          *
43453          * @name lineString
43454          * @param {Array<Array<number>>} coordinates an array of Positions
43455          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43456          * @param {Object} [options={}] Optional Parameters
43457          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43458          * @param {string|number} [options.id] Identifier associated with the Feature
43459          * @returns {Feature<LineString>} LineString Feature
43460          * @example
43461          * var linestring1 = turf.lineString([[-24, 63], [-23, 60], [-25, 65], [-20, 69]], {name: 'line 1'});
43462          * var linestring2 = turf.lineString([[-14, 43], [-13, 40], [-15, 45], [-10, 49]], {name: 'line 2'});
43463          *
43464          * //=linestring1
43465          * //=linestring2
43466          */
43467         function lineString(coordinates, properties, options) {
43468             if (options === void 0) { options = {}; }
43469             if (coordinates.length < 2) {
43470                 throw new Error("coordinates must be an array of two or more positions");
43471             }
43472             var geom = {
43473                 type: "LineString",
43474                 coordinates: coordinates,
43475             };
43476             return feature(geom, properties, options);
43477         }
43478         exports.lineString = lineString;
43479         /**
43480          * Creates a {@link LineString} {@link FeatureCollection} from an Array of LineString coordinates.
43481          *
43482          * @name lineStrings
43483          * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
43484          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43485          * @param {Object} [options={}] Optional Parameters
43486          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north]
43487          * associated with the FeatureCollection
43488          * @param {string|number} [options.id] Identifier associated with the FeatureCollection
43489          * @returns {FeatureCollection<LineString>} LineString FeatureCollection
43490          * @example
43491          * var linestrings = turf.lineStrings([
43492          *   [[-24, 63], [-23, 60], [-25, 65], [-20, 69]],
43493          *   [[-14, 43], [-13, 40], [-15, 45], [-10, 49]]
43494          * ]);
43495          *
43496          * //=linestrings
43497          */
43498         function lineStrings(coordinates, properties, options) {
43499             if (options === void 0) { options = {}; }
43500             return featureCollection(coordinates.map(function (coords) {
43501                 return lineString(coords, properties);
43502             }), options);
43503         }
43504         exports.lineStrings = lineStrings;
43505         /**
43506          * Takes one or more {@link Feature|Features} and creates a {@link FeatureCollection}.
43507          *
43508          * @name featureCollection
43509          * @param {Feature[]} features input features
43510          * @param {Object} [options={}] Optional Parameters
43511          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43512          * @param {string|number} [options.id] Identifier associated with the Feature
43513          * @returns {FeatureCollection} FeatureCollection of Features
43514          * @example
43515          * var locationA = turf.point([-75.343, 39.984], {name: 'Location A'});
43516          * var locationB = turf.point([-75.833, 39.284], {name: 'Location B'});
43517          * var locationC = turf.point([-75.534, 39.123], {name: 'Location C'});
43518          *
43519          * var collection = turf.featureCollection([
43520          *   locationA,
43521          *   locationB,
43522          *   locationC
43523          * ]);
43524          *
43525          * //=collection
43526          */
43527         function featureCollection(features, options) {
43528             if (options === void 0) { options = {}; }
43529             var fc = { type: "FeatureCollection" };
43530             if (options.id) {
43531                 fc.id = options.id;
43532             }
43533             if (options.bbox) {
43534                 fc.bbox = options.bbox;
43535             }
43536             fc.features = features;
43537             return fc;
43538         }
43539         exports.featureCollection = featureCollection;
43540         /**
43541          * Creates a {@link Feature<MultiLineString>} based on a
43542          * coordinate array. Properties can be added optionally.
43543          *
43544          * @name multiLineString
43545          * @param {Array<Array<Array<number>>>} coordinates an array of LineStrings
43546          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43547          * @param {Object} [options={}] Optional Parameters
43548          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43549          * @param {string|number} [options.id] Identifier associated with the Feature
43550          * @returns {Feature<MultiLineString>} a MultiLineString feature
43551          * @throws {Error} if no coordinates are passed
43552          * @example
43553          * var multiLine = turf.multiLineString([[[0,0],[10,10]]]);
43554          *
43555          * //=multiLine
43556          */
43557         function multiLineString(coordinates, properties, options) {
43558             if (options === void 0) { options = {}; }
43559             var geom = {
43560                 type: "MultiLineString",
43561                 coordinates: coordinates,
43562             };
43563             return feature(geom, properties, options);
43564         }
43565         exports.multiLineString = multiLineString;
43566         /**
43567          * Creates a {@link Feature<MultiPoint>} based on a
43568          * coordinate array. Properties can be added optionally.
43569          *
43570          * @name multiPoint
43571          * @param {Array<Array<number>>} coordinates an array of Positions
43572          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43573          * @param {Object} [options={}] Optional Parameters
43574          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43575          * @param {string|number} [options.id] Identifier associated with the Feature
43576          * @returns {Feature<MultiPoint>} a MultiPoint feature
43577          * @throws {Error} if no coordinates are passed
43578          * @example
43579          * var multiPt = turf.multiPoint([[0,0],[10,10]]);
43580          *
43581          * //=multiPt
43582          */
43583         function multiPoint(coordinates, properties, options) {
43584             if (options === void 0) { options = {}; }
43585             var geom = {
43586                 type: "MultiPoint",
43587                 coordinates: coordinates,
43588             };
43589             return feature(geom, properties, options);
43590         }
43591         exports.multiPoint = multiPoint;
43592         /**
43593          * Creates a {@link Feature<MultiPolygon>} based on a
43594          * coordinate array. Properties can be added optionally.
43595          *
43596          * @name multiPolygon
43597          * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygons
43598          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43599          * @param {Object} [options={}] Optional Parameters
43600          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43601          * @param {string|number} [options.id] Identifier associated with the Feature
43602          * @returns {Feature<MultiPolygon>} a multipolygon feature
43603          * @throws {Error} if no coordinates are passed
43604          * @example
43605          * var multiPoly = turf.multiPolygon([[[[0,0],[0,10],[10,10],[10,0],[0,0]]]]);
43606          *
43607          * //=multiPoly
43608          *
43609          */
43610         function multiPolygon(coordinates, properties, options) {
43611             if (options === void 0) { options = {}; }
43612             var geom = {
43613                 type: "MultiPolygon",
43614                 coordinates: coordinates,
43615             };
43616             return feature(geom, properties, options);
43617         }
43618         exports.multiPolygon = multiPolygon;
43619         /**
43620          * Creates a {@link Feature<GeometryCollection>} based on a
43621          * coordinate array. Properties can be added optionally.
43622          *
43623          * @name geometryCollection
43624          * @param {Array<Geometry>} geometries an array of GeoJSON Geometries
43625          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43626          * @param {Object} [options={}] Optional Parameters
43627          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43628          * @param {string|number} [options.id] Identifier associated with the Feature
43629          * @returns {Feature<GeometryCollection>} a GeoJSON GeometryCollection Feature
43630          * @example
43631          * var pt = turf.geometry("Point", [100, 0]);
43632          * var line = turf.geometry("LineString", [[101, 0], [102, 1]]);
43633          * var collection = turf.geometryCollection([pt, line]);
43634          *
43635          * // => collection
43636          */
43637         function geometryCollection(geometries, properties, options) {
43638             if (options === void 0) { options = {}; }
43639             var geom = {
43640                 type: "GeometryCollection",
43641                 geometries: geometries,
43642             };
43643             return feature(geom, properties, options);
43644         }
43645         exports.geometryCollection = geometryCollection;
43646         /**
43647          * Round number to precision
43648          *
43649          * @param {number} num Number
43650          * @param {number} [precision=0] Precision
43651          * @returns {number} rounded number
43652          * @example
43653          * turf.round(120.4321)
43654          * //=120
43655          *
43656          * turf.round(120.4321, 2)
43657          * //=120.43
43658          */
43659         function round(num, precision) {
43660             if (precision === void 0) { precision = 0; }
43661             if (precision && !(precision >= 0)) {
43662                 throw new Error("precision must be a positive number");
43663             }
43664             var multiplier = Math.pow(10, precision || 0);
43665             return Math.round(num * multiplier) / multiplier;
43666         }
43667         exports.round = round;
43668         /**
43669          * Convert a distance measurement (assuming a spherical Earth) from radians to a more friendly unit.
43670          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
43671          *
43672          * @name radiansToLength
43673          * @param {number} radians in radians across the sphere
43674          * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
43675          * meters, kilometres, kilometers.
43676          * @returns {number} distance
43677          */
43678         function radiansToLength(radians, units) {
43679             if (units === void 0) { units = "kilometers"; }
43680             var factor = exports.factors[units];
43681             if (!factor) {
43682                 throw new Error(units + " units is invalid");
43683             }
43684             return radians * factor;
43685         }
43686         exports.radiansToLength = radiansToLength;
43687         /**
43688          * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into radians
43689          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
43690          *
43691          * @name lengthToRadians
43692          * @param {number} distance in real units
43693          * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
43694          * meters, kilometres, kilometers.
43695          * @returns {number} radians
43696          */
43697         function lengthToRadians(distance, units) {
43698             if (units === void 0) { units = "kilometers"; }
43699             var factor = exports.factors[units];
43700             if (!factor) {
43701                 throw new Error(units + " units is invalid");
43702             }
43703             return distance / factor;
43704         }
43705         exports.lengthToRadians = lengthToRadians;
43706         /**
43707          * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into degrees
43708          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, centimeters, kilometres, feet
43709          *
43710          * @name lengthToDegrees
43711          * @param {number} distance in real units
43712          * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
43713          * meters, kilometres, kilometers.
43714          * @returns {number} degrees
43715          */
43716         function lengthToDegrees(distance, units) {
43717             return radiansToDegrees(lengthToRadians(distance, units));
43718         }
43719         exports.lengthToDegrees = lengthToDegrees;
43720         /**
43721          * Converts any bearing angle from the north line direction (positive clockwise)
43722          * and returns an angle between 0-360 degrees (positive clockwise), 0 being the north line
43723          *
43724          * @name bearingToAzimuth
43725          * @param {number} bearing angle, between -180 and +180 degrees
43726          * @returns {number} angle between 0 and 360 degrees
43727          */
43728         function bearingToAzimuth(bearing) {
43729             var angle = bearing % 360;
43730             if (angle < 0) {
43731                 angle += 360;
43732             }
43733             return angle;
43734         }
43735         exports.bearingToAzimuth = bearingToAzimuth;
43736         /**
43737          * Converts an angle in radians to degrees
43738          *
43739          * @name radiansToDegrees
43740          * @param {number} radians angle in radians
43741          * @returns {number} degrees between 0 and 360 degrees
43742          */
43743         function radiansToDegrees(radians) {
43744             var degrees = radians % (2 * Math.PI);
43745             return degrees * 180 / Math.PI;
43746         }
43747         exports.radiansToDegrees = radiansToDegrees;
43748         /**
43749          * Converts an angle in degrees to radians
43750          *
43751          * @name degreesToRadians
43752          * @param {number} degrees angle between 0 and 360 degrees
43753          * @returns {number} angle in radians
43754          */
43755         function degreesToRadians(degrees) {
43756             var radians = degrees % 360;
43757             return radians * Math.PI / 180;
43758         }
43759         exports.degreesToRadians = degreesToRadians;
43760         /**
43761          * Converts a length to the requested unit.
43762          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
43763          *
43764          * @param {number} length to be converted
43765          * @param {Units} [originalUnit="kilometers"] of the length
43766          * @param {Units} [finalUnit="kilometers"] returned unit
43767          * @returns {number} the converted length
43768          */
43769         function convertLength(length, originalUnit, finalUnit) {
43770             if (originalUnit === void 0) { originalUnit = "kilometers"; }
43771             if (finalUnit === void 0) { finalUnit = "kilometers"; }
43772             if (!(length >= 0)) {
43773                 throw new Error("length must be a positive number");
43774             }
43775             return radiansToLength(lengthToRadians(length, originalUnit), finalUnit);
43776         }
43777         exports.convertLength = convertLength;
43778         /**
43779          * Converts a area to the requested unit.
43780          * Valid units: kilometers, kilometres, meters, metres, centimetres, millimeters, acres, miles, yards, feet, inches
43781          * @param {number} area to be converted
43782          * @param {Units} [originalUnit="meters"] of the distance
43783          * @param {Units} [finalUnit="kilometers"] returned unit
43784          * @returns {number} the converted distance
43785          */
43786         function convertArea(area, originalUnit, finalUnit) {
43787             if (originalUnit === void 0) { originalUnit = "meters"; }
43788             if (finalUnit === void 0) { finalUnit = "kilometers"; }
43789             if (!(area >= 0)) {
43790                 throw new Error("area must be a positive number");
43791             }
43792             var startFactor = exports.areaFactors[originalUnit];
43793             if (!startFactor) {
43794                 throw new Error("invalid original units");
43795             }
43796             var finalFactor = exports.areaFactors[finalUnit];
43797             if (!finalFactor) {
43798                 throw new Error("invalid final units");
43799             }
43800             return (area / startFactor) * finalFactor;
43801         }
43802         exports.convertArea = convertArea;
43803         /**
43804          * isNumber
43805          *
43806          * @param {*} num Number to validate
43807          * @returns {boolean} true/false
43808          * @example
43809          * turf.isNumber(123)
43810          * //=true
43811          * turf.isNumber('foo')
43812          * //=false
43813          */
43814         function isNumber(num) {
43815             return !isNaN(num) && num !== null && !Array.isArray(num) && !/^\s*$/.test(num);
43816         }
43817         exports.isNumber = isNumber;
43818         /**
43819          * isObject
43820          *
43821          * @param {*} input variable to validate
43822          * @returns {boolean} true/false
43823          * @example
43824          * turf.isObject({elevation: 10})
43825          * //=true
43826          * turf.isObject('foo')
43827          * //=false
43828          */
43829         function isObject(input) {
43830             return (!!input) && (input.constructor === Object);
43831         }
43832         exports.isObject = isObject;
43833         /**
43834          * Validate BBox
43835          *
43836          * @private
43837          * @param {Array<number>} bbox BBox to validate
43838          * @returns {void}
43839          * @throws Error if BBox is not valid
43840          * @example
43841          * validateBBox([-180, -40, 110, 50])
43842          * //=OK
43843          * validateBBox([-180, -40])
43844          * //=Error
43845          * validateBBox('Foo')
43846          * //=Error
43847          * validateBBox(5)
43848          * //=Error
43849          * validateBBox(null)
43850          * //=Error
43851          * validateBBox(undefined)
43852          * //=Error
43853          */
43854         function validateBBox(bbox) {
43855             if (!bbox) {
43856                 throw new Error("bbox is required");
43857             }
43858             if (!Array.isArray(bbox)) {
43859                 throw new Error("bbox must be an Array");
43860             }
43861             if (bbox.length !== 4 && bbox.length !== 6) {
43862                 throw new Error("bbox must be an Array of 4 or 6 numbers");
43863             }
43864             bbox.forEach(function (num) {
43865                 if (!isNumber(num)) {
43866                     throw new Error("bbox must only contain numbers");
43867                 }
43868             });
43869         }
43870         exports.validateBBox = validateBBox;
43871         /**
43872          * Validate Id
43873          *
43874          * @private
43875          * @param {string|number} id Id to validate
43876          * @returns {void}
43877          * @throws Error if Id is not valid
43878          * @example
43879          * validateId([-180, -40, 110, 50])
43880          * //=Error
43881          * validateId([-180, -40])
43882          * //=Error
43883          * validateId('Foo')
43884          * //=OK
43885          * validateId(5)
43886          * //=OK
43887          * validateId(null)
43888          * //=Error
43889          * validateId(undefined)
43890          * //=Error
43891          */
43892         function validateId(id) {
43893             if (!id) {
43894                 throw new Error("id is required");
43895             }
43896             if (["string", "number"].indexOf(typeof id) === -1) {
43897                 throw new Error("id must be a number or a string");
43898             }
43899         }
43900         exports.validateId = validateId;
43901         // Deprecated methods
43902         function radians2degrees() {
43903             throw new Error("method has been renamed to `radiansToDegrees`");
43904         }
43905         exports.radians2degrees = radians2degrees;
43906         function degrees2radians() {
43907             throw new Error("method has been renamed to `degreesToRadians`");
43908         }
43909         exports.degrees2radians = degrees2radians;
43910         function distanceToDegrees() {
43911             throw new Error("method has been renamed to `lengthToDegrees`");
43912         }
43913         exports.distanceToDegrees = distanceToDegrees;
43914         function distanceToRadians() {
43915             throw new Error("method has been renamed to `lengthToRadians`");
43916         }
43917         exports.distanceToRadians = distanceToRadians;
43918         function radiansToDistance() {
43919             throw new Error("method has been renamed to `radiansToLength`");
43920         }
43921         exports.radiansToDistance = radiansToDistance;
43922         function bearingToAngle() {
43923             throw new Error("method has been renamed to `bearingToAzimuth`");
43924         }
43925         exports.bearingToAngle = bearingToAngle;
43926         function convertDistance() {
43927             throw new Error("method has been renamed to `convertLength`");
43928         }
43929         exports.convertDistance = convertDistance;
43930         });
43931
43932         var invariant = createCommonjsModule(function (module, exports) {
43933         Object.defineProperty(exports, "__esModule", { value: true });
43934
43935         /**
43936          * Unwrap a coordinate from a Point Feature, Geometry or a single coordinate.
43937          *
43938          * @name getCoord
43939          * @param {Array<number>|Geometry<Point>|Feature<Point>} coord GeoJSON Point or an Array of numbers
43940          * @returns {Array<number>} coordinates
43941          * @example
43942          * var pt = turf.point([10, 10]);
43943          *
43944          * var coord = turf.getCoord(pt);
43945          * //= [10, 10]
43946          */
43947         function getCoord(coord) {
43948             if (!coord) {
43949                 throw new Error("coord is required");
43950             }
43951             if (!Array.isArray(coord)) {
43952                 if (coord.type === "Feature" && coord.geometry !== null && coord.geometry.type === "Point") {
43953                     return coord.geometry.coordinates;
43954                 }
43955                 if (coord.type === "Point") {
43956                     return coord.coordinates;
43957                 }
43958             }
43959             if (Array.isArray(coord) && coord.length >= 2 && !Array.isArray(coord[0]) && !Array.isArray(coord[1])) {
43960                 return coord;
43961             }
43962             throw new Error("coord must be GeoJSON Point or an Array of numbers");
43963         }
43964         exports.getCoord = getCoord;
43965         /**
43966          * Unwrap coordinates from a Feature, Geometry Object or an Array
43967          *
43968          * @name getCoords
43969          * @param {Array<any>|Geometry|Feature} coords Feature, Geometry Object or an Array
43970          * @returns {Array<any>} coordinates
43971          * @example
43972          * var poly = turf.polygon([[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]]);
43973          *
43974          * var coords = turf.getCoords(poly);
43975          * //= [[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]]
43976          */
43977         function getCoords(coords) {
43978             if (Array.isArray(coords)) {
43979                 return coords;
43980             }
43981             // Feature
43982             if (coords.type === "Feature") {
43983                 if (coords.geometry !== null) {
43984                     return coords.geometry.coordinates;
43985                 }
43986             }
43987             else {
43988                 // Geometry
43989                 if (coords.coordinates) {
43990                     return coords.coordinates;
43991                 }
43992             }
43993             throw new Error("coords must be GeoJSON Feature, Geometry Object or an Array");
43994         }
43995         exports.getCoords = getCoords;
43996         /**
43997          * Checks if coordinates contains a number
43998          *
43999          * @name containsNumber
44000          * @param {Array<any>} coordinates GeoJSON Coordinates
44001          * @returns {boolean} true if Array contains a number
44002          */
44003         function containsNumber(coordinates) {
44004             if (coordinates.length > 1 && helpers$1.isNumber(coordinates[0]) && helpers$1.isNumber(coordinates[1])) {
44005                 return true;
44006             }
44007             if (Array.isArray(coordinates[0]) && coordinates[0].length) {
44008                 return containsNumber(coordinates[0]);
44009             }
44010             throw new Error("coordinates must only contain numbers");
44011         }
44012         exports.containsNumber = containsNumber;
44013         /**
44014          * Enforce expectations about types of GeoJSON objects for Turf.
44015          *
44016          * @name geojsonType
44017          * @param {GeoJSON} value any GeoJSON object
44018          * @param {string} type expected GeoJSON type
44019          * @param {string} name name of calling function
44020          * @throws {Error} if value is not the expected type.
44021          */
44022         function geojsonType(value, type, name) {
44023             if (!type || !name) {
44024                 throw new Error("type and name required");
44025             }
44026             if (!value || value.type !== type) {
44027                 throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + value.type);
44028             }
44029         }
44030         exports.geojsonType = geojsonType;
44031         /**
44032          * Enforce expectations about types of {@link Feature} inputs for Turf.
44033          * Internally this uses {@link geojsonType} to judge geometry types.
44034          *
44035          * @name featureOf
44036          * @param {Feature} feature a feature with an expected geometry type
44037          * @param {string} type expected GeoJSON type
44038          * @param {string} name name of calling function
44039          * @throws {Error} error if value is not the expected type.
44040          */
44041         function featureOf(feature, type, name) {
44042             if (!feature) {
44043                 throw new Error("No feature passed");
44044             }
44045             if (!name) {
44046                 throw new Error(".featureOf() requires a name");
44047             }
44048             if (!feature || feature.type !== "Feature" || !feature.geometry) {
44049                 throw new Error("Invalid input to " + name + ", Feature with geometry required");
44050             }
44051             if (!feature.geometry || feature.geometry.type !== type) {
44052                 throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type);
44053             }
44054         }
44055         exports.featureOf = featureOf;
44056         /**
44057          * Enforce expectations about types of {@link FeatureCollection} inputs for Turf.
44058          * Internally this uses {@link geojsonType} to judge geometry types.
44059          *
44060          * @name collectionOf
44061          * @param {FeatureCollection} featureCollection a FeatureCollection for which features will be judged
44062          * @param {string} type expected GeoJSON type
44063          * @param {string} name name of calling function
44064          * @throws {Error} if value is not the expected type.
44065          */
44066         function collectionOf(featureCollection, type, name) {
44067             if (!featureCollection) {
44068                 throw new Error("No featureCollection passed");
44069             }
44070             if (!name) {
44071                 throw new Error(".collectionOf() requires a name");
44072             }
44073             if (!featureCollection || featureCollection.type !== "FeatureCollection") {
44074                 throw new Error("Invalid input to " + name + ", FeatureCollection required");
44075             }
44076             for (var _i = 0, _a = featureCollection.features; _i < _a.length; _i++) {
44077                 var feature = _a[_i];
44078                 if (!feature || feature.type !== "Feature" || !feature.geometry) {
44079                     throw new Error("Invalid input to " + name + ", Feature with geometry required");
44080                 }
44081                 if (!feature.geometry || feature.geometry.type !== type) {
44082                     throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type);
44083                 }
44084             }
44085         }
44086         exports.collectionOf = collectionOf;
44087         /**
44088          * Get Geometry from Feature or Geometry Object
44089          *
44090          * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object
44091          * @returns {Geometry|null} GeoJSON Geometry Object
44092          * @throws {Error} if geojson is not a Feature or Geometry Object
44093          * @example
44094          * var point = {
44095          *   "type": "Feature",
44096          *   "properties": {},
44097          *   "geometry": {
44098          *     "type": "Point",
44099          *     "coordinates": [110, 40]
44100          *   }
44101          * }
44102          * var geom = turf.getGeom(point)
44103          * //={"type": "Point", "coordinates": [110, 40]}
44104          */
44105         function getGeom(geojson) {
44106             if (geojson.type === "Feature") {
44107                 return geojson.geometry;
44108             }
44109             return geojson;
44110         }
44111         exports.getGeom = getGeom;
44112         /**
44113          * Get GeoJSON object's type, Geometry type is prioritize.
44114          *
44115          * @param {GeoJSON} geojson GeoJSON object
44116          * @param {string} [name="geojson"] name of the variable to display in error message
44117          * @returns {string} GeoJSON type
44118          * @example
44119          * var point = {
44120          *   "type": "Feature",
44121          *   "properties": {},
44122          *   "geometry": {
44123          *     "type": "Point",
44124          *     "coordinates": [110, 40]
44125          *   }
44126          * }
44127          * var geom = turf.getType(point)
44128          * //="Point"
44129          */
44130         function getType(geojson, name) {
44131             if (geojson.type === "FeatureCollection") {
44132                 return "FeatureCollection";
44133             }
44134             if (geojson.type === "GeometryCollection") {
44135                 return "GeometryCollection";
44136             }
44137             if (geojson.type === "Feature" && geojson.geometry !== null) {
44138                 return geojson.geometry.type;
44139             }
44140             return geojson.type;
44141         }
44142         exports.getType = getType;
44143         });
44144
44145         var lineclip_1 = lineclip;
44146         var _default = lineclip;
44147
44148         lineclip.polyline = lineclip;
44149         lineclip.polygon = polygonclip;
44150
44151
44152         // Cohen-Sutherland line clippign algorithm, adapted to efficiently
44153         // handle polylines rather than just segments
44154
44155         function lineclip(points, bbox, result) {
44156
44157             var len = points.length,
44158                 codeA = bitCode(points[0], bbox),
44159                 part = [],
44160                 i, a, b, codeB, lastCode;
44161
44162             if (!result) { result = []; }
44163
44164             for (i = 1; i < len; i++) {
44165                 a = points[i - 1];
44166                 b = points[i];
44167                 codeB = lastCode = bitCode(b, bbox);
44168
44169                 while (true) {
44170
44171                     if (!(codeA | codeB)) { // accept
44172                         part.push(a);
44173
44174                         if (codeB !== lastCode) { // segment went outside
44175                             part.push(b);
44176
44177                             if (i < len - 1) { // start a new line
44178                                 result.push(part);
44179                                 part = [];
44180                             }
44181                         } else if (i === len - 1) {
44182                             part.push(b);
44183                         }
44184                         break;
44185
44186                     } else if (codeA & codeB) { // trivial reject
44187                         break;
44188
44189                     } else if (codeA) { // a outside, intersect with clip edge
44190                         a = intersect(a, b, codeA, bbox);
44191                         codeA = bitCode(a, bbox);
44192
44193                     } else { // b outside
44194                         b = intersect(a, b, codeB, bbox);
44195                         codeB = bitCode(b, bbox);
44196                     }
44197                 }
44198
44199                 codeA = lastCode;
44200             }
44201
44202             if (part.length) { result.push(part); }
44203
44204             return result;
44205         }
44206
44207         // Sutherland-Hodgeman polygon clipping algorithm
44208
44209         function polygonclip(points, bbox) {
44210
44211             var result, edge, prev, prevInside, i, p, inside;
44212
44213             // clip against each side of the clip rectangle
44214             for (edge = 1; edge <= 8; edge *= 2) {
44215                 result = [];
44216                 prev = points[points.length - 1];
44217                 prevInside = !(bitCode(prev, bbox) & edge);
44218
44219                 for (i = 0; i < points.length; i++) {
44220                     p = points[i];
44221                     inside = !(bitCode(p, bbox) & edge);
44222
44223                     // if segment goes through the clip window, add an intersection
44224                     if (inside !== prevInside) { result.push(intersect(prev, p, edge, bbox)); }
44225
44226                     if (inside) { result.push(p); } // add a point if it's inside
44227
44228                     prev = p;
44229                     prevInside = inside;
44230                 }
44231
44232                 points = result;
44233
44234                 if (!points.length) { break; }
44235             }
44236
44237             return result;
44238         }
44239
44240         // intersect a segment against one of the 4 lines that make up the bbox
44241
44242         function intersect(a, b, edge, bbox) {
44243             return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
44244                    edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
44245                    edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
44246                    edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
44247                    null;
44248         }
44249
44250         // bit code reflects the point position relative to the bbox:
44251
44252         //         left  mid  right
44253         //    top  1001  1000  1010
44254         //    mid  0001  0000  0010
44255         // bottom  0101  0100  0110
44256
44257         function bitCode(p, bbox) {
44258             var code = 0;
44259
44260             if (p[0] < bbox[0]) { code |= 1; } // left
44261             else if (p[0] > bbox[2]) { code |= 2; } // right
44262
44263             if (p[1] < bbox[1]) { code |= 4; } // bottom
44264             else if (p[1] > bbox[3]) { code |= 8; } // top
44265
44266             return code;
44267         }
44268         lineclip_1.default = _default;
44269
44270         var bboxClip_1 = createCommonjsModule(function (module, exports) {
44271         var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) {
44272             if (mod && mod.__esModule) { return mod; }
44273             var result = {};
44274             if (mod != null) { for (var k in mod) { if (Object.hasOwnProperty.call(mod, k)) { result[k] = mod[k]; } } }
44275             result["default"] = mod;
44276             return result;
44277         };
44278         Object.defineProperty(exports, "__esModule", { value: true });
44279
44280
44281         var lineclip = __importStar(lineclip_1);
44282         /**
44283          * Takes a {@link Feature} and a bbox and clips the feature to the bbox using
44284          * [lineclip](https://github.com/mapbox/lineclip).
44285          * May result in degenerate edges when clipping Polygons.
44286          *
44287          * @name bboxClip
44288          * @param {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} feature feature to clip to the bbox
44289          * @param {BBox} bbox extent in [minX, minY, maxX, maxY] order
44290          * @returns {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} clipped Feature
44291          * @example
44292          * var bbox = [0, 0, 10, 10];
44293          * var poly = turf.polygon([[[2, 2], [8, 4], [12, 8], [3, 7], [2, 2]]]);
44294          *
44295          * var clipped = turf.bboxClip(poly, bbox);
44296          *
44297          * //addToMap
44298          * var addToMap = [bbox, poly, clipped]
44299          */
44300         function bboxClip(feature, bbox) {
44301             var geom = invariant.getGeom(feature);
44302             var type = geom.type;
44303             var properties = feature.type === "Feature" ? feature.properties : {};
44304             var coords = geom.coordinates;
44305             switch (type) {
44306                 case "LineString":
44307                 case "MultiLineString":
44308                     var lines_1 = [];
44309                     if (type === "LineString") {
44310                         coords = [coords];
44311                     }
44312                     coords.forEach(function (line) {
44313                         lineclip.polyline(line, bbox, lines_1);
44314                     });
44315                     if (lines_1.length === 1) {
44316                         return helpers$1.lineString(lines_1[0], properties);
44317                     }
44318                     return helpers$1.multiLineString(lines_1, properties);
44319                 case "Polygon":
44320                     return helpers$1.polygon(clipPolygon(coords, bbox), properties);
44321                 case "MultiPolygon":
44322                     return helpers$1.multiPolygon(coords.map(function (poly) {
44323                         return clipPolygon(poly, bbox);
44324                     }), properties);
44325                 default:
44326                     throw new Error("geometry " + type + " not supported");
44327             }
44328         }
44329         exports.default = bboxClip;
44330         function clipPolygon(rings, bbox) {
44331             var outRings = [];
44332             for (var _i = 0, rings_1 = rings; _i < rings_1.length; _i++) {
44333                 var ring = rings_1[_i];
44334                 var clipped = lineclip.polygon(ring, bbox);
44335                 if (clipped.length > 0) {
44336                     if (clipped[0][0] !== clipped[clipped.length - 1][0] || clipped[0][1] !== clipped[clipped.length - 1][1]) {
44337                         clipped.push(clipped[0]);
44338                     }
44339                     if (clipped.length >= 4) {
44340                         outRings.push(clipped);
44341                     }
44342                 }
44343             }
44344             return outRings;
44345         }
44346         });
44347
44348         var fastJsonStableStringify = function (data, opts) {
44349             if (!opts) { opts = {}; }
44350             if (typeof opts === 'function') { opts = { cmp: opts }; }
44351             var cycles = (typeof opts.cycles === 'boolean') ? opts.cycles : false;
44352
44353             var cmp = opts.cmp && (function (f) {
44354                 return function (node) {
44355                     return function (a, b) {
44356                         var aobj = { key: a, value: node[a] };
44357                         var bobj = { key: b, value: node[b] };
44358                         return f(aobj, bobj);
44359                     };
44360                 };
44361             })(opts.cmp);
44362
44363             var seen = [];
44364             return (function stringify (node) {
44365                 if (node && node.toJSON && typeof node.toJSON === 'function') {
44366                     node = node.toJSON();
44367                 }
44368
44369                 if (node === undefined) { return; }
44370                 if (typeof node == 'number') { return isFinite(node) ? '' + node : 'null'; }
44371                 if (typeof node !== 'object') { return JSON.stringify(node); }
44372
44373                 var i, out;
44374                 if (Array.isArray(node)) {
44375                     out = '[';
44376                     for (i = 0; i < node.length; i++) {
44377                         if (i) { out += ','; }
44378                         out += stringify(node[i]) || 'null';
44379                     }
44380                     return out + ']';
44381                 }
44382
44383                 if (node === null) { return 'null'; }
44384
44385                 if (seen.indexOf(node) !== -1) {
44386                     if (cycles) { return JSON.stringify('__cycle__'); }
44387                     throw new TypeError('Converting circular structure to JSON');
44388                 }
44389
44390                 var seenIndex = seen.push(node) - 1;
44391                 var keys = Object.keys(node).sort(cmp && cmp(node));
44392                 out = '';
44393                 for (i = 0; i < keys.length; i++) {
44394                     var key = keys[i];
44395                     var value = stringify(node[key]);
44396
44397                     if (!value) { continue; }
44398                     if (out) { out += ','; }
44399                     out += JSON.stringify(key) + ':' + value;
44400                 }
44401                 seen.splice(seenIndex, 1);
44402                 return '{' + out + '}';
44403             })(data);
44404         };
44405
44406         function DEFAULT_COMPARE (a, b) { return a > b ? 1 : a < b ? -1 : 0; }
44407
44408         var SplayTree = function SplayTree(compare, noDuplicates) {
44409           if ( compare === void 0 ) compare = DEFAULT_COMPARE;
44410           if ( noDuplicates === void 0 ) noDuplicates = false;
44411
44412           this._compare = compare;
44413           this._root = null;
44414           this._size = 0;
44415           this._noDuplicates = !!noDuplicates;
44416         };
44417
44418         var prototypeAccessors = { size: { configurable: true } };
44419
44420
44421         SplayTree.prototype.rotateLeft = function rotateLeft (x) {
44422           var y = x.right;
44423           if (y) {
44424             x.right = y.left;
44425             if (y.left) { y.left.parent = x; }
44426             y.parent = x.parent;
44427           }
44428
44429           if (!x.parent)              { this._root = y; }
44430           else if (x === x.parent.left) { x.parent.left = y; }
44431           else                        { x.parent.right = y; }
44432           if (y) { y.left = x; }
44433           x.parent = y;
44434         };
44435
44436
44437         SplayTree.prototype.rotateRight = function rotateRight (x) {
44438           var y = x.left;
44439           if (y) {
44440             x.left = y.right;
44441             if (y.right) { y.right.parent = x; }
44442             y.parent = x.parent;
44443           }
44444
44445           if (!x.parent)             { this._root = y; }
44446           else if(x === x.parent.left) { x.parent.left = y; }
44447           else                       { x.parent.right = y; }
44448           if (y) { y.right = x; }
44449           x.parent = y;
44450         };
44451
44452
44453         SplayTree.prototype._splay = function _splay (x) {
44454           while (x.parent) {
44455             var p = x.parent;
44456             if (!p.parent) {
44457               if (p.left === x) { this.rotateRight(p); }
44458               else            { this.rotateLeft(p); }
44459             } else if (p.left === x && p.parent.left === p) {
44460               this.rotateRight(p.parent);
44461               this.rotateRight(p);
44462             } else if (p.right === x && p.parent.right === p) {
44463               this.rotateLeft(p.parent);
44464               this.rotateLeft(p);
44465             } else if (p.left === x && p.parent.right === p) {
44466               this.rotateRight(p);
44467               this.rotateLeft(p);
44468             } else {
44469               this.rotateLeft(p);
44470               this.rotateRight(p);
44471             }
44472           }
44473         };
44474
44475
44476         SplayTree.prototype.splay = function splay (x) {
44477           var p, gp, ggp, l, r;
44478
44479           while (x.parent) {
44480             p = x.parent;
44481             gp = p.parent;
44482
44483             if (gp && gp.parent) {
44484               ggp = gp.parent;
44485               if (ggp.left === gp) { ggp.left= x; }
44486               else               { ggp.right = x; }
44487               x.parent = ggp;
44488             } else {
44489               x.parent = null;
44490               this._root = x;
44491             }
44492
44493             l = x.left; r = x.right;
44494
44495             if (x === p.left) { // left
44496               if (gp) {
44497                 if (gp.left === p) {
44498                   /* zig-zig */
44499                   if (p.right) {
44500                     gp.left = p.right;
44501                     gp.left.parent = gp;
44502                   } else { gp.left = null; }
44503
44504                   p.right = gp;
44505                   gp.parent = p;
44506                 } else {
44507                   /* zig-zag */
44508                   if (l) {
44509                     gp.right = l;
44510                     l.parent = gp;
44511                   } else { gp.right = null; }
44512
44513                   x.left  = gp;
44514                   gp.parent = x;
44515                 }
44516               }
44517               if (r) {
44518                 p.left = r;
44519                 r.parent = p;
44520               } else { p.left = null; }
44521
44522               x.right= p;
44523               p.parent = x;
44524             } else { // right
44525               if (gp) {
44526                 if (gp.right === p) {
44527                   /* zig-zig */
44528                   if (p.left) {
44529                     gp.right = p.left;
44530                     gp.right.parent = gp;
44531                   } else { gp.right = null; }
44532
44533                   p.left = gp;
44534                   gp.parent = p;
44535                 } else {
44536                   /* zig-zag */
44537                   if (r) {
44538                     gp.left = r;
44539                     r.parent = gp;
44540                   } else { gp.left = null; }
44541
44542                   x.right = gp;
44543                   gp.parent = x;
44544                 }
44545               }
44546               if (l) {
44547                 p.right = l;
44548                 l.parent = p;
44549               } else { p.right = null; }
44550
44551               x.left = p;
44552               p.parent = x;
44553             }
44554           }
44555         };
44556
44557
44558         SplayTree.prototype.replace = function replace (u, v) {
44559           if (!u.parent) { this._root = v; }
44560           else if (u === u.parent.left) { u.parent.left = v; }
44561           else { u.parent.right = v; }
44562           if (v) { v.parent = u.parent; }
44563         };
44564
44565
44566         SplayTree.prototype.minNode = function minNode (u) {
44567             if ( u === void 0 ) u = this._root;
44568
44569           if (u) { while (u.left) { u = u.left; } }
44570           return u;
44571         };
44572
44573
44574         SplayTree.prototype.maxNode = function maxNode (u) {
44575             if ( u === void 0 ) u = this._root;
44576
44577           if (u) { while (u.right) { u = u.right; } }
44578           return u;
44579         };
44580
44581
44582         SplayTree.prototype.insert = function insert (key, data) {
44583           var z = this._root;
44584           var p = null;
44585           var comp = this._compare;
44586           var cmp;
44587
44588           if (this._noDuplicates) {
44589             while (z) {
44590               p = z;
44591               cmp = comp(z.key, key);
44592               if (cmp === 0) { return; }
44593               else if (comp(z.key, key) < 0) { z = z.right; }
44594               else { z = z.left; }
44595             }
44596           } else {
44597             while (z) {
44598               p = z;
44599               if (comp(z.key, key) < 0) { z = z.right; }
44600               else { z = z.left; }
44601             }
44602           }
44603
44604           z = { key: key, data: data, left: null, right: null, parent: p };
44605
44606           if (!p)                        { this._root = z; }
44607           else if (comp(p.key, z.key) < 0) { p.right = z; }
44608           else                           { p.left= z; }
44609
44610           this.splay(z);
44611           this._size++;
44612           return z;
44613         };
44614
44615
44616         SplayTree.prototype.find = function find (key) {
44617           var z  = this._root;
44618           var comp = this._compare;
44619           while (z) {
44620             var cmp = comp(z.key, key);
44621             if    (cmp < 0) { z = z.right; }
44622             else if (cmp > 0) { z = z.left; }
44623             else            { return z; }
44624           }
44625           return null;
44626         };
44627
44628         /**
44629          * Whether the tree contains a node with the given key
44630          * @param{Key} key
44631          * @return {boolean} true/false
44632          */
44633         SplayTree.prototype.contains = function contains (key) {
44634           var node     = this._root;
44635           var comparator = this._compare;
44636           while (node){
44637             var cmp = comparator(key, node.key);
44638             if    (cmp === 0) { return true; }
44639             else if (cmp < 0) { node = node.left; }
44640             else              { node = node.right; }
44641           }
44642
44643           return false;
44644         };
44645
44646
44647         SplayTree.prototype.remove = function remove (key) {
44648           var z = this.find(key);
44649
44650           if (!z) { return false; }
44651
44652           this.splay(z);
44653
44654           if (!z.left) { this.replace(z, z.right); }
44655           else if (!z.right) { this.replace(z, z.left); }
44656           else {
44657             var y = this.minNode(z.right);
44658             if (y.parent !== z) {
44659               this.replace(y, y.right);
44660               y.right = z.right;
44661               y.right.parent = y;
44662             }
44663             this.replace(z, y);
44664             y.left = z.left;
44665             y.left.parent = y;
44666           }
44667
44668           this._size--;
44669           return true;
44670         };
44671
44672
44673         SplayTree.prototype.removeNode = function removeNode (z) {
44674           if (!z) { return false; }
44675
44676           this.splay(z);
44677
44678           if (!z.left) { this.replace(z, z.right); }
44679           else if (!z.right) { this.replace(z, z.left); }
44680           else {
44681             var y = this.minNode(z.right);
44682             if (y.parent !== z) {
44683               this.replace(y, y.right);
44684               y.right = z.right;
44685               y.right.parent = y;
44686             }
44687             this.replace(z, y);
44688             y.left = z.left;
44689             y.left.parent = y;
44690           }
44691
44692           this._size--;
44693           return true;
44694         };
44695
44696
44697         SplayTree.prototype.erase = function erase (key) {
44698           var z = this.find(key);
44699           if (!z) { return; }
44700
44701           this.splay(z);
44702
44703           var s = z.left;
44704           var t = z.right;
44705
44706           var sMax = null;
44707           if (s) {
44708             s.parent = null;
44709             sMax = this.maxNode(s);
44710             this.splay(sMax);
44711             this._root = sMax;
44712           }
44713           if (t) {
44714             if (s) { sMax.right = t; }
44715             else { this._root = t; }
44716             t.parent = sMax;
44717           }
44718
44719           this._size--;
44720         };
44721
44722         /**
44723          * Removes and returns the node with smallest key
44724          * @return {?Node}
44725          */
44726         SplayTree.prototype.pop = function pop () {
44727           var node = this._root, returnValue = null;
44728           if (node) {
44729             while (node.left) { node = node.left; }
44730             returnValue = { key: node.key, data: node.data };
44731             this.remove(node.key);
44732           }
44733           return returnValue;
44734         };
44735
44736
44737         /* eslint-disable class-methods-use-this */
44738
44739         /**
44740          * Successor node
44741          * @param{Node} node
44742          * @return {?Node}
44743          */
44744         SplayTree.prototype.next = function next (node) {
44745           var successor = node;
44746           if (successor) {
44747             if (successor.right) {
44748               successor = successor.right;
44749               while (successor && successor.left) { successor = successor.left; }
44750             } else {
44751               successor = node.parent;
44752               while (successor && successor.right === node) {
44753                 node = successor; successor = successor.parent;
44754               }
44755             }
44756           }
44757           return successor;
44758         };
44759
44760
44761         /**
44762          * Predecessor node
44763          * @param{Node} node
44764          * @return {?Node}
44765          */
44766         SplayTree.prototype.prev = function prev (node) {
44767           var predecessor = node;
44768           if (predecessor) {
44769             if (predecessor.left) {
44770               predecessor = predecessor.left;
44771               while (predecessor && predecessor.right) { predecessor = predecessor.right; }
44772             } else {
44773               predecessor = node.parent;
44774               while (predecessor && predecessor.left === node) {
44775                 node = predecessor;
44776                 predecessor = predecessor.parent;
44777               }
44778             }
44779           }
44780           return predecessor;
44781         };
44782         /* eslint-enable class-methods-use-this */
44783
44784
44785         /**
44786          * @param{forEachCallback} callback
44787          * @return {SplayTree}
44788          */
44789         SplayTree.prototype.forEach = function forEach (callback) {
44790           var current = this._root;
44791           var s = [], done = false, i = 0;
44792
44793           while (!done) {
44794             // Reach the left most Node of the current Node
44795             if (current) {
44796               // Place pointer to a tree node on the stack
44797               // before traversing the node's left subtree
44798               s.push(current);
44799               current = current.left;
44800             } else {
44801               // BackTrack from the empty subtree and visit the Node
44802               // at the top of the stack; however, if the stack is
44803               // empty you are done
44804               if (s.length > 0) {
44805                 current = s.pop();
44806                 callback(current, i++);
44807
44808                 // We have visited the node and its left
44809                 // subtree. Now, it's right subtree's turn
44810                 current = current.right;
44811               } else { done = true; }
44812             }
44813           }
44814           return this;
44815         };
44816
44817
44818         /**
44819          * Walk key range from `low` to `high`. Stops if `fn` returns a value.
44820          * @param{Key}    low
44821          * @param{Key}    high
44822          * @param{Function} fn
44823          * @param{*?}     ctx
44824          * @return {SplayTree}
44825          */
44826         SplayTree.prototype.range = function range (low, high, fn, ctx) {
44827           var Q = [];
44828           var compare = this._compare;
44829           var node = this._root, cmp;
44830
44831           while (Q.length !== 0 || node) {
44832             if (node) {
44833               Q.push(node);
44834               node = node.left;
44835             } else {
44836               node = Q.pop();
44837               cmp = compare(node.key, high);
44838               if (cmp > 0) {
44839                 break;
44840               } else if (compare(node.key, low) >= 0) {
44841                 if (fn.call(ctx, node)) { return this; } // stop if smth is returned
44842               }
44843               node = node.right;
44844             }
44845           }
44846           return this;
44847         };
44848
44849         /**
44850          * Returns all keys in order
44851          * @return {Array<Key>}
44852          */
44853         SplayTree.prototype.keys = function keys () {
44854           var current = this._root;
44855           var s = [], r = [], done = false;
44856
44857           while (!done) {
44858             if (current) {
44859               s.push(current);
44860               current = current.left;
44861             } else {
44862               if (s.length > 0) {
44863                 current = s.pop();
44864                 r.push(current.key);
44865                 current = current.right;
44866               } else { done = true; }
44867             }
44868           }
44869           return r;
44870         };
44871
44872
44873         /**
44874          * Returns `data` fields of all nodes in order.
44875          * @return {Array<Value>}
44876          */
44877         SplayTree.prototype.values = function values () {
44878           var current = this._root;
44879           var s = [], r = [], done = false;
44880
44881           while (!done) {
44882             if (current) {
44883               s.push(current);
44884               current = current.left;
44885             } else {
44886               if (s.length > 0) {
44887                 current = s.pop();
44888                 r.push(current.data);
44889                 current = current.right;
44890               } else { done = true; }
44891             }
44892           }
44893           return r;
44894         };
44895
44896
44897         /**
44898          * Returns node at given index
44899          * @param{number} index
44900          * @return {?Node}
44901          */
44902         SplayTree.prototype.at = function at (index) {
44903           // removed after a consideration, more misleading than useful
44904           // index = index % this.size;
44905           // if (index < 0) index = this.size - index;
44906
44907           var current = this._root;
44908           var s = [], done = false, i = 0;
44909
44910           while (!done) {
44911             if (current) {
44912               s.push(current);
44913               current = current.left;
44914             } else {
44915               if (s.length > 0) {
44916                 current = s.pop();
44917                 if (i === index) { return current; }
44918                 i++;
44919                 current = current.right;
44920               } else { done = true; }
44921             }
44922           }
44923           return null;
44924         };
44925
44926         /**
44927          * Bulk-load items. Both array have to be same size
44928          * @param{Array<Key>}  keys
44929          * @param{Array<Value>}[values]
44930          * @param{Boolean}     [presort=false] Pre-sort keys and values, using
44931          *                                       tree's comparator. Sorting is done
44932          *                                       in-place
44933          * @return {AVLTree}
44934          */
44935         SplayTree.prototype.load = function load (keys, values, presort) {
44936             if ( keys === void 0 ) keys = [];
44937             if ( values === void 0 ) values = [];
44938             if ( presort === void 0 ) presort = false;
44939
44940           if (this._size !== 0) { throw new Error('bulk-load: tree is not empty'); }
44941           var size = keys.length;
44942           if (presort) { sort(keys, values, 0, size - 1, this._compare); }
44943           this._root = loadRecursive(null, keys, values, 0, size);
44944           this._size = size;
44945           return this;
44946         };
44947
44948
44949         SplayTree.prototype.min = function min () {
44950           var node = this.minNode(this._root);
44951           if (node) { return node.key; }
44952           else    { return null; }
44953         };
44954
44955
44956         SplayTree.prototype.max = function max () {
44957           var node = this.maxNode(this._root);
44958           if (node) { return node.key; }
44959           else    { return null; }
44960         };
44961
44962         SplayTree.prototype.isEmpty = function isEmpty () { return this._root === null; };
44963         prototypeAccessors.size.get = function () { return this._size; };
44964
44965
44966         /**
44967          * Create a tree and load it with items
44968          * @param{Array<Key>}        keys
44969          * @param{Array<Value>?}      [values]
44970
44971          * @param{Function?}          [comparator]
44972          * @param{Boolean?}           [presort=false] Pre-sort keys and values, using
44973          *                                             tree's comparator. Sorting is done
44974          *                                             in-place
44975          * @param{Boolean?}           [noDuplicates=false] Allow duplicates
44976          * @return {SplayTree}
44977          */
44978         SplayTree.createTree = function createTree (keys, values, comparator, presort, noDuplicates) {
44979           return new SplayTree(comparator, noDuplicates).load(keys, values, presort);
44980         };
44981
44982         Object.defineProperties( SplayTree.prototype, prototypeAccessors );
44983
44984
44985         function loadRecursive (parent, keys, values, start, end) {
44986           var size = end - start;
44987           if (size > 0) {
44988             var middle = start + Math.floor(size / 2);
44989             var key    = keys[middle];
44990             var data   = values[middle];
44991             var node   = { key: key, data: data, parent: parent };
44992             node.left    = loadRecursive(node, keys, values, start, middle);
44993             node.right   = loadRecursive(node, keys, values, middle + 1, end);
44994             return node;
44995           }
44996           return null;
44997         }
44998
44999
45000         function sort(keys, values, left, right, compare) {
45001           if (left >= right) { return; }
45002
45003           var pivot = keys[(left + right) >> 1];
45004           var i = left - 1;
45005           var j = right + 1;
45006
45007           while (true) {
45008             do { i++; } while (compare(keys[i], pivot) < 0);
45009             do { j--; } while (compare(keys[j], pivot) > 0);
45010             if (i >= j) { break; }
45011
45012             var tmp = keys[i];
45013             keys[i] = keys[j];
45014             keys[j] = tmp;
45015
45016             tmp = values[i];
45017             values[i] = values[j];
45018             values[j] = tmp;
45019           }
45020
45021           sort(keys, values,  left,     j, compare);
45022           sort(keys, values, j + 1, right, compare);
45023         }
45024
45025         var NORMAL               = 0;
45026         var NON_CONTRIBUTING     = 1;
45027         var SAME_TRANSITION      = 2;
45028         var DIFFERENT_TRANSITION = 3;
45029
45030         var INTERSECTION = 0;
45031         var UNION        = 1;
45032         var DIFFERENCE   = 2;
45033         var XOR          = 3;
45034
45035         /**
45036          * @param  {SweepEvent} event
45037          * @param  {SweepEvent} prev
45038          * @param  {Operation} operation
45039          */
45040         function computeFields (event, prev, operation) {
45041           // compute inOut and otherInOut fields
45042           if (prev === null) {
45043             event.inOut      = false;
45044             event.otherInOut = true;
45045
45046           // previous line segment in sweepline belongs to the same polygon
45047           } else {
45048             if (event.isSubject === prev.isSubject) {
45049               event.inOut      = !prev.inOut;
45050               event.otherInOut = prev.otherInOut;
45051
45052             // previous line segment in sweepline belongs to the clipping polygon
45053             } else {
45054               event.inOut      = !prev.otherInOut;
45055               event.otherInOut = prev.isVertical() ? !prev.inOut : prev.inOut;
45056             }
45057
45058             // compute prevInResult field
45059             if (prev) {
45060               event.prevInResult = (!inResult(prev, operation) || prev.isVertical())
45061                 ? prev.prevInResult : prev;
45062             }
45063           }
45064
45065           // check if the line segment belongs to the Boolean operation
45066           var isInResult = inResult(event, operation);
45067           if (isInResult) {
45068             event.resultTransition = determineResultTransition(event, operation);
45069           } else {
45070             event.resultTransition = 0;
45071           }
45072         }
45073
45074
45075         /* eslint-disable indent */
45076         function inResult(event, operation) {
45077           switch (event.type) {
45078             case NORMAL:
45079               switch (operation) {
45080                 case INTERSECTION:
45081                   return !event.otherInOut;
45082                 case UNION:
45083                   return event.otherInOut;
45084                 case DIFFERENCE:
45085                   // return (event.isSubject && !event.otherInOut) ||
45086                   //         (!event.isSubject && event.otherInOut);
45087                   return (event.isSubject && event.otherInOut) ||
45088                           (!event.isSubject && !event.otherInOut);
45089                 case XOR:
45090                   return true;
45091               }
45092               break;
45093             case SAME_TRANSITION:
45094               return operation === INTERSECTION || operation === UNION;
45095             case DIFFERENT_TRANSITION:
45096               return operation === DIFFERENCE;
45097             case NON_CONTRIBUTING:
45098               return false;
45099           }
45100           return false;
45101         }
45102         /* eslint-enable indent */
45103
45104
45105         function determineResultTransition(event, operation) {
45106           var thisIn = !event.inOut;
45107           var thatIn = !event.otherInOut;
45108
45109           var isIn;
45110           switch (operation) {
45111             case INTERSECTION:
45112               isIn = thisIn && thatIn; break;
45113             case UNION:
45114               isIn = thisIn || thatIn; break;
45115             case XOR:
45116               isIn = thisIn ^ thatIn; break;
45117             case DIFFERENCE:
45118               if (event.isSubject) {
45119                 isIn = thisIn && !thatIn;
45120               } else {
45121                 isIn = thatIn && !thisIn;
45122               }
45123               break;
45124           }
45125           return isIn ? +1 : -1;
45126         }
45127
45128         var SweepEvent = function SweepEvent (point, left, otherEvent, isSubject, edgeType) {
45129
45130           /**
45131            * Is left endpoint?
45132            * @type {Boolean}
45133            */
45134           this.left = left;
45135
45136           /**
45137            * @type {Array.<Number>}
45138            */
45139           this.point = point;
45140
45141           /**
45142            * Other edge reference
45143            * @type {SweepEvent}
45144            */
45145           this.otherEvent = otherEvent;
45146
45147           /**
45148            * Belongs to source or clipping polygon
45149            * @type {Boolean}
45150            */
45151           this.isSubject = isSubject;
45152
45153           /**
45154            * Edge contribution type
45155            * @type {Number}
45156            */
45157           this.type = edgeType || NORMAL;
45158
45159
45160           /**
45161            * In-out transition for the sweepline crossing polygon
45162            * @type {Boolean}
45163            */
45164           this.inOut = false;
45165
45166
45167           /**
45168            * @type {Boolean}
45169            */
45170           this.otherInOut = false;
45171
45172           /**
45173            * Previous event in result?
45174            * @type {SweepEvent}
45175            */
45176           this.prevInResult = null;
45177
45178           /**
45179            * Type of result transition (0 = not in result, +1 = out-in, -1, in-out)
45180            * @type {Number}
45181            */
45182           this.resultTransition = 0;
45183
45184           // connection step
45185
45186           /**
45187            * @type {Number}
45188            */
45189           this.otherPos = -1;
45190
45191           /**
45192            * @type {Number}
45193            */
45194           this.outputContourId = -1;
45195
45196           this.isExteriorRing = true; // TODO: Looks unused, remove?
45197         };
45198
45199         var prototypeAccessors$1 = { inResult: { configurable: true } };
45200
45201
45202         /**
45203          * @param{Array.<Number>}p
45204          * @return {Boolean}
45205          */
45206         SweepEvent.prototype.isBelow = function isBelow (p) {
45207           var p0 = this.point, p1 = this.otherEvent.point;
45208           return this.left
45209             ? (p0[0] - p[0]) * (p1[1] - p[1]) - (p1[0] - p[0]) * (p0[1] - p[1]) > 0
45210             // signedArea(this.point, this.otherEvent.point, p) > 0 :
45211             : (p1[0] - p[0]) * (p0[1] - p[1]) - (p0[0] - p[0]) * (p1[1] - p[1]) > 0;
45212             //signedArea(this.otherEvent.point, this.point, p) > 0;
45213         };
45214
45215
45216         /**
45217          * @param{Array.<Number>}p
45218          * @return {Boolean}
45219          */
45220         SweepEvent.prototype.isAbove = function isAbove (p) {
45221           return !this.isBelow(p);
45222         };
45223
45224
45225         /**
45226          * @return {Boolean}
45227          */
45228         SweepEvent.prototype.isVertical = function isVertical () {
45229           return this.point[0] === this.otherEvent.point[0];
45230         };
45231
45232
45233         /**
45234          * Does event belong to result?
45235          * @return {Boolean}
45236          */
45237         prototypeAccessors$1.inResult.get = function () {
45238           return this.resultTransition !== 0;
45239         };
45240
45241
45242         SweepEvent.prototype.clone = function clone () {
45243           var copy = new SweepEvent(
45244             this.point, this.left, this.otherEvent, this.isSubject, this.type);
45245
45246           copy.contourId      = this.contourId;
45247           copy.resultTransition = this.resultTransition;
45248           copy.prevInResult   = this.prevInResult;
45249           copy.isExteriorRing = this.isExteriorRing;
45250           copy.inOut          = this.inOut;
45251           copy.otherInOut     = this.otherInOut;
45252
45253           return copy;
45254         };
45255
45256         Object.defineProperties( SweepEvent.prototype, prototypeAccessors$1 );
45257
45258         function equals(p1, p2) {
45259           if (p1[0] === p2[0]) {
45260             if (p1[1] === p2[1]) {
45261               return true;
45262             } else {
45263               return false;
45264             }
45265           }
45266           return false;
45267         }
45268
45269         // const EPSILON = 1e-9;
45270         // const abs = Math.abs;
45271         // TODO https://github.com/w8r/martinez/issues/6#issuecomment-262847164
45272         // Precision problem.
45273         //
45274         // module.exports = function equals(p1, p2) {
45275         //   return abs(p1[0] - p2[0]) <= EPSILON && abs(p1[1] - p2[1]) <= EPSILON;
45276         // };
45277
45278         var epsilon$1 = 1.1102230246251565e-16;
45279         var splitter = 134217729;
45280         var resulterrbound = (3 + 8 * epsilon$1) * epsilon$1;
45281
45282         // fast_expansion_sum_zeroelim routine from oritinal code
45283         function sum$1(elen, e, flen, f, h) {
45284             var Q, Qnew, hh, bvirt;
45285             var enow = e[0];
45286             var fnow = f[0];
45287             var eindex = 0;
45288             var findex = 0;
45289             if ((fnow > enow) === (fnow > -enow)) {
45290                 Q = enow;
45291                 enow = e[++eindex];
45292             } else {
45293                 Q = fnow;
45294                 fnow = f[++findex];
45295             }
45296             var hindex = 0;
45297             if (eindex < elen && findex < flen) {
45298                 if ((fnow > enow) === (fnow > -enow)) {
45299                     Qnew = enow + Q;
45300                     hh = Q - (Qnew - enow);
45301                     enow = e[++eindex];
45302                 } else {
45303                     Qnew = fnow + Q;
45304                     hh = Q - (Qnew - fnow);
45305                     fnow = f[++findex];
45306                 }
45307                 Q = Qnew;
45308                 if (hh !== 0) {
45309                     h[hindex++] = hh;
45310                 }
45311                 while (eindex < elen && findex < flen) {
45312                     if ((fnow > enow) === (fnow > -enow)) {
45313                         Qnew = Q + enow;
45314                         bvirt = Qnew - Q;
45315                         hh = Q - (Qnew - bvirt) + (enow - bvirt);
45316                         enow = e[++eindex];
45317                     } else {
45318                         Qnew = Q + fnow;
45319                         bvirt = Qnew - Q;
45320                         hh = Q - (Qnew - bvirt) + (fnow - bvirt);
45321                         fnow = f[++findex];
45322                     }
45323                     Q = Qnew;
45324                     if (hh !== 0) {
45325                         h[hindex++] = hh;
45326                     }
45327                 }
45328             }
45329             while (eindex < elen) {
45330                 Qnew = Q + enow;
45331                 bvirt = Qnew - Q;
45332                 hh = Q - (Qnew - bvirt) + (enow - bvirt);
45333                 enow = e[++eindex];
45334                 Q = Qnew;
45335                 if (hh !== 0) {
45336                     h[hindex++] = hh;
45337                 }
45338             }
45339             while (findex < flen) {
45340                 Qnew = Q + fnow;
45341                 bvirt = Qnew - Q;
45342                 hh = Q - (Qnew - bvirt) + (fnow - bvirt);
45343                 fnow = f[++findex];
45344                 Q = Qnew;
45345                 if (hh !== 0) {
45346                     h[hindex++] = hh;
45347                 }
45348             }
45349             if (Q !== 0 || hindex === 0) {
45350                 h[hindex++] = Q;
45351             }
45352             return hindex;
45353         }
45354
45355         function estimate(elen, e) {
45356             var Q = e[0];
45357             for (var i = 1; i < elen; i++) { Q += e[i]; }
45358             return Q;
45359         }
45360
45361         function vec(n) {
45362             return new Float64Array(n);
45363         }
45364
45365         var ccwerrboundA = (3 + 16 * epsilon$1) * epsilon$1;
45366         var ccwerrboundB = (2 + 12 * epsilon$1) * epsilon$1;
45367         var ccwerrboundC = (9 + 64 * epsilon$1) * epsilon$1 * epsilon$1;
45368
45369         var B = vec(4);
45370         var C1 = vec(8);
45371         var C2 = vec(12);
45372         var D = vec(16);
45373         var u = vec(4);
45374
45375         function orient2dadapt(ax, ay, bx, by, cx, cy, detsum) {
45376             var acxtail, acytail, bcxtail, bcytail;
45377             var bvirt, c, ahi, alo, bhi, blo, _i, _j, _0, s1, s0, t1, t0, u3;
45378
45379             var acx = ax - cx;
45380             var bcx = bx - cx;
45381             var acy = ay - cy;
45382             var bcy = by - cy;
45383
45384             s1 = acx * bcy;
45385             c = splitter * acx;
45386             ahi = c - (c - acx);
45387             alo = acx - ahi;
45388             c = splitter * bcy;
45389             bhi = c - (c - bcy);
45390             blo = bcy - bhi;
45391             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45392             t1 = acy * bcx;
45393             c = splitter * acy;
45394             ahi = c - (c - acy);
45395             alo = acy - ahi;
45396             c = splitter * bcx;
45397             bhi = c - (c - bcx);
45398             blo = bcx - bhi;
45399             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45400             _i = s0 - t0;
45401             bvirt = s0 - _i;
45402             B[0] = s0 - (_i + bvirt) + (bvirt - t0);
45403             _j = s1 + _i;
45404             bvirt = _j - s1;
45405             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45406             _i = _0 - t1;
45407             bvirt = _0 - _i;
45408             B[1] = _0 - (_i + bvirt) + (bvirt - t1);
45409             u3 = _j + _i;
45410             bvirt = u3 - _j;
45411             B[2] = _j - (u3 - bvirt) + (_i - bvirt);
45412             B[3] = u3;
45413
45414             var det = estimate(4, B);
45415             var errbound = ccwerrboundB * detsum;
45416             if (det >= errbound || -det >= errbound) {
45417                 return det;
45418             }
45419
45420             bvirt = ax - acx;
45421             acxtail = ax - (acx + bvirt) + (bvirt - cx);
45422             bvirt = bx - bcx;
45423             bcxtail = bx - (bcx + bvirt) + (bvirt - cx);
45424             bvirt = ay - acy;
45425             acytail = ay - (acy + bvirt) + (bvirt - cy);
45426             bvirt = by - bcy;
45427             bcytail = by - (bcy + bvirt) + (bvirt - cy);
45428
45429             if (acxtail === 0 && acytail === 0 && bcxtail === 0 && bcytail === 0) {
45430                 return det;
45431             }
45432
45433             errbound = ccwerrboundC * detsum + resulterrbound * Math.abs(det);
45434             det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail);
45435             if (det >= errbound || -det >= errbound) { return det; }
45436
45437             s1 = acxtail * bcy;
45438             c = splitter * acxtail;
45439             ahi = c - (c - acxtail);
45440             alo = acxtail - ahi;
45441             c = splitter * bcy;
45442             bhi = c - (c - bcy);
45443             blo = bcy - bhi;
45444             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45445             t1 = acytail * bcx;
45446             c = splitter * acytail;
45447             ahi = c - (c - acytail);
45448             alo = acytail - ahi;
45449             c = splitter * bcx;
45450             bhi = c - (c - bcx);
45451             blo = bcx - bhi;
45452             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45453             _i = s0 - t0;
45454             bvirt = s0 - _i;
45455             u[0] = s0 - (_i + bvirt) + (bvirt - t0);
45456             _j = s1 + _i;
45457             bvirt = _j - s1;
45458             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45459             _i = _0 - t1;
45460             bvirt = _0 - _i;
45461             u[1] = _0 - (_i + bvirt) + (bvirt - t1);
45462             u3 = _j + _i;
45463             bvirt = u3 - _j;
45464             u[2] = _j - (u3 - bvirt) + (_i - bvirt);
45465             u[3] = u3;
45466             var C1len = sum$1(4, B, 4, u, C1);
45467
45468             s1 = acx * bcytail;
45469             c = splitter * acx;
45470             ahi = c - (c - acx);
45471             alo = acx - ahi;
45472             c = splitter * bcytail;
45473             bhi = c - (c - bcytail);
45474             blo = bcytail - bhi;
45475             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45476             t1 = acy * bcxtail;
45477             c = splitter * acy;
45478             ahi = c - (c - acy);
45479             alo = acy - ahi;
45480             c = splitter * bcxtail;
45481             bhi = c - (c - bcxtail);
45482             blo = bcxtail - bhi;
45483             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45484             _i = s0 - t0;
45485             bvirt = s0 - _i;
45486             u[0] = s0 - (_i + bvirt) + (bvirt - t0);
45487             _j = s1 + _i;
45488             bvirt = _j - s1;
45489             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45490             _i = _0 - t1;
45491             bvirt = _0 - _i;
45492             u[1] = _0 - (_i + bvirt) + (bvirt - t1);
45493             u3 = _j + _i;
45494             bvirt = u3 - _j;
45495             u[2] = _j - (u3 - bvirt) + (_i - bvirt);
45496             u[3] = u3;
45497             var C2len = sum$1(C1len, C1, 4, u, C2);
45498
45499             s1 = acxtail * bcytail;
45500             c = splitter * acxtail;
45501             ahi = c - (c - acxtail);
45502             alo = acxtail - ahi;
45503             c = splitter * bcytail;
45504             bhi = c - (c - bcytail);
45505             blo = bcytail - bhi;
45506             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45507             t1 = acytail * bcxtail;
45508             c = splitter * acytail;
45509             ahi = c - (c - acytail);
45510             alo = acytail - ahi;
45511             c = splitter * bcxtail;
45512             bhi = c - (c - bcxtail);
45513             blo = bcxtail - bhi;
45514             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45515             _i = s0 - t0;
45516             bvirt = s0 - _i;
45517             u[0] = s0 - (_i + bvirt) + (bvirt - t0);
45518             _j = s1 + _i;
45519             bvirt = _j - s1;
45520             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45521             _i = _0 - t1;
45522             bvirt = _0 - _i;
45523             u[1] = _0 - (_i + bvirt) + (bvirt - t1);
45524             u3 = _j + _i;
45525             bvirt = u3 - _j;
45526             u[2] = _j - (u3 - bvirt) + (_i - bvirt);
45527             u[3] = u3;
45528             var Dlen = sum$1(C2len, C2, 4, u, D);
45529
45530             return D[Dlen - 1];
45531         }
45532
45533         function orient2d(ax, ay, bx, by, cx, cy) {
45534             var detleft = (ay - cy) * (bx - cx);
45535             var detright = (ax - cx) * (by - cy);
45536             var det = detleft - detright;
45537
45538             if (detleft === 0 || detright === 0 || (detleft > 0) !== (detright > 0)) { return det; }
45539
45540             var detsum = Math.abs(detleft + detright);
45541             if (Math.abs(det) >= ccwerrboundA * detsum) { return det; }
45542
45543             return -orient2dadapt(ax, ay, bx, by, cx, cy, detsum);
45544         }
45545
45546         /**
45547          * Signed area of the triangle (p0, p1, p2)
45548          * @param  {Array.<Number>} p0
45549          * @param  {Array.<Number>} p1
45550          * @param  {Array.<Number>} p2
45551          * @return {Number}
45552          */
45553         function signedArea(p0, p1, p2) {
45554           var res = orient2d(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]);
45555           if (res > 0) { return -1; }
45556           if (res < 0) { return 1; }
45557           return 0;
45558         }
45559
45560         /**
45561          * @param  {SweepEvent} e1
45562          * @param  {SweepEvent} e2
45563          * @return {Number}
45564          */
45565         function compareEvents(e1, e2) {
45566           var p1 = e1.point;
45567           var p2 = e2.point;
45568
45569           // Different x-coordinate
45570           if (p1[0] > p2[0]) { return 1; }
45571           if (p1[0] < p2[0]) { return -1; }
45572
45573           // Different points, but same x-coordinate
45574           // Event with lower y-coordinate is processed first
45575           if (p1[1] !== p2[1]) { return p1[1] > p2[1] ? 1 : -1; }
45576
45577           return specialCases(e1, e2, p1);
45578         }
45579
45580
45581         /* eslint-disable no-unused-vars */
45582         function specialCases(e1, e2, p1, p2) {
45583           // Same coordinates, but one is a left endpoint and the other is
45584           // a right endpoint. The right endpoint is processed first
45585           if (e1.left !== e2.left)
45586             { return e1.left ? 1 : -1; }
45587
45588           // const p2 = e1.otherEvent.point, p3 = e2.otherEvent.point;
45589           // const sa = (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1])
45590           // Same coordinates, both events
45591           // are left endpoints or right endpoints.
45592           // not collinear
45593           if (signedArea(p1, e1.otherEvent.point, e2.otherEvent.point) !== 0) {
45594             // the event associate to the bottom segment is processed first
45595             return (!e1.isBelow(e2.otherEvent.point)) ? 1 : -1;
45596           }
45597
45598           return (!e1.isSubject && e2.isSubject) ? 1 : -1;
45599         }
45600         /* eslint-enable no-unused-vars */
45601
45602         /**
45603          * @param  {SweepEvent} se
45604          * @param  {Array.<Number>} p
45605          * @param  {Queue} queue
45606          * @return {Queue}
45607          */
45608         function divideSegment(se, p, queue)  {
45609           var r = new SweepEvent(p, false, se,            se.isSubject);
45610           var l = new SweepEvent(p, true,  se.otherEvent, se.isSubject);
45611
45612           /* eslint-disable no-console */
45613           if (equals(se.point, se.otherEvent.point)) {
45614             console.warn('what is that, a collapsed segment?', se);
45615           }
45616           /* eslint-enable no-console */
45617
45618           r.contourId = l.contourId = se.contourId;
45619
45620           // avoid a rounding error. The left event would be processed after the right event
45621           if (compareEvents(l, se.otherEvent) > 0) {
45622             se.otherEvent.left = true;
45623             l.left = false;
45624           }
45625
45626           // avoid a rounding error. The left event would be processed after the right event
45627           // if (compareEvents(se, r) > 0) {}
45628
45629           se.otherEvent.otherEvent = l;
45630           se.otherEvent = r;
45631
45632           queue.push(l);
45633           queue.push(r);
45634
45635           return queue;
45636         }
45637
45638         //const EPS = 1e-9;
45639
45640         /**
45641          * Finds the magnitude of the cross product of two vectors (if we pretend
45642          * they're in three dimensions)
45643          *
45644          * @param {Object} a First vector
45645          * @param {Object} b Second vector
45646          * @private
45647          * @returns {Number} The magnitude of the cross product
45648          */
45649         function crossProduct(a, b) {
45650           return (a[0] * b[1]) - (a[1] * b[0]);
45651         }
45652
45653         /**
45654          * Finds the dot product of two vectors.
45655          *
45656          * @param {Object} a First vector
45657          * @param {Object} b Second vector
45658          * @private
45659          * @returns {Number} The dot product
45660          */
45661         function dotProduct(a, b) {
45662           return (a[0] * b[0]) + (a[1] * b[1]);
45663         }
45664
45665         /**
45666          * Finds the intersection (if any) between two line segments a and b, given the
45667          * line segments' end points a1, a2 and b1, b2.
45668          *
45669          * This algorithm is based on Schneider and Eberly.
45670          * http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf
45671          * Page 244.
45672          *
45673          * @param {Array.<Number>} a1 point of first line
45674          * @param {Array.<Number>} a2 point of first line
45675          * @param {Array.<Number>} b1 point of second line
45676          * @param {Array.<Number>} b2 point of second line
45677          * @param {Boolean=}       noEndpointTouch whether to skip single touchpoints
45678          *                                         (meaning connected segments) as
45679          *                                         intersections
45680          * @returns {Array.<Array.<Number>>|Null} If the lines intersect, the point of
45681          * intersection. If they overlap, the two end points of the overlapping segment.
45682          * Otherwise, null.
45683          */
45684         function intersection (a1, a2, b1, b2, noEndpointTouch) {
45685           // The algorithm expects our lines in the form P + sd, where P is a point,
45686           // s is on the interval [0, 1], and d is a vector.
45687           // We are passed two points. P can be the first point of each pair. The
45688           // vector, then, could be thought of as the distance (in x and y components)
45689           // from the first point to the second point.
45690           // So first, let's make our vectors:
45691           var va = [a2[0] - a1[0], a2[1] - a1[1]];
45692           var vb = [b2[0] - b1[0], b2[1] - b1[1]];
45693           // We also define a function to convert back to regular point form:
45694
45695           /* eslint-disable arrow-body-style */
45696
45697           function toPoint(p, s, d) {
45698             return [
45699               p[0] + s * d[0],
45700               p[1] + s * d[1]
45701             ];
45702           }
45703
45704           /* eslint-enable arrow-body-style */
45705
45706           // The rest is pretty much a straight port of the algorithm.
45707           var e = [b1[0] - a1[0], b1[1] - a1[1]];
45708           var kross    = crossProduct(va, vb);
45709           var sqrKross = kross * kross;
45710           var sqrLenA  = dotProduct(va, va);
45711           //const sqrLenB  = dotProduct(vb, vb);
45712
45713           // Check for line intersection. This works because of the properties of the
45714           // cross product -- specifically, two vectors are parallel if and only if the
45715           // cross product is the 0 vector. The full calculation involves relative error
45716           // to account for possible very small line segments. See Schneider & Eberly
45717           // for details.
45718           if (sqrKross > 0/* EPS * sqrLenB * sqLenA */) {
45719             // If they're not parallel, then (because these are line segments) they
45720             // still might not actually intersect. This code checks that the
45721             // intersection point of the lines is actually on both line segments.
45722             var s = crossProduct(e, vb) / kross;
45723             if (s < 0 || s > 1) {
45724               // not on line segment a
45725               return null;
45726             }
45727             var t = crossProduct(e, va) / kross;
45728             if (t < 0 || t > 1) {
45729               // not on line segment b
45730               return null;
45731             }
45732             if (s === 0 || s === 1) {
45733               // on an endpoint of line segment a
45734               return noEndpointTouch ? null : [toPoint(a1, s, va)];
45735             }
45736             if (t === 0 || t === 1) {
45737               // on an endpoint of line segment b
45738               return noEndpointTouch ? null : [toPoint(b1, t, vb)];
45739             }
45740             return [toPoint(a1, s, va)];
45741           }
45742
45743           // If we've reached this point, then the lines are either parallel or the
45744           // same, but the segments could overlap partially or fully, or not at all.
45745           // So we need to find the overlap, if any. To do that, we can use e, which is
45746           // the (vector) difference between the two initial points. If this is parallel
45747           // with the line itself, then the two lines are the same line, and there will
45748           // be overlap.
45749           //const sqrLenE = dotProduct(e, e);
45750           kross = crossProduct(e, va);
45751           sqrKross = kross * kross;
45752
45753           if (sqrKross > 0 /* EPS * sqLenB * sqLenE */) {
45754           // Lines are just parallel, not the same. No overlap.
45755             return null;
45756           }
45757
45758           var sa = dotProduct(va, e) / sqrLenA;
45759           var sb = sa + dotProduct(va, vb) / sqrLenA;
45760           var smin = Math.min(sa, sb);
45761           var smax = Math.max(sa, sb);
45762
45763           // this is, essentially, the FindIntersection acting on floats from
45764           // Schneider & Eberly, just inlined into this function.
45765           if (smin <= 1 && smax >= 0) {
45766
45767             // overlap on an end point
45768             if (smin === 1) {
45769               return noEndpointTouch ? null : [toPoint(a1, smin > 0 ? smin : 0, va)];
45770             }
45771
45772             if (smax === 0) {
45773               return noEndpointTouch ? null : [toPoint(a1, smax < 1 ? smax : 1, va)];
45774             }
45775
45776             if (noEndpointTouch && smin === 0 && smax === 1) { return null; }
45777
45778             // There's overlap on a segment -- two points of intersection. Return both.
45779             return [
45780               toPoint(a1, smin > 0 ? smin : 0, va),
45781               toPoint(a1, smax < 1 ? smax : 1, va)
45782             ];
45783           }
45784
45785           return null;
45786         }
45787
45788         /**
45789          * @param  {SweepEvent} se1
45790          * @param  {SweepEvent} se2
45791          * @param  {Queue}      queue
45792          * @return {Number}
45793          */
45794         function possibleIntersection (se1, se2, queue) {
45795           // that disallows self-intersecting polygons,
45796           // did cost us half a day, so I'll leave it
45797           // out of respect
45798           // if (se1.isSubject === se2.isSubject) return;
45799           var inter = intersection(
45800             se1.point, se1.otherEvent.point,
45801             se2.point, se2.otherEvent.point
45802           );
45803
45804           var nintersections = inter ? inter.length : 0;
45805           if (nintersections === 0) { return 0; } // no intersection
45806
45807           // the line segments intersect at an endpoint of both line segments
45808           if ((nintersections === 1) &&
45809               (equals(se1.point, se2.point) ||
45810                equals(se1.otherEvent.point, se2.otherEvent.point))) {
45811             return 0;
45812           }
45813
45814           if (nintersections === 2 && se1.isSubject === se2.isSubject) {
45815             // if(se1.contourId === se2.contourId){
45816             // console.warn('Edges of the same polygon overlap',
45817             //   se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point);
45818             // }
45819             //throw new Error('Edges of the same polygon overlap');
45820             return 0;
45821           }
45822
45823           // The line segments associated to se1 and se2 intersect
45824           if (nintersections === 1) {
45825
45826             // if the intersection point is not an endpoint of se1
45827             if (!equals(se1.point, inter[0]) && !equals(se1.otherEvent.point, inter[0])) {
45828               divideSegment(se1, inter[0], queue);
45829             }
45830
45831             // if the intersection point is not an endpoint of se2
45832             if (!equals(se2.point, inter[0]) && !equals(se2.otherEvent.point, inter[0])) {
45833               divideSegment(se2, inter[0], queue);
45834             }
45835             return 1;
45836           }
45837
45838           // The line segments associated to se1 and se2 overlap
45839           var events        = [];
45840           var leftCoincide  = false;
45841           var rightCoincide = false;
45842
45843           if (equals(se1.point, se2.point)) {
45844             leftCoincide = true; // linked
45845           } else if (compareEvents(se1, se2) === 1) {
45846             events.push(se2, se1);
45847           } else {
45848             events.push(se1, se2);
45849           }
45850
45851           if (equals(se1.otherEvent.point, se2.otherEvent.point)) {
45852             rightCoincide = true;
45853           } else if (compareEvents(se1.otherEvent, se2.otherEvent) === 1) {
45854             events.push(se2.otherEvent, se1.otherEvent);
45855           } else {
45856             events.push(se1.otherEvent, se2.otherEvent);
45857           }
45858
45859           if ((leftCoincide && rightCoincide) || leftCoincide) {
45860             // both line segments are equal or share the left endpoint
45861             se2.type = NON_CONTRIBUTING;
45862             se1.type = (se2.inOut === se1.inOut)
45863               ? SAME_TRANSITION : DIFFERENT_TRANSITION;
45864
45865             if (leftCoincide && !rightCoincide) {
45866               // honestly no idea, but changing events selection from [2, 1]
45867               // to [0, 1] fixes the overlapping self-intersecting polygons issue
45868               divideSegment(events[1].otherEvent, events[0].point, queue);
45869             }
45870             return 2;
45871           }
45872
45873           // the line segments share the right endpoint
45874           if (rightCoincide) {
45875             divideSegment(events[0], events[1].point, queue);
45876             return 3;
45877           }
45878
45879           // no line segment includes totally the other one
45880           if (events[0] !== events[3].otherEvent) {
45881             divideSegment(events[0], events[1].point, queue);
45882             divideSegment(events[1], events[2].point, queue);
45883             return 3;
45884           }
45885
45886           // one line segment includes the other one
45887           divideSegment(events[0], events[1].point, queue);
45888           divideSegment(events[3].otherEvent, events[2].point, queue);
45889
45890           return 3;
45891         }
45892
45893         /**
45894          * @param  {SweepEvent} le1
45895          * @param  {SweepEvent} le2
45896          * @return {Number}
45897          */
45898         function compareSegments(le1, le2) {
45899           if (le1 === le2) { return 0; }
45900
45901           // Segments are not collinear
45902           if (signedArea(le1.point, le1.otherEvent.point, le2.point) !== 0 ||
45903             signedArea(le1.point, le1.otherEvent.point, le2.otherEvent.point) !== 0) {
45904
45905             // If they share their left endpoint use the right endpoint to sort
45906             if (equals(le1.point, le2.point)) { return le1.isBelow(le2.otherEvent.point) ? -1 : 1; }
45907
45908             // Different left endpoint: use the left endpoint to sort
45909             if (le1.point[0] === le2.point[0]) { return le1.point[1] < le2.point[1] ? -1 : 1; }
45910
45911             // has the line segment associated to e1 been inserted
45912             // into S after the line segment associated to e2 ?
45913             if (compareEvents(le1, le2) === 1) { return le2.isAbove(le1.point) ? -1 : 1; }
45914
45915             // The line segment associated to e2 has been inserted
45916             // into S after the line segment associated to e1
45917             return le1.isBelow(le2.point) ? -1 : 1;
45918           }
45919
45920           if (le1.isSubject === le2.isSubject) { // same polygon
45921             var p1 = le1.point, p2 = le2.point;
45922             if (p1[0] === p2[0] && p1[1] === p2[1]/*equals(le1.point, le2.point)*/) {
45923               p1 = le1.otherEvent.point; p2 = le2.otherEvent.point;
45924               if (p1[0] === p2[0] && p1[1] === p2[1]) { return 0; }
45925               else { return le1.contourId > le2.contourId ? 1 : -1; }
45926             }
45927           } else { // Segments are collinear, but belong to separate polygons
45928             return le1.isSubject ? -1 : 1;
45929           }
45930
45931           return compareEvents(le1, le2) === 1 ? 1 : -1;
45932         }
45933
45934         function subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation) {
45935           var sweepLine = new SplayTree(compareSegments);
45936           var sortedEvents = [];
45937
45938           var rightbound = Math.min(sbbox[2], cbbox[2]);
45939
45940           var prev, next, begin;
45941
45942           while (eventQueue.length !== 0) {
45943             var event = eventQueue.pop();
45944             sortedEvents.push(event);
45945
45946             // optimization by bboxes for intersection and difference goes here
45947             if ((operation === INTERSECTION && event.point[0] > rightbound) ||
45948                 (operation === DIFFERENCE   && event.point[0] > sbbox[2])) {
45949               break;
45950             }
45951
45952             if (event.left) {
45953               next  = prev = sweepLine.insert(event);
45954               begin = sweepLine.minNode();
45955
45956               if (prev !== begin) { prev = sweepLine.prev(prev); }
45957               else                { prev = null; }
45958
45959               next = sweepLine.next(next);
45960
45961               var prevEvent = prev ? prev.key : null;
45962               var prevprevEvent = (void 0);
45963               computeFields(event, prevEvent, operation);
45964               if (next) {
45965                 if (possibleIntersection(event, next.key, eventQueue) === 2) {
45966                   computeFields(event, prevEvent, operation);
45967                   computeFields(event, next.key, operation);
45968                 }
45969               }
45970
45971               if (prev) {
45972                 if (possibleIntersection(prev.key, event, eventQueue) === 2) {
45973                   var prevprev = prev;
45974                   if (prevprev !== begin) { prevprev = sweepLine.prev(prevprev); }
45975                   else                    { prevprev = null; }
45976
45977                   prevprevEvent = prevprev ? prevprev.key : null;
45978                   computeFields(prevEvent, prevprevEvent, operation);
45979                   computeFields(event,     prevEvent,     operation);
45980                 }
45981               }
45982             } else {
45983               event = event.otherEvent;
45984               next = prev = sweepLine.find(event);
45985
45986               if (prev && next) {
45987
45988                 if (prev !== begin) { prev = sweepLine.prev(prev); }
45989                 else                { prev = null; }
45990
45991                 next = sweepLine.next(next);
45992                 sweepLine.remove(event);
45993
45994                 if (next && prev) {
45995                   possibleIntersection(prev.key, next.key, eventQueue);
45996                 }
45997               }
45998             }
45999           }
46000           return sortedEvents;
46001         }
46002
46003         var Contour = function Contour() {
46004           this.points = [];
46005           this.holeIds = [];
46006           this.holeOf = null;
46007           this.depth = null;
46008         };
46009
46010         Contour.prototype.isExterior = function isExterior () {
46011           return this.holeOf == null;
46012         };
46013
46014         /**
46015          * @param  {Array.<SweepEvent>} sortedEvents
46016          * @return {Array.<SweepEvent>}
46017          */
46018         function orderEvents(sortedEvents) {
46019           var event, i, len, tmp;
46020           var resultEvents = [];
46021           for (i = 0, len = sortedEvents.length; i < len; i++) {
46022             event = sortedEvents[i];
46023             if ((event.left && event.inResult) ||
46024               (!event.left && event.otherEvent.inResult)) {
46025               resultEvents.push(event);
46026             }
46027           }
46028           // Due to overlapping edges the resultEvents array can be not wholly sorted
46029           var sorted = false;
46030           while (!sorted) {
46031             sorted = true;
46032             for (i = 0, len = resultEvents.length; i < len; i++) {
46033               if ((i + 1) < len &&
46034                 compareEvents(resultEvents[i], resultEvents[i + 1]) === 1) {
46035                 tmp = resultEvents[i];
46036                 resultEvents[i] = resultEvents[i + 1];
46037                 resultEvents[i + 1] = tmp;
46038                 sorted = false;
46039               }
46040             }
46041           }
46042
46043
46044           for (i = 0, len = resultEvents.length; i < len; i++) {
46045             event = resultEvents[i];
46046             event.otherPos = i;
46047           }
46048
46049           // imagine, the right event is found in the beginning of the queue,
46050           // when his left counterpart is not marked yet
46051           for (i = 0, len = resultEvents.length; i < len; i++) {
46052             event = resultEvents[i];
46053             if (!event.left) {
46054               tmp = event.otherPos;
46055               event.otherPos = event.otherEvent.otherPos;
46056               event.otherEvent.otherPos = tmp;
46057             }
46058           }
46059
46060           return resultEvents;
46061         }
46062
46063
46064         /**
46065          * @param  {Number} pos
46066          * @param  {Array.<SweepEvent>} resultEvents
46067          * @param  {Object>}    processed
46068          * @return {Number}
46069          */
46070         function nextPos(pos, resultEvents, processed, origPos) {
46071           var newPos = pos + 1,
46072               p = resultEvents[pos].point,
46073               p1;
46074           var length = resultEvents.length;
46075
46076           if (newPos < length)
46077             { p1 = resultEvents[newPos].point; }
46078
46079           while (newPos < length && p1[0] === p[0] && p1[1] === p[1]) {
46080             if (!processed[newPos]) {
46081               return newPos;
46082             } else   {
46083               newPos++;
46084             }
46085             p1 = resultEvents[newPos].point;
46086           }
46087
46088           newPos = pos - 1;
46089
46090           while (processed[newPos] && newPos > origPos) {
46091             newPos--;
46092           }
46093
46094           return newPos;
46095         }
46096
46097
46098         function initializeContourFromContext(event, contours, contourId) {
46099           var contour = new Contour();
46100           if (event.prevInResult != null) {
46101             var prevInResult = event.prevInResult;
46102             // Note that it is valid to query the "previous in result" for its output contour id,
46103             // because we must have already processed it (i.e., assigned an output contour id)
46104             // in an earlier iteration, otherwise it wouldn't be possible that it is "previous in
46105             // result".
46106             var lowerContourId = prevInResult.outputContourId;
46107             var lowerResultTransition = prevInResult.resultTransition;
46108             if (lowerResultTransition > 0) {
46109               // We are inside. Now we have to check if the thing below us is another hole or
46110               // an exterior contour.
46111               var lowerContour = contours[lowerContourId];
46112               if (lowerContour.holeOf != null) {
46113                 // The lower contour is a hole => Connect the new contour as a hole to its parent,
46114                 // and use same depth.
46115                 var parentContourId = lowerContour.holeOf;
46116                 contours[parentContourId].holeIds.push(contourId);
46117                 contour.holeOf = parentContourId;
46118                 contour.depth = contours[lowerContourId].depth;
46119               } else {
46120                 // The lower contour is an exterior contour => Connect the new contour as a hole,
46121                 // and increment depth.
46122                 contours[lowerContourId].holeIds.push(contourId);
46123                 contour.holeOf = lowerContourId;
46124                 contour.depth = contours[lowerContourId].depth + 1;
46125               }
46126             } else {
46127               // We are outside => this contour is an exterior contour of same depth.
46128               contour.holeOf = null;
46129               contour.depth = contours[lowerContourId].depth;
46130             }
46131           } else {
46132             // There is no lower/previous contour => this contour is an exterior contour of depth 0.
46133             contour.holeOf = null;
46134             contour.depth = 0;
46135           }
46136           return contour;
46137         }
46138
46139         /**
46140          * @param  {Array.<SweepEvent>} sortedEvents
46141          * @return {Array.<*>} polygons
46142          */
46143         function connectEdges(sortedEvents) {
46144           var i, len;
46145           var resultEvents = orderEvents(sortedEvents);
46146
46147           // "false"-filled array
46148           var processed = {};
46149           var contours = [];
46150
46151           var loop = function (  ) {
46152
46153             if (processed[i]) {
46154               return;
46155             }
46156
46157             var contourId = contours.length;
46158             var contour = initializeContourFromContext(resultEvents[i], contours, contourId);
46159
46160             // Helper function that combines marking an event as processed with assigning its output contour ID
46161             var markAsProcessed = function (pos) {
46162               processed[pos] = true;
46163               resultEvents[pos].outputContourId = contourId;
46164             };
46165
46166             var pos = i;
46167             var origPos = i;
46168
46169             var initial = resultEvents[i].point;
46170             contour.points.push(initial);
46171
46172             /* eslint no-constant-condition: "off" */
46173             while (true) {
46174               markAsProcessed(pos);
46175
46176               pos = resultEvents[pos].otherPos;
46177
46178               markAsProcessed(pos);
46179               contour.points.push(resultEvents[pos].point);
46180
46181               pos = nextPos(pos, resultEvents, processed, origPos);
46182
46183               if (pos == origPos) {
46184                 break;
46185               }
46186             }
46187
46188             contours.push(contour);
46189           };
46190
46191           for (i = 0, len = resultEvents.length; i < len; i++) loop(  );
46192
46193           return contours;
46194         }
46195
46196         var tinyqueue = TinyQueue;
46197         var _default$1 = TinyQueue;
46198
46199         function TinyQueue(data, compare) {
46200             if (!(this instanceof TinyQueue)) { return new TinyQueue(data, compare); }
46201
46202             this.data = data || [];
46203             this.length = this.data.length;
46204             this.compare = compare || defaultCompare$1;
46205
46206             if (this.length > 0) {
46207                 for (var i = (this.length >> 1) - 1; i >= 0; i--) { this._down(i); }
46208             }
46209         }
46210
46211         function defaultCompare$1(a, b) {
46212             return a < b ? -1 : a > b ? 1 : 0;
46213         }
46214
46215         TinyQueue.prototype = {
46216
46217             push: function (item) {
46218                 this.data.push(item);
46219                 this.length++;
46220                 this._up(this.length - 1);
46221             },
46222
46223             pop: function () {
46224                 if (this.length === 0) { return undefined; }
46225
46226                 var top = this.data[0];
46227                 this.length--;
46228
46229                 if (this.length > 0) {
46230                     this.data[0] = this.data[this.length];
46231                     this._down(0);
46232                 }
46233                 this.data.pop();
46234
46235                 return top;
46236             },
46237
46238             peek: function () {
46239                 return this.data[0];
46240             },
46241
46242             _up: function (pos) {
46243                 var data = this.data;
46244                 var compare = this.compare;
46245                 var item = data[pos];
46246
46247                 while (pos > 0) {
46248                     var parent = (pos - 1) >> 1;
46249                     var current = data[parent];
46250                     if (compare(item, current) >= 0) { break; }
46251                     data[pos] = current;
46252                     pos = parent;
46253                 }
46254
46255                 data[pos] = item;
46256             },
46257
46258             _down: function (pos) {
46259                 var data = this.data;
46260                 var compare = this.compare;
46261                 var halfLength = this.length >> 1;
46262                 var item = data[pos];
46263
46264                 while (pos < halfLength) {
46265                     var left = (pos << 1) + 1;
46266                     var right = left + 1;
46267                     var best = data[left];
46268
46269                     if (right < this.length && compare(data[right], best) < 0) {
46270                         left = right;
46271                         best = data[right];
46272                     }
46273                     if (compare(best, item) >= 0) { break; }
46274
46275                     data[pos] = best;
46276                     pos = left;
46277                 }
46278
46279                 data[pos] = item;
46280             }
46281         };
46282         tinyqueue.default = _default$1;
46283
46284         var max$2 = Math.max;
46285         var min = Math.min;
46286
46287         var contourId = 0;
46288
46289
46290         function processPolygon(contourOrHole, isSubject, depth, Q, bbox, isExteriorRing) {
46291           var i, len, s1, s2, e1, e2;
46292           for (i = 0, len = contourOrHole.length - 1; i < len; i++) {
46293             s1 = contourOrHole[i];
46294             s2 = contourOrHole[i + 1];
46295             e1 = new SweepEvent(s1, false, undefined, isSubject);
46296             e2 = new SweepEvent(s2, false, e1,        isSubject);
46297             e1.otherEvent = e2;
46298
46299             if (s1[0] === s2[0] && s1[1] === s2[1]) {
46300               continue; // skip collapsed edges, or it breaks
46301             }
46302
46303             e1.contourId = e2.contourId = depth;
46304             if (!isExteriorRing) {
46305               e1.isExteriorRing = false;
46306               e2.isExteriorRing = false;
46307             }
46308             if (compareEvents(e1, e2) > 0) {
46309               e2.left = true;
46310             } else {
46311               e1.left = true;
46312             }
46313
46314             var x = s1[0], y = s1[1];
46315             bbox[0] = min(bbox[0], x);
46316             bbox[1] = min(bbox[1], y);
46317             bbox[2] = max$2(bbox[2], x);
46318             bbox[3] = max$2(bbox[3], y);
46319
46320             // Pushing it so the queue is sorted from left to right,
46321             // with object on the left having the highest priority.
46322             Q.push(e1);
46323             Q.push(e2);
46324           }
46325         }
46326
46327
46328         function fillQueue(subject, clipping, sbbox, cbbox, operation) {
46329           var eventQueue = new tinyqueue(null, compareEvents);
46330           var polygonSet, isExteriorRing, i, ii, j, jj; //, k, kk;
46331
46332           for (i = 0, ii = subject.length; i < ii; i++) {
46333             polygonSet = subject[i];
46334             for (j = 0, jj = polygonSet.length; j < jj; j++) {
46335               isExteriorRing = j === 0;
46336               if (isExteriorRing) { contourId++; }
46337               processPolygon(polygonSet[j], true, contourId, eventQueue, sbbox, isExteriorRing);
46338             }
46339           }
46340
46341           for (i = 0, ii = clipping.length; i < ii; i++) {
46342             polygonSet = clipping[i];
46343             for (j = 0, jj = polygonSet.length; j < jj; j++) {
46344               isExteriorRing = j === 0;
46345               if (operation === DIFFERENCE) { isExteriorRing = false; }
46346               if (isExteriorRing) { contourId++; }
46347               processPolygon(polygonSet[j], false, contourId, eventQueue, cbbox, isExteriorRing);
46348             }
46349           }
46350
46351           return eventQueue;
46352         }
46353
46354         var EMPTY = [];
46355
46356
46357         function trivialOperation(subject, clipping, operation) {
46358           var result = null;
46359           if (subject.length * clipping.length === 0) {
46360             if        (operation === INTERSECTION) {
46361               result = EMPTY;
46362             } else if (operation === DIFFERENCE) {
46363               result = subject;
46364             } else if (operation === UNION ||
46365                        operation === XOR) {
46366               result = (subject.length === 0) ? clipping : subject;
46367             }
46368           }
46369           return result;
46370         }
46371
46372
46373         function compareBBoxes(subject, clipping, sbbox, cbbox, operation) {
46374           var result = null;
46375           if (sbbox[0] > cbbox[2] ||
46376               cbbox[0] > sbbox[2] ||
46377               sbbox[1] > cbbox[3] ||
46378               cbbox[1] > sbbox[3]) {
46379             if        (operation === INTERSECTION) {
46380               result = EMPTY;
46381             } else if (operation === DIFFERENCE) {
46382               result = subject;
46383             } else if (operation === UNION ||
46384                        operation === XOR) {
46385               result = subject.concat(clipping);
46386             }
46387           }
46388           return result;
46389         }
46390
46391
46392         function boolean(subject, clipping, operation) {
46393           if (typeof subject[0][0][0] === 'number') {
46394             subject = [subject];
46395           }
46396           if (typeof clipping[0][0][0] === 'number') {
46397             clipping = [clipping];
46398           }
46399           var trivial = trivialOperation(subject, clipping, operation);
46400           if (trivial) {
46401             return trivial === EMPTY ? null : trivial;
46402           }
46403           var sbbox = [Infinity, Infinity, -Infinity, -Infinity];
46404           var cbbox = [Infinity, Infinity, -Infinity, -Infinity];
46405
46406           // console.time('fill queue');
46407           var eventQueue = fillQueue(subject, clipping, sbbox, cbbox, operation);
46408           //console.timeEnd('fill queue');
46409
46410           trivial = compareBBoxes(subject, clipping, sbbox, cbbox, operation);
46411           if (trivial) {
46412             return trivial === EMPTY ? null : trivial;
46413           }
46414           // console.time('subdivide edges');
46415           var sortedEvents = subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation);
46416           //console.timeEnd('subdivide edges');
46417
46418           // console.time('connect vertices');
46419           var contours = connectEdges(sortedEvents);
46420           //console.timeEnd('connect vertices');
46421
46422           // Convert contours to polygons
46423           var polygons = [];
46424           for (var i = 0; i < contours.length; i++) {
46425             var contour = contours[i];
46426             if (contour.isExterior()) {
46427               // The exterior ring goes first
46428               var rings = [contour.points];
46429               // Followed by holes if any
46430               for (var j = 0; j < contour.holeIds.length; j++) {
46431                 var holeId = contour.holeIds[j];
46432                 rings.push(contours[holeId].points);
46433               }
46434               polygons.push(rings);
46435             }
46436           }
46437
46438           return polygons;
46439         }
46440
46441         function union (subject, clipping) {
46442           return boolean(subject, clipping, UNION);
46443         }
46444
46445         var read$6 = function (buffer, offset, isLE, mLen, nBytes) {
46446           var e, m;
46447           var eLen = (nBytes * 8) - mLen - 1;
46448           var eMax = (1 << eLen) - 1;
46449           var eBias = eMax >> 1;
46450           var nBits = -7;
46451           var i = isLE ? (nBytes - 1) : 0;
46452           var d = isLE ? -1 : 1;
46453           var s = buffer[offset + i];
46454
46455           i += d;
46456
46457           e = s & ((1 << (-nBits)) - 1);
46458           s >>= (-nBits);
46459           nBits += eLen;
46460           for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}
46461
46462           m = e & ((1 << (-nBits)) - 1);
46463           e >>= (-nBits);
46464           nBits += mLen;
46465           for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}
46466
46467           if (e === 0) {
46468             e = 1 - eBias;
46469           } else if (e === eMax) {
46470             return m ? NaN : ((s ? -1 : 1) * Infinity)
46471           } else {
46472             m = m + Math.pow(2, mLen);
46473             e = e - eBias;
46474           }
46475           return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
46476         };
46477
46478         var write$6 = function (buffer, value, offset, isLE, mLen, nBytes) {
46479           var e, m, c;
46480           var eLen = (nBytes * 8) - mLen - 1;
46481           var eMax = (1 << eLen) - 1;
46482           var eBias = eMax >> 1;
46483           var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0);
46484           var i = isLE ? 0 : (nBytes - 1);
46485           var d = isLE ? 1 : -1;
46486           var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;
46487
46488           value = Math.abs(value);
46489
46490           if (isNaN(value) || value === Infinity) {
46491             m = isNaN(value) ? 1 : 0;
46492             e = eMax;
46493           } else {
46494             e = Math.floor(Math.log(value) / Math.LN2);
46495             if (value * (c = Math.pow(2, -e)) < 1) {
46496               e--;
46497               c *= 2;
46498             }
46499             if (e + eBias >= 1) {
46500               value += rt / c;
46501             } else {
46502               value += rt * Math.pow(2, 1 - eBias);
46503             }
46504             if (value * c >= 2) {
46505               e++;
46506               c /= 2;
46507             }
46508
46509             if (e + eBias >= eMax) {
46510               m = 0;
46511               e = eMax;
46512             } else if (e + eBias >= 1) {
46513               m = ((value * c) - 1) * Math.pow(2, mLen);
46514               e = e + eBias;
46515             } else {
46516               m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
46517               e = 0;
46518             }
46519           }
46520
46521           for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
46522
46523           e = (e << mLen) | m;
46524           eLen += mLen;
46525           for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
46526
46527           buffer[offset + i - d] |= s * 128;
46528         };
46529
46530         var ieee754 = {
46531                 read: read$6,
46532                 write: write$6
46533         };
46534
46535         var pbf = Pbf;
46536
46537
46538
46539         function Pbf(buf) {
46540             this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0);
46541             this.pos = 0;
46542             this.type = 0;
46543             this.length = this.buf.length;
46544         }
46545
46546         Pbf.Varint  = 0; // varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum
46547         Pbf.Fixed64 = 1; // 64-bit: double, fixed64, sfixed64
46548         Pbf.Bytes   = 2; // length-delimited: string, bytes, embedded messages, packed repeated fields
46549         Pbf.Fixed32 = 5; // 32-bit: float, fixed32, sfixed32
46550
46551         var SHIFT_LEFT_32 = (1 << 16) * (1 << 16),
46552             SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32;
46553
46554         // Threshold chosen based on both benchmarking and knowledge about browser string
46555         // data structures (which currently switch structure types at 12 bytes or more)
46556         var TEXT_DECODER_MIN_LENGTH = 12;
46557         var utf8TextDecoder = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf8');
46558
46559         Pbf.prototype = {
46560
46561             destroy: function() {
46562                 this.buf = null;
46563             },
46564
46565             // === READING =================================================================
46566
46567             readFields: function(readField, result, end) {
46568                 end = end || this.length;
46569
46570                 while (this.pos < end) {
46571                     var val = this.readVarint(),
46572                         tag = val >> 3,
46573                         startPos = this.pos;
46574
46575                     this.type = val & 0x7;
46576                     readField(tag, result, this);
46577
46578                     if (this.pos === startPos) { this.skip(val); }
46579                 }
46580                 return result;
46581             },
46582
46583             readMessage: function(readField, result) {
46584                 return this.readFields(readField, result, this.readVarint() + this.pos);
46585             },
46586
46587             readFixed32: function() {
46588                 var val = readUInt32(this.buf, this.pos);
46589                 this.pos += 4;
46590                 return val;
46591             },
46592
46593             readSFixed32: function() {
46594                 var val = readInt32(this.buf, this.pos);
46595                 this.pos += 4;
46596                 return val;
46597             },
46598
46599             // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed)
46600
46601             readFixed64: function() {
46602                 var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
46603                 this.pos += 8;
46604                 return val;
46605             },
46606
46607             readSFixed64: function() {
46608                 var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
46609                 this.pos += 8;
46610                 return val;
46611             },
46612
46613             readFloat: function() {
46614                 var val = ieee754.read(this.buf, this.pos, true, 23, 4);
46615                 this.pos += 4;
46616                 return val;
46617             },
46618
46619             readDouble: function() {
46620                 var val = ieee754.read(this.buf, this.pos, true, 52, 8);
46621                 this.pos += 8;
46622                 return val;
46623             },
46624
46625             readVarint: function(isSigned) {
46626                 var buf = this.buf,
46627                     val, b;
46628
46629                 b = buf[this.pos++]; val  =  b & 0x7f;        if (b < 0x80) { return val; }
46630                 b = buf[this.pos++]; val |= (b & 0x7f) << 7;  if (b < 0x80) { return val; }
46631                 b = buf[this.pos++]; val |= (b & 0x7f) << 14; if (b < 0x80) { return val; }
46632                 b = buf[this.pos++]; val |= (b & 0x7f) << 21; if (b < 0x80) { return val; }
46633                 b = buf[this.pos];   val |= (b & 0x0f) << 28;
46634
46635                 return readVarintRemainder(val, isSigned, this);
46636             },
46637
46638             readVarint64: function() { // for compatibility with v2.0.1
46639                 return this.readVarint(true);
46640             },
46641
46642             readSVarint: function() {
46643                 var num = this.readVarint();
46644                 return num % 2 === 1 ? (num + 1) / -2 : num / 2; // zigzag encoding
46645             },
46646
46647             readBoolean: function() {
46648                 return Boolean(this.readVarint());
46649             },
46650
46651             readString: function() {
46652                 var end = this.readVarint() + this.pos;
46653                 var pos = this.pos;
46654                 this.pos = end;
46655
46656                 if (end - pos >= TEXT_DECODER_MIN_LENGTH && utf8TextDecoder) {
46657                     // longer strings are fast with the built-in browser TextDecoder API
46658                     return readUtf8TextDecoder(this.buf, pos, end);
46659                 }
46660                 // short strings are fast with our custom implementation
46661                 return readUtf8(this.buf, pos, end);
46662             },
46663
46664             readBytes: function() {
46665                 var end = this.readVarint() + this.pos,
46666                     buffer = this.buf.subarray(this.pos, end);
46667                 this.pos = end;
46668                 return buffer;
46669             },
46670
46671             // verbose for performance reasons; doesn't affect gzipped size
46672
46673             readPackedVarint: function(arr, isSigned) {
46674                 if (this.type !== Pbf.Bytes) { return arr.push(this.readVarint(isSigned)); }
46675                 var end = readPackedEnd(this);
46676                 arr = arr || [];
46677                 while (this.pos < end) { arr.push(this.readVarint(isSigned)); }
46678                 return arr;
46679             },
46680             readPackedSVarint: function(arr) {
46681                 if (this.type !== Pbf.Bytes) { return arr.push(this.readSVarint()); }
46682                 var end = readPackedEnd(this);
46683                 arr = arr || [];
46684                 while (this.pos < end) { arr.push(this.readSVarint()); }
46685                 return arr;
46686             },
46687             readPackedBoolean: function(arr) {
46688                 if (this.type !== Pbf.Bytes) { return arr.push(this.readBoolean()); }
46689                 var end = readPackedEnd(this);
46690                 arr = arr || [];
46691                 while (this.pos < end) { arr.push(this.readBoolean()); }
46692                 return arr;
46693             },
46694             readPackedFloat: function(arr) {
46695                 if (this.type !== Pbf.Bytes) { return arr.push(this.readFloat()); }
46696                 var end = readPackedEnd(this);
46697                 arr = arr || [];
46698                 while (this.pos < end) { arr.push(this.readFloat()); }
46699                 return arr;
46700             },
46701             readPackedDouble: function(arr) {
46702                 if (this.type !== Pbf.Bytes) { return arr.push(this.readDouble()); }
46703                 var end = readPackedEnd(this);
46704                 arr = arr || [];
46705                 while (this.pos < end) { arr.push(this.readDouble()); }
46706                 return arr;
46707             },
46708             readPackedFixed32: function(arr) {
46709                 if (this.type !== Pbf.Bytes) { return arr.push(this.readFixed32()); }
46710                 var end = readPackedEnd(this);
46711                 arr = arr || [];
46712                 while (this.pos < end) { arr.push(this.readFixed32()); }
46713                 return arr;
46714             },
46715             readPackedSFixed32: function(arr) {
46716                 if (this.type !== Pbf.Bytes) { return arr.push(this.readSFixed32()); }
46717                 var end = readPackedEnd(this);
46718                 arr = arr || [];
46719                 while (this.pos < end) { arr.push(this.readSFixed32()); }
46720                 return arr;
46721             },
46722             readPackedFixed64: function(arr) {
46723                 if (this.type !== Pbf.Bytes) { return arr.push(this.readFixed64()); }
46724                 var end = readPackedEnd(this);
46725                 arr = arr || [];
46726                 while (this.pos < end) { arr.push(this.readFixed64()); }
46727                 return arr;
46728             },
46729             readPackedSFixed64: function(arr) {
46730                 if (this.type !== Pbf.Bytes) { return arr.push(this.readSFixed64()); }
46731                 var end = readPackedEnd(this);
46732                 arr = arr || [];
46733                 while (this.pos < end) { arr.push(this.readSFixed64()); }
46734                 return arr;
46735             },
46736
46737             skip: function(val) {
46738                 var type = val & 0x7;
46739                 if (type === Pbf.Varint) { while (this.buf[this.pos++] > 0x7f) {} }
46740                 else if (type === Pbf.Bytes) { this.pos = this.readVarint() + this.pos; }
46741                 else if (type === Pbf.Fixed32) { this.pos += 4; }
46742                 else if (type === Pbf.Fixed64) { this.pos += 8; }
46743                 else { throw new Error('Unimplemented type: ' + type); }
46744             },
46745
46746             // === WRITING =================================================================
46747
46748             writeTag: function(tag, type) {
46749                 this.writeVarint((tag << 3) | type);
46750             },
46751
46752             realloc: function(min) {
46753                 var length = this.length || 16;
46754
46755                 while (length < this.pos + min) { length *= 2; }
46756
46757                 if (length !== this.length) {
46758                     var buf = new Uint8Array(length);
46759                     buf.set(this.buf);
46760                     this.buf = buf;
46761                     this.length = length;
46762                 }
46763             },
46764
46765             finish: function() {
46766                 this.length = this.pos;
46767                 this.pos = 0;
46768                 return this.buf.subarray(0, this.length);
46769             },
46770
46771             writeFixed32: function(val) {
46772                 this.realloc(4);
46773                 writeInt32(this.buf, val, this.pos);
46774                 this.pos += 4;
46775             },
46776
46777             writeSFixed32: function(val) {
46778                 this.realloc(4);
46779                 writeInt32(this.buf, val, this.pos);
46780                 this.pos += 4;
46781             },
46782
46783             writeFixed64: function(val) {
46784                 this.realloc(8);
46785                 writeInt32(this.buf, val & -1, this.pos);
46786                 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
46787                 this.pos += 8;
46788             },
46789
46790             writeSFixed64: function(val) {
46791                 this.realloc(8);
46792                 writeInt32(this.buf, val & -1, this.pos);
46793                 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
46794                 this.pos += 8;
46795             },
46796
46797             writeVarint: function(val) {
46798                 val = +val || 0;
46799
46800                 if (val > 0xfffffff || val < 0) {
46801                     writeBigVarint(val, this);
46802                     return;
46803                 }
46804
46805                 this.realloc(4);
46806
46807                 this.buf[this.pos++] =           val & 0x7f  | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) { return; }
46808                 this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) { return; }
46809                 this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) { return; }
46810                 this.buf[this.pos++] =   (val >>> 7) & 0x7f;
46811             },
46812
46813             writeSVarint: function(val) {
46814                 this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2);
46815             },
46816
46817             writeBoolean: function(val) {
46818                 this.writeVarint(Boolean(val));
46819             },
46820
46821             writeString: function(str) {
46822                 str = String(str);
46823                 this.realloc(str.length * 4);
46824
46825                 this.pos++; // reserve 1 byte for short string length
46826
46827                 var startPos = this.pos;
46828                 // write the string directly to the buffer and see how much was written
46829                 this.pos = writeUtf8(this.buf, str, this.pos);
46830                 var len = this.pos - startPos;
46831
46832                 if (len >= 0x80) { makeRoomForExtraLength(startPos, len, this); }
46833
46834                 // finally, write the message length in the reserved place and restore the position
46835                 this.pos = startPos - 1;
46836                 this.writeVarint(len);
46837                 this.pos += len;
46838             },
46839
46840             writeFloat: function(val) {
46841                 this.realloc(4);
46842                 ieee754.write(this.buf, val, this.pos, true, 23, 4);
46843                 this.pos += 4;
46844             },
46845
46846             writeDouble: function(val) {
46847                 this.realloc(8);
46848                 ieee754.write(this.buf, val, this.pos, true, 52, 8);
46849                 this.pos += 8;
46850             },
46851
46852             writeBytes: function(buffer) {
46853                 var len = buffer.length;
46854                 this.writeVarint(len);
46855                 this.realloc(len);
46856                 for (var i = 0; i < len; i++) { this.buf[this.pos++] = buffer[i]; }
46857             },
46858
46859             writeRawMessage: function(fn, obj) {
46860                 this.pos++; // reserve 1 byte for short message length
46861
46862                 // write the message directly to the buffer and see how much was written
46863                 var startPos = this.pos;
46864                 fn(obj, this);
46865                 var len = this.pos - startPos;
46866
46867                 if (len >= 0x80) { makeRoomForExtraLength(startPos, len, this); }
46868
46869                 // finally, write the message length in the reserved place and restore the position
46870                 this.pos = startPos - 1;
46871                 this.writeVarint(len);
46872                 this.pos += len;
46873             },
46874
46875             writeMessage: function(tag, fn, obj) {
46876                 this.writeTag(tag, Pbf.Bytes);
46877                 this.writeRawMessage(fn, obj);
46878             },
46879
46880             writePackedVarint:   function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedVarint, arr); }   },
46881             writePackedSVarint:  function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedSVarint, arr); }  },
46882             writePackedBoolean:  function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedBoolean, arr); }  },
46883             writePackedFloat:    function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedFloat, arr); }    },
46884             writePackedDouble:   function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedDouble, arr); }   },
46885             writePackedFixed32:  function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedFixed32, arr); }  },
46886             writePackedSFixed32: function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedSFixed32, arr); } },
46887             writePackedFixed64:  function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedFixed64, arr); }  },
46888             writePackedSFixed64: function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedSFixed64, arr); } },
46889
46890             writeBytesField: function(tag, buffer) {
46891                 this.writeTag(tag, Pbf.Bytes);
46892                 this.writeBytes(buffer);
46893             },
46894             writeFixed32Field: function(tag, val) {
46895                 this.writeTag(tag, Pbf.Fixed32);
46896                 this.writeFixed32(val);
46897             },
46898             writeSFixed32Field: function(tag, val) {
46899                 this.writeTag(tag, Pbf.Fixed32);
46900                 this.writeSFixed32(val);
46901             },
46902             writeFixed64Field: function(tag, val) {
46903                 this.writeTag(tag, Pbf.Fixed64);
46904                 this.writeFixed64(val);
46905             },
46906             writeSFixed64Field: function(tag, val) {
46907                 this.writeTag(tag, Pbf.Fixed64);
46908                 this.writeSFixed64(val);
46909             },
46910             writeVarintField: function(tag, val) {
46911                 this.writeTag(tag, Pbf.Varint);
46912                 this.writeVarint(val);
46913             },
46914             writeSVarintField: function(tag, val) {
46915                 this.writeTag(tag, Pbf.Varint);
46916                 this.writeSVarint(val);
46917             },
46918             writeStringField: function(tag, str) {
46919                 this.writeTag(tag, Pbf.Bytes);
46920                 this.writeString(str);
46921             },
46922             writeFloatField: function(tag, val) {
46923                 this.writeTag(tag, Pbf.Fixed32);
46924                 this.writeFloat(val);
46925             },
46926             writeDoubleField: function(tag, val) {
46927                 this.writeTag(tag, Pbf.Fixed64);
46928                 this.writeDouble(val);
46929             },
46930             writeBooleanField: function(tag, val) {
46931                 this.writeVarintField(tag, Boolean(val));
46932             }
46933         };
46934
46935         function readVarintRemainder(l, s, p) {
46936             var buf = p.buf,
46937                 h, b;
46938
46939             b = buf[p.pos++]; h  = (b & 0x70) >> 4;  if (b < 0x80) { return toNum(l, h, s); }
46940             b = buf[p.pos++]; h |= (b & 0x7f) << 3;  if (b < 0x80) { return toNum(l, h, s); }
46941             b = buf[p.pos++]; h |= (b & 0x7f) << 10; if (b < 0x80) { return toNum(l, h, s); }
46942             b = buf[p.pos++]; h |= (b & 0x7f) << 17; if (b < 0x80) { return toNum(l, h, s); }
46943             b = buf[p.pos++]; h |= (b & 0x7f) << 24; if (b < 0x80) { return toNum(l, h, s); }
46944             b = buf[p.pos++]; h |= (b & 0x01) << 31; if (b < 0x80) { return toNum(l, h, s); }
46945
46946             throw new Error('Expected varint not more than 10 bytes');
46947         }
46948
46949         function readPackedEnd(pbf) {
46950             return pbf.type === Pbf.Bytes ?
46951                 pbf.readVarint() + pbf.pos : pbf.pos + 1;
46952         }
46953
46954         function toNum(low, high, isSigned) {
46955             if (isSigned) {
46956                 return high * 0x100000000 + (low >>> 0);
46957             }
46958
46959             return ((high >>> 0) * 0x100000000) + (low >>> 0);
46960         }
46961
46962         function writeBigVarint(val, pbf) {
46963             var low, high;
46964
46965             if (val >= 0) {
46966                 low  = (val % 0x100000000) | 0;
46967                 high = (val / 0x100000000) | 0;
46968             } else {
46969                 low  = ~(-val % 0x100000000);
46970                 high = ~(-val / 0x100000000);
46971
46972                 if (low ^ 0xffffffff) {
46973                     low = (low + 1) | 0;
46974                 } else {
46975                     low = 0;
46976                     high = (high + 1) | 0;
46977                 }
46978             }
46979
46980             if (val >= 0x10000000000000000 || val < -0x10000000000000000) {
46981                 throw new Error('Given varint doesn\'t fit into 10 bytes');
46982             }
46983
46984             pbf.realloc(10);
46985
46986             writeBigVarintLow(low, high, pbf);
46987             writeBigVarintHigh(high, pbf);
46988         }
46989
46990         function writeBigVarintLow(low, high, pbf) {
46991             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
46992             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
46993             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
46994             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
46995             pbf.buf[pbf.pos]   = low & 0x7f;
46996         }
46997
46998         function writeBigVarintHigh(high, pbf) {
46999             var lsb = (high & 0x07) << 4;
47000
47001             pbf.buf[pbf.pos++] |= lsb         | ((high >>>= 3) ? 0x80 : 0); if (!high) { return; }
47002             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) { return; }
47003             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) { return; }
47004             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) { return; }
47005             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) { return; }
47006             pbf.buf[pbf.pos++]  = high & 0x7f;
47007         }
47008
47009         function makeRoomForExtraLength(startPos, len, pbf) {
47010             var extraLen =
47011                 len <= 0x3fff ? 1 :
47012                 len <= 0x1fffff ? 2 :
47013                 len <= 0xfffffff ? 3 : Math.floor(Math.log(len) / (Math.LN2 * 7));
47014
47015             // if 1 byte isn't enough for encoding message length, shift the data to the right
47016             pbf.realloc(extraLen);
47017             for (var i = pbf.pos - 1; i >= startPos; i--) { pbf.buf[i + extraLen] = pbf.buf[i]; }
47018         }
47019
47020         function writePackedVarint(arr, pbf)   { for (var i = 0; i < arr.length; i++) { pbf.writeVarint(arr[i]); }   }
47021         function writePackedSVarint(arr, pbf)  { for (var i = 0; i < arr.length; i++) { pbf.writeSVarint(arr[i]); }  }
47022         function writePackedFloat(arr, pbf)    { for (var i = 0; i < arr.length; i++) { pbf.writeFloat(arr[i]); }    }
47023         function writePackedDouble(arr, pbf)   { for (var i = 0; i < arr.length; i++) { pbf.writeDouble(arr[i]); }   }
47024         function writePackedBoolean(arr, pbf)  { for (var i = 0; i < arr.length; i++) { pbf.writeBoolean(arr[i]); }  }
47025         function writePackedFixed32(arr, pbf)  { for (var i = 0; i < arr.length; i++) { pbf.writeFixed32(arr[i]); }  }
47026         function writePackedSFixed32(arr, pbf) { for (var i = 0; i < arr.length; i++) { pbf.writeSFixed32(arr[i]); } }
47027         function writePackedFixed64(arr, pbf)  { for (var i = 0; i < arr.length; i++) { pbf.writeFixed64(arr[i]); }  }
47028         function writePackedSFixed64(arr, pbf) { for (var i = 0; i < arr.length; i++) { pbf.writeSFixed64(arr[i]); } }
47029
47030         // Buffer code below from https://github.com/feross/buffer, MIT-licensed
47031
47032         function readUInt32(buf, pos) {
47033             return ((buf[pos]) |
47034                 (buf[pos + 1] << 8) |
47035                 (buf[pos + 2] << 16)) +
47036                 (buf[pos + 3] * 0x1000000);
47037         }
47038
47039         function writeInt32(buf, val, pos) {
47040             buf[pos] = val;
47041             buf[pos + 1] = (val >>> 8);
47042             buf[pos + 2] = (val >>> 16);
47043             buf[pos + 3] = (val >>> 24);
47044         }
47045
47046         function readInt32(buf, pos) {
47047             return ((buf[pos]) |
47048                 (buf[pos + 1] << 8) |
47049                 (buf[pos + 2] << 16)) +
47050                 (buf[pos + 3] << 24);
47051         }
47052
47053         function readUtf8(buf, pos, end) {
47054             var str = '';
47055             var i = pos;
47056
47057             while (i < end) {
47058                 var b0 = buf[i];
47059                 var c = null; // codepoint
47060                 var bytesPerSequence =
47061                     b0 > 0xEF ? 4 :
47062                     b0 > 0xDF ? 3 :
47063                     b0 > 0xBF ? 2 : 1;
47064
47065                 if (i + bytesPerSequence > end) { break; }
47066
47067                 var b1, b2, b3;
47068
47069                 if (bytesPerSequence === 1) {
47070                     if (b0 < 0x80) {
47071                         c = b0;
47072                     }
47073                 } else if (bytesPerSequence === 2) {
47074                     b1 = buf[i + 1];
47075                     if ((b1 & 0xC0) === 0x80) {
47076                         c = (b0 & 0x1F) << 0x6 | (b1 & 0x3F);
47077                         if (c <= 0x7F) {
47078                             c = null;
47079                         }
47080                     }
47081                 } else if (bytesPerSequence === 3) {
47082                     b1 = buf[i + 1];
47083                     b2 = buf[i + 2];
47084                     if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80) {
47085                         c = (b0 & 0xF) << 0xC | (b1 & 0x3F) << 0x6 | (b2 & 0x3F);
47086                         if (c <= 0x7FF || (c >= 0xD800 && c <= 0xDFFF)) {
47087                             c = null;
47088                         }
47089                     }
47090                 } else if (bytesPerSequence === 4) {
47091                     b1 = buf[i + 1];
47092                     b2 = buf[i + 2];
47093                     b3 = buf[i + 3];
47094                     if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) {
47095                         c = (b0 & 0xF) << 0x12 | (b1 & 0x3F) << 0xC | (b2 & 0x3F) << 0x6 | (b3 & 0x3F);
47096                         if (c <= 0xFFFF || c >= 0x110000) {
47097                             c = null;
47098                         }
47099                     }
47100                 }
47101
47102                 if (c === null) {
47103                     c = 0xFFFD;
47104                     bytesPerSequence = 1;
47105
47106                 } else if (c > 0xFFFF) {
47107                     c -= 0x10000;
47108                     str += String.fromCharCode(c >>> 10 & 0x3FF | 0xD800);
47109                     c = 0xDC00 | c & 0x3FF;
47110                 }
47111
47112                 str += String.fromCharCode(c);
47113                 i += bytesPerSequence;
47114             }
47115
47116             return str;
47117         }
47118
47119         function readUtf8TextDecoder(buf, pos, end) {
47120             return utf8TextDecoder.decode(buf.subarray(pos, end));
47121         }
47122
47123         function writeUtf8(buf, str, pos) {
47124             for (var i = 0, c, lead; i < str.length; i++) {
47125                 c = str.charCodeAt(i); // code point
47126
47127                 if (c > 0xD7FF && c < 0xE000) {
47128                     if (lead) {
47129                         if (c < 0xDC00) {
47130                             buf[pos++] = 0xEF;
47131                             buf[pos++] = 0xBF;
47132                             buf[pos++] = 0xBD;
47133                             lead = c;
47134                             continue;
47135                         } else {
47136                             c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000;
47137                             lead = null;
47138                         }
47139                     } else {
47140                         if (c > 0xDBFF || (i + 1 === str.length)) {
47141                             buf[pos++] = 0xEF;
47142                             buf[pos++] = 0xBF;
47143                             buf[pos++] = 0xBD;
47144                         } else {
47145                             lead = c;
47146                         }
47147                         continue;
47148                     }
47149                 } else if (lead) {
47150                     buf[pos++] = 0xEF;
47151                     buf[pos++] = 0xBF;
47152                     buf[pos++] = 0xBD;
47153                     lead = null;
47154                 }
47155
47156                 if (c < 0x80) {
47157                     buf[pos++] = c;
47158                 } else {
47159                     if (c < 0x800) {
47160                         buf[pos++] = c >> 0x6 | 0xC0;
47161                     } else {
47162                         if (c < 0x10000) {
47163                             buf[pos++] = c >> 0xC | 0xE0;
47164                         } else {
47165                             buf[pos++] = c >> 0x12 | 0xF0;
47166                             buf[pos++] = c >> 0xC & 0x3F | 0x80;
47167                         }
47168                         buf[pos++] = c >> 0x6 & 0x3F | 0x80;
47169                     }
47170                     buf[pos++] = c & 0x3F | 0x80;
47171                 }
47172             }
47173             return pos;
47174         }
47175
47176         var pointGeometry = Point;
47177
47178         /**
47179          * A standalone point geometry with useful accessor, comparison, and
47180          * modification methods.
47181          *
47182          * @class Point
47183          * @param {Number} x the x-coordinate. this could be longitude or screen
47184          * pixels, or any other sort of unit.
47185          * @param {Number} y the y-coordinate. this could be latitude or screen
47186          * pixels, or any other sort of unit.
47187          * @example
47188          * var point = new Point(-77, 38);
47189          */
47190         function Point(x, y) {
47191             this.x = x;
47192             this.y = y;
47193         }
47194
47195         Point.prototype = {
47196
47197             /**
47198              * Clone this point, returning a new point that can be modified
47199              * without affecting the old one.
47200              * @return {Point} the clone
47201              */
47202             clone: function() { return new Point(this.x, this.y); },
47203
47204             /**
47205              * Add this point's x & y coordinates to another point,
47206              * yielding a new point.
47207              * @param {Point} p the other point
47208              * @return {Point} output point
47209              */
47210             add:     function(p) { return this.clone()._add(p); },
47211
47212             /**
47213              * Subtract this point's x & y coordinates to from point,
47214              * yielding a new point.
47215              * @param {Point} p the other point
47216              * @return {Point} output point
47217              */
47218             sub:     function(p) { return this.clone()._sub(p); },
47219
47220             /**
47221              * Multiply this point's x & y coordinates by point,
47222              * yielding a new point.
47223              * @param {Point} p the other point
47224              * @return {Point} output point
47225              */
47226             multByPoint:    function(p) { return this.clone()._multByPoint(p); },
47227
47228             /**
47229              * Divide this point's x & y coordinates by point,
47230              * yielding a new point.
47231              * @param {Point} p the other point
47232              * @return {Point} output point
47233              */
47234             divByPoint:     function(p) { return this.clone()._divByPoint(p); },
47235
47236             /**
47237              * Multiply this point's x & y coordinates by a factor,
47238              * yielding a new point.
47239              * @param {Point} k factor
47240              * @return {Point} output point
47241              */
47242             mult:    function(k) { return this.clone()._mult(k); },
47243
47244             /**
47245              * Divide this point's x & y coordinates by a factor,
47246              * yielding a new point.
47247              * @param {Point} k factor
47248              * @return {Point} output point
47249              */
47250             div:     function(k) { return this.clone()._div(k); },
47251
47252             /**
47253              * Rotate this point around the 0, 0 origin by an angle a,
47254              * given in radians
47255              * @param {Number} a angle to rotate around, in radians
47256              * @return {Point} output point
47257              */
47258             rotate:  function(a) { return this.clone()._rotate(a); },
47259
47260             /**
47261              * Rotate this point around p point by an angle a,
47262              * given in radians
47263              * @param {Number} a angle to rotate around, in radians
47264              * @param {Point} p Point to rotate around
47265              * @return {Point} output point
47266              */
47267             rotateAround:  function(a,p) { return this.clone()._rotateAround(a,p); },
47268
47269             /**
47270              * Multiply this point by a 4x1 transformation matrix
47271              * @param {Array<Number>} m transformation matrix
47272              * @return {Point} output point
47273              */
47274             matMult: function(m) { return this.clone()._matMult(m); },
47275
47276             /**
47277              * Calculate this point but as a unit vector from 0, 0, meaning
47278              * that the distance from the resulting point to the 0, 0
47279              * coordinate will be equal to 1 and the angle from the resulting
47280              * point to the 0, 0 coordinate will be the same as before.
47281              * @return {Point} unit vector point
47282              */
47283             unit:    function() { return this.clone()._unit(); },
47284
47285             /**
47286              * Compute a perpendicular point, where the new y coordinate
47287              * is the old x coordinate and the new x coordinate is the old y
47288              * coordinate multiplied by -1
47289              * @return {Point} perpendicular point
47290              */
47291             perp:    function() { return this.clone()._perp(); },
47292
47293             /**
47294              * Return a version of this point with the x & y coordinates
47295              * rounded to integers.
47296              * @return {Point} rounded point
47297              */
47298             round:   function() { return this.clone()._round(); },
47299
47300             /**
47301              * Return the magitude of this point: this is the Euclidean
47302              * distance from the 0, 0 coordinate to this point's x and y
47303              * coordinates.
47304              * @return {Number} magnitude
47305              */
47306             mag: function() {
47307                 return Math.sqrt(this.x * this.x + this.y * this.y);
47308             },
47309
47310             /**
47311              * Judge whether this point is equal to another point, returning
47312              * true or false.
47313              * @param {Point} other the other point
47314              * @return {boolean} whether the points are equal
47315              */
47316             equals: function(other) {
47317                 return this.x === other.x &&
47318                        this.y === other.y;
47319             },
47320
47321             /**
47322              * Calculate the distance from this point to another point
47323              * @param {Point} p the other point
47324              * @return {Number} distance
47325              */
47326             dist: function(p) {
47327                 return Math.sqrt(this.distSqr(p));
47328             },
47329
47330             /**
47331              * Calculate the distance from this point to another point,
47332              * without the square root step. Useful if you're comparing
47333              * relative distances.
47334              * @param {Point} p the other point
47335              * @return {Number} distance
47336              */
47337             distSqr: function(p) {
47338                 var dx = p.x - this.x,
47339                     dy = p.y - this.y;
47340                 return dx * dx + dy * dy;
47341             },
47342
47343             /**
47344              * Get the angle from the 0, 0 coordinate to this point, in radians
47345              * coordinates.
47346              * @return {Number} angle
47347              */
47348             angle: function() {
47349                 return Math.atan2(this.y, this.x);
47350             },
47351
47352             /**
47353              * Get the angle from this point to another point, in radians
47354              * @param {Point} b the other point
47355              * @return {Number} angle
47356              */
47357             angleTo: function(b) {
47358                 return Math.atan2(this.y - b.y, this.x - b.x);
47359             },
47360
47361             /**
47362              * Get the angle between this point and another point, in radians
47363              * @param {Point} b the other point
47364              * @return {Number} angle
47365              */
47366             angleWith: function(b) {
47367                 return this.angleWithSep(b.x, b.y);
47368             },
47369
47370             /*
47371              * Find the angle of the two vectors, solving the formula for
47372              * the cross product a x b = |a||b|sin(θ) for θ.
47373              * @param {Number} x the x-coordinate
47374              * @param {Number} y the y-coordinate
47375              * @return {Number} the angle in radians
47376              */
47377             angleWithSep: function(x, y) {
47378                 return Math.atan2(
47379                     this.x * y - this.y * x,
47380                     this.x * x + this.y * y);
47381             },
47382
47383             _matMult: function(m) {
47384                 var x = m[0] * this.x + m[1] * this.y,
47385                     y = m[2] * this.x + m[3] * this.y;
47386                 this.x = x;
47387                 this.y = y;
47388                 return this;
47389             },
47390
47391             _add: function(p) {
47392                 this.x += p.x;
47393                 this.y += p.y;
47394                 return this;
47395             },
47396
47397             _sub: function(p) {
47398                 this.x -= p.x;
47399                 this.y -= p.y;
47400                 return this;
47401             },
47402
47403             _mult: function(k) {
47404                 this.x *= k;
47405                 this.y *= k;
47406                 return this;
47407             },
47408
47409             _div: function(k) {
47410                 this.x /= k;
47411                 this.y /= k;
47412                 return this;
47413             },
47414
47415             _multByPoint: function(p) {
47416                 this.x *= p.x;
47417                 this.y *= p.y;
47418                 return this;
47419             },
47420
47421             _divByPoint: function(p) {
47422                 this.x /= p.x;
47423                 this.y /= p.y;
47424                 return this;
47425             },
47426
47427             _unit: function() {
47428                 this._div(this.mag());
47429                 return this;
47430             },
47431
47432             _perp: function() {
47433                 var y = this.y;
47434                 this.y = this.x;
47435                 this.x = -y;
47436                 return this;
47437             },
47438
47439             _rotate: function(angle) {
47440                 var cos = Math.cos(angle),
47441                     sin = Math.sin(angle),
47442                     x = cos * this.x - sin * this.y,
47443                     y = sin * this.x + cos * this.y;
47444                 this.x = x;
47445                 this.y = y;
47446                 return this;
47447             },
47448
47449             _rotateAround: function(angle, p) {
47450                 var cos = Math.cos(angle),
47451                     sin = Math.sin(angle),
47452                     x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y),
47453                     y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y);
47454                 this.x = x;
47455                 this.y = y;
47456                 return this;
47457             },
47458
47459             _round: function() {
47460                 this.x = Math.round(this.x);
47461                 this.y = Math.round(this.y);
47462                 return this;
47463             }
47464         };
47465
47466         /**
47467          * Construct a point from an array if necessary, otherwise if the input
47468          * is already a Point, or an unknown type, return it unchanged
47469          * @param {Array<Number>|Point|*} a any kind of input value
47470          * @return {Point} constructed point, or passed-through value.
47471          * @example
47472          * // this
47473          * var point = Point.convert([0, 1]);
47474          * // is equivalent to
47475          * var point = new Point(0, 1);
47476          */
47477         Point.convert = function (a) {
47478             if (a instanceof Point) {
47479                 return a;
47480             }
47481             if (Array.isArray(a)) {
47482                 return new Point(a[0], a[1]);
47483             }
47484             return a;
47485         };
47486
47487         var vectortilefeature = VectorTileFeature;
47488
47489         function VectorTileFeature(pbf, end, extent, keys, values) {
47490             // Public
47491             this.properties = {};
47492             this.extent = extent;
47493             this.type = 0;
47494
47495             // Private
47496             this._pbf = pbf;
47497             this._geometry = -1;
47498             this._keys = keys;
47499             this._values = values;
47500
47501             pbf.readFields(readFeature, this, end);
47502         }
47503
47504         function readFeature(tag, feature, pbf) {
47505             if (tag == 1) { feature.id = pbf.readVarint(); }
47506             else if (tag == 2) { readTag(pbf, feature); }
47507             else if (tag == 3) { feature.type = pbf.readVarint(); }
47508             else if (tag == 4) { feature._geometry = pbf.pos; }
47509         }
47510
47511         function readTag(pbf, feature) {
47512             var end = pbf.readVarint() + pbf.pos;
47513
47514             while (pbf.pos < end) {
47515                 var key = feature._keys[pbf.readVarint()],
47516                     value = feature._values[pbf.readVarint()];
47517                 feature.properties[key] = value;
47518             }
47519         }
47520
47521         VectorTileFeature.types = ['Unknown', 'Point', 'LineString', 'Polygon'];
47522
47523         VectorTileFeature.prototype.loadGeometry = function() {
47524             var pbf = this._pbf;
47525             pbf.pos = this._geometry;
47526
47527             var end = pbf.readVarint() + pbf.pos,
47528                 cmd = 1,
47529                 length = 0,
47530                 x = 0,
47531                 y = 0,
47532                 lines = [],
47533                 line;
47534
47535             while (pbf.pos < end) {
47536                 if (length <= 0) {
47537                     var cmdLen = pbf.readVarint();
47538                     cmd = cmdLen & 0x7;
47539                     length = cmdLen >> 3;
47540                 }
47541
47542                 length--;
47543
47544                 if (cmd === 1 || cmd === 2) {
47545                     x += pbf.readSVarint();
47546                     y += pbf.readSVarint();
47547
47548                     if (cmd === 1) { // moveTo
47549                         if (line) { lines.push(line); }
47550                         line = [];
47551                     }
47552
47553                     line.push(new pointGeometry(x, y));
47554
47555                 } else if (cmd === 7) {
47556
47557                     // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90
47558                     if (line) {
47559                         line.push(line[0].clone()); // closePolygon
47560                     }
47561
47562                 } else {
47563                     throw new Error('unknown command ' + cmd);
47564                 }
47565             }
47566
47567             if (line) { lines.push(line); }
47568
47569             return lines;
47570         };
47571
47572         VectorTileFeature.prototype.bbox = function() {
47573             var pbf = this._pbf;
47574             pbf.pos = this._geometry;
47575
47576             var end = pbf.readVarint() + pbf.pos,
47577                 cmd = 1,
47578                 length = 0,
47579                 x = 0,
47580                 y = 0,
47581                 x1 = Infinity,
47582                 x2 = -Infinity,
47583                 y1 = Infinity,
47584                 y2 = -Infinity;
47585
47586             while (pbf.pos < end) {
47587                 if (length <= 0) {
47588                     var cmdLen = pbf.readVarint();
47589                     cmd = cmdLen & 0x7;
47590                     length = cmdLen >> 3;
47591                 }
47592
47593                 length--;
47594
47595                 if (cmd === 1 || cmd === 2) {
47596                     x += pbf.readSVarint();
47597                     y += pbf.readSVarint();
47598                     if (x < x1) { x1 = x; }
47599                     if (x > x2) { x2 = x; }
47600                     if (y < y1) { y1 = y; }
47601                     if (y > y2) { y2 = y; }
47602
47603                 } else if (cmd !== 7) {
47604                     throw new Error('unknown command ' + cmd);
47605                 }
47606             }
47607
47608             return [x1, y1, x2, y2];
47609         };
47610
47611         VectorTileFeature.prototype.toGeoJSON = function(x, y, z) {
47612             var size = this.extent * Math.pow(2, z),
47613                 x0 = this.extent * x,
47614                 y0 = this.extent * y,
47615                 coords = this.loadGeometry(),
47616                 type = VectorTileFeature.types[this.type],
47617                 i, j;
47618
47619             function project(line) {
47620                 for (var j = 0; j < line.length; j++) {
47621                     var p = line[j], y2 = 180 - (p.y + y0) * 360 / size;
47622                     line[j] = [
47623                         (p.x + x0) * 360 / size - 180,
47624                         360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90
47625                     ];
47626                 }
47627             }
47628
47629             switch (this.type) {
47630             case 1:
47631                 var points = [];
47632                 for (i = 0; i < coords.length; i++) {
47633                     points[i] = coords[i][0];
47634                 }
47635                 coords = points;
47636                 project(coords);
47637                 break;
47638
47639             case 2:
47640                 for (i = 0; i < coords.length; i++) {
47641                     project(coords[i]);
47642                 }
47643                 break;
47644
47645             case 3:
47646                 coords = classifyRings(coords);
47647                 for (i = 0; i < coords.length; i++) {
47648                     for (j = 0; j < coords[i].length; j++) {
47649                         project(coords[i][j]);
47650                     }
47651                 }
47652                 break;
47653             }
47654
47655             if (coords.length === 1) {
47656                 coords = coords[0];
47657             } else {
47658                 type = 'Multi' + type;
47659             }
47660
47661             var result = {
47662                 type: "Feature",
47663                 geometry: {
47664                     type: type,
47665                     coordinates: coords
47666                 },
47667                 properties: this.properties
47668             };
47669
47670             if ('id' in this) {
47671                 result.id = this.id;
47672             }
47673
47674             return result;
47675         };
47676
47677         // classifies an array of rings into polygons with outer rings and holes
47678
47679         function classifyRings(rings) {
47680             var len = rings.length;
47681
47682             if (len <= 1) { return [rings]; }
47683
47684             var polygons = [],
47685                 polygon,
47686                 ccw;
47687
47688             for (var i = 0; i < len; i++) {
47689                 var area = signedArea$1(rings[i]);
47690                 if (area === 0) { continue; }
47691
47692                 if (ccw === undefined) { ccw = area < 0; }
47693
47694                 if (ccw === area < 0) {
47695                     if (polygon) { polygons.push(polygon); }
47696                     polygon = [rings[i]];
47697
47698                 } else {
47699                     polygon.push(rings[i]);
47700                 }
47701             }
47702             if (polygon) { polygons.push(polygon); }
47703
47704             return polygons;
47705         }
47706
47707         function signedArea$1(ring) {
47708             var sum = 0;
47709             for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
47710                 p1 = ring[i];
47711                 p2 = ring[j];
47712                 sum += (p2.x - p1.x) * (p1.y + p2.y);
47713             }
47714             return sum;
47715         }
47716
47717         var vectortilelayer = VectorTileLayer;
47718
47719         function VectorTileLayer(pbf, end) {
47720             // Public
47721             this.version = 1;
47722             this.name = null;
47723             this.extent = 4096;
47724             this.length = 0;
47725
47726             // Private
47727             this._pbf = pbf;
47728             this._keys = [];
47729             this._values = [];
47730             this._features = [];
47731
47732             pbf.readFields(readLayer, this, end);
47733
47734             this.length = this._features.length;
47735         }
47736
47737         function readLayer(tag, layer, pbf) {
47738             if (tag === 15) { layer.version = pbf.readVarint(); }
47739             else if (tag === 1) { layer.name = pbf.readString(); }
47740             else if (tag === 5) { layer.extent = pbf.readVarint(); }
47741             else if (tag === 2) { layer._features.push(pbf.pos); }
47742             else if (tag === 3) { layer._keys.push(pbf.readString()); }
47743             else if (tag === 4) { layer._values.push(readValueMessage(pbf)); }
47744         }
47745
47746         function readValueMessage(pbf) {
47747             var value = null,
47748                 end = pbf.readVarint() + pbf.pos;
47749
47750             while (pbf.pos < end) {
47751                 var tag = pbf.readVarint() >> 3;
47752
47753                 value = tag === 1 ? pbf.readString() :
47754                     tag === 2 ? pbf.readFloat() :
47755                     tag === 3 ? pbf.readDouble() :
47756                     tag === 4 ? pbf.readVarint64() :
47757                     tag === 5 ? pbf.readVarint() :
47758                     tag === 6 ? pbf.readSVarint() :
47759                     tag === 7 ? pbf.readBoolean() : null;
47760             }
47761
47762             return value;
47763         }
47764
47765         // return feature `i` from this layer as a `VectorTileFeature`
47766         VectorTileLayer.prototype.feature = function(i) {
47767             if (i < 0 || i >= this._features.length) { throw new Error('feature index out of bounds'); }
47768
47769             this._pbf.pos = this._features[i];
47770
47771             var end = this._pbf.readVarint() + this._pbf.pos;
47772             return new vectortilefeature(this._pbf, end, this.extent, this._keys, this._values);
47773         };
47774
47775         var vectortile = VectorTile;
47776
47777         function VectorTile(pbf, end) {
47778             this.layers = pbf.readFields(readTile, {}, end);
47779         }
47780
47781         function readTile(tag, layers, pbf) {
47782             if (tag === 3) {
47783                 var layer = new vectortilelayer(pbf, pbf.readVarint() + pbf.pos);
47784                 if (layer.length) { layers[layer.name] = layer; }
47785             }
47786         }
47787
47788         var VectorTile$1 = vectortile;
47789         var VectorTileFeature$1 = vectortilefeature;
47790         var VectorTileLayer$1 = vectortilelayer;
47791
47792         var vectorTile = {
47793                 VectorTile: VectorTile$1,
47794                 VectorTileFeature: VectorTileFeature$1,
47795                 VectorTileLayer: VectorTileLayer$1
47796         };
47797
47798         var tiler$7 = utilTiler().tileSize(512).margin(1);
47799         var dispatch$8 = dispatch('loadedData');
47800         var _vtCache;
47801
47802
47803         function abortRequest$7(controller) {
47804             controller.abort();
47805         }
47806
47807
47808         function vtToGeoJSON(data, tile, mergeCache) {
47809             var vectorTile$1 = new vectorTile.VectorTile(new pbf(data));
47810             var layers = Object.keys(vectorTile$1.layers);
47811             if (!Array.isArray(layers)) { layers = [layers]; }
47812
47813             var features = [];
47814             layers.forEach(function(layerID) {
47815                 var layer = vectorTile$1.layers[layerID];
47816                 if (layer) {
47817                     for (var i = 0; i < layer.length; i++) {
47818                         var feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
47819                         var geometry = feature.geometry;
47820
47821                         // Treat all Polygons as MultiPolygons
47822                         if (geometry.type === 'Polygon') {
47823                             geometry.type = 'MultiPolygon';
47824                             geometry.coordinates = [geometry.coordinates];
47825                         }
47826
47827                         // Clip to tile bounds
47828                         if (geometry.type === 'MultiPolygon') {
47829                             var isClipped = false;
47830                             var featureClip = bboxClip_1(feature, tile.extent.rectangle());
47831                             if (!fastDeepEqual(feature.geometry, featureClip.geometry)) {
47832                                 // feature = featureClip;
47833                                 isClipped = true;
47834                             }
47835                             if (!feature.geometry.coordinates.length) { continue; }   // not actually on this tile
47836                             if (!feature.geometry.coordinates[0].length) { continue; }   // not actually on this tile
47837                         }
47838
47839                         // Generate some unique IDs and add some metadata
47840                         var featurehash = utilHashcode(fastJsonStableStringify(feature));
47841                         var propertyhash = utilHashcode(fastJsonStableStringify(feature.properties || {}));
47842                         feature.__layerID__ = layerID.replace(/[^_a-zA-Z0-9\-]/g, '_');
47843                         feature.__featurehash__ = featurehash;
47844                         feature.__propertyhash__ = propertyhash;
47845                         features.push(feature);
47846
47847                         // Clipped Polygons at same zoom with identical properties can get merged
47848                         if (isClipped && geometry.type === 'MultiPolygon') {
47849                             var merged = mergeCache[propertyhash];
47850                             if (merged && merged.length) {
47851                                 var other = merged[0];
47852                                 var coords = union(
47853                                     feature.geometry.coordinates,
47854                                     other.geometry.coordinates
47855                                 );
47856
47857                                 if (!coords || !coords.length) {
47858                                     continue;  // something failed in martinez union
47859                                 }
47860
47861                                 merged.push(feature);
47862                                 for (var j = 0; j < merged.length; j++) {      // all these features get...
47863                                     merged[j].geometry.coordinates = coords;   // same coords
47864                                     merged[j].__featurehash__ = featurehash;   // same hash, so deduplication works
47865                                 }
47866                             } else {
47867                                 mergeCache[propertyhash] = [feature];
47868                             }
47869                         }
47870                     }
47871                 }
47872             });
47873
47874             return features;
47875         }
47876
47877
47878         function loadTile(source, tile) {
47879             if (source.loaded[tile.id] || source.inflight[tile.id]) { return; }
47880
47881             var url = source.template
47882                 .replace('{x}', tile.xyz[0])
47883                 .replace('{y}', tile.xyz[1])
47884                 // TMS-flipped y coordinate
47885                 .replace(/\{[t-]y\}/, Math.pow(2, tile.xyz[2]) - tile.xyz[1] - 1)
47886                 .replace(/\{z(oom)?\}/, tile.xyz[2])
47887                 .replace(/\{switch:([^}]+)\}/, function(s, r) {
47888                     var subdomains = r.split(',');
47889                     return subdomains[(tile.xyz[0] + tile.xyz[1]) % subdomains.length];
47890                 });
47891
47892
47893             var controller = new AbortController();
47894             source.inflight[tile.id] = controller;
47895
47896             fetch(url, { signal: controller.signal })
47897                 .then(function(response) {
47898                     if (!response.ok) {
47899                         throw new Error(response.status + ' ' + response.statusText);
47900                     }
47901                     source.loaded[tile.id] = [];
47902                     delete source.inflight[tile.id];
47903                     return response.arrayBuffer();
47904                 })
47905                 .then(function(data) {
47906                     if (!data) {
47907                         throw new Error('No Data');
47908                     }
47909
47910                     var z = tile.xyz[2];
47911                     if (!source.canMerge[z]) {
47912                         source.canMerge[z] = {};  // initialize mergeCache
47913                     }
47914
47915                     source.loaded[tile.id] = vtToGeoJSON(data, tile, source.canMerge[z]);
47916                     dispatch$8.call('loadedData');
47917                 })
47918                 .catch(function() {
47919                     source.loaded[tile.id] = [];
47920                     delete source.inflight[tile.id];
47921                 });
47922         }
47923
47924
47925         var serviceVectorTile = {
47926
47927             init: function() {
47928                 if (!_vtCache) {
47929                     this.reset();
47930                 }
47931
47932                 this.event = utilRebind(this, dispatch$8, 'on');
47933             },
47934
47935
47936             reset: function() {
47937                 for (var sourceID in _vtCache) {
47938                     var source = _vtCache[sourceID];
47939                     if (source && source.inflight) {
47940                         Object.values(source.inflight).forEach(abortRequest$7);
47941                     }
47942                 }
47943
47944                 _vtCache = {};
47945             },
47946
47947
47948             addSource: function(sourceID, template) {
47949                 _vtCache[sourceID] = { template: template, inflight: {}, loaded: {}, canMerge: {} };
47950                 return _vtCache[sourceID];
47951             },
47952
47953
47954             data: function(sourceID, projection) {
47955                 var source = _vtCache[sourceID];
47956                 if (!source) { return []; }
47957
47958                 var tiles = tiler$7.getTiles(projection);
47959                 var seen = {};
47960                 var results = [];
47961
47962                 for (var i = 0; i < tiles.length; i++) {
47963                     var features = source.loaded[tiles[i].id];
47964                     if (!features || !features.length) { continue; }
47965
47966                     for (var j = 0; j < features.length; j++) {
47967                         var feature = features[j];
47968                         var hash = feature.__featurehash__;
47969                         if (seen[hash]) { continue; }
47970                         seen[hash] = true;
47971
47972                         // return a shallow copy, because the hash may change
47973                         // later if this feature gets merged with another
47974                         results.push(Object.assign({}, feature));  // shallow copy
47975                     }
47976                 }
47977
47978                 return results;
47979             },
47980
47981
47982             loadTiles: function(sourceID, template, projection) {
47983                 var source = _vtCache[sourceID];
47984                 if (!source) {
47985                     source = this.addSource(sourceID, template);
47986                 }
47987
47988                 var tiles = tiler$7.getTiles(projection);
47989
47990                 // abort inflight requests that are no longer needed
47991                 Object.keys(source.inflight).forEach(function(k) {
47992                     var wanted = tiles.find(function(tile) { return k === tile.id; });
47993                     if (!wanted) {
47994                         abortRequest$7(source.inflight[k]);
47995                         delete source.inflight[k];
47996                     }
47997                 });
47998
47999                 tiles.forEach(function(tile) {
48000                     loadTile(source, tile);
48001                 });
48002             },
48003
48004
48005             cache: function() {
48006                 return _vtCache;
48007             }
48008
48009         };
48010
48011         var apibase$5 = 'https://www.wikidata.org/w/api.php?';
48012         var _wikidataCache = {};
48013
48014
48015         var serviceWikidata = {
48016
48017             init: function() {},
48018
48019             reset: function() {
48020                 _wikidataCache = {};
48021             },
48022
48023
48024             // Search for Wikidata items matching the query
48025             itemsForSearchQuery: function(query, callback) {
48026                 if (!query) {
48027                     if (callback) { callback('No query', {}); }
48028                     return;
48029                 }
48030
48031                 var lang = this.languagesToQuery()[0];
48032
48033                 var url = apibase$5 + utilQsString({
48034                     action: 'wbsearchentities',
48035                     format: 'json',
48036                     formatversion: 2,
48037                     search: query,
48038                     type: 'item',
48039                     // the language to search
48040                     language: lang,
48041                     // the langauge for the label and description in the result
48042                     uselang: lang,
48043                     limit: 10,
48044                     origin: '*'
48045                 });
48046
48047                 d3_json(url)
48048                     .then(function(result) {
48049                         if (result && result.error) {
48050                             throw new Error(result.error);
48051                         }
48052                         if (callback) { callback(null, result.search || {}); }
48053                     })
48054                     .catch(function(err) {
48055                         if (callback) { callback(err.message, {}); }
48056                     });
48057             },
48058
48059
48060             // Given a Wikipedia language and article title,
48061             // return an array of corresponding Wikidata entities.
48062             itemsByTitle: function(lang, title, callback) {
48063                 if (!title) {
48064                     if (callback) { callback('No title', {}); }
48065                     return;
48066                 }
48067
48068                 lang = lang || 'en';
48069                 var url = apibase$5 + utilQsString({
48070                     action: 'wbgetentities',
48071                     format: 'json',
48072                     formatversion: 2,
48073                     sites: lang.replace(/-/g, '_') + 'wiki',
48074                     titles: title,
48075                     languages: 'en', // shrink response by filtering to one language
48076                     origin: '*'
48077                 });
48078
48079                 d3_json(url)
48080                     .then(function(result) {
48081                         if (result && result.error) {
48082                             throw new Error(result.error);
48083                         }
48084                         if (callback) { callback(null, result.entities || {}); }
48085                     })
48086                     .catch(function(err) {
48087                         if (callback) { callback(err.message, {}); }
48088                     });
48089             },
48090
48091
48092             languagesToQuery: function() {
48093                 var localeCode = _mainLocalizer.localeCode().toLowerCase();
48094                 // HACK: en-us isn't a wikidata language. We should really be filtering by
48095                 // the languages known to be supported by wikidata.
48096                 if (localeCode === 'en-us') { localeCode = 'en'; }
48097                 return utilArrayUniq([
48098                     localeCode,
48099                     _mainLocalizer.languageCode().toLowerCase(),
48100                     'en'
48101                 ]);
48102             },
48103
48104
48105             entityByQID: function(qid, callback) {
48106                 if (!qid) {
48107                     callback('No qid', {});
48108                     return;
48109                 }
48110                 if (_wikidataCache[qid]) {
48111                     if (callback) { callback(null, _wikidataCache[qid]); }
48112                     return;
48113                 }
48114
48115                 var langs = this.languagesToQuery();
48116                 var url = apibase$5 + utilQsString({
48117                     action: 'wbgetentities',
48118                     format: 'json',
48119                     formatversion: 2,
48120                     ids: qid,
48121                     props: 'labels|descriptions|claims|sitelinks',
48122                     sitefilter: langs.map(function(d) { return d + 'wiki'; }).join('|'),
48123                     languages: langs.join('|'),
48124                     languagefallback: 1,
48125                     origin: '*'
48126                 });
48127
48128                 d3_json(url)
48129                     .then(function(result) {
48130                         if (result && result.error) {
48131                             throw new Error(result.error);
48132                         }
48133                         if (callback) { callback(null, result.entities[qid] || {}); }
48134                     })
48135                     .catch(function(err) {
48136                         if (callback) { callback(err.message, {}); }
48137                     });
48138             },
48139
48140
48141             // Pass `params` object of the form:
48142             // {
48143             //   qid: 'string'      // brand wikidata  (e.g. 'Q37158')
48144             // }
48145             //
48146             // Get an result object used to display tag documentation
48147             // {
48148             //   title:        'string',
48149             //   description:  'string',
48150             //   editURL:      'string',
48151             //   imageURL:     'string',
48152             //   wiki:         { title: 'string', text: 'string', url: 'string' }
48153             // }
48154             //
48155             getDocs: function(params, callback) {
48156                 var langs = this.languagesToQuery();
48157                 this.entityByQID(params.qid, function(err, entity) {
48158                     if (err || !entity) {
48159                         callback(err || 'No entity');
48160                         return;
48161                     }
48162
48163                     var i;
48164                     var description;
48165                     if (entity.descriptions && Object.keys(entity.descriptions).length > 0) {
48166                         description = entity.descriptions[Object.keys(entity.descriptions)[0]].value;
48167                     }
48168
48169                     // prepare result
48170                     var result = {
48171                         title: entity.id,
48172                         description: description,
48173                         editURL: 'https://www.wikidata.org/wiki/' + entity.id
48174                     };
48175
48176                     // add image
48177                     if (entity.claims) {
48178                         var imageroot = 'https://commons.wikimedia.org/w/index.php';
48179                         var props = ['P154','P18'];  // logo image, image
48180                         var prop, image;
48181                         for (i = 0; i < props.length; i++) {
48182                             prop = entity.claims[props[i]];
48183                             if (prop && Object.keys(prop).length > 0) {
48184                                 image = prop[Object.keys(prop)[0]].mainsnak.datavalue.value;
48185                                 if (image) {
48186                                     result.imageURL = imageroot + '?' + utilQsString({
48187                                         title: 'Special:Redirect/file/' + image,
48188                                         width: 400
48189                                     });
48190                                     break;
48191                                 }
48192                             }
48193                         }
48194                     }
48195
48196                     if (entity.sitelinks) {
48197                         var englishLocale = _mainLocalizer.languageCode().toLowerCase() === 'en';
48198
48199                         // must be one of these that we requested..
48200                         for (i = 0; i < langs.length; i++) {   // check each, in order of preference
48201                             var w = langs[i] + 'wiki';
48202                             if (entity.sitelinks[w]) {
48203                                 var title = entity.sitelinks[w].title;
48204                                 var tKey = 'inspector.wiki_reference';
48205                                 if (!englishLocale && langs[i] === 'en') {   // user's locale isn't English but
48206                                     tKey = 'inspector.wiki_en_reference';    // we are sending them to enwiki anyway..
48207                                 }
48208
48209                                 result.wiki = {
48210                                     title: title,
48211                                     text: tKey,
48212                                     url: 'https://' + langs[i] + '.wikipedia.org/wiki/' + title.replace(/ /g, '_')
48213                                 };
48214                                 break;
48215                             }
48216                         }
48217                     }
48218
48219                     callback(null, result);
48220                 });
48221             }
48222
48223         };
48224
48225         var endpoint = 'https://en.wikipedia.org/w/api.php?';
48226
48227         var serviceWikipedia = {
48228
48229             init: function() {},
48230             reset: function() {},
48231
48232
48233             search: function(lang, query, callback) {
48234                 if (!query) {
48235                     if (callback) { callback('No Query', []); }
48236                     return;
48237                 }
48238
48239                 lang = lang || 'en';
48240                 var url = endpoint.replace('en', lang) +
48241                     utilQsString({
48242                         action: 'query',
48243                         list: 'search',
48244                         srlimit: '10',
48245                         srinfo: 'suggestion',
48246                         format: 'json',
48247                         origin: '*',
48248                         srsearch: query
48249                     });
48250
48251                 d3_json(url)
48252                     .then(function(result) {
48253                         if (result && result.error) {
48254                             throw new Error(result.error);
48255                         } else if (!result || !result.query || !result.query.search) {
48256                             throw new Error('No Results');
48257                         }
48258                         if (callback) {
48259                             var titles = result.query.search.map(function(d) { return d.title; });
48260                             callback(null, titles);
48261                         }
48262                     })
48263                     .catch(function(err) {
48264                         if (callback) { callback(err, []); }
48265                     });
48266             },
48267
48268
48269             suggestions: function(lang, query, callback) {
48270                 if (!query) {
48271                     if (callback) { callback('', []); }
48272                     return;
48273                 }
48274
48275                 lang = lang || 'en';
48276                 var url = endpoint.replace('en', lang) +
48277                     utilQsString({
48278                         action: 'opensearch',
48279                         namespace: 0,
48280                         suggest: '',
48281                         format: 'json',
48282                         origin: '*',
48283                         search: query
48284                     });
48285
48286                 d3_json(url)
48287                     .then(function(result) {
48288                         if (result && result.error) {
48289                             throw new Error(result.error);
48290                         } else if (!result || result.length < 2) {
48291                             throw new Error('No Results');
48292                         }
48293                         if (callback) { callback(null, result[1] || []); }
48294                     })
48295                     .catch(function(err) {
48296                         if (callback) { callback(err.message, []); }
48297                     });
48298             },
48299
48300
48301             translations: function(lang, title, callback) {
48302                 if (!title) {
48303                     if (callback) { callback('No Title'); }
48304                     return;
48305                 }
48306
48307                 var url = endpoint.replace('en', lang) +
48308                     utilQsString({
48309                         action: 'query',
48310                         prop: 'langlinks',
48311                         format: 'json',
48312                         origin: '*',
48313                         lllimit: 500,
48314                         titles: title
48315                     });
48316
48317                 d3_json(url)
48318                     .then(function(result) {
48319                         if (result && result.error) {
48320                             throw new Error(result.error);
48321                         } else if (!result || !result.query || !result.query.pages) {
48322                             throw new Error('No Results');
48323                         }
48324                         if (callback) {
48325                             var list = result.query.pages[Object.keys(result.query.pages)[0]];
48326                             var translations = {};
48327                             if (list && list.langlinks) {
48328                                 list.langlinks.forEach(function(d) { translations[d.lang] = d['*']; });
48329                             }
48330                             callback(null, translations);
48331                         }
48332                     })
48333                     .catch(function(err) {
48334                         if (callback) { callback(err.message); }
48335                     });
48336             }
48337
48338         };
48339
48340         var services = {
48341             geocoder: serviceNominatim,
48342             keepRight: serviceKeepRight,
48343             improveOSM: serviceImproveOSM,
48344             osmose: serviceOsmose,
48345             mapillary: serviceMapillary,
48346             openstreetcam: serviceOpenstreetcam,
48347             osm: serviceOsm,
48348             osmWikibase: serviceOsmWikibase,
48349             maprules: serviceMapRules,
48350             streetside: serviceStreetside,
48351             taginfo: serviceTaginfo,
48352             vectorTile: serviceVectorTile,
48353             wikidata: serviceWikidata,
48354             wikipedia: serviceWikipedia
48355         };
48356
48357         function svgIcon(name, svgklass, useklass) {
48358             return function drawIcon(selection) {
48359                 selection.selectAll('svg.icon' + (svgklass ? '.' + svgklass.split(' ')[0] : ''))
48360                     .data([0])
48361                     .enter()
48362                     .append('svg')
48363                     .attr('class', 'icon ' + (svgklass || ''))
48364                     .append('use')
48365                     .attr('xlink:href', name)
48366                     .attr('class', useklass);
48367             };
48368         }
48369
48370         function uiNoteComments() {
48371             var _note;
48372
48373
48374             function noteComments(selection) {
48375                 if (_note.isNew()) { return; } // don't draw .comments-container
48376
48377                 var comments = selection.selectAll('.comments-container')
48378                     .data([0]);
48379
48380                 comments = comments.enter()
48381                     .append('div')
48382                     .attr('class', 'comments-container')
48383                     .merge(comments);
48384
48385                 var commentEnter = comments.selectAll('.comment')
48386                     .data(_note.comments)
48387                     .enter()
48388                     .append('div')
48389                     .attr('class', 'comment');
48390
48391                 commentEnter
48392                     .append('div')
48393                     .attr('class', function(d) { return 'comment-avatar user-' + d.uid; })
48394                     .call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
48395
48396                 var mainEnter = commentEnter
48397                     .append('div')
48398                     .attr('class', 'comment-main');
48399
48400                 var metadataEnter = mainEnter
48401                     .append('div')
48402                     .attr('class', 'comment-metadata');
48403
48404                 metadataEnter
48405                     .append('div')
48406                     .attr('class', 'comment-author')
48407                     .each(function(d) {
48408                         var selection = select(this);
48409                         var osm = services.osm;
48410                         if (osm && d.user) {
48411                             selection = selection
48412                                 .append('a')
48413                                 .attr('class', 'comment-author-link')
48414                                 .attr('href', osm.userURL(d.user))
48415                                 .attr('tabindex', -1)
48416                                 .attr('target', '_blank');
48417                         }
48418                         selection
48419                             .text(function(d) { return d.user || _t('note.anonymous'); });
48420                     });
48421
48422                 metadataEnter
48423                     .append('div')
48424                     .attr('class', 'comment-date')
48425                     .text(function(d) {
48426                         return _t('note.status.' + d.action, { when: localeDateString(d.date) });
48427                     });
48428
48429                 mainEnter
48430                     .append('div')
48431                     .attr('class', 'comment-text')
48432                     .html(function(d) { return d.html; });
48433
48434                 comments
48435                     .call(replaceAvatars);
48436             }
48437
48438
48439             function replaceAvatars(selection) {
48440                 var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
48441                 var osm = services.osm;
48442                 if (showThirdPartyIcons !== 'true' || !osm) { return; }
48443
48444                 var uids = {};  // gather uids in the comment thread
48445                 _note.comments.forEach(function(d) {
48446                     if (d.uid) { uids[d.uid] = true; }
48447                 });
48448
48449                 Object.keys(uids).forEach(function(uid) {
48450                     osm.loadUser(uid, function(err, user) {
48451                         if (!user || !user.image_url) { return; }
48452
48453                         selection.selectAll('.comment-avatar.user-' + uid)
48454                             .html('')
48455                             .append('img')
48456                             .attr('class', 'icon comment-avatar-icon')
48457                             .attr('src', user.image_url)
48458                             .attr('alt', user.display_name);
48459                     });
48460                 });
48461             }
48462
48463
48464             function localeDateString(s) {
48465                 if (!s) { return null; }
48466                 var options = { day: 'numeric', month: 'short', year: 'numeric' };
48467                 s = s.replace(/-/g, '/'); // fix browser-specific Date() issues
48468                 var d = new Date(s);
48469                 if (isNaN(d.getTime())) { return null; }
48470                 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
48471             }
48472
48473
48474             noteComments.note = function(val) {
48475                 if (!arguments.length) { return _note; }
48476                 _note = val;
48477                 return noteComments;
48478             };
48479
48480
48481             return noteComments;
48482         }
48483
48484         function uiNoteHeader() {
48485             var _note;
48486
48487
48488             function noteHeader(selection) {
48489                 var header = selection.selectAll('.note-header')
48490                     .data(
48491                         (_note ? [_note] : []),
48492                         function(d) { return d.status + d.id; }
48493                     );
48494
48495                 header.exit()
48496                     .remove();
48497
48498                 var headerEnter = header.enter()
48499                     .append('div')
48500                     .attr('class', 'note-header');
48501
48502                 var iconEnter = headerEnter
48503                     .append('div')
48504                     .attr('class', function(d) { return 'note-header-icon ' + d.status; })
48505                     .classed('new', function(d) { return d.id < 0; });
48506
48507                 iconEnter
48508                     .append('div')
48509                     .attr('class', 'preset-icon-28')
48510                     .call(svgIcon('#iD-icon-note', 'note-fill'));
48511
48512                 iconEnter.each(function(d) {
48513                     var statusIcon = '#iD-icon-' + (d.id < 0 ? 'plus' : (d.status === 'open' ? 'close' : 'apply'));
48514                     iconEnter
48515                         .append('div')
48516                         .attr('class', 'note-icon-annotation')
48517                         .call(svgIcon(statusIcon, 'icon-annotation'));
48518                 });
48519
48520                 headerEnter
48521                     .append('div')
48522                     .attr('class', 'note-header-label')
48523                     .text(function(d) {
48524                         if (_note.isNew()) { return _t('note.new'); }
48525                         return _t('note.note') + ' ' + d.id + ' ' +
48526                             (d.status === 'closed' ? _t('note.closed') : '');
48527                     });
48528             }
48529
48530
48531             noteHeader.note = function(val) {
48532                 if (!arguments.length) { return _note; }
48533                 _note = val;
48534                 return noteHeader;
48535             };
48536
48537
48538             return noteHeader;
48539         }
48540
48541         function uiNoteReport() {
48542             var _note;
48543
48544             function noteReport(selection) {
48545                 var url;
48546                 if (services.osm && (_note instanceof osmNote) && (!_note.isNew())) {
48547                     url = services.osm.noteReportURL(_note);
48548                 }
48549
48550                 var link = selection.selectAll('.note-report')
48551                     .data(url ? [url] : []);
48552
48553                 // exit
48554                 link.exit()
48555                     .remove();
48556
48557                 // enter
48558                 var linkEnter = link.enter()
48559                     .append('a')
48560                     .attr('class', 'note-report')
48561                     .attr('target', '_blank')
48562                     .attr('href', function(d) { return d; })
48563                     .call(svgIcon('#iD-icon-out-link', 'inline'));
48564
48565                 linkEnter
48566                     .append('span')
48567                     .text(_t('note.report'));
48568             }
48569
48570
48571             noteReport.note = function(val) {
48572                 if (!arguments.length) { return _note; }
48573                 _note = val;
48574                 return noteReport;
48575             };
48576
48577             return noteReport;
48578         }
48579
48580         function uiViewOnOSM(context) {
48581             var _what;   // an osmEntity or osmNote
48582
48583
48584             function viewOnOSM(selection) {
48585                 var url;
48586                 if (_what instanceof osmEntity) {
48587                     url = context.connection().entityURL(_what);
48588                 } else if (_what instanceof osmNote) {
48589                     url = context.connection().noteURL(_what);
48590                 }
48591
48592                 var data = ((!_what || _what.isNew()) ? [] : [_what]);
48593                 var link = selection.selectAll('.view-on-osm')
48594                     .data(data, function(d) { return d.id; });
48595
48596                 // exit
48597                 link.exit()
48598                     .remove();
48599
48600                 // enter
48601                 var linkEnter = link.enter()
48602                     .append('a')
48603                     .attr('class', 'view-on-osm')
48604                     .attr('target', '_blank')
48605                     .attr('href', url)
48606                     .call(svgIcon('#iD-icon-out-link', 'inline'));
48607
48608                 linkEnter
48609                     .append('span')
48610                     .text(_t('inspector.view_on_osm'));
48611             }
48612
48613
48614             viewOnOSM.what = function(_) {
48615                 if (!arguments.length) { return _what; }
48616                 _what = _;
48617                 return viewOnOSM;
48618             };
48619
48620             return viewOnOSM;
48621         }
48622
48623         function uiNoteEditor(context) {
48624             var dispatch$1 = dispatch('change');
48625             var noteComments = uiNoteComments();
48626             var noteHeader = uiNoteHeader();
48627
48628             // var formFields = uiFormFields(context);
48629
48630             var _note;
48631             var _newNote;
48632             // var _fieldsArr;
48633
48634
48635             function noteEditor(selection) {
48636
48637                 var header = selection.selectAll('.header')
48638                     .data([0]);
48639
48640                 var headerEnter = header.enter()
48641                     .append('div')
48642                     .attr('class', 'header fillL');
48643
48644                 headerEnter
48645                     .append('button')
48646                     .attr('class', 'close')
48647                     .on('click', function() {
48648                         context.enter(modeBrowse(context));
48649                     })
48650                     .call(svgIcon('#iD-icon-close'));
48651
48652                 headerEnter
48653                     .append('h3')
48654                     .text(_t('note.title'));
48655
48656
48657                 var body = selection.selectAll('.body')
48658                     .data([0]);
48659
48660                 body = body.enter()
48661                     .append('div')
48662                     .attr('class', 'body')
48663                     .merge(body);
48664
48665                 var editor = body.selectAll('.note-editor')
48666                     .data([0]);
48667
48668                 editor.enter()
48669                     .append('div')
48670                     .attr('class', 'modal-section note-editor')
48671                     .merge(editor)
48672                     .call(noteHeader.note(_note))
48673                     .call(noteComments.note(_note))
48674                     .call(noteSaveSection);
48675
48676                 var footer = selection.selectAll('.footer')
48677                     .data([0]);
48678
48679                 footer.enter()
48680                     .append('div')
48681                     .attr('class', 'footer')
48682                     .merge(footer)
48683                     .call(uiViewOnOSM(context).what(_note))
48684                     .call(uiNoteReport().note(_note));
48685
48686
48687                 // rerender the note editor on any auth change
48688                 var osm = services.osm;
48689                 if (osm) {
48690                     osm.on('change.note-save', function() {
48691                         selection.call(noteEditor);
48692                     });
48693                 }
48694             }
48695
48696
48697             function noteSaveSection(selection) {
48698                 var isSelected = (_note && _note.id === context.selectedNoteID());
48699                 var noteSave = selection.selectAll('.note-save')
48700                     .data((isSelected ? [_note] : []), function(d) { return d.status + d.id; });
48701
48702                 // exit
48703                 noteSave.exit()
48704                     .remove();
48705
48706                 // enter
48707                 var noteSaveEnter = noteSave.enter()
48708                     .append('div')
48709                     .attr('class', 'note-save save-section cf');
48710
48711                 // // if new note, show categories to pick from
48712                 // if (_note.isNew()) {
48713                 //     var presets = presetManager;
48714
48715                 //     // NOTE: this key isn't a age and therefore there is no documentation (yet)
48716                 //     _fieldsArr = [
48717                 //         uiField(context, presets.field('category'), null, { show: true, revert: false }),
48718                 //     ];
48719
48720                 //     _fieldsArr.forEach(function(field) {
48721                 //         field
48722                 //             .on('change', changeCategory);
48723                 //     });
48724
48725                 //     noteSaveEnter
48726                 //         .append('div')
48727                 //         .attr('class', 'note-category')
48728                 //         .call(formFields.fieldsArr(_fieldsArr));
48729                 // }
48730
48731                 // function changeCategory() {
48732                 //     // NOTE: perhaps there is a better way to get value
48733                 //     var val = context.container().select('input[name=\'category\']:checked').property('__data__') || undefined;
48734
48735                 //     // store the unsaved category with the note itself
48736                 //     _note = _note.update({ newCategory: val });
48737                 //     var osm = services.osm;
48738                 //     if (osm) {
48739                 //         osm.replaceNote(_note);  // update note cache
48740                 //     }
48741                 //     noteSave
48742                 //         .call(noteSaveButtons);
48743                 // }
48744
48745                 noteSaveEnter
48746                     .append('h4')
48747                     .attr('class', '.note-save-header')
48748                     .text(function() {
48749                         return _note.isNew() ? _t('note.newDescription') : _t('note.newComment');
48750                     });
48751
48752                 var commentTextarea = noteSaveEnter
48753                     .append('textarea')
48754                     .attr('class', 'new-comment-input')
48755                     .attr('placeholder', _t('note.inputPlaceholder'))
48756                     .attr('maxlength', 1000)
48757                     .property('value', function(d) { return d.newComment; })
48758                     .call(utilNoAuto)
48759                     .on('keydown.note-input', keydown)
48760                     .on('input.note-input', changeInput)
48761                     .on('blur.note-input', changeInput);
48762
48763                 if (_newNote) {
48764                     // autofocus the comment field for new notes
48765                     commentTextarea.node().focus();
48766                 }
48767
48768                 // update
48769                 noteSave = noteSaveEnter
48770                     .merge(noteSave)
48771                     .call(userDetails)
48772                     .call(noteSaveButtons);
48773
48774
48775                 // fast submit if user presses cmd+enter
48776                 function keydown() {
48777                     if (!(event.keyCode === 13 && event.metaKey)) { return; }
48778
48779                     var osm = services.osm;
48780                     if (!osm) { return; }
48781
48782                     var hasAuth = osm.authenticated();
48783                     if (!hasAuth) { return; }
48784
48785                     if (!_note.newComment) { return; }
48786
48787                     event.preventDefault();
48788
48789                     select(this)
48790                         .on('keydown.note-input', null);
48791
48792                     // focus on button and submit
48793                     window.setTimeout(function() {
48794                         if (_note.isNew()) {
48795                             noteSave.selectAll('.save-button').node().focus();
48796                             clickSave(_note);
48797                         } else  {
48798                             noteSave.selectAll('.comment-button').node().focus();
48799                             clickComment(_note);
48800                         }
48801                     }, 10);
48802                 }
48803
48804
48805                 function changeInput() {
48806                     var input = select(this);
48807                     var val = input.property('value').trim() || undefined;
48808
48809                     // store the unsaved comment with the note itself
48810                     _note = _note.update({ newComment: val });
48811
48812                     var osm = services.osm;
48813                     if (osm) {
48814                         osm.replaceNote(_note);  // update note cache
48815                     }
48816
48817                     noteSave
48818                         .call(noteSaveButtons);
48819                 }
48820             }
48821
48822
48823             function userDetails(selection) {
48824                 var detailSection = selection.selectAll('.detail-section')
48825                     .data([0]);
48826
48827                 detailSection = detailSection.enter()
48828                     .append('div')
48829                     .attr('class', 'detail-section')
48830                     .merge(detailSection);
48831
48832                 var osm = services.osm;
48833                 if (!osm) { return; }
48834
48835                 // Add warning if user is not logged in
48836                 var hasAuth = osm.authenticated();
48837                 var authWarning = detailSection.selectAll('.auth-warning')
48838                     .data(hasAuth ? [] : [0]);
48839
48840                 authWarning.exit()
48841                     .transition()
48842                     .duration(200)
48843                     .style('opacity', 0)
48844                     .remove();
48845
48846                 var authEnter = authWarning.enter()
48847                     .insert('div', '.tag-reference-body')
48848                     .attr('class', 'field-warning auth-warning')
48849                     .style('opacity', 0);
48850
48851                 authEnter
48852                     .call(svgIcon('#iD-icon-alert', 'inline'));
48853
48854                 authEnter
48855                     .append('span')
48856                     .text(_t('note.login'));
48857
48858                 authEnter
48859                     .append('a')
48860                     .attr('target', '_blank')
48861                     .call(svgIcon('#iD-icon-out-link', 'inline'))
48862                     .append('span')
48863                     .text(_t('login'))
48864                     .on('click.note-login', function() {
48865                         event.preventDefault();
48866                         osm.authenticate();
48867                     });
48868
48869                 authEnter
48870                     .transition()
48871                     .duration(200)
48872                     .style('opacity', 1);
48873
48874
48875                 var prose = detailSection.selectAll('.note-save-prose')
48876                     .data(hasAuth ? [0] : []);
48877
48878                 prose.exit()
48879                     .remove();
48880
48881                 prose = prose.enter()
48882                     .append('p')
48883                     .attr('class', 'note-save-prose')
48884                     .text(_t('note.upload_explanation'))
48885                     .merge(prose);
48886
48887                 osm.userDetails(function(err, user) {
48888                     if (err) { return; }
48889
48890                     var userLink = select(document.createElement('div'));
48891
48892                     if (user.image_url) {
48893                         userLink
48894                             .append('img')
48895                             .attr('src', user.image_url)
48896                             .attr('class', 'icon pre-text user-icon');
48897                     }
48898
48899                     userLink
48900                         .append('a')
48901                         .attr('class', 'user-info')
48902                         .text(user.display_name)
48903                         .attr('href', osm.userURL(user.display_name))
48904                         .attr('tabindex', -1)
48905                         .attr('target', '_blank');
48906
48907                     prose
48908                         .html(_t('note.upload_explanation_with_user', { user: userLink.html() }));
48909                 });
48910             }
48911
48912
48913             function noteSaveButtons(selection) {
48914                 var osm = services.osm;
48915                 var hasAuth = osm && osm.authenticated();
48916
48917                 var isSelected = (_note && _note.id === context.selectedNoteID());
48918                 var buttonSection = selection.selectAll('.buttons')
48919                     .data((isSelected ? [_note] : []), function(d) { return d.status + d.id; });
48920
48921                 // exit
48922                 buttonSection.exit()
48923                     .remove();
48924
48925                 // enter
48926                 var buttonEnter = buttonSection.enter()
48927                     .append('div')
48928                     .attr('class', 'buttons');
48929
48930                 if (_note.isNew()) {
48931                     buttonEnter
48932                         .append('button')
48933                         .attr('class', 'button cancel-button secondary-action')
48934                         .text(_t('confirm.cancel'));
48935
48936                     buttonEnter
48937                         .append('button')
48938                         .attr('class', 'button save-button action')
48939                         .text(_t('note.save'));
48940
48941                 } else {
48942                     buttonEnter
48943                         .append('button')
48944                         .attr('class', 'button status-button action');
48945
48946                     buttonEnter
48947                         .append('button')
48948                         .attr('class', 'button comment-button action')
48949                         .text(_t('note.comment'));
48950                 }
48951
48952
48953                 // update
48954                 buttonSection = buttonSection
48955                     .merge(buttonEnter);
48956
48957                 buttonSection.select('.cancel-button')   // select and propagate data
48958                     .on('click.cancel', clickCancel);
48959
48960                 buttonSection.select('.save-button')     // select and propagate data
48961                     .attr('disabled', isSaveDisabled)
48962                     .on('click.save', clickSave);
48963
48964                 buttonSection.select('.status-button')   // select and propagate data
48965                     .attr('disabled', (hasAuth ? null : true))
48966                     .text(function(d) {
48967                         var action = (d.status === 'open' ? 'close' : 'open');
48968                         var andComment = (d.newComment ? '_comment' : '');
48969                         return _t('note.' + action + andComment);
48970                     })
48971                     .on('click.status', clickStatus);
48972
48973                 buttonSection.select('.comment-button')   // select and propagate data
48974                     .attr('disabled', isSaveDisabled)
48975                     .on('click.comment', clickComment);
48976
48977
48978                 function isSaveDisabled(d) {
48979                     return (hasAuth && d.status === 'open' && d.newComment) ? null : true;
48980                 }
48981             }
48982
48983
48984
48985             function clickCancel(d) {
48986                 this.blur();    // avoid keeping focus on the button - #4641
48987                 var osm = services.osm;
48988                 if (osm) {
48989                     osm.removeNote(d);
48990                 }
48991                 context.enter(modeBrowse(context));
48992                 dispatch$1.call('change');
48993             }
48994
48995
48996             function clickSave(d) {
48997                 this.blur();    // avoid keeping focus on the button - #4641
48998                 var osm = services.osm;
48999                 if (osm) {
49000                     osm.postNoteCreate(d, function(err, note) {
49001                         dispatch$1.call('change', note);
49002                     });
49003                 }
49004             }
49005
49006
49007             function clickStatus(d) {
49008                 this.blur();    // avoid keeping focus on the button - #4641
49009                 var osm = services.osm;
49010                 if (osm) {
49011                     var setStatus = (d.status === 'open' ? 'closed' : 'open');
49012                     osm.postNoteUpdate(d, setStatus, function(err, note) {
49013                         dispatch$1.call('change', note);
49014                     });
49015                 }
49016             }
49017
49018             function clickComment(d) {
49019                 this.blur();    // avoid keeping focus on the button - #4641
49020                 var osm = services.osm;
49021                 if (osm) {
49022                     osm.postNoteUpdate(d, d.status, function(err, note) {
49023                         dispatch$1.call('change', note);
49024                     });
49025                 }
49026             }
49027
49028
49029             noteEditor.note = function(val) {
49030                 if (!arguments.length) { return _note; }
49031                 _note = val;
49032                 return noteEditor;
49033             };
49034
49035             noteEditor.newNote = function(val) {
49036                 if (!arguments.length) { return _newNote; }
49037                 _newNote = val;
49038                 return noteEditor;
49039             };
49040
49041
49042             return utilRebind(noteEditor, dispatch$1, 'on');
49043         }
49044
49045         function modeSelectNote(context, selectedNoteID) {
49046             var mode = {
49047                 id: 'select-note',
49048                 button: 'browse'
49049             };
49050
49051             var _keybinding = utilKeybinding('select-note');
49052             var _noteEditor = uiNoteEditor(context)
49053                 .on('change', function() {
49054                     context.map().pan([0,0]);  // trigger a redraw
49055                     var note = checkSelectedID();
49056                     if (!note) { return; }
49057                     context.ui().sidebar
49058                         .show(_noteEditor.note(note));
49059                 });
49060
49061             var _behaviors = [
49062                 behaviorBreathe(),
49063                 behaviorHover(context),
49064                 behaviorSelect(context),
49065                 behaviorLasso(context),
49066                 modeDragNode(context).behavior,
49067                 modeDragNote(context).behavior
49068             ];
49069
49070             var _newFeature = false;
49071
49072
49073             function checkSelectedID() {
49074                 if (!services.osm) { return; }
49075                 var note = services.osm.getNote(selectedNoteID);
49076                 if (!note) {
49077                     context.enter(modeBrowse(context));
49078                 }
49079                 return note;
49080             }
49081
49082
49083             // class the note as selected, or return to browse mode if the note is gone
49084             function selectNote(drawn) {
49085                 if (!checkSelectedID()) { return; }
49086
49087                 var selection = context.surface().selectAll('.layer-notes .note-' + selectedNoteID);
49088
49089                 if (selection.empty()) {
49090                     // Return to browse mode if selected DOM elements have
49091                     // disappeared because the user moved them out of view..
49092                     var source = event && event.type === 'zoom' && event.sourceEvent;
49093                     if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
49094                         context.enter(modeBrowse(context));
49095                     }
49096
49097                 } else {
49098                     selection
49099                         .classed('selected', true);
49100
49101                     context.selectedNoteID(selectedNoteID);
49102                 }
49103             }
49104
49105
49106             function esc() {
49107                 if (context.container().select('.combobox').size()) { return; }
49108                 context.enter(modeBrowse(context));
49109             }
49110
49111
49112             mode.zoomToSelected = function() {
49113                 if (!services.osm) { return; }
49114                 var note = services.osm.getNote(selectedNoteID);
49115                 if (note) {
49116                     context.map().centerZoomEase(note.loc, 20);
49117                 }
49118             };
49119
49120
49121             mode.newFeature = function(val) {
49122                 if (!arguments.length) { return _newFeature; }
49123                 _newFeature = val;
49124                 return mode;
49125             };
49126
49127
49128             mode.enter = function() {
49129                 var note = checkSelectedID();
49130                 if (!note) { return; }
49131
49132                 _behaviors.forEach(context.install);
49133
49134                 _keybinding
49135                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
49136                     .on('⎋', esc, true);
49137
49138                 select(document)
49139                     .call(_keybinding);
49140
49141                 selectNote();
49142
49143                 var sidebar = context.ui().sidebar;
49144                 sidebar.show(_noteEditor.note(note).newNote(_newFeature));
49145
49146                 // expand the sidebar, avoid obscuring the note if needed
49147                 sidebar.expand(sidebar.intersects(note.extent()));
49148
49149                 context.map()
49150                     .on('drawn.select', selectNote);
49151             };
49152
49153
49154             mode.exit = function() {
49155                 _behaviors.forEach(context.uninstall);
49156
49157                 select(document)
49158                     .call(_keybinding.unbind);
49159
49160                 context.surface()
49161                     .selectAll('.layer-notes .selected')
49162                     .classed('selected hover', false);
49163
49164                 context.map()
49165                     .on('drawn.select', null);
49166
49167                 context.ui().sidebar
49168                     .hide();
49169
49170                 context.selectedNoteID(null);
49171             };
49172
49173
49174             return mode;
49175         }
49176
49177         function modeDragNote(context) {
49178             var mode = {
49179                 id: 'drag-note',
49180                 button: 'browse'
49181             };
49182
49183             var edit = behaviorEdit(context);
49184
49185             var _nudgeInterval;
49186             var _lastLoc;
49187             var _note;    // most current note.. dragged note may have stale datum.
49188
49189
49190             function startNudge(nudge) {
49191                 if (_nudgeInterval) { window.clearInterval(_nudgeInterval); }
49192                 _nudgeInterval = window.setInterval(function() {
49193                     context.map().pan(nudge);
49194                     doMove(nudge);
49195                 }, 50);
49196             }
49197
49198
49199             function stopNudge() {
49200                 if (_nudgeInterval) {
49201                     window.clearInterval(_nudgeInterval);
49202                     _nudgeInterval = null;
49203                 }
49204             }
49205
49206
49207             function origin(note) {
49208                 return context.projection(note.loc);
49209             }
49210
49211
49212             function start(note) {
49213                 _note = note;
49214                 var osm = services.osm;
49215                 if (osm) {
49216                     // Get latest note from cache.. The marker may have a stale datum bound to it
49217                     // and dragging it around can sometimes delete the users note comment.
49218                     _note = osm.getNote(_note.id);
49219                 }
49220
49221                 context.surface().selectAll('.note-' + _note.id)
49222                     .classed('active', true);
49223
49224                 context.perform(actionNoop());
49225                 context.enter(mode);
49226                 context.selectedNoteID(_note.id);
49227             }
49228
49229
49230             function move() {
49231                 event.sourceEvent.stopPropagation();
49232                 _lastLoc = context.projection.invert(event.point);
49233
49234                 doMove();
49235                 var nudge = geoViewportEdge(event.point, context.map().dimensions());
49236                 if (nudge) {
49237                     startNudge(nudge);
49238                 } else {
49239                     stopNudge();
49240                 }
49241             }
49242
49243
49244             function doMove(nudge) {
49245                 nudge = nudge || [0, 0];
49246
49247                 var currPoint = (event && event.point) || context.projection(_lastLoc);
49248                 var currMouse = geoVecSubtract(currPoint, nudge);
49249                 var loc = context.projection.invert(currMouse);
49250
49251                 _note = _note.move(loc);
49252
49253                 var osm = services.osm;
49254                 if (osm) {
49255                     osm.replaceNote(_note);  // update note cache
49256                 }
49257
49258                 context.replace(actionNoop());   // trigger redraw
49259             }
49260
49261
49262             function end() {
49263                 context.replace(actionNoop());   // trigger redraw
49264
49265                 context
49266                     .selectedNoteID(_note.id)
49267                     .enter(modeSelectNote(context, _note.id));
49268             }
49269
49270
49271             var drag = behaviorDrag()
49272                 .selector('.layer-touch.markers .target.note.new')
49273                 .surface(context.container().select('.main-map').node())
49274                 .origin(origin)
49275                 .on('start', start)
49276                 .on('move', move)
49277                 .on('end', end);
49278
49279
49280             mode.enter = function() {
49281                 context.install(edit);
49282             };
49283
49284
49285             mode.exit = function() {
49286                 context.ui().sidebar.hover.cancel();
49287                 context.uninstall(edit);
49288
49289                 context.surface()
49290                     .selectAll('.active')
49291                     .classed('active', false);
49292
49293                 stopNudge();
49294             };
49295
49296             mode.behavior = drag;
49297
49298             return mode;
49299         }
49300
49301         function uiDataHeader() {
49302             var _datum;
49303
49304
49305             function dataHeader(selection) {
49306                 var header = selection.selectAll('.data-header')
49307                     .data(
49308                         (_datum ? [_datum] : []),
49309                         function(d) { return d.__featurehash__; }
49310                     );
49311
49312                 header.exit()
49313                     .remove();
49314
49315                 var headerEnter = header.enter()
49316                     .append('div')
49317                     .attr('class', 'data-header');
49318
49319                 var iconEnter = headerEnter
49320                     .append('div')
49321                     .attr('class', 'data-header-icon');
49322
49323                 iconEnter
49324                     .append('div')
49325                     .attr('class', 'preset-icon-28')
49326                     .call(svgIcon('#iD-icon-data', 'note-fill'));
49327
49328                 headerEnter
49329                     .append('div')
49330                     .attr('class', 'data-header-label')
49331                     .text(_t('map_data.layers.custom.title'));
49332             }
49333
49334
49335             dataHeader.datum = function(val) {
49336                 if (!arguments.length) { return _datum; }
49337                 _datum = val;
49338                 return this;
49339             };
49340
49341
49342             return dataHeader;
49343         }
49344
49345         // This code assumes that the combobox values will not have duplicate entries.
49346         // It is keyed on the `value` of the entry. Data should be an array of objects like:
49347         //   [{
49348         //       value:  'display text',  // required
49349         //       title:  'hover text'     // optional
49350         //   }, ...]
49351
49352         var _comboHideTimerID;
49353
49354         function uiCombobox(context, klass) {
49355             var dispatch$1 = dispatch('accept', 'cancel');
49356             var container = context.container();
49357
49358             var _suggestions = [];
49359             var _data = [];
49360             var _fetched = {};
49361             var _selected = null;
49362             var _canAutocomplete = true;
49363             var _caseSensitive = false;
49364             var _cancelFetch = false;
49365             var _minItems = 2;
49366             var _tDown = 0;
49367             var _mouseEnterHandler, _mouseLeaveHandler;
49368
49369             var _fetcher = function(val, cb) {
49370                 cb(_data.filter(function(d) {
49371                     var terms = d.terms || [];
49372                     terms.push(d.value);
49373                     return terms.some(function(term) {
49374                         return term
49375                             .toString()
49376                             .toLowerCase()
49377                             .indexOf(val.toLowerCase()) !== -1;
49378                     });
49379                 }));
49380             };
49381
49382             var combobox = function(input, attachTo) {
49383                 if (!input || input.empty()) { return; }
49384
49385                 input
49386                     .classed('combobox-input', true)
49387                     .on('focus.combo-input', focus)
49388                     .on('blur.combo-input', blur)
49389                     .on('keydown.combo-input', keydown)
49390                     .on('keyup.combo-input', keyup)
49391                     .on('input.combo-input', change)
49392                     .on('mousedown.combo-input', mousedown)
49393                     .each(function() {
49394                         var parent = this.parentNode;
49395                         var sibling = this.nextSibling;
49396
49397                         select(parent).selectAll('.combobox-caret')
49398                             .filter(function(d) { return d === input.node(); })
49399                             .data([input.node()])
49400                             .enter()
49401                             .insert('div', function() { return sibling; })
49402                             .attr('class', 'combobox-caret')
49403                             .on('mousedown.combo-caret', function() {
49404                                 event.preventDefault(); // don't steal focus from input
49405                                 input.node().focus(); // focus the input as if it was clicked
49406                                 mousedown();
49407                             })
49408                             .on('mouseup.combo-caret', function() {
49409                                 event.preventDefault(); // don't steal focus from input
49410                                 mouseup();
49411                             });
49412                     });
49413
49414
49415                 function mousedown() {
49416                     if (event.button !== 0) { return; }    // left click only
49417                     _tDown = +new Date();
49418
49419                     // clear selection
49420                     var start = input.property('selectionStart');
49421                     var end = input.property('selectionEnd');
49422                     if (start !== end) {
49423                         var val = utilGetSetValue(input);
49424                         input.node().setSelectionRange(val.length, val.length);
49425                         return;
49426                     }
49427
49428                     input.on('mouseup.combo-input', mouseup);
49429                 }
49430
49431
49432                 function mouseup() {
49433                     input.on('mouseup.combo-input', null);
49434                     if (event.button !== 0) { return; }    // left click only
49435                     if (input.node() !== document.activeElement) { return; }   // exit if this input is not focused
49436
49437                     var start = input.property('selectionStart');
49438                     var end = input.property('selectionEnd');
49439                     if (start !== end) { return; }  // exit if user is selecting
49440
49441                     // not showing or showing for a different field - try to show it.
49442                     var combo = container.selectAll('.combobox');
49443                     if (combo.empty() || combo.datum() !== input.node()) {
49444                         var tOrig = _tDown;
49445                         window.setTimeout(function() {
49446                             if (tOrig !== _tDown) { return; }   // exit if user double clicked
49447                             fetchComboData('', function() {
49448                                 show();
49449                                 render();
49450                             });
49451                         }, 250);
49452
49453                     } else {
49454                         hide();
49455                     }
49456                 }
49457
49458
49459                 function focus() {
49460                     fetchComboData('');   // prefetch values (may warm taginfo cache)
49461                 }
49462
49463
49464                 function blur() {
49465                     _comboHideTimerID = window.setTimeout(hide, 75);
49466                 }
49467
49468
49469                 function show() {
49470                     hide();   // remove any existing
49471
49472                     container
49473                         .insert('div', ':first-child')
49474                         .datum(input.node())
49475                         .attr('class', 'combobox' + (klass ? ' combobox-' + klass : ''))
49476                         .style('position', 'absolute')
49477                         .style('display', 'block')
49478                         .style('left', '0px')
49479                         .on('mousedown.combo-container', function () {
49480                             // prevent moving focus out of the input field
49481                             event.preventDefault();
49482                         });
49483
49484                     container
49485                         .on('scroll.combo-scroll', render, true);
49486                 }
49487
49488
49489                 function hide() {
49490                     if (_comboHideTimerID) {
49491                         window.clearTimeout(_comboHideTimerID);
49492                         _comboHideTimerID = undefined;
49493                     }
49494
49495                     container.selectAll('.combobox')
49496                         .remove();
49497
49498                     container
49499                         .on('scroll.combo-scroll', null);
49500                 }
49501
49502
49503                 function keydown() {
49504                     var shown = !container.selectAll('.combobox').empty();
49505                     var tagName = input.node() ? input.node().tagName.toLowerCase() : '';
49506
49507                     switch (event.keyCode) {
49508                         case 8:   // ⌫ Backspace
49509                         case 46:  // ⌦ Delete
49510                             event.stopPropagation();
49511                             _selected = null;
49512                             render();
49513                             input.on('input.combo-input', function() {
49514                                 var start = input.property('selectionStart');
49515                                 input.node().setSelectionRange(start, start);
49516                                 input.on('input.combo-input', change);
49517                             });
49518                             break;
49519
49520                         case 9:   // ⇥ Tab
49521                             accept();
49522                             break;
49523
49524                         case 13:  // ↩ Return
49525                             event.preventDefault();
49526                             event.stopPropagation();
49527                             break;
49528
49529                         case 38:  // ↑ Up arrow
49530                             if (tagName === 'textarea' && !shown) { return; }
49531                             event.preventDefault();
49532                             if (tagName === 'input' && !shown) {
49533                                 show();
49534                             }
49535                             nav(-1);
49536                             break;
49537
49538                         case 40:  // ↓ Down arrow
49539                             if (tagName === 'textarea' && !shown) { return; }
49540                             event.preventDefault();
49541                             if (tagName === 'input' && !shown) {
49542                                 show();
49543                             }
49544                             nav(+1);
49545                             break;
49546                     }
49547                 }
49548
49549
49550                 function keyup() {
49551                     switch (event.keyCode) {
49552                         case 27:  // ⎋ Escape
49553                             cancel();
49554                             break;
49555
49556                         case 13:  // ↩ Return
49557                             accept();
49558                             break;
49559                     }
49560                 }
49561
49562
49563                 // Called whenever the input value is changed (e.g. on typing)
49564                 function change() {
49565                     fetchComboData(value(), function() {
49566                         _selected = null;
49567                         var val = input.property('value');
49568
49569                         if (_suggestions.length) {
49570                             if (input.property('selectionEnd') === val.length) {
49571                                 _selected = tryAutocomplete();
49572                             }
49573
49574                             if (!_selected) {
49575                                 _selected = val;
49576                             }
49577                         }
49578
49579                         if (val.length) {
49580                             var combo = container.selectAll('.combobox');
49581                             if (combo.empty()) {
49582                                 show();
49583                             }
49584                         } else {
49585                             hide();
49586                         }
49587
49588                         render();
49589                     });
49590                 }
49591
49592
49593                 // Called when the user presses up/down arrows to navigate the list
49594                 function nav(dir) {
49595                     if (_suggestions.length) {
49596                         // try to determine previously selected index..
49597                         var index = -1;
49598                         for (var i = 0; i < _suggestions.length; i++) {
49599                             if (_selected && _suggestions[i].value === _selected) {
49600                                 index = i;
49601                                 break;
49602                             }
49603                         }
49604
49605                         // pick new _selected
49606                         index = Math.max(Math.min(index + dir, _suggestions.length - 1), 0);
49607                         _selected = _suggestions[index].value;
49608                         input.property('value', _selected);
49609                     }
49610
49611                     render();
49612                     ensureVisible();
49613                 }
49614
49615
49616                 function ensureVisible() {
49617                     var combo = container.selectAll('.combobox');
49618                     if (combo.empty()) { return; }
49619
49620                     var containerRect = container.node().getBoundingClientRect();
49621                     var comboRect = combo.node().getBoundingClientRect();
49622
49623                     if (comboRect.bottom > containerRect.bottom) {
49624                         var node = attachTo ? attachTo.node() : input.node();
49625                         node.scrollIntoView({ behavior: 'instant', block: 'center' });
49626                         render();
49627                     }
49628
49629                     // https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move
49630                     var selected = combo.selectAll('.combobox-option.selected').node();
49631                     if (selected) {
49632                         selected.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
49633                     }
49634                 }
49635
49636
49637                 function value() {
49638                     var value = input.property('value');
49639                     var start = input.property('selectionStart');
49640                     var end = input.property('selectionEnd');
49641
49642                     if (start && end) {
49643                         value = value.substring(0, start);
49644                     }
49645
49646                     return value;
49647                 }
49648
49649
49650                 function fetchComboData(v, cb) {
49651                     _cancelFetch = false;
49652
49653                     _fetcher.call(input, v, function(results) {
49654                         // already chose a value, don't overwrite or autocomplete it
49655                         if (_cancelFetch) { return; }
49656
49657                         _suggestions = results;
49658                         results.forEach(function(d) { _fetched[d.value] = d; });
49659
49660                         if (cb) {
49661                             cb();
49662                         }
49663                     });
49664                 }
49665
49666
49667                 function tryAutocomplete() {
49668                     if (!_canAutocomplete) { return; }
49669
49670                     var val = _caseSensitive ? value() : value().toLowerCase();
49671                     if (!val) { return; }
49672
49673                     // Don't autocomplete if user is typing a number - #4935
49674                     if (!isNaN(parseFloat(val)) && isFinite(val)) { return; }
49675
49676                     var bestIndex = -1;
49677                     for (var i = 0; i < _suggestions.length; i++) {
49678                         var suggestion = _suggestions[i].value;
49679                         var compare = _caseSensitive ? suggestion : suggestion.toLowerCase();
49680
49681                         // if search string matches suggestion exactly, pick it..
49682                         if (compare === val) {
49683                             bestIndex = i;
49684                             break;
49685
49686                         // otherwise lock in the first result that starts with the search string..
49687                         } else if (bestIndex === -1 && compare.indexOf(val) === 0) {
49688                             bestIndex = i;
49689                         }
49690                     }
49691
49692                     if (bestIndex !== -1) {
49693                         var bestVal = _suggestions[bestIndex].value;
49694                         input.property('value', bestVal);
49695                         input.node().setSelectionRange(val.length, bestVal.length);
49696                         return bestVal;
49697                     }
49698                 }
49699
49700
49701                 function render() {
49702                     if (_suggestions.length < _minItems || document.activeElement !== input.node()) {
49703                         hide();
49704                         return;
49705                     }
49706
49707                     var shown = !container.selectAll('.combobox').empty();
49708                     if (!shown) { return; }
49709
49710                     var combo = container.selectAll('.combobox');
49711                     var options = combo.selectAll('.combobox-option')
49712                         .data(_suggestions, function(d) { return d.value; });
49713
49714                     options.exit()
49715                         .remove();
49716
49717                     // enter/update
49718                     options.enter()
49719                         .append('a')
49720                         .attr('class', 'combobox-option')
49721                         .attr('title', function(d) { return d.title; })
49722                         .text(function(d) { return d.display || d.value; })
49723                         .on('mouseenter', _mouseEnterHandler)
49724                         .on('mouseleave', _mouseLeaveHandler)
49725                         .merge(options)
49726                         .classed('selected', function(d) { return d.value === _selected; })
49727                         .on('click.combo-option', accept)
49728                         .order();
49729
49730                     var node = attachTo ? attachTo.node() : input.node();
49731                     var containerRect = container.node().getBoundingClientRect();
49732                     var rect = node.getBoundingClientRect();
49733
49734                     combo
49735                         .style('left', (rect.left + 5 - containerRect.left) + 'px')
49736                         .style('width', (rect.width - 10) + 'px')
49737                         .style('top', (rect.height + rect.top - containerRect.top) + 'px');
49738                 }
49739
49740
49741                 // Dispatches an 'accept' event
49742                 // Then hides the combobox.
49743                 function accept(d) {
49744                     _cancelFetch = true;
49745                     var thiz = input.node();
49746
49747                     if (d) {   // user clicked on a suggestion
49748                         utilGetSetValue(input, d.value);    // replace field contents
49749                         utilTriggerEvent(input, 'change');
49750                     }
49751
49752                     // clear (and keep) selection
49753                     var val = utilGetSetValue(input);
49754                     thiz.setSelectionRange(val.length, val.length);
49755
49756                     d = _fetched[val];
49757                     dispatch$1.call('accept', thiz, d, val);
49758                     hide();
49759                 }
49760
49761
49762                 // Dispatches an 'cancel' event
49763                 // Then hides the combobox.
49764                 function cancel() {
49765                     _cancelFetch = true;
49766                     var thiz = input.node();
49767
49768                     // clear (and remove) selection, and replace field contents
49769                     var val = utilGetSetValue(input);
49770                     var start = input.property('selectionStart');
49771                     var end = input.property('selectionEnd');
49772                     val = val.slice(0, start) + val.slice(end);
49773                     utilGetSetValue(input, val);
49774                     thiz.setSelectionRange(val.length, val.length);
49775
49776                     dispatch$1.call('cancel', thiz);
49777                     hide();
49778                 }
49779
49780             };
49781
49782
49783             combobox.canAutocomplete = function(val) {
49784                 if (!arguments.length) { return _canAutocomplete; }
49785                 _canAutocomplete = val;
49786                 return combobox;
49787             };
49788
49789             combobox.caseSensitive = function(val) {
49790                 if (!arguments.length) { return _caseSensitive; }
49791                 _caseSensitive = val;
49792                 return combobox;
49793             };
49794
49795             combobox.data = function(val) {
49796                 if (!arguments.length) { return _data; }
49797                 _data = val;
49798                 return combobox;
49799             };
49800
49801             combobox.fetcher = function(val) {
49802                 if (!arguments.length) { return _fetcher; }
49803                 _fetcher = val;
49804                 return combobox;
49805             };
49806
49807             combobox.minItems = function(val) {
49808                 if (!arguments.length) { return _minItems; }
49809                 _minItems = val;
49810                 return combobox;
49811             };
49812
49813             combobox.itemsMouseEnter = function(val) {
49814                 if (!arguments.length) { return _mouseEnterHandler; }
49815                 _mouseEnterHandler = val;
49816                 return combobox;
49817             };
49818
49819             combobox.itemsMouseLeave = function(val) {
49820                 if (!arguments.length) { return _mouseLeaveHandler; }
49821                 _mouseLeaveHandler = val;
49822                 return combobox;
49823             };
49824
49825             return utilRebind(combobox, dispatch$1, 'on');
49826         }
49827
49828
49829         uiCombobox.off = function(input, context) {
49830             input
49831                 .on('focus.combo-input', null)
49832                 .on('blur.combo-input', null)
49833                 .on('keydown.combo-input', null)
49834                 .on('keyup.combo-input', null)
49835                 .on('input.combo-input', null)
49836                 .on('mousedown.combo-input', null)
49837                 .on('mouseup.combo-input', null);
49838
49839
49840             context.container()
49841                 .on('scroll.combo-scroll', null);
49842         };
49843
49844         // toggles the visibility of ui elements, using a combination of the
49845         // hide class, which sets display=none, and a d3 transition for opacity.
49846         // this will cause blinking when called repeatedly, so check that the
49847         // value actually changes between calls.
49848         function uiToggle(show, callback) {
49849             return function(selection) {
49850                 selection
49851                     .style('opacity', show ? 0 : 1)
49852                     .classed('hide', false)
49853                     .transition()
49854                     .style('opacity', show ? 1 : 0)
49855                     .on('end', function() {
49856                         select(this)
49857                             .classed('hide', !show)
49858                             .style('opacity', null);
49859                         if (callback) { callback.apply(this); }
49860                     });
49861             };
49862         }
49863
49864         function uiDisclosure(context, key, expandedDefault) {
49865             var dispatch$1 = dispatch('toggled');
49866             var _expanded;
49867             var _title = utilFunctor('');
49868             var _updatePreference = true;
49869             var _content = function () {};
49870
49871
49872             var disclosure = function(selection) {
49873
49874                 if (_expanded === undefined || _expanded === null) {
49875                     // loading _expanded here allows it to be reset by calling `disclosure.expanded(null)`
49876
49877                     var preference = corePreferences('disclosure.' + key + '.expanded');
49878                     _expanded = preference === null ? !!expandedDefault : (preference === 'true');
49879                 }
49880
49881                 var hideToggle = selection.selectAll('.hide-toggle-' + key)
49882                     .data([0]);
49883
49884                 // enter
49885                 var hideToggleEnter = hideToggle.enter()
49886                     .append('a')
49887                     .attr('href', '#')
49888                     .attr('class', 'hide-toggle hide-toggle-' + key)
49889                     .call(svgIcon('', 'pre-text', 'hide-toggle-icon'));
49890
49891                 hideToggleEnter
49892                     .append('span')
49893                     .attr('class', 'hide-toggle-text');
49894
49895                 // update
49896                 hideToggle = hideToggleEnter
49897                     .merge(hideToggle);
49898
49899                 hideToggle
49900                     .on('click', toggle)
49901                     .classed('expanded', _expanded);
49902
49903                 hideToggle.selectAll('.hide-toggle-text')
49904                     .text(_title());
49905
49906                 hideToggle.selectAll('.hide-toggle-icon')
49907                     .attr('xlink:href', _expanded ? '#iD-icon-down'
49908                         : (_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward'
49909                     );
49910
49911
49912                 var wrap = selection.selectAll('.disclosure-wrap')
49913                     .data([0]);
49914
49915                 // enter/update
49916                 wrap = wrap.enter()
49917                     .append('div')
49918                     .attr('class', 'disclosure-wrap disclosure-wrap-' + key)
49919                     .merge(wrap)
49920                     .classed('hide', !_expanded);
49921
49922                 if (_expanded) {
49923                     wrap
49924                         .call(_content);
49925                 }
49926
49927
49928                 function toggle() {
49929                     event.preventDefault();
49930
49931                     _expanded = !_expanded;
49932
49933                     if (_updatePreference) {
49934                         corePreferences('disclosure.' + key + '.expanded', _expanded);
49935                     }
49936
49937                     hideToggle
49938                         .classed('expanded', _expanded);
49939
49940                     hideToggle.selectAll('.hide-toggle-icon')
49941                         .attr('xlink:href', _expanded ? '#iD-icon-down'
49942                             : (_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward'
49943                         );
49944
49945                     wrap
49946                         .call(uiToggle(_expanded));
49947
49948                     if (_expanded) {
49949                         wrap
49950                             .call(_content);
49951                     }
49952
49953                     dispatch$1.call('toggled', this, _expanded);
49954                 }
49955             };
49956
49957
49958             disclosure.title = function(val) {
49959                 if (!arguments.length) { return _title; }
49960                 _title = utilFunctor(val);
49961                 return disclosure;
49962             };
49963
49964
49965             disclosure.expanded = function(val) {
49966                 if (!arguments.length) { return _expanded; }
49967                 _expanded = val;
49968                 return disclosure;
49969             };
49970
49971
49972             disclosure.updatePreference = function(val) {
49973                 if (!arguments.length) { return _updatePreference; }
49974                 _updatePreference = val;
49975                 return disclosure;
49976             };
49977
49978
49979             disclosure.content = function(val) {
49980                 if (!arguments.length) { return _content; }
49981                 _content = val;
49982                 return disclosure;
49983             };
49984
49985
49986             return utilRebind(disclosure, dispatch$1, 'on');
49987         }
49988
49989         // A unit of controls or info to be used in a layout, such as within a pane.
49990         // Can be labeled and collapsible.
49991         function uiSection(id, context) {
49992
49993             var _classes = utilFunctor('');
49994             var _shouldDisplay;
49995             var _content;
49996
49997             var _disclosure;
49998             var _title;
49999             var _expandedByDefault = utilFunctor(true);
50000             var _disclosureContent;
50001             var _disclosureExpanded;
50002
50003             var _containerSelection = select(null);
50004
50005             var section = {
50006                 id: id
50007             };
50008
50009             section.classes = function(val) {
50010                 if (!arguments.length) { return _classes; }
50011                 _classes = utilFunctor(val);
50012                 return section;
50013             };
50014
50015             section.title = function(val) {
50016                 if (!arguments.length) { return _title; }
50017                 _title = utilFunctor(val);
50018                 return section;
50019             };
50020
50021             section.expandedByDefault = function(val) {
50022                 if (!arguments.length) { return _expandedByDefault; }
50023                 _expandedByDefault = utilFunctor(val);
50024                 return section;
50025             };
50026
50027             section.shouldDisplay = function(val) {
50028                 if (!arguments.length) { return _shouldDisplay; }
50029                 _shouldDisplay = utilFunctor(val);
50030                 return section;
50031             };
50032
50033             section.content = function(val) {
50034                 if (!arguments.length) { return _content; }
50035                 _content = val;
50036                 return section;
50037             };
50038
50039             section.disclosureContent = function(val) {
50040                 if (!arguments.length) { return _disclosureContent; }
50041                 _disclosureContent = val;
50042                 return section;
50043             };
50044
50045             section.disclosureExpanded = function(val) {
50046                 if (!arguments.length) { return _disclosureExpanded; }
50047                 _disclosureExpanded = val;
50048                 return section;
50049             };
50050
50051             // may be called multiple times
50052             section.render = function(selection) {
50053
50054                 _containerSelection = selection
50055                     .selectAll('.section-' + id)
50056                     .data([0]);
50057
50058                 var sectionEnter = _containerSelection
50059                     .enter()
50060                     .append('div')
50061                     .attr('class', 'section section-' + id + ' ' + (_classes && _classes() || ''));
50062
50063                 _containerSelection = sectionEnter
50064                     .merge(_containerSelection);
50065
50066                 _containerSelection
50067                     .call(renderContent);
50068             };
50069
50070             section.reRender = function() {
50071                 _containerSelection
50072                     .call(renderContent);
50073             };
50074
50075             section.selection = function() {
50076                 return _containerSelection;
50077             };
50078
50079             section.disclosure = function() {
50080                 return _disclosure;
50081             };
50082
50083             // may be called multiple times
50084             function renderContent(selection) {
50085                 if (_shouldDisplay) {
50086                     var shouldDisplay = _shouldDisplay();
50087                     selection.classed('hide', !shouldDisplay);
50088                     if (!shouldDisplay) {
50089                         selection.html('');
50090                         return;
50091                     }
50092                 }
50093
50094                 if (_disclosureContent) {
50095                     if (!_disclosure) {
50096                         _disclosure = uiDisclosure(context, id.replace(/-/g, '_'), _expandedByDefault())
50097                             .title(_title || '')
50098                             /*.on('toggled', function(expanded) {
50099                                 if (expanded) { selection.node().parentNode.scrollTop += 200; }
50100                             })*/
50101                             .content(_disclosureContent);
50102                     }
50103                     if (_disclosureExpanded !== undefined) {
50104                         _disclosure.expanded(_disclosureExpanded);
50105                         _disclosureExpanded = undefined;
50106                     }
50107                     selection
50108                         .call(_disclosure);
50109
50110                     return;
50111                 }
50112
50113                 if (_content) {
50114                     selection
50115                         .call(_content);
50116                 }
50117             }
50118
50119             return section;
50120         }
50121
50122         // Pass `which` object of the form:
50123         // {
50124         //   key: 'string',     // required
50125         //   value: 'string'    // optional
50126         // }
50127         //   -or-
50128         // {
50129         //   rtype: 'string'    // relation type  (e.g. 'multipolygon')
50130         // }
50131         //   -or-
50132         // {
50133         //   qid: 'string'      // brand wikidata  (e.g. 'Q37158')
50134         // }
50135         //
50136         function uiTagReference(what) {
50137             var wikibase = what.qid ? services.wikidata : services.osmWikibase;
50138             var tagReference = {};
50139
50140             var _button = select(null);
50141             var _body = select(null);
50142             var _loaded;
50143             var _showing;
50144
50145
50146             function load() {
50147                 if (!wikibase) { return; }
50148
50149                 _button
50150                     .classed('tag-reference-loading', true);
50151
50152                 wikibase.getDocs(what, gotDocs);
50153             }
50154
50155
50156             function gotDocs(err, docs) {
50157                 _body.html('');
50158
50159                 if (!docs || !docs.title) {
50160                     _body
50161                         .append('p')
50162                         .attr('class', 'tag-reference-description')
50163                         .text(_t('inspector.no_documentation_key'));
50164                     done();
50165                     return;
50166                 }
50167
50168                 if (docs.imageURL) {
50169                     _body
50170                         .append('img')
50171                         .attr('class', 'tag-reference-wiki-image')
50172                         .attr('src', docs.imageURL)
50173                         .on('load', function() { done(); })
50174                         .on('error', function() { select(this).remove(); done(); });
50175                 } else {
50176                     done();
50177                 }
50178
50179                 _body
50180                     .append('p')
50181                     .attr('class', 'tag-reference-description')
50182                     .text(docs.description || _t('inspector.no_documentation_key'))
50183                     .append('a')
50184                     .attr('class', 'tag-reference-edit')
50185                     .attr('target', '_blank')
50186                     .attr('tabindex', -1)
50187                     .attr('title', _t('inspector.edit_reference'))
50188                     .attr('href', docs.editURL)
50189                     .call(svgIcon('#iD-icon-edit', 'inline'));
50190
50191                 if (docs.wiki) {
50192                     _body
50193                       .append('a')
50194                       .attr('class', 'tag-reference-link')
50195                       .attr('target', '_blank')
50196                       .attr('tabindex', -1)
50197                       .attr('href', docs.wiki.url)
50198                       .call(svgIcon('#iD-icon-out-link', 'inline'))
50199                       .append('span')
50200                       .text(_t(docs.wiki.text));
50201                 }
50202
50203                 // Add link to info about "good changeset comments" - #2923
50204                 if (what.key === 'comment') {
50205                     _body
50206                         .append('a')
50207                         .attr('class', 'tag-reference-comment-link')
50208                         .attr('target', '_blank')
50209                         .attr('tabindex', -1)
50210                         .call(svgIcon('#iD-icon-out-link', 'inline'))
50211                         .attr('href', _t('commit.about_changeset_comments_link'))
50212                         .append('span')
50213                         .text(_t('commit.about_changeset_comments'));
50214                 }
50215             }
50216
50217
50218             function done() {
50219                 _loaded = true;
50220
50221                 _button
50222                     .classed('tag-reference-loading', false);
50223
50224                 _body
50225                     .classed('expanded', true)
50226                     .transition()
50227                     .duration(200)
50228                     .style('max-height', '200px')
50229                     .style('opacity', '1');
50230
50231                 _showing = true;
50232
50233                 _button.selectAll('svg.icon use').each(function() {
50234                     var iconUse = select(this);
50235                     if (iconUse.attr('href') === '#iD-icon-info') {
50236                         iconUse.attr('href', '#iD-icon-info-filled');
50237                     }
50238                 });
50239             }
50240
50241
50242             function hide() {
50243                 _body
50244                     .transition()
50245                     .duration(200)
50246                     .style('max-height', '0px')
50247                     .style('opacity', '0')
50248                     .on('end', function () {
50249                         _body.classed('expanded', false);
50250                     });
50251
50252                 _showing = false;
50253
50254                 _button.selectAll('svg.icon use').each(function() {
50255                     var iconUse = select(this);
50256                     if (iconUse.attr('href') === '#iD-icon-info-filled') {
50257                         iconUse.attr('href', '#iD-icon-info');
50258                     }
50259                 });
50260
50261             }
50262
50263
50264             tagReference.button = function(selection, klass, iconName) {
50265                 _button = selection.selectAll('.tag-reference-button')
50266                     .data([0]);
50267
50268                 _button = _button.enter()
50269                     .append('button')
50270                     .attr('class', 'tag-reference-button ' + klass)
50271                     .attr('title', _t('icons.information'))
50272                     .attr('tabindex', -1)
50273                     .call(svgIcon('#iD-icon-' + (iconName || 'inspect')))
50274                     .merge(_button);
50275
50276                 _button
50277                     .on('click', function () {
50278                         event.stopPropagation();
50279                         event.preventDefault();
50280                         this.blur();    // avoid keeping focus on the button - #4641
50281                         if (_showing) {
50282                             hide();
50283                         } else if (_loaded) {
50284                             done();
50285                         } else {
50286                             load();
50287                         }
50288                     });
50289             };
50290
50291
50292             tagReference.body = function(selection) {
50293                 var itemID = what.qid || what.rtype || (what.key + '-' + what.value);
50294                 _body = selection.selectAll('.tag-reference-body')
50295                     .data([itemID], function(d) { return d; });
50296
50297                 _body.exit()
50298                     .remove();
50299
50300                 _body = _body.enter()
50301                     .append('div')
50302                     .attr('class', 'tag-reference-body')
50303                     .style('max-height', '0')
50304                     .style('opacity', '0')
50305                     .merge(_body);
50306
50307                 if (_showing === false) {
50308                     hide();
50309                 }
50310             };
50311
50312
50313             tagReference.showing = function(val) {
50314                 if (!arguments.length) { return _showing; }
50315                 _showing = val;
50316                 return tagReference;
50317             };
50318
50319
50320             return tagReference;
50321         }
50322
50323         function uiSectionRawTagEditor(id, context) {
50324
50325             var section = uiSection(id, context)
50326                 .classes('raw-tag-editor')
50327                 .title(function() {
50328                     var count = Object.keys(_tags).filter(function(d) { return d; }).length;
50329                     return _t('inspector.title_count', { title: _t('inspector.tags'), count: count });
50330                 })
50331                 .expandedByDefault(false)
50332                 .disclosureContent(renderDisclosureContent);
50333
50334             var taginfo = services.taginfo;
50335             var dispatch$1 = dispatch('change');
50336             var availableViews = [
50337                 { id: 'text', icon: '#fas-i-cursor' },
50338                 { id: 'list', icon: '#fas-th-list' }
50339             ];
50340
50341             var _tagView = (corePreferences('raw-tag-editor-view') || 'list');   // 'list, 'text'
50342             var _readOnlyTags = [];
50343             // the keys in the order we want them to display
50344             var _orderedKeys = [];
50345             var _showBlank = false;
50346             var _pendingChange = null;
50347             var _state;
50348             var _presets;
50349             var _tags;
50350             var _entityIDs;
50351
50352             function renderDisclosureContent(wrap) {
50353
50354                 // remove deleted keys
50355                 _orderedKeys = _orderedKeys.filter(function(key) {
50356                     return _tags[key] !== undefined;
50357                 });
50358
50359                 // When switching to a different entity or changing the state (hover/select)
50360                 // reorder the keys alphabetically.
50361                 // We trigger this by emptying the `_orderedKeys` array, then it will be rebuilt here.
50362                 // Otherwise leave their order alone - #5857, #5927
50363                 var all = Object.keys(_tags).sort();
50364                 var missingKeys = utilArrayDifference(all, _orderedKeys);
50365                 for (var i in missingKeys) {
50366                     _orderedKeys.push(missingKeys[i]);
50367                 }
50368
50369                 // assemble row data
50370                 var rowData = _orderedKeys.map(function(key, i) {
50371                     return { index: i, key: key, value: _tags[key] };
50372                 });
50373
50374                 // append blank row last, if necessary
50375                 if (!rowData.length || _showBlank) {
50376                     _showBlank = false;
50377                     rowData.push({ index: rowData.length, key: '', value: '' });
50378                 }
50379
50380
50381                 // View Options
50382                 var options = wrap.selectAll('.raw-tag-options')
50383                     .data([0]);
50384
50385                 options.exit()
50386                     .remove();
50387
50388                 var optionsEnter = options.enter()
50389                     .insert('div', ':first-child')
50390                     .attr('class', 'raw-tag-options');
50391
50392                 var optionEnter = optionsEnter.selectAll('.raw-tag-option')
50393                     .data(availableViews, function(d) { return d.id; })
50394                     .enter();
50395
50396                 optionEnter
50397                     .append('button')
50398                     .attr('class', function(d) {
50399                         return 'raw-tag-option raw-tag-option-' + d.id + (_tagView === d.id ? ' selected' : '');
50400                     })
50401                     .attr('title', function(d) { return _t('icons.' + d.id); })
50402                     .on('click', function(d) {
50403                         _tagView = d.id;
50404                         corePreferences('raw-tag-editor-view', d.id);
50405
50406                         wrap.selectAll('.raw-tag-option')
50407                             .classed('selected', function(datum) { return datum === d; });
50408
50409                         wrap.selectAll('.tag-text')
50410                             .classed('hide', (d.id !== 'text'))
50411                             .each(setTextareaHeight);
50412
50413                         wrap.selectAll('.tag-list, .add-row')
50414                             .classed('hide', (d.id !== 'list'));
50415                     })
50416                     .each(function(d) {
50417                         select(this)
50418                             .call(svgIcon(d.icon));
50419                     });
50420
50421
50422                 // View as Text
50423                 var textData = rowsToText(rowData);
50424                 var textarea = wrap.selectAll('.tag-text')
50425                     .data([0]);
50426
50427                 textarea = textarea.enter()
50428                     .append('textarea')
50429                     .attr('class', 'tag-text' + (_tagView !== 'text' ? ' hide' : ''))
50430                     .call(utilNoAuto)
50431                     .attr('placeholder', _t('inspector.key_value'))
50432                     .attr('spellcheck', 'false')
50433                     .merge(textarea);
50434
50435                 textarea
50436                     .call(utilGetSetValue, textData)
50437                     .each(setTextareaHeight)
50438                     .on('input', setTextareaHeight)
50439                     .on('blur', textChanged)
50440                     .on('change', textChanged);
50441
50442
50443                 // View as List
50444                 var list = wrap.selectAll('.tag-list')
50445                     .data([0]);
50446
50447                 list = list.enter()
50448                     .append('ul')
50449                     .attr('class', 'tag-list' + (_tagView !== 'list' ? ' hide' : ''))
50450                     .merge(list);
50451
50452
50453                 // Container for the Add button
50454                 var addRowEnter = wrap.selectAll('.add-row')
50455                     .data([0])
50456                     .enter()
50457                     .append('div')
50458                     .attr('class', 'add-row' + (_tagView !== 'list' ? ' hide' : ''));
50459
50460                 addRowEnter
50461                     .append('button')
50462                     .attr('class', 'add-tag')
50463                     .call(svgIcon('#iD-icon-plus', 'light'))
50464                     .on('click', addTag);
50465
50466                 addRowEnter
50467                     .append('div')
50468                     .attr('class', 'space-value');   // preserve space
50469
50470                 addRowEnter
50471                     .append('div')
50472                     .attr('class', 'space-buttons');  // preserve space
50473
50474
50475                 // Tag list items
50476                 var items = list.selectAll('.tag-row')
50477                     .data(rowData, function(d) { return d.key; });
50478
50479                 items.exit()
50480                     .each(unbind)
50481                     .remove();
50482
50483
50484                 // Enter
50485                 var itemsEnter = items.enter()
50486                     .append('li')
50487                     .attr('class', 'tag-row')
50488                     .classed('readonly', isReadOnly);
50489
50490                 var innerWrap = itemsEnter.append('div')
50491                     .attr('class', 'inner-wrap');
50492
50493                 innerWrap
50494                     .append('div')
50495                     .attr('class', 'key-wrap')
50496                     .append('input')
50497                     .property('type', 'text')
50498                     .attr('class', 'key')
50499                     .call(utilNoAuto)
50500                     .on('blur', keyChange)
50501                     .on('change', keyChange);
50502
50503                 innerWrap
50504                     .append('div')
50505                     .attr('class', 'value-wrap')
50506                     .append('input')
50507                     .property('type', 'text')
50508                     .attr('class', 'value')
50509                     .call(utilNoAuto)
50510                     .on('blur', valueChange)
50511                     .on('change', valueChange)
50512                     .on('keydown.push-more', pushMore);
50513
50514                 innerWrap
50515                     .append('button')
50516                     .attr('tabindex', -1)
50517                     .attr('class', 'form-field-button remove')
50518                     .attr('title', _t('icons.remove'))
50519                     .call(svgIcon('#iD-operation-delete'));
50520
50521
50522                 // Update
50523                 items = items
50524                     .merge(itemsEnter)
50525                     .sort(function(a, b) { return a.index - b.index; });
50526
50527                 items
50528                     .each(function(d) {
50529                         var row = select(this);
50530                         var key = row.select('input.key');      // propagate bound data
50531                         var value = row.select('input.value');  // propagate bound data
50532
50533                         if (_entityIDs && taginfo && _state !== 'hover') {
50534                             bindTypeahead(key, value);
50535                         }
50536
50537                         var reference;
50538
50539                         if (typeof d.value !== 'string') {
50540                             reference = uiTagReference({ key: d.key });
50541                         } else {
50542                             var isRelation = _entityIDs && _entityIDs.some(function(entityID) {
50543                                 return context.entity(entityID).type === 'relation';
50544                             });
50545                             if (isRelation && d.key === 'type') {
50546                                 reference = uiTagReference({ rtype: d.value });
50547                             } else {
50548                                 reference = uiTagReference({ key: d.key, value: d.value });
50549                             }
50550                         }
50551
50552                         if (_state === 'hover') {
50553                             reference.showing(false);
50554                         }
50555
50556                         row.select('.inner-wrap')      // propagate bound data
50557                             .call(reference.button);
50558
50559                         row.call(reference.body);
50560
50561                         row.select('button.remove');   // propagate bound data
50562                     });
50563
50564                 items.selectAll('input.key')
50565                     .attr('title', function(d) { return d.key; })
50566                     .call(utilGetSetValue, function(d) { return d.key; })
50567                     .attr('readonly', function(d) {
50568                         return (isReadOnly(d) || (typeof d.value !== 'string')) || null;
50569                     });
50570
50571                 items.selectAll('input.value')
50572                     .attr('title', function(d) {
50573                         return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : d.value;
50574                     })
50575                     .classed('mixed', function(d) {
50576                         return Array.isArray(d.value);
50577                     })
50578                     .attr('placeholder', function(d) {
50579                         return typeof d.value === 'string' ? null : _t('inspector.multiple_values');
50580                     })
50581                     .call(utilGetSetValue, function(d) {
50582                         return typeof d.value === 'string' ? d.value : '';
50583                     })
50584                     .attr('readonly', function(d) {
50585                         return isReadOnly(d) || null;
50586                     });
50587
50588                 items.selectAll('button.remove')
50589                     .on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'down', removeTag);  // 'click' fires too late - #5878
50590
50591             }
50592
50593             function isReadOnly(d) {
50594                 for (var i = 0; i < _readOnlyTags.length; i++) {
50595                     if (d.key.match(_readOnlyTags[i]) !== null) {
50596                         return true;
50597                     }
50598                 }
50599                 return false;
50600             }
50601
50602             function setTextareaHeight() {
50603                 if (_tagView !== 'text') { return; }
50604
50605                 var selection = select(this);
50606                 selection.style('height', null);
50607                 selection.style('height', selection.node().scrollHeight + 5 + 'px');
50608             }
50609
50610             function stringify(s) {
50611                 return JSON.stringify(s).slice(1, -1);   // without leading/trailing "
50612             }
50613
50614             function unstringify(s) {
50615                 var leading = '';
50616                 var trailing = '';
50617                 if (s.length < 1 || s.charAt(0) !== '"') {
50618                     leading = '"';
50619                 }
50620                 if (s.length < 2 || s.charAt(s.length - 1) !== '"' ||
50621                     (s.charAt(s.length - 1) === '"' && s.charAt(s.length - 2) === '\\')
50622                 ) {
50623                     trailing = '"';
50624                 }
50625                 return JSON.parse(leading + s + trailing);
50626             }
50627
50628             function rowsToText(rows) {
50629                 var str = rows
50630                     .filter(function(row) { return row.key && row.key.trim() !== ''; })
50631                     .map(function(row) {
50632                         var rawVal = row.value;
50633                         if (typeof rawVal !== 'string') { rawVal = '*'; }
50634                         var val = rawVal ? stringify(rawVal) : '';
50635                         return stringify(row.key) + '=' + val;
50636                     })
50637                     .join('\n');
50638
50639                 if (_state !== 'hover' && str.length) {
50640                     return str + '\n';
50641                 }
50642                 return  str;
50643             }
50644
50645             function textChanged() {
50646                 var newText = this.value.trim();
50647                 var newTags = {};
50648                 newText.split('\n').forEach(function(row) {
50649                     var m = row.match(/^\s*([^=]+)=(.*)$/);
50650                     if (m !== null) {
50651                         var k = context.cleanTagKey(unstringify(m[1].trim()));
50652                         var v = context.cleanTagValue(unstringify(m[2].trim()));
50653                         newTags[k] = v;
50654                     }
50655                 });
50656
50657                 var tagDiff = utilTagDiff(_tags, newTags);
50658                 if (!tagDiff.length) { return; }
50659
50660                 _pendingChange  = _pendingChange || {};
50661
50662                 tagDiff.forEach(function(change) {
50663                     if (isReadOnly({ key: change.key })) { return; }
50664
50665                     // skip unchanged multiselection placeholders
50666                     if (change.newVal === '*' && typeof change.oldVal !== 'string') { return; }
50667
50668                     if (change.type === '-') {
50669                         _pendingChange[change.key] = undefined;
50670                     } else if (change.type === '+') {
50671                         _pendingChange[change.key] = change.newVal || '';
50672                     }
50673                 });
50674
50675                 if (Object.keys(_pendingChange).length === 0) {
50676                     _pendingChange = null;
50677                     return;
50678                 }
50679
50680                 scheduleChange();
50681             }
50682
50683             function pushMore() {
50684                 // if pressing Tab on the last value field with content, add a blank row
50685                 if (event.keyCode === 9 && !event.shiftKey &&
50686                     section.selection().selectAll('.tag-list li:last-child input.value').node() === this &&
50687                     utilGetSetValue(select(this))) {
50688                     addTag();
50689                 }
50690             }
50691
50692             function bindTypeahead(key, value) {
50693                 if (isReadOnly(key.datum())) { return; }
50694
50695                 if (Array.isArray(value.datum().value)) {
50696                     value.call(uiCombobox(context, 'tag-value')
50697                         .minItems(1)
50698                         .fetcher(function(value, callback) {
50699                             var keyString = utilGetSetValue(key);
50700                             if (!_tags[keyString]) { return; }
50701                             var data = _tags[keyString].filter(Boolean).map(function(tagValue) {
50702                                 return {
50703                                     value: tagValue,
50704                                     title: tagValue
50705                                 };
50706                             });
50707                             callback(data);
50708                         }));
50709                     return;
50710                 }
50711
50712                 var geometry = context.graph().geometry(_entityIDs[0]);
50713
50714                 key.call(uiCombobox(context, 'tag-key')
50715                     .fetcher(function(value, callback) {
50716                         taginfo.keys({
50717                             debounce: true,
50718                             geometry: geometry,
50719                             query: value
50720                         }, function(err, data) {
50721                             if (!err) {
50722                                 var filtered = data.filter(function(d) { return _tags[d.value] === undefined; });
50723                                 callback(sort(value, filtered));
50724                             }
50725                         });
50726                     }));
50727
50728                 value.call(uiCombobox(context, 'tag-value')
50729                     .fetcher(function(value, callback) {
50730                         taginfo.values({
50731                             debounce: true,
50732                             key: utilGetSetValue(key),
50733                             geometry: geometry,
50734                             query: value
50735                         }, function(err, data) {
50736                             if (!err) { callback(sort(value, data)); }
50737                         });
50738                     }));
50739
50740
50741                 function sort(value, data) {
50742                     var sameletter = [];
50743                     var other = [];
50744                     for (var i = 0; i < data.length; i++) {
50745                         if (data[i].value.substring(0, value.length) === value) {
50746                             sameletter.push(data[i]);
50747                         } else {
50748                             other.push(data[i]);
50749                         }
50750                     }
50751                     return sameletter.concat(other);
50752                 }
50753             }
50754
50755             function unbind() {
50756                 var row = select(this);
50757
50758                 row.selectAll('input.key')
50759                     .call(uiCombobox.off, context);
50760
50761                 row.selectAll('input.value')
50762                     .call(uiCombobox.off, context);
50763             }
50764
50765             function keyChange(d) {
50766                 if (select(this).attr('readonly')) { return; }
50767
50768                 var kOld = d.key;
50769
50770                 // exit if we are currently about to delete this row anyway - #6366
50771                 if (_pendingChange && _pendingChange.hasOwnProperty(kOld) && _pendingChange[kOld] === undefined) { return; }
50772
50773                 var kNew = context.cleanTagKey(this.value.trim());
50774
50775                 // allow no change if the key should be readonly
50776                 if (isReadOnly({ key: kNew })) {
50777                     this.value = kOld;
50778                     return;
50779                 }
50780
50781                 if (kNew &&
50782                     kNew !== kOld &&
50783                     _tags[kNew] !== undefined) {
50784                     // new key is already in use, switch focus to the existing row
50785
50786                     this.value = kOld;                // reset the key
50787                     section.selection().selectAll('.tag-list input.value')
50788                         .each(function(d) {
50789                             if (d.key === kNew) {     // send focus to that other value combo instead
50790                                 var input = select(this).node();
50791                                 input.focus();
50792                                 input.select();
50793                             }
50794                         });
50795                     return;
50796                 }
50797
50798
50799                 var row = this.parentNode.parentNode;
50800                 var inputVal = select(row).selectAll('input.value');
50801                 var vNew = context.cleanTagValue(utilGetSetValue(inputVal));
50802
50803                 _pendingChange = _pendingChange || {};
50804
50805                 if (kOld) {
50806                     _pendingChange[kOld] = undefined;
50807                 }
50808
50809                 _pendingChange[kNew] = vNew;
50810
50811                 // update the ordered key index so this row doesn't change position
50812                 var existingKeyIndex = _orderedKeys.indexOf(kOld);
50813                 if (existingKeyIndex !== -1) { _orderedKeys[existingKeyIndex] = kNew; }
50814
50815                 d.key = kNew;    // update datum to avoid exit/enter on tag update
50816                 d.value = vNew;
50817
50818                 this.value = kNew;
50819                 utilGetSetValue(inputVal, vNew);
50820                 scheduleChange();
50821             }
50822
50823             function valueChange(d) {
50824                 if (isReadOnly(d)) { return; }
50825
50826                 // exit if this is a multiselection and no value was entered
50827                 if (typeof d.value !== 'string' && !this.value) { return; }
50828
50829                 // exit if we are currently about to delete this row anyway - #6366
50830                 if (_pendingChange && _pendingChange.hasOwnProperty(d.key) && _pendingChange[d.key] === undefined) { return; }
50831
50832                 _pendingChange = _pendingChange || {};
50833
50834                 _pendingChange[d.key] = context.cleanTagValue(this.value);
50835                 scheduleChange();
50836             }
50837
50838             function removeTag(d) {
50839                 if (isReadOnly(d)) { return; }
50840
50841                 if (d.key === '') {    // removing the blank row
50842                     _showBlank = false;
50843                     section.reRender();
50844
50845                 } else {
50846                     // remove the key from the ordered key index
50847                     _orderedKeys = _orderedKeys.filter(function(key) { return key !== d.key; });
50848
50849                     _pendingChange  = _pendingChange || {};
50850                     _pendingChange[d.key] = undefined;
50851                     scheduleChange();
50852                 }
50853             }
50854
50855             function addTag() {
50856                 // Delay render in case this click is blurring an edited combo.
50857                 // Without the setTimeout, the `content` render would wipe out the pending tag change.
50858                 window.setTimeout(function() {
50859                     _showBlank = true;
50860                     section.reRender();
50861                     section.selection().selectAll('.tag-list li:last-child input.key').node().focus();
50862                 }, 20);
50863             }
50864
50865             function scheduleChange() {
50866                 // Cache IDs in case the editor is reloaded before the change event is called. - #6028
50867                 var entityIDs = _entityIDs;
50868
50869                 // Delay change in case this change is blurring an edited combo. - #5878
50870                 window.setTimeout(function() {
50871                     if (!_pendingChange) { return; }
50872
50873                     dispatch$1.call('change', this, entityIDs, _pendingChange);
50874                     _pendingChange = null;
50875                 }, 10);
50876             }
50877
50878
50879             section.state = function(val) {
50880                 if (!arguments.length) { return _state; }
50881                 if (_state !== val) {
50882                     _orderedKeys = [];
50883                     _state = val;
50884                 }
50885                 return section;
50886             };
50887
50888
50889             section.presets = function(val) {
50890                 if (!arguments.length) { return _presets; }
50891                 _presets = val;
50892                 if (_presets && _presets.length && _presets[0].isFallback()) {
50893                     section.disclosureExpanded(true);
50894                 } else {
50895                     section.disclosureExpanded(null);
50896                 }
50897                 return section;
50898             };
50899
50900
50901             section.tags = function(val) {
50902                 if (!arguments.length) { return _tags; }
50903                 _tags = val;
50904                 return section;
50905             };
50906
50907
50908             section.entityIDs = function(val) {
50909                 if (!arguments.length) { return _entityIDs; }
50910                 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
50911                     _entityIDs = val;
50912                     _orderedKeys = [];
50913                 }
50914                 return section;
50915             };
50916
50917
50918             // pass an array of regular expressions to test against the tag key
50919             section.readOnlyTags = function(val) {
50920                 if (!arguments.length) { return _readOnlyTags; }
50921                 _readOnlyTags = val;
50922                 return section;
50923             };
50924
50925
50926             return utilRebind(section, dispatch$1, 'on');
50927         }
50928
50929         function uiDataEditor(context) {
50930             var dataHeader = uiDataHeader();
50931             var rawTagEditor = uiSectionRawTagEditor('custom-data-tag-editor', context)
50932                 .expandedByDefault(true)
50933                 .readOnlyTags([/./]);
50934             var _datum;
50935
50936
50937             function dataEditor(selection) {
50938
50939                 var header = selection.selectAll('.header')
50940                     .data([0]);
50941
50942                 var headerEnter = header.enter()
50943                     .append('div')
50944                     .attr('class', 'header fillL');
50945
50946                 headerEnter
50947                     .append('button')
50948                     .attr('class', 'close')
50949                     .on('click', function() {
50950                         context.enter(modeBrowse(context));
50951                     })
50952                     .call(svgIcon('#iD-icon-close'));
50953
50954                 headerEnter
50955                     .append('h3')
50956                     .text(_t('map_data.title'));
50957
50958
50959                 var body = selection.selectAll('.body')
50960                     .data([0]);
50961
50962                 body = body.enter()
50963                     .append('div')
50964                     .attr('class', 'body')
50965                     .merge(body);
50966
50967                 var editor = body.selectAll('.data-editor')
50968                     .data([0]);
50969
50970                 // enter/update
50971                 editor.enter()
50972                     .append('div')
50973                     .attr('class', 'modal-section data-editor')
50974                     .merge(editor)
50975                     .call(dataHeader.datum(_datum));
50976
50977                 var rte = body.selectAll('.raw-tag-editor')
50978                     .data([0]);
50979
50980                 // enter/update
50981                 rte.enter()
50982                     .append('div')
50983                     .attr('class', 'raw-tag-editor data-editor')
50984                     .merge(rte)
50985                     .call(rawTagEditor
50986                         .tags((_datum && _datum.properties) || {})
50987                         .state('hover')
50988                         .render
50989                     )
50990                     .selectAll('textarea.tag-text')
50991                     .attr('readonly', true)
50992                     .classed('readonly', true);
50993             }
50994
50995
50996             dataEditor.datum = function(val) {
50997                 if (!arguments.length) { return _datum; }
50998                 _datum = val;
50999                 return this;
51000             };
51001
51002
51003             return dataEditor;
51004         }
51005
51006         function modeSelectData(context, selectedDatum) {
51007             var mode = {
51008                 id: 'select-data',
51009                 button: 'browse'
51010             };
51011
51012             var keybinding = utilKeybinding('select-data');
51013             var dataEditor = uiDataEditor(context);
51014
51015             var behaviors = [
51016                 behaviorBreathe(),
51017                 behaviorHover(context),
51018                 behaviorSelect(context),
51019                 behaviorLasso(context),
51020                 modeDragNode(context).behavior,
51021                 modeDragNote(context).behavior
51022             ];
51023
51024
51025             // class the data as selected, or return to browse mode if the data is gone
51026             function selectData(drawn) {
51027                 var selection = context.surface().selectAll('.layer-mapdata .data' + selectedDatum.__featurehash__);
51028
51029                 if (selection.empty()) {
51030                     // Return to browse mode if selected DOM elements have
51031                     // disappeared because the user moved them out of view..
51032                     var source = event && event.type === 'zoom' && event.sourceEvent;
51033                     if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
51034                         context.enter(modeBrowse(context));
51035                     }
51036                 } else {
51037                     selection.classed('selected', true);
51038                 }
51039             }
51040
51041
51042             function esc() {
51043                 if (context.container().select('.combobox').size()) { return; }
51044                 context.enter(modeBrowse(context));
51045             }
51046
51047
51048             mode.zoomToSelected = function() {
51049                 var extent = geoExtent(d3_geoBounds(selectedDatum));
51050                 context.map().centerZoomEase(extent.center(), context.map().trimmedExtentZoom(extent));
51051             };
51052
51053
51054             mode.enter = function() {
51055                 behaviors.forEach(context.install);
51056
51057                 keybinding
51058                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
51059                     .on('⎋', esc, true);
51060
51061                 select(document)
51062                     .call(keybinding);
51063
51064                 selectData();
51065
51066                 var sidebar = context.ui().sidebar;
51067                 sidebar.show(dataEditor.datum(selectedDatum));
51068
51069                 // expand the sidebar, avoid obscuring the data if needed
51070                 var extent = geoExtent(d3_geoBounds(selectedDatum));
51071                 sidebar.expand(sidebar.intersects(extent));
51072
51073                 context.map()
51074                     .on('drawn.select-data', selectData);
51075             };
51076
51077
51078             mode.exit = function() {
51079                 behaviors.forEach(context.uninstall);
51080
51081                 select(document)
51082                     .call(keybinding.unbind);
51083
51084                 context.surface()
51085                     .selectAll('.layer-mapdata .selected')
51086                     .classed('selected hover', false);
51087
51088                 context.map()
51089                     .on('drawn.select-data', null);
51090
51091                 context.ui().sidebar
51092                     .hide();
51093             };
51094
51095
51096             return mode;
51097         }
51098
51099         function uiImproveOsmComments() {
51100           var _qaItem;
51101
51102           function issueComments(selection) {
51103             // make the div immediately so it appears above the buttons
51104             var comments = selection.selectAll('.comments-container')
51105               .data([0]);
51106
51107             comments = comments.enter()
51108               .append('div')
51109                 .attr('class', 'comments-container')
51110               .merge(comments);
51111
51112             // must retrieve comments from API before they can be displayed
51113             services.improveOSM.getComments(_qaItem)
51114               .then(function (d) {
51115                 if (!d.comments) { return; } // nothing to do here
51116
51117                 var commentEnter = comments.selectAll('.comment')
51118                   .data(d.comments)
51119                   .enter()
51120                   .append('div')
51121                     .attr('class', 'comment');
51122
51123                 commentEnter
51124                   .append('div')
51125                     .attr('class', 'comment-avatar')
51126                     .call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
51127
51128                 var mainEnter = commentEnter
51129                   .append('div')
51130                   .attr('class', 'comment-main');
51131
51132                 var metadataEnter = mainEnter
51133                   .append('div')
51134                     .attr('class', 'comment-metadata');
51135
51136                 metadataEnter
51137                   .append('div')
51138                     .attr('class', 'comment-author')
51139                     .each(function(d) {
51140                       var osm = services.osm;
51141                       var selection = select(this);
51142                       if (osm && d.username) {
51143                         selection = selection
51144                           .append('a')
51145                           .attr('class', 'comment-author-link')
51146                           .attr('href', osm.userURL(d.username))
51147                           .attr('tabindex', -1)
51148                           .attr('target', '_blank');
51149                       }
51150                       selection
51151                         .text(function (d) { return d.username; });
51152                     });
51153
51154                 metadataEnter
51155                   .append('div')
51156                     .attr('class', 'comment-date')
51157                     .text(function (d) { return _t('note.status.commented', { when: localeDateString(d.timestamp) }); });
51158
51159                 mainEnter
51160                   .append('div')
51161                     .attr('class', 'comment-text')
51162                   .append('p')
51163                     .text(function (d) { return d.text; });
51164             })
51165             .catch(function (err) {
51166               console.log(err); // eslint-disable-line no-console
51167             });
51168           }
51169
51170           function localeDateString(s) {
51171             if (!s) { return null; }
51172             var options = { day: 'numeric', month: 'short', year: 'numeric' };
51173             var d = new Date(s * 1000); // timestamp is served in seconds, date takes ms
51174             if (isNaN(d.getTime())) { return null; }
51175             return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
51176           }
51177
51178           issueComments.issue = function(val) {
51179             if (!arguments.length) { return _qaItem; }
51180             _qaItem = val;
51181             return issueComments;
51182           };
51183
51184           return issueComments;
51185         }
51186
51187         function uiImproveOsmDetails(context) {
51188           var _qaItem;
51189
51190
51191           function issueDetail(d) {
51192             if (d.desc) { return d.desc; }
51193             var issueKey = d.issueKey;
51194             d.replacements = d.replacements || {};
51195             d.replacements.default = _t('inspector.unknown');  // special key `default` works as a fallback string
51196             return _t(("QA.improveOSM.error_types." + issueKey + ".description"), d.replacements);
51197           }
51198
51199
51200           function improveOsmDetails(selection) {
51201             var details = selection.selectAll('.error-details')
51202               .data(
51203                 (_qaItem ? [_qaItem] : []),
51204                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51205               );
51206
51207             details.exit()
51208               .remove();
51209
51210             var detailsEnter = details.enter()
51211               .append('div')
51212                 .attr('class', 'error-details qa-details-container');
51213
51214
51215             // description
51216             var descriptionEnter = detailsEnter
51217               .append('div')
51218                 .attr('class', 'qa-details-subsection');
51219
51220             descriptionEnter
51221               .append('h4')
51222                 .text(function () { return _t('QA.keepRight.detail_description'); });
51223
51224             descriptionEnter
51225               .append('div')
51226                 .attr('class', 'qa-details-description-text')
51227                 .html(issueDetail);
51228
51229             // If there are entity links in the error message..
51230             var relatedEntities = [];
51231             descriptionEnter.selectAll('.error_entity_link, .error_object_link')
51232               .each(function() {
51233                 var link = select(this);
51234                 var isObjectLink = link.classed('error_object_link');
51235                 var entityID = isObjectLink ?
51236                   (utilEntityRoot(_qaItem.objectType) + _qaItem.objectId)
51237                   : this.textContent;
51238                 var entity = context.hasEntity(entityID);
51239
51240                 relatedEntities.push(entityID);
51241
51242                 // Add click handler
51243                 link
51244                   .on('mouseenter', function () {
51245                     utilHighlightEntities([entityID], true, context);
51246                   })
51247                   .on('mouseleave', function () {
51248                     utilHighlightEntities([entityID], false, context);
51249                   })
51250                   .on('click', function () {
51251                     event.preventDefault();
51252
51253                     utilHighlightEntities([entityID], false, context);
51254
51255                     var osmlayer = context.layers().layer('osm');
51256                     if (!osmlayer.enabled()) {
51257                       osmlayer.enabled(true);
51258                     }
51259
51260                     context.map().centerZoom(_qaItem.loc, 20);
51261
51262                     if (entity) {
51263                       context.enter(modeSelect(context, [entityID]));
51264                     } else {
51265                       context.loadEntity(entityID, function () {
51266                         context.enter(modeSelect(context, [entityID]));
51267                       });
51268                     }
51269                   });
51270
51271                 // Replace with friendly name if possible
51272                 // (The entity may not yet be loaded into the graph)
51273                 if (entity) {
51274                   var name = utilDisplayName(entity);  // try to use common name
51275
51276                   if (!name && !isObjectLink) {
51277                     var preset = _mainPresetIndex.match(entity, context.graph());
51278                     name = preset && !preset.isFallback() && preset.name();  // fallback to preset name
51279                   }
51280
51281                   if (name) {
51282                     this.innerText = name;
51283                   }
51284                 }
51285               });
51286
51287             // Don't hide entities related to this error - #5880
51288             context.features().forceVisible(relatedEntities);
51289             context.map().pan([0,0]);  // trigger a redraw
51290           }
51291
51292           improveOsmDetails.issue = function(val) {
51293             if (!arguments.length) { return _qaItem; }
51294             _qaItem = val;
51295             return improveOsmDetails;
51296           };
51297
51298           return improveOsmDetails;
51299         }
51300
51301         function uiImproveOsmHeader() {
51302           var _qaItem;
51303
51304
51305           function issueTitle(d) {
51306             var issueKey = d.issueKey;
51307             d.replacements = d.replacements || {};
51308             d.replacements.default = _t('inspector.unknown');  // special key `default` works as a fallback string
51309             return _t(("QA.improveOSM.error_types." + issueKey + ".title"), d.replacements);
51310           }
51311
51312
51313           function improveOsmHeader(selection) {
51314             var header = selection.selectAll('.qa-header')
51315               .data(
51316                 (_qaItem ? [_qaItem] : []),
51317                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51318               );
51319
51320             header.exit()
51321               .remove();
51322
51323             var headerEnter = header.enter()
51324               .append('div')
51325                 .attr('class', 'qa-header');
51326
51327             var svgEnter = headerEnter
51328               .append('div')
51329                 .attr('class', 'qa-header-icon')
51330                 .classed('new', function (d) { return d.id < 0; })
51331               .append('svg')
51332                 .attr('width', '20px')
51333                 .attr('height', '30px')
51334                 .attr('viewbox', '0 0 20 30')
51335                 .attr('class', function (d) { return ("preset-icon-28 qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.itemType)); });
51336
51337             svgEnter
51338               .append('polygon')
51339                 .attr('fill', 'currentColor')
51340                 .attr('class', 'qaItem-fill')
51341                 .attr('points', '16,3 4,3 1,6 1,17 4,20 7,20 10,27 13,20 16,20 19,17.033 19,6');
51342
51343             svgEnter
51344               .append('use')
51345                 .attr('class', 'icon-annotation')
51346                 .attr('width', '13px')
51347                 .attr('height', '13px')
51348                 .attr('transform', 'translate(3.5, 5)')
51349                 .attr('xlink:href', function (d) {
51350                   var picon = d.icon;
51351                   if (!picon) {
51352                     return '';
51353                   } else {
51354                     var isMaki = /^maki-/.test(picon);
51355                     return ("#" + picon + (isMaki ? '-11' : ''));
51356                   }
51357                 });
51358
51359             headerEnter
51360               .append('div')
51361                 .attr('class', 'qa-header-label')
51362                 .text(issueTitle);
51363           }
51364
51365           improveOsmHeader.issue = function(val) {
51366             if (!arguments.length) { return _qaItem; }
51367             _qaItem = val;
51368             return improveOsmHeader;
51369           };
51370
51371           return improveOsmHeader;
51372         }
51373
51374         function uiImproveOsmEditor(context) {
51375           var dispatch$1 = dispatch('change');
51376           var qaDetails = uiImproveOsmDetails(context);
51377           var qaComments = uiImproveOsmComments();
51378           var qaHeader = uiImproveOsmHeader();
51379
51380           var _qaItem;
51381
51382           function improveOsmEditor(selection) {
51383
51384             var headerEnter = selection.selectAll('.header')
51385               .data([0])
51386               .enter()
51387               .append('div')
51388                 .attr('class', 'header fillL');
51389
51390             headerEnter
51391               .append('button')
51392                 .attr('class', 'close')
51393                 .on('click', function () { return context.enter(modeBrowse(context)); })
51394                 .call(svgIcon('#iD-icon-close'));
51395
51396             headerEnter
51397               .append('h3')
51398                 .text(_t('QA.improveOSM.title'));
51399
51400             var body = selection.selectAll('.body')
51401               .data([0]);
51402
51403             body = body.enter()
51404               .append('div')
51405                 .attr('class', 'body')
51406               .merge(body);
51407
51408             var editor = body.selectAll('.qa-editor')
51409               .data([0]);
51410
51411             editor.enter()
51412               .append('div')
51413                 .attr('class', 'modal-section qa-editor')
51414               .merge(editor)
51415                 .call(qaHeader.issue(_qaItem))
51416                 .call(qaDetails.issue(_qaItem))
51417                 .call(qaComments.issue(_qaItem))
51418                 .call(improveOsmSaveSection);
51419           }
51420
51421           function improveOsmSaveSection(selection) {
51422             var isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51423             var isShown = (_qaItem && (isSelected || _qaItem.newComment || _qaItem.comment));
51424             var saveSection = selection.selectAll('.qa-save')
51425               .data(
51426                 (isShown ? [_qaItem] : []),
51427                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51428               );
51429
51430             // exit
51431             saveSection.exit()
51432               .remove();
51433
51434             // enter
51435             var saveSectionEnter = saveSection.enter()
51436               .append('div')
51437                 .attr('class', 'qa-save save-section cf');
51438
51439             saveSectionEnter
51440               .append('h4')
51441                 .attr('class', '.qa-save-header')
51442                 .text(_t('note.newComment'));
51443
51444             saveSectionEnter
51445               .append('textarea')
51446                 .attr('class', 'new-comment-input')
51447                 .attr('placeholder', _t('QA.keepRight.comment_placeholder'))
51448                 .attr('maxlength', 1000)
51449                 .property('value', function (d) { return d.newComment; })
51450                 .call(utilNoAuto)
51451                 .on('input', changeInput)
51452                 .on('blur', changeInput);
51453
51454             // update
51455             saveSection = saveSectionEnter
51456               .merge(saveSection)
51457                 .call(qaSaveButtons);
51458
51459             function changeInput() {
51460               var input = select(this);
51461               var val = input.property('value').trim();
51462
51463               if (val === '') {
51464                 val = undefined;
51465               }
51466
51467               // store the unsaved comment with the issue itself
51468               _qaItem = _qaItem.update({ newComment: val });
51469
51470               var qaService = services.improveOSM;
51471               if (qaService) {
51472                 qaService.replaceItem(_qaItem);
51473               }
51474
51475               saveSection
51476                 .call(qaSaveButtons);
51477             }
51478           }
51479
51480           function qaSaveButtons(selection) {
51481             var isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51482             var buttonSection = selection.selectAll('.buttons')
51483               .data((isSelected ? [_qaItem] : []), function (d) { return d.status + d.id; });
51484
51485             // exit
51486             buttonSection.exit()
51487               .remove();
51488
51489             // enter
51490             var buttonEnter = buttonSection.enter()
51491               .append('div')
51492                 .attr('class', 'buttons');
51493
51494             buttonEnter
51495               .append('button')
51496                 .attr('class', 'button comment-button action')
51497                 .text(_t('QA.keepRight.save_comment'));
51498
51499             buttonEnter
51500               .append('button')
51501                 .attr('class', 'button close-button action');
51502
51503             buttonEnter
51504               .append('button')
51505                 .attr('class', 'button ignore-button action');
51506
51507             // update
51508             buttonSection = buttonSection
51509               .merge(buttonEnter);
51510
51511             buttonSection.select('.comment-button')
51512               .attr('disabled', function (d) { return d.newComment ? null : true; })
51513               .on('click.comment', function(d) {
51514                 this.blur();    // avoid keeping focus on the button - #4641
51515                 var qaService = services.improveOSM;
51516                 if (qaService) {
51517                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
51518                 }
51519               });
51520
51521             buttonSection.select('.close-button')
51522               .text(function (d) {
51523                 var andComment = (d.newComment ? '_comment' : '');
51524                 return _t(("QA.keepRight.close" + andComment));
51525               })
51526               .on('click.close', function(d) {
51527                 this.blur();    // avoid keeping focus on the button - #4641
51528                 var qaService = services.improveOSM;
51529                 if (qaService) {
51530                   d.newStatus = 'SOLVED';
51531                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
51532                 }
51533               });
51534
51535             buttonSection.select('.ignore-button')
51536               .text(function (d) {
51537                 var andComment = (d.newComment ? '_comment' : '');
51538                 return _t(("QA.keepRight.ignore" + andComment));
51539               })
51540               .on('click.ignore', function(d) {
51541                 this.blur();    // avoid keeping focus on the button - #4641
51542                 var qaService = services.improveOSM;
51543                 if (qaService) {
51544                   d.newStatus = 'INVALID';
51545                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
51546                 }
51547               });
51548           }
51549
51550           // NOTE: Don't change method name until UI v3 is merged
51551           improveOsmEditor.error = function(val) {
51552             if (!arguments.length) { return _qaItem; }
51553             _qaItem = val;
51554             return improveOsmEditor;
51555           };
51556
51557           return utilRebind(improveOsmEditor, dispatch$1, 'on');
51558         }
51559
51560         function uiKeepRightDetails(context) {
51561           var _qaItem;
51562
51563
51564           function issueDetail(d) {
51565             var itemType = d.itemType;
51566             var parentIssueType = d.parentIssueType;
51567             var unknown = _t('inspector.unknown');
51568             var replacements = d.replacements || {};
51569             replacements.default = unknown;  // special key `default` works as a fallback string
51570
51571             var detail = _t(("QA.keepRight.errorTypes." + itemType + ".description"), replacements);
51572             if (detail === unknown) {
51573               detail = _t(("QA.keepRight.errorTypes." + parentIssueType + ".description"), replacements);
51574             }
51575             return detail;
51576           }
51577
51578
51579           function keepRightDetails(selection) {
51580             var details = selection.selectAll('.error-details')
51581               .data(
51582                 (_qaItem ? [_qaItem] : []),
51583                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51584               );
51585
51586             details.exit()
51587               .remove();
51588
51589             var detailsEnter = details.enter()
51590               .append('div')
51591                 .attr('class', 'error-details qa-details-container');
51592
51593             // description
51594             var descriptionEnter = detailsEnter
51595               .append('div')
51596                 .attr('class', 'qa-details-subsection');
51597
51598             descriptionEnter
51599               .append('h4')
51600                 .text(function () { return _t('QA.keepRight.detail_description'); });
51601
51602             descriptionEnter
51603               .append('div')
51604                 .attr('class', 'qa-details-description-text')
51605                 .html(issueDetail);
51606
51607             // If there are entity links in the error message..
51608             var relatedEntities = [];
51609             descriptionEnter.selectAll('.error_entity_link, .error_object_link')
51610               .each(function() {
51611                 var link = select(this);
51612                 var isObjectLink = link.classed('error_object_link');
51613                 var entityID = isObjectLink ?
51614                   (utilEntityRoot(_qaItem.objectType) + _qaItem.objectId)
51615                   : this.textContent;
51616                 var entity = context.hasEntity(entityID);
51617
51618                 relatedEntities.push(entityID);
51619
51620                 // Add click handler
51621                 link
51622                   .on('mouseenter', function () {
51623                     utilHighlightEntities([entityID], true, context);
51624                   })
51625                   .on('mouseleave', function () {
51626                     utilHighlightEntities([entityID], false, context);
51627                   })
51628                   .on('click', function () {
51629                     event.preventDefault();
51630
51631                     utilHighlightEntities([entityID], false, context);
51632
51633                     var osmlayer = context.layers().layer('osm');
51634                     if (!osmlayer.enabled()) {
51635                       osmlayer.enabled(true);
51636                     }
51637
51638                     context.map().centerZoomEase(_qaItem.loc, 20);
51639
51640                     if (entity) {
51641                       context.enter(modeSelect(context, [entityID]));
51642                     } else {
51643                       context.loadEntity(entityID, function () {
51644                         context.enter(modeSelect(context, [entityID]));
51645                       });
51646                     }
51647                   });
51648
51649                 // Replace with friendly name if possible
51650                 // (The entity may not yet be loaded into the graph)
51651                 if (entity) {
51652                   var name = utilDisplayName(entity);  // try to use common name
51653
51654                   if (!name && !isObjectLink) {
51655                     var preset = _mainPresetIndex.match(entity, context.graph());
51656                     name = preset && !preset.isFallback() && preset.name();  // fallback to preset name
51657                   }
51658
51659                   if (name) {
51660                     this.innerText = name;
51661                   }
51662                 }
51663               });
51664
51665             // Don't hide entities related to this issue - #5880
51666             context.features().forceVisible(relatedEntities);
51667             context.map().pan([0,0]);  // trigger a redraw
51668           }
51669
51670           keepRightDetails.issue = function(val) {
51671             if (!arguments.length) { return _qaItem; }
51672             _qaItem = val;
51673             return keepRightDetails;
51674           };
51675
51676           return keepRightDetails;
51677         }
51678
51679         function uiKeepRightHeader() {
51680           var _qaItem;
51681
51682
51683           function issueTitle(d) {
51684             var itemType = d.itemType;
51685             var parentIssueType = d.parentIssueType;
51686             var unknown = _t('inspector.unknown');
51687             var replacements = d.replacements || {};
51688             replacements.default = unknown;  // special key `default` works as a fallback string
51689
51690             var title = _t(("QA.keepRight.errorTypes." + itemType + ".title"), replacements);
51691             if (title === unknown) {
51692               title = _t(("QA.keepRight.errorTypes." + parentIssueType + ".title"), replacements);
51693             }
51694             return title;
51695           }
51696
51697
51698           function keepRightHeader(selection) {
51699             var header = selection.selectAll('.qa-header')
51700               .data(
51701                 (_qaItem ? [_qaItem] : []),
51702                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51703               );
51704
51705             header.exit()
51706               .remove();
51707
51708             var headerEnter = header.enter()
51709               .append('div')
51710                 .attr('class', 'qa-header');
51711
51712             var iconEnter = headerEnter
51713               .append('div')
51714                 .attr('class', 'qa-header-icon')
51715                 .classed('new', function (d) { return d.id < 0; });
51716
51717             iconEnter
51718               .append('div')
51719                 .attr('class', function (d) { return ("preset-icon-28 qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.parentIssueType)); })
51720                 .call(svgIcon('#iD-icon-bolt', 'qaItem-fill'));
51721
51722             headerEnter
51723               .append('div')
51724                 .attr('class', 'qa-header-label')
51725                 .text(issueTitle);
51726           }
51727
51728
51729           keepRightHeader.issue = function(val) {
51730             if (!arguments.length) { return _qaItem; }
51731             _qaItem = val;
51732             return keepRightHeader;
51733           };
51734
51735           return keepRightHeader;
51736         }
51737
51738         function uiViewOnKeepRight() {
51739           var _qaItem;
51740
51741           function viewOnKeepRight(selection) {
51742             var url;
51743             if (services.keepRight && (_qaItem instanceof QAItem)) {
51744               url = services.keepRight.issueURL(_qaItem);
51745             }
51746
51747             var link = selection.selectAll('.view-on-keepRight')
51748               .data(url ? [url] : []);
51749
51750             // exit
51751             link.exit()
51752               .remove();
51753
51754             // enter
51755             var linkEnter = link.enter()
51756               .append('a')
51757                 .attr('class', 'view-on-keepRight')
51758                 .attr('target', '_blank')
51759                 .attr('rel', 'noopener') // security measure
51760                 .attr('href', function (d) { return d; })
51761                 .call(svgIcon('#iD-icon-out-link', 'inline'));
51762
51763             linkEnter
51764               .append('span')
51765                 .text(_t('inspector.view_on_keepRight'));
51766           }
51767
51768           viewOnKeepRight.what = function(val) {
51769             if (!arguments.length) { return _qaItem; }
51770             _qaItem = val;
51771             return viewOnKeepRight;
51772           };
51773
51774           return viewOnKeepRight;
51775         }
51776
51777         function uiKeepRightEditor(context) {
51778           var dispatch$1 = dispatch('change');
51779           var qaDetails = uiKeepRightDetails(context);
51780           var qaHeader = uiKeepRightHeader();
51781
51782           var _qaItem;
51783
51784           function keepRightEditor(selection) {
51785
51786             var headerEnter = selection.selectAll('.header')
51787               .data([0])
51788               .enter()
51789               .append('div')
51790                 .attr('class', 'header fillL');
51791
51792             headerEnter
51793               .append('button')
51794                 .attr('class', 'close')
51795                 .on('click', function () { return context.enter(modeBrowse(context)); })
51796                 .call(svgIcon('#iD-icon-close'));
51797
51798             headerEnter
51799               .append('h3')
51800                 .text(_t('QA.keepRight.title'));
51801
51802
51803             var body = selection.selectAll('.body')
51804               .data([0]);
51805
51806             body = body.enter()
51807               .append('div')
51808                 .attr('class', 'body')
51809               .merge(body);
51810
51811             var editor = body.selectAll('.qa-editor')
51812               .data([0]);
51813
51814             editor.enter()
51815               .append('div')
51816                 .attr('class', 'modal-section qa-editor')
51817               .merge(editor)
51818                 .call(qaHeader.issue(_qaItem))
51819                 .call(qaDetails.issue(_qaItem))
51820                 .call(keepRightSaveSection);
51821
51822
51823             var footer = selection.selectAll('.footer')
51824               .data([0]);
51825
51826             footer.enter()
51827               .append('div')
51828               .attr('class', 'footer')
51829               .merge(footer)
51830               .call(uiViewOnKeepRight().what(_qaItem));
51831           }
51832
51833
51834           function keepRightSaveSection(selection) {
51835             var isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51836             var isShown = (_qaItem && (isSelected || _qaItem.newComment || _qaItem.comment));
51837             var saveSection = selection.selectAll('.qa-save')
51838               .data(
51839                 (isShown ? [_qaItem] : []),
51840                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51841               );
51842
51843             // exit
51844             saveSection.exit()
51845               .remove();
51846
51847             // enter
51848             var saveSectionEnter = saveSection.enter()
51849               .append('div')
51850                 .attr('class', 'qa-save save-section cf');
51851
51852             saveSectionEnter
51853               .append('h4')
51854                 .attr('class', '.qa-save-header')
51855                 .text(_t('QA.keepRight.comment'));
51856
51857             saveSectionEnter
51858               .append('textarea')
51859                 .attr('class', 'new-comment-input')
51860                 .attr('placeholder', _t('QA.keepRight.comment_placeholder'))
51861                 .attr('maxlength', 1000)
51862                 .property('value', function (d) { return d.newComment || d.comment; })
51863                 .call(utilNoAuto)
51864                 .on('input', changeInput)
51865                 .on('blur', changeInput);
51866
51867             // update
51868             saveSection = saveSectionEnter
51869               .merge(saveSection)
51870                 .call(qaSaveButtons);
51871
51872             function changeInput() {
51873               var input = select(this);
51874               var val = input.property('value').trim();
51875
51876               if (val === _qaItem.comment) {
51877                 val = undefined;
51878               }
51879
51880               // store the unsaved comment with the issue itself
51881               _qaItem = _qaItem.update({ newComment: val });
51882
51883               var qaService = services.keepRight;
51884               if (qaService) {
51885                 qaService.replaceItem(_qaItem);  // update keepright cache
51886               }
51887
51888               saveSection
51889                 .call(qaSaveButtons);
51890             }
51891           }
51892
51893
51894           function qaSaveButtons(selection) {
51895             var isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51896             var buttonSection = selection.selectAll('.buttons')
51897               .data((isSelected ? [_qaItem] : []), function (d) { return d.status + d.id; });
51898
51899             // exit
51900             buttonSection.exit()
51901               .remove();
51902
51903             // enter
51904             var buttonEnter = buttonSection.enter()
51905               .append('div')
51906                 .attr('class', 'buttons');
51907
51908             buttonEnter
51909               .append('button')
51910                 .attr('class', 'button comment-button action')
51911                 .text(_t('QA.keepRight.save_comment'));
51912
51913             buttonEnter
51914               .append('button')
51915                 .attr('class', 'button close-button action');
51916
51917             buttonEnter
51918               .append('button')
51919                 .attr('class', 'button ignore-button action');
51920
51921             // update
51922             buttonSection = buttonSection
51923               .merge(buttonEnter);
51924
51925             buttonSection.select('.comment-button')   // select and propagate data
51926               .attr('disabled', function (d) { return d.newComment ? null : true; })
51927               .on('click.comment', function(d) {
51928                 this.blur();    // avoid keeping focus on the button - #4641
51929                 var qaService = services.keepRight;
51930                 if (qaService) {
51931                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
51932                 }
51933               });
51934
51935             buttonSection.select('.close-button')   // select and propagate data
51936               .text(function (d) {
51937                 var andComment = (d.newComment ? '_comment' : '');
51938                 return _t(("QA.keepRight.close" + andComment));
51939               })
51940               .on('click.close', function(d) {
51941                 this.blur();    // avoid keeping focus on the button - #4641
51942                 var qaService = services.keepRight;
51943                 if (qaService) {
51944                   d.newStatus = 'ignore_t';   // ignore temporarily (item fixed)
51945                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
51946                 }
51947               });
51948
51949             buttonSection.select('.ignore-button')   // select and propagate data
51950               .text(function (d) {
51951                 var andComment = (d.newComment ? '_comment' : '');
51952                 return _t(("QA.keepRight.ignore" + andComment));
51953               })
51954               .on('click.ignore', function(d) {
51955                 this.blur();    // avoid keeping focus on the button - #4641
51956                 var qaService = services.keepRight;
51957                 if (qaService) {
51958                   d.newStatus = 'ignore';   // ignore permanently (false positive)
51959                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
51960                 }
51961               });
51962           }
51963
51964           // NOTE: Don't change method name until UI v3 is merged
51965           keepRightEditor.error = function(val) {
51966             if (!arguments.length) { return _qaItem; }
51967             _qaItem = val;
51968             return keepRightEditor;
51969           };
51970
51971           return utilRebind(keepRightEditor, dispatch$1, 'on');
51972         }
51973
51974         function uiOsmoseDetails(context) {
51975           var _qaItem;
51976
51977           function issueString(d, type) {
51978             if (!d) { return ''; }
51979
51980             // Issue strings are cached from Osmose API
51981             var s = services.osmose.getStrings(d.itemType);
51982             return (type in s) ? s[type] : '';
51983           }
51984
51985
51986           function osmoseDetails(selection) {
51987             var details = selection.selectAll('.error-details')
51988               .data(
51989                 _qaItem ? [_qaItem] : [],
51990                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51991               );
51992
51993             details.exit()
51994               .remove();
51995
51996             var detailsEnter = details.enter()
51997               .append('div')
51998                 .attr('class', 'error-details qa-details-container');
51999
52000
52001             // Description
52002             if (issueString(_qaItem, 'detail')) {
52003               var div = detailsEnter
52004                 .append('div')
52005                   .attr('class', 'qa-details-subsection');
52006
52007               div
52008                 .append('h4')
52009                   .text(function () { return _t('QA.keepRight.detail_description'); });
52010
52011               div
52012                 .append('p')
52013                   .attr('class', 'qa-details-description-text')
52014                   .html(function (d) { return issueString(d, 'detail'); })
52015                 .selectAll('a')
52016                   .attr('rel', 'noopener')
52017                   .attr('target', '_blank');
52018             }
52019
52020             // Elements (populated later as data is requested)
52021             var detailsDiv = detailsEnter
52022               .append('div')
52023                 .attr('class', 'qa-details-subsection');
52024
52025             var elemsDiv = detailsEnter
52026               .append('div')
52027                 .attr('class', 'qa-details-subsection');
52028
52029             // Suggested Fix (musn't exist for every issue type)
52030             if (issueString(_qaItem, 'fix')) {
52031               var div$1 = detailsEnter
52032                 .append('div')
52033                   .attr('class', 'qa-details-subsection');
52034
52035               div$1
52036                 .append('h4')
52037                   .text(function () { return _t('QA.osmose.fix_title'); });
52038
52039               div$1
52040                 .append('p')
52041                   .html(function (d) { return issueString(d, 'fix'); })
52042                 .selectAll('a')
52043                   .attr('rel', 'noopener')
52044                   .attr('target', '_blank');
52045             }
52046
52047             // Common Pitfalls (musn't exist for every issue type)
52048             if (issueString(_qaItem, 'trap')) {
52049               var div$2 = detailsEnter
52050                 .append('div')
52051                   .attr('class', 'qa-details-subsection');
52052
52053               div$2
52054                 .append('h4')
52055                   .text(function () { return _t('QA.osmose.trap_title'); });
52056
52057               div$2
52058                 .append('p')
52059                   .html(function (d) { return issueString(d, 'trap'); })
52060                 .selectAll('a')
52061                   .attr('rel', 'noopener')
52062                   .attr('target', '_blank');
52063             }
52064
52065             // Save current item to check if UI changed by time request resolves
52066             var thisItem = _qaItem;
52067             services.osmose.loadIssueDetail(_qaItem)
52068               .then(function (d) {
52069                 // No details to add if there are no associated issue elements
52070                 if (!d.elems || d.elems.length === 0) { return; }
52071
52072                 // Do nothing if UI has moved on by the time this resolves
52073                 if (
52074                   context.selectedErrorID() !== thisItem.id
52075                   && context.container().selectAll((".qaItem.osmose.hover.itemId-" + (thisItem.id))).empty()
52076                 ) { return; }
52077
52078                 // Things like keys and values are dynamically added to a subtitle string
52079                 if (d.detail) {
52080                   detailsDiv
52081                     .append('h4')
52082                       .text(function () { return _t('QA.osmose.detail_title'); });
52083
52084                   detailsDiv
52085                     .append('p')
52086                       .html(function (d) { return d.detail; })
52087                     .selectAll('a')
52088                       .attr('rel', 'noopener')
52089                       .attr('target', '_blank');
52090                 }
52091
52092                 // Create list of linked issue elements
52093                 elemsDiv
52094                   .append('h4')
52095                     .text(function () { return _t('QA.osmose.elems_title'); });
52096
52097                 elemsDiv
52098                   .append('ul').selectAll('li')
52099                   .data(d.elems)
52100                   .enter()
52101                   .append('li')
52102                   .append('a')
52103                     .attr('class', 'error_entity_link')
52104                     .text(function (d) { return d; })
52105                     .each(function() {
52106                       var link = select(this);
52107                       var entityID = this.textContent;
52108                       var entity = context.hasEntity(entityID);
52109
52110                       // Add click handler
52111                       link
52112                         .on('mouseenter', function () {
52113                           utilHighlightEntities([entityID], true, context);
52114                         })
52115                         .on('mouseleave', function () {
52116                           utilHighlightEntities([entityID], false, context);
52117                         })
52118                         .on('click', function () {
52119                           event.preventDefault();
52120
52121                           utilHighlightEntities([entityID], false, context);
52122
52123                           var osmlayer = context.layers().layer('osm');
52124                           if (!osmlayer.enabled()) {
52125                             osmlayer.enabled(true);
52126                           }
52127
52128                           context.map().centerZoom(d.loc, 20);
52129
52130                           if (entity) {
52131                             context.enter(modeSelect(context, [entityID]));
52132                           } else {
52133                             context.loadEntity(entityID, function () {
52134                               context.enter(modeSelect(context, [entityID]));
52135                             });
52136                           }
52137                         });
52138
52139                       // Replace with friendly name if possible
52140                       // (The entity may not yet be loaded into the graph)
52141                       if (entity) {
52142                         var name = utilDisplayName(entity);  // try to use common name
52143
52144                         if (!name) {
52145                           var preset = _mainPresetIndex.match(entity, context.graph());
52146                           name = preset && !preset.isFallback() && preset.name();  // fallback to preset name
52147                         }
52148
52149                         if (name) {
52150                           this.innerText = name;
52151                         }
52152                       }
52153                     });
52154
52155                 // Don't hide entities related to this issue - #5880
52156                 context.features().forceVisible(d.elems);
52157                 context.map().pan([0,0]);  // trigger a redraw
52158               })
52159               .catch(function (err) {
52160                 console.log(err); // eslint-disable-line no-console
52161               });
52162           }
52163
52164
52165           osmoseDetails.issue = function(val) {
52166             if (!arguments.length) { return _qaItem; }
52167             _qaItem = val;
52168             return osmoseDetails;
52169           };
52170
52171
52172           return osmoseDetails;
52173         }
52174
52175         function uiOsmoseHeader() {
52176           var _qaItem;
52177
52178           function issueTitle(d) {
52179             var unknown = _t('inspector.unknown');
52180
52181             if (!d) { return unknown; }
52182
52183             // Issue titles supplied by Osmose
52184             var s = services.osmose.getStrings(d.itemType);
52185             return ('title' in s) ? s.title : unknown;
52186           }
52187
52188           function osmoseHeader(selection) {
52189             var header = selection.selectAll('.qa-header')
52190               .data(
52191                 (_qaItem ? [_qaItem] : []),
52192                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
52193               );
52194
52195             header.exit()
52196               .remove();
52197
52198             var headerEnter = header.enter()
52199               .append('div')
52200                 .attr('class', 'qa-header');
52201
52202             var svgEnter = headerEnter
52203               .append('div')
52204                 .attr('class', 'qa-header-icon')
52205                 .classed('new', function (d) { return d.id < 0; })
52206               .append('svg')
52207                 .attr('width', '20px')
52208                 .attr('height', '30px')
52209                 .attr('viewbox', '0 0 20 30')
52210                 .attr('class', function (d) { return ("preset-icon-28 qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.itemType)); });
52211
52212             svgEnter
52213               .append('polygon')
52214                 .attr('fill', function (d) { return services.osmose.getColor(d.item); })
52215                 .attr('class', 'qaItem-fill')
52216                 .attr('points', '16,3 4,3 1,6 1,17 4,20 7,20 10,27 13,20 16,20 19,17.033 19,6');
52217
52218             svgEnter
52219               .append('use')
52220                 .attr('class', 'icon-annotation')
52221                 .attr('width', '13px')
52222                 .attr('height', '13px')
52223                 .attr('transform', 'translate(3.5, 5)')
52224                 .attr('xlink:href', function (d) {
52225                   var picon = d.icon;
52226
52227                   if (!picon) {
52228                     return '';
52229                   } else {
52230                     var isMaki = /^maki-/.test(picon);
52231                     return ("#" + picon + (isMaki ? '-11' : ''));
52232                   }
52233                 });
52234
52235             headerEnter
52236               .append('div')
52237                 .attr('class', 'qa-header-label')
52238                 .text(issueTitle);
52239           }
52240
52241           osmoseHeader.issue = function(val) {
52242             if (!arguments.length) { return _qaItem; }
52243             _qaItem = val;
52244             return osmoseHeader;
52245           };
52246
52247           return osmoseHeader;
52248         }
52249
52250         function uiViewOnOsmose() {
52251           var _qaItem;
52252
52253           function viewOnOsmose(selection) {
52254             var url;
52255             if (services.osmose && (_qaItem instanceof QAItem)) {
52256               url = services.osmose.itemURL(_qaItem);
52257             }
52258
52259             var link = selection.selectAll('.view-on-osmose')
52260               .data(url ? [url] : []);
52261
52262             // exit
52263             link.exit()
52264               .remove();
52265
52266             // enter
52267             var linkEnter = link.enter()
52268               .append('a')
52269                 .attr('class', 'view-on-osmose')
52270                 .attr('target', '_blank')
52271                 .attr('rel', 'noopener') // security measure
52272                 .attr('href', function (d) { return d; })
52273                 .call(svgIcon('#iD-icon-out-link', 'inline'));
52274
52275             linkEnter
52276               .append('span')
52277                 .text(_t('inspector.view_on_osmose'));
52278           }
52279
52280           viewOnOsmose.what = function(val) {
52281             if (!arguments.length) { return _qaItem; }
52282             _qaItem = val;
52283             return viewOnOsmose;
52284           };
52285
52286           return viewOnOsmose;
52287         }
52288
52289         function uiOsmoseEditor(context) {
52290           var dispatch$1 = dispatch('change');
52291           var qaDetails = uiOsmoseDetails(context);
52292           var qaHeader = uiOsmoseHeader();
52293
52294           var _qaItem;
52295
52296           function osmoseEditor(selection) {
52297
52298             var header = selection.selectAll('.header')
52299               .data([0]);
52300
52301             var headerEnter = header.enter()
52302               .append('div')
52303                 .attr('class', 'header fillL');
52304
52305             headerEnter
52306               .append('button')
52307                 .attr('class', 'close')
52308                 .on('click', function () { return context.enter(modeBrowse(context)); })
52309                 .call(svgIcon('#iD-icon-close'));
52310
52311             headerEnter
52312               .append('h3')
52313                 .text(_t('QA.osmose.title'));
52314
52315             var body = selection.selectAll('.body')
52316               .data([0]);
52317
52318             body = body.enter()
52319                 .append('div')
52320                 .attr('class', 'body')
52321               .merge(body);
52322
52323             var editor = body.selectAll('.qa-editor')
52324               .data([0]);
52325
52326             editor.enter()
52327               .append('div')
52328                 .attr('class', 'modal-section qa-editor')
52329               .merge(editor)
52330                 .call(qaHeader.issue(_qaItem))
52331                 .call(qaDetails.issue(_qaItem))
52332                 .call(osmoseSaveSection);
52333
52334             var footer = selection.selectAll('.footer')
52335               .data([0]);
52336
52337             footer.enter()
52338               .append('div')
52339               .attr('class', 'footer')
52340               .merge(footer)
52341               .call(uiViewOnOsmose().what(_qaItem));
52342           }
52343
52344           function osmoseSaveSection(selection) {
52345             var isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
52346             var isShown = (_qaItem && isSelected);
52347             var saveSection = selection.selectAll('.qa-save')
52348               .data(
52349                 (isShown ? [_qaItem] : []),
52350                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
52351               );
52352
52353             // exit
52354             saveSection.exit()
52355               .remove();
52356
52357             // enter
52358             var saveSectionEnter = saveSection.enter()
52359               .append('div')
52360                 .attr('class', 'qa-save save-section cf');
52361
52362             // update
52363             saveSection = saveSectionEnter
52364               .merge(saveSection)
52365                 .call(qaSaveButtons);
52366           }
52367
52368           function qaSaveButtons(selection) {
52369             var isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
52370             var buttonSection = selection.selectAll('.buttons')
52371               .data((isSelected ? [_qaItem] : []), function (d) { return d.status + d.id; });
52372
52373             // exit
52374             buttonSection.exit()
52375               .remove();
52376
52377             // enter
52378             var buttonEnter = buttonSection.enter()
52379               .append('div')
52380                 .attr('class', 'buttons');
52381
52382             buttonEnter
52383               .append('button')
52384                 .attr('class', 'button close-button action');
52385
52386             buttonEnter
52387               .append('button')
52388                 .attr('class', 'button ignore-button action');
52389
52390             // update
52391             buttonSection = buttonSection
52392               .merge(buttonEnter);
52393
52394             buttonSection.select('.close-button')
52395               .text(function () { return _t('QA.keepRight.close'); })
52396               .on('click.close', function(d) {
52397                 this.blur();    // avoid keeping focus on the button - #4641
52398                 var qaService = services.osmose;
52399                 if (qaService) {
52400                   d.newStatus = 'done';
52401                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
52402                 }
52403               });
52404
52405             buttonSection.select('.ignore-button')
52406               .text(function () { return _t('QA.keepRight.ignore'); })
52407               .on('click.ignore', function(d) {
52408                 this.blur();    // avoid keeping focus on the button - #4641
52409                 var qaService = services.osmose;
52410                 if (qaService) {
52411                   d.newStatus = 'false';
52412                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
52413                 }
52414               });
52415           }
52416
52417           // NOTE: Don't change method name until UI v3 is merged
52418           osmoseEditor.error = function(val) {
52419             if (!arguments.length) { return _qaItem; }
52420             _qaItem = val;
52421             return osmoseEditor;
52422           };
52423
52424           return utilRebind(osmoseEditor, dispatch$1, 'on');
52425         }
52426
52427         // NOTE: Don't change name of this until UI v3 is merged
52428         function modeSelectError(context, selectedErrorID, selectedErrorService) {
52429             var mode = {
52430                 id: 'select-error',
52431                 button: 'browse'
52432             };
52433
52434             var keybinding = utilKeybinding('select-error');
52435
52436             var errorService = services[selectedErrorService];
52437             var errorEditor;
52438             switch (selectedErrorService) {
52439                 case 'improveOSM':
52440                     errorEditor = uiImproveOsmEditor(context)
52441                     .on('change', function() {
52442                         context.map().pan([0,0]);  // trigger a redraw
52443                         var error = checkSelectedID();
52444                         if (!error) { return; }
52445                         context.ui().sidebar
52446                             .show(errorEditor.error(error));
52447                     });
52448                     break;
52449                 case 'keepRight':
52450                     errorEditor = uiKeepRightEditor(context)
52451                     .on('change', function() {
52452                         context.map().pan([0,0]);  // trigger a redraw
52453                         var error = checkSelectedID();
52454                         if (!error) { return; }
52455                         context.ui().sidebar
52456                             .show(errorEditor.error(error));
52457                     });
52458                     break;
52459                 case 'osmose':
52460                     errorEditor = uiOsmoseEditor(context)
52461                     .on('change', function() {
52462                         context.map().pan([0,0]);  // trigger a redraw
52463                         var error = checkSelectedID();
52464                         if (!error) { return; }
52465                         context.ui().sidebar
52466                             .show(errorEditor.error(error));
52467                     });
52468                     break;
52469             }
52470
52471
52472             var behaviors = [
52473                 behaviorBreathe(),
52474                 behaviorHover(context),
52475                 behaviorSelect(context),
52476                 behaviorLasso(context),
52477                 modeDragNode(context).behavior,
52478                 modeDragNote(context).behavior
52479             ];
52480
52481
52482             function checkSelectedID() {
52483                 if (!errorService) { return; }
52484                 var error = errorService.getError(selectedErrorID);
52485                 if (!error) {
52486                     context.enter(modeBrowse(context));
52487                 }
52488                 return error;
52489             }
52490
52491
52492             mode.zoomToSelected = function() {
52493                 if (!errorService) { return; }
52494                 var error = errorService.getError(selectedErrorID);
52495                 if (error) {
52496                     context.map().centerZoomEase(error.loc, 20);
52497                 }
52498             };
52499
52500
52501             mode.enter = function() {
52502                 var error = checkSelectedID();
52503                 if (!error) { return; }
52504
52505                 behaviors.forEach(context.install);
52506                 keybinding
52507                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
52508                     .on('⎋', esc, true);
52509
52510                 select(document)
52511                     .call(keybinding);
52512
52513                 selectError();
52514
52515                 var sidebar = context.ui().sidebar;
52516                 sidebar.show(errorEditor.error(error));
52517
52518                 context.map()
52519                     .on('drawn.select-error', selectError);
52520
52521
52522                 // class the error as selected, or return to browse mode if the error is gone
52523                 function selectError(drawn) {
52524                     if (!checkSelectedID()) { return; }
52525
52526                     var selection = context.surface()
52527                         .selectAll('.itemId-' + selectedErrorID + '.' + selectedErrorService);
52528
52529                     if (selection.empty()) {
52530                         // Return to browse mode if selected DOM elements have
52531                         // disappeared because the user moved them out of view..
52532                         var source = event && event.type === 'zoom' && event.sourceEvent;
52533                         if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
52534                             context.enter(modeBrowse(context));
52535                         }
52536
52537                     } else {
52538                         selection
52539                             .classed('selected', true);
52540
52541                         context.selectedErrorID(selectedErrorID);
52542                     }
52543                 }
52544
52545                 function esc() {
52546                     if (context.container().select('.combobox').size()) { return; }
52547                     context.enter(modeBrowse(context));
52548                 }
52549             };
52550
52551
52552             mode.exit = function() {
52553                 behaviors.forEach(context.uninstall);
52554
52555                 select(document)
52556                     .call(keybinding.unbind);
52557
52558                 context.surface()
52559                     .selectAll('.qaItem.selected')
52560                     .classed('selected hover', false);
52561
52562                 context.map()
52563                     .on('drawn.select-error', null);
52564
52565                 context.ui().sidebar
52566                     .hide();
52567
52568                 context.selectedErrorID(null);
52569                 context.features().forceVisible([]);
52570             };
52571
52572
52573             return mode;
52574         }
52575
52576         function behaviorSelect(context) {
52577             var _tolerancePx = 4; // see also behaviorDrag
52578             var _lastMouseEvent = null;
52579             var _showMenu = false;
52580             var _downPointers = {};
52581             var _longPressTimeout = null;
52582             var _lastInteractionType = null;
52583             // the id of the down pointer that's enabling multiselection while down
52584             var _multiselectionPointerId = null;
52585
52586             // use pointer events on supported platforms; fallback to mouse events
52587             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
52588
52589
52590             function keydown() {
52591
52592                 if (event.keyCode === 32) {
52593                     // don't react to spacebar events during text input
52594                     var activeNode = document.activeElement;
52595                     if (activeNode && new Set(['INPUT', 'TEXTAREA']).has(activeNode.nodeName)) { return; }
52596                 }
52597
52598                 if (event.keyCode === 93 ||  // context menu key
52599                     event.keyCode === 32) {  // spacebar
52600                     event.preventDefault();
52601                 }
52602
52603                 if (event.repeat) { return; } // ignore repeated events for held keys
52604
52605                 // if any key is pressed the user is probably doing something other than long-pressing
52606                 cancelLongPress();
52607
52608                 if (event.shiftKey) {
52609                     context.surface()
52610                         .classed('behavior-multiselect', true);
52611                 }
52612
52613                 if (event.keyCode === 32) {  // spacebar
52614                     if (!_downPointers.spacebar && _lastMouseEvent) {
52615                         cancelLongPress();
52616                         _longPressTimeout = window.setTimeout(didLongPress, 500, 'spacebar', 'spacebar');
52617
52618                         _downPointers.spacebar = {
52619                             firstEvent: _lastMouseEvent,
52620                             lastEvent: _lastMouseEvent
52621                         };
52622                     }
52623                 }
52624             }
52625
52626
52627             function keyup() {
52628                 cancelLongPress();
52629
52630                 if (!event.shiftKey) {
52631                     context.surface()
52632                         .classed('behavior-multiselect', false);
52633                 }
52634
52635                 if (event.keyCode === 93) {  // context menu key
52636                     event.preventDefault();
52637                     _lastInteractionType = 'menukey';
52638                     contextmenu();
52639                 } else if (event.keyCode === 32) {  // spacebar
52640                     var pointer = _downPointers.spacebar;
52641                     if (pointer) {
52642                         delete _downPointers.spacebar;
52643
52644                         if (pointer.done) { return; }
52645
52646                         event.preventDefault();
52647                         _lastInteractionType = 'spacebar';
52648                         click(pointer.firstEvent, pointer.lastEvent, 'spacebar');
52649                     }
52650                 }
52651             }
52652
52653
52654             function pointerdown() {
52655                 var id = (event.pointerId || 'mouse').toString();
52656
52657                 cancelLongPress();
52658
52659                 if (event.buttons && event.buttons !== 1) { return; }
52660
52661                 context.ui().closeEditMenu();
52662
52663                 _longPressTimeout = window.setTimeout(didLongPress, 500, id, 'longdown-' + (event.pointerType || 'mouse'));
52664
52665                 _downPointers[id] = {
52666                     firstEvent: event,
52667                     lastEvent: event
52668                 };
52669             }
52670
52671
52672             function didLongPress(id, interactionType) {
52673                 var pointer = _downPointers[id];
52674                 if (!pointer) { return; }
52675
52676                 for (var i in _downPointers) {
52677                     // don't allow this or any currently down pointer to trigger another click
52678                     _downPointers[i].done = true;
52679                 }
52680
52681                 // treat long presses like right-clicks
52682                 _longPressTimeout = null;
52683                 _lastInteractionType = interactionType;
52684                 _showMenu = true;
52685
52686                 click(pointer.firstEvent, pointer.lastEvent, id);
52687             }
52688
52689
52690             function pointermove() {
52691                 var id = (event.pointerId || 'mouse').toString();
52692                 if (_downPointers[id]) {
52693                     _downPointers[id].lastEvent = event;
52694                 }
52695                 if (!event.pointerType || event.pointerType === 'mouse') {
52696                     _lastMouseEvent = event;
52697                     if (_downPointers.spacebar) {
52698                         _downPointers.spacebar.lastEvent = event;
52699                     }
52700                 }
52701             }
52702
52703
52704             function pointerup() {
52705                 var id = (event.pointerId || 'mouse').toString();
52706                 var pointer = _downPointers[id];
52707                 if (!pointer) { return; }
52708
52709                 delete _downPointers[id];
52710
52711                 if (_multiselectionPointerId === id) {
52712                     _multiselectionPointerId = null;
52713                 }
52714
52715                 if (pointer.done) { return; }
52716
52717                 click(pointer.firstEvent, event, id);
52718             }
52719
52720
52721             function pointercancel() {
52722                 var id = (event.pointerId || 'mouse').toString();
52723                 if (!_downPointers[id]) { return; }
52724
52725                 delete _downPointers[id];
52726
52727                 if (_multiselectionPointerId === id) {
52728                     _multiselectionPointerId = null;
52729                 }
52730             }
52731
52732
52733             function contextmenu() {
52734                 var e = event;
52735                 e.preventDefault();
52736
52737                 if (!+e.clientX && !+e.clientY) {
52738                     if (_lastMouseEvent) {
52739                         e.sourceEvent = _lastMouseEvent;
52740                     } else {
52741                         return;
52742                     }
52743                 } else {
52744                     _lastMouseEvent = event;
52745                     _lastInteractionType = 'rightclick';
52746                 }
52747
52748                 _showMenu = true;
52749                 click(event, event);
52750             }
52751
52752
52753             function click(firstEvent, lastEvent, pointerId) {
52754                 cancelLongPress();
52755
52756                 var mapNode = context.container().select('.main-map').node();
52757
52758                 // Use the `main-map` coordinate system since the surface and supersurface
52759                 // are transformed when drag-panning.
52760                 var pointGetter = utilFastMouse(mapNode);
52761                 var p1 = pointGetter(firstEvent);
52762                 var p2 = pointGetter(lastEvent);
52763                 var dist = geoVecLength(p1, p2);
52764
52765                 if (dist > _tolerancePx ||
52766                     !mapContains(lastEvent)) {
52767
52768                     resetProperties();
52769                     return;
52770                 }
52771
52772                 var targetDatum = lastEvent.target.__data__;
52773
52774                 var multiselectEntityId;
52775
52776                 if (!_multiselectionPointerId) {
52777                     // If a different pointer than the one triggering this click is down on a
52778                     // feature, treat this and all future clicks as multiselection until that
52779                     // pointer is raised.
52780                     var selectPointerInfo = pointerDownOnSelection(pointerId);
52781                     if (selectPointerInfo) {
52782                         _multiselectionPointerId = selectPointerInfo.pointerId;
52783                         // if the other feature isn't selected yet, make sure we select it
52784                         multiselectEntityId = !selectPointerInfo.selected && selectPointerInfo.entityId;
52785                         _downPointers[selectPointerInfo.pointerId].done = true;
52786                     }
52787                 }
52788
52789                 // support multiselect if data is already selected
52790                 var isMultiselect = context.mode().id === 'select' && (
52791                     // and shift key is down
52792                     (event && event.shiftKey) ||
52793                     // or we're lasso-selecting
52794                     context.surface().select('.lasso').node() ||
52795                     // or a pointer is down over a selected feature
52796                     (_multiselectionPointerId && !multiselectEntityId)
52797                 );
52798
52799                 processClick(targetDatum, isMultiselect, p2, multiselectEntityId);
52800
52801                 function mapContains(event) {
52802                     var rect = mapNode.getBoundingClientRect();
52803                     return event.clientX >= rect.left &&
52804                         event.clientX <= rect.right &&
52805                         event.clientY >= rect.top &&
52806                         event.clientY <= rect.bottom;
52807                 }
52808
52809                 function pointerDownOnSelection(skipPointerId) {
52810                     var mode = context.mode();
52811                     var selectedIDs = mode.id === 'select' ? mode.selectedIDs() : [];
52812                     for (var pointerId in _downPointers) {
52813                         if (pointerId === 'spacebar' || pointerId === skipPointerId) { continue; }
52814
52815                         var pointerInfo = _downPointers[pointerId];
52816
52817                         var p1 = pointGetter(pointerInfo.firstEvent);
52818                         var p2 = pointGetter(pointerInfo.lastEvent);
52819                         if (geoVecLength(p1, p2) > _tolerancePx) { continue; }
52820
52821                         var datum = pointerInfo.firstEvent.target.__data__;
52822                         var entity = (datum && datum.properties && datum.properties.entity) || datum;
52823                         if (context.graph().hasEntity(entity.id)) { return {
52824                             pointerId: pointerId,
52825                             entityId: entity.id,
52826                             selected: selectedIDs.indexOf(entity.id) !== -1
52827                         }; }
52828                     }
52829                     return null;
52830                 }
52831             }
52832
52833
52834             function processClick(datum, isMultiselect, point, alsoSelectId) {
52835                 var mode = context.mode();
52836                 var showMenu = _showMenu;
52837                 var interactionType = _lastInteractionType;
52838
52839                 var entity = datum && datum.properties && datum.properties.entity;
52840                 if (entity) { datum = entity; }
52841
52842                 if (datum && datum.type === 'midpoint') {
52843                     // treat targeting midpoints as if targeting the parent way
52844                     datum = datum.parents[0];
52845                 }
52846
52847                 var newMode;
52848
52849                 if (datum instanceof osmEntity) {
52850                     // targeting an entity
52851                     var selectedIDs = context.selectedIDs();
52852                     context.selectedNoteID(null);
52853                     context.selectedErrorID(null);
52854
52855                     if (!isMultiselect) {
52856                         // don't change the selection if we're toggling the menu atop a multiselection
52857                         if (!showMenu ||
52858                             selectedIDs.length <= 1 ||
52859                             selectedIDs.indexOf(datum.id) === -1) {
52860
52861                             if (alsoSelectId === datum.id) { alsoSelectId = null; }
52862
52863                             selectedIDs = (alsoSelectId ? [alsoSelectId] : []).concat([datum.id]);
52864                             // always enter modeSelect even if the entity is already
52865                             // selected since listeners may expect `context.enter` events,
52866                             // e.g. in the walkthrough
52867                             newMode = mode.id === 'select' ? mode.selectedIDs(selectedIDs) : modeSelect(context, selectedIDs).selectBehavior(behavior);
52868                             context.enter(newMode);
52869                         }
52870
52871                     } else {
52872                         if (selectedIDs.indexOf(datum.id) !== -1) {
52873                             // clicked entity is already in the selectedIDs list..
52874                             if (!showMenu) {
52875                                 // deselect clicked entity, then reenter select mode or return to browse mode..
52876                                 selectedIDs = selectedIDs.filter(function(id) { return id !== datum.id; });
52877                                 newMode = selectedIDs.length ? mode.selectedIDs(selectedIDs) : modeBrowse(context).selectBehavior(behavior);
52878                                 context.enter(newMode);
52879                             }
52880                         } else {
52881                             // clicked entity is not in the selected list, add it..
52882                             selectedIDs = selectedIDs.concat([datum.id]);
52883                             newMode = mode.selectedIDs(selectedIDs);
52884                             context.enter(newMode);
52885                         }
52886                     }
52887
52888                 } else if (datum && datum.__featurehash__ && !isMultiselect) {
52889                     // targeting custom data
52890                     context
52891                         .selectedNoteID(null)
52892                         .enter(modeSelectData(context, datum));
52893
52894                 } else if (datum instanceof osmNote && !isMultiselect) {
52895                     // targeting a note
52896                     context
52897                         .selectedNoteID(datum.id)
52898                         .enter(modeSelectNote(context, datum.id));
52899
52900                 } else if (datum instanceof QAItem & !isMultiselect) {
52901                     // targeting an external QA issue
52902                     context
52903                         .selectedErrorID(datum.id)
52904                         .enter(modeSelectError(context, datum.id, datum.service));
52905
52906                 } else {
52907                     // targeting nothing
52908                     context.selectedNoteID(null);
52909                     context.selectedErrorID(null);
52910                     if (!isMultiselect && mode.id !== 'browse') {
52911                         context.enter(modeBrowse(context));
52912                     }
52913                 }
52914
52915                 context.ui().closeEditMenu();
52916
52917                 // always request to show the edit menu in case the mode needs it
52918                 if (showMenu) { context.ui().showEditMenu(point, interactionType); }
52919
52920                 resetProperties();
52921             }
52922
52923
52924             function cancelLongPress() {
52925                 if (_longPressTimeout) { window.clearTimeout(_longPressTimeout); }
52926                 _longPressTimeout = null;
52927             }
52928
52929
52930             function resetProperties() {
52931                 cancelLongPress();
52932                 _showMenu = false;
52933                 _lastInteractionType = null;
52934                 // don't reset _lastMouseEvent since it might still be useful
52935             }
52936
52937
52938             function behavior(selection) {
52939                 resetProperties();
52940                 _lastMouseEvent = context.map().lastPointerEvent();
52941
52942                 select(window)
52943                     .on('keydown.select', keydown)
52944                     .on('keyup.select', keyup)
52945                     .on(_pointerPrefix + 'move.select', pointermove, true)
52946                     .on(_pointerPrefix + 'up.select', pointerup, true)
52947                     .on('pointercancel.select', pointercancel, true)
52948                     .on('contextmenu.select-window', function() {
52949                         // Edge and IE really like to show the contextmenu on the
52950                         // menubar when user presses a keyboard menu button
52951                         // even after we've already preventdefaulted the key event.
52952                         var e = event;
52953                         if (+e.clientX === 0 && +e.clientY === 0) {
52954                             event.preventDefault();
52955                         }
52956                     });
52957
52958                 selection
52959                     .on(_pointerPrefix + 'down.select', pointerdown)
52960                     .on('contextmenu.select', contextmenu);
52961
52962                 if (event && event.shiftKey) {
52963                     context.surface()
52964                         .classed('behavior-multiselect', true);
52965                 }
52966             }
52967
52968
52969             behavior.off = function(selection) {
52970                 cancelLongPress();
52971
52972                 select(window)
52973                     .on('keydown.select', null)
52974                     .on('keyup.select', null)
52975                     .on('contextmenu.select-window', null)
52976                     .on(_pointerPrefix + 'move.select', null, true)
52977                     .on(_pointerPrefix + 'up.select', null, true)
52978                     .on('pointercancel.select', null, true);
52979
52980                 selection
52981                     .on(_pointerPrefix + 'down.select', null)
52982                     .on('contextmenu.select', null);
52983
52984                 context.surface()
52985                     .classed('behavior-multiselect', false);
52986             };
52987
52988
52989             return behavior;
52990         }
52991
52992         function behaviorDrawWay(context, wayID, mode, startGraph) {
52993
52994             var dispatch$1 = dispatch('rejectedSelfIntersection');
52995
52996             var behavior = behaviorDraw(context);
52997
52998             // Must be set by `drawWay.nodeIndex` before each install of this behavior.
52999             var _nodeIndex;
53000
53001             var _origWay;
53002             var _wayGeometry;
53003             var _headNodeID;
53004             var _annotation;
53005
53006             var _pointerHasMoved = false;
53007
53008             // The osmNode to be placed.
53009             // This is temporary and just follows the mouse cursor until an "add" event occurs.
53010             var _drawNode;
53011
53012             var _didResolveTempEdit = false;
53013
53014             function createDrawNode(loc) {
53015                 // don't make the draw node until we actually need it
53016                 _drawNode = osmNode({ loc: loc });
53017
53018                 context.pauseChangeDispatch();
53019                 context.replace(function actionAddDrawNode(graph) {
53020                     // add the draw node to the graph and insert it into the way
53021                     var way = graph.entity(wayID);
53022                     return graph
53023                         .replace(_drawNode)
53024                         .replace(way.addNode(_drawNode.id, _nodeIndex));
53025                 }, _annotation);
53026                 context.resumeChangeDispatch();
53027
53028                 setActiveElements();
53029             }
53030
53031             function removeDrawNode() {
53032
53033                 context.pauseChangeDispatch();
53034                 context.replace(
53035                     function actionDeleteDrawNode(graph) {
53036                        var way = graph.entity(wayID);
53037                        return graph
53038                            .replace(way.removeNode(_drawNode.id))
53039                            .remove(_drawNode);
53040                    },
53041                     _annotation
53042                 );
53043                 _drawNode = undefined;
53044                 context.resumeChangeDispatch();
53045             }
53046
53047
53048             function keydown() {
53049                 if (event.keyCode === utilKeybinding.modifierCodes.alt) {
53050                     if (context.surface().classed('nope')) {
53051                         context.surface()
53052                             .classed('nope-suppressed', true);
53053                     }
53054                     context.surface()
53055                         .classed('nope', false)
53056                         .classed('nope-disabled', true);
53057                 }
53058             }
53059
53060
53061             function keyup() {
53062                 if (event.keyCode === utilKeybinding.modifierCodes.alt) {
53063                     if (context.surface().classed('nope-suppressed')) {
53064                         context.surface()
53065                             .classed('nope', true);
53066                     }
53067                     context.surface()
53068                         .classed('nope-suppressed', false)
53069                         .classed('nope-disabled', false);
53070                 }
53071             }
53072
53073
53074             function allowsVertex(d) {
53075                 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
53076             }
53077
53078
53079             // related code
53080             // - `mode/drag_node.js`     `doMove()`
53081             // - `behavior/draw.js`      `click()`
53082             // - `behavior/draw_way.js`  `move()`
53083             function move(datum) {
53084
53085                 var loc = context.map().mouseCoordinates();
53086
53087                 if (!_drawNode) { createDrawNode(loc); }
53088
53089                 context.surface().classed('nope-disabled', event.altKey);
53090
53091                 var targetLoc = datum && datum.properties && datum.properties.entity &&
53092                     allowsVertex(datum.properties.entity) && datum.properties.entity.loc;
53093                 var targetNodes = datum && datum.properties && datum.properties.nodes;
53094
53095                 if (targetLoc) {   // snap to node/vertex - a point target with `.loc`
53096                     loc = targetLoc;
53097
53098                 } else if (targetNodes) {   // snap to way - a line target with `.nodes`
53099                     var choice = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, _drawNode.id);
53100                     if (choice) {
53101                         loc = choice.loc;
53102                     }
53103                 }
53104
53105                 context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
53106                 _drawNode = context.entity(_drawNode.id);
53107                 checkGeometry(true /* includeDrawNode */);
53108             }
53109
53110
53111             // Check whether this edit causes the geometry to break.
53112             // If so, class the surface with a nope cursor.
53113             // `includeDrawNode` - Only check the relevant line segments if finishing drawing
53114             function checkGeometry(includeDrawNode) {
53115                 var nopeDisabled = context.surface().classed('nope-disabled');
53116                 var isInvalid = isInvalidGeometry(includeDrawNode);
53117
53118                 if (nopeDisabled) {
53119                     context.surface()
53120                         .classed('nope', false)
53121                         .classed('nope-suppressed', isInvalid);
53122                 } else {
53123                     context.surface()
53124                         .classed('nope', isInvalid)
53125                         .classed('nope-suppressed', false);
53126                 }
53127             }
53128
53129
53130             function isInvalidGeometry(includeDrawNode) {
53131
53132                 var testNode = _drawNode;
53133
53134                 // we only need to test the single way we're drawing
53135                 var parentWay = context.graph().entity(wayID);
53136                 var nodes = context.graph().childNodes(parentWay).slice();  // shallow copy
53137
53138                 if (includeDrawNode) {
53139                     if (parentWay.isClosed()) {
53140                         // don't test the last segment for closed ways - #4655
53141                         // (still test the first segement)
53142                         nodes.pop();
53143                     }
53144                 } else { // discount the draw node
53145
53146                     if (parentWay.isClosed()) {
53147                         if (nodes.length < 3) { return false; }
53148                         if (_drawNode) { nodes.splice(-2, 1); }
53149                         testNode = nodes[nodes.length - 2];
53150                     } else {
53151                         // there's nothing we need to test if we ignore the draw node on open ways
53152                         return false;
53153                     }
53154                 }
53155
53156                 return testNode && geoHasSelfIntersections(nodes, testNode.id);
53157             }
53158
53159
53160             function undone() {
53161
53162                 // undoing removed the temp edit
53163                 _didResolveTempEdit = true;
53164
53165                 context.pauseChangeDispatch();
53166
53167                 var nextMode;
53168
53169                 if (context.graph() === startGraph) { // we've undone back to the beginning
53170                     nextMode = modeSelect(context, [wayID]);
53171                 } else {
53172                     context.history()
53173                         .on('undone.draw', null);
53174                     // remove whatever segment was drawn previously
53175                     context.undo();
53176
53177                     if (context.graph() === startGraph) { // we've undone back to the beginning
53178                         nextMode = modeSelect(context, [wayID]);
53179                     } else {
53180                         // continue drawing
53181                         nextMode = mode;
53182                     }
53183                 }
53184
53185                 // clear the redo stack by adding and removing an edit
53186                 context.perform(actionNoop());
53187                 context.pop(1);
53188
53189                 context.resumeChangeDispatch();
53190                 context.enter(nextMode);
53191             }
53192
53193
53194             function setActiveElements() {
53195                 if (!_drawNode) { return; }
53196
53197                 context.surface().selectAll('.' + _drawNode.id)
53198                     .classed('active', true);
53199             }
53200
53201
53202             function resetToStartGraph() {
53203                 while (context.graph() !== startGraph) {
53204                     context.pop();
53205                 }
53206             }
53207
53208
53209             var drawWay = function(surface) {
53210                 _drawNode = undefined;
53211                 _didResolveTempEdit = false;
53212                 _origWay = context.entity(wayID);
53213                 _headNodeID = typeof _nodeIndex === 'number' ? _origWay.nodes[_nodeIndex] :
53214                     (_origWay.isClosed() ? _origWay.nodes[_origWay.nodes.length - 2] : _origWay.nodes[_origWay.nodes.length - 1]);
53215                 _wayGeometry = _origWay.geometry(context.graph());
53216                 _annotation = _t((_origWay.isDegenerate() ?
53217                     'operations.start.annotation.' :
53218                     'operations.continue.annotation.') + _wayGeometry
53219                 );
53220                 _pointerHasMoved = false;
53221
53222                 // Push an annotated state for undo to return back to.
53223                 // We must make sure to replace or remove it later.
53224                 context.pauseChangeDispatch();
53225                 context.perform(actionNoop(), _annotation);
53226                 context.resumeChangeDispatch();
53227
53228                 behavior.hover()
53229                     .initialNodeID(_headNodeID);
53230
53231                 behavior
53232                     .on('move', function() {
53233                         _pointerHasMoved = true;
53234                         move.apply(this, arguments);
53235                     })
53236                     .on('down', function() {
53237                         move.apply(this, arguments);
53238                     })
53239                     .on('downcancel', function() {
53240                         if (_drawNode) { removeDrawNode(); }
53241                     })
53242                     .on('click', drawWay.add)
53243                     .on('clickWay', drawWay.addWay)
53244                     .on('clickNode', drawWay.addNode)
53245                     .on('undo', context.undo)
53246                     .on('cancel', drawWay.cancel)
53247                     .on('finish', drawWay.finish);
53248
53249                 select(window)
53250                     .on('keydown.drawWay', keydown)
53251                     .on('keyup.drawWay', keyup);
53252
53253                 context.map()
53254                     .dblclickZoomEnable(false)
53255                     .on('drawn.draw', setActiveElements);
53256
53257                 setActiveElements();
53258
53259                 surface.call(behavior);
53260
53261                 context.history()
53262                     .on('undone.draw', undone);
53263             };
53264
53265
53266             drawWay.off = function(surface) {
53267
53268                 if (!_didResolveTempEdit) {
53269                     // Drawing was interrupted unexpectedly.
53270                     // This can happen if the user changes modes,
53271                     // clicks geolocate button, a hashchange event occurs, etc.
53272
53273                     context.pauseChangeDispatch();
53274                     resetToStartGraph();
53275                     context.resumeChangeDispatch();
53276                 }
53277
53278                 _drawNode = undefined;
53279                 _nodeIndex = undefined;
53280
53281                 context.map()
53282                     .on('drawn.draw', null);
53283
53284                 surface.call(behavior.off)
53285                     .selectAll('.active')
53286                     .classed('active', false);
53287
53288                 surface
53289                     .classed('nope', false)
53290                     .classed('nope-suppressed', false)
53291                     .classed('nope-disabled', false);
53292
53293                 select(window)
53294                     .on('keydown.drawWay', null)
53295                     .on('keyup.drawWay', null);
53296
53297                 context.history()
53298                     .on('undone.draw', null);
53299             };
53300
53301
53302             function attemptAdd(d, loc, doAdd) {
53303
53304                 if (_drawNode) {
53305                     // move the node to the final loc in case move wasn't called
53306                     // consistently (e.g. on touch devices)
53307                     context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
53308                     _drawNode = context.entity(_drawNode.id);
53309                 } else {
53310                     createDrawNode(loc);
53311                 }
53312
53313                 checkGeometry(true /* includeDrawNode */);
53314                 if ((d && d.properties && d.properties.nope) || context.surface().classed('nope')) {
53315                     if (!_pointerHasMoved) {
53316                         // prevent the temporary draw node from appearing on touch devices
53317                         removeDrawNode();
53318                     }
53319                     dispatch$1.call('rejectedSelfIntersection', this);
53320                     return;   // can't click here
53321                 }
53322
53323                 context.pauseChangeDispatch();
53324                 doAdd();
53325                 // we just replaced the temporary edit with the real one
53326                 _didResolveTempEdit = true;
53327                 context.resumeChangeDispatch();
53328
53329                 context.enter(mode);
53330             }
53331
53332
53333             // Accept the current position of the drawing node
53334             drawWay.add = function(loc, d) {
53335                 attemptAdd(d, loc, function() {
53336                     // don't need to do anything extra
53337                 });
53338             };
53339
53340
53341             // Connect the way to an existing way
53342             drawWay.addWay = function(loc, edge, d) {
53343                 attemptAdd(d, loc, function() {
53344                     context.replace(
53345                         actionAddMidpoint({ loc: loc, edge: edge }, _drawNode),
53346                         _annotation
53347                     );
53348                 });
53349             };
53350
53351
53352             // Connect the way to an existing node
53353             drawWay.addNode = function(node, d) {
53354
53355                 // finish drawing if the mapper targets the prior node
53356                 if (node.id === _headNodeID ||
53357                     // or the first node when drawing an area
53358                     (_origWay.isClosed() && node.id === _origWay.first())) {
53359                     drawWay.finish();
53360                     return;
53361                 }
53362
53363                 attemptAdd(d, node.loc, function() {
53364                     context.replace(
53365                         function actionReplaceDrawNode(graph) {
53366                             // remove the temporary draw node and insert the existing node
53367                             // at the same index
53368
53369                             graph = graph
53370                                 .replace(graph.entity(wayID).removeNode(_drawNode.id))
53371                                 .remove(_drawNode);
53372                             return graph
53373                                 .replace(graph.entity(wayID).addNode(node.id, _nodeIndex));
53374                         },
53375                         _annotation
53376                     );
53377                 });
53378             };
53379
53380
53381             // Finish the draw operation, removing the temporary edit.
53382             // If the way has enough nodes to be valid, it's selected.
53383             // Otherwise, delete everything and return to browse mode.
53384             drawWay.finish = function() {
53385                 checkGeometry(false /* includeDrawNode */);
53386                 if (context.surface().classed('nope')) {
53387                     dispatch$1.call('rejectedSelfIntersection', this);
53388                     return;   // can't click here
53389                 }
53390
53391                 context.pauseChangeDispatch();
53392                 // remove the temporary edit
53393                 context.pop(1);
53394                 _didResolveTempEdit = true;
53395                 context.resumeChangeDispatch();
53396
53397                 var way = context.hasEntity(wayID);
53398                 if (!way || way.isDegenerate()) {
53399                     drawWay.cancel();
53400                     return;
53401                 }
53402
53403                 window.setTimeout(function() {
53404                     context.map().dblclickZoomEnable(true);
53405                 }, 1000);
53406
53407                 var isNewFeature = !mode.isContinuing;
53408                 context.enter(modeSelect(context, [wayID]).newFeature(isNewFeature));
53409             };
53410
53411
53412             // Cancel the draw operation, delete everything, and return to browse mode.
53413             drawWay.cancel = function() {
53414                 context.pauseChangeDispatch();
53415                 resetToStartGraph();
53416                 context.resumeChangeDispatch();
53417
53418                 window.setTimeout(function() {
53419                     context.map().dblclickZoomEnable(true);
53420                 }, 1000);
53421
53422                 context.surface()
53423                     .classed('nope', false)
53424                     .classed('nope-disabled', false)
53425                     .classed('nope-suppressed', false);
53426
53427                 context.enter(modeBrowse(context));
53428             };
53429
53430
53431             drawWay.nodeIndex = function(val) {
53432                 if (!arguments.length) { return _nodeIndex; }
53433                 _nodeIndex = val;
53434                 return drawWay;
53435             };
53436
53437
53438             drawWay.activeID = function() {
53439                 if (!arguments.length) { return _drawNode && _drawNode.id; }
53440                 // no assign
53441                 return drawWay;
53442             };
53443
53444
53445             return utilRebind(drawWay, dispatch$1, 'on');
53446         }
53447
53448         function modeDrawLine(context, wayID, startGraph, button, affix, continuing) {
53449             var mode = {
53450                 button: button,
53451                 id: 'draw-line'
53452             };
53453
53454             var behavior = behaviorDrawWay(context, wayID, mode, startGraph)
53455                 .on('rejectedSelfIntersection.modeDrawLine', function() {
53456                     context.ui().flash
53457                         .text(_t('self_intersection.error.lines'))();
53458                 });
53459
53460             mode.wayID = wayID;
53461
53462             mode.isContinuing = continuing;
53463
53464             mode.enter = function() {
53465                 behavior
53466                     .nodeIndex(affix === 'prefix' ? 0 : undefined);
53467
53468                 context.install(behavior);
53469             };
53470
53471             mode.exit = function() {
53472                 context.uninstall(behavior);
53473             };
53474
53475             mode.selectedIDs = function() {
53476                 return [wayID];
53477             };
53478
53479             mode.activeID = function() {
53480                 return (behavior && behavior.activeID()) || [];
53481             };
53482
53483             return mode;
53484         }
53485
53486         function operationContinue(context, selectedIDs) {
53487             var graph = context.graph();
53488             var entities = selectedIDs.map(function(id) { return graph.entity(id); });
53489             var geometries = Object.assign(
53490                 { line: [], vertex: [] },
53491                 utilArrayGroupBy(entities, function(entity) { return entity.geometry(graph); })
53492             );
53493             var vertex = geometries.vertex[0];
53494
53495
53496             function candidateWays() {
53497                 return graph.parentWays(vertex).filter(function(parent) {
53498                     return parent.geometry(graph) === 'line' &&
53499                         !parent.isClosed() &&
53500                         parent.affix(vertex.id) &&
53501                         (geometries.line.length === 0 || geometries.line[0] === parent);
53502                 });
53503             }
53504
53505
53506             var operation = function() {
53507                 var candidate = candidateWays()[0];
53508                 context.enter(
53509                     modeDrawLine(context, candidate.id, context.graph(), 'line', candidate.affix(vertex.id), true)
53510                 );
53511             };
53512
53513
53514             operation.available = function() {
53515                 return geometries.vertex.length === 1 &&
53516                     geometries.line.length <= 1 &&
53517                     !context.features().hasHiddenConnections(vertex, context.graph());
53518             };
53519
53520
53521             operation.disabled = function() {
53522                 var candidates = candidateWays();
53523                 if (candidates.length === 0) {
53524                     return 'not_eligible';
53525                 } else if (candidates.length > 1) {
53526                     return 'multiple';
53527                 }
53528
53529                 return false;
53530             };
53531
53532
53533             operation.tooltip = function() {
53534                 var disable = operation.disabled();
53535                 return disable ?
53536                     _t('operations.continue.' + disable) :
53537                     _t('operations.continue.description');
53538             };
53539
53540
53541             operation.annotation = function() {
53542                 return _t('operations.continue.annotation.line');
53543             };
53544
53545
53546             operation.id = 'continue';
53547             operation.keys = [_t('operations.continue.key')];
53548             operation.title = _t('operations.continue.title');
53549             operation.behavior = behaviorOperation(context).which(operation);
53550
53551             return operation;
53552         }
53553
53554         function operationCopy(context, selectedIDs) {
53555
53556             var _multi = selectedIDs.length === 1 ? 'single' : 'multiple';
53557
53558             function getFilteredIdsToCopy() {
53559                 return selectedIDs.filter(function(selectedID) {
53560                     var entity = context.graph().hasEntity(selectedID);
53561                     // don't copy untagged vertices separately from ways
53562                     return entity.hasInterestingTags() || entity.geometry(context.graph()) !== 'vertex';
53563                 });
53564             }
53565
53566             var operation = function() {
53567
53568                 if (!getSelectionText()) {
53569                     event.preventDefault();
53570                 }
53571
53572                 var graph = context.graph();
53573                 var selected = groupEntities(getFilteredIdsToCopy(), graph);
53574                 var canCopy = [];
53575                 var skip = {};
53576                 var entity;
53577                 var i;
53578
53579                 for (i = 0; i < selected.relation.length; i++) {
53580                     entity = selected.relation[i];
53581                     if (!skip[entity.id] && entity.isComplete(graph)) {
53582                         canCopy.push(entity.id);
53583                         skip = getDescendants(entity.id, graph, skip);
53584                     }
53585                 }
53586                 for (i = 0; i < selected.way.length; i++) {
53587                     entity = selected.way[i];
53588                     if (!skip[entity.id]) {
53589                         canCopy.push(entity.id);
53590                         skip = getDescendants(entity.id, graph, skip);
53591                     }
53592                 }
53593                 for (i = 0; i < selected.node.length; i++) {
53594                     entity = selected.node[i];
53595                     if (!skip[entity.id]) {
53596                         canCopy.push(entity.id);
53597                     }
53598                 }
53599
53600                 context.copyIDs(canCopy);
53601                 if (_point &&
53602                     (canCopy.length !== 1 || graph.entity(canCopy[0]).type !== 'node')) {
53603                     // store the anchor coordinates if copying more than a single node
53604                     context.copyLonLat(context.projection.invert(_point));
53605                 } else {
53606                     context.copyLonLat(null);
53607                 }
53608
53609             };
53610
53611
53612             function groupEntities(ids, graph) {
53613                 var entities = ids.map(function (id) { return graph.entity(id); });
53614                 return Object.assign(
53615                     { relation: [], way: [], node: [] },
53616                     utilArrayGroupBy(entities, 'type')
53617                 );
53618             }
53619
53620
53621             function getDescendants(id, graph, descendants) {
53622                 var entity = graph.entity(id);
53623                 var children;
53624
53625                 descendants = descendants || {};
53626
53627                 if (entity.type === 'relation') {
53628                     children = entity.members.map(function(m) { return m.id; });
53629                 } else if (entity.type === 'way') {
53630                     children = entity.nodes;
53631                 } else {
53632                     children = [];
53633                 }
53634
53635                 for (var i = 0; i < children.length; i++) {
53636                     if (!descendants[children[i]]) {
53637                         descendants[children[i]] = true;
53638                         descendants = getDescendants(children[i], graph, descendants);
53639                     }
53640                 }
53641
53642                 return descendants;
53643             }
53644
53645
53646             function getSelectionText() {
53647                 return window.getSelection().toString();
53648             }
53649
53650
53651             operation.available = function() {
53652                 return getFilteredIdsToCopy().length > 0;
53653             };
53654
53655
53656             operation.disabled = function() {
53657                 var extent = utilTotalExtent(getFilteredIdsToCopy(), context.graph());
53658                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
53659                     return 'too_large';
53660                 }
53661                 return false;
53662             };
53663
53664
53665             operation.tooltip = function() {
53666                 var disable = operation.disabled();
53667                 return disable ?
53668                     _t('operations.copy.' + disable + '.' + _multi) :
53669                     _t('operations.copy.description' + '.' + _multi);
53670             };
53671
53672
53673             operation.annotation = function() {
53674                 return selectedIDs.length === 1 ?
53675                     _t('operations.copy.annotation.single') :
53676                     _t('operations.copy.annotation.multiple', { n: selectedIDs.length.toString() });
53677             };
53678
53679
53680             var _point;
53681             operation.point = function(val) {
53682                 _point = val;
53683                 return operation;
53684             };
53685
53686
53687             operation.id = 'copy';
53688             operation.keys = [uiCmd('⌘C')];
53689             operation.title = _t('operations.copy.title');
53690             operation.behavior = behaviorOperation(context).which(operation);
53691
53692             return operation;
53693         }
53694
53695         function operationDisconnect(context, selectedIDs) {
53696             var _vertexIDs = [];
53697             var _wayIDs = [];
53698             var _otherIDs = [];
53699             var _actions = [];
53700
53701             selectedIDs.forEach(function(id) {
53702                 var entity = context.entity(id);
53703                 if (entity.type === 'way'){
53704                     _wayIDs.push(id);
53705                 } else if (entity.geometry(context.graph()) === 'vertex') {
53706                     _vertexIDs.push(id);
53707                 } else {
53708                     _otherIDs.push(id);
53709                 }
53710             });
53711
53712             var _extent, _nodes, _coords, _descriptionID = '', _annotationID = 'features';
53713
53714             if (_vertexIDs.length > 0) {
53715                 // At the selected vertices, disconnect the selected ways, if any, else
53716                 // disconnect all connected ways
53717
53718                 _extent = utilTotalExtent(_vertexIDs, context.graph());
53719
53720                 _vertexIDs.forEach(function(vertexID) {
53721                     var action = actionDisconnect(vertexID);
53722
53723                     if (_wayIDs.length > 0) {
53724                         var waysIDsForVertex = _wayIDs.filter(function(wayID) {
53725                             var way = context.entity(wayID);
53726                             return way.nodes.indexOf(vertexID) !== -1;
53727                         });
53728                         action.limitWays(waysIDsForVertex);
53729                     }
53730                     _actions.push(action);
53731                 });
53732
53733                 _descriptionID += _actions.length === 1 ? 'single_point.' : 'multiple_points.';
53734                 if (_wayIDs.length === 1) {
53735                     _descriptionID += 'single_way.' + context.graph().geometry(_wayIDs[0]);
53736                 } else {
53737                     _descriptionID += _wayIDs.length === 0 ? 'no_ways' : 'multiple_ways';
53738                 }
53739
53740             } else if (_wayIDs.length > 0) {
53741                 // Disconnect the selected ways from each other, if they're connected,
53742                 // else disconnect them from all connected ways
53743
53744                 var ways = _wayIDs.map(function(id) {
53745                     return context.entity(id);
53746                 });
53747                 _nodes = utilGetAllNodes(_wayIDs, context.graph());
53748                 _coords = _nodes.map(function(n) { return n.loc; });
53749                 _extent = utilTotalExtent(ways, context.graph());
53750
53751                 // actions for connected nodes shared by at least two selected ways
53752                 var sharedActions = [];
53753                 // actions for connected nodes
53754                 var unsharedActions = [];
53755
53756                 _nodes.forEach(function(node) {
53757                     var action = actionDisconnect(node.id).limitWays(_wayIDs);
53758                     if (action.disabled(context.graph()) !== 'not_connected') {
53759
53760                         var count = 0;
53761                         for (var i in ways) {
53762                             var way = ways[i];
53763                             if (way.nodes.indexOf(node.id) !== -1) {
53764                                 count += 1;
53765                             }
53766                             if (count > 1) { break; }
53767                         }
53768
53769                         if (count > 1) {
53770                             sharedActions.push(action);
53771                         } else {
53772                             unsharedActions.push(action);
53773                         }
53774                     }
53775                 });
53776
53777                 _descriptionID += 'no_points.';
53778                 _descriptionID += _wayIDs.length === 1 ? 'single_way.' : 'multiple_ways.';
53779
53780                 if (sharedActions.length) {
53781                     // if any nodes are shared, only disconnect the selected ways from each other
53782                     _actions = sharedActions;
53783                     _descriptionID += 'conjoined';
53784                     _annotationID = 'from_each_other';
53785                 } else {
53786                     // if no nodes are shared, disconnect the selected ways from all connected ways
53787                     _actions = unsharedActions;
53788                     if (_wayIDs.length === 1) {
53789                         _descriptionID += context.graph().geometry(_wayIDs[0]);
53790                     } else {
53791                         _descriptionID += 'separate';
53792                     }
53793                 }
53794             }
53795
53796
53797             var operation = function() {
53798                 context.perform(function(graph) {
53799                     return _actions.reduce(function(graph, action) { return action(graph); }, graph);
53800                 }, operation.annotation());
53801
53802                 context.validator().validate();
53803             };
53804
53805
53806             operation.available = function() {
53807                 if (_actions.length === 0) { return false; }
53808                 if (_otherIDs.length !== 0) { return false; }
53809
53810                 if (_vertexIDs.length !== 0 && _wayIDs.length !== 0 && !_wayIDs.every(function(wayID) {
53811                     return _vertexIDs.some(function(vertexID) {
53812                         var way = context.entity(wayID);
53813                         return way.nodes.indexOf(vertexID) !== -1;
53814                     });
53815                 })) { return false; }
53816
53817                 return true;
53818             };
53819
53820
53821             operation.disabled = function() {
53822                 var reason;
53823                 for (var actionIndex in _actions) {
53824                     reason = _actions[actionIndex].disabled(context.graph());
53825                     if (reason) { return reason; }
53826                 }
53827
53828                 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
53829                     return 'too_large.' + ((_vertexIDs.length ? _vertexIDs : _wayIDs).length === 1 ? 'single' : 'multiple');
53830                 } else if (_coords && someMissing()) {
53831                     return 'not_downloaded';
53832                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
53833                     return 'connected_to_hidden';
53834                 }
53835
53836                 return false;
53837
53838
53839                 function someMissing() {
53840                     if (context.inIntro()) { return false; }
53841                     var osm = context.connection();
53842                     if (osm) {
53843                         var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
53844                         if (missing.length) {
53845                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
53846                             return true;
53847                         }
53848                     }
53849                     return false;
53850                 }
53851             };
53852
53853
53854             operation.tooltip = function() {
53855                 var disable = operation.disabled();
53856                 if (disable) {
53857                     return _t('operations.disconnect.' + disable);
53858                 }
53859                 return _t('operations.disconnect.description.' + _descriptionID);
53860             };
53861
53862
53863             operation.annotation = function() {
53864                 return _t('operations.disconnect.annotation.' + _annotationID);
53865             };
53866
53867
53868             operation.id = 'disconnect';
53869             operation.keys = [_t('operations.disconnect.key')];
53870             operation.title = _t('operations.disconnect.title');
53871             operation.behavior = behaviorOperation(context).which(operation);
53872
53873             return operation;
53874         }
53875
53876         function operationDowngrade(context, selectedIDs) {
53877             var affectedFeatureCount = 0;
53878             var downgradeType;
53879
53880             setDowngradeTypeForEntityIDs();
53881
53882             var multi = affectedFeatureCount === 1 ? 'single' : 'multiple';
53883
53884             function setDowngradeTypeForEntityIDs() {
53885                 for (var i in selectedIDs) {
53886                     var entityID = selectedIDs[i];
53887                     var type = downgradeTypeForEntityID(entityID);
53888                     if (type) {
53889                         affectedFeatureCount += 1;
53890                         if (downgradeType && type !== downgradeType) {
53891                             downgradeType = 'building_address';
53892                         } else {
53893                             downgradeType = type;
53894                         }
53895                     }
53896                 }
53897             }
53898
53899             function downgradeTypeForEntityID(entityID) {
53900                 var graph = context.graph();
53901                 var entity = graph.entity(entityID);
53902                 var preset = _mainPresetIndex.match(entity, graph);
53903
53904                 if (!preset || preset.isFallback()) { return null; }
53905
53906                 if (entity.type === 'node' &&
53907                     preset.id !== 'address' &&
53908                     Object.keys(entity.tags).some(function(key) {
53909                         return key.match(/^addr:.{1,}/);
53910                     })) {
53911
53912                     return 'address';
53913                 }
53914                 if (entity.geometry(graph) === 'area' &&
53915                     entity.tags.building &&
53916                     !preset.tags.building) {
53917
53918                     return 'building';
53919                 }
53920
53921                 return null;
53922             }
53923
53924             var buildingKeysToKeep = ['architect', 'building', 'height', 'layer', 'source', 'type', 'wheelchair'];
53925             var addressKeysToKeep = ['source'];
53926
53927             var operation = function () {
53928                 context.perform(function(graph) {
53929
53930                     for (var i in selectedIDs) {
53931                         var entityID = selectedIDs[i];
53932                         var type = downgradeTypeForEntityID(entityID);
53933                         if (!type) { continue; }
53934
53935                         var tags = Object.assign({}, graph.entity(entityID).tags);  // shallow copy
53936                         for (var key in tags) {
53937                             if (type === 'address' && addressKeysToKeep.indexOf(key) !== -1) { continue; }
53938                             if (type === 'building') {
53939                                 if (buildingKeysToKeep.indexOf(key) !== -1 ||
53940                                     key.match(/^building:.{1,}/) ||
53941                                     key.match(/^roof:.{1,}/)) { continue; }
53942                             }
53943                             // keep address tags for buildings too
53944                             if (key.match(/^addr:.{1,}/)) { continue; }
53945
53946                             delete tags[key];
53947                         }
53948                         graph = actionChangeTags(entityID, tags)(graph);
53949                     }
53950                     return graph;
53951                 }, operation.annotation());
53952
53953                 context.validator().validate();
53954
53955                 // refresh the select mode to enable the delete operation
53956                 context.enter(modeSelect(context, selectedIDs));
53957             };
53958
53959
53960             operation.available = function () {
53961                 return downgradeType;
53962             };
53963
53964
53965             operation.disabled = function () {
53966                 if (selectedIDs.some(hasWikidataTag)) {
53967                     return 'has_wikidata_tag';
53968                 }
53969                 return false;
53970
53971                 function hasWikidataTag(id) {
53972                     var entity = context.entity(id);
53973                     return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
53974                 }
53975             };
53976
53977
53978             operation.tooltip = function () {
53979                 var disable = operation.disabled();
53980                 return disable ?
53981                     _t('operations.downgrade.' + disable + '.' + multi) :
53982                     _t('operations.downgrade.description.' + downgradeType);
53983             };
53984
53985
53986             operation.annotation = function () {
53987                 var suffix;
53988                 if (downgradeType === 'building_address') {
53989                     suffix = 'multiple';
53990                 } else {
53991                     suffix = downgradeType + '.' + multi;
53992                 }
53993                 return _t('operations.downgrade.annotation.' + suffix, { n: affectedFeatureCount});
53994             };
53995
53996
53997             operation.id = 'downgrade';
53998             operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')];
53999             operation.title = _t('operations.downgrade.title');
54000             operation.behavior = behaviorOperation(context).which(operation);
54001
54002
54003             return operation;
54004         }
54005
54006         function operationExtract(context, selectedIDs) {
54007
54008             var _amount = selectedIDs.length === 1 ? 'single' : 'multiple';
54009             var _geometries = utilArrayUniq(selectedIDs.map(function(entityID) {
54010                 return context.graph().hasEntity(entityID) && context.graph().geometry(entityID);
54011             }).filter(Boolean));
54012             var _geometryID = _geometries.length === 1 ? _geometries[0] : 'feature';
54013
54014             var _extent;
54015             var _actions = selectedIDs.map(function(entityID) {
54016                 var graph = context.graph();
54017                 var entity = graph.hasEntity(entityID);
54018                 if (!entity || !entity.hasInterestingTags()) { return; }
54019
54020                 if (entity.type === 'node' && graph.parentWays(entity).length === 0) { return; }
54021
54022                 if (entity.type !== 'node') {
54023                     var preset = _mainPresetIndex.match(entity, graph);
54024                     // only allow extraction from ways/relations if the preset supports points
54025                     if (preset.geometry.indexOf('point') === -1) { return; }
54026                 }
54027
54028                 _extent = _extent ? _extent.extend(entity.extent(graph)) : entity.extent(graph);
54029
54030                 return actionExtract(entityID);
54031             }).filter(Boolean);
54032
54033
54034             var operation = function () {
54035                 var combinedAction = function(graph) {
54036                     _actions.forEach(function(action) {
54037                         graph = action(graph);
54038                     });
54039                     return graph;
54040                 };
54041                 context.perform(combinedAction, operation.annotation());  // do the extract
54042
54043                 var extractedNodeIDs = _actions.map(function(action) {
54044                     return action.getExtractedNodeID();
54045                 });
54046                 context.enter(modeSelect(context, extractedNodeIDs));
54047             };
54048
54049
54050             operation.available = function () {
54051                 return _actions.length && selectedIDs.length === _actions.length;
54052             };
54053
54054
54055             operation.disabled = function () {
54056
54057                 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
54058                     return 'too_large';
54059                 } else if (selectedIDs.some(function(entityID) {
54060                     return context.graph().geometry(entityID) === 'vertex' && context.hasHiddenConnections(entityID);
54061                 })) {
54062                     return 'connected_to_hidden';
54063                 }
54064
54065                 return false;
54066             };
54067
54068
54069             operation.tooltip = function () {
54070                 var disableReason = operation.disabled();
54071                 if (disableReason) {
54072                     return _t('operations.extract.' + disableReason + '.' + _amount);
54073                 } else {
54074                     return _t('operations.extract.description.' + _geometryID + '.' + _amount);
54075                 }
54076             };
54077
54078
54079             operation.annotation = function () {
54080                 return _t('operations.extract.annotation.' + _amount, { n: selectedIDs.length });
54081             };
54082
54083
54084             operation.id = 'extract';
54085             operation.keys = [_t('operations.extract.key')];
54086             operation.title = _t('operations.extract.title');
54087             operation.behavior = behaviorOperation(context).which(operation);
54088
54089
54090             return operation;
54091         }
54092
54093         function operationMerge(context, selectedIDs) {
54094
54095             var _action = getAction();
54096
54097             function getAction() {
54098                 // prefer a non-disabled action first
54099                 var join = actionJoin(selectedIDs);
54100                 if (!join.disabled(context.graph())) { return join; }
54101
54102                 var merge = actionMerge(selectedIDs);
54103                 if (!merge.disabled(context.graph())) { return merge; }
54104
54105                 var mergePolygon = actionMergePolygon(selectedIDs);
54106                 if (!mergePolygon.disabled(context.graph())) { return mergePolygon; }
54107
54108                 var mergeNodes = actionMergeNodes(selectedIDs);
54109                 if (!mergeNodes.disabled(context.graph())) { return mergeNodes; }
54110
54111                 // otherwise prefer an action with an interesting disabled reason
54112                 if (join.disabled(context.graph()) !== 'not_eligible') { return join; }
54113                 if (merge.disabled(context.graph()) !== 'not_eligible') { return merge; }
54114                 if (mergePolygon.disabled(context.graph()) !== 'not_eligible') { return mergePolygon; }
54115
54116                 return mergeNodes;
54117             }
54118
54119             var operation = function() {
54120
54121                 if (operation.disabled()) { return; }
54122
54123                 context.perform(_action, operation.annotation());
54124
54125                 context.validator().validate();
54126
54127                 var resultIDs = selectedIDs.filter(context.hasEntity);
54128                 if (resultIDs.length > 1) {
54129                     var interestingIDs = resultIDs.filter(function(id) {
54130                         return context.entity(id).hasInterestingTags();
54131                     });
54132                     if (interestingIDs.length) { resultIDs = interestingIDs; }
54133                 }
54134                 context.enter(modeSelect(context, resultIDs));
54135             };
54136
54137             operation.available = function() {
54138                 return selectedIDs.length >= 2;
54139             };
54140
54141             operation.disabled = function() {
54142                 var actionDisabled = _action.disabled(context.graph());
54143                 if (actionDisabled) { return actionDisabled; }
54144
54145                 var osm = context.connection();
54146                 if (osm &&
54147                     _action.resultingWayNodesLength &&
54148                     _action.resultingWayNodesLength(context.graph()) > osm.maxWayNodes()) {
54149                     return 'too_many_vertices';
54150                 }
54151
54152                 return false;
54153             };
54154
54155             operation.tooltip = function() {
54156                 var disabled = operation.disabled();
54157                 if (disabled) {
54158                     if (disabled === 'restriction') {
54159                         return _t('operations.merge.restriction',
54160                             { relation: _mainPresetIndex.item('type/restriction').name() });
54161                     }
54162                     return _t('operations.merge.' + disabled);
54163                 }
54164                 return _t('operations.merge.description');
54165             };
54166
54167             operation.annotation = function() {
54168                 return _t('operations.merge.annotation', { n: selectedIDs.length });
54169             };
54170
54171             operation.id = 'merge';
54172             operation.keys = [_t('operations.merge.key')];
54173             operation.title = _t('operations.merge.title');
54174             operation.behavior = behaviorOperation(context).which(operation);
54175
54176             return operation;
54177         }
54178
54179         // see also `behaviorPaste`
54180         function operationPaste(context) {
54181
54182             var _pastePoint;
54183
54184             var operation = function() {
54185
54186                 if (!_pastePoint) { return; }
54187
54188                 var oldIDs = context.copyIDs();
54189                 if (!oldIDs.length) { return; }
54190
54191                 var projection = context.projection;
54192                 var extent = geoExtent();
54193                 var oldGraph = context.copyGraph();
54194                 var newIDs = [];
54195
54196                 var action = actionCopyEntities(oldIDs, oldGraph);
54197                 context.perform(action);
54198
54199                 var copies = action.copies();
54200                 var originals = new Set();
54201                 Object.values(copies).forEach(function(entity) { originals.add(entity.id); });
54202
54203                 for (var id in copies) {
54204                     var oldEntity = oldGraph.entity(id);
54205                     var newEntity = copies[id];
54206
54207                     extent._extend(oldEntity.extent(oldGraph));
54208
54209                     // Exclude child nodes from newIDs if their parent way was also copied.
54210                     var parents = context.graph().parentWays(newEntity);
54211                     var parentCopied = parents.some(function(parent) {
54212                         return originals.has(parent.id);
54213                     });
54214
54215                     if (!parentCopied) {
54216                         newIDs.push(newEntity.id);
54217                     }
54218                 }
54219
54220                 // Use the location of the copy operation to offset the paste location,
54221                 // or else use the center of the pasted extent
54222                 var copyPoint = (context.copyLonLat() && projection(context.copyLonLat())) ||
54223                     projection(extent.center());
54224                 var delta = geoVecSubtract(_pastePoint, copyPoint);
54225
54226                 // Move the pasted objects to be anchored at the paste location
54227                 context.replace(actionMove(newIDs, delta, projection), operation.annotation());
54228                 context.enter(modeSelect(context, newIDs));
54229             };
54230
54231             operation.point = function(val) {
54232                 _pastePoint = val;
54233                 return operation;
54234             };
54235
54236             operation.available = function() {
54237                 return context.mode().id === 'browse';
54238             };
54239
54240             operation.disabled = function() {
54241                 return !context.copyIDs().length;
54242             };
54243
54244             operation.tooltip = function() {
54245                 var oldGraph = context.copyGraph();
54246                 var ids = context.copyIDs();
54247                 if (!ids.length) {
54248                     return _t('operations.paste.nothing_copied');
54249                 }
54250                 return ids.length === 1 ?
54251                     _t('operations.paste.description.single', { feature: utilDisplayLabel(oldGraph.entity(ids[0]), oldGraph) }) :
54252                     _t('operations.paste.description.multiple', { n: ids.length.toString() });
54253             };
54254
54255             operation.annotation = function() {
54256                 var ids = context.copyIDs();
54257                 return ids.length === 1 ?
54258                     _t('operations.paste.annotation.single') :
54259                     _t('operations.paste.annotation.multiple', { n: ids.length.toString() });
54260             };
54261
54262             operation.id = 'paste';
54263             operation.keys = [uiCmd('⌘V')];
54264             operation.title = _t('operations.paste.title');
54265
54266             return operation;
54267         }
54268
54269         function operationReverse(context, selectedIDs) {
54270
54271             var operation = function() {
54272                 context.perform(function combinedReverseAction(graph) {
54273                     actions().forEach(function(action) {
54274                         graph = action(graph);
54275                     });
54276                     return graph;
54277                 }, operation.annotation());
54278                 context.validator().validate();
54279             };
54280
54281             function actions(situation) {
54282                 return selectedIDs.map(function(entityID) {
54283                     var entity = context.hasEntity(entityID);
54284                     if (!entity) { return; }
54285
54286                     if (situation === 'toolbar') {
54287                         if (entity.type === 'way' &&
54288                             (!entity.isOneWay() && !entity.isSided())) { return; }
54289                     }
54290
54291                     var geometry = entity.geometry(context.graph());
54292                     if (entity.type !== 'node' && geometry !== 'line') { return; }
54293
54294                     var action = actionReverse(entityID);
54295                     if (action.disabled(context.graph())) { return; }
54296
54297                     return action;
54298                 }).filter(Boolean);
54299             }
54300
54301             function reverseTypeID() {
54302                 var acts = actions();
54303                 var nodeActionCount = acts.filter(function(act) {
54304                     var entity = context.hasEntity(act.entityID());
54305                     return entity && entity.type === 'node';
54306                 }).length;
54307                 var typeID = nodeActionCount === 0 ? 'line' : (nodeActionCount === acts.length ? 'point' : 'features');
54308                 if (typeID !== 'features' && acts.length > 1) { typeID += 's'; }
54309                 return typeID;
54310             }
54311
54312
54313             operation.available = function(situation) {
54314                 return actions(situation).length > 0;
54315             };
54316
54317
54318             operation.disabled = function() {
54319                 return false;
54320             };
54321
54322
54323             operation.tooltip = function() {
54324                 return _t('operations.reverse.description.' + reverseTypeID());
54325             };
54326
54327
54328             operation.annotation = function() {
54329                 return _t('operations.reverse.annotation.' + reverseTypeID());
54330             };
54331
54332
54333             operation.id = 'reverse';
54334             operation.keys = [_t('operations.reverse.key')];
54335             operation.title = _t('operations.reverse.title');
54336             operation.behavior = behaviorOperation(context).which(operation);
54337
54338             return operation;
54339         }
54340
54341         function operationSplit(context, selectedIDs) {
54342             var vertices = selectedIDs
54343                 .filter(function(id) { return context.graph().geometry(id) === 'vertex'; });
54344             var entityID = vertices[0];
54345             var action = actionSplit(entityID);
54346             var ways = [];
54347
54348             if (vertices.length === 1) {
54349                 if (entityID && selectedIDs.length > 1) {
54350                     var ids = selectedIDs.filter(function(id) { return id !== entityID; });
54351                     action.limitWays(ids);
54352                 }
54353                 ways = action.ways(context.graph());
54354             }
54355
54356
54357             var operation = function() {
54358                 var difference = context.perform(action, operation.annotation());
54359                 context.enter(modeSelect(context, difference.extantIDs()));
54360             };
54361
54362
54363             operation.available = function() {
54364                 return vertices.length === 1;
54365             };
54366
54367
54368             operation.disabled = function() {
54369                 var reason = action.disabled(context.graph());
54370                 if (reason) {
54371                     return reason;
54372                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
54373                     return 'connected_to_hidden';
54374                 }
54375
54376                 return false;
54377             };
54378
54379
54380             operation.tooltip = function() {
54381                 var disable = operation.disabled();
54382                 if (disable) {
54383                     return _t('operations.split.' + disable);
54384                 } else if (ways.length === 1) {
54385                     return _t('operations.split.description.' + context.graph().geometry(ways[0].id));
54386                 } else {
54387                     return _t('operations.split.description.multiple');
54388                 }
54389             };
54390
54391
54392             operation.annotation = function() {
54393                 return ways.length === 1 ?
54394                     _t('operations.split.annotation.' + context.graph().geometry(ways[0].id)) :
54395                     _t('operations.split.annotation.multiple', { n: ways.length });
54396             };
54397
54398
54399             operation.id = 'split';
54400             operation.keys = [_t('operations.split.key')];
54401             operation.title = _t('operations.split.title');
54402             operation.behavior = behaviorOperation(context).which(operation);
54403
54404             return operation;
54405         }
54406
54407         function operationStraighten(context, selectedIDs) {
54408             var _wayIDs = selectedIDs.filter(function(id) { return id.charAt(0) === 'w'; });
54409             var _nodeIDs = selectedIDs.filter(function(id) { return id.charAt(0) === 'n'; });
54410             var _amount = ((_wayIDs.length ? _wayIDs : _nodeIDs).length === 1 ? 'single' : 'multiple');
54411
54412             var _nodes = utilGetAllNodes(selectedIDs, context.graph());
54413             var _coords = _nodes.map(function(n) { return n.loc; });
54414             var _extent = utilTotalExtent(selectedIDs, context.graph());
54415             var _action = chooseAction();
54416             var _geometry;
54417
54418
54419             function chooseAction() {
54420                 // straighten selected nodes
54421                 if (_wayIDs.length === 0 && _nodeIDs.length > 2) {
54422                     _geometry = 'points';
54423                     return actionStraightenNodes(_nodeIDs, context.projection);
54424
54425                 // straighten selected ways (possibly between range of 2 selected nodes)
54426                 } else if (_wayIDs.length > 0 && (_nodeIDs.length === 0 || _nodeIDs.length === 2)) {
54427                     var startNodeIDs = [];
54428                     var endNodeIDs = [];
54429
54430                     for (var i = 0; i < selectedIDs.length; i++) {
54431                         var entity = context.entity(selectedIDs[i]);
54432                         if (entity.type === 'node') {
54433                             continue;
54434                         } else if (entity.type !== 'way' || entity.isClosed()) {
54435                             return null;  // exit early, can't straighten these
54436                         }
54437
54438                         startNodeIDs.push(entity.first());
54439                         endNodeIDs.push(entity.last());
54440                     }
54441
54442                     // Remove duplicate end/startNodeIDs (duplicate nodes cannot be at the line end)
54443                     startNodeIDs = startNodeIDs.filter(function(n) {
54444                         return startNodeIDs.indexOf(n) === startNodeIDs.lastIndexOf(n);
54445                     });
54446                     endNodeIDs = endNodeIDs.filter(function(n) {
54447                         return endNodeIDs.indexOf(n) === endNodeIDs.lastIndexOf(n);
54448                     });
54449
54450                     // Ensure all ways are connected (i.e. only 2 unique endpoints/startpoints)
54451                     if (utilArrayDifference(startNodeIDs, endNodeIDs).length +
54452                         utilArrayDifference(endNodeIDs, startNodeIDs).length !== 2) { return null; }
54453
54454                     // Ensure path contains at least 3 unique nodes
54455                     var wayNodeIDs = utilGetAllNodes(_wayIDs, context.graph())
54456                         .map(function(node) { return node.id; });
54457                     if (wayNodeIDs.length <= 2) { return null; }
54458
54459                     // If range of 2 selected nodes is supplied, ensure nodes lie on the selected path
54460                     if (_nodeIDs.length === 2 && (
54461                         wayNodeIDs.indexOf(_nodeIDs[0]) === -1 || wayNodeIDs.indexOf(_nodeIDs[1]) === -1
54462                     )) { return null; }
54463
54464                     if (_nodeIDs.length) {
54465                         // If we're only straightenting between two points, we only need that extent visible
54466                         _extent = utilTotalExtent(_nodeIDs, context.graph());
54467                     }
54468
54469                     _geometry = _wayIDs.length === 1 ? 'line' : 'lines';
54470                     return actionStraightenWay(selectedIDs, context.projection);
54471                 }
54472
54473                 return null;
54474             }
54475
54476
54477             function operation() {
54478                 if (!_action) { return; }
54479
54480                 context.perform(_action, operation.annotation());
54481
54482                 window.setTimeout(function() {
54483                     context.validator().validate();
54484                 }, 300);  // after any transition
54485             }
54486
54487
54488             operation.available = function() {
54489                 return Boolean(_action);
54490             };
54491
54492
54493             operation.disabled = function() {
54494                 var reason = _action.disabled(context.graph());
54495                 if (reason) {
54496                     return reason;
54497                 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
54498                     return 'too_large';
54499                 } else if (someMissing()) {
54500                     return 'not_downloaded';
54501                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
54502                     return 'connected_to_hidden';
54503                 }
54504
54505                 return false;
54506
54507
54508                 function someMissing() {
54509                     if (context.inIntro()) { return false; }
54510                     var osm = context.connection();
54511                     if (osm) {
54512                         var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
54513                         if (missing.length) {
54514                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
54515                             return true;
54516                         }
54517                     }
54518                     return false;
54519                 }
54520             };
54521
54522
54523             operation.tooltip = function() {
54524                 var disable = operation.disabled();
54525                 return disable ?
54526                     _t('operations.straighten.' + disable + '.' + _amount) :
54527                     _t('operations.straighten.description.' + _geometry);
54528             };
54529
54530
54531             operation.annotation = function() {
54532                 return _t('operations.straighten.annotation.' + _geometry);
54533             };
54534
54535
54536             operation.id = 'straighten';
54537             operation.keys = [_t('operations.straighten.key')];
54538             operation.title = _t('operations.straighten.title');
54539             operation.behavior = behaviorOperation(context).which(operation);
54540
54541             return operation;
54542         }
54543
54544         var Operations = /*#__PURE__*/Object.freeze({
54545                 __proto__: null,
54546                 operationCircularize: operationCircularize,
54547                 operationContinue: operationContinue,
54548                 operationCopy: operationCopy,
54549                 operationDelete: operationDelete,
54550                 operationDisconnect: operationDisconnect,
54551                 operationDowngrade: operationDowngrade,
54552                 operationExtract: operationExtract,
54553                 operationMerge: operationMerge,
54554                 operationMove: operationMove,
54555                 operationOrthogonalize: operationOrthogonalize,
54556                 operationPaste: operationPaste,
54557                 operationReflectShort: operationReflectShort,
54558                 operationReflectLong: operationReflectLong,
54559                 operationReverse: operationReverse,
54560                 operationRotate: operationRotate,
54561                 operationSplit: operationSplit,
54562                 operationStraighten: operationStraighten
54563         });
54564
54565         var _relatedParent;
54566
54567
54568         function modeSelect(context, selectedIDs) {
54569             var mode = {
54570                 id: 'select',
54571                 button: 'browse'
54572             };
54573
54574             var keybinding = utilKeybinding('select');
54575
54576             var _breatheBehavior = behaviorBreathe();
54577             var _modeDragNode = modeDragNode(context);
54578             var _selectBehavior;
54579             var _behaviors = [];
54580
54581             var _operations = [];
54582             var _newFeature = false;
54583             var _follow = false;
54584
54585
54586             function singular() {
54587                 if (selectedIDs && selectedIDs.length === 1) {
54588                     return context.hasEntity(selectedIDs[0]);
54589                 }
54590             }
54591
54592             function selectedEntities() {
54593                 return selectedIDs.map(function(id) {
54594                     return context.hasEntity(id);
54595                 }).filter(Boolean);
54596             }
54597
54598
54599             function checkSelectedIDs() {
54600                 var ids = [];
54601                 if (Array.isArray(selectedIDs)) {
54602                     ids = selectedIDs.filter(function(id) {
54603                         return context.hasEntity(id);
54604                     });
54605                 }
54606
54607                 if (!ids.length) {
54608                     context.enter(modeBrowse(context));
54609                     return false;
54610                 } else if ((selectedIDs.length > 1 && ids.length === 1) ||
54611                     (selectedIDs.length === 1 && ids.length > 1)) {
54612                     // switch between single- and multi-select UI
54613                     context.enter(modeSelect(context, ids));
54614                     return false;
54615                 }
54616
54617                 selectedIDs = ids;
54618                 return true;
54619             }
54620
54621
54622             // find the common parent ways for nextVertex, previousVertex
54623             function commonParents() {
54624                 var graph = context.graph();
54625                 var commonParents = [];
54626
54627                 for (var i = 0; i < selectedIDs.length; i++) {
54628                     var entity = context.hasEntity(selectedIDs[i]);
54629                     if (!entity || entity.geometry(graph) !== 'vertex') {
54630                         return [];  // selection includes some not vertexes
54631                     }
54632
54633                     var currParents = graph.parentWays(entity).map(function(w) { return w.id; });
54634                     if (!commonParents.length) {
54635                         commonParents = currParents;
54636                         continue;
54637                     }
54638
54639                     commonParents = utilArrayIntersection(commonParents, currParents);
54640                     if (!commonParents.length) {
54641                         return [];
54642                     }
54643                 }
54644
54645                 return commonParents;
54646             }
54647
54648
54649             function singularParent() {
54650                 var parents = commonParents();
54651                 if (!parents || parents.length === 0) {
54652                     _relatedParent = null;
54653                     return null;
54654                 }
54655
54656                 // relatedParent is used when we visit a vertex with multiple
54657                 // parents, and we want to remember which parent line we started on.
54658
54659                 if (parents.length === 1) {
54660                     _relatedParent = parents[0];  // remember this parent for later
54661                     return _relatedParent;
54662                 }
54663
54664                 if (parents.indexOf(_relatedParent) !== -1) {
54665                     return _relatedParent;   // prefer the previously seen parent
54666                 }
54667
54668                 return parents[0];
54669             }
54670
54671
54672             mode.selectedIDs = function(val) {
54673                 if (!arguments.length) { return selectedIDs; }
54674                 selectedIDs = val;
54675                 return mode;
54676             };
54677
54678
54679             mode.zoomToSelected = function() {
54680                 context.map().zoomToEase(selectedEntities());
54681             };
54682
54683
54684             mode.newFeature = function(val) {
54685                 if (!arguments.length) { return _newFeature; }
54686                 _newFeature = val;
54687                 return mode;
54688             };
54689
54690
54691             mode.selectBehavior = function(val) {
54692                 if (!arguments.length) { return _selectBehavior; }
54693                 _selectBehavior = val;
54694                 return mode;
54695             };
54696
54697
54698             mode.follow = function(val) {
54699                 if (!arguments.length) { return _follow; }
54700                 _follow = val;
54701                 return mode;
54702             };
54703
54704             function loadOperations() {
54705
54706                 _operations.forEach(function(operation) {
54707                     if (operation.behavior) {
54708                         context.uninstall(operation.behavior);
54709                     }
54710                 });
54711
54712                 _operations = Object.values(Operations)
54713                     .map(function(o) { return o(context, selectedIDs); })
54714                     .filter(function(o) { return o.available() && o.id !== 'delete' && o.id !== 'downgrade' && o.id !== 'copy'; });
54715
54716                 var copyOperation = operationCopy(context, selectedIDs);
54717                 if (copyOperation.available()) {
54718                     // group copy operation with delete/downgrade
54719                     _operations.push(copyOperation);
54720                 }
54721
54722                 var downgradeOperation = operationDowngrade(context, selectedIDs);
54723                 // don't allow delete if downgrade is available
54724                 var lastOperation = !context.inIntro() && downgradeOperation.available() ? downgradeOperation : operationDelete(context, selectedIDs);
54725
54726                 _operations.push(lastOperation);
54727
54728                 _operations.forEach(function(operation) {
54729                     if (operation.behavior) {
54730                         context.install(operation.behavior);
54731                     }
54732                 });
54733
54734                 // remove any displayed menu
54735                 context.ui().closeEditMenu();
54736             }
54737
54738             mode.operations = function() {
54739                 return _operations;
54740             };
54741
54742
54743             mode.enter = function() {
54744                 if (!checkSelectedIDs()) { return; }
54745
54746                 context.features().forceVisible(selectedIDs);
54747
54748                 _modeDragNode.restoreSelectedIDs(selectedIDs);
54749
54750                 loadOperations();
54751
54752                 if (!_behaviors.length) {
54753                     if (!_selectBehavior) { _selectBehavior = behaviorSelect(context); }
54754
54755                     _behaviors = [
54756                         behaviorPaste(context),
54757                         _breatheBehavior,
54758                         behaviorHover(context).on('hover', context.ui().sidebar.hoverModeSelect),
54759                         _selectBehavior,
54760                         behaviorLasso(context),
54761                         _modeDragNode.behavior,
54762                         modeDragNote(context).behavior
54763                     ];
54764                 }
54765                 _behaviors.forEach(context.install);
54766
54767                 keybinding
54768                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
54769                     .on(['[', 'pgup'], previousVertex)
54770                     .on([']', 'pgdown'], nextVertex)
54771                     .on(['{', uiCmd('⌘['), 'home'], firstVertex)
54772                     .on(['}', uiCmd('⌘]'), 'end'], lastVertex)
54773                     .on(uiCmd('⇧←'), nudgeSelection([-10, 0]))
54774                     .on(uiCmd('⇧↑'), nudgeSelection([0, -10]))
54775                     .on(uiCmd('⇧→'), nudgeSelection([10, 0]))
54776                     .on(uiCmd('⇧↓'), nudgeSelection([0, 10]))
54777                     .on(uiCmd('⇧⌘←'), nudgeSelection([-100, 0]))
54778                     .on(uiCmd('⇧⌘↑'), nudgeSelection([0, -100]))
54779                     .on(uiCmd('⇧⌘→'), nudgeSelection([100, 0]))
54780                     .on(uiCmd('⇧⌘↓'), nudgeSelection([0, 100]))
54781                     .on(['\\', 'pause'], nextParent)
54782                     .on('⎋', esc, true);
54783
54784                 select(document)
54785                     .call(keybinding);
54786
54787                 context.ui().sidebar
54788                     .select(selectedIDs, _newFeature);
54789
54790                 context.history()
54791                     .on('change.select', function() {
54792                         loadOperations();
54793                         // reselect after change in case relation members were removed or added
54794                         selectElements();
54795                     })
54796                     .on('undone.select', checkSelectedIDs)
54797                     .on('redone.select', checkSelectedIDs);
54798
54799                 context.map()
54800                     .on('drawn.select', selectElements)
54801                     .on('crossEditableZoom.select', function() {
54802                         selectElements();
54803                         _breatheBehavior.restartIfNeeded(context.surface());
54804                     });
54805
54806                 context.map().doubleUpHandler()
54807                     .on('doubleUp.modeSelect', didDoubleUp);
54808
54809
54810                 selectElements();
54811
54812                 if (_follow) {
54813                     var extent = geoExtent();
54814                     var graph = context.graph();
54815                     selectedIDs.forEach(function(id) {
54816                         var entity = context.entity(id);
54817                         extent._extend(entity.extent(graph));
54818                     });
54819
54820                     var loc = extent.center();
54821                     context.map().centerEase(loc);
54822                     // we could enter the mode multiple times, so reset follow for next time
54823                     _follow = false;
54824                 }
54825
54826
54827                 function nudgeSelection(delta) {
54828                     return function() {
54829                         // prevent nudging during low zoom selection
54830                         if (!context.map().withinEditableZoom()) { return; }
54831
54832                         var moveOp = operationMove(context, selectedIDs);
54833                         if (moveOp.disabled()) {
54834                             context.ui().flash
54835                                 .duration(4000)
54836                                 .iconName('#iD-operation-' + moveOp.id)
54837                                 .iconClass('operation disabled')
54838                                 .text(moveOp.tooltip)();
54839                         } else {
54840                             context.perform(actionMove(selectedIDs, delta, context.projection), moveOp.annotation());
54841                         }
54842                     };
54843                 }
54844
54845
54846                 function didDoubleUp(loc) {
54847                     if (!context.map().withinEditableZoom()) { return; }
54848
54849                     var target = select(event.target);
54850
54851                     var datum = target.datum();
54852                     var entity = datum && datum.properties && datum.properties.entity;
54853                     if (!entity) { return; }
54854
54855                     if (entity instanceof osmWay && target.classed('target')) {
54856                         var choice = geoChooseEdge(context.graph().childNodes(entity), loc, context.projection);
54857                         var prev = entity.nodes[choice.index - 1];
54858                         var next = entity.nodes[choice.index];
54859
54860                         context.perform(
54861                             actionAddMidpoint({ loc: choice.loc, edge: [prev, next] }, osmNode()),
54862                             _t('operations.add.annotation.vertex')
54863                         );
54864
54865                     } else if (entity.type === 'midpoint') {
54866                         context.perform(
54867                             actionAddMidpoint({ loc: entity.loc, edge: entity.edge }, osmNode()),
54868                             _t('operations.add.annotation.vertex'));
54869                     }
54870                 }
54871
54872
54873                 function selectElements() {
54874                     if (!checkSelectedIDs()) { return; }
54875
54876                     var surface = context.surface();
54877
54878                     surface.selectAll('.selected-member')
54879                         .classed('selected-member', false);
54880
54881                     surface.selectAll('.selected')
54882                         .classed('selected', false);
54883
54884                     surface.selectAll('.related')
54885                         .classed('related', false);
54886
54887                     singularParent();
54888                     if (_relatedParent) {
54889                         surface.selectAll(utilEntitySelector([_relatedParent]))
54890                             .classed('related', true);
54891                     }
54892
54893                     if (context.map().withinEditableZoom()) {
54894                         // Apply selection styling if not in wide selection
54895
54896                         surface
54897                             .selectAll(utilDeepMemberSelector(selectedIDs, context.graph(), true /* skipMultipolgonMembers */))
54898                             .classed('selected-member', true);
54899                         surface
54900                             .selectAll(utilEntityOrDeepMemberSelector(selectedIDs, context.graph()))
54901                             .classed('selected', true);
54902                     }
54903
54904                 }
54905
54906
54907                 function esc() {
54908                     if (context.container().select('.combobox').size()) { return; }
54909                     context.enter(modeBrowse(context));
54910                 }
54911
54912
54913                 function firstVertex() {
54914                     event.preventDefault();
54915                     var entity = singular();
54916                     var parent = singularParent();
54917                     var way;
54918
54919                     if (entity && entity.type === 'way') {
54920                         way = entity;
54921                     } else if (parent) {
54922                         way = context.entity(parent);
54923                     }
54924
54925                     if (way) {
54926                         context.enter(
54927                             modeSelect(context, [way.first()]).follow(true)
54928                         );
54929                     }
54930                 }
54931
54932
54933                 function lastVertex() {
54934                     event.preventDefault();
54935                     var entity = singular();
54936                     var parent = singularParent();
54937                     var way;
54938
54939                     if (entity && entity.type === 'way') {
54940                         way = entity;
54941                     } else if (parent) {
54942                         way = context.entity(parent);
54943                     }
54944
54945                     if (way) {
54946                         context.enter(
54947                             modeSelect(context, [way.last()]).follow(true)
54948                         );
54949                     }
54950                 }
54951
54952
54953                 function previousVertex() {
54954                     event.preventDefault();
54955                     var parent = singularParent();
54956                     if (!parent) { return; }
54957
54958                     var way = context.entity(parent);
54959                     var length = way.nodes.length;
54960                     var curr = way.nodes.indexOf(selectedIDs[0]);
54961                     var index = -1;
54962
54963                     if (curr > 0) {
54964                         index = curr - 1;
54965                     } else if (way.isClosed()) {
54966                         index = length - 2;
54967                     }
54968
54969                     if (index !== -1) {
54970                         context.enter(
54971                             modeSelect(context, [way.nodes[index]]).follow(true)
54972                         );
54973                     }
54974                 }
54975
54976
54977                 function nextVertex() {
54978                     event.preventDefault();
54979                     var parent = singularParent();
54980                     if (!parent) { return; }
54981
54982                     var way = context.entity(parent);
54983                     var length = way.nodes.length;
54984                     var curr = way.nodes.indexOf(selectedIDs[0]);
54985                     var index = -1;
54986
54987                     if (curr < length - 1) {
54988                         index = curr + 1;
54989                     } else if (way.isClosed()) {
54990                         index = 0;
54991                     }
54992
54993                     if (index !== -1) {
54994                         context.enter(
54995                             modeSelect(context, [way.nodes[index]]).follow(true)
54996                         );
54997                     }
54998                 }
54999
55000
55001                 function nextParent() {
55002                     event.preventDefault();
55003                     var parents = commonParents();
55004                     if (!parents || parents.length < 2) { return; }
55005
55006                     var index = parents.indexOf(_relatedParent);
55007                     if (index < 0 || index > parents.length - 2) {
55008                         _relatedParent = parents[0];
55009                     } else {
55010                         _relatedParent = parents[index + 1];
55011                     }
55012
55013                     var surface = context.surface();
55014                     surface.selectAll('.related')
55015                         .classed('related', false);
55016
55017                     if (_relatedParent) {
55018                         surface.selectAll(utilEntitySelector([_relatedParent]))
55019                             .classed('related', true);
55020                     }
55021                 }
55022             };
55023
55024
55025             mode.exit = function() {
55026
55027                 _newFeature = false;
55028
55029                 _operations.forEach(function(operation) {
55030                     if (operation.behavior) {
55031                         context.uninstall(operation.behavior);
55032                     }
55033                 });
55034                 _operations = [];
55035
55036                 _behaviors.forEach(context.uninstall);
55037
55038                 select(document)
55039                     .call(keybinding.unbind);
55040
55041                 context.ui().closeEditMenu();
55042
55043                 context.history()
55044                     .on('change.select', null)
55045                     .on('undone.select', null)
55046                     .on('redone.select', null);
55047
55048                 var surface = context.surface();
55049
55050                 surface
55051                     .selectAll('.selected-member')
55052                     .classed('selected-member', false);
55053
55054                 surface
55055                     .selectAll('.selected')
55056                     .classed('selected', false);
55057
55058                 surface
55059                     .selectAll('.highlighted')
55060                     .classed('highlighted', false);
55061
55062                 surface
55063                     .selectAll('.related')
55064                     .classed('related', false);
55065
55066                 context.map().on('drawn.select', null);
55067                 context.ui().sidebar.hide();
55068                 context.features().forceVisible([]);
55069
55070                 var entity = singular();
55071                 if (_newFeature && entity && entity.type === 'relation' &&
55072                     // no tags
55073                     Object.keys(entity.tags).length === 0 &&
55074                     // no parent relations
55075                     context.graph().parentRelations(entity).length === 0 &&
55076                     // no members or one member with no role
55077                     (entity.members.length === 0 || (entity.members.length === 1 && !entity.members[0].role))
55078                 ) {
55079                     // the user added this relation but didn't edit it at all, so just delete it
55080                     var deleteAction = actionDeleteRelation(entity.id, true /* don't delete untagged members */);
55081                     context.perform(deleteAction, _t('operations.delete.annotation.relation'));
55082                 }
55083             };
55084
55085
55086             return mode;
55087         }
55088
55089         function uiLasso(context) {
55090             var group, polygon;
55091
55092             lasso.coordinates = [];
55093
55094             function lasso(selection) {
55095                 context.container()
55096                     .classed('lasso', true);
55097
55098                 group = selection
55099                     .append('g')
55100                     .attr('class', 'lasso hide');
55101
55102                 polygon = group
55103                     .append('path')
55104                     .attr('class', 'lasso-path');
55105
55106                 group
55107                     .call(uiToggle(true));
55108             }
55109
55110
55111             function draw() {
55112                 if (polygon) {
55113                     polygon.data([lasso.coordinates])
55114                         .attr('d', function(d) { return 'M' + d.join(' L') + ' Z'; });
55115                 }
55116             }
55117
55118
55119             lasso.extent = function () {
55120                 return lasso.coordinates.reduce(function(extent, point) {
55121                     return extent.extend(geoExtent(point));
55122                 }, geoExtent());
55123             };
55124
55125
55126             lasso.p = function(_) {
55127                 if (!arguments.length) { return lasso; }
55128                 lasso.coordinates.push(_);
55129                 draw();
55130                 return lasso;
55131             };
55132
55133
55134             lasso.close = function() {
55135                 if (group) {
55136                     group.call(uiToggle(false, function() {
55137                         select(this).remove();
55138                     }));
55139                 }
55140                 context.container().classed('lasso', false);
55141             };
55142
55143
55144             return lasso;
55145         }
55146
55147         function behaviorLasso(context) {
55148
55149             // use pointer events on supported platforms; fallback to mouse events
55150             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
55151
55152             var behavior = function(selection) {
55153                 var lasso;
55154
55155
55156                 function pointerdown() {
55157                     var button = 0;  // left
55158                     if (event.button === button && event.shiftKey === true) {
55159                         lasso = null;
55160
55161                         select(window)
55162                             .on(_pointerPrefix + 'move.lasso', pointermove)
55163                             .on(_pointerPrefix + 'up.lasso', pointerup);
55164
55165                         event.stopPropagation();
55166                     }
55167                 }
55168
55169
55170                 function pointermove() {
55171                     if (!lasso) {
55172                         lasso = uiLasso(context);
55173                         context.surface().call(lasso);
55174                     }
55175
55176                     lasso.p(context.map().mouse());
55177                 }
55178
55179
55180                 function normalize(a, b) {
55181                     return [
55182                         [Math.min(a[0], b[0]), Math.min(a[1], b[1])],
55183                         [Math.max(a[0], b[0]), Math.max(a[1], b[1])]
55184                     ];
55185                 }
55186
55187
55188                 function lassoed() {
55189                     if (!lasso) { return []; }
55190
55191                     var graph = context.graph();
55192                     var limitToNodes;
55193
55194                     if (context.map().editableDataEnabled(true /* skipZoomCheck */) && context.map().isInWideSelection()) {
55195                         // only select from the visible nodes
55196                         limitToNodes = new Set(utilGetAllNodes(context.selectedIDs(), graph));
55197                     } else if (!context.map().editableDataEnabled()) {
55198                         return [];
55199                     }
55200
55201                     var bounds = lasso.extent().map(context.projection.invert);
55202                     var extent = geoExtent(normalize(bounds[0], bounds[1]));
55203
55204                     var intersects = context.history().intersects(extent).filter(function(entity) {
55205                         return entity.type === 'node' &&
55206                             (!limitToNodes || limitToNodes.has(entity)) &&
55207                             geoPointInPolygon(context.projection(entity.loc), lasso.coordinates) &&
55208                             !context.features().isHidden(entity, graph, entity.geometry(graph));
55209                     });
55210
55211                     // sort the lassoed nodes as best we can
55212                     intersects.sort(function(node1, node2) {
55213                         var parents1 = graph.parentWays(node1);
55214                         var parents2 = graph.parentWays(node2);
55215                         if (parents1.length && parents2.length) {
55216                             // both nodes are vertices
55217
55218                             var sharedParents = utilArrayIntersection(parents1, parents2);
55219                             if (sharedParents.length) {
55220                                 var sharedParentNodes = sharedParents[0].nodes;
55221                                 // vertices are members of the same way; sort them in their listed order
55222                                 return sharedParentNodes.indexOf(node1.id) -
55223                                     sharedParentNodes.indexOf(node2.id);
55224                             } else {
55225                                 // vertices do not share a way; group them by their respective parent ways
55226                                 return parseFloat(parents1[0].id.slice(1)) -
55227                                     parseFloat(parents2[0].id.slice(1));
55228                             }
55229
55230                         } else if (parents1.length || parents2.length) {
55231                             // only one node is a vertex; sort standalone points before vertices
55232                             return parents1.length - parents2.length;
55233                         }
55234                         // both nodes are standalone points; sort left to right
55235                         return node1.loc[0] - node2.loc[0];
55236                     });
55237
55238                     return intersects.map(function(entity) { return entity.id; });
55239                 }
55240
55241
55242                 function pointerup() {
55243                     select(window)
55244                         .on(_pointerPrefix + 'move.lasso', null)
55245                         .on(_pointerPrefix + 'up.lasso', null);
55246
55247                     if (!lasso) { return; }
55248
55249                     var ids = lassoed();
55250                     lasso.close();
55251
55252                     if (ids.length) {
55253                         context.enter(modeSelect(context, ids));
55254                     }
55255                 }
55256
55257                 selection
55258                     .on(_pointerPrefix + 'down.lasso', pointerdown);
55259             };
55260
55261
55262             behavior.off = function(selection) {
55263                 selection.on(_pointerPrefix + 'down.lasso', null);
55264             };
55265
55266
55267             return behavior;
55268         }
55269
55270         function modeBrowse(context) {
55271             var mode = {
55272                 button: 'browse',
55273                 id: 'browse',
55274                 title: _t('modes.browse.title'),
55275                 description: _t('modes.browse.description')
55276             };
55277             var sidebar;
55278
55279             var _selectBehavior;
55280             var _behaviors = [];
55281
55282
55283             mode.selectBehavior = function(val) {
55284                 if (!arguments.length) { return _selectBehavior; }
55285                 _selectBehavior = val;
55286                 return mode;
55287             };
55288
55289
55290             mode.enter = function() {
55291                 if (!_behaviors.length) {
55292                     if (!_selectBehavior) { _selectBehavior = behaviorSelect(context); }
55293                     _behaviors = [
55294                         behaviorPaste(context),
55295                         behaviorHover(context).on('hover', context.ui().sidebar.hover),
55296                         _selectBehavior,
55297                         behaviorLasso(context),
55298                         modeDragNode(context).behavior,
55299                         modeDragNote(context).behavior
55300                     ];
55301                 }
55302                 _behaviors.forEach(context.install);
55303
55304                 // Get focus on the body.
55305                 if (document.activeElement && document.activeElement.blur) {
55306                     document.activeElement.blur();
55307                 }
55308
55309                 if (sidebar) {
55310                     context.ui().sidebar.show(sidebar);
55311                 } else {
55312                     context.ui().sidebar.select(null);
55313                 }
55314             };
55315
55316
55317             mode.exit = function() {
55318                 context.ui().sidebar.hover.cancel();
55319                 _behaviors.forEach(context.uninstall);
55320
55321                 if (sidebar) {
55322                     context.ui().sidebar.hide();
55323                 }
55324             };
55325
55326
55327             mode.sidebar = function(_) {
55328                 if (!arguments.length) { return sidebar; }
55329                 sidebar = _;
55330                 return mode;
55331             };
55332
55333
55334             mode.operations = function() {
55335                 return [operationPaste(context)];
55336             };
55337
55338
55339             return mode;
55340         }
55341
55342         function behaviorAddWay(context) {
55343             var dispatch$1 = dispatch('start', 'startFromWay', 'startFromNode');
55344             var draw = behaviorDraw(context);
55345
55346             function behavior(surface) {
55347                 draw.on('click', function() { dispatch$1.apply('start', this, arguments); })
55348                     .on('clickWay', function() { dispatch$1.apply('startFromWay', this, arguments); })
55349                     .on('clickNode', function() { dispatch$1.apply('startFromNode', this, arguments); })
55350                     .on('cancel', behavior.cancel)
55351                     .on('finish', behavior.cancel);
55352
55353                 context.map()
55354                     .dblclickZoomEnable(false);
55355
55356                 surface.call(draw);
55357             }
55358
55359
55360             behavior.off = function(surface) {
55361                 surface.call(draw.off);
55362             };
55363
55364
55365             behavior.cancel = function() {
55366                 window.setTimeout(function() {
55367                     context.map().dblclickZoomEnable(true);
55368                 }, 1000);
55369
55370                 context.enter(modeBrowse(context));
55371             };
55372
55373
55374             return utilRebind(behavior, dispatch$1, 'on');
55375         }
55376
55377         function behaviorHash(context) {
55378
55379             // cached window.location.hash
55380             var _cachedHash = null;
55381             // allowable latitude range
55382             var _latitudeLimit = 90 - 1e-8;
55383
55384             function computedHashParameters() {
55385                 var map = context.map();
55386                 var center = map.center();
55387                 var zoom = map.zoom();
55388                 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
55389                 var oldParams = utilObjectOmit(utilStringQs(window.location.hash),
55390                     ['comment', 'source', 'hashtags', 'walkthrough']
55391                 );
55392                 var newParams = {};
55393
55394                 delete oldParams.id;
55395                 var selected = context.selectedIDs().filter(function(id) {
55396                     return context.hasEntity(id);
55397                 });
55398                 if (selected.length) {
55399                     newParams.id = selected.join(',');
55400                 }
55401
55402                 newParams.map = zoom.toFixed(2) +
55403                     '/' + center[1].toFixed(precision) +
55404                     '/' + center[0].toFixed(precision);
55405
55406                 return Object.assign(oldParams, newParams);
55407             }
55408
55409             function computedHash() {
55410                 return '#' + utilQsString(computedHashParameters(), true);
55411             }
55412
55413             function computedTitle(includeChangeCount) {
55414
55415                 var baseTitle = context.documentTitleBase() || 'iD';
55416                 var contextual;
55417                 var changeCount;
55418                 var titleID;
55419
55420                 var selected = context.selectedIDs().filter(function(id) {
55421                     return context.hasEntity(id);
55422                 });
55423                 if (selected.length) {
55424                     var firstLabel = utilDisplayLabel(context.entity(selected[0]), context.graph());
55425                     if (selected.length > 1 ) {
55426                         contextual = _t('title.labeled_and_more', {
55427                             labeled: firstLabel,
55428                             count: (selected.length - 1).toString()
55429                         });
55430                     } else {
55431                         contextual = firstLabel;
55432                     }
55433                     titleID = 'context';
55434                 }
55435
55436                 if (includeChangeCount) {
55437                     changeCount = context.history().difference().summary().length;
55438                     if (changeCount > 0) {
55439                         titleID = contextual ? 'changes_context' : 'changes';
55440                     }
55441                 }
55442
55443                 if (titleID) {
55444                     return _t('title.format.' + titleID, {
55445                         changes: changeCount,
55446                         base: baseTitle,
55447                         context: contextual
55448                     });
55449                 }
55450
55451                 return baseTitle;
55452             }
55453
55454             function updateTitle(includeChangeCount) {
55455                 if (!context.setsDocumentTitle()) { return; }
55456
55457                 var newTitle = computedTitle(includeChangeCount);
55458                 if (document.title !== newTitle) {
55459                     document.title = newTitle;
55460                 }
55461             }
55462
55463             function updateHashIfNeeded() {
55464                 if (context.inIntro()) { return; }
55465
55466                 var latestHash = computedHash();
55467                 if (_cachedHash !== latestHash) {
55468                     _cachedHash = latestHash;
55469
55470                     // Update the URL hash without affecting the browser navigation stack,
55471                     // though unavoidably creating a browser history entry
55472                     window.history.replaceState(null, computedTitle(false /* includeChangeCount */), latestHash);
55473
55474                     // set the title we want displayed for the browser tab/window
55475                     updateTitle(true /* includeChangeCount */);
55476                 }
55477             }
55478
55479             var _throttledUpdate = throttle(updateHashIfNeeded, 500);
55480             var _throttledUpdateTitle = throttle(function() {
55481                 updateTitle(true /* includeChangeCount */);
55482             }, 500);
55483
55484             function hashchange() {
55485
55486                 // ignore spurious hashchange events
55487                 if (window.location.hash === _cachedHash) { return; }
55488
55489                 _cachedHash = window.location.hash;
55490
55491                 var q = utilStringQs(_cachedHash);
55492                 var mapArgs = (q.map || '').split('/').map(Number);
55493
55494                 if (mapArgs.length < 3 || mapArgs.some(isNaN)) {
55495                     // replace bogus hash
55496                     updateHashIfNeeded();
55497
55498                 } else {
55499                     // don't update if the new hash already reflects the state of iD
55500                     if (_cachedHash === computedHash()) { return; }
55501
55502                     var mode = context.mode();
55503
55504                     context.map().centerZoom([mapArgs[2], Math.min(_latitudeLimit, Math.max(-_latitudeLimit, mapArgs[1]))], mapArgs[0]);
55505
55506                     if (q.id) {
55507                         var ids = q.id.split(',').filter(function(id) {
55508                             return context.hasEntity(id);
55509                         });
55510                         var skip = mode && mode.id === 'select' && utilArrayIdentical(mode.selectedIDs(), ids);
55511                         if (ids.length && !skip) {
55512                             context.enter(modeSelect(context, ids));
55513                             return;
55514                         }
55515                     }
55516
55517                     var center = context.map().center();
55518                     var dist = geoSphericalDistance(center, [mapArgs[2], mapArgs[1]]);
55519                     var maxdist = 500;
55520
55521                     // Don't allow the hash location to change too much while drawing
55522                     // This can happen if the user accidently hit the back button.  #3996
55523                     if (mode && mode.id.match(/^draw/) !== null && dist > maxdist) {
55524                         context.enter(modeBrowse(context));
55525                         return;
55526                     }
55527                 }
55528             }
55529
55530             function behavior() {
55531                 context.map()
55532                     .on('move.behaviorHash', _throttledUpdate);
55533
55534                 context.history()
55535                     .on('change.behaviorHash', _throttledUpdateTitle);
55536
55537                 context
55538                     .on('enter.behaviorHash', _throttledUpdate);
55539
55540                 select(window)
55541                     .on('hashchange.behaviorHash', hashchange);
55542
55543                 if (window.location.hash) {
55544                     var q = utilStringQs(window.location.hash);
55545
55546                     if (q.id) {
55547                         //if (!context.history().hasRestorableChanges()) {
55548                             // targeting specific features: download, select, and zoom to them
55549                             context.zoomToEntity(q.id.split(',')[0], !q.map);
55550                         //}
55551                     }
55552
55553                     if (q.walkthrough === 'true') {
55554                         behavior.startWalkthrough = true;
55555                     }
55556
55557                     if (q.map) {
55558                         behavior.hadHash = true;
55559                     }
55560
55561                     hashchange();
55562
55563                     updateTitle(false);
55564                 }
55565             }
55566
55567             behavior.off = function() {
55568                 _throttledUpdate.cancel();
55569                 _throttledUpdateTitle.cancel();
55570
55571                 context.map()
55572                     .on('move.behaviorHash', null);
55573
55574                 context
55575                     .on('enter.behaviorHash', null);
55576
55577                 select(window)
55578                     .on('hashchange.behaviorHash', null);
55579
55580                 window.location.hash = '';
55581             };
55582
55583             return behavior;
55584         }
55585
55586         /*
55587             iD.coreDifference represents the difference between two graphs.
55588             It knows how to calculate the set of entities that were
55589             created, modified, or deleted, and also contains the logic
55590             for recursively extending a difference to the complete set
55591             of entities that will require a redraw, taking into account
55592             child and parent relationships.
55593          */
55594         function coreDifference(base, head) {
55595             var _changes = {};
55596             var _didChange = {};  // 'addition', 'deletion', 'geometry', 'properties'
55597             var _diff = {};
55598
55599             function checkEntityID(id) {
55600                 var h = head.entities[id];
55601                 var b = base.entities[id];
55602
55603                 if (h === b) { return; }
55604                 if (_changes[id]) { return; }
55605
55606                 if (!h && b) {
55607                     _changes[id] = { base: b, head: h };
55608                     _didChange.deletion = true;
55609                     return;
55610                 }
55611                 if (h && !b) {
55612                     _changes[id] = { base: b, head: h };
55613                     _didChange.addition = true;
55614                     return;
55615                 }
55616
55617                 if (h && b) {
55618                     if (h.members && b.members && !fastDeepEqual(h.members, b.members)) {
55619                         _changes[id] = { base: b, head: h };
55620                         _didChange.geometry = true;
55621                         _didChange.properties = true;
55622                         return;
55623                     }
55624                     if (h.loc && b.loc && !geoVecEqual(h.loc, b.loc)) {
55625                         _changes[id] = { base: b, head: h };
55626                         _didChange.geometry = true;
55627                     }
55628                     if (h.nodes && b.nodes && !fastDeepEqual(h.nodes, b.nodes)) {
55629                         _changes[id] = { base: b, head: h };
55630                         _didChange.geometry = true;
55631                     }
55632                     if (h.tags && b.tags && !fastDeepEqual(h.tags, b.tags)) {
55633                         _changes[id] = { base: b, head: h };
55634                         _didChange.properties = true;
55635                     }
55636                 }
55637             }
55638
55639             function load() {
55640                 // HOT CODE: there can be many thousands of downloaded entities, so looping
55641                 // through them all can become a performance bottleneck. Optimize by
55642                 // resolving duplicates and using a basic `for` loop
55643                 var ids = utilArrayUniq(Object.keys(head.entities).concat(Object.keys(base.entities)));
55644                 for (var i = 0; i < ids.length; i++) {
55645                     checkEntityID(ids[i]);
55646                 }
55647             }
55648             load();
55649
55650
55651             _diff.length = function length() {
55652                 return Object.keys(_changes).length;
55653             };
55654
55655
55656             _diff.changes = function changes() {
55657                 return _changes;
55658             };
55659
55660             _diff.didChange = _didChange;
55661
55662
55663             // pass true to include affected relation members
55664             _diff.extantIDs = function extantIDs(includeRelMembers) {
55665                 var result = new Set();
55666                 Object.keys(_changes).forEach(function(id) {
55667                     if (_changes[id].head) {
55668                         result.add(id);
55669                     }
55670
55671                     var h = _changes[id].head;
55672                     var b = _changes[id].base;
55673                     var entity = h || b;
55674
55675                     if (includeRelMembers && entity.type === 'relation') {
55676                         var mh = h ? h.members.map(function(m) { return m.id; }) : [];
55677                         var mb = b ? b.members.map(function(m) { return m.id; }) : [];
55678                         utilArrayUnion(mh, mb).forEach(function(memberID) {
55679                             if (head.hasEntity(memberID)) {
55680                                 result.add(memberID);
55681                             }
55682                         });
55683                     }
55684                 });
55685
55686                 return Array.from(result);
55687             };
55688
55689
55690             _diff.modified = function modified() {
55691                 var result = [];
55692                 Object.values(_changes).forEach(function(change) {
55693                     if (change.base && change.head) {
55694                         result.push(change.head);
55695                     }
55696                 });
55697                 return result;
55698             };
55699
55700
55701             _diff.created = function created() {
55702                 var result = [];
55703                 Object.values(_changes).forEach(function(change) {
55704                     if (!change.base && change.head) {
55705                         result.push(change.head);
55706                     }
55707                 });
55708                 return result;
55709             };
55710
55711
55712             _diff.deleted = function deleted() {
55713                 var result = [];
55714                 Object.values(_changes).forEach(function(change) {
55715                     if (change.base && !change.head) {
55716                         result.push(change.base);
55717                     }
55718                 });
55719                 return result;
55720             };
55721
55722
55723             _diff.summary = function summary() {
55724                 var relevant = {};
55725
55726                 var keys = Object.keys(_changes);
55727                 for (var i = 0; i < keys.length; i++) {
55728                     var change = _changes[keys[i]];
55729
55730                     if (change.head && change.head.geometry(head) !== 'vertex') {
55731                         addEntity(change.head, head, change.base ? 'modified' : 'created');
55732
55733                     } else if (change.base && change.base.geometry(base) !== 'vertex') {
55734                         addEntity(change.base, base, 'deleted');
55735
55736                     } else if (change.base && change.head) { // modified vertex
55737                         var moved    = !fastDeepEqual(change.base.loc,  change.head.loc);
55738                         var retagged = !fastDeepEqual(change.base.tags, change.head.tags);
55739
55740                         if (moved) {
55741                             addParents(change.head);
55742                         }
55743
55744                         if (retagged || (moved && change.head.hasInterestingTags())) {
55745                             addEntity(change.head, head, 'modified');
55746                         }
55747
55748                     } else if (change.head && change.head.hasInterestingTags()) { // created vertex
55749                         addEntity(change.head, head, 'created');
55750
55751                     } else if (change.base && change.base.hasInterestingTags()) { // deleted vertex
55752                         addEntity(change.base, base, 'deleted');
55753                     }
55754                 }
55755
55756                 return Object.values(relevant);
55757
55758
55759                 function addEntity(entity, graph, changeType) {
55760                     relevant[entity.id] = {
55761                         entity: entity,
55762                         graph: graph,
55763                         changeType: changeType
55764                     };
55765                 }
55766
55767                 function addParents(entity) {
55768                     var parents = head.parentWays(entity);
55769                     for (var j = parents.length - 1; j >= 0; j--) {
55770                         var parent = parents[j];
55771                         if (!(parent.id in relevant)) {
55772                             addEntity(parent, head, 'modified');
55773                         }
55774                     }
55775                 }
55776             };
55777
55778
55779             // returns complete set of entities that require a redraw
55780             //  (optionally within given `extent`)
55781             _diff.complete = function complete(extent) {
55782                 var result = {};
55783                 var id, change;
55784
55785                 for (id in _changes) {
55786                     change = _changes[id];
55787
55788                     var h = change.head;
55789                     var b = change.base;
55790                     var entity = h || b;
55791                     var i;
55792
55793                     if (extent &&
55794                         (!h || !h.intersects(extent, head)) &&
55795                         (!b || !b.intersects(extent, base)))
55796                         { continue; }
55797
55798                     result[id] = h;
55799
55800                     if (entity.type === 'way') {
55801                         var nh = h ? h.nodes : [];
55802                         var nb = b ? b.nodes : [];
55803                         var diff;
55804
55805                         diff = utilArrayDifference(nh, nb);
55806                         for (i = 0; i < diff.length; i++) {
55807                             result[diff[i]] = head.hasEntity(diff[i]);
55808                         }
55809
55810                         diff = utilArrayDifference(nb, nh);
55811                         for (i = 0; i < diff.length; i++) {
55812                             result[diff[i]] = head.hasEntity(diff[i]);
55813                         }
55814                     }
55815
55816                     if (entity.type === 'relation' && entity.isMultipolygon()) {
55817                         var mh = h ? h.members.map(function(m) { return m.id; }) : [];
55818                         var mb = b ? b.members.map(function(m) { return m.id; }) : [];
55819                         var ids = utilArrayUnion(mh, mb);
55820                         for (i = 0; i < ids.length; i++) {
55821                             var member = head.hasEntity(ids[i]);
55822                             if (!member) { continue; }   // not downloaded
55823                             if (extent && !member.intersects(extent, head)) { continue; }   // not visible
55824                             result[ids[i]] = member;
55825                         }
55826                     }
55827
55828                     addParents(head.parentWays(entity), result);
55829                     addParents(head.parentRelations(entity), result);
55830                 }
55831
55832                 return result;
55833
55834
55835                 function addParents(parents, result) {
55836                     for (var i = 0; i < parents.length; i++) {
55837                         var parent = parents[i];
55838                         if (parent.id in result) { continue; }
55839
55840                         result[parent.id] = parent;
55841                         addParents(head.parentRelations(parent), result);
55842                     }
55843                 }
55844             };
55845
55846
55847             return _diff;
55848         }
55849
55850         function coreTree(head) {
55851             // tree for entities
55852             var _rtree = new RBush();
55853             var _bboxes = {};
55854
55855             // maintain a separate tree for granular way segments
55856             var _segmentsRTree = new RBush();
55857             var _segmentsBBoxes = {};
55858             var _segmentsByWayId = {};
55859
55860             var tree = {};
55861
55862
55863             function entityBBox(entity) {
55864                 var bbox = entity.extent(head).bbox();
55865                 bbox.id = entity.id;
55866                 _bboxes[entity.id] = bbox;
55867                 return bbox;
55868             }
55869
55870
55871             function segmentBBox(segment) {
55872                 var extent = segment.extent(head);
55873                 // extent can be null if the node entites aren't in the graph for some reason
55874                 if (!extent) { return null; }
55875
55876                 var bbox = extent.bbox();
55877                 bbox.segment = segment;
55878                 _segmentsBBoxes[segment.id] = bbox;
55879                 return bbox;
55880             }
55881
55882
55883             function removeEntity(entity) {
55884                 _rtree.remove(_bboxes[entity.id]);
55885                 delete _bboxes[entity.id];
55886
55887                 if (_segmentsByWayId[entity.id]) {
55888                     _segmentsByWayId[entity.id].forEach(function(segment) {
55889                         _segmentsRTree.remove(_segmentsBBoxes[segment.id]);
55890                         delete _segmentsBBoxes[segment.id];
55891                     });
55892                     delete _segmentsByWayId[entity.id];
55893                 }
55894             }
55895
55896
55897             function loadEntities(entities) {
55898                 _rtree.load(entities.map(entityBBox));
55899
55900                 var segments = [];
55901                 entities.forEach(function(entity) {
55902                     if (entity.segments) {
55903                         var entitySegments = entity.segments(head);
55904                         // cache these to make them easy to remove later
55905                         _segmentsByWayId[entity.id] = entitySegments;
55906                         segments = segments.concat(entitySegments);
55907                     }
55908                 });
55909                 if (segments.length) { _segmentsRTree.load(segments.map(segmentBBox).filter(Boolean)); }
55910             }
55911
55912
55913             function updateParents(entity, insertions, memo) {
55914                 head.parentWays(entity).forEach(function(way) {
55915                     if (_bboxes[way.id]) {
55916                         removeEntity(way);
55917                         insertions[way.id] = way;
55918                     }
55919                     updateParents(way, insertions, memo);
55920                 });
55921
55922                 head.parentRelations(entity).forEach(function(relation) {
55923                     if (memo[entity.id]) { return; }
55924                     memo[entity.id] = true;
55925                     if (_bboxes[relation.id]) {
55926                         removeEntity(relation);
55927                         insertions[relation.id] = relation;
55928                     }
55929                     updateParents(relation, insertions, memo);
55930                 });
55931             }
55932
55933
55934             tree.rebase = function(entities, force) {
55935                 var insertions = {};
55936
55937                 for (var i = 0; i < entities.length; i++) {
55938                     var entity = entities[i];
55939                     if (!entity.visible) { continue; }
55940
55941                     if (head.entities.hasOwnProperty(entity.id) || _bboxes[entity.id]) {
55942                         if (!force) {
55943                             continue;
55944                         } else if (_bboxes[entity.id]) {
55945                             removeEntity(entity);
55946                         }
55947                     }
55948
55949                     insertions[entity.id] = entity;
55950                     updateParents(entity, insertions, {});
55951                 }
55952
55953                 loadEntities(Object.values(insertions));
55954
55955                 return tree;
55956             };
55957
55958
55959             function updateToGraph(graph) {
55960                 if (graph === head) { return; }
55961
55962                 var diff = coreDifference(head, graph);
55963
55964                 head = graph;
55965
55966                 var changed = diff.didChange;
55967                 if (!changed.addition && !changed.deletion && !changed.geometry) { return; }
55968
55969                 var insertions = {};
55970
55971                 if (changed.deletion) {
55972                     diff.deleted().forEach(function(entity) {
55973                         removeEntity(entity);
55974                     });
55975                 }
55976
55977                 if (changed.geometry) {
55978                     diff.modified().forEach(function(entity) {
55979                         removeEntity(entity);
55980                         insertions[entity.id] = entity;
55981                         updateParents(entity, insertions, {});
55982                     });
55983                 }
55984
55985                 if (changed.addition) {
55986                     diff.created().forEach(function(entity) {
55987                         insertions[entity.id] = entity;
55988                     });
55989                 }
55990
55991                 loadEntities(Object.values(insertions));
55992             }
55993
55994             // returns an array of entities with bounding boxes overlapping `extent` for the given `graph`
55995             tree.intersects = function(extent, graph) {
55996                 updateToGraph(graph);
55997                 return _rtree.search(extent.bbox())
55998                     .map(function(bbox) { return graph.entity(bbox.id); });
55999             };
56000
56001             // returns an array of segment objects with bounding boxes overlapping `extent` for the given `graph`
56002             tree.waySegments = function(extent, graph) {
56003                 updateToGraph(graph);
56004                 return _segmentsRTree.search(extent.bbox())
56005                     .map(function(bbox) { return bbox.segment; });
56006             };
56007
56008
56009             return tree;
56010         }
56011
56012         function uiModal(selection, blocking) {
56013           var this$1 = this;
56014
56015           var keybinding = utilKeybinding('modal');
56016           var previous = selection.select('div.modal');
56017           var animate = previous.empty();
56018
56019           previous.transition()
56020             .duration(200)
56021             .style('opacity', 0)
56022             .remove();
56023
56024           var shaded = selection
56025             .append('div')
56026             .attr('class', 'shaded')
56027             .style('opacity', 0);
56028
56029           shaded.close = function () {
56030             shaded
56031               .transition()
56032               .duration(200)
56033               .style('opacity',0)
56034               .remove();
56035
56036             modal
56037               .transition()
56038               .duration(200)
56039               .style('top','0px');
56040
56041             select(document)
56042               .call(keybinding.unbind);
56043           };
56044
56045
56046           var modal = shaded
56047             .append('div')
56048             .attr('class', 'modal fillL');
56049
56050           if (!blocking) {
56051             shaded.on('click.remove-modal', function () {
56052               if (event.target === this$1) {
56053                 shaded.close();
56054               }
56055             });
56056
56057             modal
56058               .append('button')
56059               .attr('class', 'close')
56060               .on('click', shaded.close)
56061               .call(svgIcon('#iD-icon-close'));
56062
56063             keybinding
56064               .on('⌫', shaded.close)
56065               .on('⎋', shaded.close);
56066
56067             select(document)
56068               .call(keybinding);
56069           }
56070
56071           modal
56072             .append('div')
56073             .attr('class', 'content');
56074
56075           if (animate) {
56076             shaded.transition().style('opacity', 1);
56077           } else {
56078             shaded.style('opacity', 1);
56079           }
56080
56081           return shaded;
56082         }
56083
56084         function uiLoading(context) {
56085           var _modalSelection = select(null);
56086           var _message = '';
56087           var _blocking = false;
56088
56089
56090           var loading = function (selection) {
56091             _modalSelection = uiModal(selection, _blocking);
56092
56093             var loadertext = _modalSelection.select('.content')
56094               .classed('loading-modal', true)
56095               .append('div')
56096               .attr('class', 'modal-section fillL');
56097
56098             loadertext
56099               .append('img')
56100               .attr('class', 'loader')
56101               .attr('src', context.imagePath('loader-white.gif'));
56102
56103             loadertext
56104               .append('h3')
56105               .text(_message);
56106
56107             _modalSelection.select('button.close')
56108               .attr('class', 'hide');
56109
56110             return loading;
56111           };
56112
56113
56114           loading.message = function(val) {
56115             if (!arguments.length) { return _message; }
56116             _message = val;
56117             return loading;
56118           };
56119
56120
56121           loading.blocking = function(val) {
56122             if (!arguments.length) { return _blocking; }
56123             _blocking = val;
56124             return loading;
56125           };
56126
56127
56128           loading.close = function () {
56129             _modalSelection.remove();
56130           };
56131
56132
56133           loading.isShown = function () {
56134             return _modalSelection && !_modalSelection.empty() && _modalSelection.node().parentNode;
56135           };
56136
56137
56138           return loading;
56139         }
56140
56141         function coreHistory(context) {
56142             var dispatch$1 = dispatch('change', 'merge', 'restore', 'undone', 'redone');
56143             var lock = utilSessionMutex('lock');
56144
56145             // restorable if iD not open in another window/tab and a saved history exists in localStorage
56146             var _hasUnresolvedRestorableChanges = lock.lock() && !!corePreferences(getKey('saved_history'));
56147
56148             var duration = 150;
56149             var _imageryUsed = [];
56150             var _photoOverlaysUsed = [];
56151             var _checkpoints = {};
56152             var _pausedGraph;
56153             var _stack;
56154             var _index;
56155             var _tree;
56156
56157
56158             // internal _act, accepts list of actions and eased time
56159             function _act(actions, t) {
56160                 actions = Array.prototype.slice.call(actions);
56161
56162                 var annotation;
56163                 if (typeof actions[actions.length - 1] !== 'function') {
56164                     annotation = actions.pop();
56165                 }
56166
56167                 var graph = _stack[_index].graph;
56168                 for (var i = 0; i < actions.length; i++) {
56169                     graph = actions[i](graph, t);
56170                 }
56171
56172                 return {
56173                     graph: graph,
56174                     annotation: annotation,
56175                     imageryUsed: _imageryUsed,
56176                     photoOverlaysUsed: _photoOverlaysUsed,
56177                     transform: context.projection.transform(),
56178                     selectedIDs: context.selectedIDs()
56179                 };
56180             }
56181
56182
56183             // internal _perform with eased time
56184             function _perform(args, t) {
56185                 var previous = _stack[_index].graph;
56186                 _stack = _stack.slice(0, _index + 1);
56187                 var actionResult = _act(args, t);
56188                 _stack.push(actionResult);
56189                 _index++;
56190                 return change(previous);
56191             }
56192
56193
56194             // internal _replace with eased time
56195             function _replace(args, t) {
56196                 var previous = _stack[_index].graph;
56197                 // assert(_index == _stack.length - 1)
56198                 var actionResult = _act(args, t);
56199                 _stack[_index] = actionResult;
56200                 return change(previous);
56201             }
56202
56203
56204             // internal _overwrite with eased time
56205             function _overwrite(args, t) {
56206                 var previous = _stack[_index].graph;
56207                 if (_index > 0) {
56208                     _index--;
56209                     _stack.pop();
56210                 }
56211                 _stack = _stack.slice(0, _index + 1);
56212                 var actionResult = _act(args, t);
56213                 _stack.push(actionResult);
56214                 _index++;
56215                 return change(previous);
56216             }
56217
56218
56219             // determine difference and dispatch a change event
56220             function change(previous) {
56221                 var difference = coreDifference(previous, history.graph());
56222                 if (!_pausedGraph) {
56223                     dispatch$1.call('change', this, difference);
56224                 }
56225                 return difference;
56226             }
56227
56228
56229             // iD uses namespaced keys so multiple installations do not conflict
56230             function getKey(n) {
56231                 return 'iD_' + window.location.origin + '_' + n;
56232             }
56233
56234
56235             var history = {
56236
56237                 graph: function() {
56238                     return _stack[_index].graph;
56239                 },
56240
56241
56242                 tree: function() {
56243                     return _tree;
56244                 },
56245
56246
56247                 base: function() {
56248                     return _stack[0].graph;
56249                 },
56250
56251
56252                 merge: function(entities/*, extent*/) {
56253                     var stack = _stack.map(function(state) { return state.graph; });
56254                     _stack[0].graph.rebase(entities, stack, false);
56255                     _tree.rebase(entities, false);
56256
56257                     dispatch$1.call('merge', this, entities);
56258                 },
56259
56260
56261                 perform: function() {
56262                     // complete any transition already in progress
56263                     select(document).interrupt('history.perform');
56264
56265                     var transitionable = false;
56266                     var action0 = arguments[0];
56267
56268                     if (arguments.length === 1 ||
56269                         (arguments.length === 2 && (typeof arguments[1] !== 'function'))) {
56270                         transitionable = !!action0.transitionable;
56271                     }
56272
56273                     if (transitionable) {
56274                         var origArguments = arguments;
56275                         select(document)
56276                             .transition('history.perform')
56277                             .duration(duration)
56278                             .ease(linear$1)
56279                             .tween('history.tween', function() {
56280                                 return function(t) {
56281                                     if (t < 1) { _overwrite([action0], t); }
56282                                 };
56283                             })
56284                             .on('start', function() {
56285                                 _perform([action0], 0);
56286                             })
56287                             .on('end interrupt', function() {
56288                                 _overwrite(origArguments, 1);
56289                             });
56290
56291                     } else {
56292                         return _perform(arguments);
56293                     }
56294                 },
56295
56296
56297                 replace: function() {
56298                     select(document).interrupt('history.perform');
56299                     return _replace(arguments, 1);
56300                 },
56301
56302
56303                 // Same as calling pop and then perform
56304                 overwrite: function() {
56305                     select(document).interrupt('history.perform');
56306                     return _overwrite(arguments, 1);
56307                 },
56308
56309
56310                 pop: function(n) {
56311                     select(document).interrupt('history.perform');
56312
56313                     var previous = _stack[_index].graph;
56314                     if (isNaN(+n) || +n < 0) {
56315                         n = 1;
56316                     }
56317                     while (n-- > 0 && _index > 0) {
56318                         _index--;
56319                         _stack.pop();
56320                     }
56321                     return change(previous);
56322                 },
56323
56324
56325                 // Back to the previous annotated state or _index = 0.
56326                 undo: function() {
56327                     select(document).interrupt('history.perform');
56328
56329                     var previousStack = _stack[_index];
56330                     var previous = previousStack.graph;
56331                     while (_index > 0) {
56332                         _index--;
56333                         if (_stack[_index].annotation) { break; }
56334                     }
56335
56336                     dispatch$1.call('undone', this, _stack[_index], previousStack);
56337                     return change(previous);
56338                 },
56339
56340
56341                 // Forward to the next annotated state.
56342                 redo: function() {
56343                     select(document).interrupt('history.perform');
56344
56345                     var previousStack = _stack[_index];
56346                     var previous = previousStack.graph;
56347                     var tryIndex = _index;
56348                     while (tryIndex < _stack.length - 1) {
56349                         tryIndex++;
56350                         if (_stack[tryIndex].annotation) {
56351                             _index = tryIndex;
56352                             dispatch$1.call('redone', this, _stack[_index], previousStack);
56353                             break;
56354                         }
56355                     }
56356
56357                     return change(previous);
56358                 },
56359
56360
56361                 pauseChangeDispatch: function() {
56362                     if (!_pausedGraph) {
56363                         _pausedGraph = _stack[_index].graph;
56364                     }
56365                 },
56366
56367
56368                 resumeChangeDispatch: function() {
56369                     if (_pausedGraph) {
56370                         var previous = _pausedGraph;
56371                         _pausedGraph = null;
56372                         return change(previous);
56373                     }
56374                 },
56375
56376
56377                 undoAnnotation: function() {
56378                     var i = _index;
56379                     while (i >= 0) {
56380                         if (_stack[i].annotation) { return _stack[i].annotation; }
56381                         i--;
56382                     }
56383                 },
56384
56385
56386                 redoAnnotation: function() {
56387                     var i = _index + 1;
56388                     while (i <= _stack.length - 1) {
56389                         if (_stack[i].annotation) { return _stack[i].annotation; }
56390                         i++;
56391                     }
56392                 },
56393
56394
56395                 // Returns the entities from the active graph with bounding boxes
56396                 // overlapping the given `extent`.
56397                 intersects: function(extent) {
56398                     return _tree.intersects(extent, _stack[_index].graph);
56399                 },
56400
56401
56402                 difference: function() {
56403                     var base = _stack[0].graph;
56404                     var head = _stack[_index].graph;
56405                     return coreDifference(base, head);
56406                 },
56407
56408
56409                 changes: function(action) {
56410                     var base = _stack[0].graph;
56411                     var head = _stack[_index].graph;
56412
56413                     if (action) {
56414                         head = action(head);
56415                     }
56416
56417                     var difference = coreDifference(base, head);
56418
56419                     return {
56420                         modified: difference.modified(),
56421                         created: difference.created(),
56422                         deleted: difference.deleted()
56423                     };
56424                 },
56425
56426
56427                 hasChanges: function() {
56428                     return this.difference().length() > 0;
56429                 },
56430
56431
56432                 imageryUsed: function(sources) {
56433                     if (sources) {
56434                         _imageryUsed = sources;
56435                         return history;
56436                     } else {
56437                         var s = new Set();
56438                         _stack.slice(1, _index + 1).forEach(function(state) {
56439                             state.imageryUsed.forEach(function(source) {
56440                                 if (source !== 'Custom') {
56441                                     s.add(source);
56442                                 }
56443                             });
56444                         });
56445                         return Array.from(s);
56446                     }
56447                 },
56448
56449
56450                 photoOverlaysUsed: function(sources) {
56451                     if (sources) {
56452                         _photoOverlaysUsed = sources;
56453                         return history;
56454                     } else {
56455                         var s = new Set();
56456                         _stack.slice(1, _index + 1).forEach(function(state) {
56457                             if (state.photoOverlaysUsed && Array.isArray(state.photoOverlaysUsed)) {
56458                                 state.photoOverlaysUsed.forEach(function(photoOverlay) {
56459                                     s.add(photoOverlay);
56460                                 });
56461                             }
56462                         });
56463                         return Array.from(s);
56464                     }
56465                 },
56466
56467
56468                 // save the current history state
56469                 checkpoint: function(key) {
56470                     _checkpoints[key] = {
56471                         stack: _stack,
56472                         index: _index
56473                     };
56474                     return history;
56475                 },
56476
56477
56478                 // restore history state to a given checkpoint or reset completely
56479                 reset: function(key) {
56480                     if (key !== undefined && _checkpoints.hasOwnProperty(key)) {
56481                         _stack = _checkpoints[key].stack;
56482                         _index = _checkpoints[key].index;
56483                     } else {
56484                         _stack = [{graph: coreGraph()}];
56485                         _index = 0;
56486                         _tree = coreTree(_stack[0].graph);
56487                         _checkpoints = {};
56488                     }
56489                     dispatch$1.call('change');
56490                     return history;
56491                 },
56492
56493
56494                 // `toIntroGraph()` is used to export the intro graph used by the walkthrough.
56495                 //
56496                 // To use it:
56497                 //  1. Start the walkthrough.
56498                 //  2. Get to a "free editing" tutorial step
56499                 //  3. Make your edits to the walkthrough map
56500                 //  4. In your browser dev console run:
56501                 //        `id.history().toIntroGraph()`
56502                 //  5. This outputs stringified JSON to the browser console
56503                 //  6. Copy it to `data/intro_graph.json` and prettify it in your code editor
56504                 toIntroGraph: function() {
56505                     var nextID = { n: 0, r: 0, w: 0 };
56506                     var permIDs = {};
56507                     var graph = this.graph();
56508                     var baseEntities = {};
56509
56510                     // clone base entities..
56511                     Object.values(graph.base().entities).forEach(function(entity) {
56512                         var copy = copyIntroEntity(entity);
56513                         baseEntities[copy.id] = copy;
56514                     });
56515
56516                     // replace base entities with head entities..
56517                     Object.keys(graph.entities).forEach(function(id) {
56518                         var entity = graph.entities[id];
56519                         if (entity) {
56520                             var copy = copyIntroEntity(entity);
56521                             baseEntities[copy.id] = copy;
56522                         } else {
56523                             delete baseEntities[id];
56524                         }
56525                     });
56526
56527                     // swap temporary for permanent ids..
56528                     Object.values(baseEntities).forEach(function(entity) {
56529                         if (Array.isArray(entity.nodes)) {
56530                             entity.nodes = entity.nodes.map(function(node) {
56531                                 return permIDs[node] || node;
56532                             });
56533                         }
56534                         if (Array.isArray(entity.members)) {
56535                             entity.members = entity.members.map(function(member) {
56536                                 member.id = permIDs[member.id] || member.id;
56537                                 return member;
56538                             });
56539                         }
56540                     });
56541
56542                     return JSON.stringify({ dataIntroGraph: baseEntities });
56543
56544
56545                     function copyIntroEntity(source) {
56546                         var copy = utilObjectOmit(source, ['type', 'user', 'v', 'version', 'visible']);
56547
56548                         // Note: the copy is no longer an osmEntity, so it might not have `tags`
56549                         if (copy.tags && !Object.keys(copy.tags)) {
56550                             delete copy.tags;
56551                         }
56552
56553                         if (Array.isArray(copy.loc)) {
56554                             copy.loc[0] = +copy.loc[0].toFixed(6);
56555                             copy.loc[1] = +copy.loc[1].toFixed(6);
56556                         }
56557
56558                         var match = source.id.match(/([nrw])-\d*/);  // temporary id
56559                         if (match !== null) {
56560                             var nrw = match[1];
56561                             var permID;
56562                             do { permID = nrw + (++nextID[nrw]); }
56563                             while (baseEntities.hasOwnProperty(permID));
56564
56565                             copy.id = permIDs[source.id] = permID;
56566                         }
56567                         return copy;
56568                     }
56569                 },
56570
56571
56572                 toJSON: function() {
56573                     if (!this.hasChanges()) { return; }
56574
56575                     var allEntities = {};
56576                     var baseEntities = {};
56577                     var base = _stack[0];
56578
56579                     var s = _stack.map(function(i) {
56580                         var modified = [];
56581                         var deleted = [];
56582
56583                         Object.keys(i.graph.entities).forEach(function(id) {
56584                             var entity = i.graph.entities[id];
56585                             if (entity) {
56586                                 var key = osmEntity.key(entity);
56587                                 allEntities[key] = entity;
56588                                 modified.push(key);
56589                             } else {
56590                                 deleted.push(id);
56591                             }
56592
56593                             // make sure that the originals of changed or deleted entities get merged
56594                             // into the base of the _stack after restoring the data from JSON.
56595                             if (id in base.graph.entities) {
56596                                 baseEntities[id] = base.graph.entities[id];
56597                             }
56598                             if (entity && entity.nodes) {
56599                                 // get originals of pre-existing child nodes
56600                                 entity.nodes.forEach(function(nodeID) {
56601                                     if (nodeID in base.graph.entities) {
56602                                         baseEntities[nodeID] = base.graph.entities[nodeID];
56603                                     }
56604                                 });
56605                             }
56606                             // get originals of parent entities too
56607                             var baseParents = base.graph._parentWays[id];
56608                             if (baseParents) {
56609                                 baseParents.forEach(function(parentID) {
56610                                     if (parentID in base.graph.entities) {
56611                                         baseEntities[parentID] = base.graph.entities[parentID];
56612                                     }
56613                                 });
56614                             }
56615                         });
56616
56617                         var x = {};
56618
56619                         if (modified.length) { x.modified = modified; }
56620                         if (deleted.length) { x.deleted = deleted; }
56621                         if (i.imageryUsed) { x.imageryUsed = i.imageryUsed; }
56622                         if (i.photoOverlaysUsed) { x.photoOverlaysUsed = i.photoOverlaysUsed; }
56623                         if (i.annotation) { x.annotation = i.annotation; }
56624                         if (i.transform) { x.transform = i.transform; }
56625                         if (i.selectedIDs) { x.selectedIDs = i.selectedIDs; }
56626
56627                         return x;
56628                     });
56629
56630                     return JSON.stringify({
56631                         version: 3,
56632                         entities: Object.values(allEntities),
56633                         baseEntities: Object.values(baseEntities),
56634                         stack: s,
56635                         nextIDs: osmEntity.id.next,
56636                         index: _index,
56637                         // note the time the changes were saved
56638                         timestamp: (new Date()).getTime()
56639                     });
56640                 },
56641
56642
56643                 fromJSON: function(json, loadChildNodes) {
56644                     var h = JSON.parse(json);
56645                     var loadComplete = true;
56646
56647                     osmEntity.id.next = h.nextIDs;
56648                     _index = h.index;
56649
56650                     if (h.version === 2 || h.version === 3) {
56651                         var allEntities = {};
56652
56653                         h.entities.forEach(function(entity) {
56654                             allEntities[osmEntity.key(entity)] = osmEntity(entity);
56655                         });
56656
56657                         if (h.version === 3) {
56658                             // This merges originals for changed entities into the base of
56659                             // the _stack even if the current _stack doesn't have them (for
56660                             // example when iD has been restarted in a different region)
56661                             var baseEntities = h.baseEntities.map(function(d) { return osmEntity(d); });
56662                             var stack = _stack.map(function(state) { return state.graph; });
56663                             _stack[0].graph.rebase(baseEntities, stack, true);
56664                             _tree.rebase(baseEntities, true);
56665
56666                             // When we restore a modified way, we also need to fetch any missing
56667                             // childnodes that would normally have been downloaded with it.. #2142
56668                             if (loadChildNodes) {
56669                                 var osm = context.connection();
56670                                 var baseWays = baseEntities
56671                                     .filter(function(e) { return e.type === 'way'; });
56672                                 var nodeIDs = baseWays
56673                                     .reduce(function(acc, way) { return utilArrayUnion(acc, way.nodes); }, []);
56674                                 var missing = nodeIDs
56675                                     .filter(function(n) { return !_stack[0].graph.hasEntity(n); });
56676
56677                                 if (missing.length && osm) {
56678                                     loadComplete = false;
56679                                     context.map().redrawEnable(false);
56680
56681                                     var loading = uiLoading(context).blocking(true);
56682                                     context.container().call(loading);
56683
56684                                     var childNodesLoaded = function(err, result) {
56685                                         if (!err) {
56686                                             var visibleGroups = utilArrayGroupBy(result.data, 'visible');
56687                                             var visibles = visibleGroups.true || [];      // alive nodes
56688                                             var invisibles = visibleGroups.false || [];   // deleted nodes
56689
56690                                             if (visibles.length) {
56691                                                 var visibleIDs = visibles.map(function(entity) { return entity.id; });
56692                                                 var stack = _stack.map(function(state) { return state.graph; });
56693                                                 missing = utilArrayDifference(missing, visibleIDs);
56694                                                 _stack[0].graph.rebase(visibles, stack, true);
56695                                                 _tree.rebase(visibles, true);
56696                                             }
56697
56698                                             // fetch older versions of nodes that were deleted..
56699                                             invisibles.forEach(function(entity) {
56700                                                 osm.loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
56701                                             });
56702                                         }
56703
56704                                         if (err || !missing.length) {
56705                                             loading.close();
56706                                             context.map().redrawEnable(true);
56707                                             dispatch$1.call('change');
56708                                             dispatch$1.call('restore', this);
56709                                         }
56710                                     };
56711
56712                                     osm.loadMultiple(missing, childNodesLoaded);
56713                                 }
56714                             }
56715                         }
56716
56717                         _stack = h.stack.map(function(d) {
56718                             var entities = {}, entity;
56719
56720                             if (d.modified) {
56721                                 d.modified.forEach(function(key) {
56722                                     entity = allEntities[key];
56723                                     entities[entity.id] = entity;
56724                                 });
56725                             }
56726
56727                             if (d.deleted) {
56728                                 d.deleted.forEach(function(id) {
56729                                     entities[id] = undefined;
56730                                 });
56731                             }
56732
56733                             return {
56734                                 graph: coreGraph(_stack[0].graph).load(entities),
56735                                 annotation: d.annotation,
56736                                 imageryUsed: d.imageryUsed,
56737                                 photoOverlaysUsed: d.photoOverlaysUsed,
56738                                 transform: d.transform,
56739                                 selectedIDs: d.selectedIDs
56740                             };
56741                         });
56742
56743                     } else { // original version
56744                         _stack = h.stack.map(function(d) {
56745                             var entities = {};
56746
56747                             for (var i in d.entities) {
56748                                 var entity = d.entities[i];
56749                                 entities[i] = entity === 'undefined' ? undefined : osmEntity(entity);
56750                             }
56751
56752                             d.graph = coreGraph(_stack[0].graph).load(entities);
56753                             return d;
56754                         });
56755                     }
56756
56757                     var transform = _stack[_index].transform;
56758                     if (transform) {
56759                         context.map().transformEase(transform, 0);   // 0 = immediate, no easing
56760                     }
56761
56762                     if (loadComplete) {
56763                         dispatch$1.call('change');
56764                         dispatch$1.call('restore', this);
56765                     }
56766
56767                     return history;
56768                 },
56769
56770
56771                 lock: function() {
56772                     return lock.lock();
56773                 },
56774
56775
56776                 unlock: function() {
56777                     lock.unlock();
56778                 },
56779
56780
56781                 save: function() {
56782                     if (lock.locked() &&
56783                         // don't overwrite existing, unresolved changes
56784                         !_hasUnresolvedRestorableChanges) {
56785
56786                         corePreferences(getKey('saved_history'), history.toJSON() || null);
56787                     }
56788                     return history;
56789                 },
56790
56791
56792                 // delete the history version saved in localStorage
56793                 clearSaved: function() {
56794                     context.debouncedSave.cancel();
56795                     if (lock.locked()) {
56796                         _hasUnresolvedRestorableChanges = false;
56797                         corePreferences(getKey('saved_history'), null);
56798
56799                         // clear the changeset metadata associated with the saved history
56800                         corePreferences('comment', null);
56801                         corePreferences('hashtags', null);
56802                         corePreferences('source', null);
56803                     }
56804                     return history;
56805                 },
56806
56807
56808                 savedHistoryJSON: function() {
56809                     return corePreferences(getKey('saved_history'));
56810                 },
56811
56812
56813                 hasRestorableChanges: function() {
56814                     return _hasUnresolvedRestorableChanges;
56815                 },
56816
56817
56818                 // load history from a version stored in localStorage
56819                 restore: function() {
56820                     if (lock.locked()) {
56821                         _hasUnresolvedRestorableChanges = false;
56822                         var json = this.savedHistoryJSON();
56823                         if (json) { history.fromJSON(json, true); }
56824                     }
56825                 },
56826
56827
56828                 _getKey: getKey
56829
56830             };
56831
56832
56833             history.reset();
56834
56835             return utilRebind(history, dispatch$1, 'on');
56836         }
56837
56838         /**
56839          * Look for roads that can be connected to other roads with a short extension
56840          */
56841         function validationAlmostJunction(context) {
56842           var type = 'almost_junction';
56843           var EXTEND_TH_METERS = 5;
56844           var WELD_TH_METERS = 0.75;
56845           // Comes from considering bounding case of parallel ways
56846           var CLOSE_NODE_TH = EXTEND_TH_METERS - WELD_TH_METERS;
56847           // Comes from considering bounding case of perpendicular ways
56848           var SIG_ANGLE_TH = Math.atan(WELD_TH_METERS / EXTEND_TH_METERS);
56849
56850           function isHighway(entity) {
56851             return entity.type === 'way'
56852               && osmRoutableHighwayTagValues[entity.tags.highway];
56853           }
56854
56855           function isTaggedAsNotContinuing(node) {
56856             return node.tags.noexit === 'yes'
56857               || node.tags.amenity === 'parking_entrance'
56858               || (node.tags.entrance && node.tags.entrance !== 'no');
56859           }
56860
56861
56862           var validation = function checkAlmostJunction(entity, graph) {
56863             if (!isHighway(entity)) { return []; }
56864             if (entity.isDegenerate()) { return []; }
56865
56866             var tree = context.history().tree();
56867             var extendableNodeInfos = findConnectableEndNodesByExtension(entity);
56868
56869             var issues = [];
56870
56871             extendableNodeInfos.forEach(function (extendableNodeInfo) {
56872               issues.push(new validationIssue({
56873                 type: type,
56874                 subtype: 'highway-highway',
56875                 severity: 'warning',
56876                 message: function message(context) {
56877                   var entity1 = context.hasEntity(this.entityIds[0]);
56878                   if (this.entityIds[0] === this.entityIds[2]) {
56879                     return entity1 ? _t('issues.almost_junction.self.message', {
56880                       feature: utilDisplayLabel(entity1, context.graph())
56881                     }) : '';
56882                   } else {
56883                     var entity2 = context.hasEntity(this.entityIds[2]);
56884                     return (entity1 && entity2) ? _t('issues.almost_junction.message', {
56885                       feature: utilDisplayLabel(entity1, context.graph()),
56886                       feature2: utilDisplayLabel(entity2, context.graph())
56887                     }) : '';
56888                   }
56889                 },
56890                 reference: showReference,
56891                 entityIds: [
56892                   entity.id,
56893                   extendableNodeInfo.node.id,
56894                   extendableNodeInfo.wid ],
56895                 loc: extendableNodeInfo.node.loc,
56896                 hash: JSON.stringify(extendableNodeInfo.node.loc),
56897                 data: {
56898                   midId: extendableNodeInfo.mid.id,
56899                   edge: extendableNodeInfo.edge,
56900                   cross_loc: extendableNodeInfo.cross_loc
56901                 },
56902                 dynamicFixes: makeFixes
56903               }));
56904             });
56905
56906             return issues;
56907
56908             function makeFixes(context) {
56909               var fixes = [new validationIssueFix({
56910                 icon: 'iD-icon-abutment',
56911                 title: _t('issues.fix.connect_features.title'),
56912                 onClick: function onClick(context) {
56913                   var annotation = _t('issues.fix.connect_almost_junction.annotation');
56914                   var ref = this.issue.entityIds;
56915                   var endNodeId = ref[1];
56916                   var crossWayId = ref[2];
56917                   var midNode = context.entity(this.issue.data.midId);
56918                   var endNode = context.entity(endNodeId);
56919                   var crossWay = context.entity(crossWayId);
56920
56921                   // When endpoints are close, just join if resulting small change in angle (#7201)
56922                   var nearEndNodes = findNearbyEndNodes(endNode, crossWay);
56923                   if (nearEndNodes.length > 0) {
56924                     var collinear = findSmallJoinAngle(midNode, endNode, nearEndNodes);
56925                     if (collinear) {
56926                       context.perform(
56927                         actionMergeNodes([collinear.id, endNode.id], collinear.loc),
56928                         annotation
56929                       );
56930                       return;
56931                     }
56932                   }
56933
56934                   var targetEdge = this.issue.data.edge;
56935                   var crossLoc = this.issue.data.cross_loc;
56936                   var edgeNodes = [context.entity(targetEdge[0]), context.entity(targetEdge[1])];
56937                   var closestNodeInfo = geoSphericalClosestNode(edgeNodes, crossLoc);
56938
56939                   // already a point nearby, just connect to that
56940                   if (closestNodeInfo.distance < WELD_TH_METERS) {
56941                     context.perform(
56942                       actionMergeNodes([closestNodeInfo.node.id, endNode.id], closestNodeInfo.node.loc),
56943                       annotation
56944                     );
56945                   // else add the end node to the edge way
56946                   } else {
56947                     context.perform(
56948                       actionAddMidpoint({loc: crossLoc, edge: targetEdge}, endNode),
56949                       annotation
56950                     );
56951                   }
56952                 }
56953               })];
56954
56955               var node = context.hasEntity(this.entityIds[1]);
56956               if (node && !node.hasInterestingTags()) {
56957                 // node has no descriptive tags, suggest noexit fix
56958                 fixes.push(new validationIssueFix({
56959                   icon: 'maki-barrier',
56960                   title: _t('issues.fix.tag_as_disconnected.title'),
56961                   onClick: function onClick(context) {
56962                     var nodeID = this.issue.entityIds[1];
56963                     var tags = Object.assign({}, context.entity(nodeID).tags);
56964                     tags.noexit = 'yes';
56965                     context.perform(
56966                       actionChangeTags(nodeID, tags),
56967                       _t('issues.fix.tag_as_disconnected.annotation')
56968                     );
56969                   }
56970                 }));
56971               }
56972
56973               return fixes;
56974             }
56975
56976             function showReference(selection) {
56977               selection.selectAll('.issue-reference')
56978                 .data([0])
56979                 .enter()
56980                 .append('div')
56981                 .attr('class', 'issue-reference')
56982                 .text(_t('issues.almost_junction.highway-highway.reference'));
56983             }
56984
56985             function isExtendableCandidate(node, way) {
56986               // can not accurately test vertices on tiles not downloaded from osm - #5938
56987               var osm = services.osm;
56988               if (osm && !osm.isDataLoaded(node.loc)) {
56989                 return false;
56990               }
56991               if (isTaggedAsNotContinuing(node) || graph.parentWays(node).length !== 1) {
56992                 return false;
56993               }
56994
56995               var occurences = 0;
56996               for (var index in way.nodes) {
56997                 if (way.nodes[index] === node.id) {
56998                   occurences += 1;
56999                   if (occurences > 1) {
57000                     return false;
57001                   }
57002                 }
57003               }
57004               return true;
57005             }
57006
57007             function findConnectableEndNodesByExtension(way) {
57008               var results = [];
57009               if (way.isClosed()) { return results; }
57010
57011               var testNodes;
57012               var indices = [0, way.nodes.length - 1];
57013               indices.forEach(function (nodeIndex) {
57014                 var nodeID = way.nodes[nodeIndex];
57015                 var node = graph.entity(nodeID);
57016
57017                 if (!isExtendableCandidate(node, way)) { return; }
57018
57019                 var connectionInfo = canConnectByExtend(way, nodeIndex);
57020                 if (!connectionInfo) { return; }
57021
57022                 testNodes = graph.childNodes(way).slice();   // shallow copy
57023                 testNodes[nodeIndex] = testNodes[nodeIndex].move(connectionInfo.cross_loc);
57024
57025                 // don't flag issue if connecting the ways would cause self-intersection
57026                 if (geoHasSelfIntersections(testNodes, nodeID)) { return; }
57027
57028                 results.push(connectionInfo);
57029               });
57030
57031               return results;
57032             }
57033
57034             function findNearbyEndNodes(node, way) {
57035               return [
57036                 way.nodes[0],
57037                 way.nodes[way.nodes.length - 1]
57038               ].map(function (d) { return graph.entity(d); })
57039               .filter(function (d) {
57040                 // Node cannot be near to itself, but other endnode of same way could be
57041                 return d.id !== node.id
57042                   && geoSphericalDistance(node.loc, d.loc) <= CLOSE_NODE_TH;
57043               });
57044             }
57045
57046             function findSmallJoinAngle(midNode, tipNode, endNodes) {
57047               // Both nodes could be close, so want to join whichever is closest to collinear
57048               var joinTo;
57049               var minAngle = Infinity;
57050
57051               // Checks midNode -> tipNode -> endNode for collinearity
57052               endNodes.forEach(function (endNode) {
57053                 var a1 = geoAngle(midNode, tipNode, context.projection) + Math.PI;
57054                 var a2 = geoAngle(midNode, endNode, context.projection) + Math.PI;
57055                 var diff = Math.max(a1, a2) - Math.min(a1, a2);
57056
57057                 if (diff < minAngle) {
57058                   joinTo = endNode;
57059                   minAngle = diff;
57060                 }
57061               });
57062
57063               /* Threshold set by considering right angle triangle
57064               based on node joining threshold and extension distance */
57065               if (minAngle <= SIG_ANGLE_TH) { return joinTo; }
57066
57067               return null;
57068             }
57069
57070             function hasTag(tags, key) {
57071               return tags[key] !== undefined && tags[key] !== 'no';
57072             }
57073
57074             function canConnectWays(way, way2) {
57075
57076               // allow self-connections
57077               if (way.id === way2.id) { return true; }
57078
57079               // if one is bridge or tunnel, both must be bridge or tunnel
57080               if ((hasTag(way.tags, 'bridge') || hasTag(way2.tags, 'bridge')) &&
57081                 !(hasTag(way.tags, 'bridge') && hasTag(way2.tags, 'bridge'))) { return false; }
57082               if ((hasTag(way.tags, 'tunnel') || hasTag(way2.tags, 'tunnel')) &&
57083                 !(hasTag(way.tags, 'tunnel') && hasTag(way2.tags, 'tunnel'))) { return false; }
57084
57085               // must have equivalent layers and levels
57086               var layer1 = way.tags.layer || '0',
57087                 layer2 = way2.tags.layer || '0';
57088               if (layer1 !== layer2) { return false; }
57089
57090               var level1 = way.tags.level || '0',
57091                 level2 = way2.tags.level || '0';
57092               if (level1 !== level2) { return false; }
57093
57094               return true;
57095             }
57096
57097             function canConnectByExtend(way, endNodeIdx) {
57098               var tipNid = way.nodes[endNodeIdx];  // the 'tip' node for extension point
57099               var midNid = endNodeIdx === 0 ? way.nodes[1] : way.nodes[way.nodes.length - 2];  // the other node of the edge
57100               var tipNode = graph.entity(tipNid);
57101               var midNode = graph.entity(midNid);
57102               var lon = tipNode.loc[0];
57103               var lat = tipNode.loc[1];
57104               var lon_range = geoMetersToLon(EXTEND_TH_METERS, lat) / 2;
57105               var lat_range = geoMetersToLat(EXTEND_TH_METERS) / 2;
57106               var queryExtent = geoExtent([
57107                 [lon - lon_range, lat - lat_range],
57108                 [lon + lon_range, lat + lat_range]
57109               ]);
57110
57111               // first, extend the edge of [midNode -> tipNode] by EXTEND_TH_METERS and find the "extended tip" location
57112               var edgeLen = geoSphericalDistance(midNode.loc, tipNode.loc);
57113               var t = EXTEND_TH_METERS / edgeLen + 1.0;
57114               var extTipLoc = geoVecInterp(midNode.loc, tipNode.loc, t);
57115
57116               // then, check if the extension part [tipNode.loc -> extTipLoc] intersects any other ways
57117               var segmentInfos = tree.waySegments(queryExtent, graph);
57118               for (var i = 0; i < segmentInfos.length; i++) {
57119                 var segmentInfo = segmentInfos[i];
57120
57121                 var way2 = graph.entity(segmentInfo.wayId);
57122
57123                 if (!isHighway(way2)) { continue; }
57124
57125                 if (!canConnectWays(way, way2)) { continue; }
57126
57127                 var nAid = segmentInfo.nodes[0],
57128                   nBid = segmentInfo.nodes[1];
57129
57130                 if (nAid === tipNid || nBid === tipNid) { continue; }
57131
57132                 var nA = graph.entity(nAid),
57133                   nB = graph.entity(nBid);
57134                 var crossLoc = geoLineIntersection([tipNode.loc, extTipLoc], [nA.loc, nB.loc]);
57135                 if (crossLoc) {
57136                   return {
57137                     mid: midNode,
57138                     node: tipNode,
57139                     wid: way2.id,
57140                     edge: [nA.id, nB.id],
57141                     cross_loc: crossLoc
57142                   };
57143                 }
57144               }
57145               return null;
57146             }
57147           };
57148
57149           validation.type = type;
57150
57151           return validation;
57152         }
57153
57154         function validationCloseNodes(context) {
57155             var type = 'close_nodes';
57156
57157             var pointThresholdMeters = 0.2;
57158
57159             var validation = function(entity, graph) {
57160                 if (entity.type === 'node') {
57161                     return getIssuesForNode(entity);
57162                 } else if (entity.type === 'way') {
57163                     return getIssuesForWay(entity);
57164                 }
57165                 return [];
57166
57167                 function getIssuesForNode(node) {
57168                     var parentWays = graph.parentWays(node);
57169                     if (parentWays.length) {
57170                         return getIssuesForVertex(node, parentWays);
57171                     } else {
57172                         return getIssuesForDetachedPoint(node);
57173                     }
57174                 }
57175
57176                 function wayTypeFor(way) {
57177
57178                     if (way.tags.boundary && way.tags.boundary !== 'no') { return 'boundary'; }
57179                     if (way.tags.indoor && way.tags.indoor !== 'no') { return 'indoor'; }
57180                     if ((way.tags.building && way.tags.building !== 'no') ||
57181                         (way.tags['building:part'] && way.tags['building:part'] !== 'no')) { return 'building'; }
57182                     if (osmPathHighwayTagValues[way.tags.highway]) { return 'path'; }
57183
57184                     var parentRelations = graph.parentRelations(way);
57185                     for (var i in parentRelations) {
57186                         var relation = parentRelations[i];
57187
57188                         if (relation.tags.type === 'boundary') { return 'boundary'; }
57189
57190                         if (relation.isMultipolygon()) {
57191                             if (relation.tags.indoor && relation.tags.indoor !== 'no') { return 'indoor'; }
57192                             if ((relation.tags.building && relation.tags.building !== 'no') ||
57193                                 (relation.tags['building:part'] && relation.tags['building:part'] !== 'no')) { return 'building'; }
57194                         }
57195                     }
57196
57197                     return 'other';
57198                 }
57199
57200                 function shouldCheckWay(way) {
57201
57202                     // don't flag issues where merging would create degenerate ways
57203                     if (way.nodes.length <= 2 ||
57204                         (way.isClosed() && way.nodes.length <= 4)) { return false; }
57205
57206                     var bbox = way.extent(graph).bbox();
57207                     var hypotenuseMeters = geoSphericalDistance([bbox.minX, bbox.minY], [bbox.maxX, bbox.maxY]);
57208                     // don't flag close nodes in very small ways
57209                     if (hypotenuseMeters < 1.5) { return false; }
57210
57211                     return true;
57212                 }
57213
57214                 function getIssuesForWay(way) {
57215                     if (!shouldCheckWay(way)) { return []; }
57216
57217                     var issues = [],
57218                         nodes = graph.childNodes(way);
57219                     for (var i = 0; i < nodes.length - 1; i++) {
57220                         var node1 = nodes[i];
57221                         var node2 = nodes[i+1];
57222
57223                         var issue = getWayIssueIfAny(node1, node2, way);
57224                         if (issue) { issues.push(issue); }
57225                     }
57226                     return issues;
57227                 }
57228
57229                 function getIssuesForVertex(node, parentWays) {
57230                     var issues = [];
57231
57232                     function checkForCloseness(node1, node2, way) {
57233                         var issue = getWayIssueIfAny(node1, node2, way);
57234                         if (issue) { issues.push(issue); }
57235                     }
57236
57237                     for (var i = 0; i < parentWays.length; i++) {
57238                         var parentWay = parentWays[i];
57239
57240                         if (!shouldCheckWay(parentWay)) { continue; }
57241
57242                         var lastIndex = parentWay.nodes.length - 1;
57243                         for (var j = 0; j < parentWay.nodes.length; j++) {
57244                             if (j !== 0) {
57245                                 if (parentWay.nodes[j-1] === node.id) {
57246                                     checkForCloseness(node, graph.entity(parentWay.nodes[j]), parentWay);
57247                                 }
57248                             }
57249                             if (j !== lastIndex) {
57250                                 if (parentWay.nodes[j+1] === node.id) {
57251                                     checkForCloseness(graph.entity(parentWay.nodes[j]), node, parentWay);
57252                                 }
57253                             }
57254                         }
57255                     }
57256                     return issues;
57257                 }
57258
57259                 function thresholdMetersForWay(way) {
57260                     if (!shouldCheckWay(way)) { return 0; }
57261
57262                     var wayType = wayTypeFor(way);
57263
57264                     // don't flag boundaries since they might be highly detailed and can't be easily verified
57265                     if (wayType === 'boundary') { return 0; }
57266                     // expect some features to be mapped with higher levels of detail
57267                     if (wayType === 'indoor') { return 0.01; }
57268                     if (wayType === 'building') { return 0.05; }
57269                     if (wayType === 'path') { return 0.1; }
57270                     return 0.2;
57271                 }
57272
57273                 function getIssuesForDetachedPoint(node) {
57274
57275                     var issues = [];
57276
57277                     var lon = node.loc[0];
57278                     var lat = node.loc[1];
57279                     var lon_range = geoMetersToLon(pointThresholdMeters, lat) / 2;
57280                     var lat_range = geoMetersToLat(pointThresholdMeters) / 2;
57281                     var queryExtent = geoExtent([
57282                         [lon - lon_range, lat - lat_range],
57283                         [lon + lon_range, lat + lat_range]
57284                     ]);
57285
57286                     var intersected = context.history().tree().intersects(queryExtent, graph);
57287                     for (var j = 0; j < intersected.length; j++) {
57288                         var nearby = intersected[j];
57289
57290                         if (nearby.id === node.id) { continue; }
57291                         if (nearby.type !== 'node' || nearby.geometry(graph) !== 'point') { continue; }
57292
57293                         if (nearby.loc === node.loc ||
57294                             geoSphericalDistance(node.loc, nearby.loc) < pointThresholdMeters) {
57295
57296                             // allow very close points if tags indicate the z-axis might vary
57297                             var zAxisKeys = { layer: true, level: true, 'addr:housenumber': true, 'addr:unit': true };
57298                             var zAxisDifferentiates = false;
57299                             for (var key in zAxisKeys) {
57300                                 var nodeValue = node.tags[key] || '0';
57301                                 var nearbyValue = nearby.tags[key] || '0';
57302                                 if (nodeValue !== nearbyValue) {
57303                                     zAxisDifferentiates = true;
57304                                     break;
57305                                 }
57306                             }
57307                             if (zAxisDifferentiates) { continue; }
57308
57309                             issues.push(new validationIssue({
57310                                 type: type,
57311                                 subtype: 'detached',
57312                                 severity: 'warning',
57313                                 message: function(context) {
57314                                     var entity = context.hasEntity(this.entityIds[0]),
57315                                         entity2 = context.hasEntity(this.entityIds[1]);
57316                                     return (entity && entity2) ? _t('issues.close_nodes.detached.message', {
57317                                         feature: utilDisplayLabel(entity, context.graph()),
57318                                         feature2: utilDisplayLabel(entity2, context.graph())
57319                                     }) : '';
57320                                 },
57321                                 reference: showReference,
57322                                 entityIds: [node.id, nearby.id],
57323                                 dynamicFixes: function() {
57324                                     return [
57325                                         new validationIssueFix({
57326                                             icon: 'iD-operation-disconnect',
57327                                             title: _t('issues.fix.move_points_apart.title')
57328                                         }),
57329                                         new validationIssueFix({
57330                                             icon: 'iD-icon-layers',
57331                                             title: _t('issues.fix.use_different_layers_or_levels.title')
57332                                         })
57333                                     ];
57334                                 }
57335                             }));
57336                         }
57337                     }
57338
57339                     return issues;
57340
57341                     function showReference(selection) {
57342                         var referenceText = _t('issues.close_nodes.detached.reference');
57343                         selection.selectAll('.issue-reference')
57344                             .data([0])
57345                             .enter()
57346                             .append('div')
57347                             .attr('class', 'issue-reference')
57348                             .text(referenceText);
57349                     }
57350                 }
57351
57352                 function getWayIssueIfAny(node1, node2, way) {
57353                     if (node1.id === node2.id ||
57354                         (node1.hasInterestingTags() && node2.hasInterestingTags())) {
57355                         return null;
57356                     }
57357
57358                     if (node1.loc !== node2.loc) {
57359                         var parentWays1 = graph.parentWays(node1);
57360                         var parentWays2 = new Set(graph.parentWays(node2));
57361
57362                         var sharedWays = parentWays1.filter(function(parentWay) {
57363                             return parentWays2.has(parentWay);
57364                         });
57365
57366                         var thresholds = sharedWays.map(function(parentWay) {
57367                             return thresholdMetersForWay(parentWay);
57368                         });
57369
57370                         var threshold = Math.min.apply(Math, thresholds);
57371                         var distance = geoSphericalDistance(node1.loc, node2.loc);
57372                         if (distance > threshold) { return null; }
57373                     }
57374
57375                     return new validationIssue({
57376                         type: type,
57377                         subtype: 'vertices',
57378                         severity: 'warning',
57379                         message: function(context) {
57380                             var entity = context.hasEntity(this.entityIds[0]);
57381                             return entity ? _t('issues.close_nodes.message', { way: utilDisplayLabel(entity, context.graph()) }) : '';
57382                         },
57383                         reference: showReference,
57384                         entityIds: [way.id, node1.id, node2.id],
57385                         loc: node1.loc,
57386                         dynamicFixes: function() {
57387                             return [
57388                                 new validationIssueFix({
57389                                     icon: 'iD-icon-plus',
57390                                     title: _t('issues.fix.merge_points.title'),
57391                                     onClick: function(context) {
57392                                         var entityIds = this.issue.entityIds;
57393                                         var action = actionMergeNodes([entityIds[1], entityIds[2]]);
57394                                         context.perform(action, _t('issues.fix.merge_close_vertices.annotation'));
57395                                     }
57396                                 }),
57397                                 new validationIssueFix({
57398                                     icon: 'iD-operation-disconnect',
57399                                     title: _t('issues.fix.move_points_apart.title')
57400                                 })
57401                             ];
57402                         }
57403                     });
57404
57405                     function showReference(selection) {
57406                         var referenceText = _t('issues.close_nodes.reference');
57407                         selection.selectAll('.issue-reference')
57408                             .data([0])
57409                             .enter()
57410                             .append('div')
57411                             .attr('class', 'issue-reference')
57412                             .text(referenceText);
57413                     }
57414                 }
57415
57416             };
57417
57418
57419             validation.type = type;
57420
57421             return validation;
57422         }
57423
57424         function validationCrossingWays(context) {
57425             var type = 'crossing_ways';
57426
57427             // returns the way or its parent relation, whichever has a useful feature type
57428             function getFeatureWithFeatureTypeTagsForWay(way, graph) {
57429                 if (getFeatureType(way, graph) === null) {
57430                     // if the way doesn't match a feature type, check its parent relations
57431                     var parentRels = graph.parentRelations(way);
57432                     for (var i = 0; i < parentRels.length; i++) {
57433                         var rel = parentRels[i];
57434                         if (getFeatureType(rel, graph) !== null) {
57435                             return rel;
57436                         }
57437                     }
57438                 }
57439                 return way;
57440             }
57441
57442
57443             function hasTag(tags, key) {
57444                 return tags[key] !== undefined && tags[key] !== 'no';
57445             }
57446
57447             function taggedAsIndoor(tags) {
57448                 return hasTag(tags, 'indoor') ||
57449                     hasTag(tags, 'level') ||
57450                     tags.highway === 'corridor';
57451             }
57452
57453             function allowsBridge(featureType) {
57454                 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
57455             }
57456             function allowsTunnel(featureType) {
57457                 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
57458             }
57459
57460
57461             function getFeatureTypeForCrossingCheck(way, graph) {
57462                 var feature = getFeatureWithFeatureTypeTagsForWay(way, graph);
57463                 return getFeatureType(feature, graph);
57464             }
57465
57466             // discard
57467             var ignoredBuildings = {
57468                 demolished: true, dismantled: true, proposed: true, razed: true
57469             };
57470
57471
57472             function getFeatureType(entity, graph) {
57473
57474                 var geometry = entity.geometry(graph);
57475                 if (geometry !== 'line' && geometry !== 'area') { return null; }
57476
57477                 var tags = entity.tags;
57478
57479                 if (hasTag(tags, 'building') && !ignoredBuildings[tags.building]) { return 'building'; }
57480                 if (hasTag(tags, 'highway') && osmRoutableHighwayTagValues[tags.highway]) { return 'highway'; }
57481
57482                 // don't check railway or waterway areas
57483                 if (geometry !== 'line') { return null; }
57484
57485                 if (hasTag(tags, 'railway') && osmRailwayTrackTagValues[tags.railway]) { return 'railway'; }
57486                 if (hasTag(tags, 'waterway') && osmFlowingWaterwayTagValues[tags.waterway]) { return 'waterway'; }
57487
57488                 return null;
57489             }
57490
57491
57492             function isLegitCrossing(way1, featureType1, way2, featureType2) {
57493                 var tags1 = way1.tags;
57494                 var tags2 = way2.tags;
57495
57496                 // assume 0 by default
57497                 var level1 = tags1.level || '0';
57498                 var level2 = tags2.level || '0';
57499
57500                 if (taggedAsIndoor(tags1) && taggedAsIndoor(tags2) && level1 !== level2) {
57501                     // assume features don't interact if they're indoor on different levels
57502                     return true;
57503                 }
57504
57505                 // assume 0 by default; don't use way.layer() since we account for structures here
57506                 var layer1 = tags1.layer || '0';
57507                 var layer2 = tags2.layer || '0';
57508
57509                 if (allowsBridge(featureType1) && allowsBridge(featureType2)) {
57510                     if (hasTag(tags1, 'bridge') && !hasTag(tags2, 'bridge')) { return true; }
57511                     if (!hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge')) { return true; }
57512                     // crossing bridges must use different layers
57513                     if (hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge') && layer1 !== layer2) { return true; }
57514                 } else if (allowsBridge(featureType1) && hasTag(tags1, 'bridge')) { return true; }
57515                 else if (allowsBridge(featureType2) && hasTag(tags2, 'bridge')) { return true; }
57516
57517                 if (allowsTunnel(featureType1) && allowsTunnel(featureType2)) {
57518                     if (hasTag(tags1, 'tunnel') && !hasTag(tags2, 'tunnel')) { return true; }
57519                     if (!hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel')) { return true; }
57520                     // crossing tunnels must use different layers
57521                     if (hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel') && layer1 !== layer2) { return true; }
57522                 } else if (allowsTunnel(featureType1) && hasTag(tags1, 'tunnel')) { return true; }
57523                 else if (allowsTunnel(featureType2) && hasTag(tags2, 'tunnel')) { return true; }
57524
57525                 // don't flag crossing waterways and pier/highways
57526                 if (featureType1 === 'waterway' && featureType2 === 'highway' && tags2.man_made === 'pier') { return true; }
57527                 if (featureType2 === 'waterway' && featureType1 === 'highway' && tags1.man_made === 'pier') { return true; }
57528
57529                 if (featureType1 === 'building' || featureType2 === 'building') {
57530                     // for building crossings, different layers are enough
57531                     if (layer1 !== layer2) { return true; }
57532                 }
57533                 return false;
57534             }
57535
57536
57537             // highway values for which we shouldn't recommend connecting to waterways
57538             var highwaysDisallowingFords = {
57539                 motorway: true, motorway_link: true, trunk: true, trunk_link: true,
57540                 primary: true, primary_link: true, secondary: true, secondary_link: true
57541             };
57542             var nonCrossingHighways = { track: true };
57543
57544             function tagsForConnectionNodeIfAllowed(entity1, entity2, graph) {
57545                 var featureType1 = getFeatureType(entity1, graph);
57546                 var featureType2 = getFeatureType(entity2, graph);
57547
57548                 var geometry1 = entity1.geometry(graph);
57549                 var geometry2 = entity2.geometry(graph);
57550                 var bothLines = geometry1 === 'line' && geometry2 === 'line';
57551
57552                 if (featureType1 === featureType2) {
57553                     if (featureType1 === 'highway') {
57554                         var entity1IsPath = osmPathHighwayTagValues[entity1.tags.highway];
57555                         var entity2IsPath = osmPathHighwayTagValues[entity2.tags.highway];
57556                         if ((entity1IsPath || entity2IsPath) && entity1IsPath !== entity2IsPath) {
57557                             // one feature is a path but not both
57558
57559                             var roadFeature = entity1IsPath ? entity2 : entity1;
57560                             if (nonCrossingHighways[roadFeature.tags.highway]) {
57561                                 // don't mark path connections with certain roads as crossings
57562                                 return {};
57563                             }
57564                             var pathFeature = entity1IsPath ? entity1 : entity2;
57565                             if (['marked', 'unmarked'].indexOf(pathFeature.tags.crossing) !== -1) {
57566                                 // if the path is a crossing, match the crossing type
57567                                 return bothLines ? { highway: 'crossing', crossing: pathFeature.tags.crossing } : {};
57568                             }
57569                             // don't add a `crossing` subtag to ambiguous crossings
57570                             return bothLines ? { highway: 'crossing' } : {};
57571                         }
57572                         return {};
57573                     }
57574                     if (featureType1 === 'waterway') { return {}; }
57575                     if (featureType1 === 'railway') { return {}; }
57576
57577                 } else {
57578                     var featureTypes = [featureType1, featureType2];
57579                     if (featureTypes.indexOf('highway') !== -1) {
57580                         if (featureTypes.indexOf('railway') !== -1) {
57581                             if (osmPathHighwayTagValues[entity1.tags.highway] ||
57582                                 osmPathHighwayTagValues[entity2.tags.highway]) {
57583                                 // path-rail connections use this tag
57584                                 return bothLines ? { railway: 'crossing' } : {};
57585                             } else {
57586                                 // road-rail connections use this tag
57587                                 return bothLines ? { railway: 'level_crossing' } : {};
57588                             }
57589                         }
57590
57591                         if (featureTypes.indexOf('waterway') !== -1) {
57592                             // do not allow fords on structures
57593                             if (hasTag(entity1.tags, 'tunnel') && hasTag(entity2.tags, 'tunnel')) { return null; }
57594                             if (hasTag(entity1.tags, 'bridge') && hasTag(entity2.tags, 'bridge')) { return null; }
57595
57596                             if (highwaysDisallowingFords[entity1.tags.highway] ||
57597                                 highwaysDisallowingFords[entity2.tags.highway]) {
57598                                 // do not allow fords on major highways
57599                                 return null;
57600                             }
57601                             return bothLines ? { ford: 'yes' } : {};
57602                         }
57603                     }
57604                 }
57605                 return null;
57606             }
57607
57608
57609             function findCrossingsByWay(way1, graph, tree) {
57610                 var edgeCrossInfos = [];
57611                 if (way1.type !== 'way') { return edgeCrossInfos; }
57612
57613                 var way1FeatureType = getFeatureTypeForCrossingCheck(way1, graph);
57614                 if (way1FeatureType === null) { return edgeCrossInfos; }
57615
57616                 var checkedSingleCrossingWays = {};
57617
57618                 // declare vars ahead of time to reduce garbage collection
57619                 var i, j;
57620                 var extent;
57621                 var n1, n2, nA, nB, nAId, nBId;
57622                 var segment1, segment2;
57623                 var oneOnly;
57624                 var segmentInfos, segment2Info, way2, way2FeatureType;
57625                 var way1Nodes = graph.childNodes(way1);
57626                 var comparedWays = {};
57627                 for (i = 0; i < way1Nodes.length - 1; i++) {
57628                     n1 = way1Nodes[i];
57629                     n2 = way1Nodes[i + 1];
57630                     extent = geoExtent([
57631                         [
57632                             Math.min(n1.loc[0], n2.loc[0]),
57633                             Math.min(n1.loc[1], n2.loc[1])
57634                         ],
57635                         [
57636                             Math.max(n1.loc[0], n2.loc[0]),
57637                             Math.max(n1.loc[1], n2.loc[1])
57638                         ]
57639                     ]);
57640
57641                     // Optimize by only checking overlapping segments, not every segment
57642                     // of overlapping ways
57643                     segmentInfos = tree.waySegments(extent, graph);
57644
57645                     for (j = 0; j < segmentInfos.length; j++) {
57646                         segment2Info = segmentInfos[j];
57647
57648                         // don't check for self-intersection in this validation
57649                         if (segment2Info.wayId === way1.id) { continue; }
57650
57651                         // skip if this way was already checked and only one issue is needed
57652                         if (checkedSingleCrossingWays[segment2Info.wayId]) { continue; }
57653
57654                         // mark this way as checked even if there are no crossings
57655                         comparedWays[segment2Info.wayId] = true;
57656
57657                         way2 = graph.hasEntity(segment2Info.wayId);
57658                         if (!way2) { continue; }
57659
57660                         // only check crossing highway, waterway, building, and railway
57661                         way2FeatureType = getFeatureTypeForCrossingCheck(way2, graph);
57662                         if (way2FeatureType === null ||
57663                             isLegitCrossing(way1, way1FeatureType, way2, way2FeatureType)) {
57664                             continue;
57665                         }
57666
57667                         // create only one issue for building crossings
57668                         oneOnly = way1FeatureType === 'building' || way2FeatureType === 'building';
57669
57670                         nAId = segment2Info.nodes[0];
57671                         nBId = segment2Info.nodes[1];
57672                         if (nAId === n1.id || nAId === n2.id ||
57673                             nBId === n1.id || nBId === n2.id) {
57674                             // n1 or n2 is a connection node; skip
57675                             continue;
57676                         }
57677                         nA = graph.hasEntity(nAId);
57678                         if (!nA) { continue; }
57679                         nB = graph.hasEntity(nBId);
57680                         if (!nB) { continue; }
57681
57682                         segment1 = [n1.loc, n2.loc];
57683                         segment2 = [nA.loc, nB.loc];
57684                         var point = geoLineIntersection(segment1, segment2);
57685                         if (point) {
57686                             edgeCrossInfos.push({
57687                                 wayInfos: [
57688                                     {
57689                                         way: way1,
57690                                         featureType: way1FeatureType,
57691                                         edge: [n1.id, n2.id]
57692                                     },
57693                                     {
57694                                         way: way2,
57695                                         featureType: way2FeatureType,
57696                                         edge: [nA.id, nB.id]
57697                                     }
57698                                 ],
57699                                 crossPoint: point
57700                             });
57701                             if (oneOnly) {
57702                                 checkedSingleCrossingWays[way2.id] = true;
57703                                 break;
57704                             }
57705                         }
57706                     }
57707                 }
57708                 return edgeCrossInfos;
57709             }
57710
57711
57712             function waysToCheck(entity, graph) {
57713                 var featureType = getFeatureType(entity, graph);
57714                 if (!featureType) { return []; }
57715
57716                 if (entity.type === 'way') {
57717                     return [entity];
57718                 } else if (entity.type === 'relation') {
57719                     return entity.members.reduce(function(array, member) {
57720                         if (member.type === 'way' &&
57721                             // only look at geometry ways
57722                             (!member.role || member.role === 'outer' || member.role === 'inner')) {
57723                             var entity = graph.hasEntity(member.id);
57724                             // don't add duplicates
57725                             if (entity && array.indexOf(entity) === -1) {
57726                                 array.push(entity);
57727                             }
57728                         }
57729                         return array;
57730                     }, []);
57731                 }
57732                 return [];
57733             }
57734
57735
57736             var validation = function checkCrossingWays(entity, graph) {
57737
57738                 var tree = context.history().tree();
57739
57740                 var ways = waysToCheck(entity, graph);
57741
57742                 var issues = [];
57743                 // declare these here to reduce garbage collection
57744                 var wayIndex, crossingIndex, crossings;
57745                 for (wayIndex in ways) {
57746                     crossings = findCrossingsByWay(ways[wayIndex], graph, tree);
57747                     for (crossingIndex in crossings) {
57748                         issues.push(createIssue(crossings[crossingIndex], graph));
57749                     }
57750                 }
57751                 return issues;
57752             };
57753
57754
57755             function createIssue(crossing, graph) {
57756
57757                 // use the entities with the tags that define the feature type
57758                 crossing.wayInfos.sort(function(way1Info, way2Info) {
57759                     var type1 = way1Info.featureType;
57760                     var type2 = way2Info.featureType;
57761                     if (type1 === type2) {
57762                         return utilDisplayLabel(way1Info.way, graph) > utilDisplayLabel(way2Info.way, graph);
57763                     } else if (type1 === 'waterway') {
57764                         return true;
57765                     } else if (type2 === 'waterway') {
57766                         return false;
57767                     }
57768                     return type1 < type2;
57769                 });
57770                 var entities = crossing.wayInfos.map(function(wayInfo) {
57771                     return getFeatureWithFeatureTypeTagsForWay(wayInfo.way, graph);
57772                 });
57773                 var edges = [crossing.wayInfos[0].edge, crossing.wayInfos[1].edge];
57774                 var featureTypes = [crossing.wayInfos[0].featureType, crossing.wayInfos[1].featureType];
57775
57776                 var connectionTags = tagsForConnectionNodeIfAllowed(entities[0], entities[1], graph);
57777
57778                 var featureType1 = crossing.wayInfos[0].featureType;
57779                 var featureType2 = crossing.wayInfos[1].featureType;
57780
57781                 var isCrossingIndoors = taggedAsIndoor(entities[0].tags) && taggedAsIndoor(entities[1].tags);
57782                 var isCrossingTunnels = allowsTunnel(featureType1) && hasTag(entities[0].tags, 'tunnel') &&
57783                                         allowsTunnel(featureType2) && hasTag(entities[1].tags, 'tunnel');
57784                 var isCrossingBridges = allowsBridge(featureType1) && hasTag(entities[0].tags, 'bridge') &&
57785                                         allowsBridge(featureType2) && hasTag(entities[1].tags, 'bridge');
57786
57787                 var subtype = [featureType1, featureType2].sort().join('-');
57788
57789                 var crossingTypeID = subtype;
57790
57791                 if (isCrossingIndoors) {
57792                     crossingTypeID = 'indoor-indoor';
57793                 } else if (isCrossingTunnels) {
57794                     crossingTypeID = 'tunnel-tunnel';
57795                 } else if (isCrossingBridges) {
57796                     crossingTypeID = 'bridge-bridge';
57797                 }
57798                 if (connectionTags && (isCrossingIndoors || isCrossingTunnels || isCrossingBridges)) {
57799                     crossingTypeID += '_connectable';
57800                 }
57801
57802                 return new validationIssue({
57803                     type: type,
57804                     subtype: subtype,
57805                     severity: 'warning',
57806                     message: function(context) {
57807                         var graph = context.graph();
57808                         var entity1 = graph.hasEntity(this.entityIds[0]),
57809                             entity2 = graph.hasEntity(this.entityIds[1]);
57810                         return (entity1 && entity2) ? _t('issues.crossing_ways.message', {
57811                             feature: utilDisplayLabel(entity1, graph),
57812                             feature2: utilDisplayLabel(entity2, graph)
57813                         }) : '';
57814                     },
57815                     reference: showReference,
57816                     entityIds: entities.map(function(entity) {
57817                         return entity.id;
57818                     }),
57819                     data: {
57820                         edges: edges,
57821                         featureTypes: featureTypes,
57822                         connectionTags: connectionTags
57823                     },
57824                     // differentiate based on the loc since two ways can cross multiple times
57825                     hash: crossing.crossPoint.toString() +
57826                         // if the edges change then so does the fix
57827                         edges.slice().sort(function(edge1, edge2) {
57828                             // order to assure hash is deterministic
57829                             return edge1[0] < edge2[0] ? -1 : 1;
57830                         }).toString() +
57831                         // ensure the correct connection tags are added in the fix
57832                         JSON.stringify(connectionTags),
57833                     loc: crossing.crossPoint,
57834                     dynamicFixes: function(context) {
57835                         var mode = context.mode();
57836                         if (!mode || mode.id !== 'select' || mode.selectedIDs().length !== 1) { return []; }
57837
57838                         var selectedIndex = this.entityIds[0] === mode.selectedIDs()[0] ? 0 : 1;
57839                         var selectedFeatureType = this.data.featureTypes[selectedIndex];
57840                         var otherFeatureType = this.data.featureTypes[selectedIndex === 0 ? 1 : 0];
57841
57842                         var fixes = [];
57843
57844                         if (connectionTags) {
57845                             fixes.push(makeConnectWaysFix(this.data.connectionTags));
57846                         }
57847
57848                         if (isCrossingIndoors) {
57849                             fixes.push(new validationIssueFix({
57850                                 icon: 'iD-icon-layers',
57851                                 title: _t('issues.fix.use_different_levels.title')
57852                             }));
57853                         } else if (isCrossingTunnels ||
57854                             isCrossingBridges ||
57855                             featureType1 === 'building' ||
57856                             featureType2 === 'building')  {
57857
57858                             fixes.push(makeChangeLayerFix('higher'));
57859                             fixes.push(makeChangeLayerFix('lower'));
57860
57861                         // can only add bridge/tunnel if both features are lines
57862                         } else if (context.graph().geometry(this.entityIds[0]) === 'line' &&
57863                             context.graph().geometry(this.entityIds[1]) === 'line') {
57864
57865                             // don't recommend adding bridges to waterways since they're uncommmon
57866                             if (allowsBridge(selectedFeatureType) && selectedFeatureType !== 'waterway') {
57867                                 fixes.push(makeAddBridgeOrTunnelFix('add_a_bridge', 'temaki-bridge', 'bridge'));
57868                             }
57869
57870                             // don't recommend adding tunnels under waterways since they're uncommmon
57871                             var skipTunnelFix = otherFeatureType === 'waterway' && selectedFeatureType !== 'waterway';
57872                             if (allowsTunnel(selectedFeatureType) && !skipTunnelFix) {
57873                                 fixes.push(makeAddBridgeOrTunnelFix('add_a_tunnel', 'temaki-tunnel', 'tunnel'));
57874                             }
57875                         }
57876
57877                         // repositioning the features is always an option
57878                         fixes.push(new validationIssueFix({
57879                             icon: 'iD-operation-move',
57880                             title: _t('issues.fix.reposition_features.title')
57881                         }));
57882
57883                         return fixes;
57884                     }
57885                 });
57886
57887                 function showReference(selection) {
57888                     selection.selectAll('.issue-reference')
57889                         .data([0])
57890                         .enter()
57891                         .append('div')
57892                         .attr('class', 'issue-reference')
57893                         .text(_t('issues.crossing_ways.' + crossingTypeID + '.reference'));
57894                 }
57895             }
57896
57897             function makeAddBridgeOrTunnelFix(fixTitleID, iconName, bridgeOrTunnel){
57898                 return new validationIssueFix({
57899                     icon: iconName,
57900                     title: _t('issues.fix.' + fixTitleID + '.title'),
57901                     onClick: function(context) {
57902                         var mode = context.mode();
57903                         if (!mode || mode.id !== 'select') { return; }
57904
57905                         var selectedIDs = mode.selectedIDs();
57906                         if (selectedIDs.length !== 1) { return; }
57907
57908                         var selectedWayID = selectedIDs[0];
57909                         if (!context.hasEntity(selectedWayID)) { return; }
57910
57911                         var resultWayIDs = [selectedWayID];
57912
57913                         var edge, crossedEdge, crossedWayID;
57914                         if (this.issue.entityIds[0] === selectedWayID) {
57915                             edge = this.issue.data.edges[0];
57916                             crossedEdge = this.issue.data.edges[1];
57917                             crossedWayID = this.issue.entityIds[1];
57918                         } else {
57919                             edge = this.issue.data.edges[1];
57920                             crossedEdge = this.issue.data.edges[0];
57921                             crossedWayID = this.issue.entityIds[0];
57922                         }
57923
57924                         var crossingLoc = this.issue.loc;
57925
57926                         var projection = context.projection;
57927
57928                         var action = function actionAddStructure(graph) {
57929
57930                             var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
57931
57932                             var crossedWay = graph.hasEntity(crossedWayID);
57933                             // use the explicit width of the crossed feature as the structure length, if available
57934                             var structLengthMeters = crossedWay && crossedWay.tags.width && parseFloat(crossedWay.tags.width);
57935                             if (!structLengthMeters) {
57936                                 // if no explicit width is set, approximate the width based on the tags
57937                                 structLengthMeters = crossedWay && crossedWay.impliedLineWidthMeters();
57938                             }
57939                             if (structLengthMeters) {
57940                                 if (getFeatureType(crossedWay, graph) === 'railway') {
57941                                     // bridges over railways are generally much longer than the rail bed itself, compensate
57942                                     structLengthMeters *= 2;
57943                                 }
57944                             } else {
57945                                 // should ideally never land here since all rail/water/road tags should have an implied width
57946                                 structLengthMeters = 8;
57947                             }
57948
57949                             var a1 = geoAngle(edgeNodes[0], edgeNodes[1], projection) + Math.PI;
57950                             var a2 = geoAngle(graph.entity(crossedEdge[0]), graph.entity(crossedEdge[1]), projection) + Math.PI;
57951                             var crossingAngle = Math.max(a1, a2) - Math.min(a1, a2);
57952                             if (crossingAngle > Math.PI) { crossingAngle -= Math.PI; }
57953                             // lengthen the structure to account for the angle of the crossing
57954                             structLengthMeters = ((structLengthMeters / 2) / Math.sin(crossingAngle)) * 2;
57955
57956                             // add padding since the structure must extend past the edges of the crossed feature
57957                             structLengthMeters += 4;
57958
57959                             // clamp the length to a reasonable range
57960                             structLengthMeters = Math.min(Math.max(structLengthMeters, 4), 50);
57961
57962                             function geomToProj(geoPoint) {
57963                                 return [
57964                                     geoLonToMeters(geoPoint[0], geoPoint[1]),
57965                                     geoLatToMeters(geoPoint[1])
57966                                 ];
57967                             }
57968                             function projToGeom(projPoint) {
57969                                 var lat = geoMetersToLat(projPoint[1]);
57970                                 return [
57971                                     geoMetersToLon(projPoint[0], lat),
57972                                     lat
57973                                 ];
57974                             }
57975
57976                             var projEdgeNode1 = geomToProj(edgeNodes[0].loc);
57977                             var projEdgeNode2 = geomToProj(edgeNodes[1].loc);
57978
57979                             var projectedAngle = geoVecAngle(projEdgeNode1, projEdgeNode2);
57980
57981                             var projectedCrossingLoc = geomToProj(crossingLoc);
57982                             var linearToSphericalMetersRatio = geoVecLength(projEdgeNode1, projEdgeNode2) /
57983                                 geoSphericalDistance(edgeNodes[0].loc, edgeNodes[1].loc);
57984
57985                             function locSphericalDistanceFromCrossingLoc(angle, distanceMeters) {
57986                                 var lengthSphericalMeters = distanceMeters * linearToSphericalMetersRatio;
57987                                 return projToGeom([
57988                                     projectedCrossingLoc[0] + Math.cos(angle) * lengthSphericalMeters,
57989                                     projectedCrossingLoc[1] + Math.sin(angle) * lengthSphericalMeters
57990                                 ]);
57991                             }
57992
57993                             var endpointLocGetter1 = function(lengthMeters) {
57994                                 return locSphericalDistanceFromCrossingLoc(projectedAngle, lengthMeters);
57995                             };
57996                             var endpointLocGetter2 = function(lengthMeters) {
57997                                 return locSphericalDistanceFromCrossingLoc(projectedAngle + Math.PI, lengthMeters);
57998                             };
57999
58000                             // avoid creating very short edges from splitting too close to another node
58001                             var minEdgeLengthMeters = 0.55;
58002
58003                             // decide where to bound the structure along the way, splitting as necessary
58004                             function determineEndpoint(edge, endNode, locGetter) {
58005                                 var newNode;
58006
58007                                 var idealLengthMeters = structLengthMeters / 2;
58008
58009                                 // distance between the crossing location and the end of the edge,
58010                                 // the maximum length of this side of the structure
58011                                 var crossingToEdgeEndDistance = geoSphericalDistance(crossingLoc, endNode.loc);
58012
58013                                 if (crossingToEdgeEndDistance - idealLengthMeters > minEdgeLengthMeters) {
58014                                     // the edge is long enough to insert a new node
58015
58016                                     // the loc that would result in the full expected length
58017                                     var idealNodeLoc = locGetter(idealLengthMeters);
58018
58019                                     newNode = osmNode();
58020                                     graph = actionAddMidpoint({ loc: idealNodeLoc, edge: edge }, newNode)(graph);
58021
58022                                 } else {
58023                                     var edgeCount = 0;
58024                                     endNode.parentIntersectionWays(graph).forEach(function(way) {
58025                                         way.nodes.forEach(function(nodeID) {
58026                                             if (nodeID === endNode.id) {
58027                                                 if ((endNode.id === way.first() && endNode.id !== way.last()) ||
58028                                                     (endNode.id === way.last() && endNode.id !== way.first())) {
58029                                                     edgeCount += 1;
58030                                                 } else {
58031                                                     edgeCount += 2;
58032                                                 }
58033                                             }
58034                                         });
58035                                     });
58036
58037                                     if (edgeCount >= 3) {
58038                                         // the end node is a junction, try to leave a segment
58039                                         // between it and the structure - #7202
58040
58041                                         var insetLength = crossingToEdgeEndDistance - minEdgeLengthMeters;
58042                                         if (insetLength > minEdgeLengthMeters) {
58043                                             var insetNodeLoc = locGetter(insetLength);
58044                                             newNode = osmNode();
58045                                             graph = actionAddMidpoint({ loc: insetNodeLoc, edge: edge }, newNode)(graph);
58046                                         }
58047                                     }
58048                                 }
58049
58050                                 // if the edge is too short to subdivide as desired, then
58051                                 // just bound the structure at the existing end node
58052                                 if (!newNode) { newNode = endNode; }
58053
58054                                 var splitAction = actionSplit(newNode.id)
58055                                     .limitWays(resultWayIDs); // only split selected or created ways
58056
58057                                 // do the split
58058                                 graph = splitAction(graph);
58059                                 if (splitAction.getCreatedWayIDs().length) {
58060                                     resultWayIDs.push(splitAction.getCreatedWayIDs()[0]);
58061                                 }
58062
58063                                 return newNode;
58064                             }
58065
58066                             var structEndNode1 = determineEndpoint(edge, edgeNodes[1], endpointLocGetter1);
58067                             var structEndNode2 = determineEndpoint([edgeNodes[0].id, structEndNode1.id], edgeNodes[0], endpointLocGetter2);
58068
58069                             var structureWay = resultWayIDs.map(function(id) {
58070                                 return graph.entity(id);
58071                             }).find(function(way) {
58072                                 return way.nodes.indexOf(structEndNode1.id) !== -1 &&
58073                                     way.nodes.indexOf(structEndNode2.id) !== -1;
58074                             });
58075
58076                             var tags = Object.assign({}, structureWay.tags); // copy tags
58077                             if (bridgeOrTunnel === 'bridge'){
58078                                 tags.bridge = 'yes';
58079                                 tags.layer = '1';
58080                             } else {
58081                                 var tunnelValue = 'yes';
58082                                 if (getFeatureType(structureWay, graph) === 'waterway') {
58083                                     // use `tunnel=culvert` for waterways by default
58084                                     tunnelValue = 'culvert';
58085                                 }
58086                                 tags.tunnel = tunnelValue;
58087                                 tags.layer = '-1';
58088                             }
58089                             // apply the structure tags to the way
58090                             graph = actionChangeTags(structureWay.id, tags)(graph);
58091                             return graph;
58092                         };
58093
58094                         context.perform(action, _t('issues.fix.' + fixTitleID + '.annotation'));
58095                         context.enter(modeSelect(context, resultWayIDs));
58096                     }
58097                 });
58098             }
58099
58100             function makeConnectWaysFix(connectionTags) {
58101
58102                 var fixTitleID = 'connect_features';
58103                 if (connectionTags.ford) {
58104                     fixTitleID = 'connect_using_ford';
58105                 }
58106
58107                 return new validationIssueFix({
58108                     icon: 'iD-icon-crossing',
58109                     title: _t('issues.fix.' + fixTitleID + '.title'),
58110                     onClick: function(context) {
58111                         var loc = this.issue.loc;
58112                         var connectionTags = this.issue.data.connectionTags;
58113                         var edges = this.issue.data.edges;
58114
58115                         context.perform(
58116                             function actionConnectCrossingWays(graph) {
58117                                 // create the new node for the points
58118                                 var node = osmNode({ loc: loc, tags: connectionTags });
58119                                 graph = graph.replace(node);
58120
58121                                 var nodesToMerge = [node.id];
58122                                 var mergeThresholdInMeters = 0.75;
58123
58124                                 edges.forEach(function(edge) {
58125                                     var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
58126                                     var closestNodeInfo = geoSphericalClosestNode(edgeNodes, loc);
58127                                     // if there is already a point nearby, use that
58128                                     if (closestNodeInfo.distance < mergeThresholdInMeters) {
58129                                         nodesToMerge.push(closestNodeInfo.node.id);
58130                                     // else add the new node to the way
58131                                     } else {
58132                                         graph = actionAddMidpoint({loc: loc, edge: edge}, node)(graph);
58133                                     }
58134                                 });
58135
58136                                 if (nodesToMerge.length > 1) {
58137                                     // if we're using nearby nodes, merge them with the new node
58138                                     graph = actionMergeNodes(nodesToMerge, loc)(graph);
58139                                 }
58140
58141                                 return graph;
58142                             },
58143                             _t('issues.fix.connect_crossing_features.annotation')
58144                         );
58145                     }
58146                 });
58147             }
58148
58149             function makeChangeLayerFix(higherOrLower) {
58150                 return new validationIssueFix({
58151                     icon: 'iD-icon-' + (higherOrLower === 'higher' ? 'up' : 'down'),
58152                     title: _t('issues.fix.tag_this_as_' + higherOrLower + '.title'),
58153                     onClick: function(context) {
58154
58155                         var mode = context.mode();
58156                         if (!mode || mode.id !== 'select') { return; }
58157
58158                         var selectedIDs = mode.selectedIDs();
58159                         if (selectedIDs.length !== 1) { return; }
58160
58161                         var selectedID = selectedIDs[0];
58162                         if (!this.issue.entityIds.some(function(entityId) {
58163                             return entityId === selectedID;
58164                         })) { return; }
58165
58166                         var entity = context.hasEntity(selectedID);
58167                         if (!entity) { return; }
58168
58169                         var tags = Object.assign({}, entity.tags);   // shallow copy
58170                         var layer = tags.layer && Number(tags.layer);
58171                         if (layer && !isNaN(layer)) {
58172                             if (higherOrLower === 'higher') {
58173                                 layer += 1;
58174                             } else {
58175                                 layer -= 1;
58176                             }
58177                         } else {
58178                             if (higherOrLower === 'higher') {
58179                                 layer = 1;
58180                             } else {
58181                                 layer = -1;
58182                             }
58183                         }
58184                         tags.layer = layer.toString();
58185                         context.perform(
58186                             actionChangeTags(entity.id, tags),
58187                             _t('operations.change_tags.annotation')
58188                         );
58189                     }
58190                 });
58191             }
58192
58193             validation.type = type;
58194
58195             return validation;
58196         }
58197
58198         function validationDisconnectedWay() {
58199             var type = 'disconnected_way';
58200
58201             function isTaggedAsHighway(entity) {
58202                 return osmRoutableHighwayTagValues[entity.tags.highway];
58203             }
58204
58205             var validation = function checkDisconnectedWay(entity, graph) {
58206
58207                 var routingIslandWays = routingIslandForEntity(entity);
58208                 if (!routingIslandWays) { return []; }
58209
58210                 return [new validationIssue({
58211                     type: type,
58212                     subtype: 'highway',
58213                     severity: 'warning',
58214                     message: function(context) {
58215                         if (this.entityIds.length === 1) {
58216                             var entity = context.hasEntity(this.entityIds[0]);
58217                             return entity ? _t('issues.disconnected_way.highway.message', { highway: utilDisplayLabel(entity, context.graph()) }) : '';
58218                         }
58219                         return _t('issues.disconnected_way.routable.message.multiple', { count: this.entityIds.length.toString() });
58220                     },
58221                     reference: showReference,
58222                     entityIds: Array.from(routingIslandWays).map(function(way) { return way.id; }),
58223                     dynamicFixes: makeFixes
58224                 })];
58225
58226
58227                 function makeFixes(context) {
58228
58229                     var fixes = [];
58230
58231                     var singleEntity = this.entityIds.length === 1 && context.hasEntity(this.entityIds[0]);
58232
58233                     if (singleEntity) {
58234
58235                         if (singleEntity.type === 'way' && !singleEntity.isClosed()) {
58236
58237                             var textDirection = _mainLocalizer.textDirection();
58238
58239                             var startFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.first(), 'start');
58240                             if (startFix) { fixes.push(startFix); }
58241
58242                             var endFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.last(), 'end');
58243                             if (endFix) { fixes.push(endFix); }
58244                         }
58245                         if (!fixes.length) {
58246                             fixes.push(new validationIssueFix({
58247                                 title: _t('issues.fix.connect_feature.title')
58248                             }));
58249                         }
58250
58251                         fixes.push(new validationIssueFix({
58252                             icon: 'iD-operation-delete',
58253                             title: _t('issues.fix.delete_feature.title'),
58254                             entityIds: [singleEntity.id],
58255                             onClick: function(context) {
58256                                 var id = this.issue.entityIds[0];
58257                                 var operation = operationDelete(context, [id]);
58258                                 if (!operation.disabled()) {
58259                                     operation();
58260                                 }
58261                             }
58262                         }));
58263                     } else {
58264                         fixes.push(new validationIssueFix({
58265                             title: _t('issues.fix.connect_features.title')
58266                         }));
58267                     }
58268
58269                     return fixes;
58270                 }
58271
58272
58273                 function showReference(selection) {
58274                     selection.selectAll('.issue-reference')
58275                         .data([0])
58276                         .enter()
58277                         .append('div')
58278                         .attr('class', 'issue-reference')
58279                         .text(_t('issues.disconnected_way.routable.reference'));
58280                 }
58281
58282                 function routingIslandForEntity(entity) {
58283
58284                     var routingIsland = new Set();  // the interconnected routable features
58285                     var waysToCheck = [];           // the queue of remaining routable ways to traverse
58286
58287                     function queueParentWays(node) {
58288                         graph.parentWays(node).forEach(function(parentWay) {
58289                             if (!routingIsland.has(parentWay) &&    // only check each feature once
58290                                 isRoutableWay(parentWay, false)) {  // only check routable features
58291                                 routingIsland.add(parentWay);
58292                                 waysToCheck.push(parentWay);
58293                             }
58294                         });
58295                     }
58296
58297                     if (entity.type === 'way' && isRoutableWay(entity, true)) {
58298
58299                         routingIsland.add(entity);
58300                         waysToCheck.push(entity);
58301
58302                     } else if (entity.type === 'node' && isRoutableNode(entity)) {
58303
58304                         routingIsland.add(entity);
58305                         queueParentWays(entity);
58306
58307                     } else {
58308                         // this feature isn't routable, cannot be a routing island
58309                         return null;
58310                     }
58311
58312                     while (waysToCheck.length) {
58313                         var wayToCheck = waysToCheck.pop();
58314                         var childNodes = graph.childNodes(wayToCheck);
58315                         for (var i in childNodes) {
58316                             var vertex = childNodes[i];
58317
58318                             if (isConnectedVertex(vertex)) {
58319                                 // found a link to the wider network, not a routing island
58320                                 return null;
58321                             }
58322
58323                             if (isRoutableNode(vertex)) {
58324                                 routingIsland.add(vertex);
58325                             }
58326
58327                             queueParentWays(vertex);
58328                         }
58329                     }
58330
58331                     // no network link found, this is a routing island, return its members
58332                     return routingIsland;
58333                 }
58334
58335                 function isConnectedVertex(vertex) {
58336                     // assume ways overlapping unloaded tiles are connected to the wider road network  - #5938
58337                     var osm = services.osm;
58338                     if (osm && !osm.isDataLoaded(vertex.loc)) { return true; }
58339
58340                     // entrances are considered connected
58341                     if (vertex.tags.entrance &&
58342                         vertex.tags.entrance !== 'no') { return true; }
58343                     if (vertex.tags.amenity === 'parking_entrance') { return true; }
58344
58345                     return false;
58346                 }
58347
58348                 function isRoutableNode(node) {
58349                     // treat elevators as distinct features in the highway network
58350                     if (node.tags.highway === 'elevator') { return true; }
58351                     return false;
58352                 }
58353
58354                 function isRoutableWay(way, ignoreInnerWays) {
58355                     if (isTaggedAsHighway(way) || way.tags.route === 'ferry') { return true; }
58356
58357                     return graph.parentRelations(way).some(function(parentRelation) {
58358                         if (parentRelation.tags.type === 'route' &&
58359                             parentRelation.tags.route === 'ferry') { return true; }
58360
58361                         if (parentRelation.isMultipolygon() &&
58362                             isTaggedAsHighway(parentRelation) &&
58363                             (!ignoreInnerWays || parentRelation.memberById(way.id).role !== 'inner')) { return true; }
58364                     });
58365                 }
58366
58367                 function makeContinueDrawingFixIfAllowed(textDirection, vertexID, whichEnd) {
58368                     var vertex = graph.hasEntity(vertexID);
58369                     if (!vertex || vertex.tags.noexit === 'yes') { return null; }
58370
58371                     var useLeftContinue = (whichEnd === 'start' && textDirection === 'ltr') ||
58372                         (whichEnd === 'end' && textDirection === 'rtl');
58373
58374                     return new validationIssueFix({
58375                         icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
58376                         title: _t('issues.fix.continue_from_' + whichEnd + '.title'),
58377                         entityIds: [vertexID],
58378                         onClick: function(context) {
58379                             var wayId = this.issue.entityIds[0];
58380                             var way = context.hasEntity(wayId);
58381                             var vertexId = this.entityIds[0];
58382                             var vertex = context.hasEntity(vertexId);
58383
58384                             if (!way || !vertex) { return; }
58385
58386                             // make sure the vertex is actually visible and editable
58387                             var map = context.map();
58388                             if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
58389                                 map.zoomToEase(vertex);
58390                             }
58391
58392                             context.enter(
58393                                 modeDrawLine(context, wayId, context.graph(), 'line', way.affix(vertexId), true)
58394                             );
58395                         }
58396                     });
58397                 }
58398
58399             };
58400
58401             validation.type = type;
58402
58403             return validation;
58404         }
58405
58406         function validationFormatting() {
58407             var type = 'invalid_format';
58408
58409             var validation = function(entity) {
58410                 var issues = [];
58411
58412                 function isValidEmail(email) {
58413                     // Emails in OSM are going to be official so they should be pretty simple
58414                     // Using negated lists to better support all possible unicode characters (#6494)
58415                     var valid_email = /^[^\(\)\\,":;<>@\[\]]+@[^\(\)\\,":;<>@\[\]\.]+(?:\.[a-z0-9-]+)*$/i;
58416
58417                     // An empty value is also acceptable
58418                     return (!email || valid_email.test(email));
58419                 }
58420                 /*
58421                 function isSchemePresent(url) {
58422                     var valid_scheme = /^https?:\/\//i;
58423                     return (!url || valid_scheme.test(url));
58424                 }
58425                 */
58426                 function showReferenceEmail(selection) {
58427                     selection.selectAll('.issue-reference')
58428                         .data([0])
58429                         .enter()
58430                         .append('div')
58431                         .attr('class', 'issue-reference')
58432                         .text(_t('issues.invalid_format.email.reference'));
58433                 }
58434                 /*
58435                 function showReferenceWebsite(selection) {
58436                     selection.selectAll('.issue-reference')
58437                         .data([0])
58438                         .enter()
58439                         .append('div')
58440                         .attr('class', 'issue-reference')
58441                         .text(t('issues.invalid_format.website.reference'));
58442                 }
58443
58444                 if (entity.tags.website) {
58445                     // Multiple websites are possible
58446                     // If ever we support ES6, arrow functions make this nicer
58447                     var websites = entity.tags.website
58448                         .split(';')
58449                         .map(function(s) { return s.trim(); })
58450                         .filter(function(x) { return !isSchemePresent(x); });
58451
58452                     if (websites.length) {
58453                         issues.push(new validationIssue({
58454                             type: type,
58455                             subtype: 'website',
58456                             severity: 'warning',
58457                             message: function(context) {
58458                                 var entity = context.hasEntity(this.entityIds[0]);
58459                                 return entity ? t('issues.invalid_format.website.message' + this.data,
58460                                     { feature: utilDisplayLabel(entity, context.graph()), site: websites.join(', ') }) : '';
58461                             },
58462                             reference: showReferenceWebsite,
58463                             entityIds: [entity.id],
58464                             hash: websites.join(),
58465                             data: (websites.length > 1) ? '_multi' : ''
58466                         }));
58467                     }
58468                 }
58469                 */
58470                 if (entity.tags.email) {
58471                     // Multiple emails are possible
58472                     var emails = entity.tags.email
58473                         .split(';')
58474                         .map(function(s) { return s.trim(); })
58475                         .filter(function(x) { return !isValidEmail(x); });
58476
58477                     if (emails.length) {
58478                         issues.push(new validationIssue({
58479                             type: type,
58480                             subtype: 'email',
58481                             severity: 'warning',
58482                             message: function(context) {
58483                                 var entity = context.hasEntity(this.entityIds[0]);
58484                                 return entity ? _t('issues.invalid_format.email.message' + this.data,
58485                                     { feature: utilDisplayLabel(entity, context.graph()), email: emails.join(', ') }) : '';
58486                             },
58487                             reference: showReferenceEmail,
58488                             entityIds: [entity.id],
58489                             hash: emails.join(),
58490                             data: (emails.length > 1) ? '_multi' : ''
58491                         }));
58492                     }
58493                 }
58494
58495                 return issues;
58496             };
58497
58498             validation.type = type;
58499
58500             return validation;
58501         }
58502
58503         function validationHelpRequest(context) {
58504             var type = 'help_request';
58505
58506             var validation = function checkFixmeTag(entity) {
58507
58508                 if (!entity.tags.fixme) { return []; }
58509
58510                 // don't flag fixmes on features added by the user
58511                 if (entity.version === undefined) { return []; }
58512
58513                 if (entity.v !== undefined) {
58514                     var baseEntity = context.history().base().hasEntity(entity.id);
58515                     // don't flag fixmes added by the user on existing features
58516                     if (!baseEntity || !baseEntity.tags.fixme) { return []; }
58517                 }
58518
58519                 return [new validationIssue({
58520                     type: type,
58521                     subtype: 'fixme_tag',
58522                     severity: 'warning',
58523                     message: function(context) {
58524                         var entity = context.hasEntity(this.entityIds[0]);
58525                         return entity ? _t('issues.fixme_tag.message', { feature: utilDisplayLabel(entity, context.graph()) }) : '';
58526                     },
58527                     dynamicFixes: function() {
58528                         return [
58529                             new validationIssueFix({
58530                                 title: _t('issues.fix.address_the_concern.title')
58531                             })
58532                         ];
58533                     },
58534                     reference: showReference,
58535                     entityIds: [entity.id]
58536                 })];
58537
58538                 function showReference(selection) {
58539                     selection.selectAll('.issue-reference')
58540                         .data([0])
58541                         .enter()
58542                         .append('div')
58543                         .attr('class', 'issue-reference')
58544                         .text(_t('issues.fixme_tag.reference'));
58545                 }
58546             };
58547
58548             validation.type = type;
58549
58550             return validation;
58551         }
58552
58553         function validationImpossibleOneway() {
58554             var type = 'impossible_oneway';
58555
58556             var validation = function checkImpossibleOneway(entity, graph) {
58557
58558                 if (entity.type !== 'way' || entity.geometry(graph) !== 'line') { return []; }
58559
58560                 if (entity.isClosed()) { return []; }
58561
58562                 if (!typeForWay(entity)) { return []; }
58563
58564                 if (!isOneway(entity)) { return []; }
58565
58566                 var firstIssues = issuesForNode(entity, entity.first());
58567                 var lastIssues = issuesForNode(entity, entity.last());
58568
58569                 return firstIssues.concat(lastIssues);
58570
58571                 function typeForWay(way) {
58572                     if (way.geometry(graph) !== 'line') { return null; }
58573
58574                     if (osmRoutableHighwayTagValues[way.tags.highway]) { return 'highway'; }
58575                     if (osmFlowingWaterwayTagValues[way.tags.waterway]) { return 'waterway'; }
58576                     return null;
58577                 }
58578
58579                 function isOneway(way) {
58580                     if (way.tags.oneway === 'yes') { return true; }
58581                     if (way.tags.oneway) { return false; }
58582
58583                     for (var key in way.tags) {
58584                         if (osmOneWayTags[key] && osmOneWayTags[key][way.tags[key]]) {
58585                             return true;
58586                         }
58587                     }
58588                     return false;
58589                 }
58590
58591                 function nodeOccursMoreThanOnce(way, nodeID) {
58592                     var occurences = 0;
58593                     for (var index in way.nodes) {
58594                         if (way.nodes[index] === nodeID) {
58595                             occurences += 1;
58596                             if (occurences > 1) { return true; }
58597                         }
58598                     }
58599                     return false;
58600                 }
58601
58602                 function isConnectedViaOtherTypes(way, node) {
58603
58604                     var wayType = typeForWay(way);
58605
58606                     if (wayType === 'highway') {
58607                         // entrances are considered connected
58608                         if (node.tags.entrance && node.tags.entrance !== 'no') { return true; }
58609                         if (node.tags.amenity === 'parking_entrance') { return true; }
58610                     } else if (wayType === 'waterway') {
58611                         if (node.id === way.first()) {
58612                             // multiple waterways may start at the same spring
58613                             if (node.tags.natural === 'spring') { return true; }
58614                         } else {
58615                             // multiple waterways may end at the same drain
58616                             if (node.tags.manhole === 'drain') { return true; }
58617                         }
58618                     }
58619
58620                     return graph.parentWays(node).some(function(parentWay) {
58621                         if (parentWay.id === way.id) { return false; }
58622
58623                         if (wayType === 'highway') {
58624
58625                             // allow connections to highway areas
58626                             if (parentWay.geometry(graph) === 'area' &&
58627                                 osmRoutableHighwayTagValues[parentWay.tags.highway]) { return true; }
58628
58629                             // count connections to ferry routes as connected
58630                             if (parentWay.tags.route === 'ferry') { return true; }
58631
58632                             return graph.parentRelations(parentWay).some(function(parentRelation) {
58633                                 if (parentRelation.tags.type === 'route' &&
58634                                     parentRelation.tags.route === 'ferry') { return true; }
58635
58636                                 // allow connections to highway multipolygons
58637                                 return parentRelation.isMultipolygon() && osmRoutableHighwayTagValues[parentRelation.tags.highway];
58638                             });
58639                         } else if (wayType === 'waterway') {
58640                             // multiple waterways may start or end at a water body at the same node
58641                             if (parentWay.tags.natural === 'water' ||
58642                                 parentWay.tags.natural === 'coastline') { return true; }
58643                         }
58644                         return false;
58645                     });
58646                 }
58647
58648                 function issuesForNode(way, nodeID) {
58649
58650                     var isFirst = nodeID === way.first();
58651
58652                     var wayType = typeForWay(way);
58653
58654                     // ignore if this way is self-connected at this node
58655                     if (nodeOccursMoreThanOnce(way, nodeID)) { return []; }
58656
58657                     var osm = services.osm;
58658                     if (!osm) { return []; }
58659
58660                     var node = graph.hasEntity(nodeID);
58661
58662                     // ignore if this node or its tile are unloaded
58663                     if (!node || !osm.isDataLoaded(node.loc)) { return []; }
58664
58665                     if (isConnectedViaOtherTypes(way, node)) { return []; }
58666
58667                     var attachedWaysOfSameType = graph.parentWays(node).filter(function(parentWay) {
58668                         if (parentWay.id === way.id) { return false; }
58669                         return typeForWay(parentWay) === wayType;
58670                     });
58671
58672                     // assume it's okay for waterways to start or end disconnected for now
58673                     if (wayType === 'waterway' && attachedWaysOfSameType.length === 0) { return []; }
58674
58675                     var attachedOneways = attachedWaysOfSameType.filter(function(attachedWay) {
58676                         return isOneway(attachedWay);
58677                     });
58678
58679                     // ignore if the way is connected to some non-oneway features
58680                     if (attachedOneways.length < attachedWaysOfSameType.length) { return []; }
58681
58682                     if (attachedOneways.length) {
58683                         var connectedEndpointsOkay = attachedOneways.some(function(attachedOneway) {
58684                             if ((isFirst ? attachedOneway.first() : attachedOneway.last()) !== nodeID) { return true; }
58685                             if (nodeOccursMoreThanOnce(attachedOneway, nodeID)) { return true; }
58686                             return false;
58687                         });
58688                         if (connectedEndpointsOkay) { return []; }
58689                     }
58690
58691                     var placement = isFirst ? 'start' : 'end',
58692                         messageID = wayType + '.',
58693                         referenceID = wayType + '.';
58694
58695                     if (wayType === 'waterway') {
58696                         messageID += 'connected.' + placement;
58697                         referenceID += 'connected';
58698                     } else {
58699                         messageID += placement;
58700                         referenceID += placement;
58701                     }
58702
58703                     return [new validationIssue({
58704                         type: type,
58705                         subtype: wayType,
58706                         severity: 'warning',
58707                         message: function(context) {
58708                             var entity = context.hasEntity(this.entityIds[0]);
58709                             return entity ? _t('issues.impossible_oneway.' + messageID + '.message', {
58710                                 feature: utilDisplayLabel(entity, context.graph())
58711                             }) : '';
58712                         },
58713                         reference: getReference(referenceID),
58714                         entityIds: [way.id, node.id],
58715                         dynamicFixes: function() {
58716
58717                             var fixes = [];
58718
58719                             if (attachedOneways.length) {
58720                                 fixes.push(new validationIssueFix({
58721                                     icon: 'iD-operation-reverse',
58722                                     title: _t('issues.fix.reverse_feature.title'),
58723                                     entityIds: [way.id],
58724                                     onClick: function(context) {
58725                                         var id = this.issue.entityIds[0];
58726                                         context.perform(actionReverse(id), _t('operations.reverse.annotation'));
58727                                     }
58728                                 }));
58729                             }
58730                             if (node.tags.noexit !== 'yes') {
58731                                 var textDirection = _mainLocalizer.textDirection();
58732                                 var useLeftContinue = (isFirst && textDirection === 'ltr') ||
58733                                     (!isFirst && textDirection === 'rtl');
58734                                 fixes.push(new validationIssueFix({
58735                                     icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
58736                                     title: _t('issues.fix.continue_from_' + (isFirst ? 'start' : 'end') + '.title'),
58737                                     onClick: function(context) {
58738                                         var entityID = this.issue.entityIds[0];
58739                                         var vertexID = this.issue.entityIds[1];
58740                                         var way = context.entity(entityID);
58741                                         var vertex = context.entity(vertexID);
58742                                         continueDrawing(way, vertex, context);
58743                                     }
58744                                 }));
58745                             }
58746
58747                             return fixes;
58748                         },
58749                         loc: node.loc
58750                     })];
58751
58752                     function getReference(referenceID) {
58753                         return function showReference(selection) {
58754                             selection.selectAll('.issue-reference')
58755                                 .data([0])
58756                                 .enter()
58757                                 .append('div')
58758                                 .attr('class', 'issue-reference')
58759                                 .text(_t('issues.impossible_oneway.' + referenceID + '.reference'));
58760                         };
58761                     }
58762                 }
58763             };
58764
58765             function continueDrawing(way, vertex, context) {
58766                 // make sure the vertex is actually visible and editable
58767                 var map = context.map();
58768                 if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
58769                     map.zoomToEase(vertex);
58770                 }
58771
58772                 context.enter(
58773                     modeDrawLine(context, way.id, context.graph(), 'line', way.affix(vertex.id), true)
58774                 );
58775             }
58776
58777             validation.type = type;
58778
58779             return validation;
58780         }
58781
58782         function validationIncompatibleSource() {
58783             var type = 'incompatible_source';
58784             var invalidSources = [
58785                 {
58786                     id:'google', regex:'google', exceptRegex: 'books.google|Google Books|drive.google|googledrive|Google Drive'
58787                 }
58788             ];
58789
58790             var validation = function checkIncompatibleSource(entity) {
58791
58792                 var entitySources = entity.tags && entity.tags.source && entity.tags.source.split(';');
58793
58794                 if (!entitySources) { return []; }
58795
58796                 var issues = [];
58797
58798                 invalidSources.forEach(function(invalidSource) {
58799
58800                     var hasInvalidSource = entitySources.some(function(source) {
58801                         if (!source.match(new RegExp(invalidSource.regex, 'i'))) { return false; }
58802                         if (invalidSource.exceptRegex && source.match(new RegExp(invalidSource.exceptRegex, 'i'))) { return false; }
58803                         return true;
58804                     });
58805
58806                     if (!hasInvalidSource) { return; }
58807
58808                     issues.push(new validationIssue({
58809                         type: type,
58810                         severity: 'warning',
58811                         message: function(context) {
58812                             var entity = context.hasEntity(this.entityIds[0]);
58813                             return entity ? _t('issues.incompatible_source.' + invalidSource.id + '.feature.message', {
58814                                 feature: utilDisplayLabel(entity, context.graph())
58815                             }) : '';
58816                         },
58817                         reference: getReference(invalidSource.id),
58818                         entityIds: [entity.id],
58819                         dynamicFixes: function() {
58820                             return [
58821                                 new validationIssueFix({
58822                                     title: _t('issues.fix.remove_proprietary_data.title')
58823                                 })
58824                             ];
58825                         }
58826                     }));
58827                 });
58828
58829                 return issues;
58830
58831
58832                 function getReference(id) {
58833                     return function showReference(selection) {
58834                         selection.selectAll('.issue-reference')
58835                             .data([0])
58836                             .enter()
58837                             .append('div')
58838                             .attr('class', 'issue-reference')
58839                             .text(_t('issues.incompatible_source.' + id + '.reference'));
58840                     };
58841                 }
58842             };
58843
58844             validation.type = type;
58845
58846             return validation;
58847         }
58848
58849         function validationMaprules() {
58850             var type = 'maprules';
58851
58852             var validation = function checkMaprules(entity, graph) {
58853                 if (!services.maprules) { return []; }
58854
58855                 var rules = services.maprules.validationRules();
58856                 var issues = [];
58857
58858                 for (var i = 0; i < rules.length; i++) {
58859                     var rule = rules[i];
58860                     rule.findIssues(entity, graph, issues);
58861                 }
58862
58863                 return issues;
58864             };
58865
58866
58867             validation.type = type;
58868
58869             return validation;
58870         }
58871
58872         function validationMismatchedGeometry() {
58873             var type = 'mismatched_geometry';
58874
58875             function tagSuggestingLineIsArea(entity) {
58876                 if (entity.type !== 'way' || entity.isClosed()) { return null; }
58877
58878                 var tagSuggestingArea = entity.tagSuggestingArea();
58879                 if (!tagSuggestingArea) {
58880                     return null;
58881                 }
58882
58883                 var asLine = _mainPresetIndex.matchTags(tagSuggestingArea, 'line');
58884                 var asArea = _mainPresetIndex.matchTags(tagSuggestingArea, 'area');
58885                 if (asLine && asArea && (asLine === asArea)) {
58886                     // these tags also allow lines and making this an area wouldn't matter
58887                     return null;
58888                 }
58889
58890                 return tagSuggestingArea;
58891             }
58892
58893
58894             function makeConnectEndpointsFixOnClick(way, graph) {
58895                 // must have at least three nodes to close this automatically
58896                 if (way.nodes.length < 3) { return null; }
58897
58898                 var nodes = graph.childNodes(way), testNodes;
58899                 var firstToLastDistanceMeters = geoSphericalDistance(nodes[0].loc, nodes[nodes.length-1].loc);
58900
58901                 // if the distance is very small, attempt to merge the endpoints
58902                 if (firstToLastDistanceMeters < 0.75) {
58903                     testNodes = nodes.slice();   // shallow copy
58904                     testNodes.pop();
58905                     testNodes.push(testNodes[0]);
58906                     // make sure this will not create a self-intersection
58907                     if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
58908                         return function(context) {
58909                             var way = context.entity(this.issue.entityIds[0]);
58910                             context.perform(
58911                                 actionMergeNodes([way.nodes[0], way.nodes[way.nodes.length-1]], nodes[0].loc),
58912                                 _t('issues.fix.connect_endpoints.annotation')
58913                             );
58914                         };
58915                     }
58916                 }
58917
58918                 // if the points were not merged, attempt to close the way
58919                 testNodes = nodes.slice();   // shallow copy
58920                 testNodes.push(testNodes[0]);
58921                 // make sure this will not create a self-intersection
58922                 if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
58923                     return function(context) {
58924                         var wayId = this.issue.entityIds[0];
58925                         var way = context.entity(wayId);
58926                         var nodeId = way.nodes[0];
58927                         var index = way.nodes.length;
58928                         context.perform(
58929                             actionAddVertex(wayId, nodeId, index),
58930                             _t('issues.fix.connect_endpoints.annotation')
58931                         );
58932                     };
58933                 }
58934             }
58935
58936             function lineTaggedAsAreaIssue(entity) {
58937
58938                 var tagSuggestingArea = tagSuggestingLineIsArea(entity);
58939                 if (!tagSuggestingArea) { return null; }
58940
58941                 return new validationIssue({
58942                     type: type,
58943                     subtype: 'area_as_line',
58944                     severity: 'warning',
58945                     message: function(context) {
58946                         var entity = context.hasEntity(this.entityIds[0]);
58947                         return entity ? _t('issues.tag_suggests_area.message', {
58948                             feature: utilDisplayLabel(entity, context.graph()),
58949                             tag: utilTagText({ tags: tagSuggestingArea })
58950                         }) : '';
58951                     },
58952                     reference: showReference,
58953                     entityIds: [entity.id],
58954                     hash: JSON.stringify(tagSuggestingArea),
58955                     dynamicFixes: function(context) {
58956
58957                         var fixes = [];
58958
58959                         var entity = context.entity(this.entityIds[0]);
58960                         var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity, context.graph());
58961
58962                         fixes.push(new validationIssueFix({
58963                             title: _t('issues.fix.connect_endpoints.title'),
58964                             onClick: connectEndsOnClick
58965                         }));
58966
58967                         fixes.push(new validationIssueFix({
58968                             icon: 'iD-operation-delete',
58969                             title: _t('issues.fix.remove_tag.title'),
58970                             onClick: function(context) {
58971                                 var entityId = this.issue.entityIds[0];
58972                                 var entity = context.entity(entityId);
58973                                 var tags = Object.assign({}, entity.tags);  // shallow copy
58974                                 for (var key in tagSuggestingArea) {
58975                                     delete tags[key];
58976                                 }
58977                                 context.perform(
58978                                     actionChangeTags(entityId, tags),
58979                                     _t('issues.fix.remove_tag.annotation')
58980                                 );
58981                             }
58982                         }));
58983
58984                         return fixes;
58985                     }
58986                 });
58987
58988
58989                 function showReference(selection) {
58990                     selection.selectAll('.issue-reference')
58991                         .data([0])
58992                         .enter()
58993                         .append('div')
58994                         .attr('class', 'issue-reference')
58995                         .text(_t('issues.tag_suggests_area.reference'));
58996                 }
58997             }
58998
58999             function vertexTaggedAsPointIssue(entity, graph) {
59000                 // we only care about nodes
59001                 if (entity.type !== 'node') { return null; }
59002
59003                 // ignore tagless points
59004                 if (Object.keys(entity.tags).length === 0) { return null; }
59005
59006                 // address lines are special so just ignore them
59007                 if (entity.isOnAddressLine(graph)) { return null; }
59008
59009                 var geometry = entity.geometry(graph);
59010                 var allowedGeometries = osmNodeGeometriesForTags(entity.tags);
59011
59012                 if (geometry === 'point' && !allowedGeometries.point && allowedGeometries.vertex) {
59013
59014                     return new validationIssue({
59015                         type: type,
59016                         subtype: 'vertex_as_point',
59017                         severity: 'warning',
59018                         message: function(context) {
59019                             var entity = context.hasEntity(this.entityIds[0]);
59020                             return entity ? _t('issues.vertex_as_point.message', {
59021                                 feature: utilDisplayLabel(entity, context.graph())
59022                             }) : '';
59023                         },
59024                         reference: function showReference(selection) {
59025                             selection.selectAll('.issue-reference')
59026                                 .data([0])
59027                                 .enter()
59028                                 .append('div')
59029                                 .attr('class', 'issue-reference')
59030                                 .text(_t('issues.vertex_as_point.reference'));
59031                         },
59032                         entityIds: [entity.id]
59033                     });
59034
59035                 } else if (geometry === 'vertex' && !allowedGeometries.vertex && allowedGeometries.point) {
59036
59037                     return new validationIssue({
59038                         type: type,
59039                         subtype: 'point_as_vertex',
59040                         severity: 'warning',
59041                         message: function(context) {
59042                             var entity = context.hasEntity(this.entityIds[0]);
59043                             return entity ? _t('issues.point_as_vertex.message', {
59044                                 feature: utilDisplayLabel(entity, context.graph())
59045                             }) : '';
59046                         },
59047                         reference: function showReference(selection) {
59048                             selection.selectAll('.issue-reference')
59049                                 .data([0])
59050                                 .enter()
59051                                 .append('div')
59052                                 .attr('class', 'issue-reference')
59053                                 .text(_t('issues.point_as_vertex.reference'));
59054                         },
59055                         entityIds: [entity.id],
59056                         dynamicFixes: function(context) {
59057
59058                             var entityId = this.entityIds[0];
59059
59060                             var extractOnClick = null;
59061                             if (!context.hasHiddenConnections(entityId)) {
59062
59063                                 extractOnClick = function(context) {
59064                                     var entityId = this.issue.entityIds[0];
59065                                     var action = actionExtract(entityId);
59066                                     context.perform(
59067                                         action,
59068                                         _t('operations.extract.annotation.single')
59069                                     );
59070                                     // re-enter mode to trigger updates
59071                                     context.enter(modeSelect(context, [action.getExtractedNodeID()]));
59072                                 };
59073                             }
59074
59075                             return [
59076                                 new validationIssueFix({
59077                                     icon: 'iD-operation-extract',
59078                                     title: _t('issues.fix.extract_point.title'),
59079                                     onClick: extractOnClick
59080                                 })
59081                             ];
59082                         }
59083                     });
59084                 }
59085
59086                 return null;
59087             }
59088
59089             function unclosedMultipolygonPartIssues(entity, graph) {
59090
59091                 if (entity.type !== 'relation' ||
59092                     !entity.isMultipolygon() ||
59093                     entity.isDegenerate() ||
59094                     // cannot determine issues for incompletely-downloaded relations
59095                     !entity.isComplete(graph)) { return null; }
59096
59097                 var sequences = osmJoinWays(entity.members, graph);
59098
59099                 var issues = [];
59100
59101                 for (var i in sequences) {
59102                     var sequence = sequences[i];
59103
59104                     if (!sequence.nodes) { continue; }
59105
59106                     var firstNode = sequence.nodes[0];
59107                     var lastNode = sequence.nodes[sequence.nodes.length - 1];
59108
59109                     // part is closed if the first and last nodes are the same
59110                     if (firstNode === lastNode) { continue; }
59111
59112                     var issue = new validationIssue({
59113                         type: type,
59114                         subtype: 'unclosed_multipolygon_part',
59115                         severity: 'warning',
59116                         message: function(context) {
59117                             var entity = context.hasEntity(this.entityIds[0]);
59118                             return entity ? _t('issues.unclosed_multipolygon_part.message', {
59119                                 feature: utilDisplayLabel(entity, context.graph())
59120                             }) : '';
59121                         },
59122                         reference: showReference,
59123                         loc: sequence.nodes[0].loc,
59124                         entityIds: [entity.id],
59125                         hash: sequence.map(function(way) {
59126                             return way.id;
59127                         }).join()
59128                     });
59129                     issues.push(issue);
59130                 }
59131
59132                 return issues;
59133
59134                 function showReference(selection) {
59135                     selection.selectAll('.issue-reference')
59136                         .data([0])
59137                         .enter()
59138                         .append('div')
59139                         .attr('class', 'issue-reference')
59140                         .text(_t('issues.unclosed_multipolygon_part.reference'));
59141                 }
59142             }
59143
59144             var validation = function checkMismatchedGeometry(entity, graph) {
59145                 var issues = [
59146                     vertexTaggedAsPointIssue(entity, graph),
59147                     lineTaggedAsAreaIssue(entity)
59148                 ];
59149                 issues = issues.concat(unclosedMultipolygonPartIssues(entity, graph));
59150                 return issues.filter(Boolean);
59151             };
59152
59153             validation.type = type;
59154
59155             return validation;
59156         }
59157
59158         function validationMissingRole() {
59159             var type = 'missing_role';
59160
59161             var validation = function checkMissingRole(entity, graph) {
59162                 var issues = [];
59163                 if (entity.type === 'way') {
59164                     graph.parentRelations(entity).forEach(function(relation) {
59165                         if (!relation.isMultipolygon()) { return; }
59166
59167                         var member = relation.memberById(entity.id);
59168                         if (member && isMissingRole(member)) {
59169                             issues.push(makeIssue(entity, relation, member));
59170                         }
59171                     });
59172                 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
59173                     entity.indexedMembers().forEach(function(member) {
59174                         var way = graph.hasEntity(member.id);
59175                         if (way && isMissingRole(member)) {
59176                             issues.push(makeIssue(way, entity, member));
59177                         }
59178                     });
59179                 }
59180
59181                 return issues;
59182             };
59183
59184
59185             function isMissingRole(member) {
59186                 return !member.role || !member.role.trim().length;
59187             }
59188
59189
59190             function makeIssue(way, relation, member) {
59191                 return new validationIssue({
59192                     type: type,
59193                     severity: 'warning',
59194                     message: function(context) {
59195                         var member = context.hasEntity(this.entityIds[1]),
59196                             relation = context.hasEntity(this.entityIds[0]);
59197                         return (member && relation) ? _t('issues.missing_role.message', {
59198                             member: utilDisplayLabel(member, context.graph()),
59199                             relation: utilDisplayLabel(relation, context.graph())
59200                         }) : '';
59201                     },
59202                     reference: showReference,
59203                     entityIds: [relation.id, way.id],
59204                     data: {
59205                         member: member
59206                     },
59207                     hash: member.index.toString(),
59208                     dynamicFixes: function() {
59209                         return [
59210                             makeAddRoleFix('inner'),
59211                             makeAddRoleFix('outer'),
59212                             new validationIssueFix({
59213                                 icon: 'iD-operation-delete',
59214                                 title: _t('issues.fix.remove_from_relation.title'),
59215                                 onClick: function(context) {
59216                                     context.perform(
59217                                         actionDeleteMember(this.issue.entityIds[0], this.issue.data.member.index),
59218                                         _t('operations.delete_member.annotation')
59219                                     );
59220                                 }
59221                             })
59222                         ];
59223                     }
59224                 });
59225
59226
59227                 function showReference(selection) {
59228                     selection.selectAll('.issue-reference')
59229                         .data([0])
59230                         .enter()
59231                         .append('div')
59232                         .attr('class', 'issue-reference')
59233                         .text(_t('issues.missing_role.multipolygon.reference'));
59234                 }
59235             }
59236
59237
59238             function makeAddRoleFix(role) {
59239                 return new validationIssueFix({
59240                     title: _t('issues.fix.set_as_' + role + '.title'),
59241                     onClick: function(context) {
59242                         var oldMember = this.issue.data.member;
59243                         var member = { id: this.issue.entityIds[1], type: oldMember.type, role: role };
59244                         context.perform(
59245                             actionChangeMember(this.issue.entityIds[0], member, oldMember.index),
59246                             _t('operations.change_role.annotation')
59247                         );
59248                     }
59249                 });
59250             }
59251
59252             validation.type = type;
59253
59254             return validation;
59255         }
59256
59257         function validationMissingTag(context) {
59258             var type = 'missing_tag';
59259
59260             function hasDescriptiveTags(entity, graph) {
59261                 var keys = Object.keys(entity.tags)
59262                     .filter(function(k) {
59263                         if (k === 'area' || k === 'name') {
59264                             return false;
59265                         } else {
59266                             return osmIsInterestingTag(k);
59267                         }
59268                     });
59269
59270                 if (entity.type === 'relation' &&
59271                     keys.length === 1 &&
59272                     entity.tags.type === 'multipolygon') {
59273                     // this relation's only interesting tag just says its a multipolygon,
59274                     // which is not descriptive enough
59275
59276                     // It's okay for a simple multipolygon to have no descriptive tags
59277                     // if its outer way has them (old model, see `outdated_tags.js`)
59278                     return osmOldMultipolygonOuterMemberOfRelation(entity, graph);
59279                 }
59280
59281                 return keys.length > 0;
59282             }
59283
59284             function isUnknownRoad(entity) {
59285                 return entity.type === 'way' && entity.tags.highway === 'road';
59286             }
59287
59288             function isUntypedRelation(entity) {
59289                 return entity.type === 'relation' && !entity.tags.type;
59290             }
59291
59292             var validation = function checkMissingTag(entity, graph) {
59293
59294                 var subtype;
59295
59296                 var osm = context.connection();
59297                 var isUnloadedNode = entity.type === 'node' && osm && !osm.isDataLoaded(entity.loc);
59298
59299                 // we can't know if the node is a vertex if the tile is undownloaded
59300                 if (!isUnloadedNode &&
59301                     // allow untagged nodes that are part of ways
59302                     entity.geometry(graph) !== 'vertex' &&
59303                     // allow untagged entities that are part of relations
59304                     !entity.hasParentRelations(graph)) {
59305
59306                     if (Object.keys(entity.tags).length === 0) {
59307                         subtype = 'any';
59308                     } else if (!hasDescriptiveTags(entity, graph)) {
59309                         subtype = 'descriptive';
59310                     } else if (isUntypedRelation(entity)) {
59311                         subtype = 'relation_type';
59312                     }
59313                 }
59314
59315                 // flag an unknown road even if it's a member of a relation
59316                 if (!subtype && isUnknownRoad(entity)) {
59317                     subtype = 'highway_classification';
59318                 }
59319
59320                 if (!subtype) { return []; }
59321
59322                 var messageID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag.' + subtype;
59323                 var referenceID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag';
59324
59325                 // can always delete if the user created it in the first place..
59326                 var canDelete = (entity.version === undefined || entity.v !== undefined);
59327                 var severity = (canDelete && subtype !== 'highway_classification') ? 'error' : 'warning';
59328
59329                 return [new validationIssue({
59330                     type: type,
59331                     subtype: subtype,
59332                     severity: severity,
59333                     message: function(context) {
59334                         var entity = context.hasEntity(this.entityIds[0]);
59335                         return entity ? _t('issues.' + messageID + '.message', {
59336                             feature: utilDisplayLabel(entity, context.graph())
59337                         }) : '';
59338                     },
59339                     reference: showReference,
59340                     entityIds: [entity.id],
59341                     dynamicFixes: function(context) {
59342
59343                         var fixes = [];
59344
59345                         var selectFixType = subtype === 'highway_classification' ? 'select_road_type' : 'select_preset';
59346
59347                         fixes.push(new validationIssueFix({
59348                             icon: 'iD-icon-search',
59349                             title: _t('issues.fix.' + selectFixType + '.title'),
59350                             onClick: function(context) {
59351                                 context.ui().sidebar.showPresetList();
59352                             }
59353                         }));
59354
59355                         var deleteOnClick;
59356
59357                         var id = this.entityIds[0];
59358                         var operation = operationDelete(context, [id]);
59359                         var disabledReasonID = operation.disabled();
59360                         if (!disabledReasonID) {
59361                             deleteOnClick = function(context) {
59362                                 var id = this.issue.entityIds[0];
59363                                 var operation = operationDelete(context, [id]);
59364                                 if (!operation.disabled()) {
59365                                     operation();
59366                                 }
59367                             };
59368                         }
59369
59370                         fixes.push(
59371                             new validationIssueFix({
59372                                 icon: 'iD-operation-delete',
59373                                 title: _t('issues.fix.delete_feature.title'),
59374                                 disabledReason: disabledReasonID ? _t('operations.delete.' + disabledReasonID + '.single') : undefined,
59375                                 onClick: deleteOnClick
59376                             })
59377                         );
59378
59379                         return fixes;
59380                     }
59381                 })];
59382
59383                 function showReference(selection) {
59384                     selection.selectAll('.issue-reference')
59385                         .data([0])
59386                         .enter()
59387                         .append('div')
59388                         .attr('class', 'issue-reference')
59389                         .text(_t('issues.' + referenceID + '.reference'));
59390                 }
59391             };
59392
59393             validation.type = type;
59394
59395             return validation;
59396         }
59397
59398         // remove spaces, punctuation, diacritics
59399         var simplify = function (str) {
59400           return diacritics.remove(
59401             str
59402               .replace(/&/g, 'and')
59403               .replace(/[\s\-=_!"#%'*{},.\/:;?\(\)\[\]@\\$\^*+<>~`’\u00a1\u00a7\u00b6\u00b7\u00bf\u037e\u0387\u055a-\u055f\u0589\u05c0\u05c3\u05c6\u05f3\u05f4\u0609\u060a\u060c\u060d\u061b\u061e\u061f\u066a-\u066d\u06d4\u0700-\u070d\u07f7-\u07f9\u0830-\u083e\u085e\u0964\u0965\u0970\u0af0\u0df4\u0e4f\u0e5a\u0e5b\u0f04-\u0f12\u0f14\u0f85\u0fd0-\u0fd4\u0fd9\u0fda\u104a-\u104f\u10fb\u1360-\u1368\u166d\u166e\u16eb-\u16ed\u1735\u1736\u17d4-\u17d6\u17d8-\u17da\u1800-\u1805\u1807-\u180a\u1944\u1945\u1a1e\u1a1f\u1aa0-\u1aa6\u1aa8-\u1aad\u1b5a-\u1b60\u1bfc-\u1bff\u1c3b-\u1c3f\u1c7e\u1c7f\u1cc0-\u1cc7\u1cd3\u2016\u2017\u2020-\u2027\u2030-\u2038\u203b-\u203e\u2041-\u2043\u2047-\u2051\u2053\u2055-\u205e\u2cf9-\u2cfc\u2cfe\u2cff\u2d70\u2e00\u2e01\u2e06-\u2e08\u2e0b\u2e0e-\u2e16\u2e18\u2e19\u2e1b\u2e1e\u2e1f\u2e2a-\u2e2e\u2e30-\u2e39\u3001-\u3003\u303d\u30fb\ua4fe\ua4ff\ua60d-\ua60f\ua673\ua67e\ua6f2-\ua6f7\ua874-\ua877\ua8ce\ua8cf\ua8f8-\ua8fa\ua92e\ua92f\ua95f\ua9c1-\ua9cd\ua9de\ua9df\uaa5c-\uaa5f\uaade\uaadf\uaaf0\uaaf1\uabeb\ufe10-\ufe16\ufe19\ufe30\ufe45\ufe46\ufe49-\ufe4c\ufe50-\ufe52\ufe54-\ufe57\ufe5f-\ufe61\ufe68\ufe6a\ufe6b\uff01-\uff03\uff05-\uff07\uff0a\uff0c\uff0e\uff0f\uff1a\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65]+/g,'')
59404               .toLowerCase()
59405           );
59406         };
59407
59408         // toParts - split a name-suggestion-index key into parts
59409         // {
59410         //   kvnd:        "amenity/fast_food|Thaï Express~(North America)",
59411         //   kvn:         "amenity/fast_food|Thaï Express",
59412         //   kv:          "amenity/fast_food",
59413         //   k:           "amenity",
59414         //   v:           "fast_food",
59415         //   n:           "Thaï Express",
59416         //   d:           "(North America)",
59417         //   nsimple:     "thaiexpress",
59418         //   kvnnsimple:  "amenity/fast_food|thaiexpress"
59419         // }
59420         var to_parts = function (kvnd) {
59421           var parts = {};
59422           parts.kvnd = kvnd;
59423
59424           var kvndparts = kvnd.split('~', 2);
59425           if (kvndparts.length > 1) { parts.d = kvndparts[1]; }
59426
59427           parts.kvn = kvndparts[0];
59428           var kvnparts = parts.kvn.split('|', 2);
59429           if (kvnparts.length > 1) { parts.n = kvnparts[1]; }
59430
59431           parts.kv = kvnparts[0];
59432           var kvparts = parts.kv.split('/', 2);
59433           parts.k = kvparts[0];
59434           parts.v = kvparts[1];
59435
59436           parts.nsimple = simplify(parts.n);
59437           parts.kvnsimple = parts.kv + '|' + parts.nsimple;
59438           return parts;
59439         };
59440
59441         var matchGroups = {adult_gaming_centre:["amenity/casino","amenity/gambling","leisure/adult_gaming_centre"],beauty:["shop/beauty","shop/hairdresser_supply"],bed:["shop/bed","shop/furniture"],beverages:["shop/alcohol","shop/beverages"],camping:["leisure/park","tourism/camp_site","tourism/caravan_site"],car_parts:["shop/car_parts","shop/car_repair","shop/tires","shop/tyres"],confectionery:["shop/candy","shop/chocolate","shop/confectionery"],convenience:["shop/beauty","shop/chemist","shop/convenience","shop/cosmetics","shop/newsagent"],coworking:["amenity/coworking_space","office/coworking","office/coworking_space"],electronics:["office/telecommunication","shop/computer","shop/electronics","shop/hifi","shop/mobile","shop/mobile_phone","shop/telecommunication"],fashion:["shop/accessories","shop/bag","shop/botique","shop/clothes","shop/department_store","shop/fashion","shop/fashion_accessories","shop/sports","shop/shoes"],financial:["amenity/bank","office/accountant","office/financial","office/financial_advisor","office/tax_advisor","shop/tax"],fitness:["leisure/fitness_centre","leisure/fitness_center","leisure/sports_centre","leisure/sports_center"],food:["amenity/cafe","amenity/fast_food","amenity/ice_cream","amenity/restaurant","shop/bakery","shop/ice_cream","shop/pastry","shop/tea","shop/coffee"],fuel:["amenity/fuel","shop/gas","shop/convenience;gas","shop/gas;convenience"],gift:["shop/gift","shop/card","shop/cards","shop/stationery"],hardware:["shop/carpet","shop/diy","shop/doityourself","shop/doors","shop/electrical","shop/flooring","shop/hardware","shop/power_tools","shop/tool_hire","shop/tools","shop/trade"],health_food:["shop/health","shop/health_food","shop/herbalist","shop/nutrition_supplements"],houseware:["shop/houseware","shop/interior_decoration"],lodging:["tourism/hotel","tourism/motel"],money_transfer:["amenity/money_transfer","shop/money_transfer"],outdoor:["shop/outdoor","shop/sports"],rental:["amenity/bicycle_rental","amenity/boat_rental","amenity/car_rental","amenity/truck_rental","amenity/vehicle_rental","shop/rental"],school:["amenity/childcare","amenity/college","amenity/kindergarten","amenity/language_school","amenity/prep_school","amenity/school","amenity/university"],supermarket:["shop/food","shop/frozen_food","shop/greengrocer","shop/grocery","shop/supermarket","shop/wholesale"],variety_store:["shop/variety_store","shop/supermarket","shop/discount","shop/convenience"],vending:["amenity/vending_machine","shop/vending_machine"],wholesale:["shop/wholesale","shop/supermarket","shop/department_store"]};
59442         var match_groups = {
59443         matchGroups: matchGroups
59444         };
59445
59446         var match_groups$1 = /*#__PURE__*/Object.freeze({
59447                 __proto__: null,
59448                 matchGroups: matchGroups,
59449                 'default': match_groups
59450         });
59451
59452         var require$$0 = getCjsExportFromNamespace(match_groups$1);
59453
59454         var matchGroups$1 = require$$0.matchGroups;
59455
59456
59457         var matcher$1 = function () {
59458           var _warnings = [];  // array of match conflict pairs
59459           var _ambiguous = {};
59460           var _matchIndex = {};
59461           var matcher = {};
59462
59463
59464           // Create an index of all the keys/simplenames for fast matching
59465           matcher.buildMatchIndex = function (brands) {
59466             // two passes - once for primary names, once for secondary/alternate names
59467             Object.keys(brands).forEach(function (kvnd) { return insertNames(kvnd, 'primary'); });
59468             Object.keys(brands).forEach(function (kvnd) { return insertNames(kvnd, 'secondary'); });
59469
59470
59471             function insertNames(kvnd, which) {
59472               var obj = brands[kvnd];
59473               var parts = to_parts(kvnd);
59474
59475               // Exit early for ambiguous names in the second pass.
59476               // They were collected in the first pass and we don't gather alt names for them.
59477               if (which === 'secondary' && parts.d) { return; }
59478
59479
59480               if (obj.countryCodes) {
59481                 parts.countryCodes = obj.countryCodes.slice();  // copy
59482               }
59483
59484               var nomatches = (obj.nomatch || []);
59485               if (nomatches.some(function (s) { return s === kvnd; })) {
59486                 console.log(("WARNING match/nomatch conflict for " + kvnd));
59487                 return;
59488               }
59489
59490               var match_kv = [parts.kv]
59491                 .concat(obj.matchTags || [])
59492                 .concat([((parts.k) + "/yes"), "building/yes"])   // #3454 - match some generic tags
59493                 .map(function (s) { return s.toLowerCase(); });
59494
59495               var match_nsimple = [];
59496               if (which === 'primary') {
59497                 match_nsimple = [parts.n]
59498                   .concat(obj.matchNames || [])
59499                   .concat(obj.tags.official_name || [])   // #2732 - match alternate names
59500                   .map(simplify);
59501
59502               } else if (which === 'secondary') {
59503                 match_nsimple = []
59504                   .concat(obj.tags.alt_name || [])        // #2732 - match alternate names
59505                   .concat(obj.tags.short_name || [])      // #2732 - match alternate names
59506                   .map(simplify);
59507               }
59508
59509               if (!match_nsimple.length) { return; }  // nothing to do
59510
59511               match_kv.forEach(function (kv) {
59512                 match_nsimple.forEach(function (nsimple) {
59513                   if (parts.d) {
59514                     // Known ambiguous names with disambiguation string ~(USA) / ~(Canada)
59515                     // FIXME: Name collisions will overwrite the initial entry (ok for now)
59516                     if (!_ambiguous[kv]) { _ambiguous[kv] = {}; }
59517                     _ambiguous[kv][nsimple] = parts;
59518
59519                   } else {
59520                     // Names we mostly expect to be unique..
59521                     if (!_matchIndex[kv]) { _matchIndex[kv] = {}; }
59522
59523                     var m = _matchIndex[kv][nsimple];
59524                     if (m) {  // There already is a match for this name, skip it
59525                       // Warn if we detect collisions in a primary name.
59526                       // Skip warning if a secondary name or a generic `*=yes` tag - #2972 / #3454
59527                       if (which === 'primary' && !/\/yes$/.test(kv)) {
59528                         _warnings.push([m.kvnd, (kvnd + " (" + kv + "/" + nsimple + ")")]);
59529                       }
59530                     } else {
59531                       _matchIndex[kv][nsimple] = parts;   // insert
59532                     }
59533                   }
59534                 });
59535               });
59536
59537             }
59538           };
59539
59540
59541           // pass a `key`, `value`, `name` and return the best match,
59542           // `countryCode` optional (if supplied, must match that too)
59543           matcher.matchKVN = function (key, value, name, countryCode) {
59544             return matcher.matchParts(to_parts((key + "/" + value + "|" + name)), countryCode);
59545           };
59546
59547
59548           // pass a parts object and return the best match,
59549           // `countryCode` optional (if supplied, must match that too)
59550           matcher.matchParts = function (parts, countryCode) {
59551             var match = null;
59552             var inGroup = false;
59553
59554             // fixme: we currently return a single match for ambiguous
59555             match = _ambiguous[parts.kv] && _ambiguous[parts.kv][parts.nsimple];
59556             if (match && matchesCountryCode(match)) { return match; }
59557
59558             // try to return an exact match
59559             match = _matchIndex[parts.kv] && _matchIndex[parts.kv][parts.nsimple];
59560             if (match && matchesCountryCode(match)) { return match; }
59561
59562             // look in match groups
59563             for (var mg in matchGroups$1) {
59564               var matchGroup = matchGroups$1[mg];
59565               match = null;
59566               inGroup = false;
59567
59568               for (var i = 0; i < matchGroup.length; i++) {
59569                 var otherkv = matchGroup[i].toLowerCase();
59570                 if (!inGroup) {
59571                   inGroup = otherkv === parts.kv;
59572                 }
59573                 if (!match) {
59574                   // fixme: we currently return a single match for ambiguous
59575                   match = _ambiguous[otherkv] && _ambiguous[otherkv][parts.nsimple];
59576                 }
59577                 if (!match) {
59578                   match = _matchIndex[otherkv] && _matchIndex[otherkv][parts.nsimple];
59579                 }
59580
59581                 if (match && !matchesCountryCode(match)) {
59582                   match = null;
59583                 }
59584
59585                 if (inGroup && match) {
59586                   return match;
59587                 }
59588               }
59589             }
59590
59591             return null;
59592
59593             function matchesCountryCode(match) {
59594               if (!countryCode) { return true; }
59595               if (!match.countryCodes) { return true; }
59596               return match.countryCodes.indexOf(countryCode) !== -1;
59597             }
59598           };
59599
59600
59601           matcher.getWarnings = function () { return _warnings; };
59602
59603           return matcher;
59604         };
59605
59606         var quickselect$1 = createCommonjsModule(function (module, exports) {
59607         (function (global, factory) {
59608                  module.exports = factory() ;
59609         }(commonjsGlobal, (function () {
59610         function quickselect(arr, k, left, right, compare) {
59611             quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
59612         }
59613
59614         function quickselectStep(arr, k, left, right, compare) {
59615
59616             while (right > left) {
59617                 if (right - left > 600) {
59618                     var n = right - left + 1;
59619                     var m = k - left + 1;
59620                     var z = Math.log(n);
59621                     var s = 0.5 * Math.exp(2 * z / 3);
59622                     var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
59623                     var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
59624                     var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
59625                     quickselectStep(arr, k, newLeft, newRight, compare);
59626                 }
59627
59628                 var t = arr[k];
59629                 var i = left;
59630                 var j = right;
59631
59632                 swap(arr, left, k);
59633                 if (compare(arr[right], t) > 0) { swap(arr, left, right); }
59634
59635                 while (i < j) {
59636                     swap(arr, i, j);
59637                     i++;
59638                     j--;
59639                     while (compare(arr[i], t) < 0) { i++; }
59640                     while (compare(arr[j], t) > 0) { j--; }
59641                 }
59642
59643                 if (compare(arr[left], t) === 0) { swap(arr, left, j); }
59644                 else {
59645                     j++;
59646                     swap(arr, j, right);
59647                 }
59648
59649                 if (j <= k) { left = j + 1; }
59650                 if (k <= j) { right = j - 1; }
59651             }
59652         }
59653
59654         function swap(arr, i, j) {
59655             var tmp = arr[i];
59656             arr[i] = arr[j];
59657             arr[j] = tmp;
59658         }
59659
59660         function defaultCompare(a, b) {
59661             return a < b ? -1 : a > b ? 1 : 0;
59662         }
59663
59664         return quickselect;
59665
59666         })));
59667         });
59668
59669         var rbush_1 = rbush;
59670         var _default$2 = rbush;
59671
59672
59673
59674         function rbush(maxEntries, format) {
59675             if (!(this instanceof rbush)) { return new rbush(maxEntries, format); }
59676
59677             // max entries in a node is 9 by default; min node fill is 40% for best performance
59678             this._maxEntries = Math.max(4, maxEntries || 9);
59679             this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
59680
59681             if (format) {
59682                 this._initFormat(format);
59683             }
59684
59685             this.clear();
59686         }
59687
59688         rbush.prototype = {
59689
59690             all: function () {
59691                 return this._all(this.data, []);
59692             },
59693
59694             search: function (bbox) {
59695
59696                 var node = this.data,
59697                     result = [],
59698                     toBBox = this.toBBox;
59699
59700                 if (!intersects$1(bbox, node)) { return result; }
59701
59702                 var nodesToSearch = [],
59703                     i, len, child, childBBox;
59704
59705                 while (node) {
59706                     for (i = 0, len = node.children.length; i < len; i++) {
59707
59708                         child = node.children[i];
59709                         childBBox = node.leaf ? toBBox(child) : child;
59710
59711                         if (intersects$1(bbox, childBBox)) {
59712                             if (node.leaf) { result.push(child); }
59713                             else if (contains$2(bbox, childBBox)) { this._all(child, result); }
59714                             else { nodesToSearch.push(child); }
59715                         }
59716                     }
59717                     node = nodesToSearch.pop();
59718                 }
59719
59720                 return result;
59721             },
59722
59723             collides: function (bbox) {
59724
59725                 var node = this.data,
59726                     toBBox = this.toBBox;
59727
59728                 if (!intersects$1(bbox, node)) { return false; }
59729
59730                 var nodesToSearch = [],
59731                     i, len, child, childBBox;
59732
59733                 while (node) {
59734                     for (i = 0, len = node.children.length; i < len; i++) {
59735
59736                         child = node.children[i];
59737                         childBBox = node.leaf ? toBBox(child) : child;
59738
59739                         if (intersects$1(bbox, childBBox)) {
59740                             if (node.leaf || contains$2(bbox, childBBox)) { return true; }
59741                             nodesToSearch.push(child);
59742                         }
59743                     }
59744                     node = nodesToSearch.pop();
59745                 }
59746
59747                 return false;
59748             },
59749
59750             load: function (data) {
59751                 if (!(data && data.length)) { return this; }
59752
59753                 if (data.length < this._minEntries) {
59754                     for (var i = 0, len = data.length; i < len; i++) {
59755                         this.insert(data[i]);
59756                     }
59757                     return this;
59758                 }
59759
59760                 // recursively build the tree with the given data from scratch using OMT algorithm
59761                 var node = this._build(data.slice(), 0, data.length - 1, 0);
59762
59763                 if (!this.data.children.length) {
59764                     // save as is if tree is empty
59765                     this.data = node;
59766
59767                 } else if (this.data.height === node.height) {
59768                     // split root if trees have the same height
59769                     this._splitRoot(this.data, node);
59770
59771                 } else {
59772                     if (this.data.height < node.height) {
59773                         // swap trees if inserted one is bigger
59774                         var tmpNode = this.data;
59775                         this.data = node;
59776                         node = tmpNode;
59777                     }
59778
59779                     // insert the small tree into the large tree at appropriate level
59780                     this._insert(node, this.data.height - node.height - 1, true);
59781                 }
59782
59783                 return this;
59784             },
59785
59786             insert: function (item) {
59787                 if (item) { this._insert(item, this.data.height - 1); }
59788                 return this;
59789             },
59790
59791             clear: function () {
59792                 this.data = createNode$1([]);
59793                 return this;
59794             },
59795
59796             remove: function (item, equalsFn) {
59797                 if (!item) { return this; }
59798
59799                 var node = this.data,
59800                     bbox = this.toBBox(item),
59801                     path = [],
59802                     indexes = [],
59803                     i, parent, index, goingUp;
59804
59805                 // depth-first iterative tree traversal
59806                 while (node || path.length) {
59807
59808                     if (!node) { // go up
59809                         node = path.pop();
59810                         parent = path[path.length - 1];
59811                         i = indexes.pop();
59812                         goingUp = true;
59813                     }
59814
59815                     if (node.leaf) { // check current node
59816                         index = findItem$1(item, node.children, equalsFn);
59817
59818                         if (index !== -1) {
59819                             // item found, remove the item and condense tree upwards
59820                             node.children.splice(index, 1);
59821                             path.push(node);
59822                             this._condense(path);
59823                             return this;
59824                         }
59825                     }
59826
59827                     if (!goingUp && !node.leaf && contains$2(node, bbox)) { // go down
59828                         path.push(node);
59829                         indexes.push(i);
59830                         i = 0;
59831                         parent = node;
59832                         node = node.children[0];
59833
59834                     } else if (parent) { // go right
59835                         i++;
59836                         node = parent.children[i];
59837                         goingUp = false;
59838
59839                     } else { node = null; } // nothing found
59840                 }
59841
59842                 return this;
59843             },
59844
59845             toBBox: function (item) { return item; },
59846
59847             compareMinX: compareNodeMinX$1,
59848             compareMinY: compareNodeMinY$1,
59849
59850             toJSON: function () { return this.data; },
59851
59852             fromJSON: function (data) {
59853                 this.data = data;
59854                 return this;
59855             },
59856
59857             _all: function (node, result) {
59858                 var nodesToSearch = [];
59859                 while (node) {
59860                     if (node.leaf) { result.push.apply(result, node.children); }
59861                     else { nodesToSearch.push.apply(nodesToSearch, node.children); }
59862
59863                     node = nodesToSearch.pop();
59864                 }
59865                 return result;
59866             },
59867
59868             _build: function (items, left, right, height) {
59869
59870                 var N = right - left + 1,
59871                     M = this._maxEntries,
59872                     node;
59873
59874                 if (N <= M) {
59875                     // reached leaf level; return leaf
59876                     node = createNode$1(items.slice(left, right + 1));
59877                     calcBBox$1(node, this.toBBox);
59878                     return node;
59879                 }
59880
59881                 if (!height) {
59882                     // target height of the bulk-loaded tree
59883                     height = Math.ceil(Math.log(N) / Math.log(M));
59884
59885                     // target number of root entries to maximize storage utilization
59886                     M = Math.ceil(N / Math.pow(M, height - 1));
59887                 }
59888
59889                 node = createNode$1([]);
59890                 node.leaf = false;
59891                 node.height = height;
59892
59893                 // split the items into M mostly square tiles
59894
59895                 var N2 = Math.ceil(N / M),
59896                     N1 = N2 * Math.ceil(Math.sqrt(M)),
59897                     i, j, right2, right3;
59898
59899                 multiSelect$1(items, left, right, N1, this.compareMinX);
59900
59901                 for (i = left; i <= right; i += N1) {
59902
59903                     right2 = Math.min(i + N1 - 1, right);
59904
59905                     multiSelect$1(items, i, right2, N2, this.compareMinY);
59906
59907                     for (j = i; j <= right2; j += N2) {
59908
59909                         right3 = Math.min(j + N2 - 1, right2);
59910
59911                         // pack each entry recursively
59912                         node.children.push(this._build(items, j, right3, height - 1));
59913                     }
59914                 }
59915
59916                 calcBBox$1(node, this.toBBox);
59917
59918                 return node;
59919             },
59920
59921             _chooseSubtree: function (bbox, node, level, path) {
59922
59923                 var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
59924
59925                 while (true) {
59926                     path.push(node);
59927
59928                     if (node.leaf || path.length - 1 === level) { break; }
59929
59930                     minArea = minEnlargement = Infinity;
59931
59932                     for (i = 0, len = node.children.length; i < len; i++) {
59933                         child = node.children[i];
59934                         area = bboxArea$1(child);
59935                         enlargement = enlargedArea$1(bbox, child) - area;
59936
59937                         // choose entry with the least area enlargement
59938                         if (enlargement < minEnlargement) {
59939                             minEnlargement = enlargement;
59940                             minArea = area < minArea ? area : minArea;
59941                             targetNode = child;
59942
59943                         } else if (enlargement === minEnlargement) {
59944                             // otherwise choose one with the smallest area
59945                             if (area < minArea) {
59946                                 minArea = area;
59947                                 targetNode = child;
59948                             }
59949                         }
59950                     }
59951
59952                     node = targetNode || node.children[0];
59953                 }
59954
59955                 return node;
59956             },
59957
59958             _insert: function (item, level, isNode) {
59959
59960                 var toBBox = this.toBBox,
59961                     bbox = isNode ? item : toBBox(item),
59962                     insertPath = [];
59963
59964                 // find the best node for accommodating the item, saving all nodes along the path too
59965                 var node = this._chooseSubtree(bbox, this.data, level, insertPath);
59966
59967                 // put the item into the node
59968                 node.children.push(item);
59969                 extend$3(node, bbox);
59970
59971                 // split on node overflow; propagate upwards if necessary
59972                 while (level >= 0) {
59973                     if (insertPath[level].children.length > this._maxEntries) {
59974                         this._split(insertPath, level);
59975                         level--;
59976                     } else { break; }
59977                 }
59978
59979                 // adjust bboxes along the insertion path
59980                 this._adjustParentBBoxes(bbox, insertPath, level);
59981             },
59982
59983             // split overflowed node into two
59984             _split: function (insertPath, level) {
59985
59986                 var node = insertPath[level],
59987                     M = node.children.length,
59988                     m = this._minEntries;
59989
59990                 this._chooseSplitAxis(node, m, M);
59991
59992                 var splitIndex = this._chooseSplitIndex(node, m, M);
59993
59994                 var newNode = createNode$1(node.children.splice(splitIndex, node.children.length - splitIndex));
59995                 newNode.height = node.height;
59996                 newNode.leaf = node.leaf;
59997
59998                 calcBBox$1(node, this.toBBox);
59999                 calcBBox$1(newNode, this.toBBox);
60000
60001                 if (level) { insertPath[level - 1].children.push(newNode); }
60002                 else { this._splitRoot(node, newNode); }
60003             },
60004
60005             _splitRoot: function (node, newNode) {
60006                 // split root node
60007                 this.data = createNode$1([node, newNode]);
60008                 this.data.height = node.height + 1;
60009                 this.data.leaf = false;
60010                 calcBBox$1(this.data, this.toBBox);
60011             },
60012
60013             _chooseSplitIndex: function (node, m, M) {
60014
60015                 var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
60016
60017                 minOverlap = minArea = Infinity;
60018
60019                 for (i = m; i <= M - m; i++) {
60020                     bbox1 = distBBox$1(node, 0, i, this.toBBox);
60021                     bbox2 = distBBox$1(node, i, M, this.toBBox);
60022
60023                     overlap = intersectionArea$1(bbox1, bbox2);
60024                     area = bboxArea$1(bbox1) + bboxArea$1(bbox2);
60025
60026                     // choose distribution with minimum overlap
60027                     if (overlap < minOverlap) {
60028                         minOverlap = overlap;
60029                         index = i;
60030
60031                         minArea = area < minArea ? area : minArea;
60032
60033                     } else if (overlap === minOverlap) {
60034                         // otherwise choose distribution with minimum area
60035                         if (area < minArea) {
60036                             minArea = area;
60037                             index = i;
60038                         }
60039                     }
60040                 }
60041
60042                 return index;
60043             },
60044
60045             // sorts node children by the best axis for split
60046             _chooseSplitAxis: function (node, m, M) {
60047
60048                 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX$1,
60049                     compareMinY = node.leaf ? this.compareMinY : compareNodeMinY$1,
60050                     xMargin = this._allDistMargin(node, m, M, compareMinX),
60051                     yMargin = this._allDistMargin(node, m, M, compareMinY);
60052
60053                 // if total distributions margin value is minimal for x, sort by minX,
60054                 // otherwise it's already sorted by minY
60055                 if (xMargin < yMargin) { node.children.sort(compareMinX); }
60056             },
60057
60058             // total margin of all possible split distributions where each node is at least m full
60059             _allDistMargin: function (node, m, M, compare) {
60060
60061                 node.children.sort(compare);
60062
60063                 var toBBox = this.toBBox,
60064                     leftBBox = distBBox$1(node, 0, m, toBBox),
60065                     rightBBox = distBBox$1(node, M - m, M, toBBox),
60066                     margin = bboxMargin$1(leftBBox) + bboxMargin$1(rightBBox),
60067                     i, child;
60068
60069                 for (i = m; i < M - m; i++) {
60070                     child = node.children[i];
60071                     extend$3(leftBBox, node.leaf ? toBBox(child) : child);
60072                     margin += bboxMargin$1(leftBBox);
60073                 }
60074
60075                 for (i = M - m - 1; i >= m; i--) {
60076                     child = node.children[i];
60077                     extend$3(rightBBox, node.leaf ? toBBox(child) : child);
60078                     margin += bboxMargin$1(rightBBox);
60079                 }
60080
60081                 return margin;
60082             },
60083
60084             _adjustParentBBoxes: function (bbox, path, level) {
60085                 // adjust bboxes along the given tree path
60086                 for (var i = level; i >= 0; i--) {
60087                     extend$3(path[i], bbox);
60088                 }
60089             },
60090
60091             _condense: function (path) {
60092                 // go through the path, removing empty nodes and updating bboxes
60093                 for (var i = path.length - 1, siblings; i >= 0; i--) {
60094                     if (path[i].children.length === 0) {
60095                         if (i > 0) {
60096                             siblings = path[i - 1].children;
60097                             siblings.splice(siblings.indexOf(path[i]), 1);
60098
60099                         } else { this.clear(); }
60100
60101                     } else { calcBBox$1(path[i], this.toBBox); }
60102                 }
60103             },
60104
60105             _initFormat: function (format) {
60106                 // data format (minX, minY, maxX, maxY accessors)
60107
60108                 // uses eval-type function compilation instead of just accepting a toBBox function
60109                 // because the algorithms are very sensitive to sorting functions performance,
60110                 // so they should be dead simple and without inner calls
60111
60112                 var compareArr = ['return a', ' - b', ';'];
60113
60114                 this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
60115                 this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
60116
60117                 this.toBBox = new Function('a',
60118                     'return {minX: a' + format[0] +
60119                     ', minY: a' + format[1] +
60120                     ', maxX: a' + format[2] +
60121                     ', maxY: a' + format[3] + '};');
60122             }
60123         };
60124
60125         function findItem$1(item, items, equalsFn) {
60126             if (!equalsFn) { return items.indexOf(item); }
60127
60128             for (var i = 0; i < items.length; i++) {
60129                 if (equalsFn(item, items[i])) { return i; }
60130             }
60131             return -1;
60132         }
60133
60134         // calculate node's bbox from bboxes of its children
60135         function calcBBox$1(node, toBBox) {
60136             distBBox$1(node, 0, node.children.length, toBBox, node);
60137         }
60138
60139         // min bounding rectangle of node children from k to p-1
60140         function distBBox$1(node, k, p, toBBox, destNode) {
60141             if (!destNode) { destNode = createNode$1(null); }
60142             destNode.minX = Infinity;
60143             destNode.minY = Infinity;
60144             destNode.maxX = -Infinity;
60145             destNode.maxY = -Infinity;
60146
60147             for (var i = k, child; i < p; i++) {
60148                 child = node.children[i];
60149                 extend$3(destNode, node.leaf ? toBBox(child) : child);
60150             }
60151
60152             return destNode;
60153         }
60154
60155         function extend$3(a, b) {
60156             a.minX = Math.min(a.minX, b.minX);
60157             a.minY = Math.min(a.minY, b.minY);
60158             a.maxX = Math.max(a.maxX, b.maxX);
60159             a.maxY = Math.max(a.maxY, b.maxY);
60160             return a;
60161         }
60162
60163         function compareNodeMinX$1(a, b) { return a.minX - b.minX; }
60164         function compareNodeMinY$1(a, b) { return a.minY - b.minY; }
60165
60166         function bboxArea$1(a)   { return (a.maxX - a.minX) * (a.maxY - a.minY); }
60167         function bboxMargin$1(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
60168
60169         function enlargedArea$1(a, b) {
60170             return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
60171                    (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
60172         }
60173
60174         function intersectionArea$1(a, b) {
60175             var minX = Math.max(a.minX, b.minX),
60176                 minY = Math.max(a.minY, b.minY),
60177                 maxX = Math.min(a.maxX, b.maxX),
60178                 maxY = Math.min(a.maxY, b.maxY);
60179
60180             return Math.max(0, maxX - minX) *
60181                    Math.max(0, maxY - minY);
60182         }
60183
60184         function contains$2(a, b) {
60185             return a.minX <= b.minX &&
60186                    a.minY <= b.minY &&
60187                    b.maxX <= a.maxX &&
60188                    b.maxY <= a.maxY;
60189         }
60190
60191         function intersects$1(a, b) {
60192             return b.minX <= a.maxX &&
60193                    b.minY <= a.maxY &&
60194                    b.maxX >= a.minX &&
60195                    b.maxY >= a.minY;
60196         }
60197
60198         function createNode$1(children) {
60199             return {
60200                 children: children,
60201                 height: 1,
60202                 leaf: true,
60203                 minX: Infinity,
60204                 minY: Infinity,
60205                 maxX: -Infinity,
60206                 maxY: -Infinity
60207             };
60208         }
60209
60210         // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
60211         // combines selection algorithm with binary divide & conquer approach
60212
60213         function multiSelect$1(arr, left, right, n, compare) {
60214             var stack = [left, right],
60215                 mid;
60216
60217             while (stack.length) {
60218                 right = stack.pop();
60219                 left = stack.pop();
60220
60221                 if (right - left <= n) { continue; }
60222
60223                 mid = left + Math.ceil((right - left) / n / 2) * n;
60224                 quickselect$1(arr, mid, left, right, compare);
60225
60226                 stack.push(left, mid, mid, right);
60227             }
60228         }
60229         rbush_1.default = _default$2;
60230
60231         var lineclip_1$1 = lineclip$1;
60232
60233         lineclip$1.polyline = lineclip$1;
60234         lineclip$1.polygon = polygonclip$1;
60235
60236
60237         // Cohen-Sutherland line clippign algorithm, adapted to efficiently
60238         // handle polylines rather than just segments
60239
60240         function lineclip$1(points, bbox, result) {
60241
60242             var len = points.length,
60243                 codeA = bitCode$1(points[0], bbox),
60244                 part = [],
60245                 i, a, b, codeB, lastCode;
60246
60247             if (!result) { result = []; }
60248
60249             for (i = 1; i < len; i++) {
60250                 a = points[i - 1];
60251                 b = points[i];
60252                 codeB = lastCode = bitCode$1(b, bbox);
60253
60254                 while (true) {
60255
60256                     if (!(codeA | codeB)) { // accept
60257                         part.push(a);
60258
60259                         if (codeB !== lastCode) { // segment went outside
60260                             part.push(b);
60261
60262                             if (i < len - 1) { // start a new line
60263                                 result.push(part);
60264                                 part = [];
60265                             }
60266                         } else if (i === len - 1) {
60267                             part.push(b);
60268                         }
60269                         break;
60270
60271                     } else if (codeA & codeB) { // trivial reject
60272                         break;
60273
60274                     } else if (codeA) { // a outside, intersect with clip edge
60275                         a = intersect$1(a, b, codeA, bbox);
60276                         codeA = bitCode$1(a, bbox);
60277
60278                     } else { // b outside
60279                         b = intersect$1(a, b, codeB, bbox);
60280                         codeB = bitCode$1(b, bbox);
60281                     }
60282                 }
60283
60284                 codeA = lastCode;
60285             }
60286
60287             if (part.length) { result.push(part); }
60288
60289             return result;
60290         }
60291
60292         // Sutherland-Hodgeman polygon clipping algorithm
60293
60294         function polygonclip$1(points, bbox) {
60295
60296             var result, edge, prev, prevInside, i, p, inside;
60297
60298             // clip against each side of the clip rectangle
60299             for (edge = 1; edge <= 8; edge *= 2) {
60300                 result = [];
60301                 prev = points[points.length - 1];
60302                 prevInside = !(bitCode$1(prev, bbox) & edge);
60303
60304                 for (i = 0; i < points.length; i++) {
60305                     p = points[i];
60306                     inside = !(bitCode$1(p, bbox) & edge);
60307
60308                     // if segment goes through the clip window, add an intersection
60309                     if (inside !== prevInside) { result.push(intersect$1(prev, p, edge, bbox)); }
60310
60311                     if (inside) { result.push(p); } // add a point if it's inside
60312
60313                     prev = p;
60314                     prevInside = inside;
60315                 }
60316
60317                 points = result;
60318
60319                 if (!points.length) { break; }
60320             }
60321
60322             return result;
60323         }
60324
60325         // intersect a segment against one of the 4 lines that make up the bbox
60326
60327         function intersect$1(a, b, edge, bbox) {
60328             return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
60329                    edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
60330                    edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
60331                    edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
60332                    null;
60333         }
60334
60335         // bit code reflects the point position relative to the bbox:
60336
60337         //         left  mid  right
60338         //    top  1001  1000  1010
60339         //    mid  0001  0000  0010
60340         // bottom  0101  0100  0110
60341
60342         function bitCode$1(p, bbox) {
60343             var code = 0;
60344
60345             if (p[0] < bbox[0]) { code |= 1; } // left
60346             else if (p[0] > bbox[2]) { code |= 2; } // right
60347
60348             if (p[1] < bbox[1]) { code |= 4; } // bottom
60349             else if (p[1] > bbox[3]) { code |= 8; } // top
60350
60351             return code;
60352         }
60353
60354         var whichPolygon_1 = whichPolygon;
60355
60356         function whichPolygon(data) {
60357             var bboxes = [];
60358             for (var i = 0; i < data.features.length; i++) {
60359                 var feature = data.features[i];
60360                 var coords = feature.geometry.coordinates;
60361
60362                 if (feature.geometry.type === 'Polygon') {
60363                     bboxes.push(treeItem(coords, feature.properties));
60364
60365                 } else if (feature.geometry.type === 'MultiPolygon') {
60366                     for (var j = 0; j < coords.length; j++) {
60367                         bboxes.push(treeItem(coords[j], feature.properties));
60368                     }
60369                 }
60370             }
60371
60372             var tree = rbush_1().load(bboxes);
60373
60374             function query(p, multi) {
60375                 var output = [],
60376                     result = tree.search({
60377                         minX: p[0],
60378                         minY: p[1],
60379                         maxX: p[0],
60380                         maxY: p[1]
60381                     });
60382                 for (var i = 0; i < result.length; i++) {
60383                     if (insidePolygon(result[i].coords, p)) {
60384                         if (multi)
60385                             { output.push(result[i].props); }
60386                         else
60387                             { return result[i].props; }
60388                     }
60389                 }
60390                 return multi && output.length ? output : null;
60391             }
60392
60393             query.tree = tree;
60394             query.bbox = function queryBBox(bbox) {
60395                 var output = [];
60396                 var result = tree.search({
60397                     minX: bbox[0],
60398                     minY: bbox[1],
60399                     maxX: bbox[2],
60400                     maxY: bbox[3]
60401                 });
60402                 for (var i = 0; i < result.length; i++) {
60403                     if (polygonIntersectsBBox(result[i].coords, bbox)) {
60404                         output.push(result[i].props);
60405                     }
60406                 }
60407                 return output;
60408             };
60409
60410             return query;
60411         }
60412
60413         function polygonIntersectsBBox(polygon, bbox) {
60414             var bboxCenter = [
60415                 (bbox[0] + bbox[2]) / 2,
60416                 (bbox[1] + bbox[3]) / 2
60417             ];
60418             if (insidePolygon(polygon, bboxCenter)) { return true; }
60419             for (var i = 0; i < polygon.length; i++) {
60420                 if (lineclip_1$1(polygon[i], bbox).length > 0) { return true; }
60421             }
60422             return false;
60423         }
60424
60425         // ray casting algorithm for detecting if point is in polygon
60426         function insidePolygon(rings, p) {
60427             var inside = false;
60428             for (var i = 0, len = rings.length; i < len; i++) {
60429                 var ring = rings[i];
60430                 for (var j = 0, len2 = ring.length, k = len2 - 1; j < len2; k = j++) {
60431                     if (rayIntersect(p, ring[j], ring[k])) { inside = !inside; }
60432                 }
60433             }
60434             return inside;
60435         }
60436
60437         function rayIntersect(p, p1, p2) {
60438             return ((p1[1] > p[1]) !== (p2[1] > p[1])) && (p[0] < (p2[0] - p1[0]) * (p[1] - p1[1]) / (p2[1] - p1[1]) + p1[0]);
60439         }
60440
60441         function treeItem(coords, props) {
60442             var item = {
60443                 minX: Infinity,
60444                 minY: Infinity,
60445                 maxX: -Infinity,
60446                 maxY: -Infinity,
60447                 coords: coords,
60448                 props: props
60449             };
60450
60451             for (var i = 0; i < coords[0].length; i++) {
60452                 var p = coords[0][i];
60453                 item.minX = Math.min(item.minX, p[0]);
60454                 item.minY = Math.min(item.minY, p[1]);
60455                 item.maxX = Math.max(item.maxX, p[0]);
60456                 item.maxY = Math.max(item.maxY, p[1]);
60457             }
60458             return item;
60459         }
60460
60461         var type = "FeatureCollection";
60462         var features = [{type:"Feature",properties:{m49:"680",wikidata:"Q3405693",nameEn:"Sark",country:"GB",groups:["GG","830","154","150"],level:"subterritory",driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44 01481"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.36485,49.48223],[-2.65349,49.15373],[-2.09454,49.46288],[-2.36485,49.48223]]]]}},{type:"Feature",properties:{m49:"001",wikidata:"Q2",nameEn:"World",aliases:["Earth","Planet"],level:"world"},geometry:null},{type:"Feature",properties:{m49:"142",wikidata:"Q48",nameEn:"Asia",level:"region"},geometry:null},{type:"Feature",properties:{m49:"143",wikidata:"Q27275",nameEn:"Central Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"145",wikidata:"Q27293",nameEn:"Western Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"150",wikidata:"Q46",nameEn:"Europe",level:"region"},geometry:null},{type:"Feature",properties:{m49:"151",wikidata:"Q27468",nameEn:"Eastern Europe",groups:["150"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"154",wikidata:"Q27479",nameEn:"Northern Europe",groups:["150"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"155",wikidata:"Q27496",nameEn:"Western Europe",groups:["150"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"202",wikidata:"Q132959",nameEn:"Sub-Saharan Africa",groups:["002"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"419",wikidata:"Q72829598",nameEn:"Latin America and the Caribbean",groups:["019"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"830",wikidata:"Q42314",nameEn:"Channel Islands",groups:["150","154"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"019",wikidata:"Q828",nameEn:"Americas",level:"region"},geometry:null},{type:"Feature",properties:{m49:"029",wikidata:"Q664609",nameEn:"Caribbean",groups:["419","019","003"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"034",wikidata:"Q771405",nameEn:"Southern Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"002",wikidata:"Q15",nameEn:"Africa",level:"region"},geometry:null},{type:"Feature",properties:{m49:"003",wikidata:"Q49",nameEn:"North America",groups:["019"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"017",wikidata:"Q27433",nameEn:"Middle Africa",groups:["202","002"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"039",wikidata:"Q27449",nameEn:"Southern Europe",groups:["150"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"005",wikidata:"Q18",nameEn:"South America",groups:["419","019"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"009",wikidata:"Q538",nameEn:"Oceania",level:"region"},geometry:null},{type:"Feature",properties:{m49:"061",wikidata:"Q35942",nameEn:"Polynesia",groups:["009"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"014",wikidata:"Q27407",nameEn:"Eastern Africa",groups:["202","002"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"053",wikidata:"Q45256",nameEn:"Australia and New Zealand",aliases:["Australasia"],groups:["009"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"011",wikidata:"Q4412",nameEn:"Western Africa",groups:["202","002"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"013",wikidata:"Q27611",nameEn:"Central America",groups:["419","019","003"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"021",wikidata:"Q2017699",nameEn:"Northern America",groups:["019","003"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"035",wikidata:"Q11708",nameEn:"South-eastern Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"018",wikidata:"Q27394",nameEn:"Southern Africa",groups:["202","002"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"030",wikidata:"Q27231",nameEn:"Eastern Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"015",wikidata:"Q27381",nameEn:"Northern Africa",groups:["002"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"054",wikidata:"Q37394",nameEn:"Melanesia",groups:["009"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"057",wikidata:"Q3359409",nameEn:"Micronesia",groups:["009"],level:"subregion"},geometry:null},{type:"Feature",properties:{iso1A2:"AC",iso1A3:"ASC",wikidata:"Q46197",nameEn:"Ascension Island",country:"GB",groups:["SH","011","202","002"],isoStatus:"excRes",driveSide:"left",roadSpeedUnit:"mph",callingCodes:["247"]},geometry:{type:"MultiPolygon",coordinates:[[[[-14.82771,-8.70814],[-13.33271,-8.07391],[-14.91926,-6.63386],[-14.82771,-8.70814]]]]}},{type:"Feature",properties:{iso1A2:"AD",iso1A3:"AND",iso1N3:"020",wikidata:"Q228",nameEn:"Andorra",groups:["039","150"],callingCodes:["376"]},geometry:{type:"MultiPolygon",coordinates:[[[[1.72515,42.50338],[1.73683,42.55492],[1.7858,42.57698],[1.72588,42.59098],[1.73452,42.61515],[1.68267,42.62533],[1.6625,42.61982],[1.63485,42.62957],[1.60085,42.62703],[1.55418,42.65669],[1.50867,42.64483],[1.48043,42.65203],[1.46718,42.63296],[1.47986,42.61346],[1.44197,42.60217],[1.42512,42.58292],[1.44529,42.56722],[1.4234,42.55959],[1.41245,42.53539],[1.44759,42.54431],[1.46661,42.50949],[1.41648,42.48315],[1.43838,42.47848],[1.44529,42.43724],[1.5127,42.42959],[1.55073,42.43299],[1.55937,42.45808],[1.57953,42.44957],[1.58933,42.46275],[1.65674,42.47125],[1.66826,42.50779],[1.70571,42.48867],[1.72515,42.50338]]]]}},{type:"Feature",properties:{iso1A2:"AE",iso1A3:"ARE",iso1N3:"784",wikidata:"Q878",nameEn:"United Arab Emirates",groups:["145","142"],callingCodes:["971"]},geometry:{type:"MultiPolygon",coordinates:[[[[56.26534,25.62825],[56.25341,25.61443],[56.26636,25.60643],[56.25365,25.60211],[56.20473,25.61119],[56.18363,25.65508],[56.14826,25.66351],[56.13579,25.73524],[56.17416,25.77239],[56.13963,25.82765],[56.19334,25.9795],[56.15498,26.06828],[56.08666,26.05038],[55.81777,26.18798],[55.14145,25.62624],[53.97892,24.64436],[52.82259,25.51697],[52.35509,25.00368],[52.02277,24.75635],[51.83108,24.71675],[51.58834,24.66608],[51.41644,24.39615],[51.58871,24.27256],[51.59617,24.12041],[52.56622,22.94341],[55.13599,22.63334],[55.2137,22.71065],[55.22634,23.10378],[55.57358,23.669],[55.48677,23.94946],[55.73301,24.05994],[55.8308,24.01633],[56.01799,24.07426],[55.95472,24.2172],[55.83367,24.20193],[55.77658,24.23476],[55.76558,24.23227],[55.75257,24.23466],[55.75382,24.2466],[55.75939,24.26114],[55.76781,24.26209],[55.79145,24.27914],[55.80747,24.31069],[55.83395,24.32776],[55.83271,24.41521],[55.76461,24.5287],[55.83271,24.68567],[55.83408,24.77858],[55.81348,24.80102],[55.81116,24.9116],[55.85094,24.96858],[55.90849,24.96771],[55.96316,25.00857],[56.05715,24.95727],[56.05106,24.87461],[55.97467,24.89639],[55.97836,24.87673],[56.03535,24.81161],[56.06128,24.74457],[56.13684,24.73699],[56.20062,24.78565],[56.20568,24.85063],[56.30269,24.88334],[56.34873,24.93205],[56.3227,24.97284],[56.86325,25.03856],[56.82555,25.7713],[56.26534,25.62825]],[[56.26062,25.33108],[56.3005,25.31815],[56.3111,25.30107],[56.35172,25.30681],[56.34438,25.26653],[56.27628,25.23404],[56.24341,25.22867],[56.20872,25.24104],[56.20838,25.25668],[56.24465,25.27505],[56.25008,25.28843],[56.23362,25.31253],[56.26062,25.33108]]],[[[56.28423,25.26344],[56.29379,25.2754],[56.28102,25.28486],[56.2716,25.27916],[56.27086,25.26128],[56.28423,25.26344]]]]}},{type:"Feature",properties:{iso1A2:"AF",iso1A3:"AFG",iso1N3:"004",wikidata:"Q889",nameEn:"Afghanistan",groups:["034","142"],callingCodes:["93"]},geometry:{type:"MultiPolygon",coordinates:[[[[70.61526,38.34774],[70.60407,38.28046],[70.54673,38.24541],[70.4898,38.12546],[70.17206,37.93276],[70.1863,37.84296],[70.27694,37.81258],[70.28243,37.66706],[70.15015,37.52519],[69.95971,37.5659],[69.93362,37.61378],[69.84435,37.60616],[69.80041,37.5746],[69.51888,37.5844],[69.44954,37.4869],[69.36645,37.40462],[69.45022,37.23315],[69.39529,37.16752],[69.25152,37.09426],[69.03274,37.25174],[68.96407,37.32603],[68.88168,37.33368],[68.91189,37.26704],[68.80889,37.32494],[68.81438,37.23862],[68.6798,37.27906],[68.61851,37.19815],[68.41888,37.13906],[68.41201,37.10402],[68.29253,37.10621],[68.27605,37.00977],[68.18542,37.02074],[68.02194,36.91923],[67.87917,37.0591],[67.7803,37.08978],[67.78329,37.1834],[67.51868,37.26102],[67.2581,37.17216],[67.2224,37.24545],[67.13039,37.27168],[67.08232,37.35469],[66.95598,37.40162],[66.64699,37.32958],[66.55743,37.35409],[66.30993,37.32409],[65.72274,37.55438],[65.64137,37.45061],[65.64263,37.34388],[65.51778,37.23881],[64.97945,37.21913],[64.61141,36.6351],[64.62514,36.44311],[64.57295,36.34362],[64.43288,36.24401],[64.05385,36.10433],[63.98519,36.03773],[63.56496,35.95106],[63.53475,35.90881],[63.29579,35.85985],[63.12276,35.86208],[63.10318,35.81782],[63.23262,35.67487],[63.10079,35.63024],[63.12276,35.53196],[63.0898,35.43131],[62.90853,35.37086],[62.74098,35.25432],[62.62288,35.22067],[62.48006,35.28796],[62.29878,35.13312],[62.29191,35.25964],[62.15871,35.33278],[62.05709,35.43803],[61.97743,35.4604],[61.77693,35.41341],[61.58742,35.43803],[61.27371,35.61482],[61.18187,35.30249],[61.0991,35.27845],[61.12831,35.09938],[61.06926,34.82139],[61.00197,34.70631],[60.99922,34.63064],[60.72316,34.52857],[60.91321,34.30411],[60.66502,34.31539],[60.50209,34.13992],[60.5838,33.80793],[60.5485,33.73422],[60.57762,33.59772],[60.69573,33.56054],[60.91133,33.55596],[60.88908,33.50219],[60.56485,33.12944],[60.86191,32.22565],[60.84541,31.49561],[61.70929,31.37391],[61.80569,31.16167],[61.80957,31.12576],[61.83257,31.0452],[61.8335,30.97669],[61.78268,30.92724],[61.80829,30.84224],[60.87231,29.86514],[62.47751,29.40782],[63.5876,29.50456],[64.12966,29.39157],[64.19796,29.50407],[64.62116,29.58903],[65.04005,29.53957],[66.24175,29.85181],[66.36042,29.9583],[66.23609,30.06321],[66.34869,30.404],[66.28413,30.57001],[66.39194,30.9408],[66.42645,30.95309],[66.58175,30.97532],[66.68166,31.07597],[66.72561,31.20526],[66.83273,31.26867],[67.04147,31.31561],[67.03323,31.24519],[67.29964,31.19586],[67.78854,31.33203],[67.7748,31.4188],[67.62374,31.40473],[67.58323,31.52772],[67.72056,31.52304],[67.86887,31.63536],[68.00071,31.6564],[68.1655,31.82691],[68.25614,31.80357],[68.27605,31.75863],[68.44222,31.76446],[68.57475,31.83158],[68.6956,31.75687],[68.79997,31.61665],[68.91078,31.59687],[68.95995,31.64822],[69.00939,31.62249],[69.11514,31.70782],[69.20577,31.85957],[69.3225,31.93186],[69.27032,32.14141],[69.27932,32.29119],[69.23599,32.45946],[69.2868,32.53938],[69.38155,32.56601],[69.44747,32.6678],[69.43649,32.7302],[69.38018,32.76601],[69.47082,32.85834],[69.5436,32.8768],[69.49854,32.88843],[69.49004,33.01509],[69.57656,33.09911],[69.71526,33.09911],[69.79766,33.13247],[69.85259,33.09451],[70.02563,33.14282],[70.07369,33.22557],[70.13686,33.21064],[70.32775,33.34496],[70.17062,33.53535],[70.20141,33.64387],[70.14785,33.6553],[70.14236,33.71701],[70.00503,33.73528],[69.85671,33.93719],[69.87307,33.9689],[69.90203,34.04194],[70.54336,33.9463],[70.88119,33.97933],[71.07345,34.06242],[71.06933,34.10564],[71.09307,34.11961],[71.09453,34.13524],[71.13078,34.16503],[71.12815,34.26619],[71.17662,34.36769],[71.02401,34.44835],[71.0089,34.54568],[71.11602,34.63047],[71.08718,34.69034],[71.28356,34.80882],[71.29472,34.87728],[71.50329,34.97328],[71.49917,35.00478],[71.55273,35.02615],[71.52938,35.09023],[71.67495,35.21262],[71.5541,35.28776],[71.54294,35.31037],[71.65435,35.4479],[71.49917,35.6267],[71.55273,35.71483],[71.37969,35.95865],[71.19505,36.04134],[71.60491,36.39429],[71.80267,36.49924],[72.18135,36.71838],[72.6323,36.84601],[73.82685,36.91421],[74.04856,36.82648],[74.43389,37.00977],[74.53739,36.96224],[74.56453,37.03023],[74.49981,37.24518],[74.80605,37.21565],[74.88887,37.23275],[74.8294,37.3435],[74.68383,37.3948],[74.56161,37.37734],[74.41055,37.3948],[74.23339,37.41116],[74.20308,37.34208],[73.8564,37.26158],[73.82552,37.22659],[73.64974,37.23643],[73.61129,37.27469],[73.76647,37.33913],[73.77197,37.4417],[73.29633,37.46495],[73.06884,37.31729],[72.79693,37.22222],[72.66381,37.02014],[72.54095,37.00007],[72.31676,36.98115],[71.83229,36.68084],[71.67083,36.67346],[71.57195,36.74943],[71.51502,36.89128],[71.48481,36.93218],[71.46923,36.99925],[71.45578,37.03094],[71.43097,37.05855],[71.44127,37.11856],[71.4494,37.18137],[71.4555,37.21418],[71.47386,37.2269],[71.48339,37.23937],[71.4824,37.24921],[71.48536,37.26017],[71.50674,37.31502],[71.49821,37.31975],[71.4862,37.33405],[71.47685,37.40281],[71.49612,37.4279],[71.5256,37.47971],[71.50616,37.50733],[71.49693,37.53527],[71.5065,37.60912],[71.51972,37.61945],[71.54186,37.69691],[71.55234,37.73209],[71.53053,37.76534],[71.54324,37.77104],[71.55752,37.78677],[71.59255,37.79956],[71.58843,37.92425],[71.51565,37.95349],[71.32871,37.88564],[71.296,37.93403],[71.2809,37.91995],[71.24969,37.93031],[71.27278,37.96496],[71.27622,37.99946],[71.28922,38.01272],[71.29878,38.04429],[71.36444,38.15358],[71.37803,38.25641],[71.33869,38.27335],[71.33114,38.30339],[71.21291,38.32797],[71.1451,38.40106],[71.10957,38.40671],[71.10592,38.42077],[71.09542,38.42517],[71.0556,38.40176],[71.03545,38.44779],[70.98693,38.48862],[70.92728,38.43021],[70.88719,38.46826],[70.84376,38.44688],[70.82538,38.45394],[70.81697,38.44507],[70.80521,38.44447],[70.79766,38.44944],[70.78702,38.45031],[70.78581,38.45502],[70.77132,38.45548],[70.75455,38.4252],[70.72485,38.4131],[70.69807,38.41861],[70.67438,38.40597],[70.6761,38.39144],[70.69189,38.37031],[70.64966,38.34999],[70.61526,38.34774]]]]}},{type:"Feature",properties:{iso1A2:"AG",iso1A3:"ATG",iso1N3:"028",wikidata:"Q781",nameEn:"Antigua and Barbuda",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 268"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.12601,17.9235],[-62.27053,17.22145],[-62.62949,16.82364],[-62.52079,16.69392],[-62.14123,17.02632],[-61.83929,16.66647],[-61.44461,16.81958],[-61.45764,17.9187],[-62.12601,17.9235]]]]}},{type:"Feature",properties:{iso1A2:"AI",iso1A3:"AIA",iso1N3:"660",wikidata:"Q25228",nameEn:"Anguilla",country:"GB",groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 264"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.83866,18.82518],[-63.35989,18.06012],[-62.86666,18.19278],[-62.75637,18.13489],[-62.46233,19.00569],[-63.83866,18.82518]]]]}},{type:"Feature",properties:{iso1A2:"AL",iso1A3:"ALB",iso1N3:"008",wikidata:"Q222",nameEn:"Albania",groups:["039","150"],callingCodes:["355"]},geometry:{type:"MultiPolygon",coordinates:[[[[20.07761,42.55582],[20.01834,42.54622],[20.00842,42.5109],[19.9324,42.51699],[19.82333,42.46581],[19.76549,42.50237],[19.74731,42.57422],[19.77375,42.58517],[19.73244,42.66299],[19.65972,42.62774],[19.4836,42.40831],[19.42352,42.36546],[19.42,42.33019],[19.28623,42.17745],[19.40687,42.10024],[19.37548,42.06835],[19.36867,42.02564],[19.37691,41.96977],[19.34601,41.95675],[19.33812,41.90669],[19.37451,41.8842],[19.37597,41.84849],[19.26406,41.74971],[19.0384,40.35325],[19.95905,39.82857],[19.97622,39.78684],[19.92466,39.69533],[19.98042,39.6504],[20.00957,39.69227],[20.05189,39.69112],[20.12956,39.65805],[20.15988,39.652],[20.22376,39.64532],[20.22707,39.67459],[20.27412,39.69884],[20.31961,39.72799],[20.29152,39.80421],[20.30804,39.81563],[20.38572,39.78516],[20.41475,39.81437],[20.41546,39.82832],[20.31135,39.99438],[20.37911,39.99058],[20.42373,40.06777],[20.48487,40.06271],[20.51297,40.08168],[20.55593,40.06524],[20.61081,40.07866],[20.62566,40.0897],[20.67162,40.09433],[20.71789,40.27739],[20.78234,40.35803],[20.7906,40.42726],[20.83688,40.47882],[20.94925,40.46625],[20.96908,40.51526],[21.03932,40.56299],[21.05833,40.66586],[20.98134,40.76046],[20.95752,40.76982],[20.98396,40.79109],[20.97887,40.85475],[20.97693,40.90103],[20.94305,40.92399],[20.83671,40.92752],[20.81567,40.89662],[20.73504,40.9081],[20.71634,40.91781],[20.65558,41.08009],[20.63454,41.0889],[20.59832,41.09066],[20.58546,41.11179],[20.59715,41.13644],[20.51068,41.2323],[20.49432,41.33679],[20.52119,41.34381],[20.55976,41.4087],[20.51301,41.442],[20.49039,41.49277],[20.45331,41.51436],[20.45809,41.5549],[20.52103,41.56473],[20.55508,41.58113],[20.51769,41.65975],[20.52937,41.69292],[20.51301,41.72433],[20.53405,41.78099],[20.57144,41.7897],[20.55976,41.87068],[20.59524,41.8818],[20.57946,41.91593],[20.63069,41.94913],[20.59434,42.03879],[20.55633,42.08173],[20.56955,42.12097],[20.48857,42.25444],[20.3819,42.3029],[20.34479,42.32656],[20.24399,42.32168],[20.21797,42.41237],[20.17127,42.50469],[20.07761,42.55582]]]]}},{type:"Feature",properties:{iso1A2:"AM",iso1A3:"ARM",iso1N3:"051",wikidata:"Q399",nameEn:"Armenia",groups:["145","142"],callingCodes:["374"]},geometry:{type:"MultiPolygon",coordinates:[[[[45.0133,41.29747],[44.93493,41.25685],[44.81437,41.30371],[44.80053,41.25949],[44.81749,41.23488],[44.84358,41.23088],[44.89911,41.21366],[44.87887,41.20195],[44.82084,41.21513],[44.72814,41.20338],[44.61462,41.24018],[44.59322,41.1933],[44.46791,41.18204],[44.34417,41.2382],[44.34337,41.20312],[44.32139,41.2079],[44.18148,41.24644],[44.16591,41.19141],[43.84835,41.16329],[43.74717,41.1117],[43.67712,41.13398],[43.4717,41.12611],[43.44984,41.0988],[43.47319,41.02251],[43.58683,40.98961],[43.67712,40.93084],[43.67712,40.84846],[43.74872,40.7365],[43.7425,40.66805],[43.63664,40.54159],[43.54791,40.47413],[43.60862,40.43267],[43.59928,40.34019],[43.71136,40.16673],[43.65221,40.14889],[43.65688,40.11199],[43.92307,40.01787],[44.1057,40.03555],[44.1778,40.02845],[44.26973,40.04866],[44.46635,39.97733],[44.61845,39.8281],[44.75779,39.7148],[44.88354,39.74432],[44.92869,39.72157],[45.06604,39.79277],[45.18554,39.67846],[45.17464,39.58614],[45.21784,39.58074],[45.23535,39.61373],[45.30385,39.61373],[45.29606,39.57654],[45.46992,39.49888],[45.70547,39.60174],[45.80804,39.56716],[45.83,39.46487],[45.79225,39.3695],[45.99774,39.28931],[46.02303,39.09978],[46.06973,39.0744],[46.14785,38.84206],[46.20601,38.85262],[46.34059,38.92076],[46.53497,38.86548],[46.51805,38.94982],[46.54296,39.07078],[46.44022,39.19636],[46.52584,39.18912],[46.54141,39.15895],[46.58032,39.21204],[46.63481,39.23013],[46.56476,39.24942],[46.50093,39.33736],[46.43244,39.35181],[46.37795,39.42039],[46.4013,39.45405],[46.53051,39.47809],[46.51027,39.52373],[46.57721,39.54414],[46.57098,39.56694],[46.52117,39.58734],[46.42465,39.57534],[46.40286,39.63651],[46.18493,39.60533],[45.96543,39.78859],[45.82533,39.82925],[45.7833,39.9475],[45.60895,39.97733],[45.59806,40.0131],[45.78642,40.03218],[45.83779,39.98925],[45.97944,40.181],[45.95609,40.27846],[45.65098,40.37696],[45.42994,40.53804],[45.45484,40.57707],[45.35366,40.65979],[45.4206,40.7424],[45.55914,40.78366],[45.60584,40.87436],[45.40814,40.97904],[45.44083,41.01663],[45.39725,41.02603],[45.35677,40.99784],[45.28859,41.03757],[45.26162,41.0228],[45.25897,41.0027],[45.1994,41.04518],[45.16493,41.05068],[45.1634,41.08082],[45.1313,41.09369],[45.12923,41.06059],[45.06784,41.05379],[45.08028,41.10917],[45.19942,41.13299],[45.1969,41.168],[45.11811,41.19967],[45.05201,41.19211],[45.02932,41.2101],[45.05497,41.2464],[45.0133,41.29747]],[[45.21324,40.9817],[45.21219,40.99001],[45.20518,40.99348],[45.19312,40.98998],[45.18382,41.0066],[45.20625,41.01484],[45.23487,41.00226],[45.23095,40.97828],[45.21324,40.9817]],[[45.00864,41.03411],[44.9903,41.05657],[44.96031,41.06345],[44.95383,41.07553],[44.97169,41.09176],[45.00864,41.09407],[45.03406,41.07931],[45.04517,41.06653],[45.03792,41.03938],[45.00864,41.03411]]],[[[45.50279,40.58424],[45.56071,40.64765],[45.51825,40.67382],[45.47927,40.65023],[45.50279,40.58424]]]]}},{type:"Feature",properties:{iso1A2:"AO",iso1A3:"AGO",iso1N3:"024",wikidata:"Q916",nameEn:"Angola",groups:["017","202","002"],callingCodes:["244"]},geometry:{type:"MultiPolygon",coordinates:[[[[16.55507,-5.85631],[13.04371,-5.87078],[12.42245,-6.07585],[11.95767,-5.94705],[12.20376,-5.76338],[12.26557,-5.74031],[12.52318,-5.74353],[12.52301,-5.17481],[12.53599,-5.1618],[12.53586,-5.14658],[12.51589,-5.1332],[12.49815,-5.14058],[12.46297,-5.09408],[12.60251,-5.01715],[12.63465,-4.94632],[12.70868,-4.95505],[12.8733,-4.74346],[13.11195,-4.67745],[13.09648,-4.63739],[12.91489,-4.47907],[12.87096,-4.40315],[12.76844,-4.38709],[12.64835,-4.55937],[12.40964,-4.60609],[12.32324,-4.78415],[12.25587,-4.79437],[12.20901,-4.75642],[12.16068,-4.90089],[12.00924,-5.02627],[11.50888,-5.33417],[10.5065,-17.25284],[11.75063,-17.25013],[12.07076,-17.15165],[12.52111,-17.24495],[12.97145,-16.98567],[13.36212,-16.98048],[13.95896,-17.43141],[14.28743,-17.38814],[18.39229,-17.38927],[18.84226,-17.80375],[21.14283,-17.94318],[21.42741,-18.02787],[23.47474,-17.62877],[23.20038,-17.47563],[22.17217,-16.50269],[22.00323,-16.18028],[21.97988,-13.00148],[24.03339,-12.99091],[23.90937,-12.844],[24.06672,-12.29058],[23.98804,-12.13149],[24.02603,-11.15368],[24.00027,-10.89356],[23.86868,-11.02856],[23.45631,-10.946],[23.16602,-11.10577],[22.54205,-11.05784],[22.25951,-11.24911],[22.17954,-10.85884],[22.32604,-10.76291],[22.19039,-9.94628],[21.84856,-9.59871],[21.79824,-7.29628],[20.56263,-7.28566],[20.61689,-6.90876],[20.31846,-6.91953],[20.30218,-6.98955],[19.5469,-7.00195],[19.33698,-7.99743],[18.33635,-8.00126],[17.5828,-8.13784],[16.96282,-7.21787],[16.55507,-5.85631]]]]}},{type:"Feature",properties:{iso1A2:"AQ",iso1A3:"ATA",iso1N3:"010",wikidata:"Q51",nameEn:"Antarctica",level:"region",callingCodes:["672"]},geometry:{type:"MultiPolygon",coordinates:[[[[180,-60],[-180,-60],[-180,-90],[180,-90],[180,-60]]]]}},{type:"Feature",properties:{iso1A2:"AR",iso1A3:"ARG",iso1N3:"032",wikidata:"Q414",nameEn:"Argentina",aliases:["RA"],groups:["005","419","019"],callingCodes:["54"]},geometry:{type:"MultiPolygon",coordinates:[[[[-72.31343,-50.58411],[-72.33873,-51.59954],[-71.99889,-51.98018],[-69.97824,-52.00845],[-68.41683,-52.33516],[-68.60702,-52.65781],[-68.60733,-54.9125],[-68.01394,-54.8753],[-67.46182,-54.92205],[-67.11046,-54.94199],[-66.07313,-55.19618],[-63.67376,-55.11859],[-54.78916,-36.21945],[-57.83001,-34.69099],[-58.34425,-34.15035],[-58.44442,-33.84033],[-58.40475,-33.11777],[-58.1224,-32.98842],[-58.22362,-32.52416],[-58.10036,-32.25338],[-58.20252,-31.86966],[-58.00076,-31.65016],[-58.0023,-31.53084],[-58.07569,-31.44916],[-57.98127,-31.3872],[-57.9908,-31.34924],[-57.86729,-31.06352],[-57.89476,-30.95994],[-57.8024,-30.77193],[-57.89115,-30.49572],[-57.64859,-30.35095],[-57.61478,-30.25165],[-57.65132,-30.19229],[-57.09386,-29.74211],[-56.81251,-29.48154],[-56.62789,-29.18073],[-56.57295,-29.11357],[-56.54171,-29.11447],[-56.05265,-28.62651],[-56.00458,-28.60421],[-56.01729,-28.51223],[-55.65418,-28.18304],[-55.6262,-28.17124],[-55.33303,-27.94661],[-55.16872,-27.86224],[-55.1349,-27.89759],[-54.90805,-27.73149],[-54.90159,-27.63132],[-54.67657,-27.57214],[-54.50416,-27.48232],[-54.41888,-27.40882],[-54.19268,-27.30751],[-54.19062,-27.27639],[-54.15978,-27.2889],[-53.80144,-27.09844],[-53.73372,-26.6131],[-53.68269,-26.33359],[-53.64505,-26.28089],[-53.64186,-26.25976],[-53.64632,-26.24798],[-53.63881,-26.25075],[-53.63739,-26.2496],[-53.65237,-26.23289],[-53.65018,-26.19501],[-53.73968,-26.10012],[-53.73391,-26.07006],[-53.7264,-26.0664],[-53.73086,-26.05842],[-53.73511,-26.04211],[-53.83691,-25.94849],[-53.90831,-25.55513],[-54.52926,-25.62846],[-54.5502,-25.58915],[-54.59398,-25.59224],[-54.62063,-25.91213],[-54.60664,-25.9691],[-54.67359,-25.98607],[-54.69333,-26.37705],[-54.70732,-26.45099],[-54.80868,-26.55669],[-55.00584,-26.78754],[-55.06351,-26.80195],[-55.16948,-26.96068],[-55.25243,-26.93808],[-55.39611,-26.97679],[-55.62322,-27.1941],[-55.59094,-27.32444],[-55.74475,-27.44485],[-55.89195,-27.3467],[-56.18313,-27.29851],[-56.85337,-27.5165],[-58.04205,-27.2387],[-58.59549,-27.29973],[-58.65321,-27.14028],[-58.3198,-26.83443],[-58.1188,-26.16704],[-57.87176,-25.93604],[-57.57431,-25.47269],[-57.80821,-25.13863],[-58.25492,-24.92528],[-58.33055,-24.97099],[-59.33886,-24.49935],[-59.45482,-24.34787],[-60.03367,-24.00701],[-60.28163,-24.04436],[-60.99754,-23.80934],[-61.0782,-23.62932],[-61.9756,-23.0507],[-62.22768,-22.55807],[-62.51761,-22.37684],[-62.64455,-22.25091],[-62.8078,-22.12534],[-62.81124,-21.9987],[-63.66482,-21.99918],[-63.68113,-22.0544],[-63.70963,-21.99934],[-63.93287,-21.99934],[-64.22918,-22.55807],[-64.31489,-22.88824],[-64.35108,-22.73282],[-64.4176,-22.67692],[-64.58888,-22.25035],[-64.67174,-22.18957],[-64.90014,-22.12136],[-64.99524,-22.08255],[-65.47435,-22.08908],[-65.57743,-22.07675],[-65.58694,-22.09794],[-65.61166,-22.09504],[-65.7467,-22.10105],[-65.9261,-21.93335],[-66.04832,-21.9187],[-66.03836,-21.84829],[-66.24077,-21.77837],[-66.29714,-22.08741],[-66.7298,-22.23644],[-67.18382,-22.81525],[-66.99632,-22.99839],[-67.33563,-24.04237],[-68.24825,-24.42596],[-68.56909,-24.69831],[-68.38372,-25.08636],[-68.57622,-25.32505],[-68.38372,-26.15353],[-68.56909,-26.28146],[-68.59048,-26.49861],[-68.27677,-26.90626],[-68.43363,-27.08414],[-68.77586,-27.16029],[-69.22504,-27.95042],[-69.66709,-28.44055],[-69.80969,-29.07185],[-69.99507,-29.28351],[-69.8596,-30.26131],[-70.14479,-30.36595],[-70.55832,-31.51559],[-69.88099,-33.34489],[-69.87386,-34.13344],[-70.49416,-35.24145],[-70.38008,-36.02375],[-70.95047,-36.4321],[-71.24279,-37.20264],[-70.89532,-38.6923],[-71.37826,-38.91474],[-71.92726,-40.72714],[-71.74901,-42.11711],[-72.15541,-42.15941],[-72.14828,-42.85321],[-71.64206,-43.64774],[-71.81318,-44.38097],[-71.16436,-44.46244],[-71.26418,-44.75684],[-72.06985,-44.81756],[-71.35687,-45.22075],[-71.75614,-45.61611],[-71.68577,-46.55385],[-71.94152,-47.13595],[-72.50478,-47.80586],[-72.27662,-48.28727],[-72.54042,-48.52392],[-72.56894,-48.81116],[-73.09655,-49.14342],[-73.45156,-49.79461],[-73.55259,-49.92488],[-73.15765,-50.78337],[-72.31343,-50.58411]]]]}},{type:"Feature",properties:{iso1A2:"AS",iso1A3:"ASM",iso1N3:"016",wikidata:"Q16641",nameEn:"American Samoa",country:"US",groups:["061","009"],roadSpeedUnit:"mph",callingCodes:["1 684"]},geometry:{type:"MultiPolygon",coordinates:[[[[-174.18596,-12.48057],[-171.14953,-12.4725],[-171.14262,-14.93704],[-167.73854,-14.92809],[-167.75195,-10.12005],[-174.17993,-10.13616],[-174.18596,-12.48057]]]]}},{type:"Feature",properties:{iso1A2:"AT",iso1A3:"AUT",iso1N3:"040",wikidata:"Q40",nameEn:"Austria",groups:["EU","155","150"],callingCodes:["43"]},geometry:{type:"MultiPolygon",coordinates:[[[[15.34823,48.98444],[15.28305,48.98831],[15.26177,48.95766],[15.16358,48.94278],[15.15534,48.99056],[14.99878,49.01444],[14.97612,48.96983],[14.98917,48.90082],[14.95072,48.79101],[14.98032,48.77959],[14.9782,48.7766],[14.98112,48.77524],[14.9758,48.76857],[14.95641,48.75915],[14.94773,48.76268],[14.81545,48.7874],[14.80821,48.77711],[14.80584,48.73489],[14.72756,48.69502],[14.71794,48.59794],[14.66762,48.58215],[14.60808,48.62881],[14.56139,48.60429],[14.4587,48.64695],[14.43076,48.58855],[14.33909,48.55852],[14.20691,48.5898],[14.09104,48.5943],[14.01482,48.63788],[14.06151,48.66873],[13.84023,48.76988],[13.82266,48.75544],[13.81863,48.73257],[13.79337,48.71375],[13.81791,48.69832],[13.81283,48.68426],[13.81901,48.6761],[13.82609,48.62345],[13.80038,48.59487],[13.80519,48.58026],[13.76921,48.55324],[13.7513,48.5624],[13.74816,48.53058],[13.72802,48.51208],[13.66113,48.53558],[13.65186,48.55092],[13.62508,48.55501],[13.59705,48.57013],[13.57535,48.55912],[13.51291,48.59023],[13.50131,48.58091],[13.50663,48.57506],[13.46967,48.55157],[13.45214,48.56472],[13.43695,48.55776],[13.45727,48.51092],[13.42527,48.45711],[13.43929,48.43386],[13.40709,48.37292],[13.30897,48.31575],[13.26039,48.29422],[13.18093,48.29577],[13.126,48.27867],[13.0851,48.27711],[13.02083,48.25689],[12.95306,48.20629],[12.87126,48.20318],[12.84475,48.16556],[12.836,48.1647],[12.8362,48.15876],[12.82673,48.15245],[12.80676,48.14979],[12.78595,48.12445],[12.7617,48.12796],[12.74973,48.10885],[12.76141,48.07373],[12.8549,48.01122],[12.87476,47.96195],[12.91683,47.95647],[12.9211,47.95135],[12.91985,47.94069],[12.92668,47.93879],[12.93419,47.94063],[12.93642,47.94436],[12.93886,47.94046],[12.94163,47.92927],[13.00588,47.84374],[12.98543,47.82896],[12.96311,47.79957],[12.93202,47.77302],[12.94371,47.76281],[12.9353,47.74788],[12.91711,47.74026],[12.90274,47.72513],[12.91333,47.7178],[12.92969,47.71094],[12.98578,47.7078],[13.01382,47.72116],[13.07692,47.68814],[13.09562,47.63304],[13.06407,47.60075],[13.06641,47.58577],[13.04537,47.58183],[13.05355,47.56291],[13.03252,47.53373],[13.04537,47.49426],[12.9998,47.46267],[12.98344,47.48716],[12.9624,47.47452],[12.85256,47.52741],[12.84672,47.54556],[12.80699,47.54477],[12.77427,47.58025],[12.82101,47.61493],[12.76492,47.64485],[12.77777,47.66689],[12.7357,47.6787],[12.6071,47.6741],[12.57438,47.63238],[12.53816,47.63553],[12.50076,47.62293],[12.44117,47.6741],[12.43883,47.6977],[12.37222,47.68433],[12.336,47.69534],[12.27991,47.68827],[12.26004,47.67725],[12.24017,47.69534],[12.26238,47.73544],[12.2542,47.7433],[12.22571,47.71776],[12.18303,47.70065],[12.16217,47.70105],[12.16769,47.68167],[12.18347,47.66663],[12.18507,47.65984],[12.19895,47.64085],[12.20801,47.61082],[12.20398,47.60667],[12.18568,47.6049],[12.17737,47.60121],[12.18145,47.61019],[12.17824,47.61506],[12.13734,47.60639],[12.05788,47.61742],[12.02282,47.61033],[12.0088,47.62451],[11.85572,47.60166],[11.84052,47.58354],[11.63934,47.59202],[11.60681,47.57881],[11.58811,47.55515],[11.58578,47.52281],[11.52618,47.50939],[11.4362,47.51413],[11.38128,47.47465],[11.4175,47.44621],[11.33804,47.44937],[11.29597,47.42566],[11.27844,47.39956],[11.22002,47.3964],[11.25157,47.43277],[11.20482,47.43198],[11.12536,47.41222],[11.11835,47.39719],[10.97111,47.39561],[10.97111,47.41617],[10.98513,47.42882],[10.92437,47.46991],[10.93839,47.48018],[10.90918,47.48571],[10.87061,47.4786],[10.86945,47.5015],[10.91268,47.51334],[10.88814,47.53701],[10.77596,47.51729],[10.7596,47.53228],[10.6965,47.54253],[10.68832,47.55752],[10.63456,47.5591],[10.60337,47.56755],[10.56912,47.53584],[10.48849,47.54057],[10.47329,47.58552],[10.43473,47.58394],[10.44992,47.5524],[10.4324,47.50111],[10.44291,47.48453],[10.46278,47.47901],[10.47446,47.43318],[10.4359,47.41183],[10.4324,47.38494],[10.39851,47.37623],[10.33424,47.30813],[10.23257,47.27088],[10.17531,47.27167],[10.17648,47.29149],[10.2147,47.31014],[10.19998,47.32832],[10.23757,47.37609],[10.22774,47.38904],[10.2127,47.38019],[10.17648,47.38889],[10.16362,47.36674],[10.11805,47.37228],[10.09819,47.35724],[10.06897,47.40709],[10.1052,47.4316],[10.09001,47.46005],[10.07131,47.45531],[10.03859,47.48927],[10.00003,47.48216],[9.96029,47.53899],[9.92407,47.53111],[9.87733,47.54688],[9.87499,47.52953],[9.8189,47.54688],[9.82591,47.58158],[9.80254,47.59419],[9.76748,47.5934],[9.72736,47.53457],[9.55125,47.53629],[9.56312,47.49495],[9.58208,47.48344],[9.59482,47.46305],[9.60205,47.46165],[9.60484,47.46358],[9.60841,47.47178],[9.62158,47.45858],[9.62475,47.45685],[9.6423,47.45599],[9.65728,47.45383],[9.65863,47.44847],[9.64483,47.43842],[9.6446,47.43233],[9.65043,47.41937],[9.65136,47.40504],[9.6629,47.39591],[9.67334,47.39191],[9.67445,47.38429],[9.6711,47.37824],[9.66243,47.37136],[9.65427,47.36824],[9.62476,47.36639],[9.59978,47.34671],[9.58513,47.31334],[9.55857,47.29919],[9.54773,47.2809],[9.53116,47.27029],[9.56766,47.24281],[9.55176,47.22585],[9.56981,47.21926],[9.58264,47.20673],[9.56539,47.17124],[9.62623,47.14685],[9.63395,47.08443],[9.61216,47.07732],[9.60717,47.06091],[9.87935,47.01337],[9.88266,46.93343],[9.98058,46.91434],[10.10715,46.84296],[10.22675,46.86942],[10.24128,46.93147],[10.30031,46.92093],[10.36933,47.00212],[10.48376,46.93891],[10.47197,46.85698],[10.54783,46.84505],[10.66405,46.87614],[10.75753,46.82258],[10.72974,46.78972],[11.00764,46.76896],[11.10618,46.92966],[11.33355,46.99862],[11.50739,47.00644],[11.74789,46.98484],[12.19254,47.09331],[12.21781,47.03996],[12.11675,47.01241],[12.2006,46.88854],[12.27591,46.88651],[12.38708,46.71529],[12.59992,46.6595],[12.94445,46.60401],[13.27627,46.56059],[13.64088,46.53438],[13.7148,46.5222],[13.89837,46.52331],[14.00422,46.48474],[14.04002,46.49117],[14.12097,46.47724],[14.15989,46.43327],[14.28326,46.44315],[14.314,46.43327],[14.42608,46.44614],[14.45877,46.41717],[14.52176,46.42617],[14.56463,46.37208],[14.5942,46.43434],[14.66892,46.44936],[14.72185,46.49974],[14.81836,46.51046],[14.83549,46.56614],[14.86419,46.59411],[14.87129,46.61],[14.92283,46.60848],[14.96002,46.63459],[14.98024,46.6009],[15.01451,46.641],[15.14215,46.66131],[15.23711,46.63994],[15.41235,46.65556],[15.45514,46.63697],[15.46906,46.61321],[15.54431,46.6312],[15.55333,46.64988],[15.54533,46.66985],[15.59826,46.68908],[15.62317,46.67947],[15.63255,46.68069],[15.6365,46.6894],[15.6543,46.69228],[15.6543,46.70616],[15.67411,46.70735],[15.69523,46.69823],[15.72279,46.69548],[15.73823,46.70011],[15.76771,46.69863],[15.78518,46.70712],[15.8162,46.71897],[15.87691,46.7211],[15.94864,46.68769],[15.98512,46.68463],[15.99988,46.67947],[16.04036,46.6549],[16.04347,46.68694],[16.02808,46.71094],[15.99769,46.7266],[15.98432,46.74991],[15.99126,46.78199],[15.99054,46.82772],[16.05786,46.83927],[16.10983,46.867],[16.19904,46.94134],[16.22403,46.939],[16.27594,46.9643],[16.28202,47.00159],[16.51369,47.00084],[16.43936,47.03548],[16.52176,47.05747],[16.46134,47.09395],[16.52863,47.13974],[16.44932,47.14418],[16.46442,47.16845],[16.4523,47.18812],[16.42801,47.18422],[16.41739,47.20649],[16.43663,47.21127],[16.44142,47.25079],[16.47782,47.25918],[16.45104,47.41181],[16.49908,47.39416],[16.52414,47.41007],[16.57152,47.40868],[16.6718,47.46139],[16.64821,47.50155],[16.71059,47.52692],[16.64193,47.63114],[16.58699,47.61772],[16.4222,47.66537],[16.55129,47.72268],[16.53514,47.73837],[16.54779,47.75074],[16.61183,47.76171],[16.65679,47.74197],[16.72089,47.73469],[16.7511,47.67878],[16.82938,47.68432],[16.86509,47.72268],[16.87538,47.68895],[17.08893,47.70928],[17.05048,47.79377],[17.07039,47.81129],[17.00997,47.86245],[17.08275,47.87719],[17.11022,47.92461],[17.09786,47.97336],[17.16001,48.00636],[17.07039,48.0317],[17.09168,48.09366],[17.05735,48.14179],[17.02919,48.13996],[16.97701,48.17385],[16.89461,48.31332],[16.90903,48.32519],[16.84243,48.35258],[16.83317,48.38138],[16.83588,48.3844],[16.8497,48.38321],[16.85204,48.44968],[16.94611,48.53614],[16.93955,48.60371],[16.90354,48.71541],[16.79779,48.70998],[16.71883,48.73806],[16.68518,48.7281],[16.67008,48.77699],[16.46134,48.80865],[16.40915,48.74576],[16.37345,48.729],[16.06034,48.75436],[15.84404,48.86921],[15.78087,48.87644],[15.75341,48.8516],[15.6921,48.85973],[15.61622,48.89541],[15.51357,48.91549],[15.48027,48.94481],[15.34823,48.98444]]]]}},{type:"Feature",properties:{iso1A2:"AU",iso1A3:"AUS",iso1N3:"036",wikidata:"Q408",nameEn:"Australia",groups:["053","009"],driveSide:"left",callingCodes:["61"]},geometry:{type:"MultiPolygon",coordinates:[[[[156.55918,-21.85134],[158.60851,-15.7108],[144.30183,-9.48146],[142.81927,-9.31709],[142.5723,-9.35994],[142.31447,-9.24611],[142.23304,-9.19253],[142.1462,-9.19923],[142.0953,-9.23534],[142.0601,-9.56571],[140.88922,-9.34945],[127.55165,-9.05052],[96.7091,-25.20343],[159.69067,-56.28945],[165.46901,-28.32101],[156.55918,-21.85134]]]]}},{type:"Feature",properties:{iso1A2:"AW",iso1A3:"ABW",iso1N3:"533",wikidata:"Q21203",nameEn:"Aruba",country:"NL",groups:["029","003","419","019"],callingCodes:["297"]},geometry:{type:"MultiPolygon",coordinates:[[[[-70.00823,12.98375],[-70.35625,12.58277],[-69.60231,12.17],[-70.00823,12.98375]]]]}},{type:"Feature",properties:{iso1A2:"AX",iso1A3:"ALA",iso1N3:"248",wikidata:"Q5689",nameEn:"Åland Islands",country:"FI",groups:["EU","154","150"],callingCodes:["358 18","358 457"]},geometry:{type:"MultiPolygon",coordinates:[[[[19.08191,60.19152],[20.5104,59.15546],[21.35468,59.67511],[21.02509,60.12142],[21.08159,60.20167],[21.15143,60.54555],[20.96741,60.71528],[19.23413,60.61414],[19.08191,60.19152]]]]}},{type:"Feature",properties:{iso1A2:"AZ",iso1A3:"AZE",iso1N3:"031",wikidata:"Q227",nameEn:"Azerbaijan",groups:["145","142"],callingCodes:["994"]},geometry:{type:"MultiPolygon",coordinates:[[[[46.42738,41.91323],[46.3984,41.84399],[46.30863,41.79133],[46.23962,41.75811],[46.20538,41.77205],[46.17891,41.72094],[46.19759,41.62327],[46.24429,41.59883],[46.26531,41.63339],[46.28182,41.60089],[46.3253,41.60912],[46.34039,41.5947],[46.34126,41.57454],[46.29794,41.5724],[46.33925,41.4963],[46.40307,41.48464],[46.4669,41.43331],[46.63658,41.37727],[46.72375,41.28609],[46.66148,41.20533],[46.63969,41.09515],[46.55096,41.1104],[46.48558,41.0576],[46.456,41.09984],[46.37661,41.10805],[46.27698,41.19011],[46.13221,41.19479],[45.95786,41.17956],[45.80842,41.2229],[45.69946,41.29545],[45.75705,41.35157],[45.71035,41.36208],[45.68389,41.3539],[45.45973,41.45898],[45.4006,41.42402],[45.31352,41.47168],[45.26285,41.46433],[45.1797,41.42231],[45.09867,41.34065],[45.0133,41.29747],[45.05497,41.2464],[45.02932,41.2101],[45.05201,41.19211],[45.11811,41.19967],[45.1969,41.168],[45.19942,41.13299],[45.08028,41.10917],[45.06784,41.05379],[45.12923,41.06059],[45.1313,41.09369],[45.1634,41.08082],[45.16493,41.05068],[45.1994,41.04518],[45.25897,41.0027],[45.26162,41.0228],[45.28859,41.03757],[45.35677,40.99784],[45.39725,41.02603],[45.44083,41.01663],[45.40814,40.97904],[45.60584,40.87436],[45.55914,40.78366],[45.4206,40.7424],[45.35366,40.65979],[45.45484,40.57707],[45.42994,40.53804],[45.65098,40.37696],[45.95609,40.27846],[45.97944,40.181],[45.83779,39.98925],[45.78642,40.03218],[45.59806,40.0131],[45.60895,39.97733],[45.7833,39.9475],[45.82533,39.82925],[45.96543,39.78859],[46.18493,39.60533],[46.40286,39.63651],[46.42465,39.57534],[46.52117,39.58734],[46.57098,39.56694],[46.57721,39.54414],[46.51027,39.52373],[46.53051,39.47809],[46.4013,39.45405],[46.37795,39.42039],[46.43244,39.35181],[46.50093,39.33736],[46.56476,39.24942],[46.63481,39.23013],[46.58032,39.21204],[46.54141,39.15895],[46.52584,39.18912],[46.44022,39.19636],[46.54296,39.07078],[46.51805,38.94982],[46.53497,38.86548],[46.75752,39.03231],[46.83822,39.13143],[46.92539,39.16644],[46.95341,39.13505],[47.05771,39.20143],[47.05927,39.24846],[47.31301,39.37492],[47.38978,39.45999],[47.50099,39.49615],[47.84774,39.66285],[47.98977,39.70999],[48.34264,39.42935],[48.37385,39.37584],[48.15984,39.30028],[48.12404,39.25208],[48.15361,39.19419],[48.31239,39.09278],[48.33884,39.03022],[48.28437,38.97186],[48.08627,38.94434],[48.07734,38.91616],[48.01409,38.90333],[48.02581,38.82705],[48.24773,38.71883],[48.3146,38.59958],[48.45084,38.61013],[48.58793,38.45076],[48.62217,38.40198],[48.70001,38.40564],[48.78979,38.45026],[48.81072,38.44853],[48.84969,38.45015],[48.88288,38.43975],[52.39847,39.43556],[48.80971,41.95365],[48.5867,41.84306],[48.55078,41.77917],[48.42301,41.65444],[48.40277,41.60441],[48.2878,41.56221],[48.22064,41.51472],[48.07587,41.49957],[47.87973,41.21798],[47.75831,41.19455],[47.62288,41.22969],[47.54504,41.20275],[47.49004,41.26366],[47.34579,41.27884],[47.10762,41.59044],[47.03757,41.55434],[46.99554,41.59743],[47.00955,41.63583],[46.8134,41.76252],[46.75269,41.8623],[46.58924,41.80547],[46.5332,41.87389],[46.42738,41.91323]],[[45.50279,40.58424],[45.47927,40.65023],[45.51825,40.67382],[45.56071,40.64765],[45.50279,40.58424]]],[[[45.00864,41.03411],[45.03792,41.03938],[45.04517,41.06653],[45.03406,41.07931],[45.00864,41.09407],[44.97169,41.09176],[44.95383,41.07553],[44.96031,41.06345],[44.9903,41.05657],[45.00864,41.03411]]],[[[45.21324,40.9817],[45.23095,40.97828],[45.23487,41.00226],[45.20625,41.01484],[45.18382,41.0066],[45.19312,40.98998],[45.20518,40.99348],[45.21219,40.99001],[45.21324,40.9817]]],[[[45.46992,39.49888],[45.29606,39.57654],[45.30385,39.61373],[45.23535,39.61373],[45.21784,39.58074],[45.17464,39.58614],[45.18554,39.67846],[45.06604,39.79277],[44.92869,39.72157],[44.88354,39.74432],[44.75779,39.7148],[44.80977,39.65768],[44.81043,39.62677],[44.88916,39.59653],[44.96746,39.42998],[45.05932,39.36435],[45.08751,39.35052],[45.16168,39.21952],[45.30489,39.18333],[45.40148,39.09007],[45.40452,39.07224],[45.44811,39.04927],[45.44966,38.99243],[45.6131,38.964],[45.6155,38.94304],[45.65172,38.95199],[45.83883,38.90768],[45.90266,38.87739],[45.94624,38.89072],[46.00228,38.87376],[46.06766,38.87861],[46.14785,38.84206],[46.06973,39.0744],[46.02303,39.09978],[45.99774,39.28931],[45.79225,39.3695],[45.83,39.46487],[45.80804,39.56716],[45.70547,39.60174],[45.46992,39.49888]]]]}},{type:"Feature",properties:{iso1A2:"BA",iso1A3:"BIH",iso1N3:"070",wikidata:"Q225",nameEn:"Bosnia and Herzegovina",groups:["039","150"],callingCodes:["387"]},geometry:{type:"MultiPolygon",coordinates:[[[[17.84826,45.04489],[17.66571,45.13408],[17.59104,45.10816],[17.51469,45.10791],[17.47589,45.12656],[17.45615,45.12523],[17.4498,45.16119],[17.41229,45.13335],[17.33573,45.14521],[17.32092,45.16246],[17.26815,45.18444],[17.25131,45.14957],[17.24325,45.146],[17.18438,45.14764],[17.0415,45.20759],[16.9385,45.22742],[16.92405,45.27607],[16.83804,45.18951],[16.81137,45.18434],[16.78219,45.19002],[16.74845,45.20393],[16.64962,45.20714],[16.60194,45.23042],[16.56559,45.22307],[16.5501,45.2212],[16.52982,45.22713],[16.49155,45.21153],[16.4634,45.14522],[16.40023,45.1147],[16.38309,45.05955],[16.38219,45.05139],[16.3749,45.05206],[16.35863,45.03529],[16.35404,45.00241],[16.29036,44.99732],[16.12153,45.09616],[15.98412,45.23088],[15.83512,45.22459],[15.76371,45.16508],[15.78842,45.11519],[15.74585,45.0638],[15.78568,44.97401],[15.74723,44.96818],[15.76096,44.87045],[15.79472,44.8455],[15.72584,44.82334],[15.8255,44.71501],[15.89348,44.74964],[16.05828,44.61538],[16.00884,44.58605],[16.03012,44.55572],[16.10566,44.52586],[16.16814,44.40679],[16.12969,44.38275],[16.21346,44.35231],[16.18688,44.27012],[16.36864,44.08263],[16.43662,44.07523],[16.43629,44.02826],[16.50528,44.0244],[16.55472,43.95326],[16.70922,43.84887],[16.75316,43.77157],[16.80736,43.76011],[17.00585,43.58037],[17.15828,43.49376],[17.24411,43.49376],[17.29699,43.44542],[17.25579,43.40353],[17.286,43.33065],[17.46986,43.16559],[17.64268,43.08595],[17.70879,42.97223],[17.5392,42.92787],[17.6444,42.88641],[17.68151,42.92725],[17.7948,42.89556],[17.80854,42.9182],[17.88201,42.83668],[18.24318,42.6112],[18.36197,42.61423],[18.43735,42.55921],[18.49778,42.58409],[18.53751,42.57376],[18.55504,42.58409],[18.52232,42.62279],[18.57373,42.64429],[18.54841,42.68328],[18.54603,42.69171],[18.55221,42.69045],[18.56789,42.72074],[18.47324,42.74992],[18.45921,42.81682],[18.47633,42.85829],[18.4935,42.86433],[18.49661,42.89306],[18.49076,42.95553],[18.52232,43.01451],[18.66254,43.03928],[18.64735,43.14766],[18.66605,43.2056],[18.71747,43.2286],[18.6976,43.25243],[18.76538,43.29838],[18.85342,43.32426],[18.84794,43.33735],[18.83912,43.34795],[18.90911,43.36383],[18.95819,43.32899],[18.95001,43.29327],[19.00844,43.24988],[19.04233,43.30008],[19.08206,43.29668],[19.08673,43.31453],[19.04071,43.397],[19.01078,43.43854],[18.96053,43.45042],[18.95469,43.49367],[18.91379,43.50299],[19.01078,43.55806],[19.04934,43.50384],[19.13933,43.5282],[19.15685,43.53943],[19.22807,43.5264],[19.24774,43.53061],[19.2553,43.5938],[19.33426,43.58833],[19.36653,43.60921],[19.41941,43.54056],[19.42696,43.57987],[19.50455,43.58385],[19.5176,43.71403],[19.3986,43.79668],[19.23465,43.98764],[19.24363,44.01502],[19.38439,43.96611],[19.52515,43.95573],[19.56498,43.99922],[19.61836,44.01464],[19.61991,44.05254],[19.57467,44.04716],[19.55999,44.06894],[19.51167,44.08158],[19.47321,44.1193],[19.48386,44.14332],[19.47338,44.15034],[19.43905,44.13088],[19.40927,44.16722],[19.3588,44.18353],[19.34773,44.23244],[19.32464,44.27185],[19.26945,44.26957],[19.23306,44.26097],[19.20508,44.2917],[19.18328,44.28383],[19.16741,44.28648],[19.13332,44.31492],[19.13556,44.338],[19.11547,44.34218],[19.1083,44.3558],[19.11865,44.36712],[19.10298,44.36924],[19.10365,44.37795],[19.10704,44.38249],[19.10749,44.39421],[19.11785,44.40313],[19.14681,44.41463],[19.14837,44.45253],[19.12278,44.50132],[19.13369,44.52521],[19.16699,44.52197],[19.26388,44.65412],[19.32543,44.74058],[19.36722,44.88164],[19.18183,44.92055],[19.01994,44.85493],[18.8704,44.85097],[18.76347,44.90669],[18.76369,44.93707],[18.80661,44.93561],[18.78357,44.97741],[18.65723,45.07544],[18.47939,45.05871],[18.41896,45.11083],[18.32077,45.1021],[18.24387,45.13699],[18.1624,45.07654],[18.03121,45.12632],[18.01594,45.15163],[17.99479,45.14958],[17.97834,45.13831],[17.97336,45.12245],[17.93706,45.08016],[17.87148,45.04645],[17.84826,45.04489]]]]}},{type:"Feature",properties:{iso1A2:"BB",iso1A3:"BRB",iso1N3:"052",wikidata:"Q244",nameEn:"Barbados",groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 246"]},geometry:{type:"MultiPolygon",coordinates:[[[[-58.56442,13.24471],[-59.80731,13.87556],[-60.19227,12.37597],[-58.56442,13.24471]]]]}},{type:"Feature",properties:{iso1A2:"BD",iso1A3:"BGD",iso1N3:"050",wikidata:"Q902",nameEn:"Bangladesh",groups:["034","142"],driveSide:"left",callingCodes:["880"]},geometry:{type:"MultiPolygon",coordinates:[[[[89.15869,26.13708],[89.08899,26.38845],[88.95612,26.4564],[88.92357,26.40711],[88.91321,26.37984],[89.05328,26.2469],[88.85004,26.23211],[88.78961,26.31093],[88.67837,26.26291],[88.69485,26.38353],[88.62144,26.46783],[88.4298,26.54489],[88.41196,26.63837],[88.33093,26.48929],[88.35153,26.45241],[88.36938,26.48683],[88.48749,26.45855],[88.51649,26.35923],[88.35153,26.29123],[88.34757,26.22216],[88.1844,26.14417],[88.16581,26.0238],[88.08804,25.91334],[88.13138,25.78773],[88.242,25.80811],[88.45103,25.66245],[88.4559,25.59227],[88.677,25.46959],[88.81296,25.51546],[88.85278,25.34679],[89.01105,25.30303],[89.00463,25.26583],[88.94067,25.18534],[88.44766,25.20149],[88.46277,25.07468],[88.33917,24.86803],[88.27325,24.88796],[88.21832,24.96642],[88.14004,24.93529],[88.15515,24.85806],[88.00683,24.66477],[88.08786,24.63232],[88.12296,24.51301],[88.50934,24.32474],[88.68801,24.31464],[88.74841,24.1959],[88.6976,24.14703],[88.73743,23.91751],[88.66189,23.87607],[88.58087,23.87105],[88.56507,23.64044],[88.74841,23.47361],[88.79351,23.50535],[88.79254,23.46028],[88.71133,23.2492],[88.99148,23.21134],[88.86377,23.08759],[88.88327,23.03885],[88.87063,22.95235],[88.96713,22.83346],[88.9151,22.75228],[88.94614,22.66941],[88.9367,22.58527],[89.07114,22.15335],[89.03553,21.77397],[89.13927,21.60785],[89.13606,21.42955],[92.39837,20.38919],[92.4302,20.5688],[92.31348,20.57137],[92.28464,20.63179],[92.37665,20.72172],[92.26071,21.05697],[92.17752,21.17445],[92.20087,21.337],[92.37939,21.47764],[92.43158,21.37025],[92.55105,21.3856],[92.60187,21.24615],[92.68152,21.28454],[92.59775,21.6092],[92.62187,21.87037],[92.60949,21.97638],[92.56616,22.13554],[92.60029,22.1522],[92.5181,22.71441],[92.37665,22.9435],[92.38214,23.28705],[92.26541,23.70392],[92.15417,23.73409],[92.04706,23.64229],[91.95093,23.73284],[91.95642,23.47361],[91.84789,23.42235],[91.76417,23.26619],[91.81634,23.08001],[91.7324,23.00043],[91.61571,22.93929],[91.54993,23.01051],[91.46615,23.2328],[91.4035,23.27522],[91.40848,23.07117],[91.36453,23.06612],[91.28293,23.37538],[91.15579,23.6599],[91.25192,23.83463],[91.22308,23.89616],[91.29587,24.0041],[91.35741,23.99072],[91.37414,24.10693],[91.55542,24.08687],[91.63782,24.1132],[91.65292,24.22095],[91.73257,24.14703],[91.76004,24.23848],[91.82596,24.22345],[91.89258,24.14674],[91.96603,24.3799],[92.11662,24.38997],[92.15796,24.54435],[92.25854,24.9191],[92.38626,24.86055],[92.49887,24.88796],[92.39147,25.01471],[92.33957,25.07593],[92.0316,25.1834],[91.63648,25.12846],[91.25517,25.20677],[90.87427,25.15799],[90.65042,25.17788],[90.40034,25.1534],[90.1155,25.22686],[89.90478,25.31038],[89.87629,25.28337],[89.83371,25.29548],[89.84086,25.31854],[89.81208,25.37244],[89.86129,25.61714],[89.84388,25.70042],[89.80585,25.82489],[89.86592,25.93115],[89.77728,26.04254],[89.77865,26.08387],[89.73581,26.15818],[89.70201,26.15138],[89.63968,26.22595],[89.57101,25.9682],[89.53515,26.00382],[89.35953,26.0077],[89.15869,26.13708]]]]}},{type:"Feature",properties:{iso1A2:"BE",iso1A3:"BEL",iso1N3:"056",wikidata:"Q31",nameEn:"Belgium",groups:["EU","155","150"],callingCodes:["32"]},geometry:{type:"MultiPolygon",coordinates:[[[[4.93295,51.44945],[4.93909,51.44632],[4.9524,51.45014],[4.95244,51.45207],[4.93295,51.44945]]],[[[4.91493,51.4353],[4.92652,51.43329],[4.92952,51.42984],[4.93986,51.43064],[4.94265,51.44003],[4.93471,51.43861],[4.93416,51.44185],[4.94025,51.44193],[4.93544,51.44634],[4.92879,51.44161],[4.92815,51.43856],[4.92566,51.44273],[4.92811,51.4437],[4.92287,51.44741],[4.91811,51.44621],[4.92227,51.44252],[4.91935,51.43634],[4.91493,51.4353]]],[[[4.82946,51.4213],[4.82409,51.44736],[4.84139,51.4799],[4.78803,51.50284],[4.77321,51.50529],[4.74578,51.48937],[4.72935,51.48424],[4.65442,51.42352],[4.57489,51.4324],[4.53521,51.4243],[4.52846,51.45002],[4.54675,51.47265],[4.5388,51.48184],[4.47736,51.4778],[4.38122,51.44905],[4.39747,51.43316],[4.38064,51.41965],[4.43777,51.36989],[4.39292,51.35547],[4.34086,51.35738],[4.33265,51.37687],[4.21923,51.37443],[4.24024,51.35371],[4.16721,51.29348],[4.05165,51.24171],[4.01957,51.24504],[3.97889,51.22537],[3.90125,51.20371],[3.78783,51.2151],[3.78999,51.25766],[3.58939,51.30064],[3.51502,51.28697],[3.52698,51.2458],[3.43488,51.24135],[3.41704,51.25933],[3.38289,51.27331],[3.35847,51.31572],[3.38696,51.33436],[3.36263,51.37112],[2.56575,51.85301],[2.18458,51.52087],[2.55904,51.07014],[2.57551,51.00326],[2.63074,50.94746],[2.59093,50.91751],[2.63331,50.81457],[2.71165,50.81295],[2.81056,50.71773],[2.8483,50.72276],[2.86985,50.7033],[2.87937,50.70298],[2.88504,50.70656],[2.90069,50.69263],[2.91036,50.6939],[2.90873,50.702],[2.95019,50.75138],[2.96778,50.75242],[3.00537,50.76588],[3.04314,50.77674],[3.09163,50.77717],[3.10614,50.78303],[3.11206,50.79416],[3.11987,50.79188],[3.1257,50.78603],[3.15017,50.79031],[3.16476,50.76843],[3.18339,50.74981],[3.18811,50.74025],[3.20064,50.73547],[3.19017,50.72569],[3.20845,50.71662],[3.22042,50.71019],[3.24593,50.71389],[3.26063,50.70086],[3.26141,50.69151],[3.2536,50.68977],[3.264,50.67668],[3.23951,50.6585],[3.2729,50.60718],[3.28575,50.52724],[3.37693,50.49538],[3.44629,50.51009],[3.47385,50.53397],[3.51564,50.5256],[3.49509,50.48885],[3.5683,50.50192],[3.58361,50.49049],[3.61014,50.49568],[3.64426,50.46275],[3.66153,50.45165],[3.67494,50.40239],[3.67262,50.38663],[3.65709,50.36873],[3.66976,50.34563],[3.71009,50.30305],[3.70987,50.3191],[3.73911,50.34809],[3.84314,50.35219],[3.90781,50.32814],[3.96771,50.34989],[4.0268,50.35793],[4.0689,50.3254],[4.10237,50.31247],[4.10957,50.30234],[4.11954,50.30425],[4.13665,50.25609],[4.16808,50.25786],[4.15524,50.2833],[4.17347,50.28838],[4.17861,50.27443],[4.20651,50.27333],[4.21945,50.25539],[4.15524,50.21103],[4.16014,50.19239],[4.13561,50.13078],[4.20147,50.13535],[4.23101,50.06945],[4.16294,50.04719],[4.13508,50.01976],[4.14239,49.98034],[4.20532,49.95803],[4.31963,49.97043],[4.35051,49.95315],[4.43488,49.94122],[4.51098,49.94659],[4.5414,49.96911],[4.68695,49.99685],[4.70064,50.09384],[4.75237,50.11314],[4.82438,50.16878],[4.83279,50.15331],[4.88602,50.15182],[4.8382,50.06738],[4.78827,49.95609],[4.88529,49.9236],[4.85134,49.86457],[4.86965,49.82271],[4.85464,49.78995],[4.96714,49.79872],[5.09249,49.76193],[5.14545,49.70287],[5.26232,49.69456],[5.31465,49.66846],[5.33039,49.6555],[5.30214,49.63055],[5.3137,49.61225],[5.33851,49.61599],[5.34837,49.62889],[5.3974,49.61596],[5.43713,49.5707],[5.46734,49.52648],[5.46541,49.49825],[5.55001,49.52729],[5.60909,49.51228],[5.64505,49.55146],[5.75649,49.54321],[5.7577,49.55915],[5.77435,49.56298],[5.79195,49.55228],[5.81838,49.54777],[5.84143,49.5533],[5.84692,49.55663],[5.8424,49.56082],[5.87256,49.57539],[5.86986,49.58756],[5.84971,49.58674],[5.84826,49.5969],[5.8762,49.60898],[5.87609,49.62047],[5.88393,49.62802],[5.88552,49.63507],[5.90599,49.63853],[5.90164,49.6511],[5.9069,49.66377],[5.86175,49.67862],[5.86527,49.69291],[5.88677,49.70951],[5.86503,49.72739],[5.84193,49.72161],[5.82562,49.72395],[5.83149,49.74729],[5.82245,49.75048],[5.78871,49.7962],[5.75409,49.79239],[5.74953,49.81428],[5.74364,49.82058],[5.74844,49.82435],[5.7404,49.83452],[5.74076,49.83823],[5.74975,49.83933],[5.74953,49.84709],[5.75884,49.84811],[5.74567,49.85368],[5.75861,49.85631],[5.75269,49.8711],[5.78415,49.87922],[5.73621,49.89796],[5.77314,49.93646],[5.77291,49.96056],[5.80833,49.96451],[5.81163,49.97142],[5.83467,49.97823],[5.83968,49.9892],[5.82331,49.99662],[5.81866,50.01286],[5.8551,50.02683],[5.86904,50.04614],[5.85474,50.06342],[5.8857,50.07824],[5.89488,50.11476],[5.95929,50.13295],[5.96453,50.17259],[6.02488,50.18283],[6.03093,50.16362],[6.06406,50.15344],[6.08577,50.17246],[6.12028,50.16374],[6.1137,50.13668],[6.1379,50.12964],[6.15298,50.14126],[6.14132,50.14971],[6.14588,50.17106],[6.18739,50.1822],[6.18364,50.20815],[6.16853,50.2234],[6.208,50.25179],[6.28797,50.27458],[6.29949,50.30887],[6.32488,50.32333],[6.35701,50.31139],[6.40641,50.32425],[6.40785,50.33557],[6.3688,50.35898],[6.34406,50.37994],[6.36852,50.40776],[6.37219,50.45397],[6.34005,50.46083],[6.3465,50.48833],[6.30809,50.50058],[6.26637,50.50272],[6.22335,50.49578],[6.20599,50.52089],[6.19193,50.5212],[6.18716,50.52653],[6.19579,50.5313],[6.19735,50.53576],[6.17802,50.54179],[6.17739,50.55875],[6.20281,50.56952],[6.22581,50.5907],[6.24005,50.58732],[6.24888,50.59869],[6.2476,50.60392],[6.26957,50.62444],[6.17852,50.6245],[6.11707,50.72231],[6.04428,50.72861],[6.0406,50.71848],[6.0326,50.72647],[6.03889,50.74618],[6.01976,50.75398],[5.97545,50.75441],[5.95942,50.7622],[5.89132,50.75124],[5.89129,50.75125],[5.88734,50.77092],[5.84888,50.75448],[5.84548,50.76542],[5.80673,50.7558],[5.77513,50.78308],[5.76533,50.78159],[5.74356,50.7691],[5.73904,50.75674],[5.72216,50.76398],[5.69469,50.75529],[5.68091,50.75804],[5.70107,50.7827],[5.68995,50.79641],[5.70118,50.80764],[5.65259,50.82309],[5.64009,50.84742],[5.64504,50.87107],[5.67886,50.88142],[5.69858,50.91046],[5.71626,50.90796],[5.72644,50.91167],[5.72545,50.92312],[5.74644,50.94723],[5.75927,50.95601],[5.74752,50.96202],[5.72875,50.95428],[5.71864,50.96092],[5.76242,50.99703],[5.77688,51.02483],[5.75961,51.03113],[5.77258,51.06196],[5.79835,51.05834],[5.79903,51.09371],[5.82921,51.09328],[5.83226,51.10585],[5.8109,51.10861],[5.80798,51.11661],[5.85508,51.14445],[5.82564,51.16753],[5.77697,51.1522],[5.77735,51.17845],[5.74617,51.18928],[5.70344,51.1829],[5.65528,51.18736],[5.65145,51.19788],[5.5603,51.22249],[5.5569,51.26544],[5.515,51.29462],[5.48476,51.30053],[5.46519,51.2849],[5.4407,51.28169],[5.41672,51.26248],[5.347,51.27502],[5.33886,51.26314],[5.29716,51.26104],[5.26461,51.26693],[5.23814,51.26064],[5.22542,51.26888],[5.24244,51.30495],[5.2002,51.32243],[5.16222,51.31035],[5.13377,51.31592],[5.13105,51.34791],[5.07102,51.39469],[5.10456,51.43163],[5.07891,51.4715],[5.04774,51.47022],[5.03281,51.48679],[5.0106,51.47167],[5.00393,51.44406],[4.92152,51.39487],[4.90016,51.41404],[4.84988,51.41502],[4.78941,51.41102],[4.77229,51.41337],[4.76577,51.43046],[4.78314,51.43319],[4.82946,51.4213]]]]}},{type:"Feature",properties:{iso1A2:"BF",iso1A3:"BFA",iso1N3:"854",wikidata:"Q965",nameEn:"Burkina Faso",groups:["011","202","002"],callingCodes:["226"]},geometry:{type:"MultiPolygon",coordinates:[[[[0.23859,15.00135],[0.06588,14.96961],[-0.24673,15.07805],[-0.72004,15.08655],[-1.05875,14.7921],[-1.32166,14.72774],[-1.68083,14.50023],[-1.97945,14.47709],[-1.9992,14.19011],[-2.10223,14.14878],[-2.47587,14.29671],[-2.66175,14.14713],[-2.84667,14.05532],[-2.90831,13.81174],[-2.88189,13.64921],[-3.26407,13.70699],[-3.28396,13.5422],[-3.23599,13.29035],[-3.43507,13.27272],[-3.4313,13.1588],[-3.54454,13.1781],[-3.7911,13.36665],[-3.96282,13.38164],[-3.90558,13.44375],[-3.96501,13.49778],[-4.34477,13.12927],[-4.21819,12.95722],[-4.238,12.71467],[-4.47356,12.71252],[-4.41412,12.31922],[-4.57703,12.19875],[-4.54841,12.1385],[-4.62546,12.13204],[-4.62987,12.06531],[-4.70692,12.06746],[-4.72893,12.01579],[-5.07897,11.97918],[-5.26389,11.84778],[-5.40258,11.8327],[-5.26389,11.75728],[-5.29251,11.61715],[-5.22867,11.60421],[-5.20665,11.43811],[-5.25509,11.36905],[-5.25949,11.24816],[-5.32553,11.21578],[-5.32994,11.13371],[-5.49284,11.07538],[-5.41579,10.84628],[-5.47083,10.75329],[-5.46643,10.56074],[-5.51058,10.43177],[-5.39602,10.2929],[-5.12465,10.29788],[-4.96453,9.99923],[-4.96621,9.89132],[-4.6426,9.70696],[-4.31392,9.60062],[-4.25999,9.76012],[-3.69703,9.94279],[-3.31779,9.91125],[-3.27228,9.84981],[-3.19306,9.93781],[-3.16609,9.85147],[-3.00765,9.74019],[-2.93012,9.57403],[-2.76494,9.40778],[-2.68802,9.49343],[-2.76534,9.56589],[-2.74174,9.83172],[-2.83108,10.40252],[-2.94232,10.64281],[-2.83373,11.0067],[-0.67143,10.99811],[-0.61937,10.91305],[-0.44298,11.04292],[-0.42391,11.11661],[-0.38219,11.12596],[-0.35955,11.07801],[-0.28566,11.12713],[-0.27374,11.17157],[-0.13493,11.14075],[0.50388,11.01011],[0.48852,10.98561],[0.50521,10.98035],[0.4958,10.93269],[0.66104,10.99964],[0.91245,10.99597],[0.9813,11.08876],[1.03409,11.04719],[1.42823,11.46822],[2.00988,11.42227],[2.29983,11.68254],[2.39723,11.89473],[2.05785,12.35539],[2.26349,12.41915],[0.99167,13.10727],[0.99253,13.37515],[1.18873,13.31771],[1.21217,13.37853],[1.24516,13.33968],[1.28509,13.35488],[1.24429,13.39373],[1.20088,13.38951],[1.02813,13.46635],[0.99514,13.5668],[0.77637,13.64442],[0.77377,13.6866],[0.61924,13.68491],[0.38051,14.05575],[0.16936,14.51654],[0.23859,15.00135]]]]}},{type:"Feature",properties:{iso1A2:"BG",iso1A3:"BGR",iso1N3:"100",wikidata:"Q219",nameEn:"Bulgaria",groups:["EU","151","150"],callingCodes:["359"]},geometry:{type:"MultiPolygon",coordinates:[[[[23.05288,43.79494],[22.85314,43.84452],[22.83753,43.88055],[22.87873,43.9844],[23.01674,44.01946],[23.04988,44.07694],[22.67173,44.21564],[22.61711,44.16938],[22.61688,44.06534],[22.41449,44.00514],[22.35558,43.81281],[22.41043,43.69566],[22.47582,43.6558],[22.53397,43.47225],[22.82036,43.33665],[22.89727,43.22417],[23.00806,43.19279],[22.98104,43.11199],[22.89521,43.03625],[22.78397,42.98253],[22.74826,42.88701],[22.54302,42.87774],[22.43309,42.82057],[22.4997,42.74144],[22.43983,42.56851],[22.55669,42.50144],[22.51961,42.3991],[22.47498,42.3915],[22.45919,42.33822],[22.34773,42.31725],[22.38136,42.30339],[22.47251,42.20393],[22.50289,42.19527],[22.51224,42.15457],[22.67701,42.06614],[22.86749,42.02275],[22.90254,41.87587],[22.96682,41.77137],[23.01239,41.76527],[23.03342,41.71034],[22.95513,41.63265],[22.96331,41.35782],[22.93334,41.34104],[23.1833,41.31755],[23.21953,41.33773],[23.22771,41.37106],[23.31301,41.40525],[23.33639,41.36317],[23.40416,41.39999],[23.52453,41.40262],[23.63203,41.37632],[23.67644,41.41139],[23.76525,41.40175],[23.80148,41.43943],[23.89613,41.45257],[23.91483,41.47971],[23.96975,41.44118],[24.06908,41.46132],[24.06323,41.53222],[24.10063,41.54796],[24.18126,41.51735],[24.27124,41.57682],[24.30513,41.51297],[24.52599,41.56808],[24.61129,41.42278],[24.71529,41.41928],[24.8041,41.34913],[24.82514,41.4035],[24.86136,41.39298],[24.90928,41.40876],[24.942,41.38685],[25.11611,41.34212],[25.28322,41.23411],[25.48187,41.28506],[25.52394,41.2798],[25.55082,41.31667],[25.61042,41.30614],[25.66183,41.31316],[25.70507,41.29209],[25.8266,41.34563],[25.87919,41.30526],[26.12926,41.35878],[26.16548,41.42278],[26.20288,41.43943],[26.14796,41.47533],[26.176,41.50072],[26.17951,41.55409],[26.14328,41.55496],[26.15146,41.60828],[26.07083,41.64584],[26.06148,41.70345],[26.16841,41.74858],[26.21325,41.73223],[26.22888,41.74139],[26.2654,41.71544],[26.30255,41.70925],[26.35957,41.71149],[26.32952,41.73637],[26.33589,41.76802],[26.36952,41.82265],[26.53968,41.82653],[26.57961,41.90024],[26.56051,41.92995],[26.62996,41.97644],[26.79143,41.97386],[26.95638,42.00741],[27.03277,42.0809],[27.08486,42.08735],[27.19251,42.06028],[27.22376,42.10152],[27.27411,42.10409],[27.45478,41.96591],[27.52379,41.93756],[27.55191,41.90928],[27.69949,41.97515],[27.81235,41.94803],[27.83492,41.99709],[27.91479,41.97902],[28.02971,41.98066],[28.32297,41.98371],[29.24336,43.70874],[28.23293,43.76],[27.99558,43.84193],[27.92008,44.00761],[27.73468,43.95326],[27.64542,44.04958],[27.60834,44.01206],[27.39757,44.0141],[27.26845,44.12602],[26.95141,44.13555],[26.62712,44.05698],[26.38764,44.04356],[26.10115,43.96908],[26.05584,43.90925],[25.94911,43.85745],[25.72792,43.69263],[25.39528,43.61866],[25.17144,43.70261],[25.10718,43.6831],[24.96682,43.72693],[24.73542,43.68523],[24.62281,43.74082],[24.50264,43.76314],[24.35364,43.70211],[24.18149,43.68218],[23.73978,43.80627],[23.61687,43.79289],[23.4507,43.84936],[23.26772,43.84843],[23.05288,43.79494]]]]}},{type:"Feature",properties:{iso1A2:"BH",iso1A3:"BHR",iso1N3:"048",wikidata:"Q398",nameEn:"Bahrain",groups:["145","142"],callingCodes:["973"]},geometry:{type:"MultiPolygon",coordinates:[[[[50.93865,26.30758],[50.71771,26.73086],[50.38162,26.53976],[50.26923,26.08243],[50.302,25.87592],[50.57069,25.57887],[50.80824,25.54641],[50.7801,25.595],[50.86149,25.6965],[50.81266,25.88946],[50.93865,26.30758]]]]}},{type:"Feature",properties:{iso1A2:"BI",iso1A3:"BDI",iso1N3:"108",wikidata:"Q967",nameEn:"Burundi",groups:["014","202","002"],callingCodes:["257"]},geometry:{type:"MultiPolygon",coordinates:[[[[30.54501,-2.41404],[30.42933,-2.31064],[30.14034,-2.43626],[29.95911,-2.33348],[29.88237,-2.75105],[29.36805,-2.82933],[29.32234,-2.6483],[29.0562,-2.58632],[29.04081,-2.7416],[29.00167,-2.78523],[29.00404,-2.81978],[29.0505,-2.81774],[29.09119,-2.87871],[29.09797,-2.91935],[29.16037,-2.95457],[29.17258,-2.99385],[29.25633,-3.05471],[29.21463,-3.3514],[29.23708,-3.75856],[29.43673,-4.44845],[29.63827,-4.44681],[29.75109,-4.45836],[29.77289,-4.41733],[29.82885,-4.36153],[29.88172,-4.35743],[30.03323,-4.26631],[30.22042,-4.01738],[30.45915,-3.56532],[30.84165,-3.25152],[30.83823,-2.97837],[30.6675,-2.98987],[30.57926,-2.89791],[30.4987,-2.9573],[30.40662,-2.86151],[30.52747,-2.65841],[30.41789,-2.66266],[30.54501,-2.41404]]]]}},{type:"Feature",properties:{iso1A2:"BJ",iso1A3:"BEN",iso1N3:"204",wikidata:"Q962",nameEn:"Benin",aliases:["DY"],groups:["011","202","002"],callingCodes:["229"]},geometry:{type:"MultiPolygon",coordinates:[[[[3.59375,11.70269],[3.48187,11.86092],[3.31613,11.88495],[3.25352,12.01467],[2.83978,12.40585],[2.6593,12.30631],[2.37783,12.24804],[2.39657,12.10952],[2.45824,11.98672],[2.39723,11.89473],[2.29983,11.68254],[2.00988,11.42227],[1.42823,11.46822],[1.03409,11.04719],[0.9813,11.08876],[0.91245,10.99597],[0.8804,10.803],[0.80358,10.71459],[0.77666,10.37665],[1.35507,9.99525],[1.36624,9.5951],[1.33675,9.54765],[1.41746,9.3226],[1.5649,9.16941],[1.61838,9.0527],[1.64249,6.99562],[1.55877,6.99737],[1.61812,6.74843],[1.58105,6.68619],[1.76906,6.43189],[1.79826,6.28221],[1.62913,6.24075],[1.67336,6.02702],[2.74181,6.13349],[2.70566,6.38038],[2.70464,6.50831],[2.74334,6.57291],[2.7325,6.64057],[2.78204,6.70514],[2.78823,6.76356],[2.73405,6.78508],[2.74024,6.92802],[2.71702,6.95722],[2.76965,7.13543],[2.74489,7.42565],[2.79442,7.43486],[2.78668,7.5116],[2.73405,7.5423],[2.73095,7.7755],[2.67523,7.87825],[2.77907,9.06924],[3.08017,9.10006],[3.14147,9.28375],[3.13928,9.47167],[3.25093,9.61632],[3.34726,9.70696],[3.32099,9.78032],[3.35383,9.83641],[3.54429,9.87739],[3.66908,10.18136],[3.57275,10.27185],[3.6844,10.46351],[3.78292,10.40538],[3.84243,10.59316],[3.71505,11.13015],[3.49175,11.29765],[3.59375,11.70269]]]]}},{type:"Feature",properties:{iso1A2:"BL",iso1A3:"BLM",iso1N3:"652",wikidata:"Q25362",nameEn:"Saint-Barthélemy",country:"FR",groups:["029","003","419","019"],callingCodes:["590"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.75637,18.13489],[-62.93924,18.02904],[-63.07669,17.79659],[-62.76692,17.64353],[-62.54836,17.8636],[-62.75637,18.13489]]]]}},{type:"Feature",properties:{iso1A2:"BM",iso1A3:"BMU",iso1N3:"060",wikidata:"Q23635",nameEn:"Bermuda",country:"GB",groups:["021","003","019"],driveSide:"left",callingCodes:["1 441"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.20987,32.6953],[-65.31453,32.68437],[-65.63955,31.43417],[-63.20987,32.6953]]]]}},{type:"Feature",properties:{iso1A2:"BN",iso1A3:"BRN",iso1N3:"096",wikidata:"Q921",nameEn:"Brunei",groups:["035","142"],driveSide:"left",callingCodes:["673"]},geometry:{type:"MultiPolygon",coordinates:[[[[115.16236,5.01011],[115.02521,5.35005],[114.08532,4.64632],[114.07448,4.58441],[114.15813,4.57],[114.26876,4.49878],[114.32176,4.34942],[114.32176,4.2552],[114.4416,4.27588],[114.49922,4.13108],[114.64211,4.00694],[114.78539,4.12205],[114.88039,4.4257],[114.83189,4.42387],[114.77303,4.72871],[114.8266,4.75062],[114.88841,4.81905],[114.96982,4.81146],[114.99417,4.88201],[115.05038,4.90275],[115.02955,4.82087],[115.02278,4.74137],[115.04064,4.63706],[115.07737,4.53418],[115.09978,4.39123],[115.31275,4.30806],[115.36346,4.33563],[115.2851,4.42295],[115.27819,4.63661],[115.20737,4.8256],[115.15092,4.87604],[115.16236,5.01011]]]]}},{type:"Feature",properties:{iso1A2:"BO",iso1A3:"BOL",iso1N3:"068",wikidata:"Q750",nameEn:"Bolivia",groups:["005","419","019"],callingCodes:["591"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.90248,-12.52544],[-64.22539,-12.45267],[-64.30708,-12.46398],[-64.99778,-11.98604],[-65.30027,-11.48749],[-65.28141,-10.86289],[-65.35402,-10.78685],[-65.37923,-10.35141],[-65.29019,-9.86253],[-65.40615,-9.63894],[-65.56244,-9.84266],[-65.68343,-9.75323],[-67.17784,-10.34016],[-68.71533,-11.14749],[-68.7651,-11.0496],[-68.75179,-11.03688],[-68.75265,-11.02383],[-68.74802,-11.00891],[-69.42792,-10.93451],[-69.47839,-10.95254],[-69.57156,-10.94555],[-68.98115,-11.8979],[-68.65044,-12.50689],[-68.85615,-12.87769],[-68.8864,-13.40792],[-69.05265,-13.68546],[-68.88135,-14.18639],[-69.36254,-14.94634],[-69.14856,-15.23478],[-69.40336,-15.61358],[-69.20291,-16.16668],[-69.09986,-16.22693],[-68.96238,-16.194],[-68.79464,-16.33272],[-68.98358,-16.42165],[-69.04027,-16.57214],[-69.00853,-16.66769],[-69.16896,-16.72233],[-69.62883,-17.28142],[-69.46863,-17.37466],[-69.46897,-17.4988],[-69.46623,-17.60518],[-69.34126,-17.72753],[-69.28671,-17.94844],[-69.07496,-18.03715],[-69.14807,-18.16893],[-69.07432,-18.28259],[-68.94987,-18.93302],[-68.87082,-19.06003],[-68.80602,-19.08355],[-68.61989,-19.27584],[-68.41218,-19.40499],[-68.66761,-19.72118],[-68.54611,-19.84651],[-68.57132,-20.03134],[-68.74273,-20.08817],[-68.7276,-20.46178],[-68.44023,-20.62701],[-68.55383,-20.7355],[-68.53957,-20.91542],[-68.40403,-20.94562],[-68.18816,-21.28614],[-67.85114,-22.87076],[-67.54284,-22.89771],[-67.18382,-22.81525],[-66.7298,-22.23644],[-66.29714,-22.08741],[-66.24077,-21.77837],[-66.03836,-21.84829],[-66.04832,-21.9187],[-65.9261,-21.93335],[-65.7467,-22.10105],[-65.61166,-22.09504],[-65.58694,-22.09794],[-65.57743,-22.07675],[-65.47435,-22.08908],[-64.99524,-22.08255],[-64.90014,-22.12136],[-64.67174,-22.18957],[-64.58888,-22.25035],[-64.4176,-22.67692],[-64.35108,-22.73282],[-64.31489,-22.88824],[-64.22918,-22.55807],[-63.93287,-21.99934],[-63.70963,-21.99934],[-63.68113,-22.0544],[-63.66482,-21.99918],[-62.81124,-21.9987],[-62.8078,-22.12534],[-62.64455,-22.25091],[-62.2757,-21.06657],[-62.26883,-20.55311],[-61.93912,-20.10053],[-61.73723,-19.63958],[-60.00638,-19.2981],[-59.06965,-19.29148],[-58.23216,-19.80058],[-58.16225,-20.16193],[-57.8496,-19.98346],[-58.14215,-19.76276],[-57.78463,-19.03259],[-57.71113,-19.03161],[-57.69134,-19.00544],[-57.71995,-18.97546],[-57.71995,-18.89573],[-57.76764,-18.90087],[-57.56807,-18.25655],[-57.48237,-18.24219],[-57.69877,-17.8431],[-57.73949,-17.56095],[-57.90082,-17.44555],[-57.99661,-17.5273],[-58.32935,-17.28195],[-58.5058,-16.80958],[-58.30918,-16.3699],[-58.32431,-16.25861],[-58.41506,-16.32636],[-60.16069,-16.26479],[-60.23797,-15.50267],[-60.58224,-15.09887],[-60.23968,-15.09515],[-60.27887,-14.63021],[-60.46037,-14.22496],[-60.48053,-13.77981],[-61.05527,-13.50054],[-61.81151,-13.49564],[-63.76259,-12.42952],[-63.90248,-12.52544]]]]}},{type:"Feature",properties:{iso1A2:"BQ",iso1A3:"BES",iso1N3:"535",wikidata:"Q27561",nameEn:"Caribbean Netherlands",country:"NL",groups:["029","003","419","019"],callingCodes:["599 3","599 4","599 7"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.07669,17.79659],[-63.22932,17.32592],[-63.11114,17.23125],[-62.76692,17.64353],[-63.07669,17.79659]]],[[[-63.29212,17.90532],[-63.58819,17.61311],[-63.22932,17.32592],[-63.07669,17.79659],[-63.29212,17.90532]]],[[[-67.89186,12.4116],[-68.90012,12.62309],[-68.33524,11.78151],[-68.01417,11.77722],[-67.89186,12.4116]]]]}},{type:"Feature",properties:{iso1A2:"BR",iso1A3:"BRA",iso1N3:"076",wikidata:"Q155",nameEn:"Brazil",groups:["005","419","019"],callingCodes:["55"]},geometry:{type:"MultiPolygon",coordinates:[[[[-59.69361,4.34069],[-59.78878,4.45637],[-60.15953,4.53456],[-60.04189,4.69801],[-59.98129,5.07097],[-60.20944,5.28754],[-60.32352,5.21299],[-60.73204,5.20931],[-60.5802,4.94312],[-60.86539,4.70512],[-60.98303,4.54167],[-61.15703,4.49839],[-61.31457,4.54167],[-61.29675,4.44216],[-61.48569,4.43149],[-61.54629,4.2822],[-62.13094,4.08309],[-62.44822,4.18621],[-62.57656,4.04754],[-62.74411,4.03331],[-62.7655,3.73099],[-62.98296,3.59935],[-63.21111,3.96219],[-63.4464,3.9693],[-63.42233,3.89995],[-63.50611,3.83592],[-63.67099,4.01731],[-63.70218,3.91417],[-63.86082,3.94796],[-63.99183,3.90172],[-64.14512,4.12932],[-64.57648,4.12576],[-64.72977,4.28931],[-64.84028,4.24665],[-64.48379,3.7879],[-64.02908,2.79797],[-64.0257,2.48156],[-63.39114,2.4317],[-63.39827,2.16098],[-64.06135,1.94722],[-64.08274,1.64792],[-64.34654,1.35569],[-64.38932,1.5125],[-65.11657,1.12046],[-65.57288,0.62856],[-65.50158,0.92086],[-65.6727,1.01353],[-66.28507,0.74585],[-66.85795,1.22998],[-67.08222,1.17441],[-67.15784,1.80439],[-67.299,1.87494],[-67.40488,2.22258],[-67.9292,1.82455],[-68.18632,2.00091],[-68.26699,1.83463],[-68.18128,1.72881],[-69.38621,1.70865],[-69.53746,1.76408],[-69.83491,1.69353],[-69.82987,1.07864],[-69.26017,1.06856],[-69.14422,0.84172],[-69.20976,0.57958],[-69.47696,0.71065],[-70.04162,0.55437],[-70.03658,-0.19681],[-69.603,-0.51947],[-69.59796,-0.75136],[-69.4215,-1.01853],[-69.43395,-1.42219],[-69.94708,-4.2431],[-70.00888,-4.37833],[-70.11305,-4.27281],[-70.19582,-4.3607],[-70.33236,-4.15214],[-70.77601,-4.15717],[-70.96814,-4.36915],[-71.87003,-4.51661],[-72.64391,-5.0391],[-72.83973,-5.14765],[-73.24579,-6.05764],[-73.12983,-6.43852],[-73.73986,-6.87919],[-73.77011,-7.28944],[-73.96938,-7.58465],[-73.65485,-7.77897],[-73.76576,-7.89884],[-72.92886,-9.04074],[-73.21498,-9.40904],[-72.72216,-9.41397],[-72.31883,-9.5184],[-72.14742,-9.98049],[-71.23394,-9.9668],[-70.53373,-9.42628],[-70.58453,-9.58303],[-70.55429,-9.76692],[-70.62487,-9.80666],[-70.64134,-11.0108],[-70.51395,-10.92249],[-70.38791,-11.07096],[-69.90896,-10.92744],[-69.57835,-10.94051],[-69.57156,-10.94555],[-69.47839,-10.95254],[-69.42792,-10.93451],[-68.74802,-11.00891],[-68.75265,-11.02383],[-68.75179,-11.03688],[-68.7651,-11.0496],[-68.71533,-11.14749],[-67.17784,-10.34016],[-65.68343,-9.75323],[-65.56244,-9.84266],[-65.40615,-9.63894],[-65.29019,-9.86253],[-65.37923,-10.35141],[-65.35402,-10.78685],[-65.28141,-10.86289],[-65.30027,-11.48749],[-64.99778,-11.98604],[-64.30708,-12.46398],[-64.22539,-12.45267],[-63.90248,-12.52544],[-63.76259,-12.42952],[-61.81151,-13.49564],[-61.05527,-13.50054],[-60.48053,-13.77981],[-60.46037,-14.22496],[-60.27887,-14.63021],[-60.23968,-15.09515],[-60.58224,-15.09887],[-60.23797,-15.50267],[-60.16069,-16.26479],[-58.41506,-16.32636],[-58.32431,-16.25861],[-58.30918,-16.3699],[-58.5058,-16.80958],[-58.32935,-17.28195],[-57.99661,-17.5273],[-57.90082,-17.44555],[-57.73949,-17.56095],[-57.69877,-17.8431],[-57.48237,-18.24219],[-57.56807,-18.25655],[-57.76764,-18.90087],[-57.71995,-18.89573],[-57.71995,-18.97546],[-57.69134,-19.00544],[-57.71113,-19.03161],[-57.78463,-19.03259],[-58.14215,-19.76276],[-57.8496,-19.98346],[-58.16225,-20.16193],[-57.84536,-20.93155],[-57.93492,-21.65505],[-57.88239,-21.6868],[-57.94642,-21.73799],[-57.98625,-22.09157],[-56.6508,-22.28387],[-56.5212,-22.11556],[-56.45893,-22.08072],[-56.23206,-22.25347],[-55.8331,-22.29008],[-55.74941,-22.46436],[-55.741,-22.52018],[-55.72366,-22.5519],[-55.6986,-22.56268],[-55.68742,-22.58407],[-55.62493,-22.62765],[-55.63849,-22.95122],[-55.5446,-23.22811],[-55.52288,-23.2595],[-55.5555,-23.28237],[-55.43585,-23.87157],[-55.44117,-23.9185],[-55.41784,-23.9657],[-55.12292,-23.99669],[-55.0518,-23.98666],[-55.02691,-23.97317],[-54.6238,-23.83078],[-54.32807,-24.01865],[-54.28207,-24.07305],[-54.4423,-25.13381],[-54.62033,-25.46026],[-54.60196,-25.48397],[-54.59509,-25.53696],[-54.59398,-25.59224],[-54.5502,-25.58915],[-54.52926,-25.62846],[-53.90831,-25.55513],[-53.83691,-25.94849],[-53.73511,-26.04211],[-53.73086,-26.05842],[-53.7264,-26.0664],[-53.73391,-26.07006],[-53.73968,-26.10012],[-53.65018,-26.19501],[-53.65237,-26.23289],[-53.63739,-26.2496],[-53.63881,-26.25075],[-53.64632,-26.24798],[-53.64186,-26.25976],[-53.64505,-26.28089],[-53.68269,-26.33359],[-53.73372,-26.6131],[-53.80144,-27.09844],[-54.15978,-27.2889],[-54.19062,-27.27639],[-54.19268,-27.30751],[-54.41888,-27.40882],[-54.50416,-27.48232],[-54.67657,-27.57214],[-54.90159,-27.63132],[-54.90805,-27.73149],[-55.1349,-27.89759],[-55.16872,-27.86224],[-55.33303,-27.94661],[-55.6262,-28.17124],[-55.65418,-28.18304],[-56.01729,-28.51223],[-56.00458,-28.60421],[-56.05265,-28.62651],[-56.54171,-29.11447],[-56.57295,-29.11357],[-56.62789,-29.18073],[-56.81251,-29.48154],[-57.09386,-29.74211],[-57.65132,-30.19229],[-57.22502,-30.26121],[-56.90236,-30.02578],[-56.49267,-30.39471],[-56.4795,-30.3899],[-56.4619,-30.38457],[-55.87388,-31.05053],[-55.58866,-30.84117],[-55.5634,-30.8686],[-55.55373,-30.8732],[-55.55218,-30.88193],[-55.54572,-30.89051],[-55.53431,-30.89714],[-55.53276,-30.90218],[-55.52712,-30.89997],[-55.51862,-30.89828],[-55.50841,-30.9027],[-55.50821,-30.91349],[-54.17384,-31.86168],[-53.76024,-32.0751],[-53.39572,-32.58596],[-53.37671,-32.57005],[-53.1111,-32.71147],[-53.53459,-33.16843],[-53.52794,-33.68908],[-53.44031,-33.69344],[-53.39593,-33.75169],[-53.37138,-33.74313],[-52.83257,-34.01481],[-28.34015,-20.99094],[-28.99601,1.86593],[-51.35485,4.8383],[-51.63798,4.51394],[-51.61983,4.14596],[-51.79599,3.89336],[-51.82312,3.85825],[-51.85573,3.83427],[-52.31787,3.17896],[-52.6906,2.37298],[-52.96539,2.1881],[-53.78743,2.34412],[-54.16286,2.10779],[-54.6084,2.32856],[-55.01919,2.564],[-55.71493,2.40342],[-55.96292,2.53188],[-56.13054,2.27723],[-55.92159,2.05236],[-55.89863,1.89861],[-55.99278,1.83137],[-56.47045,1.95135],[-56.7659,1.89509],[-57.07092,1.95304],[-57.09109,2.01854],[-57.23981,1.95808],[-57.35073,1.98327],[-57.55743,1.69605],[-57.77281,1.73344],[-57.97336,1.64566],[-58.01873,1.51966],[-58.33887,1.58014],[-58.4858,1.48399],[-58.53571,1.29154],[-58.84229,1.17749],[-58.92072,1.31293],[-59.25583,1.40559],[-59.74066,1.87596],[-59.7264,2.27497],[-59.91177,2.36759],[-59.99733,2.92312],[-59.79769,3.37162],[-59.86899,3.57089],[-59.51963,3.91951],[-59.73353,4.20399],[-59.69361,4.34069]]]]}},{type:"Feature",properties:{iso1A2:"BS",iso1A3:"BHS",iso1N3:"044",wikidata:"Q778",nameEn:"The Bahamas",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 242"]},geometry:{type:"MultiPolygon",coordinates:[[[[-72.98446,20.4801],[-71.70065,25.7637],[-79.14818,27.83105],[-79.89631,24.6597],[-80.88924,23.80416],[-72.98446,20.4801]]]]}},{type:"Feature",properties:{iso1A2:"BT",iso1A3:"BTN",iso1N3:"064",wikidata:"Q917",nameEn:"Bhutan",groups:["034","142"],driveSide:"left",callingCodes:["975"]},geometry:{type:"MultiPolygon",coordinates:[[[[91.6469,27.76358],[91.5629,27.84823],[91.48973,27.93903],[91.46327,28.0064],[91.25779,28.07509],[91.20019,27.98715],[90.69894,28.07784],[90.58842,28.02838],[90.13387,28.19178],[89.79762,28.23979],[89.59525,28.16433],[89.12825,27.62502],[89.0582,27.60985],[88.97213,27.51671],[88.95355,27.4106],[89.00216,27.32532],[88.96947,27.30319],[88.93678,27.33777],[88.91901,27.32483],[88.74219,27.144],[88.86984,27.10937],[88.8714,26.97488],[88.92301,26.99286],[88.95807,26.92668],[89.09554,26.89089],[89.12825,26.81661],[89.1926,26.81329],[89.37913,26.86224],[89.38319,26.85963],[89.3901,26.84225],[89.42349,26.83727],[89.63369,26.74402],[89.86124,26.73307],[90.04535,26.72422],[90.30402,26.85098],[90.39271,26.90704],[90.48504,26.8594],[90.67715,26.77215],[91.50067,26.79223],[91.83181,26.87318],[92.05523,26.8692],[92.11863,26.893],[92.03457,27.07334],[92.04702,27.26861],[92.12019,27.27829],[92.01132,27.47352],[91.65007,27.48287],[91.55819,27.6144],[91.6469,27.76358]]]]}},{type:"Feature",properties:{iso1A2:"BV",iso1A3:"BVT",iso1N3:"074",wikidata:"Q23408",nameEn:"Bouvet Island",country:"NO",groups:["005","419","019"]},geometry:{type:"MultiPolygon",coordinates:[[[[4.54042,-54.0949],[2.28941,-54.13089],[3.35353,-55.17558],[4.54042,-54.0949]]]]}},{type:"Feature",properties:{iso1A2:"BW",iso1A3:"BWA",iso1N3:"072",wikidata:"Q963",nameEn:"Botswana",groups:["018","202","002"],driveSide:"left",callingCodes:["267"]},geometry:{type:"MultiPolygon",coordinates:[[[[25.26433,-17.79571],[25.16882,-17.78253],[25.05895,-17.84452],[24.95586,-17.79674],[24.73364,-17.89338],[24.71887,-17.9218],[24.6303,-17.9863],[24.57485,-18.07151],[24.40577,-17.95726],[24.19416,-18.01919],[23.61088,-18.4881],[23.29618,-17.99855],[23.0996,-18.00075],[21.45556,-18.31795],[20.99904,-18.31743],[20.99751,-22.00026],[19.99912,-21.99991],[19.99817,-24.76768],[20.02809,-24.78725],[20.03678,-24.81004],[20.29826,-24.94869],[20.64795,-25.47827],[20.86081,-26.14892],[20.61754,-26.4692],[20.63275,-26.78181],[20.68596,-26.9039],[20.87031,-26.80047],[21.13353,-26.86661],[21.37869,-26.82083],[21.69322,-26.86152],[21.7854,-26.79199],[21.77114,-26.69015],[21.83291,-26.65959],[21.90703,-26.66808],[22.06192,-26.61882],[22.21206,-26.3773],[22.41921,-26.23078],[22.56365,-26.19668],[22.70808,-25.99186],[22.86012,-25.50572],[23.03497,-25.29971],[23.47588,-25.29971],[23.9244,-25.64286],[24.18287,-25.62916],[24.36531,-25.773],[24.44703,-25.73021],[24.67319,-25.81749],[24.8946,-25.80723],[25.01718,-25.72507],[25.12266,-25.75931],[25.33076,-25.76616],[25.58543,-25.6343],[25.6643,-25.4491],[25.69661,-25.29284],[25.72702,-25.25503],[25.88571,-24.87802],[25.84295,-24.78661],[25.8515,-24.75727],[26.39409,-24.63468],[26.46346,-24.60358],[26.51667,-24.47219],[26.84165,-24.24885],[26.99749,-23.65486],[27.33768,-23.40917],[27.52393,-23.37952],[27.6066,-23.21894],[27.74154,-23.2137],[27.93539,-23.04941],[27.93729,-22.96194],[28.04752,-22.90243],[28.04562,-22.8394],[28.34874,-22.5694],[28.63287,-22.55887],[28.91889,-22.44299],[29.0151,-22.22907],[29.10881,-22.21202],[29.15268,-22.21399],[29.18974,-22.18599],[29.21955,-22.17771],[29.37703,-22.19581],[29.3533,-22.18363],[29.24648,-22.05967],[29.1974,-22.07472],[29.14501,-22.07275],[29.08495,-22.04867],[29.04108,-22.00563],[29.02191,-21.95665],[29.02191,-21.90647],[29.04023,-21.85864],[29.07763,-21.81877],[28.58114,-21.63455],[28.49942,-21.66634],[28.29416,-21.59037],[28.01669,-21.57624],[27.91407,-21.31621],[27.69171,-21.08409],[27.72972,-20.51735],[27.69361,-20.48531],[27.28865,-20.49873],[27.29831,-20.28935],[27.21278,-20.08244],[26.72246,-19.92707],[26.17227,-19.53709],[25.96226,-19.08152],[25.99837,-19.02943],[25.94326,-18.90362],[25.82353,-18.82808],[25.79217,-18.6355],[25.68859,-18.56165],[25.53465,-18.39041],[25.39972,-18.12691],[25.31799,-18.07091],[25.23909,-17.90832],[25.26433,-17.79571]]]]}},{type:"Feature",properties:{iso1A2:"BY",iso1A3:"BLR",iso1N3:"112",wikidata:"Q184",nameEn:"Belarus",groups:["151","150"],callingCodes:["375"]},geometry:{type:"MultiPolygon",coordinates:[[[[28.15217,56.16964],[27.97865,56.11849],[27.63065,55.89687],[27.61683,55.78558],[27.3541,55.8089],[27.27804,55.78299],[27.1559,55.85032],[26.97153,55.8102],[26.87448,55.7172],[26.76872,55.67658],[26.71802,55.70645],[26.64888,55.70515],[26.63231,55.67968],[26.63167,55.57887],[26.55094,55.5093],[26.5522,55.40277],[26.44937,55.34832],[26.5709,55.32572],[26.6714,55.33902],[26.80929,55.31642],[26.83266,55.30444],[26.835,55.28182],[26.73017,55.24226],[26.72983,55.21788],[26.68075,55.19787],[26.69243,55.16718],[26.54753,55.14181],[26.51481,55.16051],[26.46249,55.12814],[26.35121,55.1525],[26.30628,55.12536],[26.23202,55.10439],[26.26941,55.08032],[26.20397,54.99729],[26.13386,54.98924],[26.05907,54.94631],[25.99129,54.95705],[25.89462,54.93438],[25.74122,54.80108],[25.75977,54.57252],[25.68045,54.5321],[25.64813,54.48704],[25.62203,54.4656],[25.63371,54.42075],[25.5376,54.33158],[25.55425,54.31591],[25.68513,54.31727],[25.78553,54.23327],[25.78563,54.15747],[25.71084,54.16704],[25.64875,54.1259],[25.54724,54.14925],[25.51452,54.17799],[25.56823,54.25212],[25.509,54.30267],[25.35559,54.26544],[25.22705,54.26271],[25.19199,54.219],[25.0728,54.13419],[24.991,54.14241],[24.96894,54.17589],[24.77131,54.11091],[24.85311,54.02862],[24.74279,53.96663],[24.69185,53.96543],[24.69652,54.01901],[24.62275,54.00217],[24.44411,53.90076],[24.34128,53.90076],[24.19638,53.96405],[23.98837,53.92554],[23.95098,53.9613],[23.81309,53.94205],[23.80543,53.89558],[23.71726,53.93379],[23.61677,53.92691],[23.51284,53.95052],[23.62004,53.60942],[23.81995,53.24131],[23.85657,53.22923],[23.91393,53.16469],[23.87548,53.0831],[23.92184,53.02079],[23.94689,52.95919],[23.91805,52.94016],[23.93763,52.71332],[23.73615,52.6149],[23.58296,52.59868],[23.45112,52.53774],[23.34141,52.44845],[23.18196,52.28812],[23.20071,52.22848],[23.47859,52.18215],[23.54314,52.12148],[23.61,52.11264],[23.64066,52.07626],[23.68733,51.9906],[23.61523,51.92066],[23.62691,51.78208],[23.53198,51.74298],[23.57053,51.55938],[23.56236,51.53673],[23.62751,51.50512],[23.6736,51.50255],[23.60906,51.62122],[23.7766,51.66809],[23.91118,51.63316],[23.8741,51.59734],[23.99907,51.58369],[24.13075,51.66979],[24.3163,51.75063],[24.29021,51.80841],[24.37123,51.88222],[24.98784,51.91273],[25.20228,51.97143],[25.46163,51.92205],[25.73673,51.91973],[25.80574,51.94556],[25.83217,51.92587],[26.00408,51.92967],[26.19084,51.86781],[26.39367,51.87315],[26.46962,51.80501],[26.69759,51.82284],[26.80043,51.75777],[26.9489,51.73788],[26.99422,51.76933],[27.20602,51.77291],[27.20948,51.66713],[27.26613,51.65957],[27.24828,51.60161],[27.47212,51.61184],[27.51058,51.5854],[27.55727,51.63486],[27.71932,51.60672],[27.67125,51.50854],[27.76052,51.47604],[27.85253,51.62293],[27.91844,51.61952],[27.95827,51.56065],[28.10658,51.57857],[28.23452,51.66988],[28.37592,51.54505],[28.47051,51.59734],[28.64429,51.5664],[28.69161,51.44695],[28.73143,51.46236],[28.75615,51.41442],[28.78224,51.45294],[28.76027,51.48802],[28.81795,51.55552],[28.95528,51.59222],[28.99098,51.56833],[29.1187,51.65872],[29.16402,51.64679],[29.20659,51.56918],[29.25603,51.57089],[29.25191,51.49828],[29.32881,51.37843],[29.42357,51.4187],[29.49773,51.39814],[29.54372,51.48372],[29.7408,51.53417],[29.77376,51.4461],[30.17888,51.51025],[30.34642,51.42555],[30.36153,51.33984],[30.56203,51.25655],[30.64992,51.35014],[30.51946,51.59649],[30.68804,51.82806],[30.76443,51.89739],[30.90897,52.00699],[30.95589,52.07775],[31.13332,52.1004],[31.25142,52.04131],[31.38326,52.12991],[31.7822,52.11406],[31.77877,52.18636],[31.6895,52.1973],[31.70735,52.26711],[31.57971,52.32146],[31.62084,52.33849],[31.61397,52.48843],[31.56316,52.51518],[31.63869,52.55361],[31.50406,52.69707],[31.57277,52.71613],[31.592,52.79011],[31.35667,52.97854],[31.24147,53.031],[31.32283,53.04101],[31.33519,53.08805],[31.3915,53.09712],[31.36403,53.13504],[31.40523,53.21406],[31.56316,53.19432],[31.62496,53.22886],[31.787,53.18033],[31.82373,53.10042],[32.15368,53.07594],[32.40773,53.18856],[32.51725,53.28431],[32.73257,53.33494],[32.74968,53.45597],[32.47777,53.5548],[32.40499,53.6656],[32.50112,53.68594],[32.45717,53.74039],[32.36663,53.7166],[32.12621,53.81586],[31.89137,53.78099],[31.77028,53.80015],[31.85019,53.91801],[31.88744,54.03653],[31.89599,54.0837],[31.57002,54.14535],[31.30791,54.25315],[31.3177,54.34067],[31.22945,54.46585],[31.08543,54.50361],[31.21399,54.63113],[31.19339,54.66947],[30.99187,54.67046],[30.98226,54.68872],[31.0262,54.70698],[30.97127,54.71967],[30.95479,54.74346],[30.75165,54.80699],[30.8264,54.90062],[30.81759,54.94064],[30.93144,54.9585],[30.95754,54.98609],[30.9081,55.02232],[30.94243,55.03964],[31.00972,55.02783],[31.02071,55.06167],[30.97369,55.17134],[30.87944,55.28223],[30.81946,55.27931],[30.8257,55.3313],[30.93144,55.3914],[30.90123,55.46621],[30.95204,55.50667],[30.93419,55.6185],[30.86003,55.63169],[30.7845,55.58514],[30.72957,55.66268],[30.67464,55.64176],[30.63344,55.73079],[30.51037,55.76568],[30.51346,55.78982],[30.48257,55.81066],[30.30987,55.83592],[30.27776,55.86819],[30.12136,55.8358],[29.97975,55.87281],[29.80672,55.79569],[29.61446,55.77716],[29.51283,55.70294],[29.3604,55.75862],[29.44692,55.95978],[29.21717,55.98971],[29.08299,56.03427],[28.73418,55.97131],[28.63668,56.07262],[28.68337,56.10173],[28.5529,56.11705],[28.43068,56.09407],[28.37987,56.11399],[28.36888,56.05805],[28.30571,56.06035],[28.15217,56.16964]]]]}},{type:"Feature",properties:{iso1A2:"BZ",iso1A3:"BLZ",iso1N3:"084",wikidata:"Q242",nameEn:"Belize",groups:["013","003","419","019"],roadSpeedUnit:"mph",callingCodes:["501"]},geometry:{type:"MultiPolygon",coordinates:[[[[-88.3268,18.49048],[-88.48242,18.49164],[-88.71505,18.0707],[-88.8716,17.89535],[-89.03839,18.0067],[-89.15105,17.95104],[-89.14985,17.81563],[-89.15025,17.04813],[-89.22683,15.88619],[-89.17418,15.90898],[-89.02415,15.9063],[-88.95358,15.88698],[-88.40779,16.09624],[-86.92368,17.61462],[-87.84815,18.18511],[-87.85693,18.18266],[-87.86657,18.19971],[-87.87604,18.18313],[-87.90671,18.15213],[-88.03165,18.16657],[-88.03238,18.41778],[-88.26593,18.47617],[-88.29909,18.47591],[-88.3268,18.49048]]]]}},{type:"Feature",properties:{iso1A2:"CA",iso1A3:"CAN",iso1N3:"124",wikidata:"Q16",nameEn:"Canada",groups:["021","003","019"],callingCodes:["1"]},geometry:{type:"MultiPolygon",coordinates:[[[[-67.20349,45.1722],[-67.19603,45.16771],[-67.15965,45.16179],[-67.11316,45.11176],[-67.0216,44.95333],[-66.96824,44.90965],[-66.98249,44.87071],[-66.96824,44.83078],[-66.93432,44.82597],[-67.16117,44.20069],[-61.98255,37.34815],[-56.27503,47.39728],[-53.12387,41.40385],[-46.37635,57.3249],[-76.75614,76.72014],[-68.21821,80.48551],[-45.47832,84.58738],[-140.97446,84.39275],[-141.00116,60.30648],[-140.5227,60.22077],[-140.45648,60.30919],[-139.98024,60.18027],[-139.68991,60.33693],[-139.05831,60.35205],[-139.20603,60.08896],[-139.05365,59.99655],[-138.71149,59.90728],[-138.62145,59.76431],[-137.60623,59.24465],[-137.4925,58.89415],[-136.82619,59.16198],[-136.52365,59.16752],[-136.47323,59.46617],[-136.33727,59.44466],[-136.22381,59.55526],[-136.31566,59.59083],[-135.48007,59.79937],[-135.03069,59.56208],[-135.00267,59.28745],[-134.7047,59.2458],[-134.55699,59.1297],[-134.48059,59.13231],[-134.27175,58.8634],[-133.84645,58.73543],[-133.38523,58.42773],[-131.8271,56.62247],[-130.77769,56.36185],[-130.33965,56.10849],[-130.10173,56.12178],[-130.00093,56.00325],[-130.00857,55.91344],[-130.15373,55.74895],[-129.97513,55.28029],[-130.08035,55.21556],[-130.18765,55.07744],[-130.27203,54.97174],[-130.44184,54.85377],[-130.64499,54.76912],[-130.61931,54.70835],[-133.92876,54.62289],[-133.36909,48.51151],[-125.03842,48.53282],[-123.50039,48.21223],[-123.15614,48.35395],[-123.26565,48.6959],[-123.0093,48.76586],[-123.0093,48.83186],[-123.32163,49.00419],[-117.03266,49.00056],[-116.04938,48.99999],[-114.0683,48.99885],[-110.0051,48.99901],[-104.05004,48.99925],[-101.36198,48.99935],[-97.24024,48.99952],[-95.15355,48.9996],[-95.15357,49.384],[-95.12903,49.37056],[-95.05825,49.35311],[-95.01419,49.35647],[-94.99532,49.36579],[-94.95681,49.37035],[-94.85381,49.32492],[-94.8159,49.32299],[-94.82487,49.29483],[-94.77355,49.11998],[-94.75017,49.09931],[-94.687,48.84077],[-94.70087,48.8339],[-94.70486,48.82365],[-94.69669,48.80918],[-94.69335,48.77883],[-94.58903,48.71803],[-94.54885,48.71543],[-94.53826,48.70216],[-94.44258,48.69223],[-94.4174,48.71049],[-94.27153,48.70232],[-94.25172,48.68404],[-94.25104,48.65729],[-94.23215,48.65202],[-93.85769,48.63284],[-93.83288,48.62745],[-93.80676,48.58232],[-93.80939,48.52439],[-93.79267,48.51631],[-93.66382,48.51845],[-93.47022,48.54357],[-93.44472,48.59147],[-93.40693,48.60948],[-93.39758,48.60364],[-93.3712,48.60599],[-93.33946,48.62787],[-93.25391,48.64266],[-92.94973,48.60866],[-92.7287,48.54005],[-92.6342,48.54133],[-92.62747,48.50278],[-92.69927,48.49573],[-92.71323,48.46081],[-92.65606,48.43471],[-92.50712,48.44921],[-92.45588,48.40624],[-92.48147,48.36609],[-92.37185,48.22259],[-92.27167,48.25046],[-92.30939,48.31251],[-92.26662,48.35651],[-92.202,48.35252],[-92.14732,48.36578],[-92.05339,48.35958],[-91.98929,48.25409],[-91.86125,48.21278],[-91.71231,48.19875],[-91.70451,48.11805],[-91.55649,48.10611],[-91.58025,48.04339],[-91.45829,48.07454],[-91.43248,48.04912],[-91.25025,48.08522],[-91.08016,48.18096],[-90.87588,48.2484],[-90.75045,48.09143],[-90.56444,48.12184],[-90.56312,48.09488],[-90.07418,48.11043],[-89.89974,47.98109],[-89.77248,48.02607],[-89.57972,48.00023],[-89.48837,48.01412],[-88.37033,48.30586],[-84.85871,46.88881],[-84.55635,46.45974],[-84.47607,46.45225],[-84.4481,46.48972],[-84.42101,46.49853],[-84.34174,46.50683],[-84.29893,46.49127],[-84.26351,46.49508],[-84.2264,46.53337],[-84.1945,46.54061],[-84.17723,46.52753],[-84.12885,46.53068],[-84.11196,46.50248],[-84.13451,46.39218],[-84.11254,46.32329],[-84.11615,46.2681],[-84.09756,46.25512],[-84.1096,46.23987],[-83.95399,46.05634],[-83.90453,46.05922],[-83.83329,46.12169],[-83.57017,46.105],[-83.43746,45.99749],[-83.59589,45.82131],[-82.48419,45.30225],[-82.42469,42.992],[-82.4146,42.97626],[-82.4253,42.95423],[-82.45331,42.93139],[-82.4826,42.8068],[-82.46613,42.76615],[-82.51063,42.66025],[-82.51858,42.611],[-82.57583,42.5718],[-82.58873,42.54984],[-82.64242,42.55594],[-82.82964,42.37355],[-83.02253,42.33045],[-83.07837,42.30978],[-83.09837,42.28877],[-83.12724,42.2376],[-83.14962,42.04089],[-83.11184,41.95671],[-82.67862,41.67615],[-78.93684,42.82887],[-78.90712,42.89733],[-78.90905,42.93022],[-78.93224,42.95229],[-78.96312,42.95509],[-78.98126,42.97],[-79.02074,42.98444],[-79.02424,43.01983],[-78.99941,43.05612],[-79.01055,43.06659],[-79.07486,43.07845],[-79.05671,43.10937],[-79.06881,43.12029],[-79.0427,43.13934],[-79.04652,43.16396],[-79.05384,43.17418],[-79.05002,43.20133],[-79.05544,43.21224],[-79.05512,43.25375],[-79.06921,43.26183],[-79.25796,43.54052],[-76.79706,43.63099],[-76.43859,44.09393],[-76.35324,44.13493],[-76.31222,44.19894],[-76.244,44.19643],[-76.1664,44.23051],[-76.16285,44.28262],[-76.00018,44.34896],[-75.95947,44.34463],[-75.8217,44.43176],[-75.76813,44.51537],[-75.41441,44.76614],[-75.2193,44.87821],[-75.01363,44.95608],[-74.99101,44.98051],[-74.8447,45.00606],[-74.66689,45.00646],[-74.32699,44.99029],[-73.35025,45.00942],[-71.50067,45.01357],[-71.48735,45.07784],[-71.42778,45.12624],[-71.40364,45.21382],[-71.44252,45.2361],[-71.37133,45.24624],[-71.29371,45.29996],[-71.22338,45.25184],[-71.19723,45.25438],[-71.14568,45.24128],[-71.08364,45.30623],[-71.01866,45.31573],[-71.0107,45.34819],[-70.95193,45.33895],[-70.91169,45.29849],[-70.89864,45.2398],[-70.84816,45.22698],[-70.80236,45.37444],[-70.82638,45.39828],[-70.78372,45.43269],[-70.65383,45.37592],[-70.62518,45.42286],[-70.72651,45.49771],[-70.68516,45.56964],[-70.54019,45.67291],[-70.38934,45.73215],[-70.41523,45.79497],[-70.25976,45.89675],[-70.24694,45.95138],[-70.31025,45.96424],[-70.23855,46.1453],[-70.29078,46.18832],[-70.18547,46.35357],[-70.05812,46.41768],[-69.99966,46.69543],[-69.22119,47.46461],[-69.05148,47.42012],[-69.05073,47.30076],[-69.05039,47.2456],[-68.89222,47.1807],[-68.70125,47.24399],[-68.60575,47.24659],[-68.57914,47.28431],[-68.38332,47.28723],[-68.37458,47.35851],[-68.23244,47.35712],[-67.94843,47.1925],[-67.87993,47.10377],[-67.78578,47.06473],[-67.78111,45.9392],[-67.75196,45.91814],[-67.80961,45.87531],[-67.75654,45.82324],[-67.80653,45.80022],[-67.80705,45.69528],[-67.6049,45.60725],[-67.43815,45.59162],[-67.42144,45.50584],[-67.50578,45.48971],[-67.42394,45.37969],[-67.48201,45.27351],[-67.34927,45.122],[-67.29754,45.14865],[-67.29748,45.18173],[-67.27039,45.1934],[-67.22751,45.16344],[-67.20349,45.1722]]]]}},{type:"Feature",properties:{iso1A2:"CC",iso1A3:"CCK",iso1N3:"166",wikidata:"Q36004",nameEn:"Cocos (Keeling) Islands",country:"AU",groups:["053","009"],driveSide:"left",callingCodes:["61"]},geometry:{type:"MultiPolygon",coordinates:[[[[96.61846,-10.82438],[96.02343,-12.68334],[97.93979,-12.33309],[96.61846,-10.82438]]]]}},{type:"Feature",properties:{iso1A2:"CD",iso1A3:"COD",iso1N3:"180",wikidata:"Q974",nameEn:"Democratic Republic of the Congo",aliases:["ZR"],groups:["017","202","002"],callingCodes:["243"]},geometry:{type:"MultiPolygon",coordinates:[[[[27.44012,5.07349],[27.09575,5.22305],[26.93064,5.13535],[26.85579,5.03887],[26.74572,5.10685],[26.48595,5.04984],[26.13371,5.25594],[25.86073,5.19455],[25.53271,5.37431],[25.34558,5.29101],[25.31256,5.03668],[24.71816,4.90509],[24.46719,5.0915],[23.38847,4.60013],[22.94817,4.82392],[22.89094,4.79321],[22.84691,4.69887],[22.78526,4.71423],[22.6928,4.47285],[22.60915,4.48821],[22.5431,4.22041],[22.45504,4.13039],[22.27682,4.11347],[22.10721,4.20723],[21.6405,4.317],[21.55904,4.25553],[21.25744,4.33676],[21.21341,4.29285],[21.11214,4.33895],[21.08793,4.39603],[20.90383,4.44877],[20.60184,4.42394],[18.62755,3.47564],[18.63857,3.19342],[18.10683,2.26876],[18.08034,1.58553],[17.85887,1.04327],[17.86989,0.58873],[17.95255,0.48128],[17.93877,0.32424],[17.81204,0.23884],[17.66051,-0.26535],[17.72112,-0.52707],[17.32438,-0.99265],[16.97999,-1.12762],[16.70724,-1.45815],[16.50336,-1.8795],[16.16173,-2.16586],[16.22785,-2.59528],[16.1755,-3.25014],[16.21407,-3.2969],[15.89448,-3.9513],[15.53081,-4.042],[15.48121,-4.22062],[15.41785,-4.28381],[15.32693,-4.27282],[15.25411,-4.31121],[15.1978,-4.32388],[14.83101,-4.80838],[14.67948,-4.92093],[14.5059,-4.84956],[14.41499,-4.8825],[14.37366,-4.56125],[14.47284,-4.42941],[14.3957,-4.36623],[14.40672,-4.28381],[13.9108,-4.50906],[13.81162,-4.41842],[13.71794,-4.44864],[13.70417,-4.72601],[13.50305,-4.77818],[13.41764,-4.89897],[13.11182,-4.5942],[13.09648,-4.63739],[13.11195,-4.67745],[12.8733,-4.74346],[12.70868,-4.95505],[12.63465,-4.94632],[12.60251,-5.01715],[12.46297,-5.09408],[12.49815,-5.14058],[12.51589,-5.1332],[12.53586,-5.14658],[12.53599,-5.1618],[12.52301,-5.17481],[12.52318,-5.74353],[12.26557,-5.74031],[12.20376,-5.76338],[11.95767,-5.94705],[12.42245,-6.07585],[13.04371,-5.87078],[16.55507,-5.85631],[16.96282,-7.21787],[17.5828,-8.13784],[18.33635,-8.00126],[19.33698,-7.99743],[19.5469,-7.00195],[20.30218,-6.98955],[20.31846,-6.91953],[20.61689,-6.90876],[20.56263,-7.28566],[21.79824,-7.29628],[21.84856,-9.59871],[22.19039,-9.94628],[22.32604,-10.76291],[22.17954,-10.85884],[22.25951,-11.24911],[22.54205,-11.05784],[23.16602,-11.10577],[23.45631,-10.946],[23.86868,-11.02856],[24.00027,-10.89356],[24.34528,-11.06816],[24.42612,-11.44975],[25.34069,-11.19707],[25.33058,-11.65767],[26.01777,-11.91488],[26.88687,-12.01868],[27.04351,-11.61312],[27.22541,-11.60323],[27.21025,-11.76157],[27.59932,-12.22123],[28.33199,-12.41375],[29.01918,-13.41353],[29.60531,-13.21685],[29.65078,-13.41844],[29.81551,-13.44683],[29.8139,-12.14898],[29.48404,-12.23604],[29.4992,-12.43843],[29.18592,-12.37921],[28.48357,-11.87532],[28.37241,-11.57848],[28.65032,-10.65133],[28.62795,-9.92942],[28.68532,-9.78],[28.56208,-9.49122],[28.51627,-9.44726],[28.52636,-9.35379],[28.36562,-9.30091],[28.38526,-9.23393],[28.9711,-8.66935],[28.88917,-8.4831],[30.79243,-8.27382],[30.2567,-7.14121],[29.52552,-6.2731],[29.43673,-4.44845],[29.23708,-3.75856],[29.21463,-3.3514],[29.25633,-3.05471],[29.17258,-2.99385],[29.16037,-2.95457],[29.09797,-2.91935],[29.09119,-2.87871],[29.0505,-2.81774],[29.00404,-2.81978],[29.00167,-2.78523],[29.04081,-2.7416],[29.00357,-2.70596],[28.94346,-2.69124],[28.89793,-2.66111],[28.90226,-2.62385],[28.89288,-2.55848],[28.87943,-2.55165],[28.86193,-2.53185],[28.86209,-2.5231],[28.87497,-2.50887],[28.88846,-2.50493],[28.89342,-2.49017],[28.89132,-2.47557],[28.86846,-2.44866],[28.86826,-2.41888],[28.89601,-2.37321],[28.95642,-2.37321],[29.00051,-2.29001],[29.105,-2.27043],[29.17562,-2.12278],[29.11847,-1.90576],[29.24458,-1.69663],[29.24323,-1.66826],[29.36322,-1.50887],[29.45038,-1.5054],[29.53062,-1.40499],[29.59061,-1.39016],[29.58388,-0.89821],[29.63006,-0.8997],[29.62708,-0.71055],[29.67176,-0.55714],[29.67474,-0.47969],[29.65091,-0.46777],[29.72687,-0.08051],[29.7224,0.07291],[29.77454,0.16675],[29.81922,0.16824],[29.87284,0.39166],[29.97413,0.52124],[29.95477,0.64486],[29.98307,0.84295],[30.1484,0.89805],[30.22139,0.99635],[30.24671,1.14974],[30.48503,1.21675],[31.30127,2.11006],[31.28042,2.17853],[31.20148,2.2217],[31.1985,2.29462],[31.12104,2.27676],[31.07934,2.30207],[31.06593,2.35862],[30.96911,2.41071],[30.91102,2.33332],[30.83059,2.42559],[30.74271,2.43601],[30.75612,2.5863],[30.8857,2.83923],[30.8574,2.9508],[30.77101,3.04897],[30.84251,3.26908],[30.93486,3.40737],[30.94081,3.50847],[30.85153,3.48867],[30.85997,3.5743],[30.80713,3.60506],[30.78512,3.67097],[30.56277,3.62703],[30.57378,3.74567],[30.55396,3.84451],[30.47691,3.83353],[30.27658,3.95653],[30.22374,3.93896],[30.1621,4.10586],[30.06964,4.13221],[29.79666,4.37809],[29.82087,4.56246],[29.49726,4.7007],[29.43341,4.50101],[29.22207,4.34297],[29.03054,4.48784],[28.8126,4.48784],[28.6651,4.42638],[28.20719,4.35614],[27.79551,4.59976],[27.76469,4.79284],[27.65462,4.89375],[27.56656,4.89375],[27.44012,5.07349]]]]}},{type:"Feature",properties:{iso1A2:"CF",iso1A3:"CAF",iso1N3:"140",wikidata:"Q929",nameEn:"Central African Republic",groups:["017","202","002"],callingCodes:["236"]},geometry:{type:"MultiPolygon",coordinates:[[[[22.87758,10.91915],[22.45889,11.00246],[21.72139,10.64136],[21.71479,10.29932],[21.63553,10.217],[21.52766,10.2105],[21.34934,9.95907],[21.26348,9.97642],[20.82979,9.44696],[20.36748,9.11019],[19.06421,9.00367],[18.86388,8.87971],[19.11044,8.68172],[18.79783,8.25929],[18.67455,8.22226],[18.62612,8.14163],[18.64153,8.08714],[18.6085,8.05009],[18.02731,8.01085],[17.93926,7.95853],[17.67288,7.98905],[16.8143,7.53971],[16.6668,7.67281],[16.658,7.75353],[16.59415,7.76444],[16.58315,7.88657],[16.41583,7.77971],[16.40703,7.68809],[15.79942,7.44149],[15.73118,7.52006],[15.49743,7.52179],[15.23397,7.25135],[15.04717,6.77085],[14.96311,6.75693],[14.79966,6.39043],[14.80122,6.34866],[14.74206,6.26356],[14.56149,6.18928],[14.43073,6.08867],[14.42917,6.00508],[14.49455,5.91683],[14.60974,5.91838],[14.62375,5.70466],[14.58951,5.59777],[14.62531,5.51411],[14.52724,5.28319],[14.57083,5.23979],[14.65489,5.21343],[14.73383,4.6135],[15.00825,4.41458],[15.08609,4.30282],[15.10644,4.1362],[15.17482,4.05131],[15.07686,4.01805],[15.73522,3.24348],[15.77725,3.26835],[16.05449,3.02306],[16.08252,2.45708],[16.19357,2.21537],[16.50126,2.84739],[16.46701,2.92512],[16.57598,3.47999],[16.68283,3.54257],[17.01746,3.55136],[17.35649,3.63045],[17.46876,3.70515],[17.60966,3.63705],[17.83421,3.61068],[17.85842,3.53378],[18.05656,3.56893],[18.14902,3.54476],[18.17323,3.47665],[18.24148,3.50302],[18.2723,3.57992],[18.39558,3.58212],[18.49245,3.63924],[18.58711,3.49423],[18.62755,3.47564],[20.60184,4.42394],[20.90383,4.44877],[21.08793,4.39603],[21.11214,4.33895],[21.21341,4.29285],[21.25744,4.33676],[21.55904,4.25553],[21.6405,4.317],[22.10721,4.20723],[22.27682,4.11347],[22.45504,4.13039],[22.5431,4.22041],[22.60915,4.48821],[22.6928,4.47285],[22.78526,4.71423],[22.84691,4.69887],[22.89094,4.79321],[22.94817,4.82392],[23.38847,4.60013],[24.46719,5.0915],[24.71816,4.90509],[25.31256,5.03668],[25.34558,5.29101],[25.53271,5.37431],[25.86073,5.19455],[26.13371,5.25594],[26.48595,5.04984],[26.74572,5.10685],[26.85579,5.03887],[26.93064,5.13535],[27.09575,5.22305],[27.44012,5.07349],[27.26886,5.25876],[27.23017,5.37167],[27.28621,5.56382],[27.22705,5.62889],[27.22705,5.71254],[26.51721,6.09655],[26.58259,6.1987],[26.32729,6.36272],[26.38022,6.63493],[25.90076,7.09549],[25.37461,7.33024],[25.35281,7.42595],[25.20337,7.50312],[25.20649,7.61115],[25.29214,7.66675],[25.25319,7.8487],[24.98855,7.96588],[24.85156,8.16933],[24.35965,8.26177],[24.13238,8.36959],[24.25691,8.69288],[23.51905,8.71749],[23.59065,8.99743],[23.44744,8.99128],[23.4848,9.16959],[23.56263,9.19418],[23.64358,9.28637],[23.64981,9.44303],[23.62179,9.53823],[23.69155,9.67566],[23.67164,9.86923],[23.3128,10.45214],[23.02221,10.69235],[22.87758,10.91915]]]]}},{type:"Feature",properties:{iso1A2:"CG",iso1A3:"COG",iso1N3:"178",wikidata:"Q971",nameEn:"Republic of the Congo",groups:["017","202","002"],callingCodes:["242"]},geometry:{type:"MultiPolygon",coordinates:[[[[18.62755,3.47564],[18.58711,3.49423],[18.49245,3.63924],[18.39558,3.58212],[18.2723,3.57992],[18.24148,3.50302],[18.17323,3.47665],[18.14902,3.54476],[18.05656,3.56893],[17.85842,3.53378],[17.83421,3.61068],[17.60966,3.63705],[17.46876,3.70515],[17.35649,3.63045],[17.01746,3.55136],[16.68283,3.54257],[16.57598,3.47999],[16.46701,2.92512],[16.50126,2.84739],[16.19357,2.21537],[16.15568,2.18955],[16.08563,2.19733],[16.05294,1.9811],[16.14634,1.70259],[16.02647,1.65591],[16.02959,1.76483],[15.48942,1.98265],[15.34776,1.91264],[15.22634,2.03243],[15.00996,1.98887],[14.61145,2.17866],[13.29457,2.16106],[13.13461,1.57238],[13.25447,1.32339],[13.15519,1.23368],[13.89582,1.4261],[14.25186,1.39842],[14.48179,0.9152],[14.26066,0.57255],[14.10909,0.58563],[13.88648,0.26652],[13.90632,-0.2287],[14.06862,-0.20826],[14.2165,-0.38261],[14.41887,-0.44799],[14.52569,-0.57818],[14.41838,-1.89412],[14.25932,-1.97624],[14.23518,-2.15671],[14.16202,-2.23916],[14.23829,-2.33715],[14.10442,-2.49268],[13.85846,-2.46935],[13.92073,-2.35581],[13.75884,-2.09293],[13.47977,-2.43224],[13.02759,-2.33098],[12.82172,-1.91091],[12.61312,-1.8129],[12.44656,-1.92025],[12.47925,-2.32626],[12.04895,-2.41704],[11.96866,-2.33559],[11.74605,-2.39936],[11.57637,-2.33379],[11.64487,-2.61865],[11.5359,-2.85654],[11.64798,-2.81146],[11.80365,-3.00424],[11.70558,-3.0773],[11.70227,-3.17465],[11.96554,-3.30267],[11.8318,-3.5812],[11.92719,-3.62768],[11.87083,-3.71571],[11.68608,-3.68942],[11.57949,-3.52798],[11.48764,-3.51089],[11.22301,-3.69888],[11.12647,-3.94169],[10.75913,-4.39519],[11.50888,-5.33417],[12.00924,-5.02627],[12.16068,-4.90089],[12.20901,-4.75642],[12.25587,-4.79437],[12.32324,-4.78415],[12.40964,-4.60609],[12.64835,-4.55937],[12.76844,-4.38709],[12.87096,-4.40315],[12.91489,-4.47907],[13.09648,-4.63739],[13.11182,-4.5942],[13.41764,-4.89897],[13.50305,-4.77818],[13.70417,-4.72601],[13.71794,-4.44864],[13.81162,-4.41842],[13.9108,-4.50906],[14.40672,-4.28381],[14.3957,-4.36623],[14.47284,-4.42941],[14.37366,-4.56125],[14.41499,-4.8825],[14.5059,-4.84956],[14.67948,-4.92093],[14.83101,-4.80838],[15.1978,-4.32388],[15.25411,-4.31121],[15.32693,-4.27282],[15.41785,-4.28381],[15.48121,-4.22062],[15.53081,-4.042],[15.89448,-3.9513],[16.21407,-3.2969],[16.1755,-3.25014],[16.22785,-2.59528],[16.16173,-2.16586],[16.50336,-1.8795],[16.70724,-1.45815],[16.97999,-1.12762],[17.32438,-0.99265],[17.72112,-0.52707],[17.66051,-0.26535],[17.81204,0.23884],[17.93877,0.32424],[17.95255,0.48128],[17.86989,0.58873],[17.85887,1.04327],[18.08034,1.58553],[18.10683,2.26876],[18.63857,3.19342],[18.62755,3.47564]]]]}},{type:"Feature",properties:{iso1A2:"CH",iso1A3:"CHE",iso1N3:"756",wikidata:"Q39",nameEn:"Switzerland",groups:["155","150"],callingCodes:["41"]},geometry:{type:"MultiPolygon",coordinates:[[[[8.72809,47.69282],[8.72617,47.69651],[8.73671,47.7169],[8.70543,47.73121],[8.74251,47.75168],[8.71778,47.76571],[8.68985,47.75686],[8.68022,47.78599],[8.65292,47.80066],[8.64425,47.76398],[8.62408,47.7626],[8.61657,47.79998],[8.56415,47.80633],[8.56814,47.78001],[8.48868,47.77215],[8.45771,47.7493],[8.44807,47.72426],[8.40569,47.69855],[8.4211,47.68407],[8.40473,47.67499],[8.41346,47.66676],[8.42264,47.66667],[8.44711,47.65379],[8.4667,47.65747],[8.46605,47.64103],[8.49656,47.64709],[8.5322,47.64687],[8.52801,47.66059],[8.56141,47.67088],[8.57683,47.66158],[8.6052,47.67258],[8.61113,47.66332],[8.62884,47.65098],[8.62049,47.63757],[8.60412,47.63735],[8.61471,47.64514],[8.60701,47.65271],[8.59545,47.64298],[8.60348,47.61204],[8.57586,47.59537],[8.55756,47.62394],[8.51686,47.63476],[8.50747,47.61897],[8.45578,47.60121],[8.46637,47.58389],[8.48949,47.588],[8.49431,47.58107],[8.43235,47.56617],[8.39477,47.57826],[8.38273,47.56608],[8.32735,47.57133],[8.30277,47.58607],[8.29524,47.5919],[8.29722,47.60603],[8.2824,47.61225],[8.26313,47.6103],[8.25863,47.61571],[8.23809,47.61204],[8.22577,47.60385],[8.22011,47.6181],[8.20617,47.62141],[8.19378,47.61636],[8.1652,47.5945],[8.14947,47.59558],[8.13823,47.59147],[8.13662,47.58432],[8.11543,47.5841],[8.10395,47.57918],[8.10002,47.56504],[8.08557,47.55768],[8.06663,47.56374],[8.04383,47.55443],[8.02136,47.55096],[8.00113,47.55616],[7.97581,47.55493],[7.95682,47.55789],[7.94494,47.54511],[7.91251,47.55031],[7.90673,47.57674],[7.88664,47.58854],[7.84412,47.5841],[7.81901,47.58798],[7.79486,47.55691],[7.75261,47.54599],[7.71961,47.54219],[7.69642,47.53297],[7.68101,47.53232],[7.6656,47.53752],[7.66174,47.54554],[7.65083,47.54662],[7.63338,47.56256],[7.67655,47.56435],[7.68904,47.57133],[7.67115,47.5871],[7.68486,47.59601],[7.69385,47.60099],[7.68229,47.59905],[7.67395,47.59212],[7.64599,47.59695],[7.64213,47.5944],[7.64309,47.59151],[7.61929,47.57683],[7.60459,47.57869],[7.60523,47.58519],[7.58945,47.59017],[7.58386,47.57536],[7.56684,47.57785],[7.56548,47.57617],[7.55689,47.57232],[7.55652,47.56779],[7.53634,47.55553],[7.52831,47.55347],[7.51723,47.54578],[7.50873,47.54546],[7.49691,47.53821],[7.50588,47.52856],[7.51904,47.53515],[7.53199,47.5284],[7.5229,47.51644],[7.49804,47.51798],[7.51076,47.49651],[7.47534,47.47932],[7.43356,47.49712],[7.42923,47.48628],[7.4583,47.47216],[7.4462,47.46264],[7.43088,47.45846],[7.40308,47.43638],[7.35603,47.43432],[7.33526,47.44186],[7.24669,47.4205],[7.17026,47.44312],[7.19583,47.49455],[7.16249,47.49025],[7.12781,47.50371],[7.07425,47.48863],[7.0231,47.50522],[6.98425,47.49432],[7.0024,47.45264],[6.93953,47.43388],[6.93744,47.40714],[6.88542,47.37262],[6.87959,47.35335],[7.03125,47.36996],[7.0564,47.35134],[7.05305,47.33304],[6.94316,47.28747],[6.95108,47.26428],[6.9508,47.24338],[6.8489,47.15933],[6.76788,47.1208],[6.68823,47.06616],[6.71531,47.0494],[6.43341,46.92703],[6.46456,46.88865],[6.43216,46.80336],[6.45209,46.77502],[6.38351,46.73171],[6.27135,46.68251],[6.11084,46.57649],[6.1567,46.54402],[6.07269,46.46244],[6.08427,46.44305],[6.06407,46.41676],[6.09926,46.40768],[6.15016,46.3778],[6.15985,46.37721],[6.16987,46.36759],[6.15738,46.3491],[6.13876,46.33844],[6.1198,46.31157],[6.11697,46.29547],[6.1013,46.28512],[6.11926,46.2634],[6.12446,46.25059],[6.10071,46.23772],[6.08563,46.24651],[6.07072,46.24085],[6.0633,46.24583],[6.05029,46.23518],[6.04602,46.23127],[6.03342,46.2383],[6.02461,46.23313],[5.97542,46.21525],[5.96515,46.19638],[5.99573,46.18587],[5.98846,46.17046],[5.98188,46.17392],[5.97508,46.15863],[5.9641,46.14412],[5.95781,46.12925],[5.97893,46.13303],[5.9871,46.14499],[6.01791,46.14228],[6.03614,46.13712],[6.04564,46.14031],[6.05203,46.15191],[6.07491,46.14879],[6.09199,46.15191],[6.09926,46.14373],[6.13397,46.1406],[6.15305,46.15194],[6.18116,46.16187],[6.18871,46.16644],[6.18707,46.17999],[6.19552,46.18401],[6.19807,46.18369],[6.20539,46.19163],[6.21114,46.1927],[6.21273,46.19409],[6.21603,46.19507],[6.21844,46.19837],[6.22222,46.19888],[6.22175,46.20045],[6.23544,46.20714],[6.23913,46.20511],[6.24821,46.20531],[6.26007,46.21165],[6.27694,46.21566],[6.29663,46.22688],[6.31041,46.24417],[6.29474,46.26221],[6.26749,46.24745],[6.24952,46.26255],[6.23775,46.27822],[6.25137,46.29014],[6.24826,46.30175],[6.21981,46.31304],[6.25432,46.3632],[6.53358,46.45431],[6.82312,46.42661],[6.8024,46.39171],[6.77152,46.34784],[6.86052,46.28512],[6.78968,46.14058],[6.89321,46.12548],[6.87868,46.03855],[6.93862,46.06502],[7.00946,45.9944],[7.04151,45.92435],[7.10685,45.85653],[7.56343,45.97421],[7.85949,45.91485],[7.9049,45.99945],[7.98881,45.99867],[8.02906,46.10331],[8.11383,46.11577],[8.16866,46.17817],[8.08814,46.26692],[8.31162,46.38044],[8.30648,46.41587],[8.42464,46.46367],[8.46317,46.43712],[8.45032,46.26869],[8.62242,46.12112],[8.75697,46.10395],[8.80778,46.10085],[8.85617,46.0748],[8.79414,46.00913],[8.78585,45.98973],[8.79362,45.99207],[8.8319,45.9879],[8.85121,45.97239],[8.86688,45.96135],[8.88904,45.95465],[8.93649,45.86775],[8.94372,45.86587],[8.93504,45.86245],[8.91129,45.8388],[8.94737,45.84285],[8.9621,45.83707],[8.99663,45.83466],[9.00324,45.82055],[9.0298,45.82127],[9.03279,45.82865],[9.03793,45.83548],[9.03505,45.83976],[9.04059,45.8464],[9.04546,45.84968],[9.06642,45.8761],[9.09065,45.89906],[8.99257,45.9698],[9.01618,46.04928],[9.24503,46.23616],[9.29226,46.32717],[9.25502,46.43743],[9.28136,46.49685],[9.36128,46.5081],[9.40487,46.46621],[9.45936,46.50873],[9.46117,46.37481],[9.57015,46.2958],[9.71273,46.29266],[9.73086,46.35071],[9.95249,46.38045],[10.07055,46.21668],[10.14439,46.22992],[10.17862,46.25626],[10.10506,46.3372],[10.165,46.41051],[10.03715,46.44479],[10.10307,46.61003],[10.23674,46.63484],[10.25309,46.57432],[10.46136,46.53164],[10.49375,46.62049],[10.44686,46.64162],[10.40475,46.63671],[10.38659,46.67847],[10.47197,46.85698],[10.48376,46.93891],[10.36933,47.00212],[10.30031,46.92093],[10.24128,46.93147],[10.22675,46.86942],[10.10715,46.84296],[9.98058,46.91434],[9.88266,46.93343],[9.87935,47.01337],[9.60717,47.06091],[9.55721,47.04762],[9.54041,47.06495],[9.47548,47.05257],[9.47139,47.06402],[9.51362,47.08505],[9.52089,47.10019],[9.51044,47.13727],[9.48774,47.17402],[9.4891,47.19346],[9.50318,47.22153],[9.52406,47.24959],[9.53116,47.27029],[9.54773,47.2809],[9.55857,47.29919],[9.58513,47.31334],[9.59978,47.34671],[9.62476,47.36639],[9.65427,47.36824],[9.66243,47.37136],[9.6711,47.37824],[9.67445,47.38429],[9.67334,47.39191],[9.6629,47.39591],[9.65136,47.40504],[9.65043,47.41937],[9.6446,47.43233],[9.64483,47.43842],[9.65863,47.44847],[9.65728,47.45383],[9.6423,47.45599],[9.62475,47.45685],[9.62158,47.45858],[9.60841,47.47178],[9.60484,47.46358],[9.60205,47.46165],[9.59482,47.46305],[9.58208,47.48344],[9.56312,47.49495],[9.55125,47.53629],[9.25619,47.65939],[9.18203,47.65598],[9.17593,47.65399],[9.1755,47.65584],[9.1705,47.65513],[9.15181,47.66904],[9.13845,47.66389],[9.09891,47.67801],[9.02093,47.6868],[8.94093,47.65596],[8.89946,47.64769],[8.87625,47.65441],[8.87383,47.67045],[8.85065,47.68209],[8.86989,47.70504],[8.82002,47.71458],[8.80663,47.73821],[8.77309,47.72059],[8.76965,47.7075],[8.79966,47.70222],[8.79511,47.67462],[8.75856,47.68969],[8.72809,47.69282]],[[8.95861,45.96485],[8.96668,45.98436],[8.97741,45.98317],[8.97604,45.96151],[8.95861,45.96485]],[[8.70847,47.68904],[8.68985,47.69552],[8.66837,47.68437],[8.65769,47.68928],[8.67508,47.6979],[8.66416,47.71367],[8.70237,47.71453],[8.71773,47.69088],[8.70847,47.68904]]]]}},{type:"Feature",properties:{iso1A2:"CI",iso1A3:"CIV",iso1N3:"384",wikidata:"Q1008",nameEn:"Côte d'Ivoire",groups:["011","202","002"],callingCodes:["225"]},geometry:{type:"MultiPolygon",coordinates:[[[[-7.52774,3.7105],[-3.34019,4.17519],[-3.10675,5.08515],[-3.11073,5.12675],[-3.063,5.13665],[-2.96554,5.10397],[-2.95261,5.12477],[-2.75502,5.10657],[-2.73074,5.1364],[-2.77625,5.34621],[-2.72737,5.34789],[-2.76614,5.60963],[-2.85378,5.65156],[-2.93132,5.62137],[-2.96671,5.6415],[-2.95323,5.71865],[-3.01896,5.71697],[-3.25999,6.62521],[-3.21954,6.74407],[-3.23327,6.81744],[-2.95438,7.23737],[-2.97822,7.27165],[-2.92339,7.60847],[-2.79467,7.86002],[-2.78395,7.94974],[-2.74819,7.92613],[-2.67787,8.02055],[-2.61232,8.02645],[-2.62901,8.11495],[-2.49037,8.20872],[-2.58243,8.7789],[-2.66357,9.01771],[-2.77799,9.04949],[-2.69814,9.22717],[-2.68802,9.49343],[-2.76494,9.40778],[-2.93012,9.57403],[-3.00765,9.74019],[-3.16609,9.85147],[-3.19306,9.93781],[-3.27228,9.84981],[-3.31779,9.91125],[-3.69703,9.94279],[-4.25999,9.76012],[-4.31392,9.60062],[-4.6426,9.70696],[-4.96621,9.89132],[-4.96453,9.99923],[-5.12465,10.29788],[-5.39602,10.2929],[-5.51058,10.43177],[-5.65135,10.46767],[-5.78124,10.43952],[-5.99478,10.19694],[-6.18851,10.24244],[-6.1731,10.46983],[-6.24795,10.74248],[-6.325,10.68624],[-6.40646,10.69922],[-6.42847,10.5694],[-6.52974,10.59104],[-6.63541,10.66893],[-6.68164,10.35074],[-6.93921,10.35291],[-7.01186,10.25111],[-6.97444,10.21644],[-7.00966,10.15794],[-7.0603,10.14711],[-7.13331,10.24877],[-7.3707,10.24677],[-7.44555,10.44602],[-7.52261,10.4655],[-7.54462,10.40921],[-7.63048,10.46334],[-7.92107,10.15577],[-7.97971,10.17117],[-8.01225,10.1021],[-8.11921,10.04577],[-8.15652,9.94288],[-8.09434,9.86936],[-8.14657,9.55062],[-8.03463,9.39604],[-7.85056,9.41812],[-7.90777,9.20456],[-7.73862,9.08422],[-7.92518,8.99332],[-7.95503,8.81146],[-7.69882,8.66148],[-7.65653,8.36873],[-7.92518,8.50652],[-8.22991,8.48438],[-8.2411,8.24196],[-8.062,8.16071],[-7.98675,8.20134],[-7.99919,8.11023],[-7.94695,8.00925],[-8.06449,8.04989],[-8.13414,7.87991],[-8.09931,7.78626],[-8.21374,7.54466],[-8.4003,7.6285],[-8.47114,7.55676],[-8.41935,7.51203],[-8.37458,7.25794],[-8.29249,7.1691],[-8.31736,6.82837],[-8.59456,6.50612],[-8.48652,6.43797],[-8.45666,6.49977],[-8.38453,6.35887],[-8.3298,6.36381],[-8.17557,6.28222],[-8.00642,6.31684],[-7.90692,6.27728],[-7.83478,6.20309],[-7.8497,6.08932],[-7.79747,6.07696],[-7.78254,5.99037],[-7.70294,5.90625],[-7.67309,5.94337],[-7.48155,5.80974],[-7.46165,5.84934],[-7.43677,5.84687],[-7.43926,5.74787],[-7.37209,5.61173],[-7.43428,5.42355],[-7.36463,5.32944],[-7.46165,5.26256],[-7.48901,5.14118],[-7.55369,5.08667],[-7.53876,4.94294],[-7.59349,4.8909],[-7.53259,4.35145],[-7.52774,3.7105]]]]}},{type:"Feature",properties:{iso1A2:"CK",iso1A3:"COK",iso1N3:"184",wikidata:"Q26988",nameEn:"Cook Islands",country:"NZ",groups:["061","009"],driveSide:"left",callingCodes:["682"]},geometry:{type:"MultiPolygon",coordinates:[[[[-167.73854,-14.92809],[-167.73129,-23.22266],[-156.46451,-23.21255],[-156.4957,-12.32002],[-156.50903,-7.4975],[-167.75329,-7.52784],[-167.75195,-10.12005],[-167.73854,-14.92809]]]]}},{type:"Feature",properties:{iso1A2:"CL",iso1A3:"CHL",iso1N3:"152",wikidata:"Q298",nameEn:"Chile",groups:["005","419","019"],callingCodes:["56"]},geometry:{type:"MultiPolygon",coordinates:[[[[-68.60702,-52.65781],[-68.41683,-52.33516],[-69.97824,-52.00845],[-71.99889,-51.98018],[-72.33873,-51.59954],[-72.31343,-50.58411],[-73.15765,-50.78337],[-73.55259,-49.92488],[-73.45156,-49.79461],[-73.09655,-49.14342],[-72.56894,-48.81116],[-72.54042,-48.52392],[-72.27662,-48.28727],[-72.50478,-47.80586],[-71.94152,-47.13595],[-71.68577,-46.55385],[-71.75614,-45.61611],[-71.35687,-45.22075],[-72.06985,-44.81756],[-71.26418,-44.75684],[-71.16436,-44.46244],[-71.81318,-44.38097],[-71.64206,-43.64774],[-72.14828,-42.85321],[-72.15541,-42.15941],[-71.74901,-42.11711],[-71.92726,-40.72714],[-71.37826,-38.91474],[-70.89532,-38.6923],[-71.24279,-37.20264],[-70.95047,-36.4321],[-70.38008,-36.02375],[-70.49416,-35.24145],[-69.87386,-34.13344],[-69.88099,-33.34489],[-70.55832,-31.51559],[-70.14479,-30.36595],[-69.8596,-30.26131],[-69.99507,-29.28351],[-69.80969,-29.07185],[-69.66709,-28.44055],[-69.22504,-27.95042],[-68.77586,-27.16029],[-68.43363,-27.08414],[-68.27677,-26.90626],[-68.59048,-26.49861],[-68.56909,-26.28146],[-68.38372,-26.15353],[-68.57622,-25.32505],[-68.38372,-25.08636],[-68.56909,-24.69831],[-68.24825,-24.42596],[-67.33563,-24.04237],[-66.99632,-22.99839],[-67.18382,-22.81525],[-67.54284,-22.89771],[-67.85114,-22.87076],[-68.18816,-21.28614],[-68.40403,-20.94562],[-68.53957,-20.91542],[-68.55383,-20.7355],[-68.44023,-20.62701],[-68.7276,-20.46178],[-68.74273,-20.08817],[-68.57132,-20.03134],[-68.54611,-19.84651],[-68.66761,-19.72118],[-68.41218,-19.40499],[-68.61989,-19.27584],[-68.80602,-19.08355],[-68.87082,-19.06003],[-68.94987,-18.93302],[-69.07432,-18.28259],[-69.14807,-18.16893],[-69.07496,-18.03715],[-69.28671,-17.94844],[-69.34126,-17.72753],[-69.46623,-17.60518],[-69.46897,-17.4988],[-69.66483,-17.65083],[-69.79087,-17.65563],[-69.82868,-17.72048],[-69.75305,-17.94605],[-69.81607,-18.12582],[-69.96732,-18.25992],[-70.16394,-18.31737],[-70.31267,-18.31258],[-70.378,-18.3495],[-70.59118,-18.35072],[-113.52687,-26.52828],[-68.11646,-58.14883],[-66.07313,-55.19618],[-67.11046,-54.94199],[-67.46182,-54.92205],[-68.01394,-54.8753],[-68.60733,-54.9125],[-68.60702,-52.65781]]]]}},{type:"Feature",properties:{iso1A2:"CM",iso1A3:"CMR",iso1N3:"120",wikidata:"Q1009",nameEn:"Cameroon",groups:["017","202","002"],callingCodes:["237"]},geometry:{type:"MultiPolygon",coordinates:[[[[14.83314,12.62963],[14.55058,12.78256],[14.56101,12.91036],[14.46881,13.08259],[14.08251,13.0797],[14.20204,12.53405],[14.17523,12.41916],[14.22215,12.36533],[14.4843,12.35223],[14.6474,12.17466],[14.61612,11.7798],[14.55207,11.72001],[14.64591,11.66166],[14.6124,11.51283],[14.17821,11.23831],[13.97489,11.30258],[13.78945,11.00154],[13.7403,11.00593],[13.70753,10.94451],[13.73434,10.9255],[13.54964,10.61236],[13.5705,10.53183],[13.43644,10.13326],[13.34111,10.12299],[13.25025,10.03647],[13.25323,10.00127],[13.286,9.9822],[13.27409,9.93232],[13.24132,9.91031],[13.25025,9.86042],[13.29941,9.8296],[13.25472,9.76795],[13.22642,9.57266],[13.02385,9.49334],[12.85628,9.36698],[12.91958,9.33905],[12.90022,9.11411],[12.81085,8.91992],[12.79,8.75361],[12.71701,8.7595],[12.68722,8.65938],[12.44146,8.6152],[12.4489,8.52536],[12.26123,8.43696],[12.24782,8.17904],[12.19271,8.10826],[12.20909,7.97553],[11.99908,7.67302],[12.01844,7.52981],[11.93205,7.47812],[11.84864,7.26098],[11.87396,7.09398],[11.63117,6.9905],[11.55818,6.86186],[11.57755,6.74059],[11.51499,6.60892],[11.42264,6.5882],[11.42041,6.53789],[11.09495,6.51717],[11.09644,6.68437],[10.94302,6.69325],[10.8179,6.83377],[10.83727,6.9358],[10.60789,7.06885],[10.59746,7.14719],[10.57214,7.16345],[10.53639,6.93432],[10.21466,6.88996],[10.15135,7.03781],[9.86314,6.77756],[9.77824,6.79088],[9.70674,6.51717],[9.51757,6.43874],[8.84209,5.82562],[8.88156,5.78857],[8.83687,5.68483],[8.92029,5.58403],[8.78027,5.1243],[8.60302,4.87353],[8.34397,4.30689],[9.22018,3.72052],[9.81162,2.33797],[9.82123,2.35097],[9.83754,2.32428],[9.83238,2.29079],[9.84716,2.24676],[9.89012,2.20457],[9.90749,2.20049],[9.991,2.16561],[11.3561,2.17217],[11.37116,2.29975],[13.28534,2.25716],[13.29457,2.16106],[14.61145,2.17866],[15.00996,1.98887],[15.22634,2.03243],[15.34776,1.91264],[15.48942,1.98265],[16.02959,1.76483],[16.02647,1.65591],[16.14634,1.70259],[16.05294,1.9811],[16.08563,2.19733],[16.15568,2.18955],[16.19357,2.21537],[16.08252,2.45708],[16.05449,3.02306],[15.77725,3.26835],[15.73522,3.24348],[15.07686,4.01805],[15.17482,4.05131],[15.10644,4.1362],[15.08609,4.30282],[15.00825,4.41458],[14.73383,4.6135],[14.65489,5.21343],[14.57083,5.23979],[14.52724,5.28319],[14.62531,5.51411],[14.58951,5.59777],[14.62375,5.70466],[14.60974,5.91838],[14.49455,5.91683],[14.42917,6.00508],[14.43073,6.08867],[14.56149,6.18928],[14.74206,6.26356],[14.80122,6.34866],[14.79966,6.39043],[14.96311,6.75693],[15.04717,6.77085],[15.23397,7.25135],[15.49743,7.52179],[15.56964,7.58936],[15.59272,7.7696],[15.50743,7.79302],[15.20426,8.50892],[15.09484,8.65982],[14.83566,8.80557],[14.35707,9.19611],[14.37094,9.2954],[13.97544,9.6365],[14.01793,9.73169],[14.1317,9.82413],[14.20411,10.00055],[14.4673,10.00264],[14.80082,9.93818],[14.95722,9.97926],[15.05999,9.94845],[15.14043,9.99246],[15.24618,9.99246],[15.41408,9.92876],[15.68761,9.99344],[15.50535,10.1098],[15.30874,10.31063],[15.23724,10.47764],[15.14936,10.53915],[15.15532,10.62846],[15.06737,10.80921],[15.09127,10.87431],[15.04957,11.02347],[15.10021,11.04101],[15.0585,11.40481],[15.13149,11.5537],[15.06595,11.71126],[15.11579,11.79313],[15.04808,11.8731],[15.05786,12.0608],[15.0349,12.10698],[15.00146,12.1223],[14.96952,12.0925],[14.89019,12.16593],[14.90827,12.3269],[14.83314,12.62963]]]]}},{type:"Feature",properties:{iso1A2:"CN",iso1A3:"CHN",iso1N3:"156",wikidata:"Q148",nameEn:"China",aliases:["RC"],groups:["030","142"],callingCodes:["86"]},geometry:{type:"MultiPolygon",coordinates:[[[[125.6131,53.07229],[125.17522,53.20225],[124.46078,53.21881],[123.86158,53.49391],[123.26989,53.54843],[122.85966,53.47395],[122.35063,53.49565],[121.39213,53.31888],[120.85633,53.28499],[120.0451,52.7359],[120.04049,52.58773],[120.46454,52.63811],[120.71673,52.54099],[120.61346,52.32447],[120.77337,52.20805],[120.65907,51.93544],[120.10963,51.671],[119.13553,50.37412],[119.38598,50.35162],[119.27996,50.13348],[119.11003,50.00276],[118.61623,49.93809],[117.82343,49.52696],[117.48208,49.62324],[117.27597,49.62544],[117.07142,49.68482],[116.71193,49.83813],[116.03781,48.87014],[116.06565,48.81716],[115.78876,48.51781],[115.811,48.25699],[115.52082,48.15367],[115.57128,47.91988],[115.94296,47.67741],[116.08431,47.80693],[116.2527,47.87766],[116.4465,47.83662],[116.67405,47.89039],[116.87527,47.88836],[117.08918,47.82242],[117.37875,47.63627],[117.50181,47.77216],[117.80196,48.01661],[118.03676,48.00982],[118.11009,48.04],[118.22677,48.03853],[118.29654,48.00246],[118.55766,47.99277],[118.7564,47.76947],[119.12343,47.66458],[119.13995,47.53997],[119.35892,47.48104],[119.31964,47.42617],[119.54918,47.29505],[119.56019,47.24874],[119.62403,47.24575],[119.71209,47.19192],[119.85518,46.92196],[119.91242,46.90091],[119.89261,46.66423],[119.80455,46.67631],[119.77373,46.62947],[119.68127,46.59015],[119.65265,46.62342],[119.42827,46.63783],[119.37306,46.61132],[119.30261,46.6083],[119.24978,46.64761],[119.10448,46.65516],[119.00541,46.74273],[118.92616,46.72765],[118.89974,46.77139],[118.8337,46.77742],[118.78747,46.68689],[118.30534,46.73519],[117.69554,46.50991],[117.60748,46.59771],[117.41782,46.57862],[117.36609,46.36335],[117.07252,46.35818],[116.83166,46.38637],[116.75551,46.33083],[116.58612,46.30211],[116.26678,45.96479],[116.24012,45.8778],[116.27366,45.78637],[116.16989,45.68603],[115.91898,45.6227],[115.69688,45.45761],[115.35757,45.39106],[114.94546,45.37377],[114.74612,45.43585],[114.54801,45.38337],[114.5166,45.27189],[114.08071,44.92847],[113.909,44.91444],[113.63821,44.74326],[112.74662,44.86297],[112.4164,45.06858],[111.98695,45.09074],[111.76275,44.98032],[111.40498,44.3461],[111.96289,43.81596],[111.93776,43.68709],[111.79758,43.6637],[111.59087,43.51207],[111.0149,43.3289],[110.4327,42.78293],[110.08401,42.6411],[109.89402,42.63111],[109.452,42.44842],[109.00679,42.45302],[108.84489,42.40246],[108.23156,42.45532],[107.57258,42.40898],[107.49681,42.46221],[107.29755,42.41395],[107.24774,42.36107],[106.76517,42.28741],[105.24708,41.7442],[105.01119,41.58382],[104.91272,41.64619],[104.51667,41.66113],[104.52258,41.8706],[103.92804,41.78246],[103.3685,41.89696],[102.72403,42.14675],[102.42826,42.15137],[102.07645,42.22519],[101.80515,42.50074],[101.28833,42.58524],[100.84979,42.67087],[100.33297,42.68231],[99.50671,42.56535],[97.1777,42.7964],[96.37926,42.72055],[96.35658,42.90363],[95.89543,43.2528],[95.52594,43.99353],[95.32891,44.02407],[95.39772,44.2805],[95.01191,44.25274],[94.71959,44.35284],[94.10003,44.71016],[93.51161,44.95964],[91.64048,45.07408],[90.89169,45.19667],[90.65114,45.49314],[90.70907,45.73437],[91.03026,46.04194],[90.99672,46.14207],[90.89639,46.30711],[91.07696,46.57315],[91.0147,46.58171],[91.03649,46.72916],[90.84035,46.99525],[90.76108,46.99399],[90.48542,47.30438],[90.48854,47.41826],[90.33598,47.68303],[90.10871,47.7375],[90.06512,47.88177],[89.76624,47.82745],[89.55453,48.0423],[89.0711,47.98528],[88.93186,48.10263],[88.8011,48.11302],[88.58316,48.21893],[88.58939,48.34531],[87.96361,48.58478],[88.0788,48.71436],[87.73822,48.89582],[87.88171,48.95853],[87.81333,49.17354],[87.48983,49.13794],[87.478,49.07403],[87.28386,49.11626],[86.87238,49.12432],[86.73568,48.99918],[86.75343,48.70331],[86.38069,48.46064],[85.73581,48.3939],[85.5169,48.05493],[85.61067,47.49753],[85.69696,47.2898],[85.54294,47.06171],[85.22443,47.04816],[84.93995,46.87399],[84.73077,47.01394],[83.92184,46.98912],[83.04622,47.19053],[82.21792,45.56619],[82.58474,45.40027],[82.51374,45.1755],[81.73278,45.3504],[80.11169,45.03352],[79.8987,44.89957],[80.38384,44.63073],[80.40229,44.23319],[80.40031,44.10986],[80.75156,43.44948],[80.69718,43.32589],[80.77771,43.30065],[80.78817,43.14235],[80.62913,43.141],[80.3735,43.01557],[80.58999,42.9011],[80.38169,42.83142],[80.26886,42.8366],[80.16892,42.61137],[80.26841,42.23797],[80.17807,42.21166],[80.17842,42.03211],[79.92977,42.04113],[78.3732,41.39603],[78.15757,41.38565],[78.12873,41.23091],[77.81287,41.14307],[77.76206,41.01574],[77.52723,41.00227],[77.3693,41.0375],[77.28004,41.0033],[76.99302,41.0696],[76.75681,40.95354],[76.5261,40.46114],[76.33659,40.3482],[75.96168,40.38064],[75.91361,40.2948],[75.69663,40.28642],[75.5854,40.66874],[75.22834,40.45382],[75.08243,40.43945],[74.82013,40.52197],[74.78168,40.44886],[74.85996,40.32857],[74.69875,40.34668],[74.35063,40.09742],[74.25533,40.13191],[73.97049,40.04378],[73.83006,39.76136],[73.9051,39.75073],[73.92354,39.69565],[73.94683,39.60733],[73.87018,39.47879],[73.59831,39.46425],[73.59241,39.40843],[73.5004,39.38402],[73.55396,39.3543],[73.54572,39.27567],[73.60638,39.24534],[73.75823,39.023],[73.81728,39.04007],[73.82964,38.91517],[73.7445,38.93867],[73.7033,38.84782],[73.80656,38.66449],[73.79806,38.61106],[73.97933,38.52945],[74.17022,38.65504],[74.51217,38.47034],[74.69619,38.42947],[74.69894,38.22155],[74.80331,38.19889],[74.82665,38.07359],[74.9063,38.03033],[74.92416,37.83428],[75.00935,37.77486],[74.8912,37.67576],[74.94338,37.55501],[75.06011,37.52779],[75.15899,37.41443],[75.09719,37.37297],[75.12328,37.31839],[74.88887,37.23275],[74.80605,37.21565],[74.49981,37.24518],[74.56453,37.03023],[75.13839,37.02622],[75.40481,36.95382],[75.45562,36.71971],[75.72737,36.7529],[75.92391,36.56986],[76.0324,36.41198],[76.00906,36.17511],[75.93028,36.13136],[76.15325,35.9264],[76.14913,35.82848],[76.33453,35.84296],[76.50961,35.8908],[76.77323,35.66062],[76.84539,35.67356],[76.96624,35.5932],[77.44277,35.46132],[77.70232,35.46244],[77.80532,35.52058],[78.11664,35.48022],[78.03466,35.3785],[78.00033,35.23954],[78.22692,34.88771],[78.18435,34.7998],[78.27781,34.61484],[78.54964,34.57283],[78.56475,34.50835],[78.74465,34.45174],[79.05364,34.32482],[78.99802,34.3027],[78.91769,34.15452],[78.66225,34.08858],[78.65657,34.03195],[78.73367,34.01121],[78.77349,33.73871],[78.67599,33.66445],[78.73636,33.56521],[79.15252,33.17156],[79.14016,33.02545],[79.46562,32.69668],[79.26768,32.53277],[79.13174,32.47766],[79.0979,32.38051],[78.99322,32.37948],[78.96713,32.33655],[78.7831,32.46873],[78.73916,32.69438],[78.38897,32.53938],[78.4645,32.45367],[78.49609,32.2762],[78.68754,32.10256],[78.74404,32.00384],[78.78036,31.99478],[78.69933,31.78723],[78.84516,31.60631],[78.71032,31.50197],[78.77898,31.31209],[79.01931,31.42817],[79.14016,31.43403],[79.22805,31.34963],[79.59884,30.93943],[79.93255,30.88288],[80.20721,30.58541],[80.54504,30.44936],[80.83343,30.32023],[81.03953,30.20059],[81.12842,30.01395],[81.24362,30.0126],[81.29032,30.08806],[81.2623,30.14596],[81.33355,30.15303],[81.39928,30.21862],[81.41018,30.42153],[81.62033,30.44703],[81.99082,30.33423],[82.10135,30.35439],[82.10757,30.23745],[82.19475,30.16884],[82.16984,30.0692],[82.38622,30.02608],[82.5341,29.9735],[82.73024,29.81695],[83.07116,29.61957],[83.28131,29.56813],[83.44787,29.30513],[83.63156,29.16249],[83.82303,29.30513],[83.97559,29.33091],[84.18107,29.23451],[84.24801,29.02783],[84.2231,28.89571],[84.47528,28.74023],[84.62317,28.73887],[84.85511,28.58041],[85.06059,28.68562],[85.19135,28.62825],[85.18668,28.54076],[85.10729,28.34092],[85.38127,28.28336],[85.4233,28.32996],[85.59765,28.30529],[85.60854,28.25045],[85.69105,28.38475],[85.71907,28.38064],[85.74864,28.23126],[85.84672,28.18187],[85.90743,28.05144],[85.97813,27.99023],[85.94946,27.9401],[86.06309,27.90021],[86.12069,27.93047],[86.08333,28.02121],[86.088,28.09264],[86.18607,28.17364],[86.22966,27.9786],[86.42736,27.91122],[86.51609,27.96623],[86.56265,28.09569],[86.74181,28.10638],[86.75582,28.04182],[87.03757,27.94835],[87.11696,27.84104],[87.56996,27.84517],[87.72718,27.80938],[87.82681,27.95248],[88.13378,27.88015],[88.1278,27.95417],[88.25332,27.9478],[88.54858,28.06057],[88.63235,28.12356],[88.83559,28.01936],[88.88091,27.85192],[88.77517,27.45415],[88.82981,27.38814],[88.91901,27.32483],[88.93678,27.33777],[88.96947,27.30319],[89.00216,27.32532],[88.95355,27.4106],[88.97213,27.51671],[89.0582,27.60985],[89.12825,27.62502],[89.59525,28.16433],[89.79762,28.23979],[90.13387,28.19178],[90.58842,28.02838],[90.69894,28.07784],[91.20019,27.98715],[91.25779,28.07509],[91.46327,28.0064],[91.48973,27.93903],[91.5629,27.84823],[91.6469,27.76358],[91.84722,27.76325],[91.87057,27.7195],[92.27432,27.89077],[92.32101,27.79363],[92.42538,27.80092],[92.7275,27.98662],[92.73025,28.05814],[92.65472,28.07632],[92.67486,28.15018],[92.93075,28.25671],[93.14635,28.37035],[93.18069,28.50319],[93.44621,28.67189],[93.72797,28.68821],[94.35897,29.01965],[94.2752,29.11687],[94.69318,29.31739],[94.81353,29.17804],[95.0978,29.14446],[95.11291,29.09527],[95.2214,29.10727],[95.26122,29.07727],[95.3038,29.13847],[95.41091,29.13007],[95.50842,29.13487],[95.72086,29.20797],[95.75149,29.32063],[95.84899,29.31464],[96.05361,29.38167],[96.31316,29.18643],[96.18682,29.11087],[96.20467,29.02325],[96.3626,29.10607],[96.61391,28.72742],[96.40929,28.51526],[96.48895,28.42955],[96.6455,28.61657],[96.85561,28.4875],[96.88445,28.39452],[96.98882,28.32564],[97.1289,28.3619],[97.34547,28.21385],[97.41729,28.29783],[97.47085,28.2688],[97.50518,28.49716],[97.56835,28.55628],[97.70705,28.5056],[97.79632,28.33168],[97.90069,28.3776],[98.15337,28.12114],[98.13964,27.9478],[98.32641,27.51385],[98.42529,27.55404],[98.43353,27.67086],[98.69582,27.56499],[98.7333,26.85615],[98.77547,26.61994],[98.72741,26.36183],[98.67797,26.24487],[98.7329,26.17218],[98.66884,26.09165],[98.63128,26.15492],[98.57085,26.11547],[98.60763,26.01512],[98.70818,25.86241],[98.63128,25.79937],[98.54064,25.85129],[98.40606,25.61129],[98.31268,25.55307],[98.25774,25.6051],[98.16848,25.62739],[98.18084,25.56298],[98.12591,25.50722],[98.14925,25.41547],[97.92541,25.20815],[97.83614,25.2715],[97.77023,25.11492],[97.72216,25.08508],[97.72903,24.91332],[97.79949,24.85655],[97.76481,24.8289],[97.73127,24.83015],[97.70181,24.84557],[97.64354,24.79171],[97.56648,24.76475],[97.56383,24.75535],[97.5542,24.74943],[97.54675,24.74202],[97.56525,24.72838],[97.56286,24.54535],[97.52757,24.43748],[97.60029,24.4401],[97.66998,24.45288],[97.7098,24.35658],[97.65624,24.33781],[97.66723,24.30027],[97.71941,24.29652],[97.76799,24.26365],[97.72998,24.2302],[97.72799,24.18883],[97.75305,24.16902],[97.72903,24.12606],[97.62363,24.00506],[97.5247,23.94032],[97.64667,23.84574],[97.72302,23.89288],[97.79456,23.94836],[97.79416,23.95663],[97.84328,23.97603],[97.86545,23.97723],[97.88811,23.97446],[97.8955,23.97758],[97.89676,23.97931],[97.89683,23.98389],[97.88814,23.98605],[97.88414,23.99405],[97.88616,24.00463],[97.90998,24.02094],[97.93951,24.01953],[97.98691,24.03897],[97.99583,24.04932],[98.04709,24.07616],[98.05302,24.07408],[98.05671,24.07961],[98.0607,24.07812],[98.06703,24.08028],[98.07806,24.07988],[98.20666,24.11406],[98.54476,24.13119],[98.59256,24.08371],[98.85319,24.13042],[98.87998,24.15624],[98.89632,24.10612],[98.67797,23.9644],[98.68209,23.80492],[98.79607,23.77947],[98.82933,23.72921],[98.81775,23.694],[98.88396,23.59555],[98.80294,23.5345],[98.82877,23.47908],[98.87683,23.48995],[98.92104,23.36946],[98.87573,23.33038],[98.93958,23.31414],[98.92515,23.29535],[98.88597,23.18656],[99.05975,23.16382],[99.04601,23.12215],[99.25741,23.09025],[99.34127,23.13099],[99.52214,23.08218],[99.54218,22.90014],[99.43537,22.94086],[99.45654,22.85726],[99.31243,22.73893],[99.38247,22.57544],[99.37972,22.50188],[99.28771,22.4105],[99.17318,22.18025],[99.19176,22.16983],[99.1552,22.15874],[99.33166,22.09656],[99.47585,22.13345],[99.85351,22.04183],[99.96612,22.05965],[99.99084,21.97053],[99.94003,21.82782],[99.98654,21.71064],[100.04956,21.66843],[100.12679,21.70539],[100.17486,21.65306],[100.10757,21.59945],[100.12542,21.50365],[100.1625,21.48704],[100.18447,21.51898],[100.25863,21.47043],[100.35201,21.53176],[100.42892,21.54325],[100.4811,21.46148],[100.57861,21.45637],[100.72143,21.51898],[100.87265,21.67396],[101.11744,21.77659],[101.15156,21.56129],[101.2124,21.56422],[101.19349,21.41959],[101.26912,21.36482],[101.2229,21.23271],[101.29326,21.17254],[101.54563,21.25668],[101.6068,21.23329],[101.59491,21.18621],[101.60886,21.17947],[101.66977,21.20004],[101.70548,21.14911],[101.7622,21.14813],[101.79266,21.19025],[101.76745,21.21571],[101.83887,21.20983],[101.84412,21.25291],[101.74014,21.30967],[101.74224,21.48276],[101.7727,21.51794],[101.7475,21.5873],[101.80001,21.57461],[101.83257,21.61562],[101.74555,21.72852],[101.7791,21.83019],[101.62566,21.96574],[101.57525,22.13026],[101.60675,22.13513],[101.53638,22.24794],[101.56789,22.28876],[101.61306,22.27515],[101.68973,22.46843],[101.7685,22.50337],[101.86828,22.38397],[101.90714,22.38688],[101.91344,22.44417],[101.98487,22.42766],[102.03633,22.46164],[102.1245,22.43372],[102.14099,22.40092],[102.16621,22.43336],[102.26428,22.41321],[102.25339,22.4607],[102.41061,22.64184],[102.38415,22.67919],[102.42618,22.69212],[102.46665,22.77108],[102.51802,22.77969],[102.57095,22.7036],[102.60675,22.73376],[102.8636,22.60735],[102.9321,22.48659],[103.0722,22.44775],[103.07843,22.50097],[103.17961,22.55705],[103.15782,22.59873],[103.18895,22.64471],[103.28079,22.68063],[103.32282,22.8127],[103.43179,22.75816],[103.43646,22.70648],[103.52675,22.59155],[103.57812,22.65764],[103.56255,22.69499],[103.64506,22.79979],[103.87904,22.56683],[103.93286,22.52703],[103.94513,22.52553],[103.95191,22.5134],[103.96352,22.50584],[103.96783,22.51173],[103.97384,22.50634],[103.99247,22.51958],[104.01088,22.51823],[104.03734,22.72945],[104.11384,22.80363],[104.27084,22.8457],[104.25683,22.76534],[104.35593,22.69353],[104.47225,22.75813],[104.58122,22.85571],[104.60457,22.81841],[104.65283,22.83419],[104.72755,22.81984],[104.77114,22.90017],[104.84942,22.93631],[104.86765,22.95178],[104.8334,23.01484],[104.79478,23.12934],[104.87382,23.12854],[104.87992,23.17141],[104.91435,23.18666],[104.9486,23.17235],[104.96532,23.20463],[104.98712,23.19176],[105.07002,23.26248],[105.11672,23.25247],[105.17276,23.28679],[105.22569,23.27249],[105.32376,23.39684],[105.40782,23.28107],[105.42805,23.30824],[105.49966,23.20669],[105.56037,23.16806],[105.57594,23.075],[105.72382,23.06641],[105.8726,22.92756],[105.90119,22.94168],[105.99568,22.94178],[106.00179,22.99049],[106.19705,22.98475],[106.27022,22.87722],[106.34961,22.86718],[106.49749,22.91164],[106.51306,22.94891],[106.55976,22.92311],[106.60179,22.92884],[106.6516,22.86862],[106.6734,22.89587],[106.71387,22.88296],[106.71128,22.85982],[106.78422,22.81532],[106.81271,22.8226],[106.83685,22.8098],[106.82404,22.7881],[106.76293,22.73491],[106.72321,22.63606],[106.71698,22.58432],[106.65316,22.5757],[106.61269,22.60301],[106.58395,22.474],[106.55665,22.46498],[106.57221,22.37],[106.55976,22.34841],[106.6516,22.33977],[106.69986,22.22309],[106.67495,22.1885],[106.6983,22.15102],[106.70142,22.02409],[106.68274,21.99811],[106.69276,21.96013],[106.72551,21.97923],[106.74345,22.00965],[106.81038,21.97934],[106.9178,21.97357],[106.92714,21.93459],[106.97228,21.92592],[106.99252,21.95191],[107.05634,21.92303],[107.06101,21.88982],[107.00964,21.85948],[107.02615,21.81981],[107.10771,21.79879],[107.20734,21.71493],[107.24625,21.7077],[107.29296,21.74674],[107.35834,21.6672],[107.35989,21.60063],[107.38636,21.59774],[107.41593,21.64839],[107.47197,21.6672],[107.49532,21.62958],[107.49065,21.59774],[107.54047,21.5934],[107.56537,21.61945],[107.66967,21.60787],[107.80355,21.66141],[107.86114,21.65128],[107.90006,21.5905],[107.92652,21.58906],[107.95232,21.5388],[107.96774,21.53601],[107.97074,21.54072],[107.97383,21.53961],[107.97932,21.54503],[108.02926,21.54997],[108.0569,21.53604],[108.10003,21.47338],[108.00365,17.98159],[111.60491,13.57105],[118.41371,24.06775],[118.11703,24.39734],[118.28244,24.51231],[118.35291,24.51645],[118.42453,24.54644],[118.56434,24.49266],[120.49232,25.22863],[121.03532,26.8787],[123.5458,31.01942],[122.29378,31.76513],[122.80525,33.30571],[123.85601,37.49093],[123.90497,38.79949],[124.17532,39.8232],[124.23201,39.9248],[124.35029,39.95639],[124.37089,40.03004],[124.3322,40.05573],[124.38556,40.11047],[124.40719,40.13655],[124.86913,40.45387],[125.71172,40.85223],[125.76869,40.87908],[126.00335,40.92835],[126.242,41.15454],[126.53189,41.35206],[126.60631,41.65565],[126.90729,41.79955],[127.17841,41.59714],[127.29712,41.49473],[127.92943,41.44291],[128.02633,41.42103],[128.03311,41.39232],[128.12967,41.37931],[128.18546,41.41279],[128.20061,41.40895],[128.30716,41.60322],[128.15119,41.74568],[128.04487,42.01769],[128.94007,42.03537],[128.96068,42.06657],[129.15178,42.17224],[129.22285,42.26491],[129.22423,42.3553],[129.28541,42.41574],[129.42882,42.44702],[129.54701,42.37254],[129.60482,42.44461],[129.72541,42.43739],[129.75294,42.59409],[129.77183,42.69435],[129.7835,42.76521],[129.80719,42.79218],[129.83277,42.86746],[129.85261,42.96494],[129.8865,43.00395],[129.95082,43.01051],[129.96409,42.97306],[130.12957,42.98361],[130.09764,42.91425],[130.26095,42.9027],[130.23068,42.80125],[130.2385,42.71127],[130.41826,42.6011],[130.44361,42.54849],[130.50123,42.61636],[130.55143,42.52158],[130.62107,42.58413],[130.56576,42.68925],[130.40213,42.70788],[130.44361,42.76205],[130.66524,42.84753],[131.02438,42.86518],[131.02668,42.91246],[131.135,42.94114],[131.10274,43.04734],[131.20414,43.13654],[131.19031,43.21385],[131.30324,43.39498],[131.29402,43.46695],[131.19492,43.53047],[131.21105,43.82383],[131.26176,43.94011],[131.23583,43.96085],[131.25484,44.03131],[131.30365,44.04262],[131.1108,44.70266],[130.95639,44.85154],[131.48415,44.99513],[131.68466,45.12374],[131.66852,45.2196],[131.76532,45.22609],[131.86903,45.33636],[131.99417,45.2567],[132.83978,45.05916],[132.96373,45.0212],[133.12293,45.1332],[133.09279,45.25693],[133.19419,45.51913],[133.41083,45.57723],[133.48457,45.86203],[133.60442,45.90053],[133.67569,45.9759],[133.72695,46.05576],[133.68047,46.14697],[133.88097,46.25066],[133.91496,46.4274],[133.84104,46.46681],[134.03538,46.75668],[134.20016,47.33458],[134.50898,47.4812],[134.7671,47.72051],[134.55508,47.98651],[134.67098,48.1564],[134.75328,48.36763],[134.49516,48.42884],[132.66989,47.96491],[132.57309,47.71741],[131.90448,47.68011],[131.2635,47.73325],[131.09871,47.6852],[130.95985,47.6957],[130.90915,47.90623],[130.65103,48.10052],[130.84462,48.30942],[130.52147,48.61745],[130.66946,48.88251],[130.43232,48.90844],[130.2355,48.86741],[129.85416,49.11067],[129.67598,49.29596],[129.50685,49.42398],[129.40398,49.44194],[129.35317,49.3481],[129.23232,49.40353],[129.11153,49.36813],[128.72896,49.58676],[127.83476,49.5748],[127.53516,49.84306],[127.49299,50.01251],[127.60515,50.23503],[127.37384,50.28393],[127.36009,50.43787],[127.28765,50.46585],[127.36335,50.58306],[127.28165,50.72075],[127.14586,50.91152],[126.93135,51.0841],[126.90369,51.3238],[126.68349,51.70607],[126.44606,51.98254],[126.558,52.13738],[125.6131,53.07229]],[[113.56865,22.20973],[113.57123,22.20416],[113.60504,22.20464],[113.63011,22.10782],[113.57191,22.07696],[113.54839,22.10909],[113.54942,22.14519],[113.54093,22.15497],[113.52659,22.18271],[113.53552,22.20607],[113.53301,22.21235],[113.53591,22.21369],[113.54093,22.21314],[113.54333,22.21688],[113.5508,22.21672],[113.56865,22.20973]],[[114.50148,22.15017],[113.92195,22.13873],[113.83338,22.1826],[113.81621,22.2163],[113.86771,22.42972],[114.03113,22.5065],[114.05438,22.5026],[114.05729,22.51104],[114.06272,22.51617],[114.07267,22.51855],[114.07817,22.52997],[114.08606,22.53276],[114.09048,22.53716],[114.09692,22.53435],[114.1034,22.5352],[114.11181,22.52878],[114.11656,22.53415],[114.12665,22.54003],[114.13823,22.54319],[114.1482,22.54091],[114.15123,22.55163],[114.1597,22.56041],[114.17247,22.55944],[114.18338,22.55444],[114.20655,22.55706],[114.22185,22.55343],[114.22888,22.5436],[114.25154,22.55977],[114.44998,22.55977],[114.50148,22.15017]]]]}},{type:"Feature",properties:{iso1A2:"CO",iso1A3:"COL",iso1N3:"170",wikidata:"Q739",nameEn:"Colombia",groups:["005","419","019"],callingCodes:["57"]},geometry:{type:"MultiPolygon",coordinates:[[[[-71.19849,12.65801],[-81.58685,18.0025],[-82.06974,14.49418],[-82.56142,11.91792],[-78.79327,9.93766],[-77.58292,9.22278],[-77.32389,8.81247],[-77.45064,8.49991],[-77.17257,7.97422],[-77.57185,7.51147],[-77.72514,7.72348],[-77.72157,7.47612],[-77.81426,7.48319],[-77.89178,7.22681],[-78.06168,7.07793],[-82.12561,4.00341],[-78.87137,1.47457],[-78.42749,1.15389],[-77.85677,0.80197],[-77.7148,0.85003],[-77.68613,0.83029],[-77.66416,0.81604],[-77.67815,0.73863],[-77.49984,0.64476],[-77.52001,0.40782],[-76.89177,0.24736],[-76.4094,0.24015],[-76.41215,0.38228],[-76.23441,0.42294],[-75.82927,0.09578],[-75.25764,-0.11943],[-75.18513,-0.0308],[-74.42701,-0.50218],[-74.26675,-0.97229],[-73.65312,-1.26222],[-72.92587,-2.44514],[-71.75223,-2.15058],[-70.94377,-2.23142],[-70.04609,-2.73906],[-70.71396,-3.7921],[-70.52393,-3.87553],[-70.3374,-3.79505],[-69.94708,-4.2431],[-69.43395,-1.42219],[-69.4215,-1.01853],[-69.59796,-0.75136],[-69.603,-0.51947],[-70.03658,-0.19681],[-70.04162,0.55437],[-69.47696,0.71065],[-69.20976,0.57958],[-69.14422,0.84172],[-69.26017,1.06856],[-69.82987,1.07864],[-69.83491,1.69353],[-69.53746,1.76408],[-69.38621,1.70865],[-68.18128,1.72881],[-68.26699,1.83463],[-68.18632,2.00091],[-67.9292,1.82455],[-67.40488,2.22258],[-67.299,1.87494],[-67.15784,1.80439],[-67.08222,1.17441],[-66.85795,1.22998],[-67.21967,2.35778],[-67.65696,2.81691],[-67.85862,2.79173],[-67.85862,2.86727],[-67.30945,3.38393],[-67.50067,3.75812],[-67.62671,3.74303],[-67.85358,4.53249],[-67.83341,5.31104],[-67.59141,5.5369],[-67.63914,5.64963],[-67.58558,5.84537],[-67.43513,5.98835],[-67.4625,6.20625],[-67.60654,6.2891],[-69.41843,6.1072],[-70.10716,6.96516],[-70.7596,7.09799],[-71.03941,6.98163],[-71.37234,7.01588],[-71.42212,7.03854],[-71.44118,7.02116],[-71.82441,7.04314],[-72.04895,7.03837],[-72.19437,7.37034],[-72.43132,7.40034],[-72.47415,7.48928],[-72.45321,7.57232],[-72.47827,7.65604],[-72.46763,7.79518],[-72.44454,7.86031],[-72.46183,7.90682],[-72.45806,7.91141],[-72.47042,7.92306],[-72.48183,7.92909],[-72.48801,7.94329],[-72.47213,7.96106],[-72.39137,8.03534],[-72.35163,8.01163],[-72.36987,8.19976],[-72.4042,8.36513],[-72.65474,8.61428],[-72.77415,9.10165],[-72.94052,9.10663],[-73.02119,9.27584],[-73.36905,9.16636],[-72.98085,9.85253],[-72.88002,10.44309],[-72.4767,11.1117],[-72.24983,11.14138],[-71.9675,11.65536],[-71.3275,11.85],[-70.92579,11.96275],[-71.19849,12.65801]]]]}},{type:"Feature",properties:{iso1A2:"CP",iso1A3:"CPT",wikidata:"Q161258",nameEn:"Clipperton Island",country:"FR",isoStatus:"excRes"},geometry:{type:"MultiPolygon",coordinates:[[[[-110.36279,9.79626],[-108.755,9.84085],[-109.04145,11.13245],[-110.36279,9.79626]]]]}},{type:"Feature",properties:{iso1A2:"CR",iso1A3:"CRI",iso1N3:"188",wikidata:"Q800",nameEn:"Costa Rica",groups:["013","003","419","019"],callingCodes:["506"]},geometry:{type:"MultiPolygon",coordinates:[[[[-83.68276,11.01562],[-83.66597,10.79916],[-83.90838,10.71161],[-84.68197,11.07568],[-84.92439,10.9497],[-85.60529,11.22607],[-85.71223,11.06868],[-86.14524,11.09059],[-87.41779,5.02401],[-82.94503,7.93865],[-82.89978,8.04083],[-82.89137,8.05755],[-82.88641,8.10219],[-82.9388,8.26634],[-83.05209,8.33394],[-82.93056,8.43465],[-82.8679,8.44042],[-82.8382,8.48117],[-82.83322,8.52464],[-82.83975,8.54755],[-82.82739,8.60153],[-82.8794,8.6981],[-82.92068,8.74832],[-82.91377,8.774],[-82.88253,8.83331],[-82.72126,8.97125],[-82.93516,9.07687],[-82.93516,9.46741],[-82.84871,9.4973],[-82.87919,9.62645],[-82.77206,9.59573],[-82.66667,9.49746],[-82.61345,9.49881],[-82.56507,9.57279],[-82.51044,9.65379],[-83.54024,10.96805],[-83.68276,11.01562]]]]}},{type:"Feature",properties:{iso1A2:"CU",iso1A3:"CUB",iso1N3:"192",wikidata:"Q241",nameEn:"Cuba",groups:["029","003","419","019"],callingCodes:["53"]},geometry:{type:"MultiPolygon",coordinates:[[[[-73.62304,20.6935],[-82.02215,24.23074],[-85.77883,21.92705],[-74.81171,18.82201],[-73.62304,20.6935]]]]}},{type:"Feature",properties:{iso1A2:"CV",iso1A3:"CPV",iso1N3:"132",wikidata:"Q1011",nameEn:"Cape Verde",groups:["011","202","002"],callingCodes:["238"]},geometry:{type:"MultiPolygon",coordinates:[[[[-28.81604,14.57305],[-20.39702,14.12816],[-23.37101,19.134],[-28.81604,14.57305]]]]}},{type:"Feature",properties:{iso1A2:"CW",iso1A3:"CUW",iso1N3:"531",wikidata:"Q25279",nameEn:"Curaçao",country:"NL",groups:["029","003","419","019"],callingCodes:["599"]},geometry:{type:"MultiPolygon",coordinates:[[[[-68.90012,12.62309],[-69.59009,12.46019],[-68.99639,11.79035],[-68.33524,11.78151],[-68.90012,12.62309]]]]}},{type:"Feature",properties:{iso1A2:"CX",iso1A3:"CXR",iso1N3:"162",wikidata:"Q31063",nameEn:"Christmas Island",country:"AU",groups:["053","009"],driveSide:"left",callingCodes:["61"]},geometry:{type:"MultiPolygon",coordinates:[[[[105.66835,-9.31927],[104.67494,-11.2566],[106.66176,-11.14349],[105.66835,-9.31927]]]]}},{type:"Feature",properties:{iso1A2:"CY",iso1A3:"CYP",iso1N3:"196",wikidata:"Q229",nameEn:"Cyprus",groups:["EU","145","142"],driveSide:"left",callingCodes:["357"]},geometry:{type:"MultiPolygon",coordinates:[[[[33.70639,34.99303],[33.71514,35.00294],[33.69731,35.01754],[33.69938,35.03123],[33.67678,35.03866],[33.67742,35.05963],[33.68474,35.06602],[33.69095,35.06237],[33.70861,35.07644],[33.7161,35.07279],[33.70209,35.04882],[33.71482,35.03722],[33.73824,35.05321],[33.76106,35.04253],[33.78581,35.05104],[33.82067,35.07826],[33.84168,35.06823],[33.8541,35.07201],[33.87479,35.08881],[33.87097,35.09389],[33.87622,35.10457],[33.87224,35.12293],[33.88561,35.12449],[33.88943,35.12007],[33.88737,35.11408],[33.89853,35.11377],[33.91789,35.08688],[33.91299,35.07579],[33.90247,35.07686],[33.89485,35.06873],[33.88367,35.07877],[33.85261,35.0574],[33.8355,35.05777],[33.82051,35.0667],[33.8012,35.04786],[33.81524,35.04192],[33.83055,35.02865],[33.82875,35.01685],[33.84045,35.00616],[33.85216,35.00579],[33.85891,35.001],[33.85621,34.98956],[33.83505,34.98108],[33.84811,34.97075],[33.86432,34.97592],[33.90075,34.96623],[33.98684,34.76642],[35.48515,34.70851],[35.51152,36.10954],[32.82353,35.70297],[30.15137,34.08517],[32.74412,34.43926],[32.75515,34.64985],[32.76136,34.68318],[32.79433,34.67883],[32.82717,34.70622],[32.86014,34.70585],[32.86167,34.68734],[32.9068,34.66102],[32.91398,34.67343],[32.93043,34.67091],[32.92807,34.66736],[32.93449,34.66241],[32.93693,34.67027],[32.94379,34.67111],[32.94683,34.67907],[32.95539,34.68471],[32.99135,34.68061],[32.98668,34.67268],[32.99014,34.65518],[32.97736,34.65277],[32.97079,34.66112],[32.95325,34.66462],[32.94796,34.6587],[32.94976,34.65204],[32.95471,34.64528],[32.95323,34.64075],[32.95891,34.62919],[32.96718,34.63446],[32.96968,34.64046],[33.0138,34.64424],[33.26744,34.49942],[33.83531,34.73974],[33.70575,34.97947],[33.70639,34.99303]]],[[[33.74144,35.01053],[33.7492,35.01319],[33.74983,35.02274],[33.74265,35.02329],[33.73781,35.02181],[33.7343,35.01178],[33.74144,35.01053]]],[[[33.77312,34.9976],[33.75994,35.00113],[33.75682,34.99916],[33.76605,34.99543],[33.76738,34.99188],[33.7778,34.98981],[33.77843,34.988],[33.78149,34.98854],[33.78318,34.98699],[33.78571,34.98951],[33.78917,34.98854],[33.79191,34.98914],[33.78516,34.99582],[33.77553,34.99518],[33.77312,34.9976]]]]}},{type:"Feature",properties:{iso1A2:"CZ",iso1A3:"CZE",iso1N3:"203",wikidata:"Q213",nameEn:"Czechia",groups:["EU","151","150"],callingCodes:["420"]},geometry:{type:"MultiPolygon",coordinates:[[[[14.82803,50.86966],[14.79139,50.81438],[14.70661,50.84096],[14.61993,50.86049],[14.63434,50.8883],[14.65259,50.90513],[14.64802,50.93241],[14.58024,50.91443],[14.56374,50.922],[14.59702,50.96148],[14.59908,50.98685],[14.58215,50.99306],[14.56432,51.01008],[14.53438,51.00374],[14.53321,51.01679],[14.49873,51.02242],[14.50809,51.0427],[14.49991,51.04692],[14.49154,51.04382],[14.49202,51.02286],[14.45827,51.03712],[14.41335,51.02086],[14.30098,51.05515],[14.25665,50.98935],[14.28776,50.97718],[14.32353,50.98556],[14.32793,50.97379],[14.30251,50.96606],[14.31422,50.95243],[14.39848,50.93866],[14.38691,50.89907],[14.30098,50.88448],[14.27123,50.89386],[14.24314,50.88761],[14.22331,50.86049],[14.02982,50.80662],[13.98864,50.8177],[13.89113,50.78533],[13.89444,50.74142],[13.82942,50.7251],[13.76316,50.73487],[13.70204,50.71771],[13.65977,50.73096],[13.52474,50.70394],[13.53748,50.67654],[13.5226,50.64721],[13.49742,50.63133],[13.46413,50.60102],[13.42189,50.61243],[13.37485,50.64931],[13.37805,50.627],[13.32264,50.60317],[13.32594,50.58009],[13.29454,50.57904],[13.25158,50.59268],[13.19043,50.50237],[13.13424,50.51709],[13.08301,50.50132],[13.0312,50.50944],[13.02038,50.4734],[13.02147,50.44763],[12.98433,50.42016],[12.94058,50.40944],[12.82465,50.45738],[12.73476,50.43237],[12.73044,50.42268],[12.70731,50.39948],[12.67261,50.41949],[12.51356,50.39694],[12.48747,50.37278],[12.49214,50.35228],[12.48256,50.34784],[12.46643,50.35527],[12.43722,50.33774],[12.43371,50.32506],[12.39924,50.32302],[12.40158,50.29521],[12.36594,50.28289],[12.35425,50.23993],[12.33263,50.24367],[12.32445,50.20442],[12.33847,50.19432],[12.32596,50.17146],[12.29232,50.17524],[12.28063,50.19544],[12.28755,50.22429],[12.23943,50.24594],[12.24791,50.25525],[12.26953,50.25189],[12.25119,50.27079],[12.20823,50.2729],[12.18013,50.32146],[12.10907,50.32041],[12.13716,50.27396],[12.09287,50.25032],[12.19335,50.19997],[12.21484,50.16399],[12.1917,50.13434],[12.2073,50.10315],[12.23709,50.10213],[12.27433,50.0771],[12.26111,50.06331],[12.30798,50.05719],[12.49908,49.97305],[12.47264,49.94222],[12.55197,49.92094],[12.48256,49.83575],[12.46603,49.78882],[12.40489,49.76321],[12.4462,49.70233],[12.52553,49.68415],[12.53544,49.61888],[12.56188,49.6146],[12.60155,49.52887],[12.64782,49.52565],[12.64121,49.47628],[12.669,49.42935],[12.71227,49.42363],[12.75854,49.3989],[12.78168,49.34618],[12.88414,49.33541],[12.88249,49.35479],[12.94859,49.34079],[13.03618,49.30417],[13.02957,49.27399],[13.05883,49.26259],[13.17665,49.16713],[13.17019,49.14339],[13.20405,49.12303],[13.23689,49.11412],[13.28242,49.1228],[13.39479,49.04812],[13.40802,48.98851],[13.50221,48.93752],[13.50552,48.97441],[13.58319,48.96899],[13.61624,48.9462],[13.67739,48.87886],[13.73854,48.88538],[13.76994,48.83537],[13.78977,48.83319],[13.8096,48.77877],[13.84023,48.76988],[14.06151,48.66873],[14.01482,48.63788],[14.09104,48.5943],[14.20691,48.5898],[14.33909,48.55852],[14.43076,48.58855],[14.4587,48.64695],[14.56139,48.60429],[14.60808,48.62881],[14.66762,48.58215],[14.71794,48.59794],[14.72756,48.69502],[14.80584,48.73489],[14.80821,48.77711],[14.81545,48.7874],[14.94773,48.76268],[14.95641,48.75915],[14.9758,48.76857],[14.98112,48.77524],[14.9782,48.7766],[14.98032,48.77959],[14.95072,48.79101],[14.98917,48.90082],[14.97612,48.96983],[14.99878,49.01444],[15.15534,48.99056],[15.16358,48.94278],[15.26177,48.95766],[15.28305,48.98831],[15.34823,48.98444],[15.48027,48.94481],[15.51357,48.91549],[15.61622,48.89541],[15.6921,48.85973],[15.75341,48.8516],[15.78087,48.87644],[15.84404,48.86921],[16.06034,48.75436],[16.37345,48.729],[16.40915,48.74576],[16.46134,48.80865],[16.67008,48.77699],[16.68518,48.7281],[16.71883,48.73806],[16.79779,48.70998],[16.90354,48.71541],[16.93955,48.60371],[17.00215,48.70887],[17.11202,48.82925],[17.19355,48.87602],[17.29054,48.85546],[17.3853,48.80936],[17.45671,48.85004],[17.5295,48.81117],[17.7094,48.86721],[17.73126,48.87885],[17.77944,48.92318],[17.87831,48.92679],[17.91814,49.01784],[18.06885,49.03157],[18.1104,49.08624],[18.15022,49.24518],[18.18456,49.28909],[18.36446,49.3267],[18.4139,49.36517],[18.4084,49.40003],[18.44686,49.39467],[18.54848,49.47059],[18.53063,49.49022],[18.57183,49.51162],[18.6144,49.49824],[18.67757,49.50895],[18.74761,49.492],[18.84521,49.51672],[18.84786,49.5446],[18.80479,49.6815],[18.72838,49.68163],[18.69817,49.70473],[18.62676,49.71983],[18.62943,49.74603],[18.62645,49.75002],[18.61368,49.75426],[18.61278,49.7618],[18.57183,49.83334],[18.60341,49.86256],[18.57045,49.87849],[18.57697,49.91565],[18.54299,49.92537],[18.54495,49.9079],[18.53423,49.89906],[18.41604,49.93498],[18.33562,49.94747],[18.33278,49.92415],[18.31914,49.91565],[18.27794,49.93863],[18.27107,49.96779],[18.21752,49.97309],[18.20241,49.99958],[18.10628,50.00223],[18.07898,50.04535],[18.03212,50.06574],[18.00396,50.04954],[18.04585,50.03311],[18.04585,50.01194],[18.00191,50.01723],[17.86886,49.97452],[17.77669,50.02253],[17.7506,50.07896],[17.6888,50.12037],[17.66683,50.10275],[17.59404,50.16437],[17.70528,50.18812],[17.76296,50.23382],[17.72176,50.25665],[17.74648,50.29966],[17.69292,50.32859],[17.67764,50.28977],[17.58889,50.27837],[17.3702,50.28123],[17.34548,50.2628],[17.34273,50.32947],[17.27681,50.32246],[17.19991,50.3654],[17.19579,50.38817],[17.14498,50.38117],[17.1224,50.39494],[16.89229,50.45117],[16.85933,50.41093],[16.90877,50.38642],[16.94448,50.31281],[16.99803,50.30316],[17.02138,50.27772],[16.99803,50.25753],[17.02825,50.23118],[17.00353,50.21449],[16.98018,50.24172],[16.8456,50.20834],[16.7014,50.09659],[16.63137,50.1142],[16.55446,50.16613],[16.56407,50.21009],[16.42674,50.32509],[16.39379,50.3207],[16.3622,50.34875],[16.36495,50.37679],[16.30289,50.38292],[16.28118,50.36891],[16.22821,50.41054],[16.21585,50.40627],[16.19526,50.43291],[16.31413,50.50274],[16.34572,50.49575],[16.44597,50.58041],[16.33611,50.66579],[16.23174,50.67101],[16.20839,50.63096],[16.10265,50.66405],[16.02437,50.60046],[15.98317,50.61528],[16.0175,50.63009],[15.97219,50.69799],[15.87331,50.67188],[15.81683,50.75666],[15.73186,50.73885],[15.43798,50.80833],[15.3803,50.77187],[15.36656,50.83956],[15.2773,50.8907],[15.27043,50.97724],[15.2361,50.99886],[15.1743,50.9833],[15.16744,51.01959],[15.11937,50.99021],[15.10152,51.01095],[15.06218,51.02269],[15.03895,51.0123],[15.02433,51.0242],[14.96419,50.99108],[15.01088,50.97984],[14.99852,50.86817],[14.82803,50.86966]]]]}},{type:"Feature",properties:{iso1A2:"DE",iso1A3:"DEU",iso1N3:"276",wikidata:"Q183",nameEn:"Germany",groups:["EU","155","150"],callingCodes:["49"]},geometry:{type:"MultiPolygon",coordinates:[[[[8.70847,47.68904],[8.71773,47.69088],[8.70237,47.71453],[8.66416,47.71367],[8.67508,47.6979],[8.65769,47.68928],[8.66837,47.68437],[8.68985,47.69552],[8.70847,47.68904]]],[[[8.72617,47.69651],[8.72809,47.69282],[8.75856,47.68969],[8.79511,47.67462],[8.79966,47.70222],[8.76965,47.7075],[8.77309,47.72059],[8.80663,47.73821],[8.82002,47.71458],[8.86989,47.70504],[8.85065,47.68209],[8.87383,47.67045],[8.87625,47.65441],[8.89946,47.64769],[8.94093,47.65596],[9.02093,47.6868],[9.09891,47.67801],[9.13845,47.66389],[9.15181,47.66904],[9.1705,47.65513],[9.1755,47.65584],[9.17593,47.65399],[9.18203,47.65598],[9.25619,47.65939],[9.55125,47.53629],[9.72736,47.53457],[9.76748,47.5934],[9.80254,47.59419],[9.82591,47.58158],[9.8189,47.54688],[9.87499,47.52953],[9.87733,47.54688],[9.92407,47.53111],[9.96029,47.53899],[10.00003,47.48216],[10.03859,47.48927],[10.07131,47.45531],[10.09001,47.46005],[10.1052,47.4316],[10.06897,47.40709],[10.09819,47.35724],[10.11805,47.37228],[10.16362,47.36674],[10.17648,47.38889],[10.2127,47.38019],[10.22774,47.38904],[10.23757,47.37609],[10.19998,47.32832],[10.2147,47.31014],[10.17648,47.29149],[10.17531,47.27167],[10.23257,47.27088],[10.33424,47.30813],[10.39851,47.37623],[10.4324,47.38494],[10.4359,47.41183],[10.47446,47.43318],[10.46278,47.47901],[10.44291,47.48453],[10.4324,47.50111],[10.44992,47.5524],[10.43473,47.58394],[10.47329,47.58552],[10.48849,47.54057],[10.56912,47.53584],[10.60337,47.56755],[10.63456,47.5591],[10.68832,47.55752],[10.6965,47.54253],[10.7596,47.53228],[10.77596,47.51729],[10.88814,47.53701],[10.91268,47.51334],[10.86945,47.5015],[10.87061,47.4786],[10.90918,47.48571],[10.93839,47.48018],[10.92437,47.46991],[10.98513,47.42882],[10.97111,47.41617],[10.97111,47.39561],[11.11835,47.39719],[11.12536,47.41222],[11.20482,47.43198],[11.25157,47.43277],[11.22002,47.3964],[11.27844,47.39956],[11.29597,47.42566],[11.33804,47.44937],[11.4175,47.44621],[11.38128,47.47465],[11.4362,47.51413],[11.52618,47.50939],[11.58578,47.52281],[11.58811,47.55515],[11.60681,47.57881],[11.63934,47.59202],[11.84052,47.58354],[11.85572,47.60166],[12.0088,47.62451],[12.02282,47.61033],[12.05788,47.61742],[12.13734,47.60639],[12.17824,47.61506],[12.18145,47.61019],[12.17737,47.60121],[12.18568,47.6049],[12.20398,47.60667],[12.20801,47.61082],[12.19895,47.64085],[12.18507,47.65984],[12.18347,47.66663],[12.16769,47.68167],[12.16217,47.70105],[12.18303,47.70065],[12.22571,47.71776],[12.2542,47.7433],[12.26238,47.73544],[12.24017,47.69534],[12.26004,47.67725],[12.27991,47.68827],[12.336,47.69534],[12.37222,47.68433],[12.43883,47.6977],[12.44117,47.6741],[12.50076,47.62293],[12.53816,47.63553],[12.57438,47.63238],[12.6071,47.6741],[12.7357,47.6787],[12.77777,47.66689],[12.76492,47.64485],[12.82101,47.61493],[12.77427,47.58025],[12.80699,47.54477],[12.84672,47.54556],[12.85256,47.52741],[12.9624,47.47452],[12.98344,47.48716],[12.9998,47.46267],[13.04537,47.49426],[13.03252,47.53373],[13.05355,47.56291],[13.04537,47.58183],[13.06641,47.58577],[13.06407,47.60075],[13.09562,47.63304],[13.07692,47.68814],[13.01382,47.72116],[12.98578,47.7078],[12.92969,47.71094],[12.91333,47.7178],[12.90274,47.72513],[12.91711,47.74026],[12.9353,47.74788],[12.94371,47.76281],[12.93202,47.77302],[12.96311,47.79957],[12.98543,47.82896],[13.00588,47.84374],[12.94163,47.92927],[12.93886,47.94046],[12.93642,47.94436],[12.93419,47.94063],[12.92668,47.93879],[12.91985,47.94069],[12.9211,47.95135],[12.91683,47.95647],[12.87476,47.96195],[12.8549,48.01122],[12.76141,48.07373],[12.74973,48.10885],[12.7617,48.12796],[12.78595,48.12445],[12.80676,48.14979],[12.82673,48.15245],[12.8362,48.15876],[12.836,48.1647],[12.84475,48.16556],[12.87126,48.20318],[12.95306,48.20629],[13.02083,48.25689],[13.0851,48.27711],[13.126,48.27867],[13.18093,48.29577],[13.26039,48.29422],[13.30897,48.31575],[13.40709,48.37292],[13.43929,48.43386],[13.42527,48.45711],[13.45727,48.51092],[13.43695,48.55776],[13.45214,48.56472],[13.46967,48.55157],[13.50663,48.57506],[13.50131,48.58091],[13.51291,48.59023],[13.57535,48.55912],[13.59705,48.57013],[13.62508,48.55501],[13.65186,48.55092],[13.66113,48.53558],[13.72802,48.51208],[13.74816,48.53058],[13.7513,48.5624],[13.76921,48.55324],[13.80519,48.58026],[13.80038,48.59487],[13.82609,48.62345],[13.81901,48.6761],[13.81283,48.68426],[13.81791,48.69832],[13.79337,48.71375],[13.81863,48.73257],[13.82266,48.75544],[13.84023,48.76988],[13.8096,48.77877],[13.78977,48.83319],[13.76994,48.83537],[13.73854,48.88538],[13.67739,48.87886],[13.61624,48.9462],[13.58319,48.96899],[13.50552,48.97441],[13.50221,48.93752],[13.40802,48.98851],[13.39479,49.04812],[13.28242,49.1228],[13.23689,49.11412],[13.20405,49.12303],[13.17019,49.14339],[13.17665,49.16713],[13.05883,49.26259],[13.02957,49.27399],[13.03618,49.30417],[12.94859,49.34079],[12.88249,49.35479],[12.88414,49.33541],[12.78168,49.34618],[12.75854,49.3989],[12.71227,49.42363],[12.669,49.42935],[12.64121,49.47628],[12.64782,49.52565],[12.60155,49.52887],[12.56188,49.6146],[12.53544,49.61888],[12.52553,49.68415],[12.4462,49.70233],[12.40489,49.76321],[12.46603,49.78882],[12.48256,49.83575],[12.55197,49.92094],[12.47264,49.94222],[12.49908,49.97305],[12.30798,50.05719],[12.26111,50.06331],[12.27433,50.0771],[12.23709,50.10213],[12.2073,50.10315],[12.1917,50.13434],[12.21484,50.16399],[12.19335,50.19997],[12.09287,50.25032],[12.13716,50.27396],[12.10907,50.32041],[12.18013,50.32146],[12.20823,50.2729],[12.25119,50.27079],[12.26953,50.25189],[12.24791,50.25525],[12.23943,50.24594],[12.28755,50.22429],[12.28063,50.19544],[12.29232,50.17524],[12.32596,50.17146],[12.33847,50.19432],[12.32445,50.20442],[12.33263,50.24367],[12.35425,50.23993],[12.36594,50.28289],[12.40158,50.29521],[12.39924,50.32302],[12.43371,50.32506],[12.43722,50.33774],[12.46643,50.35527],[12.48256,50.34784],[12.49214,50.35228],[12.48747,50.37278],[12.51356,50.39694],[12.67261,50.41949],[12.70731,50.39948],[12.73044,50.42268],[12.73476,50.43237],[12.82465,50.45738],[12.94058,50.40944],[12.98433,50.42016],[13.02147,50.44763],[13.02038,50.4734],[13.0312,50.50944],[13.08301,50.50132],[13.13424,50.51709],[13.19043,50.50237],[13.25158,50.59268],[13.29454,50.57904],[13.32594,50.58009],[13.32264,50.60317],[13.37805,50.627],[13.37485,50.64931],[13.42189,50.61243],[13.46413,50.60102],[13.49742,50.63133],[13.5226,50.64721],[13.53748,50.67654],[13.52474,50.70394],[13.65977,50.73096],[13.70204,50.71771],[13.76316,50.73487],[13.82942,50.7251],[13.89444,50.74142],[13.89113,50.78533],[13.98864,50.8177],[14.02982,50.80662],[14.22331,50.86049],[14.24314,50.88761],[14.27123,50.89386],[14.30098,50.88448],[14.38691,50.89907],[14.39848,50.93866],[14.31422,50.95243],[14.30251,50.96606],[14.32793,50.97379],[14.32353,50.98556],[14.28776,50.97718],[14.25665,50.98935],[14.30098,51.05515],[14.41335,51.02086],[14.45827,51.03712],[14.49202,51.02286],[14.49154,51.04382],[14.49991,51.04692],[14.50809,51.0427],[14.49873,51.02242],[14.53321,51.01679],[14.53438,51.00374],[14.56432,51.01008],[14.58215,50.99306],[14.59908,50.98685],[14.59702,50.96148],[14.56374,50.922],[14.58024,50.91443],[14.64802,50.93241],[14.65259,50.90513],[14.63434,50.8883],[14.61993,50.86049],[14.70661,50.84096],[14.79139,50.81438],[14.82803,50.86966],[14.81664,50.88148],[14.89681,50.9422],[14.89252,50.94999],[14.92942,50.99744],[14.95529,51.04552],[14.97938,51.07742],[14.98229,51.11354],[14.99689,51.12205],[14.99079,51.14284],[14.99646,51.14365],[15.00083,51.14974],[14.99414,51.15813],[14.99311,51.16249],[15.0047,51.16874],[15.01242,51.21285],[15.04288,51.28387],[14.98008,51.33449],[14.96899,51.38367],[14.9652,51.44793],[14.94749,51.47155],[14.73219,51.52922],[14.72652,51.53902],[14.73047,51.54606],[14.71125,51.56209],[14.7727,51.61263],[14.75759,51.62318],[14.75392,51.67445],[14.69065,51.70842],[14.66386,51.73282],[14.64625,51.79472],[14.60493,51.80473],[14.59089,51.83302],[14.6588,51.88359],[14.6933,51.9044],[14.70601,51.92944],[14.7177,51.94048],[14.72163,51.95188],[14.71836,51.95606],[14.7139,51.95643],[14.70488,51.97679],[14.71339,52.00337],[14.76026,52.06624],[14.72971,52.09167],[14.6917,52.10283],[14.67683,52.13936],[14.70616,52.16927],[14.68344,52.19612],[14.71319,52.22144],[14.70139,52.25038],[14.58149,52.28007],[14.56378,52.33838],[14.55228,52.35264],[14.54423,52.42568],[14.63056,52.48993],[14.60081,52.53116],[14.6289,52.57136],[14.61073,52.59847],[14.22071,52.81175],[14.13806,52.82392],[14.12256,52.84311],[14.15873,52.87715],[14.14056,52.95786],[14.25954,53.00264],[14.35044,53.05829],[14.38679,53.13669],[14.36696,53.16444],[14.37853,53.20405],[14.40662,53.21098],[14.45125,53.26241],[14.44133,53.27427],[14.4215,53.27724],[14.35209,53.49506],[14.3273,53.50587],[14.30416,53.55499],[14.31904,53.61581],[14.2853,53.63392],[14.28477,53.65955],[14.27133,53.66613],[14.2836,53.67721],[14.26782,53.69866],[14.27249,53.74464],[14.21323,53.8664],[14.20823,53.90776],[14.18544,53.91258],[14.20647,53.91671],[14.22634,53.9291],[14.20811,54.12784],[13.93395,54.84044],[12.85844,54.82438],[11.90309,54.38543],[11.00303,54.63689],[10.31111,54.65968],[10.16755,54.73883],[9.89314,54.84171],[9.73563,54.8247],[9.61187,54.85548],[9.62734,54.88057],[9.58937,54.88785],[9.4659,54.83131],[9.43155,54.82586],[9.41213,54.84254],[9.38532,54.83968],[9.36496,54.81749],[9.33849,54.80233],[9.32771,54.80602],[9.2474,54.8112],[9.23445,54.83432],[9.24631,54.84726],[9.20571,54.85841],[9.14275,54.87421],[9.04629,54.87249],[8.92795,54.90452],[8.81178,54.90518],[8.76387,54.8948],[8.63979,54.91069],[8.55769,54.91837],[8.45719,55.06747],[8.02459,55.09613],[5.45168,54.20039],[6.91025,53.44221],[7.00198,53.32672],[7.19052,53.31866],[7.21679,53.20058],[7.22681,53.18165],[7.17898,53.13817],[7.21694,53.00742],[7.07253,52.81083],[7.04557,52.63318],[6.77307,52.65375],[6.71641,52.62905],[6.69507,52.488],[6.94293,52.43597],[6.99041,52.47235],[7.03417,52.40237],[7.07044,52.37805],[7.02703,52.27941],[7.06365,52.23789],[7.03729,52.22695],[6.9897,52.2271],[6.97189,52.20329],[6.83984,52.11728],[6.76117,52.11895],[6.68128,52.05052],[6.83035,51.9905],[6.82357,51.96711],[6.72319,51.89518],[6.68386,51.91861],[6.58556,51.89386],[6.50231,51.86313],[6.47179,51.85395],[6.38815,51.87257],[6.40704,51.82771],[6.30593,51.84998],[6.29872,51.86801],[6.21443,51.86801],[6.15349,51.90439],[6.11551,51.89769],[6.16902,51.84094],[6.10337,51.84829],[6.06705,51.86136],[5.99848,51.83195],[5.94568,51.82786],[5.98665,51.76944],[5.95003,51.7493],[6.04091,51.71821],[6.02767,51.6742],[6.11759,51.65609],[6.09055,51.60564],[6.18017,51.54096],[6.21724,51.48568],[6.20654,51.40049],[6.22641,51.39948],[6.22674,51.36135],[6.16977,51.33169],[6.07889,51.24432],[6.07889,51.17038],[6.17384,51.19589],[6.16706,51.15677],[5.98292,51.07469],[5.9541,51.03496],[5.9134,51.06736],[5.86735,51.05182],[5.87849,51.01969],[5.90493,51.00198],[5.90296,50.97356],[5.95282,50.98728],[6.02697,50.98303],[6.01615,50.93367],[6.09297,50.92066],[6.07486,50.89307],[6.08805,50.87223],[6.07693,50.86025],[6.07431,50.84674],[6.05702,50.85179],[6.05623,50.8572],[6.01921,50.84435],[6.02328,50.81694],[6.00462,50.80065],[5.98404,50.80988],[5.97497,50.79992],[6.02624,50.77453],[6.01976,50.75398],[6.03889,50.74618],[6.0326,50.72647],[6.0406,50.71848],[6.04428,50.72861],[6.11707,50.72231],[6.17852,50.6245],[6.26957,50.62444],[6.2476,50.60392],[6.24888,50.59869],[6.24005,50.58732],[6.22581,50.5907],[6.20281,50.56952],[6.17739,50.55875],[6.17802,50.54179],[6.19735,50.53576],[6.19579,50.5313],[6.18716,50.52653],[6.19193,50.5212],[6.20599,50.52089],[6.22335,50.49578],[6.26637,50.50272],[6.30809,50.50058],[6.3465,50.48833],[6.34005,50.46083],[6.37219,50.45397],[6.36852,50.40776],[6.34406,50.37994],[6.3688,50.35898],[6.40785,50.33557],[6.40641,50.32425],[6.35701,50.31139],[6.32488,50.32333],[6.29949,50.30887],[6.28797,50.27458],[6.208,50.25179],[6.16853,50.2234],[6.18364,50.20815],[6.18739,50.1822],[6.14588,50.17106],[6.14132,50.14971],[6.15298,50.14126],[6.1379,50.12964],[6.12055,50.09171],[6.11274,50.05916],[6.13458,50.04141],[6.13044,50.02929],[6.14666,50.02207],[6.13794,50.01466],[6.13273,50.02019],[6.1295,50.01849],[6.13806,50.01056],[6.14948,50.00908],[6.14147,49.99563],[6.1701,49.98518],[6.16466,49.97086],[6.17872,49.9537],[6.18554,49.95622],[6.18045,49.96611],[6.19089,49.96991],[6.19856,49.95053],[6.22094,49.94955],[6.22608,49.929],[6.21882,49.92403],[6.22926,49.92096],[6.23496,49.89972],[6.26146,49.88203],[6.28874,49.87592],[6.29692,49.86685],[6.30963,49.87021],[6.32303,49.85133],[6.32098,49.83728],[6.33585,49.83785],[6.34267,49.84974],[6.36576,49.85032],[6.40022,49.82029],[6.42521,49.81591],[6.42905,49.81091],[6.44131,49.81443],[6.45425,49.81164],[6.47111,49.82263],[6.48718,49.81267],[6.50647,49.80916],[6.51215,49.80124],[6.52121,49.81338],[6.53122,49.80666],[6.52169,49.79787],[6.50534,49.78952],[6.51669,49.78336],[6.51056,49.77515],[6.51828,49.76855],[6.51646,49.75961],[6.50174,49.75292],[6.50193,49.73291],[6.51805,49.72425],[6.51397,49.72058],[6.50261,49.72718],[6.49535,49.72645],[6.49694,49.72205],[6.5042,49.71808],[6.50647,49.71353],[6.49785,49.71118],[6.48014,49.69767],[6.46048,49.69092],[6.44654,49.67799],[6.42937,49.66857],[6.42726,49.66078],[6.43768,49.66021],[6.4413,49.65722],[6.41861,49.61723],[6.39822,49.60081],[6.385,49.59946],[6.37464,49.58886],[6.38342,49.5799],[6.38024,49.57593],[6.36676,49.57813],[6.35825,49.57053],[6.38228,49.55855],[6.38072,49.55171],[6.35666,49.52931],[6.36788,49.50377],[6.36907,49.48931],[6.36778,49.46937],[6.38352,49.46463],[6.39168,49.4667],[6.40274,49.46546],[6.42432,49.47683],[6.55404,49.42464],[6.533,49.40748],[6.60091,49.36864],[6.58807,49.35358],[6.572,49.35027],[6.60186,49.31055],[6.66583,49.28065],[6.69274,49.21661],[6.71843,49.2208],[6.73256,49.20486],[6.71137,49.18808],[6.73765,49.16375],[6.78265,49.16793],[6.83385,49.15162],[6.84703,49.15734],[6.86225,49.18185],[6.85016,49.19354],[6.85119,49.20038],[6.83555,49.21249],[6.85939,49.22376],[6.89298,49.20863],[6.91875,49.22261],[6.93831,49.2223],[6.94028,49.21641],[6.95963,49.203],[6.97273,49.2099],[7.01318,49.19018],[7.03459,49.19096],[7.0274,49.17042],[7.03178,49.15734],[7.04662,49.13724],[7.04409,49.12123],[7.04843,49.11422],[7.05548,49.11185],[7.06642,49.11415],[7.07162,49.1255],[7.09007,49.13094],[7.07859,49.15031],[7.10715,49.15631],[7.10384,49.13787],[7.12504,49.14253],[7.1358,49.1282],[7.1593,49.1204],[7.23473,49.12971],[7.29514,49.11426],[7.3195,49.14231],[7.35995,49.14399],[7.3662,49.17308],[7.44052,49.18354],[7.44455,49.16765],[7.49473,49.17],[7.49172,49.13915],[7.53012,49.09818],[7.56416,49.08136],[7.62575,49.07654],[7.63618,49.05428],[7.75948,49.04562],[7.79557,49.06583],[7.86386,49.03499],[7.93641,49.05544],[7.97783,49.03161],[8.14189,48.97833],[8.22604,48.97352],[8.20031,48.95856],[8.19989,48.95825],[8.12813,48.87985],[8.10253,48.81829],[8.06802,48.78957],[8.0326,48.79017],[8.01534,48.76085],[7.96994,48.75606],[7.96812,48.72491],[7.89002,48.66317],[7.84098,48.64217],[7.80057,48.5857],[7.80167,48.54758],[7.80647,48.51239],[7.76833,48.48945],[7.73109,48.39192],[7.74562,48.32736],[7.69022,48.30018],[7.6648,48.22219],[7.57137,48.12292],[7.56966,48.03265],[7.62302,47.97898],[7.55673,47.87371],[7.52921,47.77747],[7.54761,47.72912],[7.53722,47.71635],[7.51266,47.70197],[7.51915,47.68335],[7.52067,47.66437],[7.53384,47.65115],[7.5591,47.63849],[7.57423,47.61628],[7.58851,47.60794],[7.59301,47.60058],[7.58945,47.59017],[7.60523,47.58519],[7.60459,47.57869],[7.61929,47.57683],[7.64309,47.59151],[7.64213,47.5944],[7.64599,47.59695],[7.67395,47.59212],[7.68229,47.59905],[7.69385,47.60099],[7.68486,47.59601],[7.67115,47.5871],[7.68904,47.57133],[7.67655,47.56435],[7.63338,47.56256],[7.65083,47.54662],[7.66174,47.54554],[7.6656,47.53752],[7.68101,47.53232],[7.69642,47.53297],[7.71961,47.54219],[7.75261,47.54599],[7.79486,47.55691],[7.81901,47.58798],[7.84412,47.5841],[7.88664,47.58854],[7.90673,47.57674],[7.91251,47.55031],[7.94494,47.54511],[7.95682,47.55789],[7.97581,47.55493],[8.00113,47.55616],[8.02136,47.55096],[8.04383,47.55443],[8.06663,47.56374],[8.08557,47.55768],[8.10002,47.56504],[8.10395,47.57918],[8.11543,47.5841],[8.13662,47.58432],[8.13823,47.59147],[8.14947,47.59558],[8.1652,47.5945],[8.19378,47.61636],[8.20617,47.62141],[8.22011,47.6181],[8.22577,47.60385],[8.23809,47.61204],[8.25863,47.61571],[8.26313,47.6103],[8.2824,47.61225],[8.29722,47.60603],[8.29524,47.5919],[8.30277,47.58607],[8.32735,47.57133],[8.35512,47.57014],[8.38273,47.56608],[8.39477,47.57826],[8.43235,47.56617],[8.49431,47.58107],[8.48949,47.588],[8.46637,47.58389],[8.45578,47.60121],[8.50747,47.61897],[8.51686,47.63476],[8.55756,47.62394],[8.57586,47.59537],[8.60348,47.61204],[8.59545,47.64298],[8.60701,47.65271],[8.61471,47.64514],[8.60412,47.63735],[8.62049,47.63757],[8.62884,47.65098],[8.61113,47.66332],[8.6052,47.67258],[8.57683,47.66158],[8.56141,47.67088],[8.52801,47.66059],[8.5322,47.64687],[8.49656,47.64709],[8.46605,47.64103],[8.4667,47.65747],[8.44711,47.65379],[8.42264,47.66667],[8.41346,47.66676],[8.40473,47.67499],[8.4211,47.68407],[8.40569,47.69855],[8.44807,47.72426],[8.45771,47.7493],[8.48868,47.77215],[8.56814,47.78001],[8.56415,47.80633],[8.61657,47.79998],[8.62408,47.7626],[8.64425,47.76398],[8.65292,47.80066],[8.68022,47.78599],[8.68985,47.75686],[8.71778,47.76571],[8.74251,47.75168],[8.70543,47.73121],[8.73671,47.7169],[8.72617,47.69651]]]]}},{type:"Feature",properties:{iso1A2:"DG",iso1A3:"DGA",wikidata:"Q184851",nameEn:"Diego Garcia",country:"GB",groups:["IO","014","202","002"],isoStatus:"excRes",callingCodes:["246"]},geometry:{type:"MultiPolygon",coordinates:[[[[73.14823,-7.76302],[73.09982,-6.07324],[71.43792,-7.73904],[73.14823,-7.76302]]]]}},{type:"Feature",properties:{iso1A2:"DJ",iso1A3:"DJI",iso1N3:"262",wikidata:"Q977",nameEn:"Djibouti",groups:["014","202","002"],callingCodes:["253"]},geometry:{type:"MultiPolygon",coordinates:[[[[43.42425,11.70983],[43.90659,12.3823],[43.32909,12.59711],[43.29075,12.79154],[42.86195,12.58747],[42.7996,12.42629],[42.6957,12.36201],[42.46941,12.52661],[42.4037,12.46478],[41.95461,11.81157],[41.82878,11.72361],[41.77727,11.49902],[41.8096,11.33606],[41.80056,10.97127],[42.06302,10.92599],[42.13691,10.97586],[42.42669,10.98493],[42.62989,11.09711],[42.75111,11.06992],[42.79037,10.98493],[42.95776,10.98533],[43.42425,11.70983]]]]}},{type:"Feature",properties:{iso1A2:"DK",iso1A3:"DNK",iso1N3:"208",wikidata:"Q35",nameEn:"Denmark",groups:["EU","154","150"],callingCodes:["45"]},geometry:{type:"MultiPolygon",coordinates:[[[[12.16597,56.60205],[10.40861,58.38489],[7.28637,57.35913],[8.02459,55.09613],[8.45719,55.06747],[8.55769,54.91837],[8.63979,54.91069],[8.76387,54.8948],[8.81178,54.90518],[8.92795,54.90452],[9.04629,54.87249],[9.14275,54.87421],[9.20571,54.85841],[9.24631,54.84726],[9.23445,54.83432],[9.2474,54.8112],[9.32771,54.80602],[9.33849,54.80233],[9.36496,54.81749],[9.38532,54.83968],[9.41213,54.84254],[9.43155,54.82586],[9.4659,54.83131],[9.58937,54.88785],[9.62734,54.88057],[9.61187,54.85548],[9.73563,54.8247],[9.89314,54.84171],[10.16755,54.73883],[10.31111,54.65968],[11.00303,54.63689],[11.90309,54.38543],[12.85844,54.82438],[13.93395,54.84044],[15.36991,54.73263],[15.79951,55.54655],[14.89259,55.5623],[14.28399,55.1553],[12.84405,55.13257],[12.60345,55.42675],[12.88472,55.63369],[12.6372,55.91371],[12.65312,56.04345],[12.07466,56.29488],[12.16597,56.60205]]]]}},{type:"Feature",properties:{iso1A2:"DM",iso1A3:"DMA",iso1N3:"212",wikidata:"Q784",nameEn:"Dominica",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 767"]},geometry:{type:"MultiPolygon",coordinates:[[[[-61.51867,14.96709],[-60.69955,15.22234],[-60.95725,15.70997],[-61.44899,15.79571],[-61.81728,15.58058],[-61.51867,14.96709]]]]}},{type:"Feature",properties:{iso1A2:"DO",iso1A3:"DOM",iso1N3:"214",wikidata:"Q786",nameEn:"Dominican Republic",groups:["029","003","419","019"],callingCodes:["1 809","1 829","1 849"]},geometry:{type:"MultiPolygon",coordinates:[[[[-67.87844,21.7938],[-72.38946,20.27111],[-71.77419,19.73128],[-71.75865,19.70231],[-71.7429,19.58445],[-71.71449,19.55364],[-71.71268,19.53374],[-71.6802,19.45008],[-71.69448,19.37866],[-71.77766,19.33823],[-71.73229,19.26686],[-71.62642,19.21212],[-71.65337,19.11759],[-71.69938,19.10916],[-71.71088,19.08353],[-71.74088,19.0437],[-71.88102,18.95007],[-71.77766,18.95007],[-71.72624,18.87802],[-71.71885,18.78423],[-71.82556,18.62551],[-71.95412,18.64939],[-72.00201,18.62312],[-71.88102,18.50125],[-71.90875,18.45821],[-71.69952,18.34101],[-71.78271,18.18302],[-71.75465,18.14405],[-71.74994,18.11115],[-71.73783,18.07177],[-71.75671,18.03456],[-72.29523,17.48026],[-68.39466,16.14167],[-67.87844,21.7938]]]]}},{type:"Feature",properties:{iso1A2:"DZ",iso1A3:"DZA",iso1N3:"012",wikidata:"Q262",nameEn:"Algeria",groups:["015","002"],callingCodes:["213"]},geometry:{type:"MultiPolygon",coordinates:[[[[8.59123,37.14286],[2.46645,37.97429],[-2.27707,35.35051],[-2.21248,35.08532],[-2.21445,35.04378],[-2.04734,34.93218],[-1.97833,34.93218],[-1.97469,34.886],[-1.73707,34.74226],[-1.84569,34.61907],[-1.69788,34.48056],[-1.78042,34.39018],[-1.64666,34.10405],[-1.73494,33.71721],[-1.59508,33.59929],[-1.67067,33.27084],[-1.46249,33.0499],[-1.54244,32.95499],[-1.37794,32.73628],[-0.9912,32.52467],[-1.24998,32.32993],[-1.24453,32.1917],[-1.15735,32.12096],[-1.22829,32.07832],[-2.46166,32.16603],[-2.93873,32.06557],[-2.82784,31.79459],[-3.66314,31.6339],[-3.66386,31.39202],[-3.77647,31.31912],[-3.77103,31.14984],[-3.54944,31.0503],[-3.65418,30.85566],[-3.64735,30.67539],[-4.31774,30.53229],[-4.6058,30.28343],[-5.21671,29.95253],[-5.58831,29.48103],[-5.72121,29.52322],[-5.75616,29.61407],[-6.69965,29.51623],[-6.78351,29.44634],[-6.95824,29.50924],[-7.61585,29.36252],[-8.6715,28.71194],[-8.66879,27.6666],[-8.66674,27.31569],[-4.83423,24.99935],[1.15698,21.12843],[1.20992,20.73533],[3.24648,19.81703],[3.12501,19.1366],[3.36082,18.9745],[4.26651,19.14224],[5.8153,19.45101],[7.38361,20.79165],[7.48273,20.87258],[11.96886,23.51735],[11.62498,24.26669],[11.41061,24.21456],[10.85323,24.5595],[10.33159,24.5465],[10.02432,24.98124],[10.03146,25.35635],[9.38834,26.19288],[9.51696,26.39148],[9.89569,26.57696],[9.78136,29.40961],[9.3876,30.16738],[9.55544,30.23971],[9.07483,32.07865],[8.35999,32.50101],[8.31895,32.83483],[8.1179,33.05086],[8.11433,33.10175],[7.83028,33.18851],[7.73687,33.42114],[7.54088,33.7726],[7.52851,34.06493],[7.66174,34.20167],[7.74207,34.16492],[7.81242,34.21841],[7.86264,34.3987],[8.20482,34.57575],[8.29655,34.72798],[8.25189,34.92009],[8.30727,34.95378],[8.3555,35.10007],[8.47318,35.23376],[8.30329,35.29884],[8.36086,35.47774],[8.35371,35.66373],[8.26472,35.73669],[8.2626,35.91733],[8.40731,36.42208],[8.18936,36.44939],[8.16167,36.48817],[8.47609,36.66607],[8.46537,36.7706],[8.57613,36.78062],[8.67706,36.8364],[8.62972,36.86499],[8.64044,36.9401],[8.59123,37.14286]]]]}},{type:"Feature",properties:{iso1A2:"EA",wikidata:"Q28868874",nameEn:"Ceuta, Melilla",country:"ES",groups:["015","002"],isoStatus:"excRes",callingCodes:["34"]},geometry:{type:"MultiPolygon",coordinates:[[[[-5.38491,35.92591],[-5.37338,35.88417],[-5.35844,35.87375],[-5.34379,35.8711],[-5.27056,35.88794],[-5.27635,35.91222],[-5.38491,35.92591]]],[[[-2.92224,35.3401],[-2.96038,35.31609],[-2.96648,35.30475],[-2.96978,35.29459],[-2.97035,35.28852],[-2.96507,35.28801],[-2.96826,35.28296],[-2.96516,35.27967],[-2.95431,35.2728],[-2.95065,35.26576],[-2.93893,35.26737],[-2.92674,35.27313],[-2.92181,35.28599],[-2.92224,35.3401]]]]}},{type:"Feature",properties:{iso1A2:"EC",iso1A3:"ECU",iso1N3:"218",wikidata:"Q736",nameEn:"Ecuador",groups:["005","419","019"],callingCodes:["593"]},geometry:{type:"MultiPolygon",coordinates:[[[[-75.25764,-0.11943],[-75.82927,0.09578],[-76.23441,0.42294],[-76.41215,0.38228],[-76.4094,0.24015],[-76.89177,0.24736],[-77.52001,0.40782],[-77.49984,0.64476],[-77.67815,0.73863],[-77.66416,0.81604],[-77.68613,0.83029],[-77.7148,0.85003],[-77.85677,0.80197],[-78.42749,1.15389],[-78.87137,1.47457],[-93.12365,2.64343],[-92.46744,-2.52874],[-80.30602,-3.39149],[-80.20647,-3.431],[-80.24123,-3.46124],[-80.24475,-3.47846],[-80.24586,-3.48677],[-80.23651,-3.48652],[-80.22629,-3.501],[-80.20535,-3.51667],[-80.21642,-3.5888],[-80.19848,-3.59249],[-80.18741,-3.63994],[-80.19926,-3.68894],[-80.13232,-3.90317],[-80.46386,-4.01342],[-80.4822,-4.05477],[-80.45023,-4.20938],[-80.32114,-4.21323],[-80.46386,-4.41516],[-80.39256,-4.48269],[-80.13945,-4.29786],[-79.79722,-4.47558],[-79.59402,-4.46848],[-79.26248,-4.95167],[-79.1162,-4.97774],[-79.01659,-5.01481],[-78.85149,-4.66795],[-78.68394,-4.60754],[-78.34362,-3.38633],[-78.24589,-3.39907],[-78.22642,-3.51113],[-78.14324,-3.47653],[-78.19369,-3.36431],[-77.94147,-3.05454],[-76.6324,-2.58397],[-76.05203,-2.12179],[-75.57429,-1.55961],[-75.3872,-0.9374],[-75.22862,-0.95588],[-75.22862,-0.60048],[-75.53615,-0.19213],[-75.60169,-0.18708],[-75.61997,-0.10012],[-75.40192,-0.17196],[-75.25764,-0.11943]]]]}},{type:"Feature",properties:{iso1A2:"EE",iso1A3:"EST",iso1N3:"233",wikidata:"Q191",nameEn:"Estonia",aliases:["EW"],groups:["EU","154","150"],callingCodes:["372"]},geometry:{type:"MultiPolygon",coordinates:[[[[26.32936,60.00121],[20.5104,59.15546],[19.84909,57.57876],[22.80496,57.87798],[23.20055,57.56697],[24.26221,57.91787],[24.3579,57.87471],[25.19484,58.0831],[25.28237,57.98539],[25.29581,58.08288],[25.73499,57.90193],[26.05949,57.84744],[26.0324,57.79037],[26.02456,57.78342],[26.027,57.78158],[26.0266,57.77441],[26.02069,57.77169],[26.02415,57.76865],[26.03332,57.7718],[26.0543,57.76105],[26.08098,57.76619],[26.2029,57.7206],[26.1866,57.6849],[26.29253,57.59244],[26.46527,57.56885],[26.54675,57.51813],[26.90364,57.62823],[27.34698,57.52242],[27.31919,57.57672],[27.40393,57.62125],[27.3746,57.66834],[27.52615,57.72843],[27.50171,57.78842],[27.56689,57.83356],[27.78526,57.83963],[27.81841,57.89244],[27.67282,57.92627],[27.62393,58.09462],[27.48541,58.22615],[27.55489,58.39525],[27.36366,58.78381],[27.74429,58.98351],[27.80482,59.1116],[27.87978,59.18097],[27.90911,59.24353],[28.00689,59.28351],[28.14215,59.28934],[28.19284,59.35791],[28.20537,59.36491],[28.21137,59.38058],[28.19061,59.39962],[28.04187,59.47017],[27.85643,59.58538],[26.90044,59.63819],[26.32936,60.00121]]]]}},{type:"Feature",properties:{iso1A2:"EG",iso1A3:"EGY",iso1N3:"818",wikidata:"Q79",nameEn:"Egypt",groups:["015","002"],callingCodes:["20"]},geometry:{type:"MultiPolygon",coordinates:[[[[33.62659,31.82938],[25.63787,31.9359],[25.14001,31.67534],[25.06041,31.57937],[24.83101,31.31921],[25.01077,30.73861],[24.71117,30.17441],[24.99968,29.24574],[24.99885,21.99535],[33.17563,22.00405],[34.0765,22.00501],[37.8565,22.00903],[34.51305,27.70027],[34.46254,27.99552],[34.88293,29.37455],[34.92298,29.45305],[34.26742,31.21998],[34.24012,31.29591],[34.23572,31.2966],[34.21853,31.32363],[34.052,31.46619],[33.62659,31.82938]]]]}},{type:"Feature",properties:{iso1A2:"EH",iso1A3:"ESH",iso1N3:"732",wikidata:"Q6250",nameEn:"Western Sahara",groups:["015","002"],callingCodes:["212"]},geometry:{type:"MultiPolygon",coordinates:[[[[-8.66879,27.6666],[-8.77527,27.66663],[-8.71787,26.9898],[-9.08698,26.98639],[-9.56957,26.90042],[-9.81998,26.71379],[-10.68417,26.90984],[-11.35695,26.8505],[-11.23622,26.72023],[-11.38635,26.611],[-11.62052,26.05229],[-12.06001,26.04442],[-12.12281,25.13682],[-12.92147,24.39502],[-13.00628,24.01923],[-13.75627,23.77231],[-14.10361,22.75501],[-14.1291,22.41636],[-14.48112,22.00886],[-14.47329,21.63839],[-14.78487,21.36587],[-16.44269,21.39745],[-16.9978,21.36239],[-17.02707,21.34022],[-17.21511,21.34226],[-17.35589,20.80492],[-17.0471,20.76408],[-17.0695,20.85742],[-17.06781,20.92697],[-17.0396,20.9961],[-17.0357,21.05368],[-16.99806,21.12142],[-16.95474,21.33997],[-13.01525,21.33343],[-13.08438,22.53866],[-13.15313,22.75649],[-13.10753,22.89493],[-13.00412,23.02297],[-12.5741,23.28975],[-12.36213,23.3187],[-12.14969,23.41935],[-12.00251,23.4538],[-12.0002,25.9986],[-8.66721,25.99918],[-8.66674,27.31569],[-8.66879,27.6666]]]]}},{type:"Feature",properties:{iso1A2:"ER",iso1A3:"ERI",iso1N3:"232",wikidata:"Q986",nameEn:"Eritrea",groups:["014","202","002"],callingCodes:["291"]},geometry:{type:"MultiPolygon",coordinates:[[[[41.37609,16.19728],[39.63762,18.37348],[38.57727,17.98125],[38.45916,17.87167],[38.37133,17.66269],[38.13362,17.53906],[37.50967,17.32199],[37.42694,17.04041],[36.99777,17.07172],[36.92193,16.23451],[36.76371,15.80831],[36.69761,15.75323],[36.54276,15.23478],[36.44337,15.14963],[36.54376,14.25597],[36.56536,14.26177],[36.55659,14.28237],[36.63364,14.31172],[36.85787,14.32201],[37.01622,14.2561],[37.09486,14.27155],[37.13206,14.40746],[37.3106,14.44657],[37.47319,14.2149],[37.528,14.18413],[37.91287,14.89447],[38.0364,14.72745],[38.25562,14.67287],[38.3533,14.51323],[38.45748,14.41445],[38.78306,14.4754],[38.98058,14.54895],[39.02834,14.63717],[39.16074,14.65187],[39.14772,14.61827],[39.19547,14.56996],[39.23888,14.56365],[39.26927,14.48801],[39.2302,14.44598],[39.2519,14.40393],[39.37685,14.54402],[39.52756,14.49011],[39.50585,14.55735],[39.58182,14.60987],[39.76632,14.54264],[39.9443,14.41024],[40.07236,14.54264],[40.14649,14.53969],[40.21128,14.39342],[40.25686,14.41445],[40.9167,14.11152],[41.25097,13.60787],[41.62864,13.38626],[42.05841,12.80912],[42.21469,12.75832],[42.2798,12.6355],[42.4037,12.46478],[42.46941,12.52661],[42.6957,12.36201],[42.7996,12.42629],[42.86195,12.58747],[43.29075,12.79154],[42.63806,13.58268],[41.29956,15.565],[41.37609,16.19728]]]]}},{type:"Feature",properties:{iso1A2:"ES",iso1A3:"ESP",iso1N3:"724",wikidata:"Q29",nameEn:"Spain",groups:["EU","039","150"],callingCodes:["34"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.41312,35.17111],[-2.41265,35.1877],[-2.44896,35.18777],[-2.44887,35.17075],[-2.41312,35.17111]]],[[[-3.90602,35.21494],[-3.88926,35.20841],[-3.88617,35.21406],[-3.90288,35.22024],[-3.90602,35.21494]]],[[[-4.30191,35.17419],[-4.30112,35.17058],[-4.29436,35.17149],[-4.30191,35.17419]]],[[[-7.27694,35.93599],[-5.64962,35.93752],[-5.10878,36.05227],[-2.85819,35.63219],[-2.27707,35.35051],[2.46645,37.97429],[5.18061,39.43581],[3.4481,42.4358],[3.17156,42.43545],[3.11379,42.43646],[3.10027,42.42621],[3.08167,42.42748],[3.03734,42.47363],[2.96518,42.46692],[2.94283,42.48174],[2.92107,42.4573],[2.88413,42.45938],[2.86983,42.46843],[2.85675,42.45444],[2.84335,42.45724],[2.77464,42.41046],[2.75497,42.42578],[2.72056,42.42298],[2.65311,42.38771],[2.6747,42.33974],[2.57934,42.35808],[2.55516,42.35351],[2.54382,42.33406],[2.48457,42.33933],[2.43508,42.37568],[2.43299,42.39423],[2.38504,42.39977],[2.25551,42.43757],[2.20578,42.41633],[2.16599,42.42314],[2.12789,42.41291],[2.11621,42.38393],[2.06241,42.35906],[2.00488,42.35399],[1.96482,42.37787],[1.9574,42.42401],[1.94084,42.43039],[1.94061,42.43333],[1.94292,42.44316],[1.93663,42.45439],[1.88853,42.4501],[1.83037,42.48395],[1.76335,42.48863],[1.72515,42.50338],[1.70571,42.48867],[1.66826,42.50779],[1.65674,42.47125],[1.58933,42.46275],[1.57953,42.44957],[1.55937,42.45808],[1.55073,42.43299],[1.5127,42.42959],[1.44529,42.43724],[1.43838,42.47848],[1.41648,42.48315],[1.46661,42.50949],[1.44759,42.54431],[1.41245,42.53539],[1.4234,42.55959],[1.44529,42.56722],[1.42512,42.58292],[1.44197,42.60217],[1.35562,42.71944],[1.15928,42.71407],[1.0804,42.78569],[0.98292,42.78754],[0.96166,42.80629],[0.93089,42.79154],[0.711,42.86372],[0.66121,42.84021],[0.65421,42.75872],[0.67873,42.69458],[0.40214,42.69779],[0.36251,42.72282],[0.29407,42.67431],[0.25336,42.7174],[0.17569,42.73424],[-0.02468,42.68513],[-0.10519,42.72761],[-0.16141,42.79535],[-0.17939,42.78974],[-0.3122,42.84788],[-0.38833,42.80132],[-0.41319,42.80776],[-0.44334,42.79939],[-0.50863,42.82713],[-0.55497,42.77846],[-0.67637,42.88303],[-0.69837,42.87945],[-0.72608,42.89318],[-0.73422,42.91228],[-0.72037,42.92541],[-0.75478,42.96916],[-0.81652,42.95166],[-0.97133,42.96239],[-1.00963,42.99279],[-1.10333,43.0059],[-1.22881,43.05534],[-1.25244,43.04164],[-1.30531,43.06859],[-1.30052,43.09581],[-1.27118,43.11961],[-1.32209,43.1127],[-1.34419,43.09665],[-1.35272,43.02658],[-1.44067,43.047],[-1.47555,43.08372],[-1.41562,43.12815],[-1.3758,43.24511],[-1.40942,43.27272],[-1.45289,43.27049],[-1.50992,43.29481],[-1.55963,43.28828],[-1.57674,43.25269],[-1.61341,43.25269],[-1.63052,43.28591],[-1.62481,43.30726],[-1.69407,43.31378],[-1.73074,43.29481],[-1.7397,43.32979],[-1.75079,43.3317],[-1.75334,43.34107],[-1.77068,43.34396],[-1.78714,43.35476],[-1.78332,43.36399],[-1.79319,43.37497],[-1.77289,43.38957],[-1.81005,43.59738],[-10.14298,44.17365],[-9.14112,41.86623],[-8.87157,41.86488],[-8.81794,41.90375],[-8.75712,41.92833],[-8.74606,41.9469],[-8.7478,41.96282],[-8.69071,41.98862],[-8.6681,41.99703],[-8.65832,42.02972],[-8.64626,42.03668],[-8.63791,42.04691],[-8.59493,42.05708],[-8.58086,42.05147],[-8.54563,42.0537],[-8.5252,42.06264],[-8.52837,42.07658],[-8.48185,42.0811],[-8.44123,42.08218],[-8.42512,42.07199],[-8.40143,42.08052],[-8.38323,42.07683],[-8.36353,42.09065],[-8.33912,42.08358],[-8.32161,42.10218],[-8.29809,42.106],[-8.2732,42.12396],[-8.24681,42.13993],[-8.22406,42.1328],[-8.1986,42.15402],[-8.18947,42.13853],[-8.19406,42.12141],[-8.18178,42.06436],[-8.11729,42.08537],[-8.08847,42.05767],[-8.08796,42.01398],[-8.16232,41.9828],[-8.2185,41.91237],[-8.19551,41.87459],[-8.16944,41.87944],[-8.16455,41.81753],[-8.0961,41.81024],[-8.01136,41.83453],[-7.9804,41.87337],[-7.92336,41.8758],[-7.90707,41.92432],[-7.88751,41.92553],[-7.88055,41.84571],[-7.84188,41.88065],[-7.69848,41.90977],[-7.65774,41.88308],[-7.58603,41.87944],[-7.62188,41.83089],[-7.52737,41.83939],[-7.49803,41.87095],[-7.45566,41.86488],[-7.44759,41.84451],[-7.42854,41.83262],[-7.42864,41.80589],[-7.37092,41.85031],[-7.32366,41.8406],[-7.18677,41.88793],[-7.18549,41.97515],[-7.14115,41.98855],[-7.08574,41.97401],[-7.07596,41.94977],[-7.01078,41.94977],[-6.98144,41.9728],[-6.95537,41.96553],[-6.94396,41.94403],[-6.82174,41.94493],[-6.81196,41.99097],[-6.76959,41.98734],[-6.75004,41.94129],[-6.61967,41.94008],[-6.58544,41.96674],[-6.5447,41.94371],[-6.56752,41.88429],[-6.51374,41.8758],[-6.56426,41.74219],[-6.54633,41.68623],[-6.49907,41.65823],[-6.44204,41.68258],[-6.29863,41.66432],[-6.19128,41.57638],[-6.26777,41.48796],[-6.3306,41.37677],[-6.38553,41.38655],[-6.38551,41.35274],[-6.55937,41.24417],[-6.65046,41.24725],[-6.68286,41.21641],[-6.69711,41.1858],[-6.77319,41.13049],[-6.75655,41.10187],[-6.79241,41.05397],[-6.80942,41.03629],[-6.84781,41.02692],[-6.88843,41.03027],[-6.913,41.03922],[-6.9357,41.02888],[-6.8527,40.93958],[-6.84292,40.89771],[-6.80707,40.88047],[-6.79892,40.84842],[-6.82337,40.84472],[-6.82826,40.74603],[-6.79567,40.65955],[-6.84292,40.56801],[-6.80218,40.55067],[-6.7973,40.51723],[-6.84944,40.46394],[-6.84618,40.42177],[-6.78426,40.36468],[-6.80218,40.33239],[-6.86085,40.2976],[-6.86085,40.26776],[-7.00426,40.23169],[-7.02544,40.18564],[-7.00589,40.12087],[-6.94233,40.10716],[-6.86737,40.01986],[-6.91463,39.86618],[-6.97492,39.81488],[-7.01613,39.66877],[-7.24707,39.66576],[-7.33507,39.64569],[-7.54121,39.66717],[-7.49477,39.58794],[-7.2927,39.45847],[-7.3149,39.34857],[-7.23403,39.27579],[-7.23566,39.20132],[-7.12811,39.17101],[-7.14929,39.11287],[-7.10692,39.10275],[-7.04011,39.11919],[-6.97004,39.07619],[-6.95211,39.0243],[-7.051,38.907],[-7.03848,38.87221],[-7.26174,38.72107],[-7.265,38.61674],[-7.32529,38.44336],[-7.15581,38.27597],[-7.09389,38.17227],[-6.93418,38.21454],[-7.00375,38.01914],[-7.05966,38.01966],[-7.10366,38.04404],[-7.12648,38.00296],[-7.24544,37.98884],[-7.27314,37.90145],[-7.33441,37.81193],[-7.41981,37.75729],[-7.51759,37.56119],[-7.46878,37.47127],[-7.43974,37.38913],[-7.43227,37.25152],[-7.41854,37.23813],[-7.41133,37.20314],[-7.39769,37.16868],[-7.37282,36.96896],[-7.27694,35.93599]],[[-5.28217,36.09907],[-5.3004,36.07439],[-5.32837,36.05935],[-5.36503,36.06205],[-5.39074,36.10278],[-5.40134,36.14896],[-5.38545,36.15481],[-5.36494,36.15496],[-5.34536,36.15501],[-5.33822,36.15272],[-5.27801,36.14942],[-5.28217,36.09907]]],[[[1.99838,42.44682],[2.01564,42.45171],[1.99216,42.46208],[1.98579,42.47486],[1.99766,42.4858],[1.98916,42.49351],[1.98022,42.49569],[1.97697,42.48568],[1.97227,42.48487],[1.97003,42.48081],[1.96215,42.47854],[1.95606,42.45785],[1.96125,42.45364],[1.98378,42.44697],[1.99838,42.44682]]]]}},{type:"Feature",properties:{iso1A2:"ET",iso1A3:"ETH",iso1N3:"231",wikidata:"Q115",nameEn:"Ethiopia",groups:["014","202","002"],callingCodes:["251"]},geometry:{type:"MultiPolygon",coordinates:[[[[42.4037,12.46478],[42.2798,12.6355],[42.21469,12.75832],[42.05841,12.80912],[41.62864,13.38626],[41.25097,13.60787],[40.9167,14.11152],[40.25686,14.41445],[40.21128,14.39342],[40.14649,14.53969],[40.07236,14.54264],[39.9443,14.41024],[39.76632,14.54264],[39.58182,14.60987],[39.50585,14.55735],[39.52756,14.49011],[39.37685,14.54402],[39.2519,14.40393],[39.2302,14.44598],[39.26927,14.48801],[39.23888,14.56365],[39.19547,14.56996],[39.14772,14.61827],[39.16074,14.65187],[39.02834,14.63717],[38.98058,14.54895],[38.78306,14.4754],[38.45748,14.41445],[38.3533,14.51323],[38.25562,14.67287],[38.0364,14.72745],[37.91287,14.89447],[37.528,14.18413],[37.47319,14.2149],[37.3106,14.44657],[37.13206,14.40746],[37.09486,14.27155],[37.01622,14.2561],[36.85787,14.32201],[36.63364,14.31172],[36.55659,14.28237],[36.56536,14.26177],[36.54376,14.25597],[36.44653,13.95666],[36.48824,13.83954],[36.38993,13.56459],[36.24545,13.36759],[36.13374,12.92665],[36.16651,12.88019],[36.14268,12.70879],[36.01458,12.72478],[35.70476,12.67101],[35.24302,11.91132],[35.11492,11.85156],[35.05832,11.71158],[35.09556,11.56278],[34.95704,11.24448],[35.01215,11.19626],[34.93172,10.95946],[34.97789,10.91559],[34.97491,10.86147],[34.86916,10.78832],[34.86618,10.74588],[34.77532,10.69027],[34.77383,10.74588],[34.59062,10.89072],[34.4372,10.781],[34.2823,10.53508],[34.34783,10.23914],[34.32102,10.11599],[34.22718,10.02506],[34.20484,9.9033],[34.13186,9.7492],[34.08717,9.55243],[34.10229,9.50238],[34.14304,9.04654],[34.14453,8.60204],[34.01346,8.50041],[33.89579,8.4842],[33.87195,8.41938],[33.71407,8.3678],[33.66938,8.44442],[33.54575,8.47094],[33.3119,8.45474],[33.19721,8.40317],[33.1853,8.29264],[33.18083,8.13047],[33.08401,8.05822],[33.0006,7.90333],[33.04944,7.78989],[33.24637,7.77939],[33.32531,7.71297],[33.44745,7.7543],[33.71407,7.65983],[33.87642,7.5491],[34.02984,7.36449],[34.03878,7.27437],[34.01495,7.25664],[34.19369,7.12807],[34.19369,7.04382],[34.35753,6.91963],[34.47669,6.91076],[34.53925,6.82794],[34.53776,6.74808],[34.65096,6.72589],[34.77459,6.5957],[34.87736,6.60161],[35.01738,6.46991],[34.96227,6.26415],[35.00546,5.89387],[35.12611,5.68937],[35.13058,5.62118],[35.31188,5.50106],[35.29938,5.34042],[35.50792,5.42431],[35.8576,5.33413],[35.81968,5.10757],[35.82118,4.77382],[35.9419,4.61933],[35.95449,4.53244],[36.03924,4.44406],[36.84474,4.44518],[37.07724,4.33503],[38.14168,3.62487],[38.45812,3.60445],[38.52336,3.62551],[38.91938,3.51198],[39.07736,3.5267],[39.19954,3.47834],[39.49444,3.45521],[39.51551,3.40895],[39.55132,3.39634],[39.58339,3.47434],[39.76808,3.67058],[39.86043,3.86974],[40.77498,4.27683],[41.1754,3.94079],[41.89488,3.97375],[42.07619,4.17667],[42.55853,4.20518],[42.84526,4.28357],[42.97746,4.44032],[43.04177,4.57923],[43.40263,4.79289],[44.02436,4.9451],[44.98104,4.91821],[47.97917,8.00124],[47.92477,8.00111],[46.99339,7.9989],[44.19222,8.93028],[43.32613,9.59205],[43.23518,9.84605],[43.0937,9.90579],[42.87643,10.18441],[42.69452,10.62672],[42.95776,10.98533],[42.79037,10.98493],[42.75111,11.06992],[42.62989,11.09711],[42.42669,10.98493],[42.13691,10.97586],[42.06302,10.92599],[41.80056,10.97127],[41.8096,11.33606],[41.77727,11.49902],[41.82878,11.72361],[41.95461,11.81157],[42.4037,12.46478]]]]}},{type:"Feature",properties:{iso1A2:"EU",iso1A3:"EUE",wikidata:"Q458",nameEn:"European Union",level:"union",isoStatus:"excRes"},geometry:null},{type:"Feature",properties:{iso1A2:"FI",iso1A3:"FIN",iso1N3:"246",wikidata:"Q33",nameEn:"Finland",aliases:["SF"],groups:["EU","154","150"],callingCodes:["358"]},geometry:{type:"MultiPolygon",coordinates:[[[[29.12697,69.69193],[28.36883,69.81658],[28.32849,69.88605],[27.97558,69.99671],[27.95542,70.0965],[27.57226,70.06215],[27.05802,69.92069],[26.64461,69.96565],[26.40261,69.91377],[25.96904,69.68397],[25.69679,69.27039],[25.75729,68.99383],[25.61613,68.89602],[25.42455,68.90328],[25.12206,68.78684],[25.10189,68.63307],[24.93048,68.61102],[24.90023,68.55579],[24.74898,68.65143],[24.18432,68.73936],[24.02299,68.81601],[23.781,68.84514],[23.68017,68.70276],[23.13064,68.64684],[22.53321,68.74393],[22.38367,68.71561],[22.27276,68.89514],[21.63833,69.27485],[21.27827,69.31281],[21.00732,69.22755],[20.98641,69.18809],[21.11099,69.10291],[21.05775,69.0356],[20.72171,69.11874],[20.55258,69.06069],[20.78802,69.03087],[20.91658,68.96764],[20.85104,68.93142],[20.90649,68.89696],[21.03001,68.88969],[22.00429,68.50692],[22.73028,68.40881],[23.10336,68.26551],[23.15377,68.14759],[23.26469,68.15134],[23.40081,68.05545],[23.65793,67.9497],[23.45627,67.85297],[23.54701,67.59306],[23.39577,67.46974],[23.75372,67.43688],[23.75372,67.29914],[23.54701,67.25435],[23.58735,67.20752],[23.56214,67.17038],[23.98563,66.84149],[23.98059,66.79585],[23.89488,66.772],[23.85959,66.56434],[23.63776,66.43568],[23.67591,66.3862],[23.64982,66.30603],[23.71339,66.21299],[23.90497,66.15802],[24.15791,65.85385],[24.14798,65.83466],[24.15107,65.81427],[24.14112,65.39731],[20.15877,63.06556],[19.23413,60.61414],[20.96741,60.71528],[21.15143,60.54555],[21.08159,60.20167],[21.02509,60.12142],[21.35468,59.67511],[20.5104,59.15546],[26.32936,60.00121],[27.44953,60.22766],[27.71177,60.3893],[27.77352,60.52722],[28.47974,60.93365],[28.82816,61.1233],[29.01829,61.17448],[31.10136,62.43042],[31.38369,62.66284],[31.58535,62.91642],[31.29294,63.09035],[31.23244,63.22239],[30.49637,63.46666],[29.98213,63.75795],[30.25437,63.83364],[30.55687,64.09036],[30.4762,64.25728],[30.06279,64.35782],[30.01238,64.57513],[30.12329,64.64862],[30.05271,64.79072],[29.68972,64.80789],[29.61914,65.05993],[29.84096,65.1109],[29.8813,65.22101],[29.61914,65.23791],[29.68972,65.31803],[29.84096,65.56945],[29.74013,65.64025],[29.97205,65.70256],[30.16363,65.66935],[29.91155,66.13863],[28.9839,66.94139],[29.91155,67.51507],[30.02041,67.67523],[29.66955,67.79872],[29.34179,68.06655],[28.62982,68.19816],[28.43941,68.53366],[28.78224,68.86696],[28.45957,68.91417],[28.91738,69.04774],[28.81248,69.11997],[28.8629,69.22395],[29.31664,69.47994],[29.12697,69.69193]]]]}},{type:"Feature",properties:{iso1A2:"FJ",iso1A3:"FJI",iso1N3:"242",wikidata:"Q712",nameEn:"Fiji",groups:["054","009"],driveSide:"left",callingCodes:["679"]},geometry:{type:"MultiPolygon",coordinates:[[[[174,-22.5],[179.99999,-22.5],[179.99999,-11.5],[174,-11.5],[174,-22.5]]],[[[-178.60161,-14.95666],[-180,-14.96041],[-180,-22.90585],[-176.74538,-22.89767],[-176.76826,-14.95183],[-178.60161,-14.95666]]]]}},{type:"Feature",properties:{iso1A2:"FK",iso1A3:"FLK",iso1N3:"238",wikidata:"Q9648",nameEn:"Falkland Islands",country:"GB",groups:["005","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["500"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.67376,-55.11859],[-54.56126,-51.26248],[-61.26735,-50.63919],[-63.67376,-55.11859]]]]}},{type:"Feature",properties:{iso1A2:"FM",iso1A3:"FSM",iso1N3:"583",wikidata:"Q702",nameEn:"Federated States of Micronesia",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["691"]},geometry:{type:"MultiPolygon",coordinates:[[[[136.04605,12.45908],[136.27107,6.73747],[156.88247,-1.39237],[165.35175,6.367],[159.04653,10.59067],[136.04605,12.45908]]]]}},{type:"Feature",properties:{iso1A2:"FO",iso1A3:"FRO",iso1N3:"234",wikidata:"Q4628",nameEn:"Faroe Islands",country:"DK",groups:["154","150"],callingCodes:["298"]},geometry:{type:"MultiPolygon",coordinates:[[[[-8.51774,62.35338],[-6.51083,60.95272],[-5.70102,62.77194],[-8.51774,62.35338]]]]}},{type:"Feature",properties:{iso1A2:"FR",iso1A3:"FRA",iso1N3:"250",wikidata:"Q142",nameEn:"France",groups:["EU","155","150"],callingCodes:["33"]},geometry:null},{type:"Feature",properties:{iso1A2:"FX",iso1A3:"FXX",iso1N3:"249",wikidata:"Q212429",nameEn:"Metropolitan France",country:"FR",groups:["EU","155","150"],isoStatus:"excRes",callingCodes:["33"]},geometry:{type:"MultiPolygon",coordinates:[[[[2.55904,51.07014],[2.18458,51.52087],[1.17405,50.74239],[-2.02963,49.91866],[-2.09454,49.46288],[-1.83944,49.23037],[-2.00491,48.86706],[-2.65349,49.15373],[-6.13339,48.73907],[-1.81005,43.59738],[-1.77289,43.38957],[-1.79319,43.37497],[-1.78332,43.36399],[-1.78714,43.35476],[-1.77068,43.34396],[-1.75334,43.34107],[-1.75079,43.3317],[-1.7397,43.32979],[-1.73074,43.29481],[-1.69407,43.31378],[-1.62481,43.30726],[-1.63052,43.28591],[-1.61341,43.25269],[-1.57674,43.25269],[-1.55963,43.28828],[-1.50992,43.29481],[-1.45289,43.27049],[-1.40942,43.27272],[-1.3758,43.24511],[-1.41562,43.12815],[-1.47555,43.08372],[-1.44067,43.047],[-1.35272,43.02658],[-1.34419,43.09665],[-1.32209,43.1127],[-1.27118,43.11961],[-1.30052,43.09581],[-1.30531,43.06859],[-1.25244,43.04164],[-1.22881,43.05534],[-1.10333,43.0059],[-1.00963,42.99279],[-0.97133,42.96239],[-0.81652,42.95166],[-0.75478,42.96916],[-0.72037,42.92541],[-0.73422,42.91228],[-0.72608,42.89318],[-0.69837,42.87945],[-0.67637,42.88303],[-0.55497,42.77846],[-0.50863,42.82713],[-0.44334,42.79939],[-0.41319,42.80776],[-0.38833,42.80132],[-0.3122,42.84788],[-0.17939,42.78974],[-0.16141,42.79535],[-0.10519,42.72761],[-0.02468,42.68513],[0.17569,42.73424],[0.25336,42.7174],[0.29407,42.67431],[0.36251,42.72282],[0.40214,42.69779],[0.67873,42.69458],[0.65421,42.75872],[0.66121,42.84021],[0.711,42.86372],[0.93089,42.79154],[0.96166,42.80629],[0.98292,42.78754],[1.0804,42.78569],[1.15928,42.71407],[1.35562,42.71944],[1.44197,42.60217],[1.47986,42.61346],[1.46718,42.63296],[1.48043,42.65203],[1.50867,42.64483],[1.55418,42.65669],[1.60085,42.62703],[1.63485,42.62957],[1.6625,42.61982],[1.68267,42.62533],[1.73452,42.61515],[1.72588,42.59098],[1.7858,42.57698],[1.73683,42.55492],[1.72515,42.50338],[1.76335,42.48863],[1.83037,42.48395],[1.88853,42.4501],[1.93663,42.45439],[1.94292,42.44316],[1.94061,42.43333],[1.94084,42.43039],[1.9574,42.42401],[1.96482,42.37787],[2.00488,42.35399],[2.06241,42.35906],[2.11621,42.38393],[2.12789,42.41291],[2.16599,42.42314],[2.20578,42.41633],[2.25551,42.43757],[2.38504,42.39977],[2.43299,42.39423],[2.43508,42.37568],[2.48457,42.33933],[2.54382,42.33406],[2.55516,42.35351],[2.57934,42.35808],[2.6747,42.33974],[2.65311,42.38771],[2.72056,42.42298],[2.75497,42.42578],[2.77464,42.41046],[2.84335,42.45724],[2.85675,42.45444],[2.86983,42.46843],[2.88413,42.45938],[2.92107,42.4573],[2.94283,42.48174],[2.96518,42.46692],[3.03734,42.47363],[3.08167,42.42748],[3.10027,42.42621],[3.11379,42.43646],[3.17156,42.43545],[3.4481,42.4358],[7.60802,41.05927],[10.09675,41.44089],[9.56115,43.20816],[7.50102,43.51859],[7.42422,43.72209],[7.40903,43.7296],[7.41113,43.73156],[7.41291,43.73168],[7.41298,43.73311],[7.41233,43.73439],[7.42062,43.73977],[7.42299,43.74176],[7.42443,43.74087],[7.42809,43.74396],[7.43013,43.74895],[7.43624,43.75014],[7.43708,43.75197],[7.4389,43.75151],[7.4379,43.74963],[7.47823,43.73341],[7.53006,43.78405],[7.50423,43.84345],[7.49355,43.86551],[7.51162,43.88301],[7.56075,43.89932],[7.56858,43.94506],[7.60771,43.95772],[7.65266,43.9763],[7.66848,43.99943],[7.6597,44.03009],[7.72508,44.07578],[7.66878,44.12795],[7.68694,44.17487],[7.63245,44.17877],[7.62155,44.14881],[7.36364,44.11882],[7.34547,44.14359],[7.27827,44.1462],[7.16929,44.20352],[7.00764,44.23736],[6.98221,44.28289],[6.89171,44.36637],[6.88784,44.42043],[6.94504,44.43112],[6.86233,44.49834],[6.85507,44.53072],[6.96042,44.62129],[6.95133,44.66264],[7.00582,44.69364],[7.07484,44.68073],[7.00401,44.78782],[7.02217,44.82519],[6.93499,44.8664],[6.90774,44.84322],[6.75518,44.89915],[6.74519,44.93661],[6.74791,45.01939],[6.66981,45.02324],[6.62803,45.11175],[6.7697,45.16044],[6.85144,45.13226],[6.96706,45.20841],[7.07074,45.21228],[7.13115,45.25386],[7.10572,45.32924],[7.18019,45.40071],[7.00037,45.509],[6.98948,45.63869],[6.80785,45.71864],[6.80785,45.83265],[6.95315,45.85163],[7.04151,45.92435],[7.00946,45.9944],[6.93862,46.06502],[6.87868,46.03855],[6.89321,46.12548],[6.78968,46.14058],[6.86052,46.28512],[6.77152,46.34784],[6.8024,46.39171],[6.82312,46.42661],[6.53358,46.45431],[6.25432,46.3632],[6.21981,46.31304],[6.24826,46.30175],[6.25137,46.29014],[6.23775,46.27822],[6.24952,46.26255],[6.26749,46.24745],[6.29474,46.26221],[6.31041,46.24417],[6.29663,46.22688],[6.27694,46.21566],[6.26007,46.21165],[6.24821,46.20531],[6.23913,46.20511],[6.23544,46.20714],[6.22175,46.20045],[6.22222,46.19888],[6.21844,46.19837],[6.21603,46.19507],[6.21273,46.19409],[6.21114,46.1927],[6.20539,46.19163],[6.19807,46.18369],[6.19552,46.18401],[6.18707,46.17999],[6.18871,46.16644],[6.18116,46.16187],[6.15305,46.15194],[6.13397,46.1406],[6.09926,46.14373],[6.09199,46.15191],[6.07491,46.14879],[6.05203,46.15191],[6.04564,46.14031],[6.03614,46.13712],[6.01791,46.14228],[5.9871,46.14499],[5.97893,46.13303],[5.95781,46.12925],[5.9641,46.14412],[5.97508,46.15863],[5.98188,46.17392],[5.98846,46.17046],[5.99573,46.18587],[5.96515,46.19638],[5.97542,46.21525],[6.02461,46.23313],[6.03342,46.2383],[6.04602,46.23127],[6.05029,46.23518],[6.0633,46.24583],[6.07072,46.24085],[6.08563,46.24651],[6.10071,46.23772],[6.12446,46.25059],[6.11926,46.2634],[6.1013,46.28512],[6.11697,46.29547],[6.1198,46.31157],[6.13876,46.33844],[6.15738,46.3491],[6.16987,46.36759],[6.15985,46.37721],[6.15016,46.3778],[6.09926,46.40768],[6.06407,46.41676],[6.08427,46.44305],[6.07269,46.46244],[6.1567,46.54402],[6.11084,46.57649],[6.27135,46.68251],[6.38351,46.73171],[6.45209,46.77502],[6.43216,46.80336],[6.46456,46.88865],[6.43341,46.92703],[6.71531,47.0494],[6.68823,47.06616],[6.76788,47.1208],[6.8489,47.15933],[6.9508,47.24338],[6.95108,47.26428],[6.94316,47.28747],[7.05305,47.33304],[7.0564,47.35134],[7.03125,47.36996],[6.87959,47.35335],[6.88542,47.37262],[6.93744,47.40714],[6.93953,47.43388],[7.0024,47.45264],[6.98425,47.49432],[7.0231,47.50522],[7.07425,47.48863],[7.12781,47.50371],[7.16249,47.49025],[7.19583,47.49455],[7.17026,47.44312],[7.24669,47.4205],[7.33526,47.44186],[7.35603,47.43432],[7.40308,47.43638],[7.43088,47.45846],[7.4462,47.46264],[7.4583,47.47216],[7.42923,47.48628],[7.43356,47.49712],[7.47534,47.47932],[7.51076,47.49651],[7.49804,47.51798],[7.5229,47.51644],[7.53199,47.5284],[7.51904,47.53515],[7.50588,47.52856],[7.49691,47.53821],[7.50873,47.54546],[7.51723,47.54578],[7.52831,47.55347],[7.53634,47.55553],[7.55652,47.56779],[7.55689,47.57232],[7.56548,47.57617],[7.56684,47.57785],[7.58386,47.57536],[7.58945,47.59017],[7.59301,47.60058],[7.58851,47.60794],[7.57423,47.61628],[7.5591,47.63849],[7.53384,47.65115],[7.52067,47.66437],[7.51915,47.68335],[7.51266,47.70197],[7.53722,47.71635],[7.54761,47.72912],[7.52921,47.77747],[7.55673,47.87371],[7.62302,47.97898],[7.56966,48.03265],[7.57137,48.12292],[7.6648,48.22219],[7.69022,48.30018],[7.74562,48.32736],[7.73109,48.39192],[7.76833,48.48945],[7.80647,48.51239],[7.80167,48.54758],[7.80057,48.5857],[7.84098,48.64217],[7.89002,48.66317],[7.96812,48.72491],[7.96994,48.75606],[8.01534,48.76085],[8.0326,48.79017],[8.06802,48.78957],[8.10253,48.81829],[8.12813,48.87985],[8.19989,48.95825],[8.20031,48.95856],[8.22604,48.97352],[8.14189,48.97833],[7.97783,49.03161],[7.93641,49.05544],[7.86386,49.03499],[7.79557,49.06583],[7.75948,49.04562],[7.63618,49.05428],[7.62575,49.07654],[7.56416,49.08136],[7.53012,49.09818],[7.49172,49.13915],[7.49473,49.17],[7.44455,49.16765],[7.44052,49.18354],[7.3662,49.17308],[7.35995,49.14399],[7.3195,49.14231],[7.29514,49.11426],[7.23473,49.12971],[7.1593,49.1204],[7.1358,49.1282],[7.12504,49.14253],[7.10384,49.13787],[7.10715,49.15631],[7.07859,49.15031],[7.09007,49.13094],[7.07162,49.1255],[7.06642,49.11415],[7.05548,49.11185],[7.04843,49.11422],[7.04409,49.12123],[7.04662,49.13724],[7.03178,49.15734],[7.0274,49.17042],[7.03459,49.19096],[7.01318,49.19018],[6.97273,49.2099],[6.95963,49.203],[6.94028,49.21641],[6.93831,49.2223],[6.91875,49.22261],[6.89298,49.20863],[6.85939,49.22376],[6.83555,49.21249],[6.85119,49.20038],[6.85016,49.19354],[6.86225,49.18185],[6.84703,49.15734],[6.83385,49.15162],[6.78265,49.16793],[6.73765,49.16375],[6.71137,49.18808],[6.73256,49.20486],[6.71843,49.2208],[6.69274,49.21661],[6.66583,49.28065],[6.60186,49.31055],[6.572,49.35027],[6.58807,49.35358],[6.60091,49.36864],[6.533,49.40748],[6.55404,49.42464],[6.42432,49.47683],[6.40274,49.46546],[6.39168,49.4667],[6.38352,49.46463],[6.36778,49.46937],[6.3687,49.4593],[6.28818,49.48465],[6.27875,49.503],[6.25029,49.50609],[6.2409,49.51408],[6.19543,49.50536],[6.17386,49.50934],[6.15366,49.50226],[6.16115,49.49297],[6.14321,49.48796],[6.12814,49.49365],[6.12346,49.4735],[6.10325,49.4707],[6.09845,49.46351],[6.10072,49.45268],[6.08373,49.45594],[6.07887,49.46399],[6.05553,49.46663],[6.04176,49.44801],[6.02743,49.44845],[6.02648,49.45451],[5.97693,49.45513],[5.96876,49.49053],[5.94224,49.49608],[5.94128,49.50034],[5.86571,49.50015],[5.83389,49.52152],[5.83467,49.52717],[5.84466,49.53027],[5.83648,49.5425],[5.81664,49.53775],[5.80871,49.5425],[5.81838,49.54777],[5.79195,49.55228],[5.77435,49.56298],[5.7577,49.55915],[5.75649,49.54321],[5.64505,49.55146],[5.60909,49.51228],[5.55001,49.52729],[5.46541,49.49825],[5.46734,49.52648],[5.43713,49.5707],[5.3974,49.61596],[5.34837,49.62889],[5.33851,49.61599],[5.3137,49.61225],[5.30214,49.63055],[5.33039,49.6555],[5.31465,49.66846],[5.26232,49.69456],[5.14545,49.70287],[5.09249,49.76193],[4.96714,49.79872],[4.85464,49.78995],[4.86965,49.82271],[4.85134,49.86457],[4.88529,49.9236],[4.78827,49.95609],[4.8382,50.06738],[4.88602,50.15182],[4.83279,50.15331],[4.82438,50.16878],[4.75237,50.11314],[4.70064,50.09384],[4.68695,49.99685],[4.5414,49.96911],[4.51098,49.94659],[4.43488,49.94122],[4.35051,49.95315],[4.31963,49.97043],[4.20532,49.95803],[4.14239,49.98034],[4.13508,50.01976],[4.16294,50.04719],[4.23101,50.06945],[4.20147,50.13535],[4.13561,50.13078],[4.16014,50.19239],[4.15524,50.21103],[4.21945,50.25539],[4.20651,50.27333],[4.17861,50.27443],[4.17347,50.28838],[4.15524,50.2833],[4.16808,50.25786],[4.13665,50.25609],[4.11954,50.30425],[4.10957,50.30234],[4.10237,50.31247],[4.0689,50.3254],[4.0268,50.35793],[3.96771,50.34989],[3.90781,50.32814],[3.84314,50.35219],[3.73911,50.34809],[3.70987,50.3191],[3.71009,50.30305],[3.66976,50.34563],[3.65709,50.36873],[3.67262,50.38663],[3.67494,50.40239],[3.66153,50.45165],[3.64426,50.46275],[3.61014,50.49568],[3.58361,50.49049],[3.5683,50.50192],[3.49509,50.48885],[3.51564,50.5256],[3.47385,50.53397],[3.44629,50.51009],[3.37693,50.49538],[3.28575,50.52724],[3.2729,50.60718],[3.23951,50.6585],[3.264,50.67668],[3.2536,50.68977],[3.26141,50.69151],[3.26063,50.70086],[3.24593,50.71389],[3.22042,50.71019],[3.20845,50.71662],[3.19017,50.72569],[3.20064,50.73547],[3.18811,50.74025],[3.18339,50.74981],[3.16476,50.76843],[3.15017,50.79031],[3.1257,50.78603],[3.11987,50.79188],[3.11206,50.79416],[3.10614,50.78303],[3.09163,50.77717],[3.04314,50.77674],[3.00537,50.76588],[2.96778,50.75242],[2.95019,50.75138],[2.90873,50.702],[2.91036,50.6939],[2.90069,50.69263],[2.88504,50.70656],[2.87937,50.70298],[2.86985,50.7033],[2.8483,50.72276],[2.81056,50.71773],[2.71165,50.81295],[2.63331,50.81457],[2.59093,50.91751],[2.63074,50.94746],[2.57551,51.00326],[2.55904,51.07014]]]]}},{type:"Feature",properties:{iso1A2:"GA",iso1A3:"GAB",iso1N3:"266",wikidata:"Q1000",nameEn:"Gabon",groups:["017","202","002"],callingCodes:["241"]},geometry:{type:"MultiPolygon",coordinates:[[[[13.29457,2.16106],[13.28534,2.25716],[11.37116,2.29975],[11.3561,2.17217],[11.35307,1.00251],[9.79648,1.0019],[9.78058,1.03996],[9.76085,1.05949],[9.73014,1.06721],[9.68638,1.06836],[9.66092,1.05865],[9.62096,1.03039],[9.54793,1.0185],[9.51998,0.96418],[9.35563,0.84865],[7.24416,-0.64092],[10.75913,-4.39519],[11.12647,-3.94169],[11.22301,-3.69888],[11.48764,-3.51089],[11.57949,-3.52798],[11.68608,-3.68942],[11.87083,-3.71571],[11.92719,-3.62768],[11.8318,-3.5812],[11.96554,-3.30267],[11.70227,-3.17465],[11.70558,-3.0773],[11.80365,-3.00424],[11.64798,-2.81146],[11.5359,-2.85654],[11.64487,-2.61865],[11.57637,-2.33379],[11.74605,-2.39936],[11.96866,-2.33559],[12.04895,-2.41704],[12.47925,-2.32626],[12.44656,-1.92025],[12.61312,-1.8129],[12.82172,-1.91091],[13.02759,-2.33098],[13.47977,-2.43224],[13.75884,-2.09293],[13.92073,-2.35581],[13.85846,-2.46935],[14.10442,-2.49268],[14.23829,-2.33715],[14.16202,-2.23916],[14.23518,-2.15671],[14.25932,-1.97624],[14.41838,-1.89412],[14.52569,-0.57818],[14.41887,-0.44799],[14.2165,-0.38261],[14.06862,-0.20826],[13.90632,-0.2287],[13.88648,0.26652],[14.10909,0.58563],[14.26066,0.57255],[14.48179,0.9152],[14.25186,1.39842],[13.89582,1.4261],[13.15519,1.23368],[13.25447,1.32339],[13.13461,1.57238],[13.29457,2.16106]]]]}},{type:"Feature",properties:{iso1A2:"GB",iso1A3:"GBR",iso1N3:"826",wikidata:"Q145",nameEn:"United Kingdom",aliases:["UK","Britain","Great Britain"],groups:["154","150"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44"]},geometry:{type:"MultiPolygon",coordinates:[[[[-5.83481,53.87749],[-4.1819,54.57861],[-3.64906,54.12723],[-5.37267,53.63269],[-5.79914,52.03902],[-7.74976,48.64773],[1.17405,50.74239],[2.18458,51.52087],[2.56575,51.85301],[-0.3751,61.32236],[-14.78497,57.60709],[-7.93366,55.84142],[-6.79943,55.54107],[-6.71944,55.27952],[-6.9734,55.19878],[-7.2471,55.06933],[-7.34464,55.04688],[-7.4033,55.00391],[-7.40004,54.94498],[-7.44404,54.9403],[-7.4473,54.87003],[-7.47626,54.83084],[-7.54508,54.79401],[-7.54671,54.74606],[-7.64449,54.75265],[-7.75041,54.7103],[-7.83352,54.73854],[-7.93293,54.66603],[-7.70315,54.62077],[-7.8596,54.53671],[-7.99812,54.54427],[-8.04538,54.48941],[-8.179,54.46763],[-8.04555,54.36292],[-7.87101,54.29299],[-7.8596,54.21779],[-7.81397,54.20159],[-7.69501,54.20731],[-7.55812,54.12239],[-7.4799,54.12239],[-7.44567,54.1539],[-7.32834,54.11475],[-7.30553,54.11869],[-7.34005,54.14698],[-7.29157,54.17191],[-7.28017,54.16714],[-7.29687,54.1354],[-7.29493,54.12013],[-7.26316,54.13863],[-7.25012,54.20063],[-7.14908,54.22732],[-7.19145,54.31296],[-7.02034,54.4212],[-6.87775,54.34682],[-6.85179,54.29176],[-6.81583,54.22791],[-6.74575,54.18788],[-6.70175,54.20218],[-6.6382,54.17071],[-6.66264,54.0666],[-6.62842,54.03503],[-6.47849,54.06947],[-6.36605,54.07234],[-6.36279,54.11248],[-6.32694,54.09337],[-6.29003,54.11278],[-6.26218,54.09785],[-5.83481,53.87749]]],[[[33.70575,34.97947],[33.83531,34.73974],[33.98684,34.76642],[33.90075,34.96623],[33.86432,34.97592],[33.84811,34.97075],[33.83505,34.98108],[33.85621,34.98956],[33.85891,35.001],[33.85216,35.00579],[33.84045,35.00616],[33.82875,35.01685],[33.83055,35.02865],[33.81524,35.04192],[33.8012,35.04786],[33.82051,35.0667],[33.8355,35.05777],[33.85261,35.0574],[33.88367,35.07877],[33.89485,35.06873],[33.90247,35.07686],[33.91299,35.07579],[33.91789,35.08688],[33.89853,35.11377],[33.88737,35.11408],[33.88943,35.12007],[33.88561,35.12449],[33.87224,35.12293],[33.87622,35.10457],[33.87097,35.09389],[33.87479,35.08881],[33.8541,35.07201],[33.84168,35.06823],[33.82067,35.07826],[33.78581,35.05104],[33.76106,35.04253],[33.73824,35.05321],[33.71482,35.03722],[33.70209,35.04882],[33.7161,35.07279],[33.70861,35.07644],[33.69095,35.06237],[33.68474,35.06602],[33.67742,35.05963],[33.67678,35.03866],[33.69938,35.03123],[33.69731,35.01754],[33.71514,35.00294],[33.70639,34.99303],[33.70575,34.97947]],[[33.77312,34.9976],[33.77553,34.99518],[33.78516,34.99582],[33.79191,34.98914],[33.78917,34.98854],[33.78571,34.98951],[33.78318,34.98699],[33.78149,34.98854],[33.77843,34.988],[33.7778,34.98981],[33.76738,34.99188],[33.76605,34.99543],[33.75682,34.99916],[33.75994,35.00113],[33.77312,34.9976]],[[33.74144,35.01053],[33.7343,35.01178],[33.73781,35.02181],[33.74265,35.02329],[33.74983,35.02274],[33.7492,35.01319],[33.74144,35.01053]]],[[[32.86014,34.70585],[32.82717,34.70622],[32.79433,34.67883],[32.76136,34.68318],[32.75515,34.64985],[32.74412,34.43926],[33.26744,34.49942],[33.0138,34.64424],[32.96968,34.64046],[32.96718,34.63446],[32.95891,34.62919],[32.95323,34.64075],[32.95471,34.64528],[32.94976,34.65204],[32.94796,34.6587],[32.95325,34.66462],[32.97079,34.66112],[32.97736,34.65277],[32.99014,34.65518],[32.98668,34.67268],[32.99135,34.68061],[32.95539,34.68471],[32.94683,34.67907],[32.94379,34.67111],[32.93693,34.67027],[32.93449,34.66241],[32.92807,34.66736],[32.93043,34.67091],[32.91398,34.67343],[32.9068,34.66102],[32.86167,34.68734],[32.86014,34.70585]]]]}},{type:"Feature",properties:{iso1A2:"GD",iso1A3:"GRD",iso1N3:"308",wikidata:"Q769",nameEn:"Grenada",aliases:["WG"],groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 473"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.14806,11.87638],[-61.57265,11.65795],[-61.13395,12.51526],[-61.38256,12.52991],[-61.73897,12.61191],[-62.14806,11.87638]]]]}},{type:"Feature",properties:{iso1A2:"GE",iso1A3:"GEO",iso1N3:"268",wikidata:"Q230",nameEn:"Georgia",groups:["145","142"],callingCodes:["995"]},geometry:{type:"MultiPolygon",coordinates:[[[[46.42738,41.91323],[45.61676,42.20768],[45.78692,42.48358],[45.36501,42.55268],[45.15318,42.70598],[44.88754,42.74934],[44.80941,42.61277],[44.70002,42.74679],[44.54202,42.75699],[43.95517,42.55396],[43.73119,42.62043],[43.81453,42.74297],[43.0419,43.02413],[43.03322,43.08883],[42.75889,43.19651],[42.66667,43.13917],[42.40563,43.23226],[41.64935,43.22331],[40.65957,43.56212],[40.10657,43.57344],[40.04445,43.47776],[40.03312,43.44262],[40.01007,43.42411],[40.01552,43.42025],[40.00853,43.40578],[40.0078,43.38551],[39.81147,43.06294],[40.89217,41.72528],[41.54366,41.52185],[41.7148,41.4932],[41.7124,41.47417],[41.81939,41.43621],[41.95134,41.52466],[42.26387,41.49346],[42.51772,41.43606],[42.59202,41.58183],[42.72794,41.59714],[42.84471,41.58912],[42.78995,41.50126],[42.84899,41.47265],[42.8785,41.50516],[43.02956,41.37891],[43.21707,41.30331],[43.13373,41.25503],[43.1945,41.25242],[43.23096,41.17536],[43.36118,41.2028],[43.44973,41.17666],[43.4717,41.12611],[43.67712,41.13398],[43.74717,41.1117],[43.84835,41.16329],[44.16591,41.19141],[44.18148,41.24644],[44.32139,41.2079],[44.34337,41.20312],[44.34417,41.2382],[44.46791,41.18204],[44.59322,41.1933],[44.61462,41.24018],[44.72814,41.20338],[44.82084,41.21513],[44.87887,41.20195],[44.89911,41.21366],[44.84358,41.23088],[44.81749,41.23488],[44.80053,41.25949],[44.81437,41.30371],[44.93493,41.25685],[45.0133,41.29747],[45.09867,41.34065],[45.1797,41.42231],[45.26285,41.46433],[45.31352,41.47168],[45.4006,41.42402],[45.45973,41.45898],[45.68389,41.3539],[45.71035,41.36208],[45.75705,41.35157],[45.69946,41.29545],[45.80842,41.2229],[45.95786,41.17956],[46.13221,41.19479],[46.27698,41.19011],[46.37661,41.10805],[46.456,41.09984],[46.48558,41.0576],[46.55096,41.1104],[46.63969,41.09515],[46.66148,41.20533],[46.72375,41.28609],[46.63658,41.37727],[46.4669,41.43331],[46.40307,41.48464],[46.33925,41.4963],[46.29794,41.5724],[46.34126,41.57454],[46.34039,41.5947],[46.3253,41.60912],[46.28182,41.60089],[46.26531,41.63339],[46.24429,41.59883],[46.19759,41.62327],[46.17891,41.72094],[46.20538,41.77205],[46.23962,41.75811],[46.30863,41.79133],[46.3984,41.84399],[46.42738,41.91323]]]]}},{type:"Feature",properties:{iso1A2:"GF",iso1A3:"GUF",iso1N3:"254",wikidata:"Q3769",nameEn:"French Guiana",country:"FR",groups:["EU","005","419","019"],callingCodes:["594"]},geometry:{type:"MultiPolygon",coordinates:[[[[-51.35485,4.8383],[-53.7094,6.2264],[-54.01074,5.68785],[-54.01877,5.52789],[-54.26916,5.26909],[-54.4717,4.91964],[-54.38444,4.13222],[-54.19367,3.84387],[-54.05128,3.63557],[-53.98914,3.627],[-53.9849,3.58697],[-54.28534,2.67798],[-54.42864,2.42442],[-54.6084,2.32856],[-54.16286,2.10779],[-53.78743,2.34412],[-52.96539,2.1881],[-52.6906,2.37298],[-52.31787,3.17896],[-51.85573,3.83427],[-51.82312,3.85825],[-51.79599,3.89336],[-51.61983,4.14596],[-51.63798,4.51394],[-51.35485,4.8383]]]]}},{type:"Feature",properties:{iso1A2:"GG",iso1A3:"GGY",iso1N3:"831",wikidata:"Q25230",nameEn:"Guernsey",country:"GB",groups:["830","154","150"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44 01481"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.65349,49.15373],[-2.36485,49.48223],[-2.09454,49.46288],[-2.02963,49.91866],[-3.28154,49.57329],[-2.65349,49.15373]]]]}},{type:"Feature",properties:{iso1A2:"GH",iso1A3:"GHA",iso1N3:"288",wikidata:"Q117",nameEn:"Ghana",groups:["011","202","002"],callingCodes:["233"]},geometry:{type:"MultiPolygon",coordinates:[[[[-0.13493,11.14075],[-0.27374,11.17157],[-0.28566,11.12713],[-0.35955,11.07801],[-0.38219,11.12596],[-0.42391,11.11661],[-0.44298,11.04292],[-0.61937,10.91305],[-0.67143,10.99811],[-2.83373,11.0067],[-2.94232,10.64281],[-2.83108,10.40252],[-2.74174,9.83172],[-2.76534,9.56589],[-2.68802,9.49343],[-2.69814,9.22717],[-2.77799,9.04949],[-2.66357,9.01771],[-2.58243,8.7789],[-2.49037,8.20872],[-2.62901,8.11495],[-2.61232,8.02645],[-2.67787,8.02055],[-2.74819,7.92613],[-2.78395,7.94974],[-2.79467,7.86002],[-2.92339,7.60847],[-2.97822,7.27165],[-2.95438,7.23737],[-3.23327,6.81744],[-3.21954,6.74407],[-3.25999,6.62521],[-3.01896,5.71697],[-2.95323,5.71865],[-2.96671,5.6415],[-2.93132,5.62137],[-2.85378,5.65156],[-2.76614,5.60963],[-2.72737,5.34789],[-2.77625,5.34621],[-2.73074,5.1364],[-2.75502,5.10657],[-2.95261,5.12477],[-2.96554,5.10397],[-3.063,5.13665],[-3.11073,5.12675],[-3.10675,5.08515],[-3.34019,4.17519],[1.07031,5.15655],[1.27574,5.93551],[1.19771,6.11522],[1.19966,6.17069],[1.09187,6.17074],[1.05969,6.22998],[1.03108,6.24064],[0.99652,6.33779],[0.89283,6.33779],[0.71048,6.53083],[0.74862,6.56517],[0.63659,6.63857],[0.6497,6.73682],[0.58176,6.76049],[0.57406,6.80348],[0.52853,6.82921],[0.56508,6.92971],[0.52098,6.94391],[0.52217,6.9723],[0.59606,7.01252],[0.65327,7.31643],[0.62943,7.41099],[0.57223,7.39326],[0.52455,7.45354],[0.51979,7.58706],[0.58295,7.62368],[0.62943,7.85751],[0.58891,8.12779],[0.6056,8.13959],[0.61156,8.18324],[0.5913,8.19622],[0.63897,8.25873],[0.73432,8.29529],[0.64731,8.48866],[0.47211,8.59945],[0.37319,8.75262],[0.52455,8.87746],[0.45424,9.04581],[0.56388,9.40697],[0.49118,9.48339],[0.36485,9.49749],[0.33148,9.44812],[0.25758,9.42696],[0.2254,9.47869],[0.31241,9.50337],[0.30406,9.521],[0.2409,9.52335],[0.23851,9.57389],[0.38153,9.58682],[0.36008,9.6256],[0.29334,9.59387],[0.26712,9.66437],[0.28261,9.69022],[0.32313,9.6491],[0.34816,9.66907],[0.34816,9.71607],[0.32075,9.72781],[0.36366,10.03309],[0.41252,10.02018],[0.41371,10.06361],[0.35293,10.09412],[0.39584,10.31112],[0.33028,10.30408],[0.29453,10.41546],[0.18846,10.4096],[0.12886,10.53149],[-0.05945,10.63458],[-0.09141,10.7147],[-0.07327,10.71845],[-0.07183,10.76794],[-0.0228,10.81916],[-0.02685,10.8783],[-0.00908,10.91644],[-0.0063,10.96417],[0.03355,10.9807],[0.02395,11.06229],[0.00342,11.08317],[-0.00514,11.10763],[-0.0275,11.11202],[-0.05733,11.08628],[-0.14462,11.10811],[-0.13493,11.14075]]]]}},{type:"Feature",properties:{iso1A2:"GI",iso1A3:"GIB",iso1N3:"292",wikidata:"Q1410",nameEn:"Gibraltar",country:"GB",groups:["039","150"],callingCodes:["350"]},geometry:{type:"MultiPolygon",coordinates:[[[[-5.28217,36.09907],[-5.27801,36.14942],[-5.33822,36.15272],[-5.34536,36.15501],[-5.36494,36.15496],[-5.38545,36.15481],[-5.40134,36.14896],[-5.39074,36.10278],[-5.36503,36.06205],[-5.32837,36.05935],[-5.3004,36.07439],[-5.28217,36.09907]]]]}},{type:"Feature",properties:{iso1A2:"GL",iso1A3:"GRL",iso1N3:"304",wikidata:"Q223",nameEn:"Greenland",country:"DK",groups:["021","003","019"],callingCodes:["299"]},geometry:{type:"MultiPolygon",coordinates:[[[[-45.47832,84.58738],[-68.21821,80.48551],[-76.75614,76.72014],[-46.37635,57.3249],[-9.68082,72.73731],[-5.7106,84.28058],[-45.47832,84.58738]]]]}},{type:"Feature",properties:{iso1A2:"GM",iso1A3:"GMB",iso1N3:"270",wikidata:"Q1005",nameEn:"The Gambia",groups:["011","202","002"],callingCodes:["220"]},geometry:{type:"MultiPolygon",coordinates:[[[[-15.14917,13.57989],[-14.36795,13.23033],[-13.79409,13.34472],[-13.8955,13.59126],[-14.34721,13.46578],[-14.93719,13.80173],[-15.36504,13.79313],[-15.47902,13.58758],[-17.43598,13.59273],[-17.43966,13.04579],[-16.74676,13.06025],[-16.69343,13.16791],[-15.80355,13.16729],[-15.80478,13.34832],[-15.26908,13.37768],[-15.14917,13.57989]]]]}},{type:"Feature",properties:{iso1A2:"GN",iso1A3:"GIN",iso1N3:"324",wikidata:"Q1006",nameEn:"Guinea",groups:["011","202","002"],callingCodes:["224"]},geometry:{type:"MultiPolygon",coordinates:[[[[-11.37536,12.40788],[-11.46267,12.44559],[-11.91331,12.42008],[-12.35415,12.32758],[-12.87336,12.51892],[-13.06603,12.49342],[-13.05296,12.64003],[-13.70523,12.68013],[-13.7039,12.60313],[-13.65089,12.49515],[-13.64168,12.42764],[-13.70851,12.24978],[-13.92745,12.24077],[-13.94589,12.16869],[-13.7039,12.00869],[-13.7039,11.70195],[-14.09799,11.63649],[-14.26623,11.67486],[-14.31513,11.60713],[-14.51173,11.49708],[-14.66677,11.51188],[-14.77786,11.36323],[-14.95993,10.99244],[-15.07174,10.89557],[-15.96748,10.162],[-14.36218,8.64107],[-13.29911,9.04245],[-13.18586,9.0925],[-13.08953,9.0409],[-12.94095,9.26335],[-12.76788,9.3133],[-12.47254,9.86834],[-12.24262,9.92386],[-12.12634,9.87203],[-11.91023,9.93927],[-11.89624,9.99763],[-11.2118,10.00098],[-10.6534,9.29919],[-10.74484,9.07998],[-10.5783,9.06386],[-10.56197,8.81225],[-10.47707,8.67669],[-10.61422,8.5314],[-10.70565,8.29235],[-10.63934,8.35326],[-10.54891,8.31174],[-10.37257,8.48941],[-10.27575,8.48711],[-10.203,8.47991],[-10.14579,8.52665],[-10.05375,8.50697],[-10.05873,8.42578],[-9.77763,8.54633],[-9.47415,8.35195],[-9.50898,8.18455],[-9.41445,8.02448],[-9.44928,7.9284],[-9.35724,7.74111],[-9.37465,7.62032],[-9.48161,7.37122],[-9.41943,7.41809],[-9.305,7.42056],[-9.20798,7.38109],[-9.18311,7.30461],[-9.09107,7.1985],[-8.93435,7.2824],[-8.85724,7.26019],[-8.8448,7.35149],[-8.72789,7.51429],[-8.67814,7.69428],[-8.55874,7.70167],[-8.55874,7.62525],[-8.47114,7.55676],[-8.4003,7.6285],[-8.21374,7.54466],[-8.09931,7.78626],[-8.13414,7.87991],[-8.06449,8.04989],[-7.94695,8.00925],[-7.99919,8.11023],[-7.98675,8.20134],[-8.062,8.16071],[-8.2411,8.24196],[-8.22991,8.48438],[-7.92518,8.50652],[-7.65653,8.36873],[-7.69882,8.66148],[-7.95503,8.81146],[-7.92518,8.99332],[-7.73862,9.08422],[-7.90777,9.20456],[-7.85056,9.41812],[-8.03463,9.39604],[-8.14657,9.55062],[-8.09434,9.86936],[-8.15652,9.94288],[-8.11921,10.04577],[-8.01225,10.1021],[-7.97971,10.17117],[-7.9578,10.2703],[-8.10207,10.44649],[-8.22711,10.41722],[-8.32614,10.69273],[-8.2667,10.91762],[-8.35083,11.06234],[-8.66923,10.99397],[-8.40058,11.37466],[-8.80854,11.66715],[-8.94784,12.34842],[-9.13689,12.50875],[-9.38067,12.48446],[-9.32097,12.29009],[-9.63938,12.18312],[-9.714,12.0226],[-10.30604,12.24634],[-10.71897,11.91552],[-10.80355,12.1053],[-10.99758,12.24634],[-11.24136,12.01286],[-11.50006,12.17826],[-11.37536,12.40788]]]]}},{type:"Feature",properties:{iso1A2:"GP",iso1A3:"GLP",iso1N3:"312",wikidata:"Q17012",nameEn:"Guadeloupe",country:"FR",groups:["EU","029","003","419","019"],callingCodes:["590"]},geometry:{type:"MultiPolygon",coordinates:[[[[-60.95725,15.70997],[-60.71337,16.48911],[-61.44461,16.81958],[-61.83929,16.66647],[-62.17275,16.35721],[-61.81728,15.58058],[-61.44899,15.79571],[-60.95725,15.70997]]]]}},{type:"Feature",properties:{iso1A2:"GQ",iso1A3:"GNQ",iso1N3:"226",wikidata:"Q983",nameEn:"Equatorial Guinea",groups:["017","202","002"],callingCodes:["240"]},geometry:{type:"MultiPolygon",coordinates:[[[[9.22018,3.72052],[8.34397,4.30689],[8.05799,3.48284],[8.0168,1.79377],[6.69416,-0.53945],[5.38965,-1.19244],[5.3459,-2.30107],[7.24416,-0.64092],[9.35563,0.84865],[9.51998,0.96418],[9.54793,1.0185],[9.62096,1.03039],[9.66092,1.05865],[9.68638,1.06836],[9.73014,1.06721],[9.76085,1.05949],[9.78058,1.03996],[9.79648,1.0019],[11.35307,1.00251],[11.3561,2.17217],[9.991,2.16561],[9.90749,2.20049],[9.89012,2.20457],[9.84716,2.24676],[9.83238,2.29079],[9.83754,2.32428],[9.82123,2.35097],[9.81162,2.33797],[9.22018,3.72052]]]]}},{type:"Feature",properties:{iso1A2:"GR",iso1A3:"GRC",iso1N3:"300",wikidata:"Q41",nameEn:"Greece",aliases:["EL"],groups:["EU","039","150"],callingCodes:["30"]},geometry:{type:"MultiPolygon",coordinates:[[[[26.03489,40.73051],[26.0754,40.72772],[26.08638,40.73214],[26.12495,40.74283],[26.12854,40.77339],[26.15685,40.80709],[26.21351,40.83298],[26.20856,40.86048],[26.26169,40.9168],[26.29441,40.89119],[26.28623,40.93005],[26.32259,40.94042],[26.35894,40.94292],[26.33297,40.98388],[26.3606,41.02027],[26.31928,41.07386],[26.32259,41.24929],[26.39861,41.25053],[26.5209,41.33993],[26.5837,41.32131],[26.62997,41.34613],[26.61767,41.42281],[26.59742,41.48058],[26.59196,41.60491],[26.5209,41.62592],[26.47958,41.67037],[26.35957,41.71149],[26.30255,41.70925],[26.2654,41.71544],[26.22888,41.74139],[26.21325,41.73223],[26.16841,41.74858],[26.06148,41.70345],[26.07083,41.64584],[26.15146,41.60828],[26.14328,41.55496],[26.17951,41.55409],[26.176,41.50072],[26.14796,41.47533],[26.20288,41.43943],[26.16548,41.42278],[26.12926,41.35878],[25.87919,41.30526],[25.8266,41.34563],[25.70507,41.29209],[25.66183,41.31316],[25.61042,41.30614],[25.55082,41.31667],[25.52394,41.2798],[25.48187,41.28506],[25.28322,41.23411],[25.11611,41.34212],[24.942,41.38685],[24.90928,41.40876],[24.86136,41.39298],[24.82514,41.4035],[24.8041,41.34913],[24.71529,41.41928],[24.61129,41.42278],[24.52599,41.56808],[24.30513,41.51297],[24.27124,41.57682],[24.18126,41.51735],[24.10063,41.54796],[24.06323,41.53222],[24.06908,41.46132],[23.96975,41.44118],[23.91483,41.47971],[23.89613,41.45257],[23.80148,41.43943],[23.76525,41.40175],[23.67644,41.41139],[23.63203,41.37632],[23.52453,41.40262],[23.40416,41.39999],[23.33639,41.36317],[23.31301,41.40525],[23.22771,41.37106],[23.21953,41.33773],[23.1833,41.31755],[22.93334,41.34104],[22.81199,41.3398],[22.76408,41.32225],[22.74538,41.16321],[22.71266,41.13945],[22.65306,41.18168],[22.62852,41.14385],[22.58295,41.11568],[22.5549,41.13065],[22.42285,41.11921],[22.26744,41.16409],[22.17629,41.15969],[22.1424,41.12449],[22.06527,41.15617],[21.90869,41.09191],[21.91102,41.04786],[21.7556,40.92525],[21.69601,40.9429],[21.57448,40.86076],[21.53007,40.90759],[21.41555,40.9173],[21.35595,40.87578],[21.25779,40.86165],[21.21105,40.8855],[21.15262,40.85546],[20.97887,40.85475],[20.98396,40.79109],[20.95752,40.76982],[20.98134,40.76046],[21.05833,40.66586],[21.03932,40.56299],[20.96908,40.51526],[20.94925,40.46625],[20.83688,40.47882],[20.7906,40.42726],[20.78234,40.35803],[20.71789,40.27739],[20.67162,40.09433],[20.62566,40.0897],[20.61081,40.07866],[20.55593,40.06524],[20.51297,40.08168],[20.48487,40.06271],[20.42373,40.06777],[20.37911,39.99058],[20.31135,39.99438],[20.41546,39.82832],[20.41475,39.81437],[20.38572,39.78516],[20.30804,39.81563],[20.29152,39.80421],[20.31961,39.72799],[20.27412,39.69884],[20.22707,39.67459],[20.22376,39.64532],[20.15988,39.652],[20.12956,39.65805],[20.05189,39.69112],[20.00957,39.69227],[19.98042,39.6504],[19.92466,39.69533],[19.97622,39.78684],[19.95905,39.82857],[19.0384,40.35325],[19.20409,39.7532],[22.5213,33.45682],[29.73302,35.92555],[29.69611,36.10365],[29.61805,36.14179],[29.61002,36.1731],[29.48192,36.18377],[29.30783,36.01033],[28.23708,36.56812],[27.95037,36.46155],[27.89482,36.69898],[27.46117,36.53789],[27.24613,36.71622],[27.45627,36.9008],[27.20312,36.94571],[27.14757,37.32],[26.95583,37.64989],[26.99377,37.69034],[27.16428,37.72343],[27.05537,37.9131],[26.21136,38.17558],[26.24183,38.44695],[26.32173,38.48731],[26.21136,38.65436],[26.61814,38.81372],[26.70773,39.0312],[26.43357,39.43096],[25.94257,39.39358],[25.61285,40.17161],[26.04292,40.3958],[25.94795,40.72797],[26.03489,40.73051]]]]}},{type:"Feature",properties:{iso1A2:"GS",iso1A3:"SGS",iso1N3:"239",wikidata:"Q35086",nameEn:"South Georgia and South Sandwich Islands",country:"GB",groups:["005","419","019"],driveSide:"left",callingCodes:["500"]},geometry:{type:"MultiPolygon",coordinates:[[[[-35.26394,-43.68272],[-53.39656,-59.87088],[-22.31757,-59.85974],[-35.26394,-43.68272]]]]}},{type:"Feature",properties:{iso1A2:"GT",iso1A3:"GTM",iso1N3:"320",wikidata:"Q774",nameEn:"Guatemala",groups:["013","003","419","019"],callingCodes:["502"]},geometry:{type:"MultiPolygon",coordinates:[[[[-89.14985,17.81563],[-90.98678,17.81655],[-90.99199,17.25192],[-91.43809,17.25373],[-91.04436,16.92175],[-90.69064,16.70697],[-90.61212,16.49832],[-90.40499,16.40524],[-90.44567,16.07573],[-91.73182,16.07371],[-92.20983,15.26077],[-92.0621,15.07406],[-92.1454,14.98143],[-92.1423,14.88647],[-92.18161,14.84147],[-92.1454,14.6804],[-92.2261,14.53423],[-92.37213,14.39277],[-90.55276,12.8866],[-90.11344,13.73679],[-90.10505,13.85104],[-89.88937,14.0396],[-89.81807,14.07073],[-89.76103,14.02923],[-89.73251,14.04133],[-89.75569,14.07073],[-89.70756,14.1537],[-89.61844,14.21937],[-89.52397,14.22628],[-89.50614,14.26084],[-89.58814,14.33165],[-89.57441,14.41637],[-89.39028,14.44561],[-89.34776,14.43013],[-89.35189,14.47553],[-89.23719,14.58046],[-89.15653,14.57802],[-89.13132,14.71582],[-89.23467,14.85596],[-89.15149,14.97775],[-89.18048,14.99967],[-89.15149,15.07392],[-88.97343,15.14039],[-88.32467,15.63665],[-88.31459,15.66942],[-88.24022,15.69247],[-88.22552,15.72294],[-88.20359,16.03858],[-88.40779,16.09624],[-88.95358,15.88698],[-89.02415,15.9063],[-89.17418,15.90898],[-89.22683,15.88619],[-89.15025,17.04813],[-89.14985,17.81563]]]]}},{type:"Feature",properties:{iso1A2:"GU",iso1A3:"GUM",iso1N3:"316",wikidata:"Q16635",nameEn:"Guam",country:"US",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["1 671"]},geometry:{type:"MultiPolygon",coordinates:[[[[146.25931,13.85876],[143.82485,13.92273],[144.61642,12.82462],[146.25931,13.85876]]]]}},{type:"Feature",properties:{iso1A2:"GW",iso1A3:"GNB",iso1N3:"624",wikidata:"Q1007",nameEn:"Guinea-Bissau",groups:["011","202","002"],callingCodes:["245"]},geometry:{type:"MultiPolygon",coordinates:[[[[-14.31513,11.60713],[-14.26623,11.67486],[-14.09799,11.63649],[-13.7039,11.70195],[-13.7039,12.00869],[-13.94589,12.16869],[-13.92745,12.24077],[-13.70851,12.24978],[-13.64168,12.42764],[-13.65089,12.49515],[-13.7039,12.60313],[-13.70523,12.68013],[-15.17582,12.6847],[-15.67302,12.42974],[-16.20591,12.46157],[-16.38191,12.36449],[-16.70562,12.34803],[-17.4623,11.92379],[-15.96748,10.162],[-15.07174,10.89557],[-14.95993,10.99244],[-14.77786,11.36323],[-14.66677,11.51188],[-14.51173,11.49708],[-14.31513,11.60713]]]]}},{type:"Feature",properties:{iso1A2:"GY",iso1A3:"GUY",iso1N3:"328",wikidata:"Q734",nameEn:"Guyana",groups:["005","419","019"],driveSide:"left",callingCodes:["592"]},geometry:{type:"MultiPolygon",coordinates:[[[[-56.84822,6.73257],[-59.54058,8.6862],[-59.98508,8.53046],[-59.85562,8.35213],[-59.80661,8.28906],[-59.83156,8.23261],[-59.97059,8.20791],[-60.02407,8.04557],[-60.38056,7.8302],[-60.51959,7.83373],[-60.64793,7.56877],[-60.71923,7.55817],[-60.59802,7.33194],[-60.63367,7.25061],[-60.54098,7.14804],[-60.44116,7.20817],[-60.28074,7.1162],[-60.39419,6.94847],[-60.54873,6.8631],[-61.13632,6.70922],[-61.20762,6.58174],[-61.15058,6.19558],[-61.4041,5.95304],[-60.73204,5.20931],[-60.32352,5.21299],[-60.20944,5.28754],[-59.98129,5.07097],[-60.04189,4.69801],[-60.15953,4.53456],[-59.78878,4.45637],[-59.69361,4.34069],[-59.73353,4.20399],[-59.51963,3.91951],[-59.86899,3.57089],[-59.79769,3.37162],[-59.99733,2.92312],[-59.91177,2.36759],[-59.7264,2.27497],[-59.74066,1.87596],[-59.25583,1.40559],[-58.92072,1.31293],[-58.84229,1.17749],[-58.53571,1.29154],[-58.4858,1.48399],[-58.33887,1.58014],[-58.01873,1.51966],[-57.97336,1.64566],[-57.77281,1.73344],[-57.55743,1.69605],[-57.35073,1.98327],[-57.23981,1.95808],[-57.09109,2.01854],[-57.07092,1.95304],[-56.7659,1.89509],[-56.47045,1.95135],[-56.55439,2.02003],[-56.70519,2.02964],[-57.35891,3.32121],[-58.0307,3.95513],[-57.8699,4.89394],[-57.37442,5.0208],[-57.22536,5.15605],[-57.31629,5.33714],[-56.84822,6.73257]]]]}},{type:"Feature",properties:{iso1A2:"HK",iso1A3:"HKG",iso1N3:"344",wikidata:"Q8646",nameEn:"Hong Kong",country:"CN",groups:["030","142"],driveSide:"left",callingCodes:["852"]},geometry:{type:"MultiPolygon",coordinates:[[[[113.92195,22.13873],[114.50148,22.15017],[114.44998,22.55977],[114.25154,22.55977],[114.22888,22.5436],[114.22185,22.55343],[114.20655,22.55706],[114.18338,22.55444],[114.17247,22.55944],[114.1597,22.56041],[114.15123,22.55163],[114.1482,22.54091],[114.13823,22.54319],[114.12665,22.54003],[114.11656,22.53415],[114.11181,22.52878],[114.1034,22.5352],[114.09692,22.53435],[114.09048,22.53716],[114.08606,22.53276],[114.07817,22.52997],[114.07267,22.51855],[114.06272,22.51617],[114.05729,22.51104],[114.05438,22.5026],[114.03113,22.5065],[113.86771,22.42972],[113.81621,22.2163],[113.83338,22.1826],[113.92195,22.13873]]]]}},{type:"Feature",properties:{iso1A2:"HM",iso1A3:"HMD",iso1N3:"334",wikidata:"Q131198",nameEn:"Heard Island and McDonald Islands",country:"AU",groups:["053","009"],driveSide:"left"},geometry:{type:"MultiPolygon",coordinates:[[[[71.08716,-53.87687],[75.44182,-53.99822],[72.87012,-51.48322],[71.08716,-53.87687]]]]}},{type:"Feature",properties:{iso1A2:"HN",iso1A3:"HND",iso1N3:"340",wikidata:"Q783",nameEn:"Honduras",groups:["013","003","419","019"],callingCodes:["504"]},geometry:{type:"MultiPolygon",coordinates:[[[[-83.86109,17.73736],[-88.20359,16.03858],[-88.22552,15.72294],[-88.24022,15.69247],[-88.31459,15.66942],[-88.32467,15.63665],[-88.97343,15.14039],[-89.15149,15.07392],[-89.18048,14.99967],[-89.15149,14.97775],[-89.23467,14.85596],[-89.13132,14.71582],[-89.15653,14.57802],[-89.23719,14.58046],[-89.35189,14.47553],[-89.34776,14.43013],[-89.04187,14.33644],[-88.94608,14.20207],[-88.85785,14.17763],[-88.815,14.11652],[-88.73182,14.10919],[-88.70661,14.04317],[-88.49738,13.97224],[-88.48982,13.86458],[-88.25791,13.91108],[-88.23018,13.99915],[-88.07641,13.98447],[-88.00331,13.86948],[-87.7966,13.91353],[-87.68821,13.80829],[-87.73106,13.75443],[-87.78148,13.52906],[-87.71657,13.50577],[-87.72115,13.46083],[-87.73841,13.44169],[-87.77354,13.45767],[-87.83467,13.44655],[-87.84675,13.41078],[-87.80177,13.35689],[-87.73714,13.32715],[-87.69751,13.25228],[-87.55124,13.12523],[-87.37107,12.98646],[-87.06306,13.00892],[-87.03785,12.98682],[-86.93197,13.05313],[-86.93383,13.18677],[-86.87066,13.30641],[-86.71267,13.30348],[-86.76812,13.79605],[-86.35219,13.77157],[-86.14801,14.04317],[-86.00685,14.08474],[-86.03458,13.99181],[-85.75477,13.8499],[-85.73964,13.9698],[-85.45762,14.11304],[-85.32149,14.2562],[-85.18602,14.24929],[-85.1575,14.53934],[-84.90082,14.80489],[-84.82596,14.82212],[-84.70119,14.68078],[-84.48373,14.63249],[-84.10584,14.76353],[-83.89551,14.76697],[-83.62101,14.89448],[-83.49268,15.01158],[-83.13724,15.00002],[-83.04763,15.03256],[-82.06974,14.49418],[-81.58685,18.0025],[-83.86109,17.73736]]]]}},{type:"Feature",properties:{iso1A2:"HR",iso1A3:"HRV",iso1N3:"191",wikidata:"Q224",nameEn:"Croatia",groups:["EU","039","150"],callingCodes:["385"]},geometry:{type:"MultiPolygon",coordinates:[[[[17.6444,42.88641],[17.5392,42.92787],[17.70879,42.97223],[17.64268,43.08595],[17.46986,43.16559],[17.286,43.33065],[17.25579,43.40353],[17.29699,43.44542],[17.24411,43.49376],[17.15828,43.49376],[17.00585,43.58037],[16.80736,43.76011],[16.75316,43.77157],[16.70922,43.84887],[16.55472,43.95326],[16.50528,44.0244],[16.43629,44.02826],[16.43662,44.07523],[16.36864,44.08263],[16.18688,44.27012],[16.21346,44.35231],[16.12969,44.38275],[16.16814,44.40679],[16.10566,44.52586],[16.03012,44.55572],[16.00884,44.58605],[16.05828,44.61538],[15.89348,44.74964],[15.8255,44.71501],[15.72584,44.82334],[15.79472,44.8455],[15.76096,44.87045],[15.74723,44.96818],[15.78568,44.97401],[15.74585,45.0638],[15.78842,45.11519],[15.76371,45.16508],[15.83512,45.22459],[15.98412,45.23088],[16.12153,45.09616],[16.29036,44.99732],[16.35404,45.00241],[16.35863,45.03529],[16.3749,45.05206],[16.38219,45.05139],[16.38309,45.05955],[16.40023,45.1147],[16.4634,45.14522],[16.49155,45.21153],[16.52982,45.22713],[16.5501,45.2212],[16.56559,45.22307],[16.60194,45.23042],[16.64962,45.20714],[16.74845,45.20393],[16.78219,45.19002],[16.81137,45.18434],[16.83804,45.18951],[16.92405,45.27607],[16.9385,45.22742],[17.0415,45.20759],[17.18438,45.14764],[17.24325,45.146],[17.25131,45.14957],[17.26815,45.18444],[17.32092,45.16246],[17.33573,45.14521],[17.41229,45.13335],[17.4498,45.16119],[17.45615,45.12523],[17.47589,45.12656],[17.51469,45.10791],[17.59104,45.10816],[17.66571,45.13408],[17.84826,45.04489],[17.87148,45.04645],[17.93706,45.08016],[17.97336,45.12245],[17.97834,45.13831],[17.99479,45.14958],[18.01594,45.15163],[18.03121,45.12632],[18.1624,45.07654],[18.24387,45.13699],[18.32077,45.1021],[18.41896,45.11083],[18.47939,45.05871],[18.65723,45.07544],[18.78357,44.97741],[18.80661,44.93561],[18.76369,44.93707],[18.76347,44.90669],[18.8704,44.85097],[19.01994,44.85493],[18.98957,44.90645],[19.02871,44.92541],[19.06853,44.89915],[19.15573,44.95409],[19.05205,44.97692],[19.1011,45.01191],[19.07952,45.14668],[19.14063,45.12972],[19.19144,45.17863],[19.43589,45.17137],[19.41941,45.23475],[19.28208,45.23813],[19.10774,45.29547],[18.97446,45.37528],[18.99918,45.49333],[19.08364,45.48804],[19.07471,45.53086],[18.94562,45.53712],[18.88776,45.57253],[18.96691,45.66731],[18.90305,45.71863],[18.85783,45.85493],[18.81394,45.91329],[18.80211,45.87995],[18.6792,45.92057],[18.57483,45.80772],[18.44368,45.73972],[18.12439,45.78905],[18.08869,45.76511],[17.99805,45.79671],[17.87377,45.78522],[17.66545,45.84207],[17.56821,45.93728],[17.35672,45.95209],[17.14592,46.16697],[16.8903,46.28122],[16.8541,46.36255],[16.7154,46.39523],[16.6639,46.45203],[16.59527,46.47524],[16.52604,46.47831],[16.5007,46.49644],[16.44036,46.5171],[16.38771,46.53608],[16.37193,46.55008],[16.29793,46.5121],[16.26733,46.51505],[16.26759,46.50566],[16.23961,46.49653],[16.25124,46.48067],[16.27398,46.42875],[16.27329,46.41467],[16.30162,46.40437],[16.30233,46.37837],[16.18824,46.38282],[16.14859,46.40547],[16.05281,46.39141],[16.05065,46.3833],[16.07314,46.36458],[16.07616,46.3463],[15.97965,46.30652],[15.79284,46.25811],[15.78817,46.21719],[15.75479,46.20336],[15.75436,46.21969],[15.67395,46.22478],[15.6434,46.21396],[15.64904,46.19229],[15.59909,46.14761],[15.6083,46.11992],[15.62317,46.09103],[15.72977,46.04682],[15.71246,46.01196],[15.70327,46.00015],[15.70636,45.92116],[15.67967,45.90455],[15.68383,45.88867],[15.68232,45.86819],[15.70411,45.8465],[15.66662,45.84085],[15.64185,45.82915],[15.57952,45.84953],[15.52234,45.82195],[15.47325,45.8253],[15.47531,45.79802],[15.40836,45.79491],[15.25423,45.72275],[15.30872,45.69014],[15.34919,45.71623],[15.4057,45.64727],[15.38952,45.63682],[15.34214,45.64702],[15.34695,45.63382],[15.31027,45.6303],[15.27747,45.60504],[15.29837,45.5841],[15.30249,45.53224],[15.38188,45.48752],[15.33051,45.45258],[15.27758,45.46678],[15.16862,45.42309],[15.05187,45.49079],[15.02385,45.48533],[14.92266,45.52788],[14.90554,45.47769],[14.81992,45.45913],[14.80124,45.49515],[14.71718,45.53442],[14.68605,45.53006],[14.69694,45.57366],[14.59576,45.62812],[14.60977,45.66403],[14.57397,45.67165],[14.53816,45.6205],[14.5008,45.60852],[14.49769,45.54424],[14.36693,45.48642],[14.32487,45.47142],[14.27681,45.4902],[14.26611,45.48239],[14.24239,45.50607],[14.22371,45.50388],[14.20348,45.46896],[14.07116,45.48752],[14.00578,45.52352],[13.96063,45.50825],[13.99488,45.47551],[13.97309,45.45258],[13.90771,45.45149],[13.88124,45.42637],[13.81742,45.43729],[13.7785,45.46787],[13.67398,45.4436],[13.62902,45.45898],[13.56979,45.4895],[13.45644,45.59464],[13.05142,45.33128],[13.12821,44.48877],[16.15283,42.18525],[18.45131,42.21682],[18.54128,42.39171],[18.52152,42.42302],[18.43588,42.48556],[18.44307,42.51077],[18.43735,42.55921],[18.36197,42.61423],[18.24318,42.6112],[17.88201,42.83668],[17.80854,42.9182],[17.7948,42.89556],[17.68151,42.92725],[17.6444,42.88641]]]]}},{type:"Feature",properties:{iso1A2:"HT",iso1A3:"HTI",iso1N3:"332",wikidata:"Q790",nameEn:"Haiti",aliases:["RH"],groups:["029","003","419","019"],callingCodes:["509"]},geometry:{type:"MultiPolygon",coordinates:[[[[-71.71885,18.78423],[-71.72624,18.87802],[-71.77766,18.95007],[-71.88102,18.95007],[-71.74088,19.0437],[-71.71088,19.08353],[-71.69938,19.10916],[-71.65337,19.11759],[-71.62642,19.21212],[-71.73229,19.26686],[-71.77766,19.33823],[-71.69448,19.37866],[-71.6802,19.45008],[-71.71268,19.53374],[-71.71449,19.55364],[-71.7429,19.58445],[-71.75865,19.70231],[-71.77419,19.73128],[-72.38946,20.27111],[-73.37289,20.43199],[-74.7289,18.71009],[-74.76465,18.06252],[-72.29523,17.48026],[-71.75671,18.03456],[-71.73783,18.07177],[-71.74994,18.11115],[-71.75465,18.14405],[-71.78271,18.18302],[-71.69952,18.34101],[-71.90875,18.45821],[-71.88102,18.50125],[-72.00201,18.62312],[-71.95412,18.64939],[-71.82556,18.62551],[-71.71885,18.78423]]]]}},{type:"Feature",properties:{iso1A2:"HU",iso1A3:"HUN",iso1N3:"348",wikidata:"Q28",nameEn:"Hungary",groups:["EU","151","150"],callingCodes:["36"]},geometry:{type:"MultiPolygon",coordinates:[[[[21.72525,48.34628],[21.67134,48.3989],[21.6068,48.50365],[21.44063,48.58456],[21.11516,48.49546],[20.83248,48.5824],[20.5215,48.53336],[20.29943,48.26104],[20.24312,48.2784],[19.92452,48.1283],[19.63338,48.25006],[19.52489,48.19791],[19.47957,48.09437],[19.28182,48.08336],[19.23924,48.0595],[19.01952,48.07052],[18.82176,48.04206],[18.76134,47.97499],[18.76821,47.87469],[18.8506,47.82308],[18.74074,47.8157],[18.66521,47.76772],[18.56496,47.76588],[18.29305,47.73541],[18.02938,47.75665],[17.71215,47.7548],[17.23699,48.02094],[17.16001,48.00636],[17.09786,47.97336],[17.11022,47.92461],[17.08275,47.87719],[17.00997,47.86245],[17.07039,47.81129],[17.05048,47.79377],[17.08893,47.70928],[16.87538,47.68895],[16.86509,47.72268],[16.82938,47.68432],[16.7511,47.67878],[16.72089,47.73469],[16.65679,47.74197],[16.61183,47.76171],[16.54779,47.75074],[16.53514,47.73837],[16.55129,47.72268],[16.4222,47.66537],[16.58699,47.61772],[16.64193,47.63114],[16.71059,47.52692],[16.64821,47.50155],[16.6718,47.46139],[16.57152,47.40868],[16.52414,47.41007],[16.49908,47.39416],[16.45104,47.41181],[16.47782,47.25918],[16.44142,47.25079],[16.43663,47.21127],[16.41739,47.20649],[16.42801,47.18422],[16.4523,47.18812],[16.46442,47.16845],[16.44932,47.14418],[16.52863,47.13974],[16.46134,47.09395],[16.52176,47.05747],[16.43936,47.03548],[16.51369,47.00084],[16.28202,47.00159],[16.27594,46.9643],[16.22403,46.939],[16.19904,46.94134],[16.10983,46.867],[16.14365,46.8547],[16.15711,46.85434],[16.21892,46.86961],[16.2365,46.87775],[16.2941,46.87137],[16.34547,46.83836],[16.3408,46.80641],[16.31303,46.79838],[16.30966,46.7787],[16.37816,46.69975],[16.42641,46.69228],[16.41863,46.66238],[16.38594,46.6549],[16.39217,46.63673],[16.50139,46.56684],[16.52885,46.53303],[16.52604,46.5051],[16.59527,46.47524],[16.6639,46.45203],[16.7154,46.39523],[16.8541,46.36255],[16.8903,46.28122],[17.14592,46.16697],[17.35672,45.95209],[17.56821,45.93728],[17.66545,45.84207],[17.87377,45.78522],[17.99805,45.79671],[18.08869,45.76511],[18.12439,45.78905],[18.44368,45.73972],[18.57483,45.80772],[18.6792,45.92057],[18.80211,45.87995],[18.81394,45.91329],[18.99712,45.93537],[19.01284,45.96529],[19.0791,45.96458],[19.10388,46.04015],[19.14543,45.9998],[19.28826,45.99694],[19.52473,46.1171],[19.56113,46.16824],[19.66007,46.19005],[19.81491,46.1313],[19.93508,46.17553],[20.01816,46.17696],[20.03533,46.14509],[20.09713,46.17315],[20.26068,46.12332],[20.28324,46.1438],[20.35573,46.16629],[20.45377,46.14405],[20.49718,46.18721],[20.63863,46.12728],[20.76085,46.21002],[20.74574,46.25467],[20.86797,46.28884],[21.06572,46.24897],[21.16872,46.30118],[21.28061,46.44941],[21.26929,46.4993],[21.33214,46.63035],[21.43926,46.65109],[21.5151,46.72147],[21.48935,46.7577],[21.52028,46.84118],[21.59307,46.86935],[21.59581,46.91628],[21.68645,46.99595],[21.648,47.03902],[21.78395,47.11104],[21.94463,47.38046],[22.01055,47.37767],[22.03389,47.42508],[22.00917,47.50492],[22.31816,47.76126],[22.41979,47.7391],[22.46559,47.76583],[22.67247,47.7871],[22.76617,47.8417],[22.77991,47.87211],[22.89849,47.95851],[22.84276,47.98602],[22.87847,48.04665],[22.81804,48.11363],[22.73427,48.12005],[22.66835,48.09162],[22.58733,48.10813],[22.59007,48.15121],[22.49806,48.25189],[22.38133,48.23726],[22.2083,48.42534],[22.14689,48.4005],[21.83339,48.36242],[21.8279,48.33321],[21.72525,48.34628]]]]}},{type:"Feature",properties:{iso1A2:"IC",wikidata:"Q5813",nameEn:"Canary Islands",country:"ES",groups:["EU","039","150"],isoStatus:"excRes",callingCodes:["34"]},geometry:{type:"MultiPolygon",coordinates:[[[[-15.92339,29.50503],[-25.3475,27.87574],[-14.43883,27.02969],[-9.94494,32.97138],[-15.92339,29.50503]]]]}},{type:"Feature",properties:{iso1A2:"ID",iso1A3:"IDN",iso1N3:"360",wikidata:"Q252",nameEn:"Indonesia",aliases:["RI"],groups:["035","142"],driveSide:"left",callingCodes:["62"]},geometry:{type:"MultiPolygon",coordinates:[[[[141.02352,0.08993],[128.97621,3.08804],[126.69413,6.02692],[124.97752,4.82064],[118.41402,3.99509],[118.07935,4.15511],[117.89538,4.16637],[117.67641,4.16535],[117.47313,4.18857],[117.25801,4.35108],[115.90217,4.37708],[115.58276,3.93499],[115.53713,3.14776],[115.11343,2.82879],[115.1721,2.49671],[114.80706,2.21665],[114.80706,1.92351],[114.57892,1.5],[114.03788,1.44787],[113.64677,1.23933],[113.01448,1.42832],[113.021,1.57819],[112.48648,1.56516],[112.2127,1.44135],[112.15679,1.17004],[111.94553,1.12016],[111.82846,0.99349],[111.55434,0.97864],[111.22979,1.08326],[110.62374,0.873],[110.49182,0.88088],[110.35354,0.98869],[109.66397,1.60425],[109.66397,1.79972],[109.57923,1.80624],[109.53794,1.91771],[109.62558,1.99182],[109.64506,2.08014],[109.71058,2.32059],[108.10426,5.42408],[105.01437,3.24936],[104.56723,1.44271],[104.34728,1.33529],[104.12282,1.27714],[104.03085,1.26954],[103.74084,1.12902],[103.66049,1.18825],[103.56591,1.19719],[103.03657,1.30383],[96.11174,6.69841],[74.28481,-3.17525],[122.14954,-11.52517],[125.68138,-9.85176],[125.09025,-9.46406],[124.97892,-9.19281],[125.04044,-9.17093],[125.09434,-9.19669],[125.18907,-9.16434],[125.18632,-9.03142],[125.11764,-8.96359],[124.97742,-9.08128],[124.94011,-8.85617],[124.46701,-9.13002],[124.45971,-9.30263],[124.38554,-9.3582],[124.35258,-9.43002],[124.3535,-9.48493],[124.28115,-9.50453],[124.28115,-9.42189],[124.21247,-9.36904],[124.14517,-9.42324],[124.10539,-9.41206],[124.04286,-9.34243],[124.04628,-9.22671],[124.33472,-9.11416],[124.92337,-8.75859],[125.31127,-8.22976],[125.65946,-8.06136],[125.87691,-8.31789],[127.42116,-8.22471],[127.55165,-9.05052],[140.88922,-9.34945],[141.00782,-9.1242],[141.01763,-6.90181],[140.85295,-6.72996],[140.99813,-6.3233],[141.02352,0.08993]]]]}},{type:"Feature",properties:{iso1A2:"IE",iso1A3:"IRL",iso1N3:"372",wikidata:"Q27",nameEn:"Ireland",groups:["EU","154","150"],driveSide:"left",callingCodes:["353"]},geometry:{type:"MultiPolygon",coordinates:[[[[-6.26218,54.09785],[-6.29003,54.11278],[-6.32694,54.09337],[-6.36279,54.11248],[-6.36605,54.07234],[-6.47849,54.06947],[-6.62842,54.03503],[-6.66264,54.0666],[-6.6382,54.17071],[-6.70175,54.20218],[-6.74575,54.18788],[-6.81583,54.22791],[-6.85179,54.29176],[-6.87775,54.34682],[-7.02034,54.4212],[-7.19145,54.31296],[-7.14908,54.22732],[-7.25012,54.20063],[-7.26316,54.13863],[-7.29493,54.12013],[-7.29687,54.1354],[-7.28017,54.16714],[-7.29157,54.17191],[-7.34005,54.14698],[-7.30553,54.11869],[-7.32834,54.11475],[-7.44567,54.1539],[-7.4799,54.12239],[-7.55812,54.12239],[-7.69501,54.20731],[-7.81397,54.20159],[-7.8596,54.21779],[-7.87101,54.29299],[-8.04555,54.36292],[-8.179,54.46763],[-8.04538,54.48941],[-7.99812,54.54427],[-7.8596,54.53671],[-7.70315,54.62077],[-7.93293,54.66603],[-7.83352,54.73854],[-7.75041,54.7103],[-7.64449,54.75265],[-7.54671,54.74606],[-7.54508,54.79401],[-7.47626,54.83084],[-7.4473,54.87003],[-7.44404,54.9403],[-7.40004,54.94498],[-7.4033,55.00391],[-7.34464,55.04688],[-7.2471,55.06933],[-6.9734,55.19878],[-6.71944,55.27952],[-6.79943,55.54107],[-7.93366,55.84142],[-22.01468,48.19557],[-5.79914,52.03902],[-5.37267,53.63269],[-5.83481,53.87749],[-6.26218,54.09785]]]]}},{type:"Feature",properties:{iso1A2:"IL",iso1A3:"ISR",iso1N3:"376",wikidata:"Q801",nameEn:"Israel",groups:["145","142"],callingCodes:["972"]},geometry:{type:"MultiPolygon",coordinates:[[[[34.052,31.46619],[34.29262,31.70393],[34.48681,31.59711],[34.56797,31.54197],[34.48892,31.48365],[34.40077,31.40926],[34.36505,31.36404],[34.37381,31.30598],[34.36523,31.28963],[34.29417,31.24194],[34.26742,31.21998],[34.92298,29.45305],[34.97718,29.54294],[34.98207,29.58147],[35.02147,29.66343],[35.14108,30.07374],[35.19183,30.34636],[35.16218,30.43535],[35.19595,30.50297],[35.21379,30.60401],[35.29311,30.71365],[35.33456,30.81224],[35.33984,30.8802],[35.41371,30.95565],[35.43658,31.12444],[35.40316,31.25535],[35.47672,31.49578],[35.39675,31.49572],[35.22921,31.37445],[35.13033,31.3551],[35.02459,31.35979],[34.92571,31.34337],[34.88932,31.37093],[34.87833,31.39321],[34.89756,31.43891],[34.93258,31.47816],[34.94356,31.50743],[34.9415,31.55601],[34.95249,31.59813],[35.00879,31.65426],[35.08226,31.69107],[35.10782,31.71594],[35.11895,31.71454],[35.12933,31.7325],[35.13931,31.73012],[35.15119,31.73634],[35.15474,31.73352],[35.16478,31.73242],[35.18023,31.72067],[35.20538,31.72388],[35.21937,31.71578],[35.22392,31.71899],[35.23972,31.70896],[35.24315,31.71244],[35.2438,31.7201],[35.24981,31.72543],[35.25182,31.73945],[35.26319,31.74846],[35.25225,31.7678],[35.26058,31.79064],[35.25573,31.81362],[35.26404,31.82567],[35.251,31.83085],[35.25753,31.8387],[35.24816,31.8458],[35.2304,31.84222],[35.2249,31.85433],[35.22817,31.8638],[35.22567,31.86745],[35.22294,31.87889],[35.22014,31.88264],[35.2136,31.88241],[35.21276,31.88153],[35.21016,31.88237],[35.20945,31.8815],[35.20791,31.8821],[35.20673,31.88151],[35.20381,31.86716],[35.21128,31.863],[35.216,31.83894],[35.21469,31.81835],[35.19461,31.82687],[35.18169,31.82542],[35.18603,31.80901],[35.14174,31.81325],[35.07677,31.85627],[35.05617,31.85685],[35.01978,31.82944],[34.9724,31.83352],[34.99712,31.85569],[35.03489,31.85919],[35.03978,31.89276],[35.03489,31.92448],[35.00124,31.93264],[34.98682,31.96935],[35.00261,32.027],[34.9863,32.09551],[34.99437,32.10962],[34.98507,32.12606],[34.99039,32.14626],[34.96009,32.17503],[34.95703,32.19522],[34.98885,32.20758],[35.01841,32.23981],[35.02939,32.2671],[35.01119,32.28684],[35.01772,32.33863],[35.04243,32.35008],[35.05142,32.3667],[35.0421,32.38242],[35.05311,32.4024],[35.05423,32.41754],[35.07059,32.4585],[35.08564,32.46948],[35.09236,32.47614],[35.10024,32.47856],[35.10882,32.4757],[35.15937,32.50466],[35.2244,32.55289],[35.25049,32.52453],[35.29306,32.50947],[35.30685,32.51024],[35.35212,32.52047],[35.40224,32.50136],[35.42034,32.46009],[35.41598,32.45593],[35.41048,32.43706],[35.42078,32.41562],[35.55807,32.38674],[35.55494,32.42687],[35.57485,32.48669],[35.56614,32.64393],[35.59813,32.65159],[35.61669,32.67999],[35.66527,32.681],[35.68467,32.70715],[35.75983,32.74803],[35.78745,32.77938],[35.83758,32.82817],[35.84021,32.8725],[35.87012,32.91976],[35.89298,32.9456],[35.87188,32.98028],[35.84802,33.1031],[35.81911,33.11077],[35.81911,33.1336],[35.84285,33.16673],[35.83846,33.19397],[35.81647,33.2028],[35.81295,33.24841],[35.77513,33.27342],[35.813,33.3172],[35.77477,33.33609],[35.62019,33.27278],[35.62283,33.24226],[35.58502,33.26653],[35.58326,33.28381],[35.56523,33.28969],[35.55555,33.25844],[35.54544,33.25513],[35.54808,33.236],[35.5362,33.23196],[35.54228,33.19865],[35.52573,33.11921],[35.50335,33.114],[35.50272,33.09056],[35.448,33.09264],[35.43059,33.06659],[35.35223,33.05617],[35.31429,33.10515],[35.1924,33.08743],[35.10645,33.09318],[34.78515,33.20368],[33.62659,31.82938],[34.052,31.46619]]]]}},{type:"Feature",properties:{iso1A2:"IM",iso1A3:"IMN",iso1N3:"833",wikidata:"Q9676",nameEn:"Isle of Man",country:"GB",groups:["154","150"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44 01624","44 07624","44 07524","44 07924"]},geometry:{type:"MultiPolygon",coordinates:[[[[-3.64906,54.12723],[-4.1819,54.57861],[-5.83481,53.87749],[-5.37267,53.63269],[-3.64906,54.12723]]]]}},{type:"Feature",properties:{iso1A2:"IN",iso1A3:"IND",iso1N3:"356",wikidata:"Q668",nameEn:"India",groups:["034","142"],driveSide:"left",callingCodes:["91"]},geometry:{type:"MultiPolygon",coordinates:[[[[78.11664,35.48022],[77.80532,35.52058],[77.70232,35.46244],[77.44277,35.46132],[76.96624,35.5932],[76.84539,35.67356],[76.77323,35.66062],[76.75475,35.52617],[76.85088,35.39754],[76.93465,35.39866],[77.11796,35.05419],[76.99251,34.93349],[76.87193,34.96906],[76.74514,34.92488],[76.74377,34.84039],[76.67648,34.76371],[76.47186,34.78965],[76.15463,34.6429],[76.04614,34.67566],[75.75438,34.51827],[75.38009,34.55021],[75.01479,34.64629],[74.6663,34.703],[74.58083,34.77386],[74.31239,34.79626],[74.12897,34.70073],[73.96423,34.68244],[73.93401,34.63386],[73.93951,34.57169],[73.89419,34.54568],[73.88732,34.48911],[73.74999,34.3781],[73.74862,34.34183],[73.8475,34.32935],[73.90517,34.35317],[73.98208,34.2522],[73.90677,34.10504],[73.88732,34.05105],[73.91341,34.01235],[74.21554,34.03853],[74.25262,34.01577],[74.26086,33.92237],[74.14001,33.83002],[74.05898,33.82089],[74.00891,33.75437],[73.96423,33.73071],[73.98968,33.66155],[73.97367,33.64061],[74.03576,33.56718],[74.10115,33.56392],[74.18121,33.4745],[74.17983,33.3679],[74.08782,33.26232],[74.01366,33.25199],[74.02144,33.18908],[74.15374,33.13477],[74.17571,33.07495],[74.31854,33.02891],[74.34875,32.97823],[74.31227,32.92795],[74.41467,32.90563],[74.45312,32.77755],[74.6289,32.75561],[74.64675,32.82604],[74.7113,32.84219],[74.65345,32.71225],[74.69542,32.66792],[74.64424,32.60985],[74.65251,32.56416],[74.67431,32.56676],[74.68362,32.49298],[74.84725,32.49075],[74.97634,32.45367],[75.03265,32.49538],[75.28259,32.36556],[75.38046,32.26836],[75.25649,32.10187],[75.00793,32.03786],[74.9269,32.0658],[74.86236,32.04485],[74.79919,31.95983],[74.58907,31.87824],[74.47771,31.72227],[74.57498,31.60382],[74.61517,31.55698],[74.59319,31.50197],[74.64713,31.45605],[74.59773,31.4136],[74.53223,31.30321],[74.51629,31.13829],[74.56023,31.08303],[74.60281,31.10419],[74.60006,31.13711],[74.6852,31.12771],[74.67971,31.05479],[74.5616,31.04153],[73.88993,30.36305],[73.95736,30.28466],[73.97225,30.19829],[73.80299,30.06969],[73.58665,30.01848],[73.3962,29.94707],[73.28094,29.56646],[73.05886,29.1878],[73.01337,29.16422],[72.94272,29.02487],[72.40402,28.78283],[72.29495,28.66367],[72.20329,28.3869],[71.9244,28.11555],[71.89921,27.96035],[70.79054,27.68423],[70.60927,28.02178],[70.37307,28.01208],[70.12502,27.8057],[70.03136,27.56627],[69.58519,27.18109],[69.50904,26.74892],[69.88555,26.56836],[70.05584,26.60398],[70.17532,26.55362],[70.17532,26.24118],[70.08193,26.08094],[70.0985,25.93238],[70.2687,25.71156],[70.37444,25.67443],[70.53649,25.68928],[70.60378,25.71898],[70.67382,25.68186],[70.66695,25.39314],[70.89148,25.15064],[70.94002,24.92843],[71.09405,24.69017],[70.97594,24.60904],[71.00341,24.46038],[71.12838,24.42662],[71.04461,24.34657],[70.94985,24.3791],[70.85784,24.30903],[70.88393,24.27398],[70.71502,24.23517],[70.57906,24.27774],[70.5667,24.43787],[70.11712,24.30915],[70.03428,24.172],[69.73335,24.17007],[69.59579,24.29777],[69.29778,24.28712],[69.19341,24.25646],[69.07806,24.29777],[68.97781,24.26021],[68.90914,24.33156],[68.7416,24.31904],[68.74643,23.97027],[68.39339,23.96838],[68.20763,23.85849],[68.11329,23.53945],[72.15131,7.6285],[78.52781,7.63099],[79.50447,8.91876],[79.42124,9.80115],[80.48418,10.20786],[94.53911,5.99016],[94.6371,13.81803],[92.61042,13.76986],[89.13606,21.42955],[89.13927,21.60785],[89.03553,21.77397],[89.07114,22.15335],[88.9367,22.58527],[88.94614,22.66941],[88.9151,22.75228],[88.96713,22.83346],[88.87063,22.95235],[88.88327,23.03885],[88.86377,23.08759],[88.99148,23.21134],[88.71133,23.2492],[88.79254,23.46028],[88.79351,23.50535],[88.74841,23.47361],[88.56507,23.64044],[88.58087,23.87105],[88.66189,23.87607],[88.73743,23.91751],[88.6976,24.14703],[88.74841,24.1959],[88.68801,24.31464],[88.50934,24.32474],[88.12296,24.51301],[88.08786,24.63232],[88.00683,24.66477],[88.15515,24.85806],[88.14004,24.93529],[88.21832,24.96642],[88.27325,24.88796],[88.33917,24.86803],[88.46277,25.07468],[88.44766,25.20149],[88.94067,25.18534],[89.00463,25.26583],[89.01105,25.30303],[88.85278,25.34679],[88.81296,25.51546],[88.677,25.46959],[88.4559,25.59227],[88.45103,25.66245],[88.242,25.80811],[88.13138,25.78773],[88.08804,25.91334],[88.16581,26.0238],[88.1844,26.14417],[88.34757,26.22216],[88.35153,26.29123],[88.51649,26.35923],[88.48749,26.45855],[88.36938,26.48683],[88.35153,26.45241],[88.33093,26.48929],[88.41196,26.63837],[88.4298,26.54489],[88.62144,26.46783],[88.69485,26.38353],[88.67837,26.26291],[88.78961,26.31093],[88.85004,26.23211],[89.05328,26.2469],[88.91321,26.37984],[88.92357,26.40711],[88.95612,26.4564],[89.08899,26.38845],[89.15869,26.13708],[89.35953,26.0077],[89.53515,26.00382],[89.57101,25.9682],[89.63968,26.22595],[89.70201,26.15138],[89.73581,26.15818],[89.77865,26.08387],[89.77728,26.04254],[89.86592,25.93115],[89.80585,25.82489],[89.84388,25.70042],[89.86129,25.61714],[89.81208,25.37244],[89.84086,25.31854],[89.83371,25.29548],[89.87629,25.28337],[89.90478,25.31038],[90.1155,25.22686],[90.40034,25.1534],[90.65042,25.17788],[90.87427,25.15799],[91.25517,25.20677],[91.63648,25.12846],[92.0316,25.1834],[92.33957,25.07593],[92.39147,25.01471],[92.49887,24.88796],[92.38626,24.86055],[92.25854,24.9191],[92.15796,24.54435],[92.11662,24.38997],[91.96603,24.3799],[91.89258,24.14674],[91.82596,24.22345],[91.76004,24.23848],[91.73257,24.14703],[91.65292,24.22095],[91.63782,24.1132],[91.55542,24.08687],[91.37414,24.10693],[91.35741,23.99072],[91.29587,24.0041],[91.22308,23.89616],[91.25192,23.83463],[91.15579,23.6599],[91.28293,23.37538],[91.36453,23.06612],[91.40848,23.07117],[91.4035,23.27522],[91.46615,23.2328],[91.54993,23.01051],[91.61571,22.93929],[91.7324,23.00043],[91.81634,23.08001],[91.76417,23.26619],[91.84789,23.42235],[91.95642,23.47361],[91.95093,23.73284],[92.04706,23.64229],[92.15417,23.73409],[92.26541,23.70392],[92.38214,23.28705],[92.37665,22.9435],[92.5181,22.71441],[92.60029,22.1522],[92.56616,22.13554],[92.60949,21.97638],[92.67532,22.03547],[92.70416,22.16017],[92.86208,22.05456],[92.89504,21.95143],[92.93899,22.02656],[92.99804,21.98964],[92.99255,22.05965],[93.04885,22.20595],[93.15734,22.18687],[93.14224,22.24535],[93.19991,22.25425],[93.18206,22.43716],[93.13537,22.45873],[93.11477,22.54374],[93.134,22.59573],[93.09417,22.69459],[93.134,22.92498],[93.12988,23.05772],[93.2878,23.00464],[93.38478,23.13698],[93.36862,23.35426],[93.38781,23.36139],[93.39981,23.38828],[93.38805,23.4728],[93.43475,23.68299],[93.3908,23.7622],[93.3908,23.92925],[93.36059,23.93176],[93.32351,24.04468],[93.34735,24.10151],[93.41415,24.07854],[93.46633,23.97067],[93.50616,23.94432],[93.62871,24.00922],[93.75952,24.0003],[93.80279,23.92549],[93.92089,23.95812],[94.14081,23.83333],[94.30215,24.23752],[94.32362,24.27692],[94.45279,24.56656],[94.50729,24.59281],[94.5526,24.70764],[94.60204,24.70889],[94.73937,25.00545],[94.74212,25.13606],[94.57458,25.20318],[94.68032,25.47003],[94.80117,25.49359],[95.18556,26.07338],[95.11428,26.1019],[95.12801,26.38397],[95.05798,26.45408],[95.23513,26.68499],[95.30339,26.65372],[95.437,26.7083],[95.81603,27.01335],[95.93002,27.04149],[96.04949,27.19428],[96.15591,27.24572],[96.40779,27.29818],[96.55761,27.29928],[96.73888,27.36638],[96.88445,27.25046],[96.85287,27.2065],[96.89132,27.17474],[97.14675,27.09041],[97.17422,27.14052],[96.91431,27.45752],[96.90112,27.62149],[97.29919,27.92233],[97.35824,27.87256],[97.38845,28.01329],[97.35412,28.06663],[97.31292,28.06784],[97.34547,28.21385],[97.1289,28.3619],[96.98882,28.32564],[96.88445,28.39452],[96.85561,28.4875],[96.6455,28.61657],[96.48895,28.42955],[96.40929,28.51526],[96.61391,28.72742],[96.3626,29.10607],[96.20467,29.02325],[96.18682,29.11087],[96.31316,29.18643],[96.05361,29.38167],[95.84899,29.31464],[95.75149,29.32063],[95.72086,29.20797],[95.50842,29.13487],[95.41091,29.13007],[95.3038,29.13847],[95.26122,29.07727],[95.2214,29.10727],[95.11291,29.09527],[95.0978,29.14446],[94.81353,29.17804],[94.69318,29.31739],[94.2752,29.11687],[94.35897,29.01965],[93.72797,28.68821],[93.44621,28.67189],[93.18069,28.50319],[93.14635,28.37035],[92.93075,28.25671],[92.67486,28.15018],[92.65472,28.07632],[92.73025,28.05814],[92.7275,27.98662],[92.42538,27.80092],[92.32101,27.79363],[92.27432,27.89077],[91.87057,27.7195],[91.84722,27.76325],[91.6469,27.76358],[91.55819,27.6144],[91.65007,27.48287],[92.01132,27.47352],[92.12019,27.27829],[92.04702,27.26861],[92.03457,27.07334],[92.11863,26.893],[92.05523,26.8692],[91.83181,26.87318],[91.50067,26.79223],[90.67715,26.77215],[90.48504,26.8594],[90.39271,26.90704],[90.30402,26.85098],[90.04535,26.72422],[89.86124,26.73307],[89.63369,26.74402],[89.42349,26.83727],[89.3901,26.84225],[89.38319,26.85963],[89.37913,26.86224],[89.1926,26.81329],[89.12825,26.81661],[89.09554,26.89089],[88.95807,26.92668],[88.92301,26.99286],[88.8714,26.97488],[88.86984,27.10937],[88.74219,27.144],[88.91901,27.32483],[88.82981,27.38814],[88.77517,27.45415],[88.88091,27.85192],[88.83559,28.01936],[88.63235,28.12356],[88.54858,28.06057],[88.25332,27.9478],[88.1278,27.95417],[88.13378,27.88015],[88.1973,27.85067],[88.19107,27.79285],[88.04008,27.49223],[88.07277,27.43007],[88.01646,27.21612],[88.01587,27.21388],[87.9887,27.11045],[88.11719,26.98758],[88.13422,26.98705],[88.12302,26.95324],[88.19107,26.75516],[88.1659,26.68177],[88.16452,26.64111],[88.09963,26.54195],[88.09414,26.43732],[88.00895,26.36029],[87.90115,26.44923],[87.89085,26.48565],[87.84193,26.43663],[87.7918,26.46737],[87.76004,26.40711],[87.67893,26.43501],[87.66803,26.40294],[87.59175,26.38342],[87.55274,26.40596],[87.51571,26.43106],[87.46566,26.44058],[87.37314,26.40815],[87.34568,26.34787],[87.26568,26.37294],[87.26587,26.40592],[87.24682,26.4143],[87.18863,26.40558],[87.14751,26.40542],[87.09147,26.45039],[87.0707,26.58571],[87.04691,26.58685],[87.01559,26.53228],[86.95912,26.52076],[86.94543,26.52076],[86.82898,26.43919],[86.76797,26.45892],[86.74025,26.42386],[86.69124,26.45169],[86.62686,26.46891],[86.61313,26.48658],[86.57073,26.49825],[86.54258,26.53819],[86.49726,26.54218],[86.31564,26.61925],[86.26235,26.61886],[86.22513,26.58863],[86.13596,26.60651],[86.02729,26.66756],[85.8492,26.56667],[85.85126,26.60866],[85.83126,26.61134],[85.76907,26.63076],[85.72315,26.67471],[85.73483,26.79613],[85.66239,26.84822],[85.61621,26.86721],[85.59461,26.85161],[85.5757,26.85955],[85.56471,26.84133],[85.47752,26.79292],[85.34302,26.74954],[85.21159,26.75933],[85.18046,26.80519],[85.19291,26.86909],[85.15883,26.86966],[85.02635,26.85381],[85.05592,26.88991],[85.00536,26.89523],[84.97186,26.9149],[84.96687,26.95599],[84.85754,26.98984],[84.82913,27.01989],[84.793,26.9968],[84.64496,27.04669],[84.69166,27.21294],[84.62161,27.33885],[84.29315,27.39],[84.25735,27.44941],[84.21376,27.45218],[84.10791,27.52399],[84.02229,27.43836],[83.93306,27.44939],[83.86182,27.4241],[83.85595,27.35797],[83.61288,27.47013],[83.39495,27.4798],[83.38872,27.39276],[83.35136,27.33885],[83.29999,27.32778],[83.2673,27.36235],[83.27197,27.38309],[83.19413,27.45632],[82.94938,27.46036],[82.93261,27.50328],[82.74119,27.49838],[82.70378,27.72122],[82.46405,27.6716],[82.06554,27.92222],[81.97214,27.93322],[81.91223,27.84995],[81.47867,28.08303],[81.48179,28.12148],[81.38683,28.17638],[81.32923,28.13521],[81.19847,28.36284],[81.08507,28.38346],[80.89648,28.47237],[80.55142,28.69182],[80.50575,28.6706],[80.52443,28.54897],[80.44504,28.63098],[80.37188,28.63371],[80.12125,28.82346],[80.06957,28.82763],[80.05743,28.91479],[80.18085,29.13649],[80.23178,29.11626],[80.26602,29.13938],[80.24112,29.21414],[80.28626,29.20327],[80.31428,29.30784],[80.24322,29.44299],[80.37939,29.57098],[80.41858,29.63581],[80.38428,29.68513],[80.36803,29.73865],[80.41554,29.79451],[80.43458,29.80466],[80.48997,29.79566],[80.56247,29.86661],[80.56957,29.88176],[80.60226,29.95732],[80.67076,29.95732],[80.8778,30.13384],[80.93695,30.18229],[81.03953,30.20059],[80.83343,30.32023],[80.54504,30.44936],[80.20721,30.58541],[79.93255,30.88288],[79.59884,30.93943],[79.22805,31.34963],[79.14016,31.43403],[79.01931,31.42817],[78.77898,31.31209],[78.71032,31.50197],[78.84516,31.60631],[78.69933,31.78723],[78.78036,31.99478],[78.74404,32.00384],[78.68754,32.10256],[78.49609,32.2762],[78.4645,32.45367],[78.38897,32.53938],[78.73916,32.69438],[78.7831,32.46873],[78.96713,32.33655],[78.99322,32.37948],[79.0979,32.38051],[79.13174,32.47766],[79.26768,32.53277],[79.46562,32.69668],[79.14016,33.02545],[79.15252,33.17156],[78.73636,33.56521],[78.67599,33.66445],[78.77349,33.73871],[78.73367,34.01121],[78.65657,34.03195],[78.66225,34.08858],[78.91769,34.15452],[78.99802,34.3027],[79.05364,34.32482],[78.74465,34.45174],[78.56475,34.50835],[78.54964,34.57283],[78.27781,34.61484],[78.18435,34.7998],[78.22692,34.88771],[78.00033,35.23954],[78.03466,35.3785],[78.11664,35.48022]]]]}},{type:"Feature",properties:{iso1A2:"IO",iso1A3:"IOT",iso1N3:"086",wikidata:"Q43448",nameEn:"British Indian Ocean Territory",country:"GB",groups:["014","202","002"],callingCodes:["246"]},geometry:{type:"MultiPolygon",coordinates:[[[[70.64754,-4.95745],[70.67958,-8.2663],[73.70488,-4.92492],[70.64754,-4.95745]]]]}},{type:"Feature",properties:{iso1A2:"IQ",iso1A3:"IRQ",iso1N3:"368",wikidata:"Q796",nameEn:"Iraq",groups:["145","142"],callingCodes:["964"]},geometry:{type:"MultiPolygon",coordinates:[[[[42.78887,37.38615],[42.56725,37.14878],[42.35724,37.10998],[42.36697,37.0627],[41.81736,36.58782],[41.40058,36.52502],[41.28864,36.35368],[41.2564,36.06012],[41.37027,35.84095],[41.38184,35.62502],[41.26569,35.42708],[41.21654,35.1508],[41.2345,34.80049],[41.12388,34.65742],[40.97676,34.39788],[40.64314,34.31604],[38.79171,33.37328],[39.08202,32.50304],[38.98762,32.47694],[39.04251,32.30203],[39.26157,32.35555],[39.29903,32.23259],[40.01521,32.05667],[42.97601,30.72204],[42.97796,30.48295],[44.72255,29.19736],[46.42415,29.05947],[46.5527,29.10283],[46.89695,29.50584],[47.15166,30.01044],[47.37192,30.10421],[47.7095,30.10453],[48.01114,29.98906],[48.06782,30.02906],[48.17332,30.02448],[48.40479,29.85763],[48.59531,29.66815],[48.83867,29.78572],[48.61441,29.93675],[48.51011,29.96238],[48.44785,30.00148],[48.4494,30.04456],[48.43384,30.08233],[48.38869,30.11062],[48.38714,30.13485],[48.41671,30.17254],[48.41117,30.19846],[48.26393,30.3408],[48.24385,30.33846],[48.21279,30.31644],[48.19425,30.32796],[48.18321,30.39703],[48.14585,30.44133],[48.02443,30.4789],[48.03221,30.9967],[47.68219,31.00004],[47.6804,31.39086],[47.86337,31.78422],[47.64771,32.07666],[47.52474,32.15972],[47.57144,32.20583],[47.37529,32.47808],[47.17218,32.45393],[46.46788,32.91992],[46.32298,32.9731],[46.17198,32.95612],[46.09103,32.98354],[46.15175,33.07229],[46.03966,33.09577],[46.05367,33.13097],[46.11905,33.11924],[46.20623,33.20395],[45.99919,33.5082],[45.86687,33.49263],[45.96183,33.55751],[45.89801,33.63661],[45.77814,33.60938],[45.50261,33.94968],[45.42789,33.9458],[45.41077,33.97421],[45.47264,34.03099],[45.56176,34.15088],[45.58667,34.30147],[45.53552,34.35148],[45.49171,34.3439],[45.46697,34.38221],[45.43879,34.45949],[45.51883,34.47692],[45.53219,34.60441],[45.59074,34.55558],[45.60224,34.55057],[45.73923,34.54416],[45.70031,34.69277],[45.65672,34.7222],[45.68284,34.76624],[45.70031,34.82322],[45.73641,34.83975],[45.79682,34.85133],[45.78904,34.91135],[45.86532,34.89858],[45.89477,34.95805],[45.87864,35.03441],[45.92173,35.0465],[45.92203,35.09538],[45.93108,35.08148],[45.94756,35.09188],[46.06508,35.03699],[46.07747,35.0838],[46.11763,35.07551],[46.19116,35.11097],[46.15642,35.1268],[46.16229,35.16984],[46.19738,35.18536],[46.18457,35.22561],[46.11367,35.23729],[46.15474,35.2883],[46.13152,35.32548],[46.05358,35.38568],[45.98453,35.49848],[46.01518,35.52012],[45.97584,35.58132],[46.03028,35.57416],[46.01307,35.59756],[46.0165,35.61501],[45.99452,35.63574],[46.0117,35.65059],[46.01631,35.69139],[46.23736,35.71414],[46.34166,35.78363],[46.32921,35.82655],[46.17198,35.8013],[46.08325,35.8581],[45.94711,35.82218],[45.89784,35.83708],[45.81442,35.82107],[45.76145,35.79898],[45.6645,35.92872],[45.60018,35.96069],[45.55245,35.99943],[45.46594,36.00042],[45.38275,35.97156],[45.33916,35.99424],[45.37652,36.06222],[45.37312,36.09917],[45.32235,36.17383],[45.30038,36.27769],[45.26261,36.3001],[45.27394,36.35846],[45.23953,36.43257],[45.11811,36.40751],[45.00759,36.5402],[45.06985,36.62645],[45.06985,36.6814],[45.01537,36.75128],[44.84725,36.77622],[44.83479,36.81362],[44.90173,36.86096],[44.91199,36.91468],[44.89862,37.01897],[44.81611,37.04383],[44.75229,37.11958],[44.78319,37.1431],[44.76698,37.16162],[44.63179,37.19229],[44.42631,37.05825],[44.38117,37.05825],[44.35315,37.04955],[44.35937,37.02843],[44.30645,36.97373],[44.25975,36.98119],[44.18503,37.09551],[44.22239,37.15756],[44.27998,37.16501],[44.2613,37.25055],[44.13521,37.32486],[44.02002,37.33229],[43.90949,37.22453],[43.84878,37.22205],[43.82699,37.19477],[43.8052,37.22825],[43.7009,37.23692],[43.63085,37.21957],[43.56702,37.25675],[43.50787,37.24436],[43.33508,37.33105],[43.30083,37.30629],[43.11403,37.37436],[42.93705,37.32015],[42.78887,37.38615]]]]}},{type:"Feature",properties:{iso1A2:"IR",iso1A3:"IRN",iso1N3:"364",wikidata:"Q794",nameEn:"Iran",groups:["034","142"],callingCodes:["98"]},geometry:{type:"MultiPolygon",coordinates:[[[[44.96746,39.42998],[44.88916,39.59653],[44.81043,39.62677],[44.71806,39.71124],[44.65422,39.72163],[44.6137,39.78393],[44.47298,39.68788],[44.48111,39.61579],[44.41849,39.56659],[44.42832,39.4131],[44.37921,39.4131],[44.29818,39.378],[44.22452,39.4169],[44.03667,39.39223],[44.1043,39.19842],[44.20946,39.13975],[44.18863,38.93881],[44.30322,38.81581],[44.26155,38.71427],[44.28065,38.6465],[44.32058,38.62752],[44.3207,38.49799],[44.3119,38.37887],[44.38309,38.36117],[44.44386,38.38295],[44.50115,38.33939],[44.42476,38.25763],[44.22509,37.88859],[44.3883,37.85433],[44.45948,37.77065],[44.55498,37.783],[44.62096,37.71985],[44.56887,37.6429],[44.61401,37.60165],[44.58449,37.45018],[44.81021,37.2915],[44.75986,37.21549],[44.7868,37.16644],[44.78319,37.1431],[44.75229,37.11958],[44.81611,37.04383],[44.89862,37.01897],[44.91199,36.91468],[44.90173,36.86096],[44.83479,36.81362],[44.84725,36.77622],[45.01537,36.75128],[45.06985,36.6814],[45.06985,36.62645],[45.00759,36.5402],[45.11811,36.40751],[45.23953,36.43257],[45.27394,36.35846],[45.26261,36.3001],[45.30038,36.27769],[45.32235,36.17383],[45.37312,36.09917],[45.37652,36.06222],[45.33916,35.99424],[45.38275,35.97156],[45.46594,36.00042],[45.55245,35.99943],[45.60018,35.96069],[45.6645,35.92872],[45.76145,35.79898],[45.81442,35.82107],[45.89784,35.83708],[45.94711,35.82218],[46.08325,35.8581],[46.17198,35.8013],[46.32921,35.82655],[46.34166,35.78363],[46.23736,35.71414],[46.01631,35.69139],[46.0117,35.65059],[45.99452,35.63574],[46.0165,35.61501],[46.01307,35.59756],[46.03028,35.57416],[45.97584,35.58132],[46.01518,35.52012],[45.98453,35.49848],[46.05358,35.38568],[46.13152,35.32548],[46.15474,35.2883],[46.11367,35.23729],[46.18457,35.22561],[46.19738,35.18536],[46.16229,35.16984],[46.15642,35.1268],[46.19116,35.11097],[46.11763,35.07551],[46.07747,35.0838],[46.06508,35.03699],[45.94756,35.09188],[45.93108,35.08148],[45.92203,35.09538],[45.92173,35.0465],[45.87864,35.03441],[45.89477,34.95805],[45.86532,34.89858],[45.78904,34.91135],[45.79682,34.85133],[45.73641,34.83975],[45.70031,34.82322],[45.68284,34.76624],[45.65672,34.7222],[45.70031,34.69277],[45.73923,34.54416],[45.60224,34.55057],[45.59074,34.55558],[45.53219,34.60441],[45.51883,34.47692],[45.43879,34.45949],[45.46697,34.38221],[45.49171,34.3439],[45.53552,34.35148],[45.58667,34.30147],[45.56176,34.15088],[45.47264,34.03099],[45.41077,33.97421],[45.42789,33.9458],[45.50261,33.94968],[45.77814,33.60938],[45.89801,33.63661],[45.96183,33.55751],[45.86687,33.49263],[45.99919,33.5082],[46.20623,33.20395],[46.11905,33.11924],[46.05367,33.13097],[46.03966,33.09577],[46.15175,33.07229],[46.09103,32.98354],[46.17198,32.95612],[46.32298,32.9731],[46.46788,32.91992],[47.17218,32.45393],[47.37529,32.47808],[47.57144,32.20583],[47.52474,32.15972],[47.64771,32.07666],[47.86337,31.78422],[47.6804,31.39086],[47.68219,31.00004],[48.03221,30.9967],[48.02443,30.4789],[48.14585,30.44133],[48.18321,30.39703],[48.19425,30.32796],[48.21279,30.31644],[48.24385,30.33846],[48.26393,30.3408],[48.41117,30.19846],[48.41671,30.17254],[48.38714,30.13485],[48.38869,30.11062],[48.43384,30.08233],[48.4494,30.04456],[48.44785,30.00148],[48.51011,29.96238],[48.61441,29.93675],[48.83867,29.78572],[49.98877,27.87827],[50.37726,27.89227],[54.39838,25.68383],[55.14145,25.62624],[55.81777,26.18798],[56.2644,26.58649],[56.68954,26.76645],[56.79239,26.41236],[56.82555,25.7713],[56.86325,25.03856],[61.5251,24.57287],[61.57592,25.0492],[61.6433,25.27541],[61.683,25.66638],[61.83968,25.7538],[61.83831,26.07249],[61.89391,26.26251],[62.05117,26.31647],[62.21304,26.26601],[62.31484,26.528],[62.77352,26.64099],[63.1889,26.65072],[63.18688,26.83844],[63.25005,26.84212],[63.25005,27.08692],[63.32283,27.14437],[63.19649,27.25674],[62.80604,27.22412],[62.79684,27.34381],[62.84905,27.47627],[62.7638,28.02992],[62.79412,28.28108],[62.59499,28.24842],[62.40259,28.42703],[61.93581,28.55284],[61.65978,28.77937],[61.53765,29.00507],[61.31508,29.38903],[60.87231,29.86514],[61.80829,30.84224],[61.78268,30.92724],[61.8335,30.97669],[61.83257,31.0452],[61.80957,31.12576],[61.80569,31.16167],[61.70929,31.37391],[60.84541,31.49561],[60.86191,32.22565],[60.56485,33.12944],[60.88908,33.50219],[60.91133,33.55596],[60.69573,33.56054],[60.57762,33.59772],[60.5485,33.73422],[60.5838,33.80793],[60.50209,34.13992],[60.66502,34.31539],[60.91321,34.30411],[60.72316,34.52857],[60.99922,34.63064],[61.00197,34.70631],[61.06926,34.82139],[61.12831,35.09938],[61.0991,35.27845],[61.18187,35.30249],[61.27371,35.61482],[61.22719,35.67038],[61.26152,35.80749],[61.22444,35.92879],[61.12007,35.95992],[61.22719,36.12759],[61.1393,36.38782],[61.18187,36.55348],[61.14516,36.64644],[60.34767,36.63214],[60.00768,37.04102],[59.74678,37.12499],[59.55178,37.13594],[59.39385,37.34257],[59.39797,37.47892],[59.33507,37.53146],[59.22905,37.51161],[58.9338,37.67374],[58.6921,37.64548],[58.5479,37.70526],[58.47786,37.6433],[58.39959,37.63134],[58.22999,37.6856],[58.21399,37.77281],[57.79534,37.89299],[57.35042,37.98546],[57.37236,38.09321],[57.21169,38.28965],[57.03453,38.18717],[56.73928,38.27887],[56.62255,38.24005],[56.43303,38.26054],[56.32454,38.18502],[56.33278,38.08132],[55.97847,38.08024],[55.76561,38.12238],[55.44152,38.08564],[55.13412,37.94705],[54.851,37.75739],[54.77684,37.62264],[54.81804,37.61285],[54.77822,37.51597],[54.67247,37.43532],[54.58664,37.45809],[54.36211,37.34912],[54.24565,37.32047],[53.89734,37.3464],[48.88288,38.43975],[48.84969,38.45015],[48.81072,38.44853],[48.78979,38.45026],[48.70001,38.40564],[48.62217,38.40198],[48.58793,38.45076],[48.45084,38.61013],[48.3146,38.59958],[48.24773,38.71883],[48.02581,38.82705],[48.01409,38.90333],[48.07734,38.91616],[48.08627,38.94434],[48.28437,38.97186],[48.33884,39.03022],[48.31239,39.09278],[48.15361,39.19419],[48.12404,39.25208],[48.15984,39.30028],[48.37385,39.37584],[48.34264,39.42935],[47.98977,39.70999],[47.84774,39.66285],[47.50099,39.49615],[47.38978,39.45999],[47.31301,39.37492],[47.05927,39.24846],[47.05771,39.20143],[46.95341,39.13505],[46.92539,39.16644],[46.83822,39.13143],[46.75752,39.03231],[46.53497,38.86548],[46.34059,38.92076],[46.20601,38.85262],[46.14785,38.84206],[46.06766,38.87861],[46.00228,38.87376],[45.94624,38.89072],[45.90266,38.87739],[45.83883,38.90768],[45.65172,38.95199],[45.6155,38.94304],[45.6131,38.964],[45.44966,38.99243],[45.44811,39.04927],[45.40452,39.07224],[45.40148,39.09007],[45.30489,39.18333],[45.16168,39.21952],[45.08751,39.35052],[45.05932,39.36435],[44.96746,39.42998]]]]}},{type:"Feature",properties:{iso1A2:"IS",iso1A3:"ISL",iso1N3:"352",wikidata:"Q189",nameEn:"Iceland",groups:["154","150"],callingCodes:["354"]},geometry:{type:"MultiPolygon",coordinates:[[[[-33.15676,62.62995],[-8.25539,63.0423],[-15.70914,69.67442],[-33.15676,62.62995]]]]}},{type:"Feature",properties:{iso1A2:"IT",iso1A3:"ITA",iso1N3:"380",wikidata:"Q38",nameEn:"Italy",groups:["EU","039","150"],callingCodes:["39"]},geometry:{type:"MultiPolygon",coordinates:[[[[8.95861,45.96485],[8.97604,45.96151],[8.97741,45.98317],[8.96668,45.98436],[8.95861,45.96485]]],[[[7.63035,43.57419],[9.56115,43.20816],[10.09675,41.44089],[7.60802,41.05927],[7.89009,38.19924],[11.2718,37.6713],[12.13667,34.20326],[14.02721,36.53141],[17.67657,35.68918],[18.83516,40.36999],[16.15283,42.18525],[13.12821,44.48877],[13.05142,45.33128],[13.45644,45.59464],[13.6076,45.64761],[13.7198,45.59352],[13.74587,45.59811],[13.78445,45.5825],[13.84106,45.58185],[13.86771,45.59898],[13.8695,45.60835],[13.9191,45.6322],[13.87933,45.65207],[13.83422,45.68703],[13.83332,45.70855],[13.8235,45.7176],[13.66986,45.79955],[13.59784,45.8072],[13.58858,45.83503],[13.57563,45.8425],[13.58644,45.88173],[13.59565,45.89446],[13.60857,45.89907],[13.61931,45.91782],[13.63815,45.93607],[13.6329,45.94894],[13.64307,45.98326],[13.63458,45.98947],[13.62074,45.98388],[13.58903,45.99009],[13.56759,45.96991],[13.52963,45.96588],[13.50104,45.98078],[13.47474,46.00546],[13.49702,46.01832],[13.50998,46.04498],[13.49568,46.04839],[13.50104,46.05986],[13.57072,46.09022],[13.64053,46.13587],[13.66472,46.17392],[13.64451,46.18966],[13.56682,46.18703],[13.56114,46.2054],[13.47587,46.22725],[13.42218,46.20758],[13.37671,46.29668],[13.44808,46.33507],[13.43418,46.35992],[13.47019,46.3621],[13.5763,46.40915],[13.5763,46.42613],[13.59777,46.44137],[13.68684,46.43881],[13.7148,46.5222],[13.64088,46.53438],[13.27627,46.56059],[12.94445,46.60401],[12.59992,46.6595],[12.38708,46.71529],[12.27591,46.88651],[12.2006,46.88854],[12.11675,47.01241],[12.21781,47.03996],[12.19254,47.09331],[11.74789,46.98484],[11.50739,47.00644],[11.33355,46.99862],[11.10618,46.92966],[11.00764,46.76896],[10.72974,46.78972],[10.75753,46.82258],[10.66405,46.87614],[10.54783,46.84505],[10.47197,46.85698],[10.38659,46.67847],[10.40475,46.63671],[10.44686,46.64162],[10.49375,46.62049],[10.46136,46.53164],[10.25309,46.57432],[10.23674,46.63484],[10.10307,46.61003],[10.03715,46.44479],[10.165,46.41051],[10.10506,46.3372],[10.17862,46.25626],[10.14439,46.22992],[10.07055,46.21668],[9.95249,46.38045],[9.73086,46.35071],[9.71273,46.29266],[9.57015,46.2958],[9.46117,46.37481],[9.45936,46.50873],[9.40487,46.46621],[9.36128,46.5081],[9.28136,46.49685],[9.25502,46.43743],[9.29226,46.32717],[9.24503,46.23616],[9.01618,46.04928],[8.99257,45.9698],[9.09065,45.89906],[9.06642,45.8761],[9.04546,45.84968],[9.04059,45.8464],[9.03505,45.83976],[9.03793,45.83548],[9.03279,45.82865],[9.0298,45.82127],[9.00324,45.82055],[8.99663,45.83466],[8.9621,45.83707],[8.94737,45.84285],[8.91129,45.8388],[8.93504,45.86245],[8.94372,45.86587],[8.93649,45.86775],[8.88904,45.95465],[8.86688,45.96135],[8.85121,45.97239],[8.8319,45.9879],[8.79362,45.99207],[8.78585,45.98973],[8.79414,46.00913],[8.85617,46.0748],[8.80778,46.10085],[8.75697,46.10395],[8.62242,46.12112],[8.45032,46.26869],[8.46317,46.43712],[8.42464,46.46367],[8.30648,46.41587],[8.31162,46.38044],[8.08814,46.26692],[8.16866,46.17817],[8.11383,46.11577],[8.02906,46.10331],[7.98881,45.99867],[7.9049,45.99945],[7.85949,45.91485],[7.56343,45.97421],[7.10685,45.85653],[7.04151,45.92435],[6.95315,45.85163],[6.80785,45.83265],[6.80785,45.71864],[6.98948,45.63869],[7.00037,45.509],[7.18019,45.40071],[7.10572,45.32924],[7.13115,45.25386],[7.07074,45.21228],[6.96706,45.20841],[6.85144,45.13226],[6.7697,45.16044],[6.62803,45.11175],[6.66981,45.02324],[6.74791,45.01939],[6.74519,44.93661],[6.75518,44.89915],[6.90774,44.84322],[6.93499,44.8664],[7.02217,44.82519],[7.00401,44.78782],[7.07484,44.68073],[7.00582,44.69364],[6.95133,44.66264],[6.96042,44.62129],[6.85507,44.53072],[6.86233,44.49834],[6.94504,44.43112],[6.88784,44.42043],[6.89171,44.36637],[6.98221,44.28289],[7.00764,44.23736],[7.16929,44.20352],[7.27827,44.1462],[7.34547,44.14359],[7.36364,44.11882],[7.62155,44.14881],[7.63245,44.17877],[7.68694,44.17487],[7.66878,44.12795],[7.72508,44.07578],[7.6597,44.03009],[7.66848,43.99943],[7.65266,43.9763],[7.60771,43.95772],[7.56858,43.94506],[7.56075,43.89932],[7.51162,43.88301],[7.49355,43.86551],[7.50423,43.84345],[7.53006,43.78405],[7.63035,43.57419]],[[12.45181,41.90056],[12.44834,41.90095],[12.44582,41.90194],[12.44815,41.90326],[12.44984,41.90545],[12.45091,41.90625],[12.45543,41.90738],[12.45561,41.90629],[12.45762,41.9058],[12.45755,41.9033],[12.45826,41.90281],[12.45834,41.90174],[12.4577,41.90115],[12.45691,41.90125],[12.45626,41.90172],[12.45435,41.90143],[12.45446,41.90028],[12.45181,41.90056]],[[12.45648,43.89369],[12.44184,43.90498],[12.41641,43.89991],[12.40935,43.9024],[12.41233,43.90956],[12.40733,43.92379],[12.41551,43.92984],[12.41165,43.93769],[12.40506,43.94325],[12.40415,43.95485],[12.41414,43.95273],[12.42005,43.9578],[12.43662,43.95698],[12.44684,43.96597],[12.46205,43.97463],[12.47853,43.98052],[12.49406,43.98492],[12.50678,43.99113],[12.51463,43.99122],[12.5154,43.98508],[12.51064,43.98165],[12.51109,43.97201],[12.50622,43.97131],[12.50875,43.96198],[12.50655,43.95796],[12.51427,43.94897],[12.51553,43.94096],[12.50496,43.93017],[12.50269,43.92363],[12.49724,43.92248],[12.49247,43.91774],[12.49429,43.90973],[12.48771,43.89706],[12.45648,43.89369]]]]}},{type:"Feature",properties:{iso1A2:"JE",iso1A3:"JEY",iso1N3:"832",wikidata:"Q785",nameEn:"Jersey",country:"GB",groups:["830","154","150"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44 01534"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.00491,48.86706],[-1.83944,49.23037],[-2.09454,49.46288],[-2.65349,49.15373],[-2.00491,48.86706]]]]}},{type:"Feature",properties:{iso1A2:"JM",iso1A3:"JAM",iso1N3:"388",wikidata:"Q766",nameEn:"Jamaica",aliases:["JA"],groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 876","1 658"]},geometry:{type:"MultiPolygon",coordinates:[[[[-75.50728,17.08879],[-76.34192,18.86145],[-78.75694,18.78765],[-78.34606,16.57862],[-75.50728,17.08879]]]]}},{type:"Feature",properties:{iso1A2:"JO",iso1A3:"JOR",iso1N3:"400",wikidata:"Q810",nameEn:"Jordan",groups:["145","142"],callingCodes:["962"]},geometry:{type:"MultiPolygon",coordinates:[[[[39.04251,32.30203],[38.98762,32.47694],[39.08202,32.50304],[38.79171,33.37328],[36.83946,32.31293],[36.40959,32.37908],[36.23948,32.50108],[36.20875,32.49529],[36.20379,32.52751],[36.08074,32.51463],[36.02239,32.65911],[35.96633,32.66237],[35.93307,32.71966],[35.88405,32.71321],[35.75983,32.74803],[35.68467,32.70715],[35.66527,32.681],[35.61669,32.67999],[35.59813,32.65159],[35.56614,32.64393],[35.57485,32.48669],[35.55494,32.42687],[35.55807,32.38674],[35.57111,32.21877],[35.52012,32.04076],[35.54375,31.96587],[35.52758,31.9131],[35.55941,31.76535],[35.47672,31.49578],[35.40316,31.25535],[35.43658,31.12444],[35.41371,30.95565],[35.33984,30.8802],[35.33456,30.81224],[35.29311,30.71365],[35.21379,30.60401],[35.19595,30.50297],[35.16218,30.43535],[35.19183,30.34636],[35.14108,30.07374],[35.02147,29.66343],[34.98207,29.58147],[34.97718,29.54294],[34.92298,29.45305],[34.88293,29.37455],[34.95987,29.35727],[36.07081,29.18469],[36.50005,29.49696],[36.75083,29.86903],[37.4971,29.99949],[37.66395,30.33245],[37.99354,30.49998],[36.99791,31.50081],[38.99233,31.99721],[39.29903,32.23259],[39.26157,32.35555],[39.04251,32.30203]]]]}},{type:"Feature",properties:{iso1A2:"JP",iso1A3:"JPN",iso1N3:"392",wikidata:"Q17",nameEn:"Japan",groups:["030","142"],driveSide:"left",callingCodes:["81"]},geometry:{type:"MultiPolygon",coordinates:[[[[145.82361,43.38904],[145.23667,43.76813],[145.82343,44.571],[140.9182,45.92937],[133.61399,37.41],[129.2669,34.87122],[122.26612,25.98197],[123.92912,17.8782],[155.16731,23.60141],[145.82361,43.38904]]]]}},{type:"Feature",properties:{iso1A2:"KE",iso1A3:"KEN",iso1N3:"404",wikidata:"Q114",nameEn:"Kenya",groups:["014","202","002"],driveSide:"left",callingCodes:["254"]},geometry:{type:"MultiPolygon",coordinates:[[[[35.9419,4.61933],[35.51424,4.61643],[35.42366,4.76969],[35.47843,4.91872],[35.30992,4.90402],[35.34151,5.02364],[34.47601,4.72162],[33.9873,4.23316],[34.06046,4.15235],[34.15429,3.80464],[34.45815,3.67385],[34.44922,3.51627],[34.39112,3.48802],[34.41794,3.44342],[34.40006,3.37949],[34.45815,3.18319],[34.56242,3.11478],[34.60114,2.93034],[34.65774,2.8753],[34.73967,2.85447],[34.78137,2.76223],[34.77244,2.70272],[34.95267,2.47209],[34.90947,2.42447],[34.98692,1.97348],[34.9899,1.6668],[34.92734,1.56109],[34.87819,1.5596],[34.7918,1.36752],[34.82606,1.30944],[34.82606,1.26626],[34.80223,1.22754],[34.67562,1.21265],[34.58029,1.14712],[34.57427,1.09868],[34.52369,1.10692],[34.43349,0.85254],[34.40041,0.80266],[34.31516,0.75693],[34.27345,0.63182],[34.20196,0.62289],[34.13493,0.58118],[34.11408,0.48884],[34.08727,0.44713],[34.10067,0.36372],[33.90936,0.10581],[33.98449,-0.13079],[33.9264,-0.54188],[33.93107,-0.99298],[34.02286,-1.00779],[34.03084,-1.05101],[34.0824,-1.02264],[37.67199,-3.06222],[37.71745,-3.304],[37.5903,-3.42735],[37.63099,-3.50723],[37.75036,-3.54243],[37.81321,-3.69179],[39.21631,-4.67835],[39.44306,-4.93877],[39.62121,-4.68136],[41.75542,-1.85308],[41.56362,-1.66375],[41.56,-1.59812],[41.00099,-0.83068],[40.98767,2.82959],[41.31368,3.14314],[41.89488,3.97375],[41.1754,3.94079],[40.77498,4.27683],[39.86043,3.86974],[39.76808,3.67058],[39.58339,3.47434],[39.55132,3.39634],[39.51551,3.40895],[39.49444,3.45521],[39.19954,3.47834],[39.07736,3.5267],[38.91938,3.51198],[38.52336,3.62551],[38.45812,3.60445],[38.14168,3.62487],[37.07724,4.33503],[36.84474,4.44518],[36.03924,4.44406],[35.95449,4.53244],[35.9419,4.61933]]]]}},{type:"Feature",properties:{iso1A2:"KG",iso1A3:"KGZ",iso1N3:"417",wikidata:"Q813",nameEn:"Kyrgyzstan",groups:["143","142"],callingCodes:["996"]},geometry:{type:"MultiPolygon",coordinates:[[[[74.88756,42.98612],[74.75,42.99029],[74.70331,43.02519],[74.64615,43.05881],[74.57491,43.13702],[74.22489,43.24657],[73.55634,43.03071],[73.50992,42.82356],[73.44393,42.43098],[71.88792,42.83578],[71.62405,42.76613],[71.53272,42.8014],[71.2724,42.77853],[71.22785,42.69248],[71.17807,42.67381],[71.15232,42.60486],[70.97717,42.50147],[70.85973,42.30188],[70.94483,42.26238],[71.13263,42.28356],[71.28719,42.18033],[70.69777,41.92554],[70.17682,41.5455],[70.48909,41.40335],[70.67586,41.47953],[70.78572,41.36419],[70.77885,41.24813],[70.86263,41.23833],[70.9615,41.16393],[71.02193,41.19494],[71.11806,41.15359],[71.25813,41.18796],[71.27187,41.11015],[71.34877,41.16807],[71.40198,41.09436],[71.46148,41.13958],[71.43814,41.19644],[71.46688,41.31883],[71.57227,41.29175],[71.6787,41.42111],[71.65914,41.49599],[71.73054,41.54713],[71.71132,41.43012],[71.76625,41.4466],[71.83914,41.3546],[71.91457,41.2982],[71.85964,41.19081],[72.07249,41.11739],[72.10745,41.15483],[72.16433,41.16483],[72.17594,41.15522],[72.14864,41.13363],[72.1792,41.10621],[72.21061,41.05607],[72.17594,41.02377],[72.18339,40.99571],[72.324,41.03381],[72.34026,41.04539],[72.34757,41.06104],[72.36138,41.04384],[72.38511,41.02785],[72.45206,41.03018],[72.48742,40.97136],[72.55109,40.96046],[72.59136,40.86947],[72.68157,40.84942],[72.84291,40.85512],[72.94454,40.8094],[73.01869,40.84681],[73.13267,40.83512],[73.13412,40.79122],[73.0612,40.76678],[72.99133,40.76457],[72.93296,40.73089],[72.8722,40.71111],[72.85372,40.7116],[72.84754,40.67229],[72.80137,40.67856],[72.74866,40.60873],[72.74894,40.59592],[72.75982,40.57273],[72.74862,40.57131],[72.74768,40.58051],[72.73995,40.58409],[72.69579,40.59778],[72.66713,40.59076],[72.66713,40.5219],[72.47795,40.5532],[72.40517,40.61917],[72.34406,40.60144],[72.41714,40.55736],[72.38384,40.51535],[72.41513,40.50856],[72.44191,40.48222],[72.40346,40.4007],[72.24368,40.46091],[72.18648,40.49893],[71.96401,40.31907],[72.05464,40.27586],[71.85002,40.25647],[71.82646,40.21872],[71.73054,40.14818],[71.71719,40.17886],[71.69621,40.18492],[71.70569,40.20391],[71.68386,40.26984],[71.61931,40.26775],[71.61725,40.20615],[71.51549,40.22986],[71.51215,40.26943],[71.4246,40.28619],[71.36663,40.31593],[71.13042,40.34106],[71.05901,40.28765],[70.95789,40.28761],[70.9818,40.22392],[70.80495,40.16813],[70.7928,40.12797],[70.65827,40.0981],[70.65946,39.9878],[70.58912,39.95211],[70.55033,39.96619],[70.47557,39.93216],[70.57384,39.99394],[70.58297,40.00891],[70.01283,40.23288],[69.67001,40.10639],[69.64704,40.12165],[69.57615,40.10524],[69.55555,40.12296],[69.53794,40.11833],[69.53855,40.0887],[69.5057,40.03277],[69.53615,39.93991],[69.43557,39.92877],[69.43134,39.98431],[69.35649,40.01994],[69.26938,39.8127],[69.3594,39.52516],[69.68677,39.59281],[69.87491,39.53882],[70.11111,39.58223],[70.2869,39.53141],[70.44757,39.60128],[70.64087,39.58792],[70.7854,39.38933],[71.06418,39.41586],[71.08752,39.50704],[71.49814,39.61397],[71.55856,39.57588],[71.5517,39.45722],[71.62688,39.44056],[71.76816,39.45456],[71.80164,39.40631],[71.7522,39.32031],[71.79202,39.27355],[71.90601,39.27674],[72.04059,39.36704],[72.09689,39.26823],[72.17242,39.2661],[72.23834,39.17248],[72.33173,39.33093],[72.62027,39.39696],[72.85934,39.35116],[73.18454,39.35536],[73.31912,39.38615],[73.45096,39.46677],[73.59831,39.46425],[73.87018,39.47879],[73.94683,39.60733],[73.92354,39.69565],[73.9051,39.75073],[73.83006,39.76136],[73.97049,40.04378],[74.25533,40.13191],[74.35063,40.09742],[74.69875,40.34668],[74.85996,40.32857],[74.78168,40.44886],[74.82013,40.52197],[75.08243,40.43945],[75.22834,40.45382],[75.5854,40.66874],[75.69663,40.28642],[75.91361,40.2948],[75.96168,40.38064],[76.33659,40.3482],[76.5261,40.46114],[76.75681,40.95354],[76.99302,41.0696],[77.28004,41.0033],[77.3693,41.0375],[77.52723,41.00227],[77.76206,41.01574],[77.81287,41.14307],[78.12873,41.23091],[78.15757,41.38565],[78.3732,41.39603],[79.92977,42.04113],[80.17842,42.03211],[80.17807,42.21166],[79.97364,42.42816],[79.52921,42.44778],[79.19763,42.804],[78.91502,42.76839],[78.48469,42.89649],[75.82823,42.94848],[75.72174,42.79672],[75.29966,42.86183],[75.22619,42.85528],[74.88756,42.98612]],[[70.74189,39.86319],[70.63105,39.77923],[70.59667,39.83542],[70.54998,39.85137],[70.52631,39.86989],[70.53651,39.89155],[70.74189,39.86319]],[[71.86463,39.98598],[71.84316,39.95582],[71.7504,39.93701],[71.71511,39.96348],[71.78838,40.01404],[71.86463,39.98598]],[[71.21139,40.03369],[71.1427,39.95026],[71.23067,39.93581],[71.16101,39.88423],[71.10531,39.91354],[71.04979,39.89808],[71.10501,39.95568],[71.09063,39.99],[71.11668,39.99291],[71.11037,40.01984],[71.01035,40.05481],[71.00236,40.18154],[71.06305,40.1771],[71.12218,40.03052],[71.21139,40.03369]]]]}},{type:"Feature",properties:{iso1A2:"KH",iso1A3:"KHM",iso1N3:"116",wikidata:"Q424",nameEn:"Cambodia",groups:["035","142"],callingCodes:["855"]},geometry:{type:"MultiPolygon",coordinates:[[[[105.87328,11.55953],[105.81645,11.56876],[105.80867,11.60536],[105.8507,11.66635],[105.88962,11.67854],[105.95188,11.63738],[106.00792,11.7197],[106.02038,11.77457],[106.06708,11.77761],[106.13158,11.73283],[106.18539,11.75171],[106.26478,11.72122],[106.30525,11.67549],[106.37219,11.69836],[106.44691,11.66787],[106.45158,11.68616],[106.41577,11.76999],[106.44535,11.8279],[106.44068,11.86294],[106.4687,11.86751],[106.4111,11.97413],[106.70687,11.96956],[106.79405,12.0807],[106.92325,12.06548],[106.99953,12.08983],[107.15831,12.27547],[107.34511,12.33327],[107.42917,12.24657],[107.4463,12.29373],[107.55059,12.36824],[107.5755,12.52177],[107.55993,12.7982],[107.49611,12.88926],[107.49144,13.01215],[107.62843,13.3668],[107.61909,13.52577],[107.53503,13.73908],[107.45252,13.78897],[107.46498,13.91593],[107.44318,13.99751],[107.38247,13.99147],[107.35757,14.02319],[107.37158,14.07906],[107.33577,14.11832],[107.40427,14.24509],[107.39493,14.32655],[107.44941,14.41552],[107.48521,14.40346],[107.52569,14.54665],[107.52102,14.59034],[107.55371,14.628],[107.54361,14.69092],[107.47238,14.61523],[107.44435,14.52785],[107.37897,14.54443],[107.3276,14.58812],[107.29803,14.58963],[107.26534,14.54292],[107.256,14.48716],[107.21241,14.48716],[107.17038,14.41782],[107.09722,14.3937],[107.03962,14.45099],[107.04585,14.41782],[106.98825,14.36806],[106.9649,14.3198],[106.90574,14.33639],[106.8497,14.29416],[106.80767,14.31226],[106.73762,14.42687],[106.63333,14.44194],[106.59908,14.50977],[106.57106,14.50525],[106.54148,14.59565],[106.50723,14.58963],[106.45898,14.55045],[106.47766,14.50977],[106.43874,14.52032],[106.40916,14.45249],[106.32355,14.44043],[106.25194,14.48415],[106.21302,14.36203],[106.00131,14.36957],[105.99509,14.32734],[106.02311,14.30623],[106.04801,14.20363],[106.10872,14.18401],[106.11962,14.11307],[106.18656,14.06324],[106.16632,14.01794],[106.10094,13.98471],[106.10405,13.9137],[105.90791,13.92881],[105.78182,14.02247],[105.78338,14.08438],[105.5561,14.15684],[105.44869,14.10703],[105.36775,14.09948],[105.2759,14.17496],[105.20894,14.34967],[105.17748,14.34432],[105.14012,14.23873],[105.08408,14.20402],[105.02804,14.23722],[104.97667,14.38806],[104.69335,14.42726],[104.55014,14.36091],[104.27616,14.39861],[103.93836,14.3398],[103.70175,14.38052],[103.71109,14.4348],[103.53518,14.42575],[103.39353,14.35639],[103.16469,14.33075],[102.93275,14.19044],[102.91251,14.01531],[102.77864,13.93374],[102.72727,13.77806],[102.56848,13.69366],[102.5481,13.6589],[102.58635,13.6286],[102.62483,13.60883],[102.57573,13.60461],[102.5358,13.56933],[102.44601,13.5637],[102.36859,13.57488],[102.33828,13.55613],[102.361,13.50551],[102.35563,13.47307],[102.35692,13.38274],[102.34611,13.35618],[102.36001,13.31142],[102.36146,13.26006],[102.43422,13.09061],[102.46011,13.08057],[102.52275,12.99813],[102.48694,12.97537],[102.49335,12.92711],[102.53053,12.77506],[102.4994,12.71736],[102.51963,12.66117],[102.57567,12.65358],[102.7796,12.43781],[102.78116,12.40284],[102.73134,12.37091],[102.70176,12.1686],[102.77026,12.06815],[102.78427,11.98746],[102.83957,11.8519],[102.90973,11.75613],[102.91449,11.65512],[102.52395,11.25257],[102.47649,9.66162],[103.99198,10.48391],[104.43778,10.42386],[104.47963,10.43046],[104.49869,10.4057],[104.59018,10.53073],[104.87933,10.52833],[104.95094,10.64003],[105.09571,10.72722],[105.02722,10.89236],[105.08326,10.95656],[105.11449,10.96332],[105.34011,10.86179],[105.42884,10.96878],[105.50045,10.94586],[105.77751,11.03671],[105.86376,10.89839],[105.84603,10.85873],[105.93403,10.83853],[105.94535,10.9168],[106.06708,10.8098],[106.18539,10.79451],[106.14301,10.98176],[106.20095,10.97795],[106.1757,11.07301],[106.1527,11.10476],[106.10444,11.07879],[105.86782,11.28343],[105.88962,11.43605],[105.87328,11.55953]]]]}},{type:"Feature",properties:{iso1A2:"KI",iso1A3:"KIR",iso1N3:"296",wikidata:"Q710",nameEn:"Kiribati",groups:["057","009"],driveSide:"left",callingCodes:["686"]},geometry:{type:"MultiPolygon",coordinates:[[[[169,3.9],[169,-3.5],[178,-3.5],[178,3.9],[169,3.9]]],[[[-158.62058,-1.35506],[-161.04969,-1.36251],[-175.33482,-1.40631],[-175.31804,-7.54825],[-174.18707,-7.54408],[-167.75329,-7.52784],[-156.50903,-7.4975],[-156.4957,-12.32002],[-149.61166,-12.30171],[-149.6249,-7.51261],[-149.65979,5.27712],[-161.06795,5.2462],[-161.05669,1.11722],[-158.62734,1.1296],[-158.62058,-1.35506]]]]}},{type:"Feature",properties:{iso1A2:"KM",iso1A3:"COM",iso1N3:"174",wikidata:"Q970",nameEn:"Comoros",groups:["014","202","002"],callingCodes:["269"]},geometry:{type:"MultiPolygon",coordinates:[[[[42.93552,-11.11413],[42.99868,-12.65261],[44.75722,-12.58368],[44.69407,-11.04481],[42.93552,-11.11413]]]]}},{type:"Feature",properties:{iso1A2:"KN",iso1A3:"KNA",iso1N3:"659",wikidata:"Q763",nameEn:"St. Kitts and Nevis",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 869"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.27053,17.22145],[-62.76692,17.64353],[-63.11114,17.23125],[-62.62949,16.82364],[-62.27053,17.22145]]]]}},{type:"Feature",properties:{iso1A2:"KP",iso1A3:"PRK",iso1N3:"408",wikidata:"Q423",nameEn:"North Korea",groups:["030","142"],callingCodes:["850"]},geometry:{type:"MultiPolygon",coordinates:[[[[130.26095,42.9027],[130.09764,42.91425],[130.12957,42.98361],[129.96409,42.97306],[129.95082,43.01051],[129.8865,43.00395],[129.85261,42.96494],[129.83277,42.86746],[129.80719,42.79218],[129.7835,42.76521],[129.77183,42.69435],[129.75294,42.59409],[129.72541,42.43739],[129.60482,42.44461],[129.54701,42.37254],[129.42882,42.44702],[129.28541,42.41574],[129.22423,42.3553],[129.22285,42.26491],[129.15178,42.17224],[128.96068,42.06657],[128.94007,42.03537],[128.04487,42.01769],[128.15119,41.74568],[128.30716,41.60322],[128.20061,41.40895],[128.18546,41.41279],[128.12967,41.37931],[128.03311,41.39232],[128.02633,41.42103],[127.92943,41.44291],[127.29712,41.49473],[127.17841,41.59714],[126.90729,41.79955],[126.60631,41.65565],[126.53189,41.35206],[126.242,41.15454],[126.00335,40.92835],[125.76869,40.87908],[125.71172,40.85223],[124.86913,40.45387],[124.40719,40.13655],[124.38556,40.11047],[124.3322,40.05573],[124.37089,40.03004],[124.35029,39.95639],[124.23201,39.9248],[124.17532,39.8232],[123.90497,38.79949],[123.85601,37.49093],[124.67666,38.05679],[124.84224,37.977],[124.87921,37.80827],[125.06408,37.66334],[125.37112,37.62643],[125.81159,37.72949],[126.13074,37.70512],[126.18776,37.74728],[126.19097,37.81462],[126.24402,37.83113],[126.43239,37.84095],[126.46818,37.80873],[126.56709,37.76857],[126.59918,37.76364],[126.66067,37.7897],[126.68793,37.83728],[126.68793,37.9175],[126.67023,37.95852],[126.84961,38.0344],[126.88106,38.10246],[126.95887,38.1347],[126.95338,38.17735],[127.04479,38.25518],[127.15749,38.30722],[127.38727,38.33227],[127.49672,38.30647],[127.55013,38.32257],[128.02917,38.31861],[128.27652,38.41657],[128.31105,38.58462],[128.37487,38.62345],[128.65655,38.61914],[131.95041,41.5445],[130.65022,42.32281],[130.66367,42.38024],[130.64181,42.41422],[130.60805,42.4317],[130.56835,42.43281],[130.55143,42.52158],[130.50123,42.61636],[130.44361,42.54849],[130.41826,42.6011],[130.2385,42.71127],[130.23068,42.80125],[130.26095,42.9027]]]]}},{type:"Feature",properties:{iso1A2:"KR",iso1A3:"KOR",iso1N3:"410",wikidata:"Q884",nameEn:"South Korea",groups:["030","142"],callingCodes:["82"]},geometry:{type:"MultiPolygon",coordinates:[[[[133.61399,37.41],[128.65655,38.61914],[128.37487,38.62345],[128.31105,38.58462],[128.27652,38.41657],[128.02917,38.31861],[127.55013,38.32257],[127.49672,38.30647],[127.38727,38.33227],[127.15749,38.30722],[127.04479,38.25518],[126.95338,38.17735],[126.95887,38.1347],[126.88106,38.10246],[126.84961,38.0344],[126.67023,37.95852],[126.68793,37.9175],[126.68793,37.83728],[126.66067,37.7897],[126.59918,37.76364],[126.56709,37.76857],[126.46818,37.80873],[126.43239,37.84095],[126.24402,37.83113],[126.19097,37.81462],[126.18776,37.74728],[126.13074,37.70512],[125.81159,37.72949],[125.37112,37.62643],[125.06408,37.66334],[124.87921,37.80827],[124.84224,37.977],[124.67666,38.05679],[123.85601,37.49093],[122.80525,33.30571],[125.99728,32.63328],[129.2669,34.87122],[133.61399,37.41]]]]}},{type:"Feature",properties:{iso1A2:"KW",iso1A3:"KWT",iso1N3:"414",wikidata:"Q817",nameEn:"Kuwait",groups:["145","142"],callingCodes:["965"]},geometry:{type:"MultiPolygon",coordinates:[[[[49.00421,28.81495],[48.59531,29.66815],[48.40479,29.85763],[48.17332,30.02448],[48.06782,30.02906],[48.01114,29.98906],[47.7095,30.10453],[47.37192,30.10421],[47.15166,30.01044],[46.89695,29.50584],[46.5527,29.10283],[47.46202,29.0014],[47.58376,28.83382],[47.59863,28.66798],[47.70561,28.5221],[48.42991,28.53628],[49.00421,28.81495]]]]}},{type:"Feature",properties:{iso1A2:"KY",iso1A3:"CYM",iso1N3:"136",wikidata:"Q5785",nameEn:"Cayman Islands",country:"GB",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 345"]},geometry:{type:"MultiPolygon",coordinates:[[[[-82.11509,19.60401],[-80.36068,18.11751],[-79.32727,20.06742],[-82.11509,19.60401]]]]}},{type:"Feature",properties:{iso1A2:"KZ",iso1A3:"KAZ",iso1N3:"398",wikidata:"Q232",nameEn:"Kazakhstan",groups:["143","142"],callingCodes:["7"]},geometry:{type:"MultiPolygon",coordinates:[[[[68.90865,55.38148],[68.19206,55.18823],[68.26661,55.09226],[68.21308,54.98645],[65.20174,54.55216],[65.24663,54.35721],[65.11033,54.33028],[64.97216,54.4212],[63.97686,54.29763],[64.02715,54.22679],[63.91224,54.20013],[63.80604,54.27079],[62.58651,54.05871],[62.56876,53.94047],[62.45931,53.90737],[62.38535,54.03961],[62.00966,54.04134],[62.03913,53.94768],[61.65318,54.02445],[61.56941,53.95703],[61.47603,54.08048],[61.3706,54.08464],[61.26863,53.92797],[60.99796,53.93699],[61.14283,53.90063],[61.22574,53.80268],[60.90626,53.62937],[61.55706,53.57144],[61.57185,53.50112],[61.37957,53.45887],[61.29082,53.50992],[61.14291,53.41481],[61.19024,53.30536],[62.14574,53.09626],[62.12799,52.99133],[62.0422,52.96105],[61.23462,53.03227],[61.05842,52.92217],[60.71989,52.75923],[60.71693,52.66245],[60.84118,52.63912],[60.84709,52.52228],[60.98021,52.50068],[61.05417,52.35096],[60.78201,52.22067],[60.72581,52.15538],[60.48915,52.15175],[60.19925,51.99173],[59.99809,51.98263],[60.09867,51.87135],[60.50986,51.7964],[60.36787,51.66815],[60.5424,51.61675],[60.92401,51.61124],[60.95655,51.48615],[61.50677,51.40687],[61.55114,51.32746],[61.6813,51.25716],[61.56889,51.23679],[61.4431,50.80679],[60.81833,50.6629],[60.31914,50.67705],[60.17262,50.83312],[60.01288,50.8163],[59.81172,50.54451],[59.51886,50.49937],[59.48928,50.64216],[58.87974,50.70852],[58.3208,51.15151],[57.75578,51.13852],[57.74986,50.93017],[57.44221,50.88354],[57.17302,51.11253],[56.17906,50.93204],[56.11398,50.7471],[55.67774,50.54508],[54.72067,51.03261],[54.56685,51.01958],[54.71476,50.61214],[54.55797,50.52006],[54.41894,50.61214],[54.46331,50.85554],[54.12248,51.11542],[53.69299,51.23466],[53.46165,51.49445],[52.54329,51.48444],[52.36119,51.74161],[51.8246,51.67916],[51.77431,51.49536],[51.301,51.48799],[51.26254,51.68466],[50.59695,51.61859],[50.26859,51.28677],[49.97277,51.2405],[49.76866,51.11067],[49.39001,51.09396],[49.41959,50.85927],[49.12673,50.78639],[48.86936,50.61589],[48.57946,50.63278],[48.90782,50.02281],[48.68352,49.89546],[48.42564,49.82283],[48.24519,49.86099],[48.10044,50.09242],[47.58551,50.47867],[47.30448,50.30894],[47.34589,50.09308],[47.18319,49.93721],[46.9078,49.86707],[46.78398,49.34026],[46.98795,49.23531],[47.04416,49.17152],[47.01458,49.07085],[46.91104,48.99715],[46.78392,48.95352],[46.49011,48.43019],[47.11516,48.27188],[47.12107,47.83687],[47.38731,47.68176],[47.41689,47.83687],[47.64973,47.76559],[48.15348,47.74545],[48.45173,47.40818],[48.52326,47.4102],[49.01136,46.72716],[48.51142,46.69268],[48.54988,46.56267],[49.16518,46.38542],[49.32259,46.26944],[49.88945,46.04554],[49.2134,44.84989],[52.26048,41.69249],[52.47884,41.78034],[52.97575,42.1308],[54.20635,42.38477],[54.95182,41.92424],[55.45471,41.25609],[56.00314,41.32584],[55.97584,44.99322],[55.97584,44.99328],[55.97584,44.99338],[55.97584,44.99343],[55.97584,44.99348],[55.97584,44.99353],[55.97584,44.99359],[55.97584,44.99369],[55.97584,44.99374],[55.97584,44.99384],[55.97584,44.9939],[55.97584,44.994],[55.97584,44.99405],[55.97584,44.99415],[55.97584,44.99421],[55.97584,44.99426],[55.97584,44.99431],[55.97584,44.99436],[55.97584,44.99441],[55.97594,44.99446],[55.97605,44.99452],[55.97605,44.99457],[55.97605,44.99462],[55.97605,44.99467],[55.97605,44.99477],[55.97615,44.99477],[55.97615,44.99483],[55.97615,44.99493],[55.97615,44.99498],[55.97615,44.99503],[55.97615,44.99508],[55.97625,44.99514],[55.97636,44.99519],[55.97636,44.99524],[55.97646,44.99529],[55.97646,44.99534],[55.97656,44.99539],[55.97667,44.99545],[55.97677,44.9955],[55.97677,44.99555],[55.97677,44.9956],[55.97687,44.9956],[55.97698,44.99565],[55.97698,44.9957],[55.97708,44.99576],[55.97718,44.99581],[55.97729,44.99586],[55.97739,44.99586],[55.97739,44.99591],[55.97749,44.99591],[55.9776,44.99591],[55.9777,44.99596],[55.9777,44.99601],[55.9778,44.99607],[55.97791,44.99607],[55.97801,44.99607],[55.97801,44.99612],[55.97811,44.99617],[55.97822,44.99617],[55.97832,44.99622],[55.97842,44.99622],[58.59711,45.58671],[61.01475,44.41383],[62.01711,43.51008],[63.34656,43.64003],[64.53885,43.56941],[64.96464,43.74748],[65.18666,43.48835],[65.53277,43.31856],[65.85194,42.85481],[66.09482,42.93426],[66.00546,41.94455],[66.53302,41.87388],[66.69129,41.1311],[67.9644,41.14611],[67.98511,41.02794],[68.08273,41.08148],[68.1271,41.0324],[67.96736,40.83798],[68.49983,40.56437],[68.63,40.59358],[68.58444,40.91447],[68.49983,40.99669],[68.62221,41.03019],[68.65662,40.93861],[68.73945,40.96989],[68.7217,41.05025],[69.01308,41.22804],[69.05006,41.36183],[69.15137,41.43078],[69.17701,41.43769],[69.18528,41.45175],[69.20439,41.45391],[69.22671,41.46298],[69.23332,41.45847],[69.25059,41.46693],[69.29778,41.43673],[69.35554,41.47211],[69.37468,41.46555],[69.45081,41.46246],[69.39485,41.51518],[69.45751,41.56863],[69.49545,41.545],[70.94483,42.26238],[70.85973,42.30188],[70.97717,42.50147],[71.15232,42.60486],[71.17807,42.67381],[71.22785,42.69248],[71.2724,42.77853],[71.53272,42.8014],[71.62405,42.76613],[71.88792,42.83578],[73.44393,42.43098],[73.50992,42.82356],[73.55634,43.03071],[74.22489,43.24657],[74.57491,43.13702],[74.64615,43.05881],[74.70331,43.02519],[74.75,42.99029],[74.88756,42.98612],[75.22619,42.85528],[75.29966,42.86183],[75.72174,42.79672],[75.82823,42.94848],[78.48469,42.89649],[78.91502,42.76839],[79.19763,42.804],[79.52921,42.44778],[79.97364,42.42816],[80.17807,42.21166],[80.26841,42.23797],[80.16892,42.61137],[80.26886,42.8366],[80.38169,42.83142],[80.58999,42.9011],[80.3735,43.01557],[80.62913,43.141],[80.78817,43.14235],[80.77771,43.30065],[80.69718,43.32589],[80.75156,43.44948],[80.40031,44.10986],[80.40229,44.23319],[80.38384,44.63073],[79.8987,44.89957],[80.11169,45.03352],[81.73278,45.3504],[82.51374,45.1755],[82.58474,45.40027],[82.21792,45.56619],[83.04622,47.19053],[83.92184,46.98912],[84.73077,47.01394],[84.93995,46.87399],[85.22443,47.04816],[85.54294,47.06171],[85.69696,47.2898],[85.61067,47.49753],[85.5169,48.05493],[85.73581,48.3939],[86.38069,48.46064],[86.75343,48.70331],[86.73568,48.99918],[86.87238,49.12432],[87.28386,49.11626],[87.31465,49.23603],[87.03071,49.25142],[86.82606,49.51796],[86.61307,49.60239],[86.79056,49.74787],[86.63674,49.80136],[86.18709,49.50259],[85.24047,49.60239],[84.99198,50.06793],[84.29385,50.27257],[83.8442,50.87375],[83.14607,51.00796],[82.55443,50.75412],[81.94999,50.79307],[81.46581,50.77658],[81.41248,50.97524],[81.06091,50.94833],[81.16999,51.15662],[80.80318,51.28262],[80.44819,51.20855],[80.4127,50.95581],[80.08138,50.77658],[79.11255,52.01171],[77.90383,53.29807],[76.54243,53.99329],[76.44076,54.16017],[76.82266,54.1798],[76.91052,54.4677],[75.3668,54.07439],[75.43398,53.98652],[75.07405,53.80831],[73.39218,53.44623],[73.25412,53.61532],[73.68921,53.86522],[73.74778,54.07194],[73.37963,53.96132],[72.71026,54.1161],[72.43415,53.92685],[72.17477,54.36303],[71.96141,54.17736],[71.10379,54.13326],[71.08706,54.33376],[71.24185,54.64965],[71.08288,54.71253],[70.96009,55.10558],[70.76493,55.3027],[70.19179,55.1476],[69.74917,55.35545],[69.34224,55.36344],[68.90865,55.38148]]]]}},{type:"Feature",properties:{iso1A2:"LA",iso1A3:"LAO",iso1N3:"418",wikidata:"Q819",nameEn:"Laos",groups:["035","142"],callingCodes:["856"]},geometry:{type:"MultiPolygon",coordinates:[[[[102.1245,22.43372],[102.03633,22.46164],[101.98487,22.42766],[101.91344,22.44417],[101.90714,22.38688],[101.86828,22.38397],[101.7685,22.50337],[101.68973,22.46843],[101.61306,22.27515],[101.56789,22.28876],[101.53638,22.24794],[101.60675,22.13513],[101.57525,22.13026],[101.62566,21.96574],[101.7791,21.83019],[101.74555,21.72852],[101.83257,21.61562],[101.80001,21.57461],[101.7475,21.5873],[101.7727,21.51794],[101.74224,21.48276],[101.74014,21.30967],[101.84412,21.25291],[101.83887,21.20983],[101.76745,21.21571],[101.79266,21.19025],[101.7622,21.14813],[101.70548,21.14911],[101.66977,21.20004],[101.60886,21.17947],[101.59491,21.18621],[101.6068,21.23329],[101.54563,21.25668],[101.29326,21.17254],[101.2229,21.23271],[101.26912,21.36482],[101.19349,21.41959],[101.2124,21.56422],[101.15156,21.56129],[101.16198,21.52808],[101.00234,21.39612],[100.80173,21.2934],[100.72716,21.31786],[100.63578,21.05639],[100.55281,21.02796],[100.50974,20.88574],[100.64628,20.88279],[100.60112,20.8347],[100.51079,20.82194],[100.36375,20.82783],[100.1957,20.68247],[100.08404,20.36626],[100.09999,20.31614],[100.09337,20.26293],[100.11785,20.24787],[100.1712,20.24324],[100.16668,20.2986],[100.22076,20.31598],[100.25769,20.3992],[100.33383,20.4028],[100.37439,20.35156],[100.41473,20.25625],[100.44992,20.23644],[100.4537,20.19971],[100.47567,20.19133],[100.51052,20.14928],[100.55218,20.17741],[100.58808,20.15791],[100.5094,19.87904],[100.398,19.75047],[100.49604,19.53504],[100.58219,19.49164],[100.64606,19.55884],[100.77231,19.48324],[100.90302,19.61901],[101.08928,19.59748],[101.26545,19.59242],[101.26991,19.48324],[101.21347,19.46223],[101.20604,19.35296],[101.24911,19.33334],[101.261,19.12717],[101.35606,19.04716],[101.25803,18.89545],[101.22832,18.73377],[101.27585,18.68875],[101.06047,18.43247],[101.18227,18.34367],[101.15108,18.25624],[101.19118,18.2125],[101.1793,18.0544],[101.02185,17.87637],[100.96541,17.57926],[101.15108,17.47586],[101.44667,17.7392],[101.72294,17.92867],[101.78087,18.07559],[101.88485,18.02474],[102.11359,18.21532],[102.45523,17.97106],[102.59234,17.96127],[102.60971,17.95411],[102.61432,17.92273],[102.5896,17.84889],[102.59485,17.83537],[102.68194,17.80151],[102.69946,17.81686],[102.67543,17.84529],[102.68538,17.86653],[102.75954,17.89561],[102.79044,17.93612],[102.81988,17.94233],[102.86323,17.97531],[102.95812,18.0054],[102.9912,17.9949],[103.01998,17.97095],[103.0566,18.00144],[103.07823,18.03833],[103.07343,18.12351],[103.1493,18.17799],[103.14994,18.23172],[103.17093,18.2618],[103.29757,18.30475],[103.23818,18.34875],[103.24779,18.37807],[103.30977,18.4341],[103.41044,18.4486],[103.47773,18.42841],[103.60957,18.40528],[103.699,18.34125],[103.82449,18.33979],[103.85642,18.28666],[103.93916,18.33914],[103.97725,18.33631],[104.06533,18.21656],[104.10927,18.10826],[104.21776,17.99335],[104.2757,17.86139],[104.35432,17.82871],[104.45404,17.66788],[104.69867,17.53038],[104.80061,17.39367],[104.80716,17.19025],[104.73712,17.01404],[104.7373,16.91125],[104.76442,16.84752],[104.7397,16.81005],[104.76099,16.69302],[104.73349,16.565],[104.88057,16.37311],[105.00262,16.25627],[105.06204,16.09792],[105.42001,16.00657],[105.38508,15.987],[105.34115,15.92737],[105.37959,15.84074],[105.42285,15.76971],[105.46573,15.74742],[105.61756,15.68792],[105.60446,15.53301],[105.58191,15.41031],[105.47635,15.3796],[105.4692,15.33709],[105.50662,15.32054],[105.58043,15.32724],[105.46661,15.13132],[105.61162,15.00037],[105.5121,14.80802],[105.53864,14.55731],[105.43783,14.43865],[105.20894,14.34967],[105.2759,14.17496],[105.36775,14.09948],[105.44869,14.10703],[105.5561,14.15684],[105.78338,14.08438],[105.78182,14.02247],[105.90791,13.92881],[106.10405,13.9137],[106.10094,13.98471],[106.16632,14.01794],[106.18656,14.06324],[106.11962,14.11307],[106.10872,14.18401],[106.04801,14.20363],[106.02311,14.30623],[105.99509,14.32734],[106.00131,14.36957],[106.21302,14.36203],[106.25194,14.48415],[106.32355,14.44043],[106.40916,14.45249],[106.43874,14.52032],[106.47766,14.50977],[106.45898,14.55045],[106.50723,14.58963],[106.54148,14.59565],[106.57106,14.50525],[106.59908,14.50977],[106.63333,14.44194],[106.73762,14.42687],[106.80767,14.31226],[106.8497,14.29416],[106.90574,14.33639],[106.9649,14.3198],[106.98825,14.36806],[107.04585,14.41782],[107.03962,14.45099],[107.09722,14.3937],[107.17038,14.41782],[107.21241,14.48716],[107.256,14.48716],[107.26534,14.54292],[107.29803,14.58963],[107.3276,14.58812],[107.37897,14.54443],[107.44435,14.52785],[107.47238,14.61523],[107.54361,14.69092],[107.51579,14.79282],[107.59285,14.87795],[107.48277,14.93751],[107.46516,15.00982],[107.61486,15.0566],[107.61926,15.13949],[107.58844,15.20111],[107.62587,15.2266],[107.60605,15.37524],[107.62367,15.42193],[107.53341,15.40496],[107.50699,15.48771],[107.3815,15.49832],[107.34408,15.62345],[107.27583,15.62769],[107.27143,15.71459],[107.21859,15.74638],[107.21419,15.83747],[107.34188,15.89464],[107.39471,15.88829],[107.46296,16.01106],[107.44975,16.08511],[107.33968,16.05549],[107.25822,16.13587],[107.14595,16.17816],[107.15035,16.26271],[107.09091,16.3092],[107.02597,16.31132],[106.97385,16.30204],[106.96638,16.34938],[106.88067,16.43594],[106.88727,16.52671],[106.84104,16.55415],[106.74418,16.41904],[106.65832,16.47816],[106.66052,16.56892],[106.61477,16.60713],[106.58267,16.6012],[106.59013,16.62259],[106.55485,16.68704],[106.55265,16.86831],[106.52183,16.87884],[106.51963,16.92097],[106.54824,16.92729],[106.55045,17.0031],[106.50862,16.9673],[106.43597,17.01362],[106.31929,17.20509],[106.29287,17.3018],[106.24444,17.24714],[106.18991,17.28227],[106.09019,17.36399],[105.85744,17.63221],[105.76612,17.67147],[105.60381,17.89356],[105.64784,17.96687],[105.46292,18.22008],[105.38366,18.15315],[105.15942,18.38691],[105.10408,18.43533],[105.1327,18.58355],[105.19654,18.64196],[105.12829,18.70453],[104.64617,18.85668],[104.5361,18.97747],[103.87125,19.31854],[104.06058,19.43484],[104.10832,19.51575],[104.05617,19.61743],[104.06498,19.66926],[104.23229,19.70242],[104.41281,19.70035],[104.53169,19.61743],[104.64837,19.62365],[104.68359,19.72729],[104.8355,19.80395],[104.8465,19.91783],[104.9874,20.09573],[104.91695,20.15567],[104.86852,20.14121],[104.61315,20.24452],[104.62195,20.36633],[104.72102,20.40554],[104.66158,20.47774],[104.47886,20.37459],[104.40621,20.3849],[104.38199,20.47155],[104.63957,20.6653],[104.27412,20.91433],[104.11121,20.96779],[103.98024,20.91531],[103.82282,20.8732],[103.73478,20.6669],[103.68633,20.66324],[103.45737,20.82382],[103.38032,20.79501],[103.21497,20.89832],[103.12055,20.89994],[103.03469,21.05821],[102.97745,21.05821],[102.89825,21.24707],[102.80794,21.25736],[102.88939,21.3107],[102.94223,21.46034],[102.86297,21.4255],[102.98846,21.58936],[102.97965,21.74076],[102.86077,21.71213],[102.85637,21.84501],[102.81894,21.83888],[102.82115,21.73667],[102.74189,21.66713],[102.67145,21.65894],[102.62301,21.91447],[102.49092,21.99002],[102.51734,22.02676],[102.18712,22.30403],[102.14099,22.40092],[102.1245,22.43372]]]]}},{type:"Feature",properties:{iso1A2:"LB",iso1A3:"LBN",iso1N3:"422",wikidata:"Q822",nameEn:"Lebanon",aliases:["RL"],groups:["145","142"],callingCodes:["961"]},geometry:{type:"MultiPolygon",coordinates:[[[[35.94816,33.47886],[35.94465,33.52774],[36.05723,33.57904],[35.9341,33.6596],[36.06778,33.82927],[36.14517,33.85118],[36.3967,33.83365],[36.38263,33.86579],[36.28589,33.91981],[36.41078,34.05253],[36.50576,34.05982],[36.5128,34.09916],[36.62537,34.20251],[36.59195,34.2316],[36.58667,34.27667],[36.60778,34.31009],[36.56556,34.31881],[36.53039,34.3798],[36.55853,34.41609],[36.46179,34.46541],[36.4442,34.50165],[36.34745,34.5002],[36.3369,34.52629],[36.39846,34.55672],[36.41429,34.61175],[36.45299,34.59438],[36.46003,34.6378],[36.42941,34.62505],[36.35384,34.65447],[36.35135,34.68516],[36.32399,34.69334],[36.29165,34.62991],[35.98718,34.64977],[35.97386,34.63322],[35.48515,34.70851],[34.78515,33.20368],[35.10645,33.09318],[35.1924,33.08743],[35.31429,33.10515],[35.35223,33.05617],[35.43059,33.06659],[35.448,33.09264],[35.50272,33.09056],[35.50335,33.114],[35.52573,33.11921],[35.54228,33.19865],[35.5362,33.23196],[35.54808,33.236],[35.54544,33.25513],[35.55555,33.25844],[35.56523,33.28969],[35.58326,33.28381],[35.58502,33.26653],[35.62283,33.24226],[35.62019,33.27278],[35.77477,33.33609],[35.81324,33.36354],[35.82577,33.40479],[35.88668,33.43183],[35.94816,33.47886]]]]}},{type:"Feature",properties:{iso1A2:"LC",iso1A3:"LCA",iso1N3:"662",wikidata:"Q760",nameEn:"St. Lucia",aliases:["WL"],groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 758"]},geometry:{type:"MultiPolygon",coordinates:[[[[-60.5958,14.23076],[-61.26561,14.25664],[-61.43129,13.68336],[-60.70539,13.41452],[-60.5958,14.23076]]]]}},{type:"Feature",properties:{iso1A2:"LI",iso1A3:"LIE",iso1N3:"438",wikidata:"Q347",nameEn:"Liechtenstein",aliases:["FL"],groups:["155","150"],callingCodes:["423"]},geometry:{type:"MultiPolygon",coordinates:[[[[9.60717,47.06091],[9.61216,47.07732],[9.63395,47.08443],[9.62623,47.14685],[9.56539,47.17124],[9.58264,47.20673],[9.56981,47.21926],[9.55176,47.22585],[9.56766,47.24281],[9.53116,47.27029],[9.52406,47.24959],[9.50318,47.22153],[9.4891,47.19346],[9.48774,47.17402],[9.51044,47.13727],[9.52089,47.10019],[9.51362,47.08505],[9.47139,47.06402],[9.47548,47.05257],[9.54041,47.06495],[9.55721,47.04762],[9.60717,47.06091]]]]}},{type:"Feature",properties:{iso1A2:"LK",iso1A3:"LKA",iso1N3:"144",wikidata:"Q854",nameEn:"Sri Lanka",groups:["034","142"],driveSide:"left",callingCodes:["94"]},geometry:{type:"MultiPolygon",coordinates:[[[[76.25812,4.62435],[85.15017,5.21497],[80.48418,10.20786],[79.42124,9.80115],[79.50447,8.91876],[76.25812,4.62435]]]]}},{type:"Feature",properties:{iso1A2:"LR",iso1A3:"LBR",iso1N3:"430",wikidata:"Q1014",nameEn:"Liberia",groups:["011","202","002"],callingCodes:["231"]},geometry:{type:"MultiPolygon",coordinates:[[[[-8.47114,7.55676],[-8.55874,7.62525],[-8.55874,7.70167],[-8.67814,7.69428],[-8.72789,7.51429],[-8.8448,7.35149],[-8.85724,7.26019],[-8.93435,7.2824],[-9.09107,7.1985],[-9.18311,7.30461],[-9.20798,7.38109],[-9.305,7.42056],[-9.41943,7.41809],[-9.48161,7.37122],[-9.37465,7.62032],[-9.35724,7.74111],[-9.44928,7.9284],[-9.41445,8.02448],[-9.50898,8.18455],[-9.47415,8.35195],[-9.77763,8.54633],[-10.05873,8.42578],[-10.05375,8.50697],[-10.14579,8.52665],[-10.203,8.47991],[-10.27575,8.48711],[-10.30084,8.30008],[-10.31635,8.28554],[-10.29839,8.21283],[-10.35227,8.15223],[-10.45023,8.15627],[-10.51554,8.1393],[-10.57523,8.04829],[-10.60492,8.04072],[-10.60422,7.7739],[-11.29417,7.21576],[-11.4027,6.97746],[-11.50429,6.92704],[-12.15048,6.15992],[-7.52774,3.7105],[-7.53259,4.35145],[-7.59349,4.8909],[-7.53876,4.94294],[-7.55369,5.08667],[-7.48901,5.14118],[-7.46165,5.26256],[-7.36463,5.32944],[-7.43428,5.42355],[-7.37209,5.61173],[-7.43926,5.74787],[-7.43677,5.84687],[-7.46165,5.84934],[-7.48155,5.80974],[-7.67309,5.94337],[-7.70294,5.90625],[-7.78254,5.99037],[-7.79747,6.07696],[-7.8497,6.08932],[-7.83478,6.20309],[-7.90692,6.27728],[-8.00642,6.31684],[-8.17557,6.28222],[-8.3298,6.36381],[-8.38453,6.35887],[-8.45666,6.49977],[-8.48652,6.43797],[-8.59456,6.50612],[-8.31736,6.82837],[-8.29249,7.1691],[-8.37458,7.25794],[-8.41935,7.51203],[-8.47114,7.55676]]]]}},{type:"Feature",properties:{iso1A2:"LS",iso1A3:"LSO",iso1N3:"426",wikidata:"Q1013",nameEn:"Lesotho",groups:["018","202","002"],driveSide:"left",callingCodes:["266"]},geometry:{type:"MultiPolygon",coordinates:[[[[29.33204,-29.45598],[29.44883,-29.3772],[29.40524,-29.21246],[28.68043,-28.58744],[28.65091,-28.57025],[28.40612,-28.6215],[28.30518,-28.69531],[28.2348,-28.69471],[28.1317,-28.7293],[28.02503,-28.85991],[27.98675,-28.8787],[27.9392,-28.84864],[27.88933,-28.88156],[27.8907,-28.91612],[27.75458,-28.89839],[27.55974,-29.18954],[27.5158,-29.2261],[27.54258,-29.25575],[27.48679,-29.29349],[27.45125,-29.29708],[27.47254,-29.31968],[27.4358,-29.33465],[27.33464,-29.48161],[27.01016,-29.65439],[27.09489,-29.72796],[27.22719,-30.00718],[27.29603,-30.05473],[27.32555,-30.14785],[27.40778,-30.14577],[27.37293,-30.19401],[27.36649,-30.27246],[27.38108,-30.33456],[27.45452,-30.32239],[27.56901,-30.42504],[27.56781,-30.44562],[27.62137,-30.50509],[27.6521,-30.51707],[27.67819,-30.53437],[27.69467,-30.55862],[27.74814,-30.60635],[28.12073,-30.68072],[28.2319,-30.28476],[28.399,-30.1592],[28.68627,-30.12885],[28.80222,-30.10579],[28.9338,-30.05072],[29.16548,-29.91706],[29.12553,-29.76266],[29.28545,-29.58456],[29.33204,-29.45598]]]]}},{type:"Feature",properties:{iso1A2:"LT",iso1A3:"LTU",iso1N3:"440",wikidata:"Q37",nameEn:"Lithuania",groups:["EU","154","150"],callingCodes:["370"]},geometry:{type:"MultiPolygon",coordinates:[[[[24.89005,56.46666],[24.83686,56.41565],[24.70022,56.40483],[24.57353,56.31525],[24.58143,56.29125],[24.42746,56.26522],[24.32334,56.30226],[24.13139,56.24881],[24.02657,56.3231],[23.75726,56.37282],[23.49803,56.34307],[23.40486,56.37689],[23.31606,56.3827],[23.17312,56.36795],[23.09531,56.30511],[22.96988,56.41213],[22.83048,56.367],[22.69354,56.36284],[22.56441,56.39305],[22.3361,56.4016],[22.09728,56.42851],[22.00548,56.41508],[21.74558,56.33181],[21.57888,56.31406],[21.49736,56.29106],[21.24644,56.16917],[21.15016,56.07818],[20.68447,56.04073],[20.60454,55.40986],[20.95181,55.27994],[21.26425,55.24456],[21.35465,55.28427],[21.38446,55.29348],[21.46766,55.21115],[21.51095,55.18507],[21.55605,55.20311],[21.64954,55.1791],[21.85521,55.09493],[21.96505,55.07353],[21.99543,55.08691],[22.03984,55.07888],[22.02582,55.05078],[22.06087,55.02935],[22.11697,55.02131],[22.14267,55.05345],[22.31562,55.0655],[22.47688,55.04408],[22.58907,55.07085],[22.60075,55.01863],[22.65451,54.97037],[22.68723,54.9811],[22.76422,54.92521],[22.85083,54.88711],[22.87317,54.79492],[22.73631,54.72952],[22.73397,54.66604],[22.75467,54.6483],[22.74225,54.64339],[22.7522,54.63525],[22.68021,54.58486],[22.71293,54.56454],[22.67788,54.532],[22.70208,54.45312],[22.7253,54.41732],[22.79705,54.36264],[22.83756,54.40827],[23.00584,54.38514],[22.99649,54.35927],[23.05726,54.34565],[23.04323,54.31567],[23.104,54.29794],[23.13905,54.31567],[23.15526,54.31076],[23.15938,54.29894],[23.24656,54.25701],[23.3494,54.25155],[23.39525,54.21672],[23.42418,54.17911],[23.45223,54.17775],[23.49196,54.14764],[23.52702,54.04622],[23.48261,53.98855],[23.51284,53.95052],[23.61677,53.92691],[23.71726,53.93379],[23.80543,53.89558],[23.81309,53.94205],[23.95098,53.9613],[23.98837,53.92554],[24.19638,53.96405],[24.34128,53.90076],[24.44411,53.90076],[24.62275,54.00217],[24.69652,54.01901],[24.69185,53.96543],[24.74279,53.96663],[24.85311,54.02862],[24.77131,54.11091],[24.96894,54.17589],[24.991,54.14241],[25.0728,54.13419],[25.19199,54.219],[25.22705,54.26271],[25.35559,54.26544],[25.509,54.30267],[25.56823,54.25212],[25.51452,54.17799],[25.54724,54.14925],[25.64875,54.1259],[25.71084,54.16704],[25.78563,54.15747],[25.78553,54.23327],[25.68513,54.31727],[25.55425,54.31591],[25.5376,54.33158],[25.63371,54.42075],[25.62203,54.4656],[25.64813,54.48704],[25.68045,54.5321],[25.75977,54.57252],[25.74122,54.80108],[25.89462,54.93438],[25.99129,54.95705],[26.05907,54.94631],[26.13386,54.98924],[26.20397,54.99729],[26.26941,55.08032],[26.23202,55.10439],[26.30628,55.12536],[26.35121,55.1525],[26.46249,55.12814],[26.51481,55.16051],[26.54753,55.14181],[26.69243,55.16718],[26.68075,55.19787],[26.72983,55.21788],[26.73017,55.24226],[26.835,55.28182],[26.83266,55.30444],[26.80929,55.31642],[26.6714,55.33902],[26.5709,55.32572],[26.44937,55.34832],[26.5522,55.40277],[26.55094,55.5093],[26.63167,55.57887],[26.63231,55.67968],[26.58248,55.6754],[26.46661,55.70375],[26.39561,55.71156],[26.18509,55.86813],[26.03815,55.95884],[25.90047,56.0013],[25.85893,56.00188],[25.81773,56.05444],[25.69246,56.08892],[25.68588,56.14725],[25.53621,56.16663],[25.39751,56.15707],[25.23099,56.19147],[25.09325,56.1878],[25.05762,56.26742],[24.89005,56.46666]]]]}},{type:"Feature",properties:{iso1A2:"LU",iso1A3:"LUX",iso1N3:"442",wikidata:"Q32",nameEn:"Luxembourg",groups:["EU","155","150"],callingCodes:["352"]},geometry:{type:"MultiPolygon",coordinates:[[[[6.1379,50.12964],[6.1137,50.13668],[6.12028,50.16374],[6.08577,50.17246],[6.06406,50.15344],[6.03093,50.16362],[6.02488,50.18283],[5.96453,50.17259],[5.95929,50.13295],[5.89488,50.11476],[5.8857,50.07824],[5.85474,50.06342],[5.86904,50.04614],[5.8551,50.02683],[5.81866,50.01286],[5.82331,49.99662],[5.83968,49.9892],[5.83467,49.97823],[5.81163,49.97142],[5.80833,49.96451],[5.77291,49.96056],[5.77314,49.93646],[5.73621,49.89796],[5.78415,49.87922],[5.75269,49.8711],[5.75861,49.85631],[5.74567,49.85368],[5.75884,49.84811],[5.74953,49.84709],[5.74975,49.83933],[5.74076,49.83823],[5.7404,49.83452],[5.74844,49.82435],[5.74364,49.82058],[5.74953,49.81428],[5.75409,49.79239],[5.78871,49.7962],[5.82245,49.75048],[5.83149,49.74729],[5.82562,49.72395],[5.84193,49.72161],[5.86503,49.72739],[5.88677,49.70951],[5.86527,49.69291],[5.86175,49.67862],[5.9069,49.66377],[5.90164,49.6511],[5.90599,49.63853],[5.88552,49.63507],[5.88393,49.62802],[5.87609,49.62047],[5.8762,49.60898],[5.84826,49.5969],[5.84971,49.58674],[5.86986,49.58756],[5.87256,49.57539],[5.8424,49.56082],[5.84692,49.55663],[5.84143,49.5533],[5.81838,49.54777],[5.80871,49.5425],[5.81664,49.53775],[5.83648,49.5425],[5.84466,49.53027],[5.83467,49.52717],[5.83389,49.52152],[5.86571,49.50015],[5.94128,49.50034],[5.94224,49.49608],[5.96876,49.49053],[5.97693,49.45513],[6.02648,49.45451],[6.02743,49.44845],[6.04176,49.44801],[6.05553,49.46663],[6.07887,49.46399],[6.08373,49.45594],[6.10072,49.45268],[6.09845,49.46351],[6.10325,49.4707],[6.12346,49.4735],[6.12814,49.49365],[6.14321,49.48796],[6.16115,49.49297],[6.15366,49.50226],[6.17386,49.50934],[6.19543,49.50536],[6.2409,49.51408],[6.25029,49.50609],[6.27875,49.503],[6.28818,49.48465],[6.3687,49.4593],[6.36778,49.46937],[6.36907,49.48931],[6.36788,49.50377],[6.35666,49.52931],[6.38072,49.55171],[6.38228,49.55855],[6.35825,49.57053],[6.36676,49.57813],[6.38024,49.57593],[6.38342,49.5799],[6.37464,49.58886],[6.385,49.59946],[6.39822,49.60081],[6.41861,49.61723],[6.4413,49.65722],[6.43768,49.66021],[6.42726,49.66078],[6.42937,49.66857],[6.44654,49.67799],[6.46048,49.69092],[6.48014,49.69767],[6.49785,49.71118],[6.50647,49.71353],[6.5042,49.71808],[6.49694,49.72205],[6.49535,49.72645],[6.50261,49.72718],[6.51397,49.72058],[6.51805,49.72425],[6.50193,49.73291],[6.50174,49.75292],[6.51646,49.75961],[6.51828,49.76855],[6.51056,49.77515],[6.51669,49.78336],[6.50534,49.78952],[6.52169,49.79787],[6.53122,49.80666],[6.52121,49.81338],[6.51215,49.80124],[6.50647,49.80916],[6.48718,49.81267],[6.47111,49.82263],[6.45425,49.81164],[6.44131,49.81443],[6.42905,49.81091],[6.42521,49.81591],[6.40022,49.82029],[6.36576,49.85032],[6.34267,49.84974],[6.33585,49.83785],[6.32098,49.83728],[6.32303,49.85133],[6.30963,49.87021],[6.29692,49.86685],[6.28874,49.87592],[6.26146,49.88203],[6.23496,49.89972],[6.22926,49.92096],[6.21882,49.92403],[6.22608,49.929],[6.22094,49.94955],[6.19856,49.95053],[6.19089,49.96991],[6.18045,49.96611],[6.18554,49.95622],[6.17872,49.9537],[6.16466,49.97086],[6.1701,49.98518],[6.14147,49.99563],[6.14948,50.00908],[6.13806,50.01056],[6.1295,50.01849],[6.13273,50.02019],[6.13794,50.01466],[6.14666,50.02207],[6.13044,50.02929],[6.13458,50.04141],[6.11274,50.05916],[6.12055,50.09171],[6.1379,50.12964]]]]}},{type:"Feature",properties:{iso1A2:"LV",iso1A3:"LVA",iso1N3:"428",wikidata:"Q211",nameEn:"Latvia",groups:["EU","154","150"],callingCodes:["371"]},geometry:{type:"MultiPolygon",coordinates:[[[[27.34698,57.52242],[26.90364,57.62823],[26.54675,57.51813],[26.46527,57.56885],[26.29253,57.59244],[26.1866,57.6849],[26.2029,57.7206],[26.08098,57.76619],[26.0543,57.76105],[26.03332,57.7718],[26.02415,57.76865],[26.02069,57.77169],[26.0266,57.77441],[26.027,57.78158],[26.02456,57.78342],[26.0324,57.79037],[26.05949,57.84744],[25.73499,57.90193],[25.29581,58.08288],[25.28237,57.98539],[25.19484,58.0831],[24.3579,57.87471],[24.26221,57.91787],[23.20055,57.56697],[22.80496,57.87798],[19.84909,57.57876],[19.64795,57.06466],[20.68447,56.04073],[21.15016,56.07818],[21.24644,56.16917],[21.49736,56.29106],[21.57888,56.31406],[21.74558,56.33181],[22.00548,56.41508],[22.09728,56.42851],[22.3361,56.4016],[22.56441,56.39305],[22.69354,56.36284],[22.83048,56.367],[22.96988,56.41213],[23.09531,56.30511],[23.17312,56.36795],[23.31606,56.3827],[23.40486,56.37689],[23.49803,56.34307],[23.75726,56.37282],[24.02657,56.3231],[24.13139,56.24881],[24.32334,56.30226],[24.42746,56.26522],[24.58143,56.29125],[24.57353,56.31525],[24.70022,56.40483],[24.83686,56.41565],[24.89005,56.46666],[25.05762,56.26742],[25.09325,56.1878],[25.23099,56.19147],[25.39751,56.15707],[25.53621,56.16663],[25.68588,56.14725],[25.69246,56.08892],[25.81773,56.05444],[25.85893,56.00188],[25.90047,56.0013],[26.03815,55.95884],[26.18509,55.86813],[26.39561,55.71156],[26.46661,55.70375],[26.58248,55.6754],[26.63231,55.67968],[26.64888,55.70515],[26.71802,55.70645],[26.76872,55.67658],[26.87448,55.7172],[26.97153,55.8102],[27.1559,55.85032],[27.27804,55.78299],[27.3541,55.8089],[27.61683,55.78558],[27.63065,55.89687],[27.97865,56.11849],[28.15217,56.16964],[28.23716,56.27588],[28.16599,56.37806],[28.19057,56.44637],[28.10069,56.524],[28.13526,56.57989],[28.04768,56.59004],[27.86101,56.88204],[27.66511,56.83921],[27.86101,57.29402],[27.52453,57.42826],[27.56832,57.53728],[27.34698,57.52242]]]]}},{type:"Feature",properties:{iso1A2:"LY",iso1A3:"LBY",iso1N3:"434",wikidata:"Q1016",nameEn:"Libya",groups:["015","002"],callingCodes:["218"]},geometry:{type:"MultiPolygon",coordinates:[[[[22.5213,33.45682],[11.66543,33.34642],[11.56255,33.16754],[11.55852,33.1409],[11.51549,33.09826],[11.46037,32.6307],[11.57828,32.48013],[11.53898,32.4138],[11.04234,32.2145],[10.7315,31.97235],[10.62788,31.96629],[10.48497,31.72956],[10.31364,31.72648],[10.12239,31.42098],[10.29516,30.90337],[9.88152,30.34074],[9.76848,30.34366],[9.55544,30.23971],[9.3876,30.16738],[9.78136,29.40961],[9.89569,26.57696],[9.51696,26.39148],[9.38834,26.19288],[10.03146,25.35635],[10.02432,24.98124],[10.33159,24.5465],[10.85323,24.5595],[11.41061,24.21456],[11.62498,24.26669],[11.96886,23.51735],[13.5631,23.16574],[14.22918,22.61719],[14.99751,23.00539],[15.99566,23.49639],[23.99539,19.49944],[23.99715,20.00038],[24.99794,19.99661],[24.99885,21.99535],[24.99968,29.24574],[24.71117,30.17441],[25.01077,30.73861],[24.83101,31.31921],[25.06041,31.57937],[25.14001,31.67534],[25.63787,31.9359],[22.5213,33.45682]]]]}},{type:"Feature",properties:{iso1A2:"MA",iso1A3:"MAR",iso1N3:"504",wikidata:"Q1028",nameEn:"Morocco",groups:["015","002"],callingCodes:["212"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.27707,35.35051],[-2.85819,35.63219],[-5.10878,36.05227],[-5.64962,35.93752],[-7.27694,35.93599],[-14.43883,27.02969],[-17.27295,21.93519],[-17.21511,21.34226],[-17.02707,21.34022],[-16.9978,21.36239],[-16.44269,21.39745],[-14.78487,21.36587],[-14.47329,21.63839],[-14.48112,22.00886],[-14.1291,22.41636],[-14.10361,22.75501],[-13.75627,23.77231],[-13.00628,24.01923],[-12.92147,24.39502],[-12.12281,25.13682],[-12.06001,26.04442],[-11.62052,26.05229],[-11.38635,26.611],[-11.23622,26.72023],[-11.35695,26.8505],[-10.68417,26.90984],[-9.81998,26.71379],[-9.56957,26.90042],[-9.08698,26.98639],[-8.71787,26.9898],[-8.77527,27.66663],[-8.66879,27.6666],[-8.6715,28.71194],[-7.61585,29.36252],[-6.95824,29.50924],[-6.78351,29.44634],[-6.69965,29.51623],[-5.75616,29.61407],[-5.72121,29.52322],[-5.58831,29.48103],[-5.21671,29.95253],[-4.6058,30.28343],[-4.31774,30.53229],[-3.64735,30.67539],[-3.65418,30.85566],[-3.54944,31.0503],[-3.77103,31.14984],[-3.77647,31.31912],[-3.66386,31.39202],[-3.66314,31.6339],[-2.82784,31.79459],[-2.93873,32.06557],[-2.46166,32.16603],[-1.22829,32.07832],[-1.15735,32.12096],[-1.24453,32.1917],[-1.24998,32.32993],[-0.9912,32.52467],[-1.37794,32.73628],[-1.54244,32.95499],[-1.46249,33.0499],[-1.67067,33.27084],[-1.59508,33.59929],[-1.73494,33.71721],[-1.64666,34.10405],[-1.78042,34.39018],[-1.69788,34.48056],[-1.84569,34.61907],[-1.73707,34.74226],[-1.97469,34.886],[-1.97833,34.93218],[-2.04734,34.93218],[-2.21445,35.04378],[-2.21248,35.08532],[-2.27707,35.35051]],[[-2.92224,35.3401],[-2.92181,35.28599],[-2.92674,35.27313],[-2.93893,35.26737],[-2.95065,35.26576],[-2.95431,35.2728],[-2.96516,35.27967],[-2.96826,35.28296],[-2.96507,35.28801],[-2.97035,35.28852],[-2.96978,35.29459],[-2.96648,35.30475],[-2.96038,35.31609],[-2.92224,35.3401]],[[-3.90602,35.21494],[-3.90288,35.22024],[-3.88617,35.21406],[-3.88926,35.20841],[-3.90602,35.21494]],[[-4.30191,35.17419],[-4.29436,35.17149],[-4.30112,35.17058],[-4.30191,35.17419]],[[-2.41312,35.17111],[-2.44887,35.17075],[-2.44896,35.18777],[-2.41265,35.1877],[-2.41312,35.17111]],[[-5.38491,35.92591],[-5.27635,35.91222],[-5.27056,35.88794],[-5.34379,35.8711],[-5.35844,35.87375],[-5.37338,35.88417],[-5.38491,35.92591]]]]}},{type:"Feature",properties:{iso1A2:"MC",iso1A3:"MCO",iso1N3:"492",wikidata:"Q235",nameEn:"Monaco",groups:["155","150"],callingCodes:["377"]},geometry:{type:"MultiPolygon",coordinates:[[[[7.47823,43.73341],[7.4379,43.74963],[7.4389,43.75151],[7.43708,43.75197],[7.43624,43.75014],[7.43013,43.74895],[7.42809,43.74396],[7.42443,43.74087],[7.42299,43.74176],[7.42062,43.73977],[7.41233,43.73439],[7.41298,43.73311],[7.41291,43.73168],[7.41113,43.73156],[7.40903,43.7296],[7.42422,43.72209],[7.47823,43.73341]]]]}},{type:"Feature",properties:{iso1A2:"MD",iso1A3:"MDA",iso1N3:"498",wikidata:"Q217",nameEn:"Moldova",groups:["151","150"],callingCodes:["373"]},geometry:{type:"MultiPolygon",coordinates:[[[[27.74422,48.45926],[27.6658,48.44034],[27.59027,48.46311],[27.5889,48.49224],[27.46942,48.454],[27.44333,48.41209],[27.37741,48.41026],[27.37604,48.44398],[27.32159,48.4434],[27.27855,48.37534],[27.13434,48.37288],[27.08078,48.43214],[27.0231,48.42485],[27.03821,48.37653],[26.93384,48.36558],[26.85556,48.41095],[26.71274,48.40388],[26.82809,48.31629],[26.79239,48.29071],[26.6839,48.35828],[26.62823,48.25804],[26.81161,48.25049],[26.87708,48.19919],[26.94265,48.1969],[26.98042,48.15752],[26.96119,48.13003],[27.04118,48.12522],[27.02985,48.09083],[27.15622,47.98538],[27.1618,47.92391],[27.29069,47.73722],[27.25519,47.71366],[27.32202,47.64009],[27.3979,47.59473],[27.47942,47.48113],[27.55731,47.46637],[27.60263,47.32507],[27.68706,47.28962],[27.73172,47.29248],[27.81892,47.1381],[28.09095,46.97621],[28.12173,46.82283],[28.24808,46.64305],[28.22281,46.50481],[28.25769,46.43334],[28.18902,46.35283],[28.19864,46.31869],[28.10937,46.22852],[28.13684,46.18099],[28.08612,46.01105],[28.13111,45.92819],[28.16568,45.6421],[28.08927,45.6051],[28.18741,45.47358],[28.21139,45.46895],[28.30201,45.54744],[28.41836,45.51715],[28.43072,45.48538],[28.51449,45.49982],[28.49252,45.56716],[28.54196,45.58062],[28.51587,45.6613],[28.47879,45.66994],[28.52823,45.73803],[28.70401,45.78019],[28.69852,45.81753],[28.78503,45.83475],[28.74383,45.96664],[28.98004,46.00385],[29.00613,46.04962],[28.94643,46.09176],[29.06656,46.19716],[28.94953,46.25852],[28.98478,46.31803],[29.004,46.31495],[28.9306,46.45699],[29.01241,46.46177],[29.02409,46.49582],[29.23547,46.55435],[29.24886,46.37912],[29.35357,46.49505],[29.49914,46.45889],[29.5939,46.35472],[29.6763,46.36041],[29.66359,46.4215],[29.74496,46.45605],[29.88329,46.35851],[29.94114,46.40114],[30.09103,46.38694],[30.16794,46.40967],[30.02511,46.45132],[29.88916,46.54302],[29.94409,46.56002],[29.9743,46.75325],[29.94522,46.80055],[29.98814,46.82358],[29.87405,46.88199],[29.75458,46.8604],[29.72986,46.92234],[29.57056,46.94766],[29.62137,47.05069],[29.61038,47.09932],[29.53044,47.07851],[29.49732,47.12878],[29.57696,47.13581],[29.54996,47.24962],[29.59665,47.25521],[29.5733,47.36508],[29.48678,47.36043],[29.47854,47.30366],[29.39889,47.30179],[29.3261,47.44664],[29.18603,47.43387],[29.11743,47.55001],[29.22414,47.60012],[29.22242,47.73607],[29.27255,47.79953],[29.20663,47.80367],[29.27804,47.88893],[29.19839,47.89261],[29.1723,47.99013],[28.9306,47.96255],[28.8414,48.03392],[28.85232,48.12506],[28.69896,48.13106],[28.53921,48.17453],[28.48428,48.0737],[28.42454,48.12047],[28.43701,48.15832],[28.38712,48.17567],[28.34009,48.13147],[28.30609,48.14018],[28.30586,48.1597],[28.34912,48.1787],[28.36996,48.20543],[28.35519,48.24957],[28.32508,48.23384],[28.2856,48.23202],[28.19314,48.20749],[28.17666,48.25963],[28.07504,48.23494],[28.09873,48.3124],[28.04527,48.32661],[27.95883,48.32368],[27.88391,48.36699],[27.87533,48.4037],[27.81902,48.41874],[27.79225,48.44244],[27.74422,48.45926]]]]}},{type:"Feature",properties:{iso1A2:"ME",iso1A3:"MNE",iso1N3:"499",wikidata:"Q236",nameEn:"Montenegro",groups:["039","150"],callingCodes:["382"]},geometry:{type:"MultiPolygon",coordinates:[[[[19.22807,43.5264],[19.15685,43.53943],[19.13933,43.5282],[19.04934,43.50384],[19.01078,43.55806],[18.91379,43.50299],[18.95469,43.49367],[18.96053,43.45042],[19.01078,43.43854],[19.04071,43.397],[19.08673,43.31453],[19.08206,43.29668],[19.04233,43.30008],[19.00844,43.24988],[18.95001,43.29327],[18.95819,43.32899],[18.90911,43.36383],[18.83912,43.34795],[18.84794,43.33735],[18.85342,43.32426],[18.76538,43.29838],[18.6976,43.25243],[18.71747,43.2286],[18.66605,43.2056],[18.64735,43.14766],[18.66254,43.03928],[18.52232,43.01451],[18.49076,42.95553],[18.49661,42.89306],[18.4935,42.86433],[18.47633,42.85829],[18.45921,42.81682],[18.47324,42.74992],[18.56789,42.72074],[18.55221,42.69045],[18.54603,42.69171],[18.54841,42.68328],[18.57373,42.64429],[18.52232,42.62279],[18.55504,42.58409],[18.53751,42.57376],[18.49778,42.58409],[18.43735,42.55921],[18.44307,42.51077],[18.43588,42.48556],[18.52152,42.42302],[18.54128,42.39171],[18.45131,42.21682],[19.26406,41.74971],[19.37597,41.84849],[19.37451,41.8842],[19.33812,41.90669],[19.34601,41.95675],[19.37691,41.96977],[19.36867,42.02564],[19.37548,42.06835],[19.40687,42.10024],[19.28623,42.17745],[19.42,42.33019],[19.42352,42.36546],[19.4836,42.40831],[19.65972,42.62774],[19.73244,42.66299],[19.77375,42.58517],[19.74731,42.57422],[19.76549,42.50237],[19.82333,42.46581],[19.9324,42.51699],[20.00842,42.5109],[20.01834,42.54622],[20.07761,42.55582],[20.0969,42.65559],[20.02915,42.71147],[20.02088,42.74789],[20.04898,42.77701],[20.2539,42.76245],[20.27869,42.81945],[20.35692,42.8335],[20.34528,42.90676],[20.16415,42.97177],[20.14896,42.99058],[20.12325,42.96237],[20.05431,42.99571],[20.04729,43.02732],[19.98887,43.0538],[19.96549,43.11098],[19.92576,43.08539],[19.79255,43.11951],[19.76918,43.16044],[19.64063,43.19027],[19.62661,43.2286],[19.54598,43.25158],[19.52962,43.31623],[19.48171,43.32644],[19.44315,43.38846],[19.22229,43.47926],[19.22807,43.5264]]]]}},{type:"Feature",properties:{iso1A2:"MF",iso1A3:"MAF",iso1N3:"663",wikidata:"Q126125",nameEn:"Saint-Martin",country:"FR",groups:["EU","029","003","419","019"],callingCodes:["590"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.93924,18.02904],[-62.75637,18.13489],[-62.86666,18.19278],[-63.35989,18.06012],[-63.33064,17.9615],[-63.13584,18.0541],[-63.11096,18.05368],[-63.09686,18.04608],[-63.07759,18.04943],[-63.0579,18.06614],[-63.04039,18.05619],[-63.02323,18.05757],[-62.93924,18.02904]]]]}},{type:"Feature",properties:{iso1A2:"MG",iso1A3:"MDG",iso1N3:"450",wikidata:"Q1019",nameEn:"Madagascar",aliases:["RM"],groups:["014","202","002"],callingCodes:["261"]},geometry:{type:"MultiPolygon",coordinates:[[[[51.94557,-12.74579],[49.10033,-10.96054],[43.72277,-16.09877],[40.40841,-23.17181],[45.90777,-29.77366],[51.94557,-12.74579]]]]}},{type:"Feature",properties:{iso1A2:"MH",iso1A3:"MHL",iso1N3:"584",wikidata:"Q709",nameEn:"Marshall Islands",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["692"]},geometry:{type:"MultiPolygon",coordinates:[[[[169,3.9],[173.53711,5.70687],[169.29099,15.77133],[159.04653,10.59067],[169,3.9]]]]}},{type:"Feature",properties:{iso1A2:"MK",iso1A3:"MKD",iso1N3:"807",wikidata:"Q221",nameEn:"North Macedonia",groups:["039","150"],callingCodes:["389"]},geometry:{type:"MultiPolygon",coordinates:[[[[22.34773,42.31725],[22.29275,42.34913],[22.29605,42.37477],[22.16384,42.32103],[22.02908,42.29848],[21.94405,42.34669],[21.91595,42.30392],[21.84654,42.3247],[21.77176,42.2648],[21.70111,42.23789],[21.58992,42.25915],[21.52145,42.24465],[21.50823,42.27156],[21.43882,42.2789],[21.43882,42.23609],[21.38428,42.24465],[21.30496,42.1418],[21.29913,42.13954],[21.31983,42.10993],[21.22728,42.08909],[21.16614,42.19815],[21.11491,42.20794],[20.75464,42.05229],[20.76786,41.91839],[20.68523,41.85318],[20.59524,41.8818],[20.55976,41.87068],[20.57144,41.7897],[20.53405,41.78099],[20.51301,41.72433],[20.52937,41.69292],[20.51769,41.65975],[20.55508,41.58113],[20.52103,41.56473],[20.45809,41.5549],[20.45331,41.51436],[20.49039,41.49277],[20.51301,41.442],[20.55976,41.4087],[20.52119,41.34381],[20.49432,41.33679],[20.51068,41.2323],[20.59715,41.13644],[20.58546,41.11179],[20.59832,41.09066],[20.63454,41.0889],[20.65558,41.08009],[20.71634,40.91781],[20.73504,40.9081],[20.81567,40.89662],[20.83671,40.92752],[20.94305,40.92399],[20.97693,40.90103],[20.97887,40.85475],[21.15262,40.85546],[21.21105,40.8855],[21.25779,40.86165],[21.35595,40.87578],[21.41555,40.9173],[21.53007,40.90759],[21.57448,40.86076],[21.69601,40.9429],[21.7556,40.92525],[21.91102,41.04786],[21.90869,41.09191],[22.06527,41.15617],[22.1424,41.12449],[22.17629,41.15969],[22.26744,41.16409],[22.42285,41.11921],[22.5549,41.13065],[22.58295,41.11568],[22.62852,41.14385],[22.65306,41.18168],[22.71266,41.13945],[22.74538,41.16321],[22.76408,41.32225],[22.81199,41.3398],[22.93334,41.34104],[22.96331,41.35782],[22.95513,41.63265],[23.03342,41.71034],[23.01239,41.76527],[22.96682,41.77137],[22.90254,41.87587],[22.86749,42.02275],[22.67701,42.06614],[22.51224,42.15457],[22.50289,42.19527],[22.47251,42.20393],[22.38136,42.30339],[22.34773,42.31725]]]]}},{type:"Feature",properties:{iso1A2:"ML",iso1A3:"MLI",iso1N3:"466",wikidata:"Q912",nameEn:"Mali",groups:["011","202","002"],callingCodes:["223"]},geometry:{type:"MultiPolygon",coordinates:[[[[-4.83423,24.99935],[-6.57191,25.0002],[-5.60725,16.49919],[-5.33435,16.33354],[-5.50165,15.50061],[-9.32979,15.50032],[-9.31106,15.69412],[-9.33314,15.7044],[-9.44673,15.60553],[-9.40447,15.4396],[-10.71721,15.4223],[-10.90932,15.11001],[-11.43483,15.62339],[-11.70705,15.51558],[-11.94903,14.76143],[-12.23936,14.76324],[-11.93043,13.84505],[-12.06897,13.71049],[-11.83345,13.33333],[-11.63025,13.39174],[-11.39935,12.97808],[-11.37536,12.40788],[-11.50006,12.17826],[-11.24136,12.01286],[-10.99758,12.24634],[-10.80355,12.1053],[-10.71897,11.91552],[-10.30604,12.24634],[-9.714,12.0226],[-9.63938,12.18312],[-9.32097,12.29009],[-9.38067,12.48446],[-9.13689,12.50875],[-8.94784,12.34842],[-8.80854,11.66715],[-8.40058,11.37466],[-8.66923,10.99397],[-8.35083,11.06234],[-8.2667,10.91762],[-8.32614,10.69273],[-8.22711,10.41722],[-8.10207,10.44649],[-7.9578,10.2703],[-7.97971,10.17117],[-7.92107,10.15577],[-7.63048,10.46334],[-7.54462,10.40921],[-7.52261,10.4655],[-7.44555,10.44602],[-7.3707,10.24677],[-7.13331,10.24877],[-7.0603,10.14711],[-7.00966,10.15794],[-6.97444,10.21644],[-7.01186,10.25111],[-6.93921,10.35291],[-6.68164,10.35074],[-6.63541,10.66893],[-6.52974,10.59104],[-6.42847,10.5694],[-6.40646,10.69922],[-6.325,10.68624],[-6.24795,10.74248],[-6.1731,10.46983],[-6.18851,10.24244],[-5.99478,10.19694],[-5.78124,10.43952],[-5.65135,10.46767],[-5.51058,10.43177],[-5.46643,10.56074],[-5.47083,10.75329],[-5.41579,10.84628],[-5.49284,11.07538],[-5.32994,11.13371],[-5.32553,11.21578],[-5.25949,11.24816],[-5.25509,11.36905],[-5.20665,11.43811],[-5.22867,11.60421],[-5.29251,11.61715],[-5.26389,11.75728],[-5.40258,11.8327],[-5.26389,11.84778],[-5.07897,11.97918],[-4.72893,12.01579],[-4.70692,12.06746],[-4.62987,12.06531],[-4.62546,12.13204],[-4.54841,12.1385],[-4.57703,12.19875],[-4.41412,12.31922],[-4.47356,12.71252],[-4.238,12.71467],[-4.21819,12.95722],[-4.34477,13.12927],[-3.96501,13.49778],[-3.90558,13.44375],[-3.96282,13.38164],[-3.7911,13.36665],[-3.54454,13.1781],[-3.4313,13.1588],[-3.43507,13.27272],[-3.23599,13.29035],[-3.28396,13.5422],[-3.26407,13.70699],[-2.88189,13.64921],[-2.90831,13.81174],[-2.84667,14.05532],[-2.66175,14.14713],[-2.47587,14.29671],[-2.10223,14.14878],[-1.9992,14.19011],[-1.97945,14.47709],[-1.68083,14.50023],[-1.32166,14.72774],[-1.05875,14.7921],[-0.72004,15.08655],[-0.24673,15.07805],[0.06588,14.96961],[0.23859,15.00135],[0.72632,14.95898],[0.96711,14.98275],[1.31275,15.27978],[3.01806,15.34571],[3.03134,15.42221],[3.50368,15.35934],[4.19893,16.39923],[4.21787,17.00118],[4.26762,17.00432],[4.26651,19.14224],[3.36082,18.9745],[3.12501,19.1366],[3.24648,19.81703],[1.20992,20.73533],[1.15698,21.12843],[-4.83423,24.99935]]]]}},{type:"Feature",properties:{iso1A2:"MM",iso1A3:"MMR",iso1N3:"104",wikidata:"Q836",nameEn:"Myanmar",aliases:["Burma","BU"],groups:["035","142"],callingCodes:["95"]},geometry:{type:"MultiPolygon",coordinates:[[[[92.62187,21.87037],[92.59775,21.6092],[92.68152,21.28454],[92.60187,21.24615],[92.55105,21.3856],[92.43158,21.37025],[92.37939,21.47764],[92.20087,21.337],[92.17752,21.17445],[92.26071,21.05697],[92.37665,20.72172],[92.28464,20.63179],[92.31348,20.57137],[92.4302,20.5688],[92.39837,20.38919],[92.61042,13.76986],[94.6371,13.81803],[97.63455,9.60854],[98.12555,9.44056],[98.33094,9.91973],[98.47298,9.95782],[98.52291,9.92389],[98.55174,9.92804],[98.7391,10.31488],[98.81944,10.52761],[98.77275,10.62548],[98.78511,10.68351],[98.86819,10.78336],[99.0069,10.85485],[98.99701,10.92962],[99.02337,10.97217],[99.06938,10.94857],[99.32756,11.28545],[99.31573,11.32081],[99.39485,11.3925],[99.47598,11.62434],[99.5672,11.62732],[99.64108,11.78948],[99.64891,11.82699],[99.53424,12.02317],[99.56445,12.14805],[99.47519,12.1353],[99.409,12.60603],[99.29254,12.68921],[99.18905,12.84799],[99.18748,12.9898],[99.10646,13.05804],[99.12225,13.19847],[99.20617,13.20575],[99.16695,13.72621],[98.97356,14.04868],[98.56762,14.37701],[98.24874,14.83013],[98.18821,15.13125],[98.22,15.21327],[98.30446,15.30667],[98.40522,15.25268],[98.41906,15.27103],[98.39351,15.34177],[98.4866,15.39154],[98.56027,15.33471],[98.58598,15.46821],[98.541,15.65406],[98.59853,15.87197],[98.57019,16.04578],[98.69585,16.13353],[98.8376,16.11706],[98.92656,16.36425],[98.84485,16.42354],[98.68074,16.27068],[98.63817,16.47424],[98.57912,16.55983],[98.5695,16.62826],[98.51113,16.64503],[98.51833,16.676],[98.51472,16.68521],[98.51579,16.69433],[98.51043,16.70107],[98.49713,16.69022],[98.50253,16.7139],[98.46994,16.73613],[98.53833,16.81934],[98.49603,16.8446],[98.52624,16.89979],[98.39441,17.06266],[98.34566,17.04822],[98.10439,17.33847],[98.11185,17.36829],[97.91829,17.54504],[97.76407,17.71595],[97.66794,17.88005],[97.73723,17.97912],[97.60841,18.23846],[97.64116,18.29778],[97.56219,18.33885],[97.50383,18.26844],[97.34522,18.54596],[97.36444,18.57138],[97.5258,18.4939],[97.76752,18.58097],[97.73836,18.88478],[97.66487,18.9371],[97.73654,18.9812],[97.73797,19.04261],[97.83479,19.09972],[97.84024,19.22217],[97.78606,19.26769],[97.84186,19.29526],[97.78769,19.39429],[97.88423,19.5041],[97.84715,19.55782],[98.04364,19.65755],[98.03314,19.80941],[98.13829,19.78541],[98.24884,19.67876],[98.51182,19.71303],[98.56065,19.67807],[98.83661,19.80931],[98.98679,19.7419],[99.0735,20.10298],[99.20328,20.12877],[99.416,20.08614],[99.52943,20.14811],[99.5569,20.20676],[99.46077,20.36198],[99.46008,20.39673],[99.68255,20.32077],[99.81096,20.33687],[99.86383,20.44371],[99.88211,20.44488],[99.88451,20.44596],[99.89168,20.44548],[99.89301,20.44311],[99.89692,20.44789],[99.90499,20.4487],[99.91616,20.44986],[99.95721,20.46301],[100.08404,20.36626],[100.1957,20.68247],[100.36375,20.82783],[100.51079,20.82194],[100.60112,20.8347],[100.64628,20.88279],[100.50974,20.88574],[100.55281,21.02796],[100.63578,21.05639],[100.72716,21.31786],[100.80173,21.2934],[101.00234,21.39612],[101.16198,21.52808],[101.15156,21.56129],[101.11744,21.77659],[100.87265,21.67396],[100.72143,21.51898],[100.57861,21.45637],[100.4811,21.46148],[100.42892,21.54325],[100.35201,21.53176],[100.25863,21.47043],[100.18447,21.51898],[100.1625,21.48704],[100.12542,21.50365],[100.10757,21.59945],[100.17486,21.65306],[100.12679,21.70539],[100.04956,21.66843],[99.98654,21.71064],[99.94003,21.82782],[99.99084,21.97053],[99.96612,22.05965],[99.85351,22.04183],[99.47585,22.13345],[99.33166,22.09656],[99.1552,22.15874],[99.19176,22.16983],[99.17318,22.18025],[99.28771,22.4105],[99.37972,22.50188],[99.38247,22.57544],[99.31243,22.73893],[99.45654,22.85726],[99.43537,22.94086],[99.54218,22.90014],[99.52214,23.08218],[99.34127,23.13099],[99.25741,23.09025],[99.04601,23.12215],[99.05975,23.16382],[98.88597,23.18656],[98.92515,23.29535],[98.93958,23.31414],[98.87573,23.33038],[98.92104,23.36946],[98.87683,23.48995],[98.82877,23.47908],[98.80294,23.5345],[98.88396,23.59555],[98.81775,23.694],[98.82933,23.72921],[98.79607,23.77947],[98.68209,23.80492],[98.67797,23.9644],[98.89632,24.10612],[98.87998,24.15624],[98.85319,24.13042],[98.59256,24.08371],[98.54476,24.13119],[98.20666,24.11406],[98.07806,24.07988],[98.06703,24.08028],[98.0607,24.07812],[98.05671,24.07961],[98.05302,24.07408],[98.04709,24.07616],[97.99583,24.04932],[97.98691,24.03897],[97.93951,24.01953],[97.90998,24.02094],[97.88616,24.00463],[97.88414,23.99405],[97.88814,23.98605],[97.89683,23.98389],[97.89676,23.97931],[97.8955,23.97758],[97.88811,23.97446],[97.86545,23.97723],[97.84328,23.97603],[97.79416,23.95663],[97.79456,23.94836],[97.72302,23.89288],[97.64667,23.84574],[97.5247,23.94032],[97.62363,24.00506],[97.72903,24.12606],[97.75305,24.16902],[97.72799,24.18883],[97.72998,24.2302],[97.76799,24.26365],[97.71941,24.29652],[97.66723,24.30027],[97.65624,24.33781],[97.7098,24.35658],[97.66998,24.45288],[97.60029,24.4401],[97.52757,24.43748],[97.56286,24.54535],[97.56525,24.72838],[97.54675,24.74202],[97.5542,24.74943],[97.56383,24.75535],[97.56648,24.76475],[97.64354,24.79171],[97.70181,24.84557],[97.73127,24.83015],[97.76481,24.8289],[97.79949,24.85655],[97.72903,24.91332],[97.72216,25.08508],[97.77023,25.11492],[97.83614,25.2715],[97.92541,25.20815],[98.14925,25.41547],[98.12591,25.50722],[98.18084,25.56298],[98.16848,25.62739],[98.25774,25.6051],[98.31268,25.55307],[98.40606,25.61129],[98.54064,25.85129],[98.63128,25.79937],[98.70818,25.86241],[98.60763,26.01512],[98.57085,26.11547],[98.63128,26.15492],[98.66884,26.09165],[98.7329,26.17218],[98.67797,26.24487],[98.72741,26.36183],[98.77547,26.61994],[98.7333,26.85615],[98.69582,27.56499],[98.43353,27.67086],[98.42529,27.55404],[98.32641,27.51385],[98.13964,27.9478],[98.15337,28.12114],[97.90069,28.3776],[97.79632,28.33168],[97.70705,28.5056],[97.56835,28.55628],[97.50518,28.49716],[97.47085,28.2688],[97.41729,28.29783],[97.34547,28.21385],[97.31292,28.06784],[97.35412,28.06663],[97.38845,28.01329],[97.35824,27.87256],[97.29919,27.92233],[96.90112,27.62149],[96.91431,27.45752],[97.17422,27.14052],[97.14675,27.09041],[96.89132,27.17474],[96.85287,27.2065],[96.88445,27.25046],[96.73888,27.36638],[96.55761,27.29928],[96.40779,27.29818],[96.15591,27.24572],[96.04949,27.19428],[95.93002,27.04149],[95.81603,27.01335],[95.437,26.7083],[95.30339,26.65372],[95.23513,26.68499],[95.05798,26.45408],[95.12801,26.38397],[95.11428,26.1019],[95.18556,26.07338],[94.80117,25.49359],[94.68032,25.47003],[94.57458,25.20318],[94.74212,25.13606],[94.73937,25.00545],[94.60204,24.70889],[94.5526,24.70764],[94.50729,24.59281],[94.45279,24.56656],[94.32362,24.27692],[94.30215,24.23752],[94.14081,23.83333],[93.92089,23.95812],[93.80279,23.92549],[93.75952,24.0003],[93.62871,24.00922],[93.50616,23.94432],[93.46633,23.97067],[93.41415,24.07854],[93.34735,24.10151],[93.32351,24.04468],[93.36059,23.93176],[93.3908,23.92925],[93.3908,23.7622],[93.43475,23.68299],[93.38805,23.4728],[93.39981,23.38828],[93.38781,23.36139],[93.36862,23.35426],[93.38478,23.13698],[93.2878,23.00464],[93.12988,23.05772],[93.134,22.92498],[93.09417,22.69459],[93.134,22.59573],[93.11477,22.54374],[93.13537,22.45873],[93.18206,22.43716],[93.19991,22.25425],[93.14224,22.24535],[93.15734,22.18687],[93.04885,22.20595],[92.99255,22.05965],[92.99804,21.98964],[92.93899,22.02656],[92.89504,21.95143],[92.86208,22.05456],[92.70416,22.16017],[92.67532,22.03547],[92.60949,21.97638],[92.62187,21.87037]]]]}},{type:"Feature",properties:{iso1A2:"MN",iso1A3:"MNG",iso1N3:"496",wikidata:"Q711",nameEn:"Mongolia",groups:["030","142"],callingCodes:["976"]},geometry:{type:"MultiPolygon",coordinates:[[[[102.14032,51.35566],[101.5044,51.50467],[101.39085,51.45753],[100.61116,51.73028],[99.89203,51.74903],[99.75578,51.90108],[99.27888,51.96876],[98.87768,52.14563],[98.74142,51.8637],[98.33222,51.71832],[98.22053,51.46579],[98.05257,51.46696],[97.83305,51.00248],[98.01472,50.86652],[97.9693,50.78044],[98.06393,50.61262],[98.31373,50.4996],[98.29481,50.33561],[97.85197,49.91339],[97.76871,49.99861],[97.56432,49.92801],[97.56811,49.84265],[97.24639,49.74737],[96.97388,49.88413],[95.80056,50.04239],[95.74757,49.97915],[95.02465,49.96941],[94.97166,50.04725],[94.6121,50.04239],[94.49477,50.17832],[94.39258,50.22193],[94.30823,50.57498],[92.99595,50.63183],[93.01109,50.79001],[92.44714,50.78762],[92.07173,50.69585],[91.86048,50.73734],[89.59711,49.90851],[89.70687,49.72535],[88.82499,49.44808],[88.42449,49.48821],[88.17223,49.46934],[88.15543,49.30314],[87.98977,49.18147],[87.81333,49.17354],[87.88171,48.95853],[87.73822,48.89582],[88.0788,48.71436],[87.96361,48.58478],[88.58939,48.34531],[88.58316,48.21893],[88.8011,48.11302],[88.93186,48.10263],[89.0711,47.98528],[89.55453,48.0423],[89.76624,47.82745],[90.06512,47.88177],[90.10871,47.7375],[90.33598,47.68303],[90.48854,47.41826],[90.48542,47.30438],[90.76108,46.99399],[90.84035,46.99525],[91.03649,46.72916],[91.0147,46.58171],[91.07696,46.57315],[90.89639,46.30711],[90.99672,46.14207],[91.03026,46.04194],[90.70907,45.73437],[90.65114,45.49314],[90.89169,45.19667],[91.64048,45.07408],[93.51161,44.95964],[94.10003,44.71016],[94.71959,44.35284],[95.01191,44.25274],[95.39772,44.2805],[95.32891,44.02407],[95.52594,43.99353],[95.89543,43.2528],[96.35658,42.90363],[96.37926,42.72055],[97.1777,42.7964],[99.50671,42.56535],[100.33297,42.68231],[100.84979,42.67087],[101.28833,42.58524],[101.80515,42.50074],[102.07645,42.22519],[102.42826,42.15137],[102.72403,42.14675],[103.3685,41.89696],[103.92804,41.78246],[104.52258,41.8706],[104.51667,41.66113],[104.91272,41.64619],[105.01119,41.58382],[105.24708,41.7442],[106.76517,42.28741],[107.24774,42.36107],[107.29755,42.41395],[107.49681,42.46221],[107.57258,42.40898],[108.23156,42.45532],[108.84489,42.40246],[109.00679,42.45302],[109.452,42.44842],[109.89402,42.63111],[110.08401,42.6411],[110.4327,42.78293],[111.0149,43.3289],[111.59087,43.51207],[111.79758,43.6637],[111.93776,43.68709],[111.96289,43.81596],[111.40498,44.3461],[111.76275,44.98032],[111.98695,45.09074],[112.4164,45.06858],[112.74662,44.86297],[113.63821,44.74326],[113.909,44.91444],[114.08071,44.92847],[114.5166,45.27189],[114.54801,45.38337],[114.74612,45.43585],[114.94546,45.37377],[115.35757,45.39106],[115.69688,45.45761],[115.91898,45.6227],[116.16989,45.68603],[116.27366,45.78637],[116.24012,45.8778],[116.26678,45.96479],[116.58612,46.30211],[116.75551,46.33083],[116.83166,46.38637],[117.07252,46.35818],[117.36609,46.36335],[117.41782,46.57862],[117.60748,46.59771],[117.69554,46.50991],[118.30534,46.73519],[118.78747,46.68689],[118.8337,46.77742],[118.89974,46.77139],[118.92616,46.72765],[119.00541,46.74273],[119.10448,46.65516],[119.24978,46.64761],[119.30261,46.6083],[119.37306,46.61132],[119.42827,46.63783],[119.65265,46.62342],[119.68127,46.59015],[119.77373,46.62947],[119.80455,46.67631],[119.89261,46.66423],[119.91242,46.90091],[119.85518,46.92196],[119.71209,47.19192],[119.62403,47.24575],[119.56019,47.24874],[119.54918,47.29505],[119.31964,47.42617],[119.35892,47.48104],[119.13995,47.53997],[119.12343,47.66458],[118.7564,47.76947],[118.55766,47.99277],[118.29654,48.00246],[118.22677,48.03853],[118.11009,48.04],[118.03676,48.00982],[117.80196,48.01661],[117.50181,47.77216],[117.37875,47.63627],[117.08918,47.82242],[116.87527,47.88836],[116.67405,47.89039],[116.4465,47.83662],[116.2527,47.87766],[116.08431,47.80693],[115.94296,47.67741],[115.57128,47.91988],[115.52082,48.15367],[115.811,48.25699],[115.78876,48.51781],[116.06565,48.81716],[116.03781,48.87014],[116.71193,49.83813],[116.62502,49.92919],[116.22402,50.04477],[115.73602,49.87688],[115.26068,49.97367],[114.9703,50.19254],[114.325,50.28098],[113.20216,49.83356],[113.02647,49.60772],[110.64493,49.1816],[110.39891,49.25083],[110.24373,49.16676],[109.51325,49.22859],[109.18017,49.34709],[108.53969,49.32325],[108.27937,49.53167],[107.95387,49.66659],[107.96116,49.93191],[107.36407,49.97612],[107.1174,50.04239],[107.00007,50.1977],[106.80326,50.30177],[106.58373,50.34044],[106.51122,50.34408],[106.49628,50.32436],[106.47156,50.31909],[106.07865,50.33474],[106.05562,50.40582],[105.32528,50.4648],[103.70343,50.13952],[102.71178,50.38873],[102.32194,50.67982],[102.14032,51.35566]]]]}},{type:"Feature",properties:{iso1A2:"MO",iso1A3:"MAC",iso1N3:"446",wikidata:"Q14773",nameEn:"Macau",aliases:["Macao"],country:"CN",groups:["030","142"],driveSide:"left",callingCodes:["853"]},geometry:{type:"MultiPolygon",coordinates:[[[[113.54942,22.14519],[113.54839,22.10909],[113.57191,22.07696],[113.63011,22.10782],[113.60504,22.20464],[113.57123,22.20416],[113.56865,22.20973],[113.5508,22.21672],[113.54333,22.21688],[113.54093,22.21314],[113.53593,22.2137],[113.53301,22.21235],[113.53552,22.20607],[113.52659,22.18271],[113.54093,22.15497],[113.54942,22.14519]]]]}},{type:"Feature",properties:{iso1A2:"MP",iso1A3:"MNP",iso1N3:"580",wikidata:"Q16644",nameEn:"Northern Mariana Islands",country:"US",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["1 670"]},geometry:{type:"MultiPolygon",coordinates:[[[[143.82485,13.92273],[146.25931,13.85876],[146.6755,21.00809],[144.18594,21.03576],[143.82485,13.92273]]]]}},{type:"Feature",properties:{iso1A2:"MQ",iso1A3:"MTQ",iso1N3:"474",wikidata:"Q17054",nameEn:"Martinique",country:"FR",groups:["EU","029","003","419","019"],callingCodes:["596"]},geometry:{type:"MultiPolygon",coordinates:[[[[-60.5958,14.23076],[-60.69955,15.22234],[-61.51867,14.96709],[-61.26561,14.25664],[-60.5958,14.23076]]]]}},{type:"Feature",properties:{iso1A2:"MR",iso1A3:"MRT",iso1N3:"478",wikidata:"Q1025",nameEn:"Mauritania",groups:["011","202","002"],callingCodes:["222"]},geometry:{type:"MultiPolygon",coordinates:[[[[-5.60725,16.49919],[-6.57191,25.0002],[-4.83423,24.99935],[-8.66674,27.31569],[-8.66721,25.99918],[-12.0002,25.9986],[-12.00251,23.4538],[-12.14969,23.41935],[-12.36213,23.3187],[-12.5741,23.28975],[-13.00412,23.02297],[-13.10753,22.89493],[-13.15313,22.75649],[-13.08438,22.53866],[-13.01525,21.33343],[-16.95474,21.33997],[-16.99806,21.12142],[-17.0357,21.05368],[-17.0396,20.9961],[-17.06781,20.92697],[-17.0695,20.85742],[-17.0471,20.76408],[-17.15288,16.07139],[-16.50854,16.09032],[-16.48967,16.0496],[-16.44814,16.09753],[-16.4429,16.20605],[-16.27016,16.51565],[-15.6509,16.50315],[-15.00557,16.64997],[-14.32144,16.61495],[-13.80075,16.13961],[-13.43135,16.09022],[-13.11029,15.52116],[-12.23936,14.76324],[-11.94903,14.76143],[-11.70705,15.51558],[-11.43483,15.62339],[-10.90932,15.11001],[-10.71721,15.4223],[-9.40447,15.4396],[-9.44673,15.60553],[-9.33314,15.7044],[-9.31106,15.69412],[-9.32979,15.50032],[-5.50165,15.50061],[-5.33435,16.33354],[-5.60725,16.49919]]]]}},{type:"Feature",properties:{iso1A2:"MS",iso1A3:"MSR",iso1N3:"500",wikidata:"Q13353",nameEn:"Montserrat",country:"GB",groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 664"]},geometry:{type:"MultiPolygon",coordinates:[[[[-61.83929,16.66647],[-62.14123,17.02632],[-62.52079,16.69392],[-62.17275,16.35721],[-61.83929,16.66647]]]]}},{type:"Feature",properties:{iso1A2:"MT",iso1A3:"MLT",iso1N3:"470",wikidata:"Q233",nameEn:"Malta",groups:["EU","039","150"],driveSide:"left",callingCodes:["356"]},geometry:{type:"MultiPolygon",coordinates:[[[[15.70991,35.79901],[14.07544,36.41525],[13.27636,35.20764],[15.70991,35.79901]]]]}},{type:"Feature",properties:{iso1A2:"MU",iso1A3:"MUS",iso1N3:"480",wikidata:"Q1027",nameEn:"Mauritius",groups:["014","202","002"],driveSide:"left",callingCodes:["230"]},geometry:{type:"MultiPolygon",coordinates:[[[[56.73473,-21.9174],[64.11105,-21.5783],[63.47388,-9.1938],[56.09755,-9.55401],[56.73473,-21.9174]]]]}},{type:"Feature",properties:{iso1A2:"MV",iso1A3:"MDV",iso1N3:"462",wikidata:"Q826",nameEn:"Maldives",groups:["034","142"],driveSide:"left",callingCodes:["960"]},geometry:{type:"MultiPolygon",coordinates:[[[[71.27292,7.36038],[73.37814,-3.88401],[74.6203,7.39289],[71.27292,7.36038]]]]}},{type:"Feature",properties:{iso1A2:"MW",iso1A3:"MWI",iso1N3:"454",wikidata:"Q1020",nameEn:"Malawi",groups:["014","202","002"],driveSide:"left",callingCodes:["265"]},geometry:{type:"MultiPolygon",coordinates:[[[[33.48052,-9.62442],[33.31581,-9.48554],[33.14925,-9.49322],[32.99397,-9.36712],[32.95389,-9.40138],[33.00476,-9.5133],[33.00256,-9.63053],[33.05485,-9.61316],[33.10163,-9.66525],[33.12144,-9.58929],[33.2095,-9.61099],[33.31517,-9.82364],[33.36581,-9.81063],[33.37902,-9.9104],[33.31297,-10.05133],[33.53863,-10.20148],[33.54797,-10.36077],[33.70675,-10.56896],[33.47636,-10.78465],[33.28022,-10.84428],[33.25998,-10.88862],[33.39697,-11.15296],[33.29267,-11.3789],[33.29267,-11.43536],[33.23663,-11.40637],[33.24252,-11.59302],[33.32692,-11.59248],[33.33937,-11.91252],[33.25998,-12.14242],[33.3705,-12.34931],[33.47636,-12.32498],[33.54485,-12.35996],[33.37517,-12.54085],[33.28177,-12.54692],[33.18837,-12.61377],[33.05917,-12.59554],[32.94397,-12.76868],[32.96733,-12.88251],[33.02181,-12.88707],[32.98289,-13.12671],[33.0078,-13.19492],[32.86113,-13.47292],[32.84176,-13.52794],[32.73683,-13.57682],[32.68436,-13.55769],[32.66468,-13.60019],[32.68654,-13.64268],[32.7828,-13.64805],[32.84528,-13.71576],[32.76962,-13.77224],[32.79015,-13.80755],[32.88985,-13.82956],[32.99042,-13.95689],[33.02977,-14.05022],[33.07568,-13.98447],[33.16749,-13.93992],[33.24249,-14.00019],[33.66677,-14.61306],[33.7247,-14.4989],[33.88503,-14.51652],[33.92898,-14.47929],[34.08588,-14.48893],[34.18733,-14.43823],[34.22355,-14.43607],[34.34453,-14.3985],[34.35843,-14.38652],[34.39277,-14.39467],[34.4192,-14.43191],[34.44641,-14.47746],[34.45053,-14.49873],[34.47628,-14.53363],[34.48932,-14.53646],[34.49636,-14.55091],[34.52366,-14.5667],[34.53962,-14.59776],[34.55112,-14.64494],[34.53516,-14.67782],[34.52057,-14.68263],[34.54503,-14.74672],[34.567,-14.77345],[34.61522,-14.99583],[34.57503,-15.30619],[34.43126,-15.44778],[34.44981,-15.60864],[34.25195,-15.90321],[34.43126,-16.04737],[34.40344,-16.20923],[35.04805,-16.83167],[35.13771,-16.81687],[35.17017,-16.93521],[35.04805,-17.00027],[35.0923,-17.13235],[35.3062,-17.1244],[35.27065,-16.93817],[35.30929,-16.82871],[35.27219,-16.69402],[35.14235,-16.56812],[35.25828,-16.4792],[35.30157,-16.2211],[35.43355,-16.11371],[35.52365,-16.15414],[35.70107,-16.10147],[35.80487,-16.03907],[35.85303,-15.41913],[35.78799,-15.17428],[35.91812,-14.89514],[35.87212,-14.89478],[35.86945,-14.67481],[35.5299,-14.27714],[35.47989,-14.15594],[34.86229,-13.48958],[34.60253,-13.48487],[34.37831,-12.17408],[34.46088,-12.0174],[34.70739,-12.15652],[34.82903,-12.04837],[34.57917,-11.87849],[34.64241,-11.57499],[34.96296,-11.57354],[34.91153,-11.39799],[34.79375,-11.32245],[34.63305,-11.11731],[34.61161,-11.01611],[34.67047,-10.93796],[34.65946,-10.6828],[34.57581,-10.56271],[34.51911,-10.12279],[34.54499,-10.0678],[34.03865,-9.49398],[33.95829,-9.54066],[33.9638,-9.62206],[33.93298,-9.71647],[33.76677,-9.58516],[33.48052,-9.62442]]]]}},{type:"Feature",properties:{iso1A2:"MX",iso1A3:"MEX",iso1N3:"484",wikidata:"Q96",nameEn:"Mexico",groups:["013","003","419","019"],callingCodes:["52"]},geometry:{type:"MultiPolygon",coordinates:[[[[-117.1243,32.53427],[-118.48109,32.5991],[-120.12904,18.41089],[-92.37213,14.39277],[-92.2261,14.53423],[-92.1454,14.6804],[-92.18161,14.84147],[-92.1423,14.88647],[-92.1454,14.98143],[-92.0621,15.07406],[-92.20983,15.26077],[-91.73182,16.07371],[-90.44567,16.07573],[-90.40499,16.40524],[-90.61212,16.49832],[-90.69064,16.70697],[-91.04436,16.92175],[-91.43809,17.25373],[-90.99199,17.25192],[-90.98678,17.81655],[-89.14985,17.81563],[-89.15105,17.95104],[-89.03839,18.0067],[-88.8716,17.89535],[-88.71505,18.0707],[-88.48242,18.49164],[-88.3268,18.49048],[-88.29909,18.47591],[-88.26593,18.47617],[-88.03238,18.41778],[-88.03165,18.16657],[-87.90671,18.15213],[-87.87604,18.18313],[-87.86657,18.19971],[-87.85693,18.18266],[-87.84815,18.18511],[-86.92368,17.61462],[-85.9092,21.8218],[-96.92418,25.97377],[-97.13927,25.96583],[-97.35946,25.92189],[-97.37332,25.83854],[-97.42511,25.83969],[-97.45669,25.86874],[-97.49828,25.89877],[-97.52025,25.88518],[-97.66511,26.01708],[-97.95155,26.0625],[-97.97017,26.05232],[-98.24603,26.07191],[-98.27075,26.09457],[-98.30491,26.10475],[-98.35126,26.15129],[-99.00546,26.3925],[-99.03053,26.41249],[-99.08477,26.39849],[-99.53573,27.30926],[-99.49744,27.43746],[-99.482,27.47128],[-99.48045,27.49016],[-99.50208,27.50021],[-99.52955,27.49747],[-99.51478,27.55836],[-99.55409,27.61314],[-100.50029,28.66117],[-100.51222,28.70679],[-100.5075,28.74066],[-100.52313,28.75598],[-100.59809,28.88197],[-100.63689,28.90812],[-100.67294,29.09744],[-100.79696,29.24688],[-100.87982,29.296],[-100.94056,29.33371],[-100.94579,29.34523],[-100.96725,29.3477],[-101.01128,29.36947],[-101.05686,29.44738],[-101.47277,29.7744],[-102.60596,29.8192],[-103.15787,28.93865],[-104.37752,29.54255],[-104.39363,29.55396],[-104.3969,29.57105],[-104.5171,29.64671],[-104.77674,30.4236],[-106.00363,31.39181],[-106.09025,31.40569],[-106.20346,31.46305],[-106.23711,31.51262],[-106.24612,31.54193],[-106.28084,31.56173],[-106.30305,31.62154],[-106.33419,31.66303],[-106.34864,31.69663],[-106.3718,31.71165],[-106.38003,31.73151],[-106.41773,31.75196],[-106.43419,31.75478],[-106.45244,31.76523],[-106.46726,31.75998],[-106.47298,31.75054],[-106.48815,31.74769],[-106.50111,31.75714],[-106.50962,31.76155],[-106.51251,31.76922],[-106.52266,31.77509],[-106.529,31.784],[-108.20899,31.78534],[-108.20979,31.33316],[-109.05235,31.3333],[-111.07523,31.33232],[-112.34553,31.7357],[-114.82011,32.49609],[-114.79524,32.55731],[-114.81141,32.55543],[-114.80584,32.62028],[-114.76736,32.64094],[-114.71871,32.71894],[-115.88053,32.63624],[-117.1243,32.53427]]]]}},{type:"Feature",properties:{iso1A2:"MY",iso1A3:"MYS",iso1N3:"458",wikidata:"Q833",nameEn:"Malaysia",groups:["035","142"],driveSide:"left",callingCodes:["60"]},geometry:{type:"MultiPolygon",coordinates:[[[[114.08532,4.64632],[109.55486,8.10026],[104.81582,8.03101],[102.46318,7.22462],[102.09086,6.23546],[102.08127,6.22679],[102.07732,6.193],[102.09182,6.14161],[102.01835,6.05407],[101.99209,6.04075],[101.97114,6.01992],[101.9714,6.00575],[101.94712,5.98421],[101.92819,5.85511],[101.91776,5.84269],[101.89188,5.8386],[101.80144,5.74505],[101.75074,5.79091],[101.69773,5.75881],[101.58019,5.93534],[101.25524,5.78633],[101.25755,5.71065],[101.14062,5.61613],[100.98815,5.79464],[101.02708,5.91013],[101.087,5.9193],[101.12388,6.11411],[101.06165,6.14161],[101.12618,6.19431],[101.10313,6.25617],[100.85884,6.24929],[100.81045,6.45086],[100.74822,6.46231],[100.74361,6.50811],[100.66986,6.45086],[100.43027,6.52389],[100.42351,6.51762],[100.41791,6.5189],[100.41152,6.52299],[100.35413,6.54932],[100.31929,6.65413],[100.32607,6.65933],[100.32671,6.66526],[100.31884,6.66423],[100.31618,6.66781],[100.30828,6.66462],[100.29651,6.68439],[100.19511,6.72559],[100.12,6.42105],[100.0756,6.4045],[99.91873,6.50233],[99.50117,6.44501],[99.31854,5.99868],[99.75778,3.86466],[103.03657,1.30383],[103.56591,1.19719],[103.62738,1.35255],[103.67468,1.43166],[103.7219,1.46108],[103.74161,1.4502],[103.76395,1.45183],[103.81181,1.47953],[103.86383,1.46288],[103.89565,1.42841],[103.93384,1.42926],[104.00131,1.42405],[104.02277,1.4438],[104.04622,1.44691],[104.07348,1.43322],[104.08871,1.42015],[104.09162,1.39694],[104.08072,1.35998],[104.12282,1.27714],[104.34728,1.33529],[104.56723,1.44271],[105.01437,3.24936],[108.10426,5.42408],[109.71058,2.32059],[109.64506,2.08014],[109.62558,1.99182],[109.53794,1.91771],[109.57923,1.80624],[109.66397,1.79972],[109.66397,1.60425],[110.35354,0.98869],[110.49182,0.88088],[110.62374,0.873],[111.22979,1.08326],[111.55434,0.97864],[111.82846,0.99349],[111.94553,1.12016],[112.15679,1.17004],[112.2127,1.44135],[112.48648,1.56516],[113.021,1.57819],[113.01448,1.42832],[113.64677,1.23933],[114.03788,1.44787],[114.57892,1.5],[114.80706,1.92351],[114.80706,2.21665],[115.1721,2.49671],[115.11343,2.82879],[115.53713,3.14776],[115.58276,3.93499],[115.90217,4.37708],[117.25801,4.35108],[117.47313,4.18857],[117.67641,4.16535],[117.89538,4.16637],[118.07935,4.15511],[118.8663,4.44172],[118.75416,4.59798],[119.44841,5.09568],[119.34756,5.53889],[117.89159,6.25755],[117.43832,7.3895],[117.17735,7.52841],[116.79524,7.43869],[115.02521,5.35005],[115.16236,5.01011],[115.15092,4.87604],[115.20737,4.8256],[115.27819,4.63661],[115.2851,4.42295],[115.36346,4.33563],[115.31275,4.30806],[115.09978,4.39123],[115.07737,4.53418],[115.04064,4.63706],[115.02278,4.74137],[115.02955,4.82087],[115.05038,4.90275],[114.99417,4.88201],[114.96982,4.81146],[114.88841,4.81905],[114.8266,4.75062],[114.77303,4.72871],[114.83189,4.42387],[114.88039,4.4257],[114.78539,4.12205],[114.64211,4.00694],[114.49922,4.13108],[114.4416,4.27588],[114.32176,4.2552],[114.32176,4.34942],[114.26876,4.49878],[114.15813,4.57],[114.07448,4.58441],[114.08532,4.64632]]]]}},{type:"Feature",properties:{iso1A2:"MZ",iso1A3:"MOZ",iso1N3:"508",wikidata:"Q1029",nameEn:"Mozambique",groups:["014","202","002"],driveSide:"left",callingCodes:["258"]},geometry:{type:"MultiPolygon",coordinates:[[[[40.74206,-10.25691],[40.44265,-10.4618],[40.00295,-10.80255],[39.58249,-10.96043],[39.24395,-11.17433],[38.88996,-11.16978],[38.47258,-11.4199],[38.21598,-11.27289],[37.93618,-11.26228],[37.8388,-11.3123],[37.76614,-11.53352],[37.3936,-11.68949],[36.80309,-11.56836],[36.62068,-11.72884],[36.19094,-11.70008],[36.19094,-11.57593],[35.82767,-11.41081],[35.63599,-11.55927],[34.96296,-11.57354],[34.64241,-11.57499],[34.57917,-11.87849],[34.82903,-12.04837],[34.70739,-12.15652],[34.46088,-12.0174],[34.37831,-12.17408],[34.60253,-13.48487],[34.86229,-13.48958],[35.47989,-14.15594],[35.5299,-14.27714],[35.86945,-14.67481],[35.87212,-14.89478],[35.91812,-14.89514],[35.78799,-15.17428],[35.85303,-15.41913],[35.80487,-16.03907],[35.70107,-16.10147],[35.52365,-16.15414],[35.43355,-16.11371],[35.30157,-16.2211],[35.25828,-16.4792],[35.14235,-16.56812],[35.27219,-16.69402],[35.30929,-16.82871],[35.27065,-16.93817],[35.3062,-17.1244],[35.0923,-17.13235],[35.04805,-17.00027],[35.17017,-16.93521],[35.13771,-16.81687],[35.04805,-16.83167],[34.40344,-16.20923],[34.43126,-16.04737],[34.25195,-15.90321],[34.44981,-15.60864],[34.43126,-15.44778],[34.57503,-15.30619],[34.61522,-14.99583],[34.567,-14.77345],[34.54503,-14.74672],[34.52057,-14.68263],[34.53516,-14.67782],[34.55112,-14.64494],[34.53962,-14.59776],[34.52366,-14.5667],[34.49636,-14.55091],[34.48932,-14.53646],[34.47628,-14.53363],[34.45053,-14.49873],[34.44641,-14.47746],[34.4192,-14.43191],[34.39277,-14.39467],[34.35843,-14.38652],[34.34453,-14.3985],[34.22355,-14.43607],[34.18733,-14.43823],[34.08588,-14.48893],[33.92898,-14.47929],[33.88503,-14.51652],[33.7247,-14.4989],[33.66677,-14.61306],[33.24249,-14.00019],[30.22098,-14.99447],[30.41902,-15.62269],[30.42568,-15.9962],[30.91597,-15.99924],[30.97761,-16.05848],[31.13171,-15.98019],[31.30563,-16.01193],[31.42451,-16.15154],[31.67988,-16.19595],[31.90223,-16.34388],[31.91324,-16.41569],[32.02772,-16.43892],[32.28529,-16.43892],[32.42838,-16.4727],[32.71017,-16.59932],[32.69917,-16.66893],[32.78943,-16.70267],[32.97655,-16.70689],[32.91051,-16.89446],[32.84113,-16.92259],[32.96554,-17.11971],[33.00517,-17.30477],[33.0426,-17.3468],[32.96554,-17.48964],[32.98536,-17.55891],[33.0492,-17.60298],[32.94133,-17.99705],[33.03159,-18.35054],[33.02278,-18.4696],[32.88629,-18.51344],[32.88629,-18.58023],[32.95013,-18.69079],[32.9017,-18.7992],[32.82465,-18.77419],[32.70137,-18.84712],[32.73439,-18.92628],[32.69917,-18.94293],[32.72118,-19.02204],[32.84006,-19.0262],[32.87088,-19.09279],[32.85107,-19.29238],[32.77966,-19.36098],[32.78282,-19.47513],[32.84446,-19.48343],[32.84666,-19.68462],[32.95013,-19.67219],[33.06461,-19.77787],[33.01178,-20.02007],[32.93032,-20.03868],[32.85987,-20.16686],[32.85987,-20.27841],[32.66174,-20.56106],[32.55167,-20.56312],[32.48122,-20.63319],[32.51644,-20.91929],[32.37115,-21.133],[32.48236,-21.32873],[32.41234,-21.31246],[31.38336,-22.36919],[31.30611,-22.422],[31.55779,-23.176],[31.56539,-23.47268],[31.67942,-23.60858],[31.70223,-23.72695],[31.77445,-23.90082],[31.87707,-23.95293],[31.90368,-24.18892],[31.9835,-24.29983],[32.03196,-25.10785],[32.01676,-25.38117],[31.97875,-25.46356],[32.00631,-25.65044],[31.92649,-25.84216],[31.974,-25.95387],[32.00916,-25.999],[32.08599,-26.00978],[32.10435,-26.15656],[32.07352,-26.40185],[32.13409,-26.5317],[32.13315,-26.84345],[32.19409,-26.84032],[32.22302,-26.84136],[32.29584,-26.852],[32.35222,-26.86027],[34.51034,-26.91792],[42.99868,-12.65261],[40.74206,-10.25691]]]]}},{type:"Feature",properties:{iso1A2:"NA",iso1A3:"NAM",iso1N3:"516",wikidata:"Q1030",nameEn:"Namibia",groups:["018","202","002"],driveSide:"left",callingCodes:["264"]},geometry:{type:"MultiPolygon",coordinates:[[[[14.28743,-17.38814],[13.95896,-17.43141],[13.36212,-16.98048],[12.97145,-16.98567],[12.52111,-17.24495],[12.07076,-17.15165],[11.75063,-17.25013],[10.5065,-17.25284],[12.51595,-32.27486],[16.45332,-28.63117],[16.46592,-28.57126],[16.59922,-28.53246],[16.90446,-28.057],[17.15405,-28.08573],[17.4579,-28.68718],[18.99885,-28.89165],[19.99882,-28.42622],[19.99817,-24.76768],[19.99912,-21.99991],[20.99751,-22.00026],[20.99904,-18.31743],[21.45556,-18.31795],[23.0996,-18.00075],[23.29618,-17.99855],[23.61088,-18.4881],[24.19416,-18.01919],[24.40577,-17.95726],[24.57485,-18.07151],[24.6303,-17.9863],[24.71887,-17.9218],[24.73364,-17.89338],[24.95586,-17.79674],[25.05895,-17.84452],[25.16882,-17.78253],[25.26433,-17.79571],[25.00198,-17.58221],[24.70864,-17.49501],[24.5621,-17.52963],[24.38712,-17.46818],[24.32811,-17.49082],[24.23619,-17.47489],[23.47474,-17.62877],[21.42741,-18.02787],[21.14283,-17.94318],[18.84226,-17.80375],[18.39229,-17.38927],[14.28743,-17.38814]]]]}},{type:"Feature",properties:{iso1A2:"NC",iso1A3:"NCL",iso1N3:"540",wikidata:"Q33788",nameEn:"New Caledonia",country:"FR",groups:["054","009"],callingCodes:["687"]},geometry:{type:"MultiPolygon",coordinates:[[[[158.65519,-23.4036],[174.90025,-23.53966],[162.93363,-17.28904],[157.83842,-18.82563],[158.65519,-23.4036]]]]}},{type:"Feature",properties:{iso1A2:"NE",iso1A3:"NER",iso1N3:"562",wikidata:"Q1032",nameEn:"Niger",aliases:["RN"],groups:["011","202","002"],callingCodes:["227"]},geometry:{type:"MultiPolygon",coordinates:[[[[14.22918,22.61719],[13.5631,23.16574],[11.96886,23.51735],[7.48273,20.87258],[7.38361,20.79165],[5.8153,19.45101],[4.26651,19.14224],[4.26762,17.00432],[4.21787,17.00118],[4.19893,16.39923],[3.50368,15.35934],[3.03134,15.42221],[3.01806,15.34571],[1.31275,15.27978],[0.96711,14.98275],[0.72632,14.95898],[0.23859,15.00135],[0.16936,14.51654],[0.38051,14.05575],[0.61924,13.68491],[0.77377,13.6866],[0.77637,13.64442],[0.99514,13.5668],[1.02813,13.46635],[1.20088,13.38951],[1.24429,13.39373],[1.28509,13.35488],[1.24516,13.33968],[1.21217,13.37853],[1.18873,13.31771],[0.99253,13.37515],[0.99167,13.10727],[2.26349,12.41915],[2.05785,12.35539],[2.39723,11.89473],[2.45824,11.98672],[2.39657,12.10952],[2.37783,12.24804],[2.6593,12.30631],[2.83978,12.40585],[3.25352,12.01467],[3.31613,11.88495],[3.48187,11.86092],[3.59375,11.70269],[3.61075,11.69181],[3.67988,11.75429],[3.67122,11.80865],[3.63063,11.83042],[3.61955,11.91847],[3.67775,11.97599],[3.63136,12.11826],[3.66364,12.25884],[3.65111,12.52223],[3.94339,12.74979],[4.10006,12.98862],[4.14367,13.17189],[4.14186,13.47586],[4.23456,13.47725],[4.4668,13.68286],[4.87425,13.78],[4.9368,13.7345],[5.07396,13.75052],[5.21026,13.73627],[5.27797,13.75474],[5.35437,13.83567],[5.52957,13.8845],[6.15771,13.64564],[6.27411,13.67835],[6.43053,13.6006],[6.69617,13.34057],[6.94445,12.99825],[7.0521,13.00076],[7.12676,13.02445],[7.22399,13.1293],[7.39241,13.09717],[7.81085,13.34902],[8.07997,13.30847],[8.25185,13.20369],[8.41853,13.06166],[8.49493,13.07519],[8.60431,13.01768],[8.64251,12.93985],[8.97413,12.83661],[9.65995,12.80614],[10.00373,13.18171],[10.19993,13.27129],[10.46731,13.28819],[10.66004,13.36422],[11.4535,13.37773],[11.88236,13.2527],[12.04209,13.14452],[12.16189,13.10056],[12.19315,13.12423],[12.47095,13.06673],[12.58033,13.27805],[12.6793,13.29157],[12.87376,13.48919],[13.05085,13.53984],[13.19844,13.52802],[13.33213,13.71195],[13.6302,13.71094],[13.47559,14.40881],[13.48259,14.46704],[13.68573,14.55276],[13.67878,14.64013],[13.809,14.72915],[13.78991,14.87519],[13.86301,15.04043],[14.37425,15.72591],[15.50373,16.89649],[15.6032,18.77402],[15.75098,19.93002],[15.99632,20.35364],[15.6721,20.70069],[15.59841,20.74039],[15.56004,20.79488],[15.55382,20.86507],[15.57248,20.92138],[15.62515,20.95395],[15.28332,21.44557],[15.20213,21.49365],[15.19692,21.99339],[14.99751,23.00539],[14.22918,22.61719]]]]}},{type:"Feature",properties:{iso1A2:"NF",iso1A3:"NFK",iso1N3:"574",wikidata:"Q31057",nameEn:"Norfolk Island",country:"AU",groups:["053","009"],driveSide:"left",callingCodes:["672 3"]},geometry:{type:"MultiPolygon",coordinates:[[[[169.82316,-28.16667],[166.29505,-28.29175],[167.94076,-30.60745],[169.82316,-28.16667]]]]}},{type:"Feature",properties:{iso1A2:"NG",iso1A3:"NGA",iso1N3:"566",wikidata:"Q1033",nameEn:"Nigeria",groups:["011","202","002"],callingCodes:["234"]},geometry:{type:"MultiPolygon",coordinates:[[[[6.15771,13.64564],[5.52957,13.8845],[5.35437,13.83567],[5.27797,13.75474],[5.21026,13.73627],[5.07396,13.75052],[4.9368,13.7345],[4.87425,13.78],[4.4668,13.68286],[4.23456,13.47725],[4.14186,13.47586],[4.14367,13.17189],[4.10006,12.98862],[3.94339,12.74979],[3.65111,12.52223],[3.66364,12.25884],[3.63136,12.11826],[3.67775,11.97599],[3.61955,11.91847],[3.63063,11.83042],[3.67122,11.80865],[3.67988,11.75429],[3.61075,11.69181],[3.59375,11.70269],[3.49175,11.29765],[3.71505,11.13015],[3.84243,10.59316],[3.78292,10.40538],[3.6844,10.46351],[3.57275,10.27185],[3.66908,10.18136],[3.54429,9.87739],[3.35383,9.83641],[3.32099,9.78032],[3.34726,9.70696],[3.25093,9.61632],[3.13928,9.47167],[3.14147,9.28375],[3.08017,9.10006],[2.77907,9.06924],[2.67523,7.87825],[2.73095,7.7755],[2.73405,7.5423],[2.78668,7.5116],[2.79442,7.43486],[2.74489,7.42565],[2.76965,7.13543],[2.71702,6.95722],[2.74024,6.92802],[2.73405,6.78508],[2.78823,6.76356],[2.78204,6.70514],[2.7325,6.64057],[2.74334,6.57291],[2.70464,6.50831],[2.70566,6.38038],[2.74181,6.13349],[5.87055,3.78489],[8.34397,4.30689],[8.60302,4.87353],[8.78027,5.1243],[8.92029,5.58403],[8.83687,5.68483],[8.88156,5.78857],[8.84209,5.82562],[9.51757,6.43874],[9.70674,6.51717],[9.77824,6.79088],[9.86314,6.77756],[10.15135,7.03781],[10.21466,6.88996],[10.53639,6.93432],[10.57214,7.16345],[10.59746,7.14719],[10.60789,7.06885],[10.83727,6.9358],[10.8179,6.83377],[10.94302,6.69325],[11.09644,6.68437],[11.09495,6.51717],[11.42041,6.53789],[11.42264,6.5882],[11.51499,6.60892],[11.57755,6.74059],[11.55818,6.86186],[11.63117,6.9905],[11.87396,7.09398],[11.84864,7.26098],[11.93205,7.47812],[12.01844,7.52981],[11.99908,7.67302],[12.20909,7.97553],[12.19271,8.10826],[12.24782,8.17904],[12.26123,8.43696],[12.4489,8.52536],[12.44146,8.6152],[12.68722,8.65938],[12.71701,8.7595],[12.79,8.75361],[12.81085,8.91992],[12.90022,9.11411],[12.91958,9.33905],[12.85628,9.36698],[13.02385,9.49334],[13.22642,9.57266],[13.25472,9.76795],[13.29941,9.8296],[13.25025,9.86042],[13.24132,9.91031],[13.27409,9.93232],[13.286,9.9822],[13.25323,10.00127],[13.25025,10.03647],[13.34111,10.12299],[13.43644,10.13326],[13.5705,10.53183],[13.54964,10.61236],[13.73434,10.9255],[13.70753,10.94451],[13.7403,11.00593],[13.78945,11.00154],[13.97489,11.30258],[14.17821,11.23831],[14.6124,11.51283],[14.64591,11.66166],[14.55207,11.72001],[14.61612,11.7798],[14.6474,12.17466],[14.4843,12.35223],[14.22215,12.36533],[14.17523,12.41916],[14.20204,12.53405],[14.08251,13.0797],[13.6302,13.71094],[13.33213,13.71195],[13.19844,13.52802],[13.05085,13.53984],[12.87376,13.48919],[12.6793,13.29157],[12.58033,13.27805],[12.47095,13.06673],[12.19315,13.12423],[12.16189,13.10056],[12.04209,13.14452],[11.88236,13.2527],[11.4535,13.37773],[10.66004,13.36422],[10.46731,13.28819],[10.19993,13.27129],[10.00373,13.18171],[9.65995,12.80614],[8.97413,12.83661],[8.64251,12.93985],[8.60431,13.01768],[8.49493,13.07519],[8.41853,13.06166],[8.25185,13.20369],[8.07997,13.30847],[7.81085,13.34902],[7.39241,13.09717],[7.22399,13.1293],[7.12676,13.02445],[7.0521,13.00076],[6.94445,12.99825],[6.69617,13.34057],[6.43053,13.6006],[6.27411,13.67835],[6.15771,13.64564]]]]}},{type:"Feature",properties:{iso1A2:"NI",iso1A3:"NIC",iso1N3:"558",wikidata:"Q811",nameEn:"Nicaragua",groups:["013","003","419","019"],callingCodes:["505"]},geometry:{type:"MultiPolygon",coordinates:[[[[-83.13724,15.00002],[-83.49268,15.01158],[-83.62101,14.89448],[-83.89551,14.76697],[-84.10584,14.76353],[-84.48373,14.63249],[-84.70119,14.68078],[-84.82596,14.82212],[-84.90082,14.80489],[-85.1575,14.53934],[-85.18602,14.24929],[-85.32149,14.2562],[-85.45762,14.11304],[-85.73964,13.9698],[-85.75477,13.8499],[-86.03458,13.99181],[-86.00685,14.08474],[-86.14801,14.04317],[-86.35219,13.77157],[-86.76812,13.79605],[-86.71267,13.30348],[-86.87066,13.30641],[-86.93383,13.18677],[-86.93197,13.05313],[-87.03785,12.98682],[-87.06306,13.00892],[-87.37107,12.98646],[-87.55124,13.12523],[-87.7346,13.13228],[-88.11443,12.63306],[-86.14524,11.09059],[-85.71223,11.06868],[-85.60529,11.22607],[-84.92439,10.9497],[-84.68197,11.07568],[-83.90838,10.71161],[-83.66597,10.79916],[-83.68276,11.01562],[-82.56142,11.91792],[-82.06974,14.49418],[-83.04763,15.03256],[-83.13724,15.00002]]]]}},{type:"Feature",properties:{iso1A2:"NL",iso1A3:"NLD",iso1N3:"528",wikidata:"Q55",nameEn:"Netherlands",groups:["EU","155","150"],callingCodes:["31"]},geometry:{type:"MultiPolygon",coordinates:[[[[5.45168,54.20039],[2.56575,51.85301],[3.36263,51.37112],[3.38696,51.33436],[3.35847,51.31572],[3.38289,51.27331],[3.41704,51.25933],[3.43488,51.24135],[3.52698,51.2458],[3.51502,51.28697],[3.58939,51.30064],[3.78999,51.25766],[3.78783,51.2151],[3.90125,51.20371],[3.97889,51.22537],[4.01957,51.24504],[4.05165,51.24171],[4.16721,51.29348],[4.24024,51.35371],[4.21923,51.37443],[4.33265,51.37687],[4.34086,51.35738],[4.39292,51.35547],[4.43777,51.36989],[4.38064,51.41965],[4.39747,51.43316],[4.38122,51.44905],[4.47736,51.4778],[4.5388,51.48184],[4.54675,51.47265],[4.52846,51.45002],[4.53521,51.4243],[4.57489,51.4324],[4.65442,51.42352],[4.72935,51.48424],[4.74578,51.48937],[4.77321,51.50529],[4.78803,51.50284],[4.84139,51.4799],[4.82409,51.44736],[4.82946,51.4213],[4.78314,51.43319],[4.76577,51.43046],[4.77229,51.41337],[4.78941,51.41102],[4.84988,51.41502],[4.90016,51.41404],[4.92152,51.39487],[5.00393,51.44406],[5.0106,51.47167],[5.03281,51.48679],[5.04774,51.47022],[5.07891,51.4715],[5.10456,51.43163],[5.07102,51.39469],[5.13105,51.34791],[5.13377,51.31592],[5.16222,51.31035],[5.2002,51.32243],[5.24244,51.30495],[5.22542,51.26888],[5.23814,51.26064],[5.26461,51.26693],[5.29716,51.26104],[5.33886,51.26314],[5.347,51.27502],[5.41672,51.26248],[5.4407,51.28169],[5.46519,51.2849],[5.48476,51.30053],[5.515,51.29462],[5.5569,51.26544],[5.5603,51.22249],[5.65145,51.19788],[5.65528,51.18736],[5.70344,51.1829],[5.74617,51.18928],[5.77735,51.17845],[5.77697,51.1522],[5.82564,51.16753],[5.85508,51.14445],[5.80798,51.11661],[5.8109,51.10861],[5.83226,51.10585],[5.82921,51.09328],[5.79903,51.09371],[5.79835,51.05834],[5.77258,51.06196],[5.75961,51.03113],[5.77688,51.02483],[5.76242,50.99703],[5.71864,50.96092],[5.72875,50.95428],[5.74752,50.96202],[5.75927,50.95601],[5.74644,50.94723],[5.72545,50.92312],[5.72644,50.91167],[5.71626,50.90796],[5.69858,50.91046],[5.67886,50.88142],[5.64504,50.87107],[5.64009,50.84742],[5.65259,50.82309],[5.70118,50.80764],[5.68995,50.79641],[5.70107,50.7827],[5.68091,50.75804],[5.69469,50.75529],[5.72216,50.76398],[5.73904,50.75674],[5.74356,50.7691],[5.76533,50.78159],[5.77513,50.78308],[5.80673,50.7558],[5.84548,50.76542],[5.84888,50.75448],[5.88734,50.77092],[5.89129,50.75125],[5.89132,50.75124],[5.95942,50.7622],[5.97545,50.75441],[6.01976,50.75398],[6.02624,50.77453],[5.97497,50.79992],[5.98404,50.80988],[6.00462,50.80065],[6.02328,50.81694],[6.01921,50.84435],[6.05623,50.8572],[6.05702,50.85179],[6.07431,50.84674],[6.07693,50.86025],[6.08805,50.87223],[6.07486,50.89307],[6.09297,50.92066],[6.01615,50.93367],[6.02697,50.98303],[5.95282,50.98728],[5.90296,50.97356],[5.90493,51.00198],[5.87849,51.01969],[5.86735,51.05182],[5.9134,51.06736],[5.9541,51.03496],[5.98292,51.07469],[6.16706,51.15677],[6.17384,51.19589],[6.07889,51.17038],[6.07889,51.24432],[6.16977,51.33169],[6.22674,51.36135],[6.22641,51.39948],[6.20654,51.40049],[6.21724,51.48568],[6.18017,51.54096],[6.09055,51.60564],[6.11759,51.65609],[6.02767,51.6742],[6.04091,51.71821],[5.95003,51.7493],[5.98665,51.76944],[5.94568,51.82786],[5.99848,51.83195],[6.06705,51.86136],[6.10337,51.84829],[6.16902,51.84094],[6.11551,51.89769],[6.15349,51.90439],[6.21443,51.86801],[6.29872,51.86801],[6.30593,51.84998],[6.40704,51.82771],[6.38815,51.87257],[6.47179,51.85395],[6.50231,51.86313],[6.58556,51.89386],[6.68386,51.91861],[6.72319,51.89518],[6.82357,51.96711],[6.83035,51.9905],[6.68128,52.05052],[6.76117,52.11895],[6.83984,52.11728],[6.97189,52.20329],[6.9897,52.2271],[7.03729,52.22695],[7.06365,52.23789],[7.02703,52.27941],[7.07044,52.37805],[7.03417,52.40237],[6.99041,52.47235],[6.94293,52.43597],[6.69507,52.488],[6.71641,52.62905],[6.77307,52.65375],[7.04557,52.63318],[7.07253,52.81083],[7.21694,53.00742],[7.17898,53.13817],[7.22681,53.18165],[7.21679,53.20058],[7.19052,53.31866],[7.00198,53.32672],[6.91025,53.44221],[5.45168,54.20039]],[[4.93295,51.44945],[4.95244,51.45207],[4.9524,51.45014],[4.93909,51.44632],[4.93295,51.44945]],[[4.91493,51.4353],[4.91935,51.43634],[4.92227,51.44252],[4.91811,51.44621],[4.92287,51.44741],[4.92811,51.4437],[4.92566,51.44273],[4.92815,51.43856],[4.92879,51.44161],[4.93544,51.44634],[4.94025,51.44193],[4.93416,51.44185],[4.93471,51.43861],[4.94265,51.44003],[4.93986,51.43064],[4.92952,51.42984],[4.92652,51.43329],[4.91493,51.4353]]]]}},{type:"Feature",properties:{iso1A2:"NO",iso1A3:"NOR",iso1N3:"578",wikidata:"Q20",nameEn:"Norway",groups:["154","150"],callingCodes:["47"]},geometry:{type:"MultiPolygon",coordinates:[[[[10.40861,58.38489],[10.64958,58.89391],[11.08911,58.98745],[11.15367,59.07862],[11.34459,59.11672],[11.4601,58.99022],[11.45199,58.89604],[11.65732,58.90177],[11.8213,59.24985],[11.69297,59.59442],[11.92112,59.69531],[11.87121,59.86039],[12.15641,59.8926],[12.36317,59.99259],[12.52003,60.13846],[12.59133,60.50559],[12.2277,61.02442],[12.69115,61.06584],[12.86939,61.35427],[12.57707,61.56547],[12.40595,61.57226],[12.14746,61.7147],[12.29187,62.25699],[12.07085,62.6297],[12.19919,63.00104],[11.98529,63.27487],[12.19919,63.47935],[12.14928,63.59373],[12.74105,64.02171],[13.23411,64.09087],[13.98222,64.00953],[14.16051,64.18725],[14.11117,64.46674],[13.64276,64.58402],[14.50926,65.31786],[14.53778,66.12399],[15.05113,66.15572],[15.49318,66.28509],[15.37197,66.48217],[16.35589,67.06419],[16.39154,67.21653],[16.09922,67.4364],[16.12774,67.52106],[16.38441,67.52923],[16.7409,67.91037],[17.30416,68.11591],[17.90787,67.96537],[18.13836,68.20874],[18.1241,68.53721],[18.39503,68.58672],[18.63032,68.50849],[18.97255,68.52416],[19.93508,68.35911],[20.22027,68.48759],[19.95647,68.55546],[20.22027,68.67246],[20.33435,68.80174],[20.28444,68.93283],[20.0695,69.04469],[20.55258,69.06069],[20.72171,69.11874],[21.05775,69.0356],[21.11099,69.10291],[20.98641,69.18809],[21.00732,69.22755],[21.27827,69.31281],[21.63833,69.27485],[22.27276,68.89514],[22.38367,68.71561],[22.53321,68.74393],[23.13064,68.64684],[23.68017,68.70276],[23.781,68.84514],[24.02299,68.81601],[24.18432,68.73936],[24.74898,68.65143],[24.90023,68.55579],[24.93048,68.61102],[25.10189,68.63307],[25.12206,68.78684],[25.42455,68.90328],[25.61613,68.89602],[25.75729,68.99383],[25.69679,69.27039],[25.96904,69.68397],[26.40261,69.91377],[26.64461,69.96565],[27.05802,69.92069],[27.57226,70.06215],[27.95542,70.0965],[27.97558,69.99671],[28.32849,69.88605],[28.36883,69.81658],[29.12697,69.69193],[29.31664,69.47994],[28.8629,69.22395],[28.81248,69.11997],[28.91738,69.04774],[29.0444,69.0119],[29.26623,69.13794],[29.27631,69.2811],[29.97205,69.41623],[30.16363,69.65244],[30.52662,69.54699],[30.95011,69.54699],[30.84095,69.80584],[31.59909,70.16571],[32.07813,72.01005],[18.46509,71.28681],[-0.3751,61.32236],[7.28637,57.35913],[10.40861,58.38489]]]]}},{type:"Feature",properties:{iso1A2:"NP",iso1A3:"NPL",iso1N3:"524",wikidata:"Q837",nameEn:"Nepal",groups:["034","142"],driveSide:"left",callingCodes:["977"]},geometry:{type:"MultiPolygon",coordinates:[[[[88.13378,27.88015],[87.82681,27.95248],[87.72718,27.80938],[87.56996,27.84517],[87.11696,27.84104],[87.03757,27.94835],[86.75582,28.04182],[86.74181,28.10638],[86.56265,28.09569],[86.51609,27.96623],[86.42736,27.91122],[86.22966,27.9786],[86.18607,28.17364],[86.088,28.09264],[86.08333,28.02121],[86.12069,27.93047],[86.06309,27.90021],[85.94946,27.9401],[85.97813,27.99023],[85.90743,28.05144],[85.84672,28.18187],[85.74864,28.23126],[85.71907,28.38064],[85.69105,28.38475],[85.60854,28.25045],[85.59765,28.30529],[85.4233,28.32996],[85.38127,28.28336],[85.10729,28.34092],[85.18668,28.54076],[85.19135,28.62825],[85.06059,28.68562],[84.85511,28.58041],[84.62317,28.73887],[84.47528,28.74023],[84.2231,28.89571],[84.24801,29.02783],[84.18107,29.23451],[83.97559,29.33091],[83.82303,29.30513],[83.63156,29.16249],[83.44787,29.30513],[83.28131,29.56813],[83.07116,29.61957],[82.73024,29.81695],[82.5341,29.9735],[82.38622,30.02608],[82.16984,30.0692],[82.19475,30.16884],[82.10757,30.23745],[82.10135,30.35439],[81.99082,30.33423],[81.62033,30.44703],[81.41018,30.42153],[81.39928,30.21862],[81.33355,30.15303],[81.2623,30.14596],[81.29032,30.08806],[81.24362,30.0126],[81.12842,30.01395],[81.03953,30.20059],[80.93695,30.18229],[80.8778,30.13384],[80.67076,29.95732],[80.60226,29.95732],[80.56957,29.88176],[80.56247,29.86661],[80.48997,29.79566],[80.43458,29.80466],[80.41554,29.79451],[80.36803,29.73865],[80.38428,29.68513],[80.41858,29.63581],[80.37939,29.57098],[80.24322,29.44299],[80.31428,29.30784],[80.28626,29.20327],[80.24112,29.21414],[80.26602,29.13938],[80.23178,29.11626],[80.18085,29.13649],[80.05743,28.91479],[80.06957,28.82763],[80.12125,28.82346],[80.37188,28.63371],[80.44504,28.63098],[80.52443,28.54897],[80.50575,28.6706],[80.55142,28.69182],[80.89648,28.47237],[81.08507,28.38346],[81.19847,28.36284],[81.32923,28.13521],[81.38683,28.17638],[81.48179,28.12148],[81.47867,28.08303],[81.91223,27.84995],[81.97214,27.93322],[82.06554,27.92222],[82.46405,27.6716],[82.70378,27.72122],[82.74119,27.49838],[82.93261,27.50328],[82.94938,27.46036],[83.19413,27.45632],[83.27197,27.38309],[83.2673,27.36235],[83.29999,27.32778],[83.35136,27.33885],[83.38872,27.39276],[83.39495,27.4798],[83.61288,27.47013],[83.85595,27.35797],[83.86182,27.4241],[83.93306,27.44939],[84.02229,27.43836],[84.10791,27.52399],[84.21376,27.45218],[84.25735,27.44941],[84.29315,27.39],[84.62161,27.33885],[84.69166,27.21294],[84.64496,27.04669],[84.793,26.9968],[84.82913,27.01989],[84.85754,26.98984],[84.96687,26.95599],[84.97186,26.9149],[85.00536,26.89523],[85.05592,26.88991],[85.02635,26.85381],[85.15883,26.86966],[85.19291,26.86909],[85.18046,26.80519],[85.21159,26.75933],[85.34302,26.74954],[85.47752,26.79292],[85.56471,26.84133],[85.5757,26.85955],[85.59461,26.85161],[85.61621,26.86721],[85.66239,26.84822],[85.73483,26.79613],[85.72315,26.67471],[85.76907,26.63076],[85.83126,26.61134],[85.85126,26.60866],[85.8492,26.56667],[86.02729,26.66756],[86.13596,26.60651],[86.22513,26.58863],[86.26235,26.61886],[86.31564,26.61925],[86.49726,26.54218],[86.54258,26.53819],[86.57073,26.49825],[86.61313,26.48658],[86.62686,26.46891],[86.69124,26.45169],[86.74025,26.42386],[86.76797,26.45892],[86.82898,26.43919],[86.94543,26.52076],[86.95912,26.52076],[87.01559,26.53228],[87.04691,26.58685],[87.0707,26.58571],[87.09147,26.45039],[87.14751,26.40542],[87.18863,26.40558],[87.24682,26.4143],[87.26587,26.40592],[87.26568,26.37294],[87.34568,26.34787],[87.37314,26.40815],[87.46566,26.44058],[87.51571,26.43106],[87.55274,26.40596],[87.59175,26.38342],[87.66803,26.40294],[87.67893,26.43501],[87.76004,26.40711],[87.7918,26.46737],[87.84193,26.43663],[87.89085,26.48565],[87.90115,26.44923],[88.00895,26.36029],[88.09414,26.43732],[88.09963,26.54195],[88.16452,26.64111],[88.1659,26.68177],[88.19107,26.75516],[88.12302,26.95324],[88.13422,26.98705],[88.11719,26.98758],[87.9887,27.11045],[88.01587,27.21388],[88.01646,27.21612],[88.07277,27.43007],[88.04008,27.49223],[88.19107,27.79285],[88.1973,27.85067],[88.13378,27.88015]]]]}},{type:"Feature",properties:{iso1A2:"NR",iso1A3:"NRU",iso1N3:"520",wikidata:"Q697",nameEn:"Nauru",groups:["057","009"],driveSide:"left",callingCodes:["674"]},geometry:{type:"MultiPolygon",coordinates:[[[[166.95155,0.14829],[166.21778,-0.7977],[167.60042,-0.88259],[166.95155,0.14829]]]]}},{type:"Feature",properties:{iso1A2:"NU",iso1A3:"NIU",iso1N3:"570",wikidata:"Q34020",nameEn:"Niue",country:"NZ",groups:["061","009"],driveSide:"left",callingCodes:["683"]},geometry:{type:"MultiPolygon",coordinates:[[[[-173.13438,-14.94228],[-173.11048,-23.23027],[-167.73129,-23.22266],[-167.73854,-14.92809],[-171.14262,-14.93704],[-173.13438,-14.94228]]]]}},{type:"Feature",properties:{iso1A2:"NZ",iso1A3:"NZL",iso1N3:"554",wikidata:"Q664",nameEn:"New Zealand",groups:["053","009"],driveSide:"left",callingCodes:["64"]},geometry:{type:"MultiPolygon",coordinates:[[[[-180,-24.21376],[-179.93224,-45.18423],[-155.99562,-45.16785],[-180,-24.21376]]],[[[161.96603,-56.07661],[179.49541,-50.04657],[179.49541,-36.79303],[169.6687,-29.09191],[161.96603,-56.07661]]]]}},{type:"Feature",properties:{iso1A2:"OM",iso1A3:"OMN",iso1N3:"512",wikidata:"Q842",nameEn:"Oman",groups:["145","142"],callingCodes:["968"]},geometry:{type:"MultiPolygon",coordinates:[[[[56.82555,25.7713],[56.79239,26.41236],[56.68954,26.76645],[56.2644,26.58649],[55.81777,26.18798],[56.08666,26.05038],[56.15498,26.06828],[56.19334,25.9795],[56.13963,25.82765],[56.17416,25.77239],[56.13579,25.73524],[56.14826,25.66351],[56.18363,25.65508],[56.20473,25.61119],[56.25365,25.60211],[56.26636,25.60643],[56.25341,25.61443],[56.26534,25.62825],[56.82555,25.7713]]],[[[56.26062,25.33108],[56.23362,25.31253],[56.25008,25.28843],[56.24465,25.27505],[56.20838,25.25668],[56.20872,25.24104],[56.24341,25.22867],[56.27628,25.23404],[56.34438,25.26653],[56.35172,25.30681],[56.3111,25.30107],[56.3005,25.31815],[56.26062,25.33108]],[[56.28423,25.26344],[56.27086,25.26128],[56.2716,25.27916],[56.28102,25.28486],[56.29379,25.2754],[56.28423,25.26344]]],[[[61.45114,22.55394],[56.86325,25.03856],[56.3227,24.97284],[56.34873,24.93205],[56.30269,24.88334],[56.20568,24.85063],[56.20062,24.78565],[56.13684,24.73699],[56.06128,24.74457],[56.03535,24.81161],[55.97836,24.87673],[55.97467,24.89639],[56.05106,24.87461],[56.05715,24.95727],[55.96316,25.00857],[55.90849,24.96771],[55.85094,24.96858],[55.81116,24.9116],[55.81348,24.80102],[55.83408,24.77858],[55.83271,24.68567],[55.76461,24.5287],[55.83271,24.41521],[55.83395,24.32776],[55.80747,24.31069],[55.79145,24.27914],[55.76781,24.26209],[55.75939,24.26114],[55.75382,24.2466],[55.75257,24.23466],[55.76558,24.23227],[55.77658,24.23476],[55.83367,24.20193],[55.95472,24.2172],[56.01799,24.07426],[55.8308,24.01633],[55.73301,24.05994],[55.48677,23.94946],[55.57358,23.669],[55.22634,23.10378],[55.2137,22.71065],[55.66469,21.99658],[54.99756,20.00083],[52.00311,19.00083],[52.78009,17.35124],[52.74267,17.29519],[52.81185,17.28568],[53.09917,16.67084],[53.32998,16.16312],[56.66759,17.24021],[61.45114,22.55394]]]]}},{type:"Feature",properties:{iso1A2:"PA",iso1A3:"PAN",iso1N3:"591",wikidata:"Q804",nameEn:"Panama",groups:["013","003","419","019"],callingCodes:["507"]},geometry:{type:"MultiPolygon",coordinates:[[[[-77.32389,8.81247],[-77.58292,9.22278],[-78.79327,9.93766],[-82.51044,9.65379],[-82.56507,9.57279],[-82.61345,9.49881],[-82.66667,9.49746],[-82.77206,9.59573],[-82.87919,9.62645],[-82.84871,9.4973],[-82.93516,9.46741],[-82.93516,9.07687],[-82.72126,8.97125],[-82.88253,8.83331],[-82.91377,8.774],[-82.92068,8.74832],[-82.8794,8.6981],[-82.82739,8.60153],[-82.83975,8.54755],[-82.83322,8.52464],[-82.8382,8.48117],[-82.8679,8.44042],[-82.93056,8.43465],[-83.05209,8.33394],[-82.9388,8.26634],[-82.88641,8.10219],[-82.89137,8.05755],[-82.89978,8.04083],[-82.94503,7.93865],[-82.13751,6.97312],[-78.06168,7.07793],[-77.89178,7.22681],[-77.81426,7.48319],[-77.72157,7.47612],[-77.72514,7.72348],[-77.57185,7.51147],[-77.17257,7.97422],[-77.45064,8.49991],[-77.32389,8.81247]]]]}},{type:"Feature",properties:{iso1A2:"PE",iso1A3:"PER",iso1N3:"604",wikidata:"Q419",nameEn:"Peru",groups:["005","419","019"],callingCodes:["51"]},geometry:{type:"MultiPolygon",coordinates:[[[[-74.26675,-0.97229],[-74.42701,-0.50218],[-75.18513,-0.0308],[-75.25764,-0.11943],[-75.40192,-0.17196],[-75.61997,-0.10012],[-75.60169,-0.18708],[-75.53615,-0.19213],[-75.22862,-0.60048],[-75.22862,-0.95588],[-75.3872,-0.9374],[-75.57429,-1.55961],[-76.05203,-2.12179],[-76.6324,-2.58397],[-77.94147,-3.05454],[-78.19369,-3.36431],[-78.14324,-3.47653],[-78.22642,-3.51113],[-78.24589,-3.39907],[-78.34362,-3.38633],[-78.68394,-4.60754],[-78.85149,-4.66795],[-79.01659,-5.01481],[-79.1162,-4.97774],[-79.26248,-4.95167],[-79.59402,-4.46848],[-79.79722,-4.47558],[-80.13945,-4.29786],[-80.39256,-4.48269],[-80.46386,-4.41516],[-80.32114,-4.21323],[-80.45023,-4.20938],[-80.4822,-4.05477],[-80.46386,-4.01342],[-80.13232,-3.90317],[-80.19926,-3.68894],[-80.18741,-3.63994],[-80.19848,-3.59249],[-80.21642,-3.5888],[-80.20535,-3.51667],[-80.22629,-3.501],[-80.23651,-3.48652],[-80.24586,-3.48677],[-80.24475,-3.47846],[-80.24123,-3.46124],[-80.20647,-3.431],[-80.30602,-3.39149],[-84.52388,-3.36941],[-85.71054,-21.15413],[-70.59118,-18.35072],[-70.378,-18.3495],[-70.31267,-18.31258],[-70.16394,-18.31737],[-69.96732,-18.25992],[-69.81607,-18.12582],[-69.75305,-17.94605],[-69.82868,-17.72048],[-69.79087,-17.65563],[-69.66483,-17.65083],[-69.46897,-17.4988],[-69.46863,-17.37466],[-69.62883,-17.28142],[-69.16896,-16.72233],[-69.00853,-16.66769],[-69.04027,-16.57214],[-68.98358,-16.42165],[-68.79464,-16.33272],[-68.96238,-16.194],[-69.09986,-16.22693],[-69.20291,-16.16668],[-69.40336,-15.61358],[-69.14856,-15.23478],[-69.36254,-14.94634],[-68.88135,-14.18639],[-69.05265,-13.68546],[-68.8864,-13.40792],[-68.85615,-12.87769],[-68.65044,-12.50689],[-68.98115,-11.8979],[-69.57156,-10.94555],[-69.57835,-10.94051],[-69.90896,-10.92744],[-70.38791,-11.07096],[-70.51395,-10.92249],[-70.64134,-11.0108],[-70.62487,-9.80666],[-70.55429,-9.76692],[-70.58453,-9.58303],[-70.53373,-9.42628],[-71.23394,-9.9668],[-72.14742,-9.98049],[-72.31883,-9.5184],[-72.72216,-9.41397],[-73.21498,-9.40904],[-72.92886,-9.04074],[-73.76576,-7.89884],[-73.65485,-7.77897],[-73.96938,-7.58465],[-73.77011,-7.28944],[-73.73986,-6.87919],[-73.12983,-6.43852],[-73.24579,-6.05764],[-72.83973,-5.14765],[-72.64391,-5.0391],[-71.87003,-4.51661],[-70.96814,-4.36915],[-70.77601,-4.15717],[-70.33236,-4.15214],[-70.19582,-4.3607],[-70.11305,-4.27281],[-70.00888,-4.37833],[-69.94708,-4.2431],[-70.3374,-3.79505],[-70.52393,-3.87553],[-70.71396,-3.7921],[-70.04609,-2.73906],[-70.94377,-2.23142],[-71.75223,-2.15058],[-72.92587,-2.44514],[-73.65312,-1.26222],[-74.26675,-0.97229]]]]}},{type:"Feature",properties:{iso1A2:"PF",iso1A3:"PYF",iso1N3:"258",wikidata:"Q30971",nameEn:"French Polynesia",country:"FR",groups:["061","009"],callingCodes:["689"]},geometry:{type:"MultiPolygon",coordinates:[[[[-149.6249,-7.51261],[-149.61166,-12.30171],[-156.4957,-12.32002],[-156.46451,-23.21255],[-156.44843,-28.52556],[-133.59543,-28.4709],[-133.61511,-21.93325],[-133.65593,-7.46952],[-149.6249,-7.51261]]]]}},{type:"Feature",properties:{iso1A2:"PG",iso1A3:"PNG",iso1N3:"598",wikidata:"Q691",nameEn:"Papua New Guinea",groups:["054","009"],driveSide:"left",callingCodes:["675"]},geometry:{type:"MultiPolygon",coordinates:[[[[141.03157,2.12829],[140.99813,-6.3233],[140.85295,-6.72996],[141.01763,-6.90181],[141.00782,-9.1242],[140.88922,-9.34945],[142.0601,-9.56571],[142.0953,-9.23534],[142.1462,-9.19923],[142.23304,-9.19253],[142.31447,-9.24611],[142.5723,-9.35994],[142.81927,-9.31709],[144.30183,-9.48146],[155.22803,-12.9001],[154.74815,-7.33315],[155.60735,-6.92266],[155.69784,-6.92661],[155.92557,-6.84664],[156.03993,-6.65703],[156.03296,-6.55528],[160.43769,-4.17974],[141.03157,2.12829]]]]}},{type:"Feature",properties:{iso1A2:"PH",iso1A3:"PHL",iso1N3:"608",wikidata:"Q928",nameEn:"Philippines",aliases:["PI","RP"],groups:["035","142"],callingCodes:["63"]},geometry:{type:"MultiPolygon",coordinates:[[[[129.19694,7.84182],[121.8109,21.77688],[120.69238,21.52331],[118.82252,14.67191],[115.39742,10.92666],[116.79524,7.43869],[117.17735,7.52841],[117.43832,7.3895],[117.89159,6.25755],[119.34756,5.53889],[119.44841,5.09568],[118.75416,4.59798],[118.8663,4.44172],[118.07935,4.15511],[118.41402,3.99509],[124.97752,4.82064],[129.19694,7.84182]]]]}},{type:"Feature",properties:{iso1A2:"PK",iso1A3:"PAK",iso1N3:"586",wikidata:"Q843",nameEn:"Pakistan",groups:["034","142"],driveSide:"left",callingCodes:["92"]},geometry:{type:"MultiPolygon",coordinates:[[[[75.72737,36.7529],[75.45562,36.71971],[75.40481,36.95382],[75.13839,37.02622],[74.56453,37.03023],[74.53739,36.96224],[74.43389,37.00977],[74.04856,36.82648],[73.82685,36.91421],[72.6323,36.84601],[72.18135,36.71838],[71.80267,36.49924],[71.60491,36.39429],[71.19505,36.04134],[71.37969,35.95865],[71.55273,35.71483],[71.49917,35.6267],[71.65435,35.4479],[71.54294,35.31037],[71.5541,35.28776],[71.67495,35.21262],[71.52938,35.09023],[71.55273,35.02615],[71.49917,35.00478],[71.50329,34.97328],[71.29472,34.87728],[71.28356,34.80882],[71.08718,34.69034],[71.11602,34.63047],[71.0089,34.54568],[71.02401,34.44835],[71.17662,34.36769],[71.12815,34.26619],[71.13078,34.16503],[71.09453,34.13524],[71.09307,34.11961],[71.06933,34.10564],[71.07345,34.06242],[70.88119,33.97933],[70.54336,33.9463],[69.90203,34.04194],[69.87307,33.9689],[69.85671,33.93719],[70.00503,33.73528],[70.14236,33.71701],[70.14785,33.6553],[70.20141,33.64387],[70.17062,33.53535],[70.32775,33.34496],[70.13686,33.21064],[70.07369,33.22557],[70.02563,33.14282],[69.85259,33.09451],[69.79766,33.13247],[69.71526,33.09911],[69.57656,33.09911],[69.49004,33.01509],[69.49854,32.88843],[69.5436,32.8768],[69.47082,32.85834],[69.38018,32.76601],[69.43649,32.7302],[69.44747,32.6678],[69.38155,32.56601],[69.2868,32.53938],[69.23599,32.45946],[69.27932,32.29119],[69.27032,32.14141],[69.3225,31.93186],[69.20577,31.85957],[69.11514,31.70782],[69.00939,31.62249],[68.95995,31.64822],[68.91078,31.59687],[68.79997,31.61665],[68.6956,31.75687],[68.57475,31.83158],[68.44222,31.76446],[68.27605,31.75863],[68.25614,31.80357],[68.1655,31.82691],[68.00071,31.6564],[67.86887,31.63536],[67.72056,31.52304],[67.58323,31.52772],[67.62374,31.40473],[67.7748,31.4188],[67.78854,31.33203],[67.29964,31.19586],[67.03323,31.24519],[67.04147,31.31561],[66.83273,31.26867],[66.72561,31.20526],[66.68166,31.07597],[66.58175,30.97532],[66.42645,30.95309],[66.39194,30.9408],[66.28413,30.57001],[66.34869,30.404],[66.23609,30.06321],[66.36042,29.9583],[66.24175,29.85181],[65.04005,29.53957],[64.62116,29.58903],[64.19796,29.50407],[64.12966,29.39157],[63.5876,29.50456],[62.47751,29.40782],[60.87231,29.86514],[61.31508,29.38903],[61.53765,29.00507],[61.65978,28.77937],[61.93581,28.55284],[62.40259,28.42703],[62.59499,28.24842],[62.79412,28.28108],[62.7638,28.02992],[62.84905,27.47627],[62.79684,27.34381],[62.80604,27.22412],[63.19649,27.25674],[63.32283,27.14437],[63.25005,27.08692],[63.25005,26.84212],[63.18688,26.83844],[63.1889,26.65072],[62.77352,26.64099],[62.31484,26.528],[62.21304,26.26601],[62.05117,26.31647],[61.89391,26.26251],[61.83831,26.07249],[61.83968,25.7538],[61.683,25.66638],[61.6433,25.27541],[61.57592,25.0492],[61.5251,24.57287],[68.11329,23.53945],[68.20763,23.85849],[68.39339,23.96838],[68.74643,23.97027],[68.7416,24.31904],[68.90914,24.33156],[68.97781,24.26021],[69.07806,24.29777],[69.19341,24.25646],[69.29778,24.28712],[69.59579,24.29777],[69.73335,24.17007],[70.03428,24.172],[70.11712,24.30915],[70.5667,24.43787],[70.57906,24.27774],[70.71502,24.23517],[70.88393,24.27398],[70.85784,24.30903],[70.94985,24.3791],[71.04461,24.34657],[71.12838,24.42662],[71.00341,24.46038],[70.97594,24.60904],[71.09405,24.69017],[70.94002,24.92843],[70.89148,25.15064],[70.66695,25.39314],[70.67382,25.68186],[70.60378,25.71898],[70.53649,25.68928],[70.37444,25.67443],[70.2687,25.71156],[70.0985,25.93238],[70.08193,26.08094],[70.17532,26.24118],[70.17532,26.55362],[70.05584,26.60398],[69.88555,26.56836],[69.50904,26.74892],[69.58519,27.18109],[70.03136,27.56627],[70.12502,27.8057],[70.37307,28.01208],[70.60927,28.02178],[70.79054,27.68423],[71.89921,27.96035],[71.9244,28.11555],[72.20329,28.3869],[72.29495,28.66367],[72.40402,28.78283],[72.94272,29.02487],[73.01337,29.16422],[73.05886,29.1878],[73.28094,29.56646],[73.3962,29.94707],[73.58665,30.01848],[73.80299,30.06969],[73.97225,30.19829],[73.95736,30.28466],[73.88993,30.36305],[74.5616,31.04153],[74.67971,31.05479],[74.6852,31.12771],[74.60006,31.13711],[74.60281,31.10419],[74.56023,31.08303],[74.51629,31.13829],[74.53223,31.30321],[74.59773,31.4136],[74.64713,31.45605],[74.59319,31.50197],[74.61517,31.55698],[74.57498,31.60382],[74.47771,31.72227],[74.58907,31.87824],[74.79919,31.95983],[74.86236,32.04485],[74.9269,32.0658],[75.00793,32.03786],[75.25649,32.10187],[75.38046,32.26836],[75.28259,32.36556],[75.03265,32.49538],[74.97634,32.45367],[74.84725,32.49075],[74.68362,32.49298],[74.67431,32.56676],[74.65251,32.56416],[74.64424,32.60985],[74.69542,32.66792],[74.65345,32.71225],[74.7113,32.84219],[74.64675,32.82604],[74.6289,32.75561],[74.45312,32.77755],[74.41467,32.90563],[74.31227,32.92795],[74.34875,32.97823],[74.31854,33.02891],[74.17571,33.07495],[74.15374,33.13477],[74.02144,33.18908],[74.01366,33.25199],[74.08782,33.26232],[74.17983,33.3679],[74.18121,33.4745],[74.10115,33.56392],[74.03576,33.56718],[73.97367,33.64061],[73.98968,33.66155],[73.96423,33.73071],[74.00891,33.75437],[74.05898,33.82089],[74.14001,33.83002],[74.26086,33.92237],[74.25262,34.01577],[74.21554,34.03853],[73.91341,34.01235],[73.88732,34.05105],[73.90677,34.10504],[73.98208,34.2522],[73.90517,34.35317],[73.8475,34.32935],[73.74862,34.34183],[73.74999,34.3781],[73.88732,34.48911],[73.89419,34.54568],[73.93951,34.57169],[73.93401,34.63386],[73.96423,34.68244],[74.12897,34.70073],[74.31239,34.79626],[74.58083,34.77386],[74.6663,34.703],[75.01479,34.64629],[75.38009,34.55021],[75.75438,34.51827],[76.04614,34.67566],[76.15463,34.6429],[76.47186,34.78965],[76.67648,34.76371],[76.74377,34.84039],[76.74514,34.92488],[76.87193,34.96906],[76.99251,34.93349],[77.11796,35.05419],[76.93465,35.39866],[76.85088,35.39754],[76.75475,35.52617],[76.77323,35.66062],[76.50961,35.8908],[76.33453,35.84296],[76.14913,35.82848],[76.15325,35.9264],[75.93028,36.13136],[76.00906,36.17511],[76.0324,36.41198],[75.92391,36.56986],[75.72737,36.7529]]]]}},{type:"Feature",properties:{iso1A2:"PL",iso1A3:"POL",iso1N3:"616",wikidata:"Q36",nameEn:"Poland",groups:["EU","151","150"],callingCodes:["48"]},geometry:{type:"MultiPolygon",coordinates:[[[[18.57853,55.25302],[14.20811,54.12784],[14.22634,53.9291],[14.20647,53.91671],[14.18544,53.91258],[14.20823,53.90776],[14.21323,53.8664],[14.27249,53.74464],[14.26782,53.69866],[14.2836,53.67721],[14.27133,53.66613],[14.28477,53.65955],[14.2853,53.63392],[14.31904,53.61581],[14.30416,53.55499],[14.3273,53.50587],[14.35209,53.49506],[14.4215,53.27724],[14.44133,53.27427],[14.45125,53.26241],[14.40662,53.21098],[14.37853,53.20405],[14.36696,53.16444],[14.38679,53.13669],[14.35044,53.05829],[14.25954,53.00264],[14.14056,52.95786],[14.15873,52.87715],[14.12256,52.84311],[14.13806,52.82392],[14.22071,52.81175],[14.61073,52.59847],[14.6289,52.57136],[14.60081,52.53116],[14.63056,52.48993],[14.54423,52.42568],[14.55228,52.35264],[14.56378,52.33838],[14.58149,52.28007],[14.70139,52.25038],[14.71319,52.22144],[14.68344,52.19612],[14.70616,52.16927],[14.67683,52.13936],[14.6917,52.10283],[14.72971,52.09167],[14.76026,52.06624],[14.71339,52.00337],[14.70488,51.97679],[14.7139,51.95643],[14.71836,51.95606],[14.72163,51.95188],[14.7177,51.94048],[14.70601,51.92944],[14.6933,51.9044],[14.6588,51.88359],[14.59089,51.83302],[14.60493,51.80473],[14.64625,51.79472],[14.66386,51.73282],[14.69065,51.70842],[14.75392,51.67445],[14.75759,51.62318],[14.7727,51.61263],[14.71125,51.56209],[14.73047,51.54606],[14.72652,51.53902],[14.73219,51.52922],[14.94749,51.47155],[14.9652,51.44793],[14.96899,51.38367],[14.98008,51.33449],[15.04288,51.28387],[15.01242,51.21285],[15.0047,51.16874],[14.99311,51.16249],[14.99414,51.15813],[15.00083,51.14974],[14.99646,51.14365],[14.99079,51.14284],[14.99689,51.12205],[14.98229,51.11354],[14.97938,51.07742],[14.95529,51.04552],[14.92942,50.99744],[14.89252,50.94999],[14.89681,50.9422],[14.81664,50.88148],[14.82803,50.86966],[14.99852,50.86817],[15.01088,50.97984],[14.96419,50.99108],[15.02433,51.0242],[15.03895,51.0123],[15.06218,51.02269],[15.10152,51.01095],[15.11937,50.99021],[15.16744,51.01959],[15.1743,50.9833],[15.2361,50.99886],[15.27043,50.97724],[15.2773,50.8907],[15.36656,50.83956],[15.3803,50.77187],[15.43798,50.80833],[15.73186,50.73885],[15.81683,50.75666],[15.87331,50.67188],[15.97219,50.69799],[16.0175,50.63009],[15.98317,50.61528],[16.02437,50.60046],[16.10265,50.66405],[16.20839,50.63096],[16.23174,50.67101],[16.33611,50.66579],[16.44597,50.58041],[16.34572,50.49575],[16.31413,50.50274],[16.19526,50.43291],[16.21585,50.40627],[16.22821,50.41054],[16.28118,50.36891],[16.30289,50.38292],[16.36495,50.37679],[16.3622,50.34875],[16.39379,50.3207],[16.42674,50.32509],[16.56407,50.21009],[16.55446,50.16613],[16.63137,50.1142],[16.7014,50.09659],[16.8456,50.20834],[16.98018,50.24172],[17.00353,50.21449],[17.02825,50.23118],[16.99803,50.25753],[17.02138,50.27772],[16.99803,50.30316],[16.94448,50.31281],[16.90877,50.38642],[16.85933,50.41093],[16.89229,50.45117],[17.1224,50.39494],[17.14498,50.38117],[17.19579,50.38817],[17.19991,50.3654],[17.27681,50.32246],[17.34273,50.32947],[17.34548,50.2628],[17.3702,50.28123],[17.58889,50.27837],[17.67764,50.28977],[17.69292,50.32859],[17.74648,50.29966],[17.72176,50.25665],[17.76296,50.23382],[17.70528,50.18812],[17.59404,50.16437],[17.66683,50.10275],[17.6888,50.12037],[17.7506,50.07896],[17.77669,50.02253],[17.86886,49.97452],[18.00191,50.01723],[18.04585,50.01194],[18.04585,50.03311],[18.00396,50.04954],[18.03212,50.06574],[18.07898,50.04535],[18.10628,50.00223],[18.20241,49.99958],[18.21752,49.97309],[18.27107,49.96779],[18.27794,49.93863],[18.31914,49.91565],[18.33278,49.92415],[18.33562,49.94747],[18.41604,49.93498],[18.53423,49.89906],[18.54495,49.9079],[18.54299,49.92537],[18.57697,49.91565],[18.57045,49.87849],[18.60341,49.86256],[18.57183,49.83334],[18.61278,49.7618],[18.61368,49.75426],[18.62645,49.75002],[18.62943,49.74603],[18.62676,49.71983],[18.69817,49.70473],[18.72838,49.68163],[18.80479,49.6815],[18.84786,49.5446],[18.84521,49.51672],[18.94536,49.52143],[18.97283,49.49914],[18.9742,49.39557],[19.18019,49.41165],[19.25435,49.53391],[19.36009,49.53747],[19.37795,49.574],[19.45348,49.61583],[19.52626,49.57311],[19.53313,49.52856],[19.57845,49.46077],[19.64162,49.45184],[19.6375,49.40897],[19.72127,49.39288],[19.78581,49.41701],[19.82237,49.27806],[19.75286,49.20751],[19.86409,49.19316],[19.90529,49.23532],[19.98494,49.22904],[20.08238,49.1813],[20.13738,49.31685],[20.21977,49.35265],[20.31453,49.34817],[20.31728,49.39914],[20.39939,49.3896],[20.46422,49.41612],[20.5631,49.375],[20.61666,49.41791],[20.72274,49.41813],[20.77971,49.35383],[20.9229,49.29626],[20.98733,49.30774],[21.09799,49.37176],[21.041,49.41791],[21.12477,49.43666],[21.19756,49.4054],[21.27858,49.45988],[21.43376,49.41433],[21.62328,49.4447],[21.77983,49.35443],[21.82927,49.39467],[21.96385,49.3437],[22.04427,49.22136],[22.56155,49.08865],[22.89122,49.00725],[22.86336,49.10513],[22.72009,49.20288],[22.748,49.32759],[22.69444,49.49378],[22.64534,49.53094],[22.78304,49.65543],[22.80261,49.69098],[22.83179,49.69875],[22.99329,49.84249],[23.28221,50.0957],[23.67635,50.33385],[23.71382,50.38248],[23.79445,50.40481],[23.99563,50.41289],[24.03668,50.44507],[24.07048,50.5071],[24.0996,50.60752],[24.0595,50.71625],[23.95925,50.79271],[23.99254,50.83847],[24.0952,50.83262],[24.14524,50.86128],[24.04576,50.90196],[23.92217,51.00836],[23.90376,51.07697],[23.80678,51.18405],[23.63858,51.32182],[23.69905,51.40871],[23.62751,51.50512],[23.56236,51.53673],[23.57053,51.55938],[23.53198,51.74298],[23.62691,51.78208],[23.61523,51.92066],[23.68733,51.9906],[23.64066,52.07626],[23.61,52.11264],[23.54314,52.12148],[23.47859,52.18215],[23.20071,52.22848],[23.18196,52.28812],[23.34141,52.44845],[23.45112,52.53774],[23.58296,52.59868],[23.73615,52.6149],[23.93763,52.71332],[23.91805,52.94016],[23.94689,52.95919],[23.92184,53.02079],[23.87548,53.0831],[23.91393,53.16469],[23.85657,53.22923],[23.81995,53.24131],[23.62004,53.60942],[23.51284,53.95052],[23.48261,53.98855],[23.52702,54.04622],[23.49196,54.14764],[23.45223,54.17775],[23.42418,54.17911],[23.39525,54.21672],[23.3494,54.25155],[23.24656,54.25701],[23.15938,54.29894],[23.15526,54.31076],[23.13905,54.31567],[23.104,54.29794],[23.04323,54.31567],[23.05726,54.34565],[22.99649,54.35927],[23.00584,54.38514],[22.83756,54.40827],[22.79705,54.36264],[21.41123,54.32395],[20.63871,54.3706],[19.8038,54.44203],[19.64312,54.45423],[18.57853,55.25302]]]]}},{type:"Feature",properties:{iso1A2:"PM",iso1A3:"SPM",iso1N3:"666",wikidata:"Q34617",nameEn:"Saint Pierre and Miquelon",country:"FR",groups:["021","003","019"],callingCodes:["508"]},geometry:{type:"MultiPolygon",coordinates:[[[[-56.72993,46.65575],[-55.90758,46.6223],[-56.27503,47.39728],[-56.72993,46.65575]]]]}},{type:"Feature",properties:{iso1A2:"PN",iso1A3:"PCN",iso1N3:"612",wikidata:"Q35672",nameEn:"Pitcairn Islands",country:"GB",groups:["061","009"],driveSide:"left",callingCodes:["64"]},geometry:{type:"MultiPolygon",coordinates:[[[[-133.59543,-28.4709],[-122.0366,-24.55017],[-133.61511,-21.93325],[-133.59543,-28.4709]]]]}},{type:"Feature",properties:{iso1A2:"PR",iso1A3:"PRI",iso1N3:"630",wikidata:"Q1183",nameEn:"Puerto Rico",country:"US",groups:["029","003","419","019"],roadSpeedUnit:"mph",callingCodes:["1 787","1 939"]},geometry:{type:"MultiPolygon",coordinates:[[[[-65.27974,17.56928],[-65.02435,18.73231],[-67.99519,18.97186],[-68.20301,17.83927],[-65.27974,17.56928]]]]}},{type:"Feature",properties:{iso1A2:"PS",iso1A3:"PSE",iso1N3:"275",wikidata:"Q23792",nameEn:"Palestine",country:"IL",groups:["145","142"],callingCodes:["970"]},geometry:{type:"MultiPolygon",coordinates:[[[[34.052,31.46619],[34.21853,31.32363],[34.23572,31.2966],[34.24012,31.29591],[34.26742,31.21998],[34.29417,31.24194],[34.36523,31.28963],[34.37381,31.30598],[34.36505,31.36404],[34.40077,31.40926],[34.48892,31.48365],[34.56797,31.54197],[34.48681,31.59711],[34.29262,31.70393],[34.052,31.46619]]],[[[35.47672,31.49578],[35.55941,31.76535],[35.52758,31.9131],[35.54375,31.96587],[35.52012,32.04076],[35.57111,32.21877],[35.55807,32.38674],[35.42078,32.41562],[35.41048,32.43706],[35.41598,32.45593],[35.42034,32.46009],[35.40224,32.50136],[35.35212,32.52047],[35.30685,32.51024],[35.29306,32.50947],[35.25049,32.52453],[35.2244,32.55289],[35.15937,32.50466],[35.10882,32.4757],[35.10024,32.47856],[35.09236,32.47614],[35.08564,32.46948],[35.07059,32.4585],[35.05423,32.41754],[35.05311,32.4024],[35.0421,32.38242],[35.05142,32.3667],[35.04243,32.35008],[35.01772,32.33863],[35.01119,32.28684],[35.02939,32.2671],[35.01841,32.23981],[34.98885,32.20758],[34.95703,32.19522],[34.96009,32.17503],[34.99039,32.14626],[34.98507,32.12606],[34.99437,32.10962],[34.9863,32.09551],[35.00261,32.027],[34.98682,31.96935],[35.00124,31.93264],[35.03489,31.92448],[35.03978,31.89276],[35.03489,31.85919],[34.99712,31.85569],[34.9724,31.83352],[35.01978,31.82944],[35.05617,31.85685],[35.07677,31.85627],[35.14174,31.81325],[35.18603,31.80901],[35.18169,31.82542],[35.19461,31.82687],[35.21469,31.81835],[35.216,31.83894],[35.21128,31.863],[35.20381,31.86716],[35.20673,31.88151],[35.20791,31.8821],[35.20945,31.8815],[35.21016,31.88237],[35.21276,31.88153],[35.2136,31.88241],[35.22014,31.88264],[35.22294,31.87889],[35.22567,31.86745],[35.22817,31.8638],[35.2249,31.85433],[35.2304,31.84222],[35.24816,31.8458],[35.25753,31.8387],[35.251,31.83085],[35.26404,31.82567],[35.25573,31.81362],[35.26058,31.79064],[35.25225,31.7678],[35.26319,31.74846],[35.25182,31.73945],[35.24981,31.72543],[35.2438,31.7201],[35.24315,31.71244],[35.23972,31.70896],[35.22392,31.71899],[35.21937,31.71578],[35.20538,31.72388],[35.18023,31.72067],[35.16478,31.73242],[35.15474,31.73352],[35.15119,31.73634],[35.13931,31.73012],[35.12933,31.7325],[35.11895,31.71454],[35.10782,31.71594],[35.08226,31.69107],[35.00879,31.65426],[34.95249,31.59813],[34.9415,31.55601],[34.94356,31.50743],[34.93258,31.47816],[34.89756,31.43891],[34.87833,31.39321],[34.88932,31.37093],[34.92571,31.34337],[35.02459,31.35979],[35.13033,31.3551],[35.22921,31.37445],[35.39675,31.49572],[35.47672,31.49578]]]]}},{type:"Feature",properties:{iso1A2:"PT",iso1A3:"PRT",iso1N3:"620",wikidata:"Q45",nameEn:"Portugal",groups:["EU","039","150"],callingCodes:["351"]},geometry:{type:"MultiPolygon",coordinates:[[[[-6.19128,41.57638],[-6.29863,41.66432],[-6.44204,41.68258],[-6.49907,41.65823],[-6.54633,41.68623],[-6.56426,41.74219],[-6.51374,41.8758],[-6.56752,41.88429],[-6.5447,41.94371],[-6.58544,41.96674],[-6.61967,41.94008],[-6.75004,41.94129],[-6.76959,41.98734],[-6.81196,41.99097],[-6.82174,41.94493],[-6.94396,41.94403],[-6.95537,41.96553],[-6.98144,41.9728],[-7.01078,41.94977],[-7.07596,41.94977],[-7.08574,41.97401],[-7.14115,41.98855],[-7.18549,41.97515],[-7.18677,41.88793],[-7.32366,41.8406],[-7.37092,41.85031],[-7.42864,41.80589],[-7.42854,41.83262],[-7.44759,41.84451],[-7.45566,41.86488],[-7.49803,41.87095],[-7.52737,41.83939],[-7.62188,41.83089],[-7.58603,41.87944],[-7.65774,41.88308],[-7.69848,41.90977],[-7.84188,41.88065],[-7.88055,41.84571],[-7.88751,41.92553],[-7.90707,41.92432],[-7.92336,41.8758],[-7.9804,41.87337],[-8.01136,41.83453],[-8.0961,41.81024],[-8.16455,41.81753],[-8.16944,41.87944],[-8.19551,41.87459],[-8.2185,41.91237],[-8.16232,41.9828],[-8.08796,42.01398],[-8.08847,42.05767],[-8.11729,42.08537],[-8.18178,42.06436],[-8.19406,42.12141],[-8.18947,42.13853],[-8.1986,42.15402],[-8.22406,42.1328],[-8.24681,42.13993],[-8.2732,42.12396],[-8.29809,42.106],[-8.32161,42.10218],[-8.33912,42.08358],[-8.36353,42.09065],[-8.38323,42.07683],[-8.40143,42.08052],[-8.42512,42.07199],[-8.44123,42.08218],[-8.48185,42.0811],[-8.52837,42.07658],[-8.5252,42.06264],[-8.54563,42.0537],[-8.58086,42.05147],[-8.59493,42.05708],[-8.63791,42.04691],[-8.64626,42.03668],[-8.65832,42.02972],[-8.6681,41.99703],[-8.69071,41.98862],[-8.7478,41.96282],[-8.74606,41.9469],[-8.75712,41.92833],[-8.81794,41.90375],[-8.87157,41.86488],[-9.14112,41.86623],[-36.43765,41.39418],[-15.92339,29.50503],[-7.37282,36.96896],[-7.39769,37.16868],[-7.41133,37.20314],[-7.41854,37.23813],[-7.43227,37.25152],[-7.43974,37.38913],[-7.46878,37.47127],[-7.51759,37.56119],[-7.41981,37.75729],[-7.33441,37.81193],[-7.27314,37.90145],[-7.24544,37.98884],[-7.12648,38.00296],[-7.10366,38.04404],[-7.05966,38.01966],[-7.00375,38.01914],[-6.93418,38.21454],[-7.09389,38.17227],[-7.15581,38.27597],[-7.32529,38.44336],[-7.265,38.61674],[-7.26174,38.72107],[-7.03848,38.87221],[-7.051,38.907],[-6.95211,39.0243],[-6.97004,39.07619],[-7.04011,39.11919],[-7.10692,39.10275],[-7.14929,39.11287],[-7.12811,39.17101],[-7.23566,39.20132],[-7.23403,39.27579],[-7.3149,39.34857],[-7.2927,39.45847],[-7.49477,39.58794],[-7.54121,39.66717],[-7.33507,39.64569],[-7.24707,39.66576],[-7.01613,39.66877],[-6.97492,39.81488],[-6.91463,39.86618],[-6.86737,40.01986],[-6.94233,40.10716],[-7.00589,40.12087],[-7.02544,40.18564],[-7.00426,40.23169],[-6.86085,40.26776],[-6.86085,40.2976],[-6.80218,40.33239],[-6.78426,40.36468],[-6.84618,40.42177],[-6.84944,40.46394],[-6.7973,40.51723],[-6.80218,40.55067],[-6.84292,40.56801],[-6.79567,40.65955],[-6.82826,40.74603],[-6.82337,40.84472],[-6.79892,40.84842],[-6.80707,40.88047],[-6.84292,40.89771],[-6.8527,40.93958],[-6.9357,41.02888],[-6.913,41.03922],[-6.88843,41.03027],[-6.84781,41.02692],[-6.80942,41.03629],[-6.79241,41.05397],[-6.75655,41.10187],[-6.77319,41.13049],[-6.69711,41.1858],[-6.68286,41.21641],[-6.65046,41.24725],[-6.55937,41.24417],[-6.38551,41.35274],[-6.38553,41.38655],[-6.3306,41.37677],[-6.26777,41.48796],[-6.19128,41.57638]]]]}},{type:"Feature",properties:{iso1A2:"PW",iso1A3:"PLW",iso1N3:"585",wikidata:"Q695",nameEn:"Palau",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["680"]},geometry:{type:"MultiPolygon",coordinates:[[[[128.97621,3.08804],[134.40878,1.79674],[136.27107,6.73747],[136.04605,12.45908],[128.97621,3.08804]]]]}},{type:"Feature",properties:{iso1A2:"PY",iso1A3:"PRY",iso1N3:"600",wikidata:"Q733",nameEn:"Paraguay",groups:["005","419","019"],callingCodes:["595"]},geometry:{type:"MultiPolygon",coordinates:[[[[-58.16225,-20.16193],[-58.23216,-19.80058],[-59.06965,-19.29148],[-60.00638,-19.2981],[-61.73723,-19.63958],[-61.93912,-20.10053],[-62.26883,-20.55311],[-62.2757,-21.06657],[-62.64455,-22.25091],[-62.51761,-22.37684],[-62.22768,-22.55807],[-61.9756,-23.0507],[-61.0782,-23.62932],[-60.99754,-23.80934],[-60.28163,-24.04436],[-60.03367,-24.00701],[-59.45482,-24.34787],[-59.33886,-24.49935],[-58.33055,-24.97099],[-58.25492,-24.92528],[-57.80821,-25.13863],[-57.57431,-25.47269],[-57.87176,-25.93604],[-58.1188,-26.16704],[-58.3198,-26.83443],[-58.65321,-27.14028],[-58.59549,-27.29973],[-58.04205,-27.2387],[-56.85337,-27.5165],[-56.18313,-27.29851],[-55.89195,-27.3467],[-55.74475,-27.44485],[-55.59094,-27.32444],[-55.62322,-27.1941],[-55.39611,-26.97679],[-55.25243,-26.93808],[-55.16948,-26.96068],[-55.06351,-26.80195],[-55.00584,-26.78754],[-54.80868,-26.55669],[-54.70732,-26.45099],[-54.69333,-26.37705],[-54.67359,-25.98607],[-54.60664,-25.9691],[-54.62063,-25.91213],[-54.59398,-25.59224],[-54.59509,-25.53696],[-54.60196,-25.48397],[-54.62033,-25.46026],[-54.4423,-25.13381],[-54.28207,-24.07305],[-54.32807,-24.01865],[-54.6238,-23.83078],[-55.02691,-23.97317],[-55.0518,-23.98666],[-55.12292,-23.99669],[-55.41784,-23.9657],[-55.44117,-23.9185],[-55.43585,-23.87157],[-55.5555,-23.28237],[-55.52288,-23.2595],[-55.5446,-23.22811],[-55.63849,-22.95122],[-55.62493,-22.62765],[-55.68742,-22.58407],[-55.6986,-22.56268],[-55.72366,-22.5519],[-55.741,-22.52018],[-55.74941,-22.46436],[-55.8331,-22.29008],[-56.23206,-22.25347],[-56.45893,-22.08072],[-56.5212,-22.11556],[-56.6508,-22.28387],[-57.98625,-22.09157],[-57.94642,-21.73799],[-57.88239,-21.6868],[-57.93492,-21.65505],[-57.84536,-20.93155],[-58.16225,-20.16193]]]]}},{type:"Feature",properties:{iso1A2:"QA",iso1A3:"QAT",iso1N3:"634",wikidata:"Q846",nameEn:"Qatar",groups:["145","142"],callingCodes:["974"]},geometry:{type:"MultiPolygon",coordinates:[[[[50.92992,24.54396],[51.09638,24.46907],[51.29972,24.50747],[51.39468,24.62785],[51.58834,24.66608],[51.83108,24.71675],[51.83682,26.70231],[50.93865,26.30758],[50.81266,25.88946],[50.86149,25.6965],[50.7801,25.595],[50.80824,25.54641],[50.57069,25.57887],[50.8133,24.74049],[50.92992,24.54396]]]]}},{type:"Feature",properties:{iso1A2:"RE",iso1A3:"REU",iso1N3:"638",wikidata:"Q17070",nameEn:"Réunion",country:"FR",groups:["EU","014","202","002"],callingCodes:["262"]},geometry:{type:"MultiPolygon",coordinates:[[[[53.37984,-21.23941],[56.73473,-21.9174],[56.62373,-20.2711],[53.37984,-21.23941]]]]}},{type:"Feature",properties:{iso1A2:"RO",iso1A3:"ROU",iso1N3:"642",wikidata:"Q218",nameEn:"Romania",groups:["EU","151","150"],callingCodes:["40"]},geometry:{type:"MultiPolygon",coordinates:[[[[27.15622,47.98538],[27.02985,48.09083],[27.04118,48.12522],[26.96119,48.13003],[26.98042,48.15752],[26.94265,48.1969],[26.87708,48.19919],[26.81161,48.25049],[26.62823,48.25804],[26.55202,48.22445],[26.33504,48.18418],[26.17711,47.99246],[26.05901,47.9897],[25.77723,47.93919],[25.63878,47.94924],[25.23778,47.89403],[25.11144,47.75203],[24.88896,47.7234],[24.81893,47.82031],[24.70632,47.84428],[24.61994,47.95062],[24.43578,47.97131],[24.34926,47.9244],[24.22566,47.90231],[24.11281,47.91487],[24.06466,47.95317],[24.02999,47.95087],[24.00801,47.968],[23.98553,47.96076],[23.96337,47.96672],[23.94192,47.94868],[23.89352,47.94512],[23.8602,47.9329],[23.80904,47.98142],[23.75188,47.99705],[23.66262,47.98786],[23.63894,48.00293],[23.5653,48.00499],[23.52803,48.01818],[23.4979,47.96858],[23.33577,48.0237],[23.27397,48.08245],[23.15999,48.12188],[23.1133,48.08061],[23.08858,48.00716],[23.0158,47.99338],[22.92241,48.02002],[22.94301,47.96672],[22.89849,47.95851],[22.77991,47.87211],[22.76617,47.8417],[22.67247,47.7871],[22.46559,47.76583],[22.41979,47.7391],[22.31816,47.76126],[22.00917,47.50492],[22.03389,47.42508],[22.01055,47.37767],[21.94463,47.38046],[21.78395,47.11104],[21.648,47.03902],[21.68645,46.99595],[21.59581,46.91628],[21.59307,46.86935],[21.52028,46.84118],[21.48935,46.7577],[21.5151,46.72147],[21.43926,46.65109],[21.33214,46.63035],[21.26929,46.4993],[21.28061,46.44941],[21.16872,46.30118],[21.06572,46.24897],[20.86797,46.28884],[20.74574,46.25467],[20.76085,46.21002],[20.63863,46.12728],[20.49718,46.18721],[20.45377,46.14405],[20.35573,46.16629],[20.28324,46.1438],[20.26068,46.12332],[20.35862,45.99356],[20.54818,45.89939],[20.65645,45.82801],[20.70069,45.7493],[20.77416,45.75601],[20.78446,45.78522],[20.82364,45.77738],[20.80361,45.65875],[20.76798,45.60969],[20.83321,45.53567],[20.77217,45.49788],[20.86026,45.47295],[20.87948,45.42743],[21.09894,45.30144],[21.17612,45.32566],[21.20392,45.2677],[21.29398,45.24148],[21.48278,45.19557],[21.51299,45.15345],[21.4505,45.04294],[21.35855,45.01941],[21.54938,44.9327],[21.56328,44.89502],[21.48202,44.87199],[21.44013,44.87613],[21.35643,44.86364],[21.38802,44.78133],[21.55007,44.77304],[21.60019,44.75208],[21.61942,44.67059],[21.67504,44.67107],[21.71692,44.65349],[21.7795,44.66165],[21.99364,44.63395],[22.08016,44.49844],[22.13234,44.47444],[22.18315,44.48179],[22.30844,44.6619],[22.45301,44.7194],[22.61917,44.61489],[22.69196,44.61587],[22.76749,44.54446],[22.70981,44.51852],[22.61368,44.55719],[22.56493,44.53419],[22.54021,44.47836],[22.45436,44.47258],[22.56012,44.30712],[22.68166,44.28206],[22.67173,44.21564],[23.04988,44.07694],[23.01674,44.01946],[22.87873,43.9844],[22.83753,43.88055],[22.85314,43.84452],[23.05288,43.79494],[23.26772,43.84843],[23.4507,43.84936],[23.61687,43.79289],[23.73978,43.80627],[24.18149,43.68218],[24.35364,43.70211],[24.50264,43.76314],[24.62281,43.74082],[24.73542,43.68523],[24.96682,43.72693],[25.10718,43.6831],[25.17144,43.70261],[25.39528,43.61866],[25.72792,43.69263],[25.94911,43.85745],[26.05584,43.90925],[26.10115,43.96908],[26.38764,44.04356],[26.62712,44.05698],[26.95141,44.13555],[27.26845,44.12602],[27.39757,44.0141],[27.60834,44.01206],[27.64542,44.04958],[27.73468,43.95326],[27.92008,44.00761],[27.99558,43.84193],[28.23293,43.76],[29.24336,43.70874],[30.04414,45.08461],[29.69272,45.19227],[29.65428,45.25629],[29.68175,45.26885],[29.59798,45.38857],[29.42632,45.44545],[29.24779,45.43388],[28.96077,45.33164],[28.94292,45.28045],[28.81383,45.3384],[28.78911,45.24179],[28.71358,45.22631],[28.5735,45.24759],[28.34554,45.32102],[28.28504,45.43907],[28.21139,45.46895],[28.18741,45.47358],[28.08927,45.6051],[28.16568,45.6421],[28.13111,45.92819],[28.08612,46.01105],[28.13684,46.18099],[28.10937,46.22852],[28.19864,46.31869],[28.18902,46.35283],[28.25769,46.43334],[28.22281,46.50481],[28.24808,46.64305],[28.12173,46.82283],[28.09095,46.97621],[27.81892,47.1381],[27.73172,47.29248],[27.68706,47.28962],[27.60263,47.32507],[27.55731,47.46637],[27.47942,47.48113],[27.3979,47.59473],[27.32202,47.64009],[27.25519,47.71366],[27.29069,47.73722],[27.1618,47.92391],[27.15622,47.98538]]]]}},{type:"Feature",properties:{iso1A2:"RS",iso1A3:"SRB",iso1N3:"688",wikidata:"Q403",nameEn:"Serbia",groups:["039","150"],callingCodes:["381"]},geometry:{type:"MultiPolygon",coordinates:[[[[19.66007,46.19005],[19.56113,46.16824],[19.52473,46.1171],[19.28826,45.99694],[19.14543,45.9998],[19.10388,46.04015],[19.0791,45.96458],[19.01284,45.96529],[18.99712,45.93537],[18.81394,45.91329],[18.85783,45.85493],[18.90305,45.71863],[18.96691,45.66731],[18.88776,45.57253],[18.94562,45.53712],[19.07471,45.53086],[19.08364,45.48804],[18.99918,45.49333],[18.97446,45.37528],[19.10774,45.29547],[19.28208,45.23813],[19.41941,45.23475],[19.43589,45.17137],[19.19144,45.17863],[19.14063,45.12972],[19.07952,45.14668],[19.1011,45.01191],[19.05205,44.97692],[19.15573,44.95409],[19.06853,44.89915],[19.02871,44.92541],[18.98957,44.90645],[19.01994,44.85493],[19.18183,44.92055],[19.36722,44.88164],[19.32543,44.74058],[19.26388,44.65412],[19.16699,44.52197],[19.13369,44.52521],[19.12278,44.50132],[19.14837,44.45253],[19.14681,44.41463],[19.11785,44.40313],[19.10749,44.39421],[19.10704,44.38249],[19.10365,44.37795],[19.10298,44.36924],[19.11865,44.36712],[19.1083,44.3558],[19.11547,44.34218],[19.13556,44.338],[19.13332,44.31492],[19.16741,44.28648],[19.18328,44.28383],[19.20508,44.2917],[19.23306,44.26097],[19.26945,44.26957],[19.32464,44.27185],[19.34773,44.23244],[19.3588,44.18353],[19.40927,44.16722],[19.43905,44.13088],[19.47338,44.15034],[19.48386,44.14332],[19.47321,44.1193],[19.51167,44.08158],[19.55999,44.06894],[19.57467,44.04716],[19.61991,44.05254],[19.61836,44.01464],[19.56498,43.99922],[19.52515,43.95573],[19.38439,43.96611],[19.24363,44.01502],[19.23465,43.98764],[19.3986,43.79668],[19.5176,43.71403],[19.50455,43.58385],[19.42696,43.57987],[19.41941,43.54056],[19.36653,43.60921],[19.33426,43.58833],[19.2553,43.5938],[19.24774,43.53061],[19.22807,43.5264],[19.22229,43.47926],[19.44315,43.38846],[19.48171,43.32644],[19.52962,43.31623],[19.54598,43.25158],[19.62661,43.2286],[19.64063,43.19027],[19.76918,43.16044],[19.79255,43.11951],[19.92576,43.08539],[19.96549,43.11098],[19.98887,43.0538],[20.04729,43.02732],[20.05431,42.99571],[20.12325,42.96237],[20.14896,42.99058],[20.16415,42.97177],[20.34528,42.90676],[20.35692,42.8335],[20.40594,42.84853],[20.43734,42.83157],[20.53484,42.8885],[20.48692,42.93208],[20.59929,43.01067],[20.64557,43.00826],[20.69515,43.09641],[20.59929,43.20492],[20.68688,43.21335],[20.73811,43.25068],[20.82145,43.26769],[20.88685,43.21697],[20.83727,43.17842],[20.96287,43.12416],[21.00749,43.13984],[21.05378,43.10707],[21.08952,43.13471],[21.14465,43.11089],[21.16734,42.99694],[21.2041,43.02277],[21.23877,43.00848],[21.23534,42.95523],[21.2719,42.8994],[21.32974,42.90424],[21.36941,42.87397],[21.44047,42.87276],[21.39045,42.74888],[21.47498,42.74695],[21.59154,42.72643],[21.58755,42.70418],[21.6626,42.67813],[21.75025,42.70125],[21.79413,42.65923],[21.75672,42.62695],[21.7327,42.55041],[21.70522,42.54176],[21.7035,42.51899],[21.62556,42.45106],[21.64209,42.41081],[21.62887,42.37664],[21.59029,42.38042],[21.57021,42.3647],[21.53467,42.36809],[21.5264,42.33634],[21.56772,42.30946],[21.58992,42.25915],[21.70111,42.23789],[21.77176,42.2648],[21.84654,42.3247],[21.91595,42.30392],[21.94405,42.34669],[22.02908,42.29848],[22.16384,42.32103],[22.29605,42.37477],[22.29275,42.34913],[22.34773,42.31725],[22.45919,42.33822],[22.47498,42.3915],[22.51961,42.3991],[22.55669,42.50144],[22.43983,42.56851],[22.4997,42.74144],[22.43309,42.82057],[22.54302,42.87774],[22.74826,42.88701],[22.78397,42.98253],[22.89521,43.03625],[22.98104,43.11199],[23.00806,43.19279],[22.89727,43.22417],[22.82036,43.33665],[22.53397,43.47225],[22.47582,43.6558],[22.41043,43.69566],[22.35558,43.81281],[22.41449,44.00514],[22.61688,44.06534],[22.61711,44.16938],[22.67173,44.21564],[22.68166,44.28206],[22.56012,44.30712],[22.45436,44.47258],[22.54021,44.47836],[22.56493,44.53419],[22.61368,44.55719],[22.70981,44.51852],[22.76749,44.54446],[22.69196,44.61587],[22.61917,44.61489],[22.45301,44.7194],[22.30844,44.6619],[22.18315,44.48179],[22.13234,44.47444],[22.08016,44.49844],[21.99364,44.63395],[21.7795,44.66165],[21.71692,44.65349],[21.67504,44.67107],[21.61942,44.67059],[21.60019,44.75208],[21.55007,44.77304],[21.38802,44.78133],[21.35643,44.86364],[21.44013,44.87613],[21.48202,44.87199],[21.56328,44.89502],[21.54938,44.9327],[21.35855,45.01941],[21.4505,45.04294],[21.51299,45.15345],[21.48278,45.19557],[21.29398,45.24148],[21.20392,45.2677],[21.17612,45.32566],[21.09894,45.30144],[20.87948,45.42743],[20.86026,45.47295],[20.77217,45.49788],[20.83321,45.53567],[20.76798,45.60969],[20.80361,45.65875],[20.82364,45.77738],[20.78446,45.78522],[20.77416,45.75601],[20.70069,45.7493],[20.65645,45.82801],[20.54818,45.89939],[20.35862,45.99356],[20.26068,46.12332],[20.09713,46.17315],[20.03533,46.14509],[20.01816,46.17696],[19.93508,46.17553],[19.81491,46.1313],[19.66007,46.19005]]]]}},{type:"Feature",properties:{iso1A2:"RU",iso1A3:"RUS",iso1N3:"643",wikidata:"Q159",nameEn:"Russia",groups:["151","150"],callingCodes:["7"]},geometry:{type:"MultiPolygon",coordinates:[[[[-179.99933,64.74703],[-172.76104,63.77445],[-169.03888,65.48473],[-168.95635,65.98512],[-168.25765,71.99091],[-179.9843,71.90735],[-179.99933,64.74703]]],[[[39.81147,43.06294],[40.0078,43.38551],[40.00853,43.40578],[40.01552,43.42025],[40.01007,43.42411],[40.03312,43.44262],[40.04445,43.47776],[40.10657,43.57344],[40.65957,43.56212],[41.64935,43.22331],[42.40563,43.23226],[42.66667,43.13917],[42.75889,43.19651],[43.03322,43.08883],[43.0419,43.02413],[43.81453,42.74297],[43.73119,42.62043],[43.95517,42.55396],[44.54202,42.75699],[44.70002,42.74679],[44.80941,42.61277],[44.88754,42.74934],[45.15318,42.70598],[45.36501,42.55268],[45.78692,42.48358],[45.61676,42.20768],[46.42738,41.91323],[46.5332,41.87389],[46.58924,41.80547],[46.75269,41.8623],[46.8134,41.76252],[47.00955,41.63583],[46.99554,41.59743],[47.03757,41.55434],[47.10762,41.59044],[47.34579,41.27884],[47.49004,41.26366],[47.54504,41.20275],[47.62288,41.22969],[47.75831,41.19455],[47.87973,41.21798],[48.07587,41.49957],[48.22064,41.51472],[48.2878,41.56221],[48.40277,41.60441],[48.42301,41.65444],[48.55078,41.77917],[48.5867,41.84306],[48.80971,41.95365],[49.2134,44.84989],[49.88945,46.04554],[49.32259,46.26944],[49.16518,46.38542],[48.54988,46.56267],[48.51142,46.69268],[49.01136,46.72716],[48.52326,47.4102],[48.45173,47.40818],[48.15348,47.74545],[47.64973,47.76559],[47.41689,47.83687],[47.38731,47.68176],[47.12107,47.83687],[47.11516,48.27188],[46.49011,48.43019],[46.78392,48.95352],[46.91104,48.99715],[47.01458,49.07085],[47.04416,49.17152],[46.98795,49.23531],[46.78398,49.34026],[46.9078,49.86707],[47.18319,49.93721],[47.34589,50.09308],[47.30448,50.30894],[47.58551,50.47867],[48.10044,50.09242],[48.24519,49.86099],[48.42564,49.82283],[48.68352,49.89546],[48.90782,50.02281],[48.57946,50.63278],[48.86936,50.61589],[49.12673,50.78639],[49.41959,50.85927],[49.39001,51.09396],[49.76866,51.11067],[49.97277,51.2405],[50.26859,51.28677],[50.59695,51.61859],[51.26254,51.68466],[51.301,51.48799],[51.77431,51.49536],[51.8246,51.67916],[52.36119,51.74161],[52.54329,51.48444],[53.46165,51.49445],[53.69299,51.23466],[54.12248,51.11542],[54.46331,50.85554],[54.41894,50.61214],[54.55797,50.52006],[54.71476,50.61214],[54.56685,51.01958],[54.72067,51.03261],[55.67774,50.54508],[56.11398,50.7471],[56.17906,50.93204],[57.17302,51.11253],[57.44221,50.88354],[57.74986,50.93017],[57.75578,51.13852],[58.3208,51.15151],[58.87974,50.70852],[59.48928,50.64216],[59.51886,50.49937],[59.81172,50.54451],[60.01288,50.8163],[60.17262,50.83312],[60.31914,50.67705],[60.81833,50.6629],[61.4431,50.80679],[61.56889,51.23679],[61.6813,51.25716],[61.55114,51.32746],[61.50677,51.40687],[60.95655,51.48615],[60.92401,51.61124],[60.5424,51.61675],[60.36787,51.66815],[60.50986,51.7964],[60.09867,51.87135],[59.99809,51.98263],[60.19925,51.99173],[60.48915,52.15175],[60.72581,52.15538],[60.78201,52.22067],[61.05417,52.35096],[60.98021,52.50068],[60.84709,52.52228],[60.84118,52.63912],[60.71693,52.66245],[60.71989,52.75923],[61.05842,52.92217],[61.23462,53.03227],[62.0422,52.96105],[62.12799,52.99133],[62.14574,53.09626],[61.19024,53.30536],[61.14291,53.41481],[61.29082,53.50992],[61.37957,53.45887],[61.57185,53.50112],[61.55706,53.57144],[60.90626,53.62937],[61.22574,53.80268],[61.14283,53.90063],[60.99796,53.93699],[61.26863,53.92797],[61.3706,54.08464],[61.47603,54.08048],[61.56941,53.95703],[61.65318,54.02445],[62.03913,53.94768],[62.00966,54.04134],[62.38535,54.03961],[62.45931,53.90737],[62.56876,53.94047],[62.58651,54.05871],[63.80604,54.27079],[63.91224,54.20013],[64.02715,54.22679],[63.97686,54.29763],[64.97216,54.4212],[65.11033,54.33028],[65.24663,54.35721],[65.20174,54.55216],[68.21308,54.98645],[68.26661,55.09226],[68.19206,55.18823],[68.90865,55.38148],[69.34224,55.36344],[69.74917,55.35545],[70.19179,55.1476],[70.76493,55.3027],[70.96009,55.10558],[71.08288,54.71253],[71.24185,54.64965],[71.08706,54.33376],[71.10379,54.13326],[71.96141,54.17736],[72.17477,54.36303],[72.43415,53.92685],[72.71026,54.1161],[73.37963,53.96132],[73.74778,54.07194],[73.68921,53.86522],[73.25412,53.61532],[73.39218,53.44623],[75.07405,53.80831],[75.43398,53.98652],[75.3668,54.07439],[76.91052,54.4677],[76.82266,54.1798],[76.44076,54.16017],[76.54243,53.99329],[77.90383,53.29807],[79.11255,52.01171],[80.08138,50.77658],[80.4127,50.95581],[80.44819,51.20855],[80.80318,51.28262],[81.16999,51.15662],[81.06091,50.94833],[81.41248,50.97524],[81.46581,50.77658],[81.94999,50.79307],[82.55443,50.75412],[83.14607,51.00796],[83.8442,50.87375],[84.29385,50.27257],[84.99198,50.06793],[85.24047,49.60239],[86.18709,49.50259],[86.63674,49.80136],[86.79056,49.74787],[86.61307,49.60239],[86.82606,49.51796],[87.03071,49.25142],[87.31465,49.23603],[87.28386,49.11626],[87.478,49.07403],[87.48983,49.13794],[87.81333,49.17354],[87.98977,49.18147],[88.15543,49.30314],[88.17223,49.46934],[88.42449,49.48821],[88.82499,49.44808],[89.70687,49.72535],[89.59711,49.90851],[91.86048,50.73734],[92.07173,50.69585],[92.44714,50.78762],[93.01109,50.79001],[92.99595,50.63183],[94.30823,50.57498],[94.39258,50.22193],[94.49477,50.17832],[94.6121,50.04239],[94.97166,50.04725],[95.02465,49.96941],[95.74757,49.97915],[95.80056,50.04239],[96.97388,49.88413],[97.24639,49.74737],[97.56811,49.84265],[97.56432,49.92801],[97.76871,49.99861],[97.85197,49.91339],[98.29481,50.33561],[98.31373,50.4996],[98.06393,50.61262],[97.9693,50.78044],[98.01472,50.86652],[97.83305,51.00248],[98.05257,51.46696],[98.22053,51.46579],[98.33222,51.71832],[98.74142,51.8637],[98.87768,52.14563],[99.27888,51.96876],[99.75578,51.90108],[99.89203,51.74903],[100.61116,51.73028],[101.39085,51.45753],[101.5044,51.50467],[102.14032,51.35566],[102.32194,50.67982],[102.71178,50.38873],[103.70343,50.13952],[105.32528,50.4648],[106.05562,50.40582],[106.07865,50.33474],[106.47156,50.31909],[106.49628,50.32436],[106.51122,50.34408],[106.58373,50.34044],[106.80326,50.30177],[107.00007,50.1977],[107.1174,50.04239],[107.36407,49.97612],[107.96116,49.93191],[107.95387,49.66659],[108.27937,49.53167],[108.53969,49.32325],[109.18017,49.34709],[109.51325,49.22859],[110.24373,49.16676],[110.39891,49.25083],[110.64493,49.1816],[113.02647,49.60772],[113.20216,49.83356],[114.325,50.28098],[114.9703,50.19254],[115.26068,49.97367],[115.73602,49.87688],[116.22402,50.04477],[116.62502,49.92919],[116.71193,49.83813],[117.07142,49.68482],[117.27597,49.62544],[117.48208,49.62324],[117.82343,49.52696],[118.61623,49.93809],[119.11003,50.00276],[119.27996,50.13348],[119.38598,50.35162],[119.13553,50.37412],[120.10963,51.671],[120.65907,51.93544],[120.77337,52.20805],[120.61346,52.32447],[120.71673,52.54099],[120.46454,52.63811],[120.04049,52.58773],[120.0451,52.7359],[120.85633,53.28499],[121.39213,53.31888],[122.35063,53.49565],[122.85966,53.47395],[123.26989,53.54843],[123.86158,53.49391],[124.46078,53.21881],[125.17522,53.20225],[125.6131,53.07229],[126.558,52.13738],[126.44606,51.98254],[126.68349,51.70607],[126.90369,51.3238],[126.93135,51.0841],[127.14586,50.91152],[127.28165,50.72075],[127.36335,50.58306],[127.28765,50.46585],[127.36009,50.43787],[127.37384,50.28393],[127.60515,50.23503],[127.49299,50.01251],[127.53516,49.84306],[127.83476,49.5748],[128.72896,49.58676],[129.11153,49.36813],[129.23232,49.40353],[129.35317,49.3481],[129.40398,49.44194],[129.50685,49.42398],[129.67598,49.29596],[129.85416,49.11067],[130.2355,48.86741],[130.43232,48.90844],[130.66946,48.88251],[130.52147,48.61745],[130.84462,48.30942],[130.65103,48.10052],[130.90915,47.90623],[130.95985,47.6957],[131.09871,47.6852],[131.2635,47.73325],[131.90448,47.68011],[132.57309,47.71741],[132.66989,47.96491],[134.49516,48.42884],[134.75328,48.36763],[134.67098,48.1564],[134.55508,47.98651],[134.7671,47.72051],[134.50898,47.4812],[134.20016,47.33458],[134.03538,46.75668],[133.84104,46.46681],[133.91496,46.4274],[133.88097,46.25066],[133.68047,46.14697],[133.72695,46.05576],[133.67569,45.9759],[133.60442,45.90053],[133.48457,45.86203],[133.41083,45.57723],[133.19419,45.51913],[133.09279,45.25693],[133.12293,45.1332],[132.96373,45.0212],[132.83978,45.05916],[131.99417,45.2567],[131.86903,45.33636],[131.76532,45.22609],[131.66852,45.2196],[131.68466,45.12374],[131.48415,44.99513],[130.95639,44.85154],[131.1108,44.70266],[131.30365,44.04262],[131.25484,44.03131],[131.23583,43.96085],[131.26176,43.94011],[131.21105,43.82383],[131.19492,43.53047],[131.29402,43.46695],[131.30324,43.39498],[131.19031,43.21385],[131.20414,43.13654],[131.10274,43.04734],[131.135,42.94114],[131.02668,42.91246],[131.02438,42.86518],[130.66524,42.84753],[130.44361,42.76205],[130.40213,42.70788],[130.56576,42.68925],[130.62107,42.58413],[130.55143,42.52158],[130.56835,42.43281],[130.60805,42.4317],[130.64181,42.41422],[130.66367,42.38024],[130.65022,42.32281],[131.95041,41.5445],[140.9182,45.92937],[145.82343,44.571],[145.23667,43.76813],[153.94307,38.42848],[180,62.52334],[180,71.53642],[155.31937,81.93282],[36.48095,82.16765],[32.07813,72.01005],[31.59909,70.16571],[30.84095,69.80584],[30.95011,69.54699],[30.52662,69.54699],[30.16363,69.65244],[29.97205,69.41623],[29.27631,69.2811],[29.26623,69.13794],[29.0444,69.0119],[28.91738,69.04774],[28.45957,68.91417],[28.78224,68.86696],[28.43941,68.53366],[28.62982,68.19816],[29.34179,68.06655],[29.66955,67.79872],[30.02041,67.67523],[29.91155,67.51507],[28.9839,66.94139],[29.91155,66.13863],[30.16363,65.66935],[29.97205,65.70256],[29.74013,65.64025],[29.84096,65.56945],[29.68972,65.31803],[29.61914,65.23791],[29.8813,65.22101],[29.84096,65.1109],[29.61914,65.05993],[29.68972,64.80789],[30.05271,64.79072],[30.12329,64.64862],[30.01238,64.57513],[30.06279,64.35782],[30.4762,64.25728],[30.55687,64.09036],[30.25437,63.83364],[29.98213,63.75795],[30.49637,63.46666],[31.23244,63.22239],[31.29294,63.09035],[31.58535,62.91642],[31.38369,62.66284],[31.10136,62.43042],[29.01829,61.17448],[28.82816,61.1233],[28.47974,60.93365],[27.77352,60.52722],[27.71177,60.3893],[27.44953,60.22766],[26.32936,60.00121],[26.90044,59.63819],[27.85643,59.58538],[28.04187,59.47017],[28.19061,59.39962],[28.21137,59.38058],[28.20537,59.36491],[28.19284,59.35791],[28.14215,59.28934],[28.00689,59.28351],[27.90911,59.24353],[27.87978,59.18097],[27.80482,59.1116],[27.74429,58.98351],[27.36366,58.78381],[27.55489,58.39525],[27.48541,58.22615],[27.62393,58.09462],[27.67282,57.92627],[27.81841,57.89244],[27.78526,57.83963],[27.56689,57.83356],[27.50171,57.78842],[27.52615,57.72843],[27.3746,57.66834],[27.40393,57.62125],[27.31919,57.57672],[27.34698,57.52242],[27.56832,57.53728],[27.52453,57.42826],[27.86101,57.29402],[27.66511,56.83921],[27.86101,56.88204],[28.04768,56.59004],[28.13526,56.57989],[28.10069,56.524],[28.19057,56.44637],[28.16599,56.37806],[28.23716,56.27588],[28.15217,56.16964],[28.30571,56.06035],[28.36888,56.05805],[28.37987,56.11399],[28.43068,56.09407],[28.5529,56.11705],[28.68337,56.10173],[28.63668,56.07262],[28.73418,55.97131],[29.08299,56.03427],[29.21717,55.98971],[29.44692,55.95978],[29.3604,55.75862],[29.51283,55.70294],[29.61446,55.77716],[29.80672,55.79569],[29.97975,55.87281],[30.12136,55.8358],[30.27776,55.86819],[30.30987,55.83592],[30.48257,55.81066],[30.51346,55.78982],[30.51037,55.76568],[30.63344,55.73079],[30.67464,55.64176],[30.72957,55.66268],[30.7845,55.58514],[30.86003,55.63169],[30.93419,55.6185],[30.95204,55.50667],[30.90123,55.46621],[30.93144,55.3914],[30.8257,55.3313],[30.81946,55.27931],[30.87944,55.28223],[30.97369,55.17134],[31.02071,55.06167],[31.00972,55.02783],[30.94243,55.03964],[30.9081,55.02232],[30.95754,54.98609],[30.93144,54.9585],[30.81759,54.94064],[30.8264,54.90062],[30.75165,54.80699],[30.95479,54.74346],[30.97127,54.71967],[31.0262,54.70698],[30.98226,54.68872],[30.99187,54.67046],[31.19339,54.66947],[31.21399,54.63113],[31.08543,54.50361],[31.22945,54.46585],[31.3177,54.34067],[31.30791,54.25315],[31.57002,54.14535],[31.89599,54.0837],[31.88744,54.03653],[31.85019,53.91801],[31.77028,53.80015],[31.89137,53.78099],[32.12621,53.81586],[32.36663,53.7166],[32.45717,53.74039],[32.50112,53.68594],[32.40499,53.6656],[32.47777,53.5548],[32.74968,53.45597],[32.73257,53.33494],[32.51725,53.28431],[32.40773,53.18856],[32.15368,53.07594],[31.82373,53.10042],[31.787,53.18033],[31.62496,53.22886],[31.56316,53.19432],[31.40523,53.21406],[31.36403,53.13504],[31.3915,53.09712],[31.33519,53.08805],[31.32283,53.04101],[31.24147,53.031],[31.35667,52.97854],[31.592,52.79011],[31.57277,52.71613],[31.50406,52.69707],[31.63869,52.55361],[31.56316,52.51518],[31.61397,52.48843],[31.62084,52.33849],[31.57971,52.32146],[31.70735,52.26711],[31.6895,52.1973],[31.77877,52.18636],[31.7822,52.11406],[31.81722,52.09955],[31.85018,52.11305],[31.96141,52.08015],[31.92159,52.05144],[32.08813,52.03319],[32.23331,52.08085],[32.2777,52.10266],[32.34044,52.1434],[32.33083,52.23685],[32.38988,52.24946],[32.3528,52.32842],[32.54781,52.32423],[32.69475,52.25535],[32.85405,52.27888],[32.89937,52.2461],[33.18913,52.3754],[33.51323,52.35779],[33.48027,52.31499],[33.55718,52.30324],[33.78789,52.37204],[34.05239,52.20132],[34.11199,52.14087],[34.09413,52.00835],[34.41136,51.82793],[34.42922,51.72852],[34.07765,51.67065],[34.17599,51.63253],[34.30562,51.5205],[34.22048,51.4187],[34.33446,51.363],[34.23009,51.26429],[34.31661,51.23936],[34.38802,51.2746],[34.6613,51.25053],[34.6874,51.18],[34.82472,51.17483],[34.97304,51.2342],[35.14058,51.23162],[35.12685,51.16191],[35.20375,51.04723],[35.31774,51.08434],[35.40837,51.04119],[35.32598,50.94524],[35.39307,50.92145],[35.41367,50.80227],[35.47704,50.77274],[35.48116,50.66405],[35.39464,50.64751],[35.47463,50.49247],[35.58003,50.45117],[35.61711,50.35707],[35.73659,50.35489],[35.80388,50.41356],[35.8926,50.43829],[36.06893,50.45205],[36.20763,50.3943],[36.30101,50.29088],[36.47817,50.31457],[36.58371,50.28563],[36.56655,50.2413],[36.64571,50.218],[36.69377,50.26982],[36.91762,50.34963],[37.08468,50.34935],[37.48204,50.46079],[37.47243,50.36277],[37.62486,50.29966],[37.62879,50.24481],[37.61113,50.21976],[37.75807,50.07896],[37.79515,50.08425],[37.90776,50.04194],[38.02999,49.94482],[38.02999,49.90592],[38.21675,49.98104],[38.18517,50.08161],[38.32524,50.08866],[38.35408,50.00664],[38.65688,49.97176],[38.68677,50.00904],[38.73311,49.90238],[38.90477,49.86787],[38.9391,49.79524],[39.1808,49.88911],[39.27968,49.75976],[39.44496,49.76067],[39.59142,49.73758],[39.65047,49.61761],[39.84548,49.56064],[40.13249,49.61672],[40.16683,49.56865],[40.03636,49.52321],[40.03087,49.45452],[40.1141,49.38798],[40.14912,49.37681],[40.18331,49.34996],[40.22176,49.25683],[40.01988,49.1761],[39.93437,49.05709],[39.6836,49.05121],[39.6683,48.99454],[39.71353,48.98959],[39.72649,48.9754],[39.74874,48.98675],[39.78368,48.91596],[39.98967,48.86901],[40.03636,48.91957],[40.08168,48.87443],[39.97182,48.79398],[39.79466,48.83739],[39.73104,48.7325],[39.71765,48.68673],[39.67226,48.59368],[39.79764,48.58668],[39.84548,48.57821],[39.86196,48.46633],[39.88794,48.44226],[39.94847,48.35055],[39.84136,48.33321],[39.84273,48.30947],[39.90041,48.3049],[39.91465,48.26743],[39.95248,48.29972],[39.9693,48.29904],[39.97325,48.31399],[39.99241,48.31768],[40.00752,48.22445],[39.94847,48.22811],[39.83724,48.06501],[39.88256,48.04482],[39.77544,48.04206],[39.82213,47.96396],[39.73935,47.82876],[38.87979,47.87719],[38.79628,47.81109],[38.76379,47.69346],[38.35062,47.61631],[38.28679,47.53552],[38.28954,47.39255],[38.22225,47.30788],[38.33074,47.30508],[38.32112,47.2585],[38.23049,47.2324],[38.22955,47.12069],[38.3384,46.98085],[38.12112,46.86078],[37.62608,46.82615],[35.23066,45.79231],[34.96015,45.75634],[34.79905,45.81009],[34.80153,45.90047],[34.75479,45.90705],[34.66679,45.97136],[34.60861,45.99347],[34.55889,45.99347],[34.52011,45.95097],[34.48729,45.94267],[34.44155,45.95995],[34.41221,46.00245],[34.33912,46.06114],[34.25111,46.0532],[34.181,46.06804],[34.12929,46.10494],[34.07311,46.11769],[34.05272,46.10838],[33.91549,46.15938],[33.85234,46.19863],[33.79715,46.20482],[33.74047,46.18555],[33.646,46.23028],[33.61517,46.22615],[33.63854,46.14147],[33.61467,46.13561],[33.57318,46.10317],[33.59087,46.06013],[33.54017,46.0123],[31.62627,45.50633],[32.99857,44.48323],[33.66142,43.9825],[39.81147,43.06294]]],[[[21.46766,55.21115],[21.38446,55.29348],[21.35465,55.28427],[21.26425,55.24456],[20.95181,55.27994],[20.60454,55.40986],[18.57853,55.25302],[19.64312,54.45423],[19.8038,54.44203],[20.63871,54.3706],[21.41123,54.32395],[22.79705,54.36264],[22.7253,54.41732],[22.70208,54.45312],[22.67788,54.532],[22.71293,54.56454],[22.68021,54.58486],[22.7522,54.63525],[22.74225,54.64339],[22.75467,54.6483],[22.73397,54.66604],[22.73631,54.72952],[22.87317,54.79492],[22.85083,54.88711],[22.76422,54.92521],[22.68723,54.9811],[22.65451,54.97037],[22.60075,55.01863],[22.58907,55.07085],[22.47688,55.04408],[22.31562,55.0655],[22.14267,55.05345],[22.11697,55.02131],[22.06087,55.02935],[22.02582,55.05078],[22.03984,55.07888],[21.99543,55.08691],[21.96505,55.07353],[21.85521,55.09493],[21.64954,55.1791],[21.55605,55.20311],[21.51095,55.18507],[21.46766,55.21115]]]]}},{type:"Feature",properties:{iso1A2:"RW",iso1A3:"RWA",iso1N3:"646",wikidata:"Q1037",nameEn:"Rwanda",groups:["014","202","002"],callingCodes:["250"]},geometry:{type:"MultiPolygon",coordinates:[[[[30.47194,-1.0555],[30.35212,-1.06896],[30.16369,-1.34303],[29.912,-1.48269],[29.82657,-1.31187],[29.59061,-1.39016],[29.53062,-1.40499],[29.45038,-1.5054],[29.36322,-1.50887],[29.24323,-1.66826],[29.24458,-1.69663],[29.11847,-1.90576],[29.17562,-2.12278],[29.105,-2.27043],[29.00051,-2.29001],[28.95642,-2.37321],[28.89601,-2.37321],[28.86826,-2.41888],[28.86846,-2.44866],[28.89132,-2.47557],[28.89342,-2.49017],[28.88846,-2.50493],[28.87497,-2.50887],[28.86209,-2.5231],[28.86193,-2.53185],[28.87943,-2.55165],[28.89288,-2.55848],[28.90226,-2.62385],[28.89793,-2.66111],[28.94346,-2.69124],[29.00357,-2.70596],[29.04081,-2.7416],[29.0562,-2.58632],[29.32234,-2.6483],[29.36805,-2.82933],[29.88237,-2.75105],[29.95911,-2.33348],[30.14034,-2.43626],[30.42933,-2.31064],[30.54501,-2.41404],[30.83915,-2.35795],[30.89303,-2.08223],[30.80802,-1.91477],[30.84079,-1.64652],[30.71974,-1.43244],[30.57123,-1.33264],[30.50889,-1.16412],[30.45116,-1.10641],[30.47194,-1.0555]]]]}},{type:"Feature",properties:{iso1A2:"SA",iso1A3:"SAU",iso1N3:"682",wikidata:"Q851",nameEn:"Saudi Arabia",groups:["145","142"],callingCodes:["966"]},geometry:{type:"MultiPolygon",coordinates:[[[[40.01521,32.05667],[39.29903,32.23259],[38.99233,31.99721],[36.99791,31.50081],[37.99354,30.49998],[37.66395,30.33245],[37.4971,29.99949],[36.75083,29.86903],[36.50005,29.49696],[36.07081,29.18469],[34.95987,29.35727],[34.88293,29.37455],[34.46254,27.99552],[34.51305,27.70027],[37.8565,22.00903],[39.63762,18.37348],[41.37609,16.19728],[42.15205,16.40211],[42.76801,16.40371],[42.94625,16.39721],[42.94351,16.49467],[42.97215,16.51093],[43.11601,16.53166],[43.15274,16.67248],[43.22066,16.65179],[43.21325,16.74416],[43.25857,16.75304],[43.26303,16.79479],[43.24801,16.80613],[43.22956,16.80613],[43.22012,16.83932],[43.18338,16.84852],[43.1398,16.90696],[43.19328,16.94703],[43.1813,16.98438],[43.18233,17.02673],[43.23967,17.03428],[43.17787,17.14717],[43.20156,17.25901],[43.32653,17.31179],[43.22533,17.38343],[43.29185,17.53224],[43.43005,17.56148],[43.70631,17.35762],[44.50126,17.47475],[46.31018,17.20464],[46.76494,17.29151],[47.00571,16.94765],[47.48245,17.10808],[47.58351,17.50366],[48.19996,18.20584],[49.04884,18.59899],[52.00311,19.00083],[54.99756,20.00083],[55.66469,21.99658],[55.2137,22.71065],[55.13599,22.63334],[52.56622,22.94341],[51.59617,24.12041],[51.58871,24.27256],[51.41644,24.39615],[51.58834,24.66608],[51.39468,24.62785],[51.29972,24.50747],[51.09638,24.46907],[50.92992,24.54396],[50.8133,24.74049],[50.57069,25.57887],[50.302,25.87592],[50.26923,26.08243],[50.38162,26.53976],[50.71771,26.73086],[50.37726,27.89227],[49.98877,27.87827],[49.00421,28.81495],[48.42991,28.53628],[47.70561,28.5221],[47.59863,28.66798],[47.58376,28.83382],[47.46202,29.0014],[46.5527,29.10283],[46.42415,29.05947],[44.72255,29.19736],[42.97796,30.48295],[42.97601,30.72204],[40.01521,32.05667]]]]}},{type:"Feature",properties:{iso1A2:"SB",iso1A3:"SLB",iso1N3:"090",wikidata:"Q685",nameEn:"Solomon Islands",groups:["054","009"],driveSide:"left",callingCodes:["677"]},geometry:{type:"MultiPolygon",coordinates:[[[[174,-12.72535],[160.43769,-4.17974],[156.03296,-6.55528],[156.03993,-6.65703],[155.92557,-6.84664],[155.69784,-6.92661],[155.60735,-6.92266],[154.74815,-7.33315],[160.04026,-13.08769],[174,-12.72535]]]]}},{type:"Feature",properties:{iso1A2:"SC",iso1A3:"SYC",iso1N3:"690",wikidata:"Q1042",nameEn:"Seychelles",groups:["014","202","002"],driveSide:"left",callingCodes:["248"]},geometry:{type:"MultiPolygon",coordinates:[[[[43.75112,-10.38913],[54.83239,-10.93575],[66.3222,5.65313],[43.75112,-10.38913]]]]}},{type:"Feature",properties:{iso1A2:"SD",iso1A3:"SDN",iso1N3:"729",wikidata:"Q1049",nameEn:"Sudan",groups:["015","002"],callingCodes:["249"]},geometry:{type:"MultiPolygon",coordinates:[[[[37.8565,22.00903],[34.0765,22.00501],[33.99686,21.76784],[33.57251,21.72406],[33.17563,22.00405],[24.99885,21.99535],[24.99794,19.99661],[23.99715,20.00038],[23.99539,19.49944],[23.99997,15.69575],[23.62785,15.7804],[23.38812,15.69649],[23.10792,15.71297],[22.93201,15.55107],[22.92579,15.47007],[22.99584,15.40105],[22.99584,15.22989],[22.66115,14.86308],[22.70474,14.69149],[22.38562,14.58907],[22.44944,14.24986],[22.55997,14.23024],[22.5553,14.11704],[22.22995,13.96754],[22.08674,13.77863],[22.29689,13.3731],[22.1599,13.19281],[22.02914,13.13976],[21.94819,13.05637],[21.81432,12.81362],[21.89371,12.68001],[21.98711,12.63292],[22.15679,12.66634],[22.22684,12.74682],[22.46345,12.61925],[22.38873,12.45514],[22.50548,12.16769],[22.48369,12.02766],[22.64092,12.07485],[22.54907,11.64372],[22.7997,11.40424],[22.93124,11.41645],[22.97249,11.21955],[22.87758,10.91915],[23.02221,10.69235],[23.3128,10.45214],[23.67164,9.86923],[23.69155,9.67566],[24.09319,9.66572],[24.12744,9.73784],[24.49389,9.79962],[24.84653,9.80643],[24.97739,9.9081],[25.05688,10.06776],[25.0918,10.33718],[25.78141,10.42599],[25.93163,10.38159],[25.93241,10.17941],[26.21338,9.91545],[26.35815,9.57946],[26.70685,9.48735],[27.14427,9.62858],[27.90704,9.61323],[28.99983,9.67155],[29.06988,9.74826],[29.53844,9.75133],[29.54,10.07949],[29.94629,10.29245],[30.00389,10.28633],[30.53005,9.95992],[30.82893,9.71451],[30.84605,9.7498],[31.28504,9.75287],[31.77539,10.28939],[31.99177,10.65065],[32.46967,11.04662],[32.39358,11.18207],[32.39578,11.70208],[32.10079,11.95203],[32.73921,11.95203],[32.73921,12.22757],[33.25876,12.22111],[33.13988,11.43248],[33.26977,10.83632],[33.24645,10.77913],[33.52294,10.64382],[33.66604,10.44254],[33.80913,10.32994],[33.90159,10.17179],[33.96984,10.15446],[33.99185,9.99623],[33.96323,9.80972],[33.9082,9.762],[33.87958,9.49937],[34.10229,9.50238],[34.08717,9.55243],[34.13186,9.7492],[34.20484,9.9033],[34.22718,10.02506],[34.32102,10.11599],[34.34783,10.23914],[34.2823,10.53508],[34.4372,10.781],[34.59062,10.89072],[34.77383,10.74588],[34.77532,10.69027],[34.86618,10.74588],[34.86916,10.78832],[34.97491,10.86147],[34.97789,10.91559],[34.93172,10.95946],[35.01215,11.19626],[34.95704,11.24448],[35.09556,11.56278],[35.05832,11.71158],[35.11492,11.85156],[35.24302,11.91132],[35.70476,12.67101],[36.01458,12.72478],[36.14268,12.70879],[36.16651,12.88019],[36.13374,12.92665],[36.24545,13.36759],[36.38993,13.56459],[36.48824,13.83954],[36.44653,13.95666],[36.54376,14.25597],[36.44337,15.14963],[36.54276,15.23478],[36.69761,15.75323],[36.76371,15.80831],[36.92193,16.23451],[36.99777,17.07172],[37.42694,17.04041],[37.50967,17.32199],[38.13362,17.53906],[38.37133,17.66269],[38.45916,17.87167],[38.57727,17.98125],[39.63762,18.37348],[37.8565,22.00903]]]]}},{type:"Feature",properties:{iso1A2:"SE",iso1A3:"SWE",iso1N3:"752",wikidata:"Q34",nameEn:"Sweden",groups:["EU","154","150"],callingCodes:["46"]},geometry:{type:"MultiPolygon",coordinates:[[[[24.15791,65.85385],[23.90497,66.15802],[23.71339,66.21299],[23.64982,66.30603],[23.67591,66.3862],[23.63776,66.43568],[23.85959,66.56434],[23.89488,66.772],[23.98059,66.79585],[23.98563,66.84149],[23.56214,67.17038],[23.58735,67.20752],[23.54701,67.25435],[23.75372,67.29914],[23.75372,67.43688],[23.39577,67.46974],[23.54701,67.59306],[23.45627,67.85297],[23.65793,67.9497],[23.40081,68.05545],[23.26469,68.15134],[23.15377,68.14759],[23.10336,68.26551],[22.73028,68.40881],[22.00429,68.50692],[21.03001,68.88969],[20.90649,68.89696],[20.85104,68.93142],[20.91658,68.96764],[20.78802,69.03087],[20.55258,69.06069],[20.0695,69.04469],[20.28444,68.93283],[20.33435,68.80174],[20.22027,68.67246],[19.95647,68.55546],[20.22027,68.48759],[19.93508,68.35911],[18.97255,68.52416],[18.63032,68.50849],[18.39503,68.58672],[18.1241,68.53721],[18.13836,68.20874],[17.90787,67.96537],[17.30416,68.11591],[16.7409,67.91037],[16.38441,67.52923],[16.12774,67.52106],[16.09922,67.4364],[16.39154,67.21653],[16.35589,67.06419],[15.37197,66.48217],[15.49318,66.28509],[15.05113,66.15572],[14.53778,66.12399],[14.50926,65.31786],[13.64276,64.58402],[14.11117,64.46674],[14.16051,64.18725],[13.98222,64.00953],[13.23411,64.09087],[12.74105,64.02171],[12.14928,63.59373],[12.19919,63.47935],[11.98529,63.27487],[12.19919,63.00104],[12.07085,62.6297],[12.29187,62.25699],[12.14746,61.7147],[12.40595,61.57226],[12.57707,61.56547],[12.86939,61.35427],[12.69115,61.06584],[12.2277,61.02442],[12.59133,60.50559],[12.52003,60.13846],[12.36317,59.99259],[12.15641,59.8926],[11.87121,59.86039],[11.92112,59.69531],[11.69297,59.59442],[11.8213,59.24985],[11.65732,58.90177],[11.45199,58.89604],[11.4601,58.99022],[11.34459,59.11672],[11.15367,59.07862],[11.08911,58.98745],[10.64958,58.89391],[10.40861,58.38489],[12.16597,56.60205],[12.07466,56.29488],[12.65312,56.04345],[12.6372,55.91371],[12.88472,55.63369],[12.60345,55.42675],[12.84405,55.13257],[14.28399,55.1553],[14.89259,55.5623],[15.79951,55.54655],[19.64795,57.06466],[19.84909,57.57876],[20.5104,59.15546],[19.08191,60.19152],[19.23413,60.61414],[20.15877,63.06556],[24.14112,65.39731],[24.15107,65.81427],[24.14798,65.83466],[24.15791,65.85385]]]]}},{type:"Feature",properties:{iso1A2:"SG",iso1A3:"SGP",iso1N3:"702",wikidata:"Q334",nameEn:"Singapore",groups:["035","142"],driveSide:"left",callingCodes:["65"]},geometry:{type:"MultiPolygon",coordinates:[[[[104.00131,1.42405],[103.93384,1.42926],[103.89565,1.42841],[103.86383,1.46288],[103.81181,1.47953],[103.76395,1.45183],[103.74161,1.4502],[103.7219,1.46108],[103.67468,1.43166],[103.62738,1.35255],[103.56591,1.19719],[103.66049,1.18825],[103.74084,1.12902],[104.03085,1.26954],[104.12282,1.27714],[104.08072,1.35998],[104.09162,1.39694],[104.08871,1.42015],[104.07348,1.43322],[104.04622,1.44691],[104.02277,1.4438],[104.00131,1.42405]]]]}},{type:"Feature",properties:{iso1A2:"SH",iso1A3:"SHN",iso1N3:"654",wikidata:"Q34497",nameEn:"Saint Helena, Ascension and Tristan da Cunha",country:"GB",groups:["011","202","002"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["290"]},geometry:{type:"MultiPolygon",coordinates:[[[[-14.82771,-8.70814],[-13.48367,-36.6746],[-11.55782,-36.60319],[-11.48092,-37.8367],[-13.41694,-37.88844],[-13.29655,-40.02846],[-9.34669,-41.00353],[-4.97086,-15.55882],[-13.33271,-8.07391],[-14.82771,-8.70814]]]]}},{type:"Feature",properties:{iso1A2:"SI",iso1A3:"SVN",iso1N3:"705",wikidata:"Q215",nameEn:"Slovenia",groups:["EU","039","150"],callingCodes:["386"]},geometry:{type:"MultiPolygon",coordinates:[[[[16.50139,46.56684],[16.39217,46.63673],[16.38594,46.6549],[16.41863,46.66238],[16.42641,46.69228],[16.37816,46.69975],[16.30966,46.7787],[16.31303,46.79838],[16.3408,46.80641],[16.34547,46.83836],[16.2941,46.87137],[16.2365,46.87775],[16.21892,46.86961],[16.15711,46.85434],[16.14365,46.8547],[16.10983,46.867],[16.05786,46.83927],[15.99054,46.82772],[15.99126,46.78199],[15.98432,46.74991],[15.99769,46.7266],[16.02808,46.71094],[16.04347,46.68694],[16.04036,46.6549],[15.99988,46.67947],[15.98512,46.68463],[15.94864,46.68769],[15.87691,46.7211],[15.8162,46.71897],[15.78518,46.70712],[15.76771,46.69863],[15.73823,46.70011],[15.72279,46.69548],[15.69523,46.69823],[15.67411,46.70735],[15.6543,46.70616],[15.6543,46.69228],[15.6365,46.6894],[15.63255,46.68069],[15.62317,46.67947],[15.59826,46.68908],[15.54533,46.66985],[15.55333,46.64988],[15.54431,46.6312],[15.46906,46.61321],[15.45514,46.63697],[15.41235,46.65556],[15.23711,46.63994],[15.14215,46.66131],[15.01451,46.641],[14.98024,46.6009],[14.96002,46.63459],[14.92283,46.60848],[14.87129,46.61],[14.86419,46.59411],[14.83549,46.56614],[14.81836,46.51046],[14.72185,46.49974],[14.66892,46.44936],[14.5942,46.43434],[14.56463,46.37208],[14.52176,46.42617],[14.45877,46.41717],[14.42608,46.44614],[14.314,46.43327],[14.28326,46.44315],[14.15989,46.43327],[14.12097,46.47724],[14.04002,46.49117],[14.00422,46.48474],[13.89837,46.52331],[13.7148,46.5222],[13.68684,46.43881],[13.59777,46.44137],[13.5763,46.42613],[13.5763,46.40915],[13.47019,46.3621],[13.43418,46.35992],[13.44808,46.33507],[13.37671,46.29668],[13.42218,46.20758],[13.47587,46.22725],[13.56114,46.2054],[13.56682,46.18703],[13.64451,46.18966],[13.66472,46.17392],[13.64053,46.13587],[13.57072,46.09022],[13.50104,46.05986],[13.49568,46.04839],[13.50998,46.04498],[13.49702,46.01832],[13.47474,46.00546],[13.50104,45.98078],[13.52963,45.96588],[13.56759,45.96991],[13.58903,45.99009],[13.62074,45.98388],[13.63458,45.98947],[13.64307,45.98326],[13.6329,45.94894],[13.63815,45.93607],[13.61931,45.91782],[13.60857,45.89907],[13.59565,45.89446],[13.58644,45.88173],[13.57563,45.8425],[13.58858,45.83503],[13.59784,45.8072],[13.66986,45.79955],[13.8235,45.7176],[13.83332,45.70855],[13.83422,45.68703],[13.87933,45.65207],[13.9191,45.6322],[13.8695,45.60835],[13.86771,45.59898],[13.84106,45.58185],[13.78445,45.5825],[13.74587,45.59811],[13.7198,45.59352],[13.6076,45.64761],[13.45644,45.59464],[13.56979,45.4895],[13.62902,45.45898],[13.67398,45.4436],[13.7785,45.46787],[13.81742,45.43729],[13.88124,45.42637],[13.90771,45.45149],[13.97309,45.45258],[13.99488,45.47551],[13.96063,45.50825],[14.00578,45.52352],[14.07116,45.48752],[14.20348,45.46896],[14.22371,45.50388],[14.24239,45.50607],[14.26611,45.48239],[14.27681,45.4902],[14.32487,45.47142],[14.36693,45.48642],[14.49769,45.54424],[14.5008,45.60852],[14.53816,45.6205],[14.57397,45.67165],[14.60977,45.66403],[14.59576,45.62812],[14.69694,45.57366],[14.68605,45.53006],[14.71718,45.53442],[14.80124,45.49515],[14.81992,45.45913],[14.90554,45.47769],[14.92266,45.52788],[15.02385,45.48533],[15.05187,45.49079],[15.16862,45.42309],[15.27758,45.46678],[15.33051,45.45258],[15.38188,45.48752],[15.30249,45.53224],[15.29837,45.5841],[15.27747,45.60504],[15.31027,45.6303],[15.34695,45.63382],[15.34214,45.64702],[15.38952,45.63682],[15.4057,45.64727],[15.34919,45.71623],[15.30872,45.69014],[15.25423,45.72275],[15.40836,45.79491],[15.47531,45.79802],[15.47325,45.8253],[15.52234,45.82195],[15.57952,45.84953],[15.64185,45.82915],[15.66662,45.84085],[15.70411,45.8465],[15.68232,45.86819],[15.68383,45.88867],[15.67967,45.90455],[15.70636,45.92116],[15.70327,46.00015],[15.71246,46.01196],[15.72977,46.04682],[15.62317,46.09103],[15.6083,46.11992],[15.59909,46.14761],[15.64904,46.19229],[15.6434,46.21396],[15.67395,46.22478],[15.75436,46.21969],[15.75479,46.20336],[15.78817,46.21719],[15.79284,46.25811],[15.97965,46.30652],[16.07616,46.3463],[16.07314,46.36458],[16.05065,46.3833],[16.05281,46.39141],[16.14859,46.40547],[16.18824,46.38282],[16.30233,46.37837],[16.30162,46.40437],[16.27329,46.41467],[16.27398,46.42875],[16.25124,46.48067],[16.23961,46.49653],[16.26759,46.50566],[16.26733,46.51505],[16.29793,46.5121],[16.37193,46.55008],[16.38771,46.53608],[16.44036,46.5171],[16.5007,46.49644],[16.52604,46.47831],[16.59527,46.47524],[16.52604,46.5051],[16.52885,46.53303],[16.50139,46.56684]]]]}},{type:"Feature",properties:{iso1A2:"SJ",iso1A3:"SJM",iso1N3:"744",wikidata:"Q842829",nameEn:"Svalbard and Jan Mayen",country:"NO",groups:["154","150"],callingCodes:["47 79"]},geometry:{type:"MultiPolygon",coordinates:[[[[-7.49892,77.24208],[32.07813,72.01005],[36.85549,84.09565],[-7.49892,77.24208]]],[[[-9.18243,72.23144],[-10.71459,70.09565],[-5.93364,70.76368],[-9.18243,72.23144]]]]}},{type:"Feature",properties:{iso1A2:"SK",iso1A3:"SVK",iso1N3:"703",wikidata:"Q214",nameEn:"Slovakia",groups:["EU","151","150"],callingCodes:["421"]},geometry:{type:"MultiPolygon",coordinates:[[[[19.82237,49.27806],[19.78581,49.41701],[19.72127,49.39288],[19.6375,49.40897],[19.64162,49.45184],[19.57845,49.46077],[19.53313,49.52856],[19.52626,49.57311],[19.45348,49.61583],[19.37795,49.574],[19.36009,49.53747],[19.25435,49.53391],[19.18019,49.41165],[18.9742,49.39557],[18.97283,49.49914],[18.94536,49.52143],[18.84521,49.51672],[18.74761,49.492],[18.67757,49.50895],[18.6144,49.49824],[18.57183,49.51162],[18.53063,49.49022],[18.54848,49.47059],[18.44686,49.39467],[18.4084,49.40003],[18.4139,49.36517],[18.36446,49.3267],[18.18456,49.28909],[18.15022,49.24518],[18.1104,49.08624],[18.06885,49.03157],[17.91814,49.01784],[17.87831,48.92679],[17.77944,48.92318],[17.73126,48.87885],[17.7094,48.86721],[17.5295,48.81117],[17.45671,48.85004],[17.3853,48.80936],[17.29054,48.85546],[17.19355,48.87602],[17.11202,48.82925],[17.00215,48.70887],[16.93955,48.60371],[16.94611,48.53614],[16.85204,48.44968],[16.8497,48.38321],[16.83588,48.3844],[16.83317,48.38138],[16.84243,48.35258],[16.90903,48.32519],[16.89461,48.31332],[16.97701,48.17385],[17.02919,48.13996],[17.05735,48.14179],[17.09168,48.09366],[17.07039,48.0317],[17.16001,48.00636],[17.23699,48.02094],[17.71215,47.7548],[18.02938,47.75665],[18.29305,47.73541],[18.56496,47.76588],[18.66521,47.76772],[18.74074,47.8157],[18.8506,47.82308],[18.76821,47.87469],[18.76134,47.97499],[18.82176,48.04206],[19.01952,48.07052],[19.23924,48.0595],[19.28182,48.08336],[19.47957,48.09437],[19.52489,48.19791],[19.63338,48.25006],[19.92452,48.1283],[20.24312,48.2784],[20.29943,48.26104],[20.5215,48.53336],[20.83248,48.5824],[21.11516,48.49546],[21.44063,48.58456],[21.6068,48.50365],[21.67134,48.3989],[21.72525,48.34628],[21.8279,48.33321],[21.83339,48.36242],[22.14689,48.4005],[22.16023,48.56548],[22.21379,48.6218],[22.34151,48.68893],[22.42934,48.92857],[22.48296,48.99172],[22.54338,49.01424],[22.56155,49.08865],[22.04427,49.22136],[21.96385,49.3437],[21.82927,49.39467],[21.77983,49.35443],[21.62328,49.4447],[21.43376,49.41433],[21.27858,49.45988],[21.19756,49.4054],[21.12477,49.43666],[21.041,49.41791],[21.09799,49.37176],[20.98733,49.30774],[20.9229,49.29626],[20.77971,49.35383],[20.72274,49.41813],[20.61666,49.41791],[20.5631,49.375],[20.46422,49.41612],[20.39939,49.3896],[20.31728,49.39914],[20.31453,49.34817],[20.21977,49.35265],[20.13738,49.31685],[20.08238,49.1813],[19.98494,49.22904],[19.90529,49.23532],[19.86409,49.19316],[19.75286,49.20751],[19.82237,49.27806]]]]}},{type:"Feature",properties:{iso1A2:"SL",iso1A3:"SLE",iso1N3:"694",wikidata:"Q1044",nameEn:"Sierra Leone",groups:["011","202","002"],callingCodes:["232"]},geometry:{type:"MultiPolygon",coordinates:[[[[-10.27575,8.48711],[-10.37257,8.48941],[-10.54891,8.31174],[-10.63934,8.35326],[-10.70565,8.29235],[-10.61422,8.5314],[-10.47707,8.67669],[-10.56197,8.81225],[-10.5783,9.06386],[-10.74484,9.07998],[-10.6534,9.29919],[-11.2118,10.00098],[-11.89624,9.99763],[-11.91023,9.93927],[-12.12634,9.87203],[-12.24262,9.92386],[-12.47254,9.86834],[-12.76788,9.3133],[-12.94095,9.26335],[-13.08953,9.0409],[-13.18586,9.0925],[-13.29911,9.04245],[-14.36218,8.64107],[-12.15048,6.15992],[-11.50429,6.92704],[-11.4027,6.97746],[-11.29417,7.21576],[-10.60422,7.7739],[-10.60492,8.04072],[-10.57523,8.04829],[-10.51554,8.1393],[-10.45023,8.15627],[-10.35227,8.15223],[-10.29839,8.21283],[-10.31635,8.28554],[-10.30084,8.30008],[-10.27575,8.48711]]]]}},{type:"Feature",properties:{iso1A2:"SM",iso1A3:"SMR",iso1N3:"674",wikidata:"Q238",nameEn:"San Marino",groups:["039","150"],callingCodes:["378"]},geometry:{type:"MultiPolygon",coordinates:[[[[12.45648,43.89369],[12.48771,43.89706],[12.49429,43.90973],[12.49247,43.91774],[12.49724,43.92248],[12.50269,43.92363],[12.50496,43.93017],[12.51553,43.94096],[12.51427,43.94897],[12.50655,43.95796],[12.50875,43.96198],[12.50622,43.97131],[12.51109,43.97201],[12.51064,43.98165],[12.5154,43.98508],[12.51463,43.99122],[12.50678,43.99113],[12.49406,43.98492],[12.47853,43.98052],[12.46205,43.97463],[12.44684,43.96597],[12.43662,43.95698],[12.42005,43.9578],[12.41414,43.95273],[12.40415,43.95485],[12.40506,43.94325],[12.41165,43.93769],[12.41551,43.92984],[12.40733,43.92379],[12.41233,43.90956],[12.40935,43.9024],[12.41641,43.89991],[12.44184,43.90498],[12.45648,43.89369]]]]}},{type:"Feature",properties:{iso1A2:"SN",iso1A3:"SEN",iso1N3:"686",wikidata:"Q1041",nameEn:"Senegal",groups:["011","202","002"],callingCodes:["221"]},geometry:{type:"MultiPolygon",coordinates:[[[[-14.32144,16.61495],[-15.00557,16.64997],[-15.6509,16.50315],[-16.27016,16.51565],[-16.4429,16.20605],[-16.44814,16.09753],[-16.48967,16.0496],[-16.50854,16.09032],[-17.15288,16.07139],[-18.35085,14.63444],[-17.43598,13.59273],[-15.47902,13.58758],[-15.36504,13.79313],[-14.93719,13.80173],[-14.34721,13.46578],[-13.8955,13.59126],[-13.79409,13.34472],[-14.36795,13.23033],[-15.14917,13.57989],[-15.26908,13.37768],[-15.80478,13.34832],[-15.80355,13.16729],[-16.69343,13.16791],[-16.74676,13.06025],[-17.43966,13.04579],[-17.4623,11.92379],[-16.70562,12.34803],[-16.38191,12.36449],[-16.20591,12.46157],[-15.67302,12.42974],[-15.17582,12.6847],[-13.70523,12.68013],[-13.05296,12.64003],[-13.06603,12.49342],[-12.87336,12.51892],[-12.35415,12.32758],[-11.91331,12.42008],[-11.46267,12.44559],[-11.37536,12.40788],[-11.39935,12.97808],[-11.63025,13.39174],[-11.83345,13.33333],[-12.06897,13.71049],[-11.93043,13.84505],[-12.23936,14.76324],[-13.11029,15.52116],[-13.43135,16.09022],[-13.80075,16.13961],[-14.32144,16.61495]]]]}},{type:"Feature",properties:{iso1A2:"SO",iso1A3:"SOM",iso1N3:"706",wikidata:"Q1045",nameEn:"Somalia",groups:["014","202","002"],callingCodes:["252"]},geometry:{type:"MultiPolygon",coordinates:[[[[48.95249,11.56816],[43.42425,11.70983],[42.95776,10.98533],[42.69452,10.62672],[42.87643,10.18441],[43.0937,9.90579],[43.23518,9.84605],[43.32613,9.59205],[44.19222,8.93028],[46.99339,7.9989],[47.92477,8.00111],[47.97917,8.00124],[44.98104,4.91821],[44.02436,4.9451],[43.40263,4.79289],[43.04177,4.57923],[42.97746,4.44032],[42.84526,4.28357],[42.55853,4.20518],[42.07619,4.17667],[41.89488,3.97375],[41.31368,3.14314],[40.98767,2.82959],[41.00099,-0.83068],[41.56,-1.59812],[41.56362,-1.66375],[41.75542,-1.85308],[49.16337,2.78611],[52.253,11.68582],[51.12877,12.56479],[48.95249,11.56816]]]]}},{type:"Feature",properties:{iso1A2:"SR",iso1A3:"SUR",iso1N3:"740",wikidata:"Q730",nameEn:"Suriname",groups:["005","419","019"],driveSide:"left",callingCodes:["597"]},geometry:{type:"MultiPolygon",coordinates:[[[[-54.26916,5.26909],[-54.01877,5.52789],[-54.01074,5.68785],[-53.7094,6.2264],[-56.84822,6.73257],[-57.31629,5.33714],[-57.22536,5.15605],[-57.37442,5.0208],[-57.8699,4.89394],[-58.0307,3.95513],[-57.35891,3.32121],[-56.70519,2.02964],[-56.55439,2.02003],[-56.47045,1.95135],[-55.99278,1.83137],[-55.89863,1.89861],[-55.92159,2.05236],[-56.13054,2.27723],[-55.96292,2.53188],[-55.71493,2.40342],[-55.01919,2.564],[-54.6084,2.32856],[-54.42864,2.42442],[-54.28534,2.67798],[-53.9849,3.58697],[-53.98914,3.627],[-54.05128,3.63557],[-54.19367,3.84387],[-54.38444,4.13222],[-54.4717,4.91964],[-54.26916,5.26909]]]]}},{type:"Feature",properties:{iso1A2:"SS",iso1A3:"SSD",iso1N3:"728",wikidata:"Q958",nameEn:"South Sudan",groups:["014","202","002"],callingCodes:["211"]},geometry:{type:"MultiPolygon",coordinates:[[[[34.10229,9.50238],[33.87958,9.49937],[33.9082,9.762],[33.96323,9.80972],[33.99185,9.99623],[33.96984,10.15446],[33.90159,10.17179],[33.80913,10.32994],[33.66604,10.44254],[33.52294,10.64382],[33.24645,10.77913],[33.26977,10.83632],[33.13988,11.43248],[33.25876,12.22111],[32.73921,12.22757],[32.73921,11.95203],[32.10079,11.95203],[32.39578,11.70208],[32.39358,11.18207],[32.46967,11.04662],[31.99177,10.65065],[31.77539,10.28939],[31.28504,9.75287],[30.84605,9.7498],[30.82893,9.71451],[30.53005,9.95992],[30.00389,10.28633],[29.94629,10.29245],[29.54,10.07949],[29.53844,9.75133],[29.06988,9.74826],[28.99983,9.67155],[27.90704,9.61323],[27.14427,9.62858],[26.70685,9.48735],[26.35815,9.57946],[26.21338,9.91545],[25.93241,10.17941],[25.93163,10.38159],[25.78141,10.42599],[25.0918,10.33718],[25.05688,10.06776],[24.97739,9.9081],[24.84653,9.80643],[24.49389,9.79962],[24.12744,9.73784],[24.09319,9.66572],[23.69155,9.67566],[23.62179,9.53823],[23.64981,9.44303],[23.64358,9.28637],[23.56263,9.19418],[23.4848,9.16959],[23.44744,8.99128],[23.59065,8.99743],[23.51905,8.71749],[24.25691,8.69288],[24.13238,8.36959],[24.35965,8.26177],[24.85156,8.16933],[24.98855,7.96588],[25.25319,7.8487],[25.29214,7.66675],[25.20649,7.61115],[25.20337,7.50312],[25.35281,7.42595],[25.37461,7.33024],[25.90076,7.09549],[26.38022,6.63493],[26.32729,6.36272],[26.58259,6.1987],[26.51721,6.09655],[27.22705,5.71254],[27.22705,5.62889],[27.28621,5.56382],[27.23017,5.37167],[27.26886,5.25876],[27.44012,5.07349],[27.56656,4.89375],[27.65462,4.89375],[27.76469,4.79284],[27.79551,4.59976],[28.20719,4.35614],[28.6651,4.42638],[28.8126,4.48784],[29.03054,4.48784],[29.22207,4.34297],[29.43341,4.50101],[29.49726,4.7007],[29.82087,4.56246],[29.79666,4.37809],[30.06964,4.13221],[30.1621,4.10586],[30.22374,3.93896],[30.27658,3.95653],[30.47691,3.83353],[30.55396,3.84451],[30.57378,3.74567],[30.56277,3.62703],[30.78512,3.67097],[30.80713,3.60506],[30.85997,3.5743],[30.85153,3.48867],[30.97601,3.693],[31.16666,3.79853],[31.29476,3.8015],[31.50478,3.67814],[31.50776,3.63652],[31.72075,3.74354],[31.81459,3.82083],[31.86821,3.78664],[31.96205,3.6499],[31.95907,3.57408],[32.05187,3.589],[32.08491,3.56287],[32.08866,3.53543],[32.19888,3.50867],[32.20782,3.6053],[32.41337,3.748],[32.72021,3.77327],[32.89746,3.81339],[33.02852,3.89296],[33.18356,3.77812],[33.51264,3.75068],[33.9873,4.23316],[34.47601,4.72162],[35.34151,5.02364],[35.30992,4.90402],[35.47843,4.91872],[35.42366,4.76969],[35.51424,4.61643],[35.9419,4.61933],[35.82118,4.77382],[35.81968,5.10757],[35.8576,5.33413],[35.50792,5.42431],[35.29938,5.34042],[35.31188,5.50106],[35.13058,5.62118],[35.12611,5.68937],[35.00546,5.89387],[34.96227,6.26415],[35.01738,6.46991],[34.87736,6.60161],[34.77459,6.5957],[34.65096,6.72589],[34.53776,6.74808],[34.53925,6.82794],[34.47669,6.91076],[34.35753,6.91963],[34.19369,7.04382],[34.19369,7.12807],[34.01495,7.25664],[34.03878,7.27437],[34.02984,7.36449],[33.87642,7.5491],[33.71407,7.65983],[33.44745,7.7543],[33.32531,7.71297],[33.24637,7.77939],[33.04944,7.78989],[33.0006,7.90333],[33.08401,8.05822],[33.18083,8.13047],[33.1853,8.29264],[33.19721,8.40317],[33.3119,8.45474],[33.54575,8.47094],[33.66938,8.44442],[33.71407,8.3678],[33.87195,8.41938],[33.89579,8.4842],[34.01346,8.50041],[34.14453,8.60204],[34.14304,9.04654],[34.10229,9.50238]]]]}},{type:"Feature",properties:{iso1A2:"ST",iso1A3:"STP",iso1N3:"678",wikidata:"Q1039",nameEn:"São Tomé and Principe",groups:["017","202","002"],callingCodes:["239"]},geometry:{type:"MultiPolygon",coordinates:[[[[5.9107,-0.09539],[6.69416,-0.53945],[8.0168,1.79377],[7.23334,2.23756],[5.9107,-0.09539]]]]}},{type:"Feature",properties:{iso1A2:"SV",iso1A3:"SLV",iso1N3:"222",wikidata:"Q792",nameEn:"El Salvador",groups:["013","003","419","019"],callingCodes:["503"]},geometry:{type:"MultiPolygon",coordinates:[[[[-89.34776,14.43013],[-89.39028,14.44561],[-89.57441,14.41637],[-89.58814,14.33165],[-89.50614,14.26084],[-89.52397,14.22628],[-89.61844,14.21937],[-89.70756,14.1537],[-89.75569,14.07073],[-89.73251,14.04133],[-89.76103,14.02923],[-89.81807,14.07073],[-89.88937,14.0396],[-90.10505,13.85104],[-90.11344,13.73679],[-90.55276,12.8866],[-88.11443,12.63306],[-87.7346,13.13228],[-87.55124,13.12523],[-87.69751,13.25228],[-87.73714,13.32715],[-87.80177,13.35689],[-87.84675,13.41078],[-87.83467,13.44655],[-87.77354,13.45767],[-87.73841,13.44169],[-87.72115,13.46083],[-87.71657,13.50577],[-87.78148,13.52906],[-87.73106,13.75443],[-87.68821,13.80829],[-87.7966,13.91353],[-88.00331,13.86948],[-88.07641,13.98447],[-88.23018,13.99915],[-88.25791,13.91108],[-88.48982,13.86458],[-88.49738,13.97224],[-88.70661,14.04317],[-88.73182,14.10919],[-88.815,14.11652],[-88.85785,14.17763],[-88.94608,14.20207],[-89.04187,14.33644],[-89.34776,14.43013]]]]}},{type:"Feature",properties:{iso1A2:"SX",iso1A3:"SXM",iso1N3:"534",wikidata:"Q26273",nameEn:"Sint Maarten",country:"NL",groups:["029","003","419","019"],callingCodes:["1 721"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.29212,17.90532],[-63.07669,17.79659],[-62.93924,18.02904],[-63.02323,18.05757],[-63.04039,18.05619],[-63.0579,18.06614],[-63.07759,18.04943],[-63.09686,18.04608],[-63.11096,18.05368],[-63.13584,18.0541],[-63.33064,17.9615],[-63.29212,17.90532]]]]}},{type:"Feature",properties:{iso1A2:"SY",iso1A3:"SYR",iso1N3:"760",wikidata:"Q858",nameEn:"Syria",groups:["145","142"],callingCodes:["963"]},geometry:{type:"MultiPolygon",coordinates:[[[[42.23683,37.2863],[42.21548,37.28026],[42.20454,37.28715],[42.22381,37.30238],[42.22257,37.31395],[42.2112,37.32491],[42.19301,37.31323],[42.18225,37.28569],[42.00894,37.17209],[41.515,37.08084],[41.21937,37.07665],[40.90856,37.13147],[40.69136,37.0996],[39.81589,36.75538],[39.21538,36.66834],[39.03217,36.70911],[38.74042,36.70629],[38.55908,36.84429],[38.38859,36.90064],[38.21064,36.91842],[37.81974,36.76055],[37.68048,36.75065],[37.49103,36.66904],[37.47253,36.63243],[37.21988,36.6736],[37.16177,36.66069],[37.10894,36.6704],[37.08279,36.63495],[37.02088,36.66422],[37.01647,36.69512],[37.04619,36.71101],[37.04399,36.73483],[36.99886,36.74012],[36.99557,36.75997],[36.66727,36.82901],[36.61581,36.74629],[36.62681,36.71189],[36.57398,36.65186],[36.58829,36.58295],[36.54206,36.49539],[36.6081,36.33772],[36.65653,36.33861],[36.68672,36.23677],[36.6125,36.22592],[36.50463,36.2419],[36.4617,36.20461],[36.39206,36.22088],[36.37474,36.01163],[36.33956,35.98687],[36.30099,36.00985],[36.28338,36.00273],[36.29769,35.96086],[36.27678,35.94839],[36.25366,35.96264],[36.19973,35.95195],[36.17441,35.92076],[36.1623,35.80925],[36.14029,35.81015],[36.13919,35.83692],[36.11827,35.85923],[35.99829,35.88242],[36.01844,35.92403],[36.00514,35.94113],[35.98499,35.94107],[35.931,35.92109],[35.51152,36.10954],[35.48515,34.70851],[35.97386,34.63322],[35.98718,34.64977],[36.29165,34.62991],[36.32399,34.69334],[36.35135,34.68516],[36.35384,34.65447],[36.42941,34.62505],[36.46003,34.6378],[36.45299,34.59438],[36.41429,34.61175],[36.39846,34.55672],[36.3369,34.52629],[36.34745,34.5002],[36.4442,34.50165],[36.46179,34.46541],[36.55853,34.41609],[36.53039,34.3798],[36.56556,34.31881],[36.60778,34.31009],[36.58667,34.27667],[36.59195,34.2316],[36.62537,34.20251],[36.5128,34.09916],[36.50576,34.05982],[36.41078,34.05253],[36.28589,33.91981],[36.38263,33.86579],[36.3967,33.83365],[36.14517,33.85118],[36.06778,33.82927],[35.9341,33.6596],[36.05723,33.57904],[35.94465,33.52774],[35.94816,33.47886],[35.88668,33.43183],[35.82577,33.40479],[35.81324,33.36354],[35.77477,33.33609],[35.813,33.3172],[35.77513,33.27342],[35.81295,33.24841],[35.81647,33.2028],[35.83846,33.19397],[35.84285,33.16673],[35.81911,33.1336],[35.81911,33.11077],[35.84802,33.1031],[35.87188,32.98028],[35.89298,32.9456],[35.87012,32.91976],[35.84021,32.8725],[35.83758,32.82817],[35.78745,32.77938],[35.75983,32.74803],[35.88405,32.71321],[35.93307,32.71966],[35.96633,32.66237],[36.02239,32.65911],[36.08074,32.51463],[36.20379,32.52751],[36.20875,32.49529],[36.23948,32.50108],[36.40959,32.37908],[36.83946,32.31293],[38.79171,33.37328],[40.64314,34.31604],[40.97676,34.39788],[41.12388,34.65742],[41.2345,34.80049],[41.21654,35.1508],[41.26569,35.42708],[41.38184,35.62502],[41.37027,35.84095],[41.2564,36.06012],[41.28864,36.35368],[41.40058,36.52502],[41.81736,36.58782],[42.36697,37.0627],[42.35724,37.10998],[42.32313,37.17814],[42.34735,37.22548],[42.2824,37.2798],[42.26039,37.27017],[42.23683,37.2863]]]]}},{type:"Feature",properties:{iso1A2:"SZ",iso1A3:"SWZ",iso1N3:"748",wikidata:"Q1050",nameEn:"Eswatini",aliases:["Swaziland"],groups:["018","202","002"],driveSide:"left",callingCodes:["268"]},geometry:{type:"MultiPolygon",coordinates:[[[[31.86881,-25.99973],[31.4175,-25.71886],[31.31237,-25.7431],[31.13073,-25.91558],[30.95819,-26.26303],[30.78927,-26.48271],[30.81101,-26.84722],[30.88826,-26.79622],[30.97757,-26.92706],[30.96088,-27.0245],[31.15027,-27.20151],[31.49834,-27.31549],[31.97592,-27.31675],[31.97463,-27.11057],[32.00893,-26.8096],[32.09664,-26.80721],[32.13315,-26.84345],[32.13409,-26.5317],[32.07352,-26.40185],[32.10435,-26.15656],[32.08599,-26.00978],[32.00916,-25.999],[31.974,-25.95387],[31.86881,-25.99973]]]]}},{type:"Feature",properties:{iso1A2:"TA",iso1A3:"TAA",wikidata:"Q220982",nameEn:"Tristan da Cunha",country:"GB",groups:["SH","011","202","002"],isoStatus:"excRes",driveSide:"left",roadSpeedUnit:"mph",callingCodes:["290 8","44 20"]},geometry:{type:"MultiPolygon",coordinates:[[[[-13.48367,-36.6746],[-13.41694,-37.88844],[-11.48092,-37.8367],[-11.55782,-36.60319],[-13.48367,-36.6746]]]]}},{type:"Feature",properties:{iso1A2:"TC",iso1A3:"TCA",iso1N3:"796",wikidata:"Q18221",nameEn:"Turks and Caicos Islands",country:"GB",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 649"]},geometry:{type:"MultiPolygon",coordinates:[[[[-72.41726,22.40371],[-72.72017,21.48055],[-71.46138,20.64433],[-70.63262,21.53631],[-72.41726,22.40371]]]]}},{type:"Feature",properties:{iso1A2:"TD",iso1A3:"TCD",iso1N3:"148",wikidata:"Q657",nameEn:"Chad",groups:["017","202","002"],callingCodes:["235"]},geometry:{type:"MultiPolygon",coordinates:[[[[23.99539,19.49944],[15.99566,23.49639],[14.99751,23.00539],[15.19692,21.99339],[15.20213,21.49365],[15.28332,21.44557],[15.62515,20.95395],[15.57248,20.92138],[15.55382,20.86507],[15.56004,20.79488],[15.59841,20.74039],[15.6721,20.70069],[15.99632,20.35364],[15.75098,19.93002],[15.6032,18.77402],[15.50373,16.89649],[14.37425,15.72591],[13.86301,15.04043],[13.78991,14.87519],[13.809,14.72915],[13.67878,14.64013],[13.68573,14.55276],[13.48259,14.46704],[13.47559,14.40881],[13.6302,13.71094],[14.08251,13.0797],[14.46881,13.08259],[14.56101,12.91036],[14.55058,12.78256],[14.83314,12.62963],[14.90827,12.3269],[14.89019,12.16593],[14.96952,12.0925],[15.00146,12.1223],[15.0349,12.10698],[15.05786,12.0608],[15.04808,11.8731],[15.11579,11.79313],[15.06595,11.71126],[15.13149,11.5537],[15.0585,11.40481],[15.10021,11.04101],[15.04957,11.02347],[15.09127,10.87431],[15.06737,10.80921],[15.15532,10.62846],[15.14936,10.53915],[15.23724,10.47764],[15.30874,10.31063],[15.50535,10.1098],[15.68761,9.99344],[15.41408,9.92876],[15.24618,9.99246],[15.14043,9.99246],[15.05999,9.94845],[14.95722,9.97926],[14.80082,9.93818],[14.4673,10.00264],[14.20411,10.00055],[14.1317,9.82413],[14.01793,9.73169],[13.97544,9.6365],[14.37094,9.2954],[14.35707,9.19611],[14.83566,8.80557],[15.09484,8.65982],[15.20426,8.50892],[15.50743,7.79302],[15.59272,7.7696],[15.56964,7.58936],[15.49743,7.52179],[15.73118,7.52006],[15.79942,7.44149],[16.40703,7.68809],[16.41583,7.77971],[16.58315,7.88657],[16.59415,7.76444],[16.658,7.75353],[16.6668,7.67281],[16.8143,7.53971],[17.67288,7.98905],[17.93926,7.95853],[18.02731,8.01085],[18.6085,8.05009],[18.64153,8.08714],[18.62612,8.14163],[18.67455,8.22226],[18.79783,8.25929],[19.11044,8.68172],[18.86388,8.87971],[19.06421,9.00367],[20.36748,9.11019],[20.82979,9.44696],[21.26348,9.97642],[21.34934,9.95907],[21.52766,10.2105],[21.63553,10.217],[21.71479,10.29932],[21.72139,10.64136],[22.45889,11.00246],[22.87758,10.91915],[22.97249,11.21955],[22.93124,11.41645],[22.7997,11.40424],[22.54907,11.64372],[22.64092,12.07485],[22.48369,12.02766],[22.50548,12.16769],[22.38873,12.45514],[22.46345,12.61925],[22.22684,12.74682],[22.15679,12.66634],[21.98711,12.63292],[21.89371,12.68001],[21.81432,12.81362],[21.94819,13.05637],[22.02914,13.13976],[22.1599,13.19281],[22.29689,13.3731],[22.08674,13.77863],[22.22995,13.96754],[22.5553,14.11704],[22.55997,14.23024],[22.44944,14.24986],[22.38562,14.58907],[22.70474,14.69149],[22.66115,14.86308],[22.99584,15.22989],[22.99584,15.40105],[22.92579,15.47007],[22.93201,15.55107],[23.10792,15.71297],[23.38812,15.69649],[23.62785,15.7804],[23.99997,15.69575],[23.99539,19.49944]]]]}},{type:"Feature",properties:{iso1A2:"TF",iso1A3:"ATF",iso1N3:"260",wikidata:"Q129003",nameEn:"French Southern and Antarctic Lands",country:"FR",groups:["014","202","002"]},geometry:{type:"MultiPolygon",coordinates:[[[[53.53458,-16.36909],[54.96649,-16.28353],[54.61476,-15.02273],[53.53458,-16.36909]]],[[[39.10324,-21.48967],[40.40841,-23.17181],[43.72277,-16.09877],[41.06663,-17.08802],[39.10324,-21.48967]]],[[[46.52682,-10.83678],[47.29063,-12.45583],[48.86266,-10.8109],[46.52682,-10.83678]]],[[[80.15867,-36.04977],[46.31615,-46.28749],[70.67507,-51.14192],[80.15867,-36.04977]]]]}},{type:"Feature",properties:{iso1A2:"TG",iso1A3:"TGO",iso1N3:"768",wikidata:"Q945",nameEn:"Togo",groups:["011","202","002"],callingCodes:["228"]},geometry:{type:"MultiPolygon",coordinates:[[[[0.50388,11.01011],[-0.13493,11.14075],[-0.14462,11.10811],[-0.05733,11.08628],[-0.0275,11.11202],[-0.00514,11.10763],[0.00342,11.08317],[0.02395,11.06229],[0.03355,10.9807],[-0.0063,10.96417],[-0.00908,10.91644],[-0.02685,10.8783],[-0.0228,10.81916],[-0.07183,10.76794],[-0.07327,10.71845],[-0.09141,10.7147],[-0.05945,10.63458],[0.12886,10.53149],[0.18846,10.4096],[0.29453,10.41546],[0.33028,10.30408],[0.39584,10.31112],[0.35293,10.09412],[0.41371,10.06361],[0.41252,10.02018],[0.36366,10.03309],[0.32075,9.72781],[0.34816,9.71607],[0.34816,9.66907],[0.32313,9.6491],[0.28261,9.69022],[0.26712,9.66437],[0.29334,9.59387],[0.36008,9.6256],[0.38153,9.58682],[0.23851,9.57389],[0.2409,9.52335],[0.30406,9.521],[0.31241,9.50337],[0.2254,9.47869],[0.25758,9.42696],[0.33148,9.44812],[0.36485,9.49749],[0.49118,9.48339],[0.56388,9.40697],[0.45424,9.04581],[0.52455,8.87746],[0.37319,8.75262],[0.47211,8.59945],[0.64731,8.48866],[0.73432,8.29529],[0.63897,8.25873],[0.5913,8.19622],[0.61156,8.18324],[0.6056,8.13959],[0.58891,8.12779],[0.62943,7.85751],[0.58295,7.62368],[0.51979,7.58706],[0.52455,7.45354],[0.57223,7.39326],[0.62943,7.41099],[0.65327,7.31643],[0.59606,7.01252],[0.52217,6.9723],[0.52098,6.94391],[0.56508,6.92971],[0.52853,6.82921],[0.57406,6.80348],[0.58176,6.76049],[0.6497,6.73682],[0.63659,6.63857],[0.74862,6.56517],[0.71048,6.53083],[0.89283,6.33779],[0.99652,6.33779],[1.03108,6.24064],[1.05969,6.22998],[1.09187,6.17074],[1.19966,6.17069],[1.19771,6.11522],[1.27574,5.93551],[1.67336,6.02702],[1.62913,6.24075],[1.79826,6.28221],[1.76906,6.43189],[1.58105,6.68619],[1.61812,6.74843],[1.55877,6.99737],[1.64249,6.99562],[1.61838,9.0527],[1.5649,9.16941],[1.41746,9.3226],[1.33675,9.54765],[1.36624,9.5951],[1.35507,9.99525],[0.77666,10.37665],[0.80358,10.71459],[0.8804,10.803],[0.91245,10.99597],[0.66104,10.99964],[0.4958,10.93269],[0.50521,10.98035],[0.48852,10.98561],[0.50388,11.01011]]]]}},{type:"Feature",properties:{iso1A2:"TH",iso1A3:"THA",iso1N3:"764",wikidata:"Q869",nameEn:"Thailand",groups:["035","142"],driveSide:"left",callingCodes:["66"]},geometry:{type:"MultiPolygon",coordinates:[[[[100.08404,20.36626],[99.95721,20.46301],[99.91616,20.44986],[99.90499,20.4487],[99.89692,20.44789],[99.89301,20.44311],[99.89168,20.44548],[99.88451,20.44596],[99.88211,20.44488],[99.86383,20.44371],[99.81096,20.33687],[99.68255,20.32077],[99.46008,20.39673],[99.46077,20.36198],[99.5569,20.20676],[99.52943,20.14811],[99.416,20.08614],[99.20328,20.12877],[99.0735,20.10298],[98.98679,19.7419],[98.83661,19.80931],[98.56065,19.67807],[98.51182,19.71303],[98.24884,19.67876],[98.13829,19.78541],[98.03314,19.80941],[98.04364,19.65755],[97.84715,19.55782],[97.88423,19.5041],[97.78769,19.39429],[97.84186,19.29526],[97.78606,19.26769],[97.84024,19.22217],[97.83479,19.09972],[97.73797,19.04261],[97.73654,18.9812],[97.66487,18.9371],[97.73836,18.88478],[97.76752,18.58097],[97.5258,18.4939],[97.36444,18.57138],[97.34522,18.54596],[97.50383,18.26844],[97.56219,18.33885],[97.64116,18.29778],[97.60841,18.23846],[97.73723,17.97912],[97.66794,17.88005],[97.76407,17.71595],[97.91829,17.54504],[98.11185,17.36829],[98.10439,17.33847],[98.34566,17.04822],[98.39441,17.06266],[98.52624,16.89979],[98.49603,16.8446],[98.53833,16.81934],[98.46994,16.73613],[98.50253,16.7139],[98.49713,16.69022],[98.51043,16.70107],[98.51579,16.69433],[98.51472,16.68521],[98.51833,16.676],[98.51113,16.64503],[98.5695,16.62826],[98.57912,16.55983],[98.63817,16.47424],[98.68074,16.27068],[98.84485,16.42354],[98.92656,16.36425],[98.8376,16.11706],[98.69585,16.13353],[98.57019,16.04578],[98.59853,15.87197],[98.541,15.65406],[98.58598,15.46821],[98.56027,15.33471],[98.4866,15.39154],[98.39351,15.34177],[98.41906,15.27103],[98.40522,15.25268],[98.30446,15.30667],[98.22,15.21327],[98.18821,15.13125],[98.24874,14.83013],[98.56762,14.37701],[98.97356,14.04868],[99.16695,13.72621],[99.20617,13.20575],[99.12225,13.19847],[99.10646,13.05804],[99.18748,12.9898],[99.18905,12.84799],[99.29254,12.68921],[99.409,12.60603],[99.47519,12.1353],[99.56445,12.14805],[99.53424,12.02317],[99.64891,11.82699],[99.64108,11.78948],[99.5672,11.62732],[99.47598,11.62434],[99.39485,11.3925],[99.31573,11.32081],[99.32756,11.28545],[99.06938,10.94857],[99.02337,10.97217],[98.99701,10.92962],[99.0069,10.85485],[98.86819,10.78336],[98.78511,10.68351],[98.77275,10.62548],[98.81944,10.52761],[98.7391,10.31488],[98.55174,9.92804],[98.52291,9.92389],[98.47298,9.95782],[98.33094,9.91973],[98.12555,9.44056],[97.63455,9.60854],[97.19814,8.18901],[99.31854,5.99868],[99.50117,6.44501],[99.91873,6.50233],[100.0756,6.4045],[100.12,6.42105],[100.19511,6.72559],[100.29651,6.68439],[100.30828,6.66462],[100.31618,6.66781],[100.31884,6.66423],[100.32671,6.66526],[100.32607,6.65933],[100.31929,6.65413],[100.35413,6.54932],[100.41152,6.52299],[100.41791,6.5189],[100.42351,6.51762],[100.43027,6.52389],[100.66986,6.45086],[100.74361,6.50811],[100.74822,6.46231],[100.81045,6.45086],[100.85884,6.24929],[101.10313,6.25617],[101.12618,6.19431],[101.06165,6.14161],[101.12388,6.11411],[101.087,5.9193],[101.02708,5.91013],[100.98815,5.79464],[101.14062,5.61613],[101.25755,5.71065],[101.25524,5.78633],[101.58019,5.93534],[101.69773,5.75881],[101.75074,5.79091],[101.80144,5.74505],[101.89188,5.8386],[101.91776,5.84269],[101.92819,5.85511],[101.94712,5.98421],[101.9714,6.00575],[101.97114,6.01992],[101.99209,6.04075],[102.01835,6.05407],[102.09182,6.14161],[102.07732,6.193],[102.08127,6.22679],[102.09086,6.23546],[102.46318,7.22462],[102.47649,9.66162],[102.52395,11.25257],[102.91449,11.65512],[102.90973,11.75613],[102.83957,11.8519],[102.78427,11.98746],[102.77026,12.06815],[102.70176,12.1686],[102.73134,12.37091],[102.78116,12.40284],[102.7796,12.43781],[102.57567,12.65358],[102.51963,12.66117],[102.4994,12.71736],[102.53053,12.77506],[102.49335,12.92711],[102.48694,12.97537],[102.52275,12.99813],[102.46011,13.08057],[102.43422,13.09061],[102.36146,13.26006],[102.36001,13.31142],[102.34611,13.35618],[102.35692,13.38274],[102.35563,13.47307],[102.361,13.50551],[102.33828,13.55613],[102.36859,13.57488],[102.44601,13.5637],[102.5358,13.56933],[102.57573,13.60461],[102.62483,13.60883],[102.58635,13.6286],[102.5481,13.6589],[102.56848,13.69366],[102.72727,13.77806],[102.77864,13.93374],[102.91251,14.01531],[102.93275,14.19044],[103.16469,14.33075],[103.39353,14.35639],[103.53518,14.42575],[103.71109,14.4348],[103.70175,14.38052],[103.93836,14.3398],[104.27616,14.39861],[104.55014,14.36091],[104.69335,14.42726],[104.97667,14.38806],[105.02804,14.23722],[105.08408,14.20402],[105.14012,14.23873],[105.17748,14.34432],[105.20894,14.34967],[105.43783,14.43865],[105.53864,14.55731],[105.5121,14.80802],[105.61162,15.00037],[105.46661,15.13132],[105.58043,15.32724],[105.50662,15.32054],[105.4692,15.33709],[105.47635,15.3796],[105.58191,15.41031],[105.60446,15.53301],[105.61756,15.68792],[105.46573,15.74742],[105.42285,15.76971],[105.37959,15.84074],[105.34115,15.92737],[105.38508,15.987],[105.42001,16.00657],[105.06204,16.09792],[105.00262,16.25627],[104.88057,16.37311],[104.73349,16.565],[104.76099,16.69302],[104.7397,16.81005],[104.76442,16.84752],[104.7373,16.91125],[104.73712,17.01404],[104.80716,17.19025],[104.80061,17.39367],[104.69867,17.53038],[104.45404,17.66788],[104.35432,17.82871],[104.2757,17.86139],[104.21776,17.99335],[104.10927,18.10826],[104.06533,18.21656],[103.97725,18.33631],[103.93916,18.33914],[103.85642,18.28666],[103.82449,18.33979],[103.699,18.34125],[103.60957,18.40528],[103.47773,18.42841],[103.41044,18.4486],[103.30977,18.4341],[103.24779,18.37807],[103.23818,18.34875],[103.29757,18.30475],[103.17093,18.2618],[103.14994,18.23172],[103.1493,18.17799],[103.07343,18.12351],[103.07823,18.03833],[103.0566,18.00144],[103.01998,17.97095],[102.9912,17.9949],[102.95812,18.0054],[102.86323,17.97531],[102.81988,17.94233],[102.79044,17.93612],[102.75954,17.89561],[102.68538,17.86653],[102.67543,17.84529],[102.69946,17.81686],[102.68194,17.80151],[102.59485,17.83537],[102.5896,17.84889],[102.61432,17.92273],[102.60971,17.95411],[102.59234,17.96127],[102.45523,17.97106],[102.11359,18.21532],[101.88485,18.02474],[101.78087,18.07559],[101.72294,17.92867],[101.44667,17.7392],[101.15108,17.47586],[100.96541,17.57926],[101.02185,17.87637],[101.1793,18.0544],[101.19118,18.2125],[101.15108,18.25624],[101.18227,18.34367],[101.06047,18.43247],[101.27585,18.68875],[101.22832,18.73377],[101.25803,18.89545],[101.35606,19.04716],[101.261,19.12717],[101.24911,19.33334],[101.20604,19.35296],[101.21347,19.46223],[101.26991,19.48324],[101.26545,19.59242],[101.08928,19.59748],[100.90302,19.61901],[100.77231,19.48324],[100.64606,19.55884],[100.58219,19.49164],[100.49604,19.53504],[100.398,19.75047],[100.5094,19.87904],[100.58808,20.15791],[100.55218,20.17741],[100.51052,20.14928],[100.47567,20.19133],[100.4537,20.19971],[100.44992,20.23644],[100.41473,20.25625],[100.37439,20.35156],[100.33383,20.4028],[100.25769,20.3992],[100.22076,20.31598],[100.16668,20.2986],[100.1712,20.24324],[100.11785,20.24787],[100.09337,20.26293],[100.09999,20.31614],[100.08404,20.36626]]]]}},{type:"Feature",properties:{iso1A2:"TJ",iso1A3:"TJK",iso1N3:"762",wikidata:"Q863",nameEn:"Tajikistan",groups:["143","142"],callingCodes:["992"]},geometry:{type:"MultiPolygon",coordinates:[[[[70.45251,41.04438],[70.38028,41.02014],[70.36655,40.90296],[69.69434,40.62615],[69.59441,40.70181],[69.53021,40.77621],[69.38327,40.7918],[69.32834,40.70233],[69.3455,40.57988],[69.2643,40.57506],[69.21063,40.54469],[69.27066,40.49274],[69.28525,40.41894],[69.30774,40.36102],[69.33794,40.34819],[69.32833,40.29794],[69.30808,40.2821],[69.24817,40.30357],[69.25229,40.26362],[69.30104,40.24502],[69.30448,40.18774],[69.2074,40.21488],[69.15659,40.2162],[69.04544,40.22904],[68.85832,40.20885],[68.84357,40.18604],[68.79276,40.17555],[68.77902,40.20492],[68.5332,40.14826],[68.52771,40.11676],[68.62796,40.07789],[69.01523,40.15771],[69.01935,40.11466],[68.96579,40.06949],[68.84906,40.04952],[68.93695,39.91167],[68.88889,39.87163],[68.63071,39.85265],[68.61972,39.68905],[68.54166,39.53929],[68.12053,39.56317],[67.70992,39.66156],[67.62889,39.60234],[67.44899,39.57799],[67.46547,39.53564],[67.39681,39.52505],[67.46822,39.46146],[67.45998,39.315],[67.36522,39.31287],[67.33226,39.23739],[67.67833,39.14479],[67.68915,39.00775],[68.09704,39.02589],[68.19743,38.85985],[68.06948,38.82115],[68.12877,38.73677],[68.05598,38.71641],[68.0807,38.64136],[68.05873,38.56087],[68.11366,38.47169],[68.06274,38.39435],[68.13289,38.40822],[68.40343,38.19484],[68.27159,37.91477],[68.12635,37.93],[67.81566,37.43107],[67.8474,37.31594],[67.78329,37.1834],[67.7803,37.08978],[67.87917,37.0591],[68.02194,36.91923],[68.18542,37.02074],[68.27605,37.00977],[68.29253,37.10621],[68.41201,37.10402],[68.41888,37.13906],[68.61851,37.19815],[68.6798,37.27906],[68.81438,37.23862],[68.80889,37.32494],[68.91189,37.26704],[68.88168,37.33368],[68.96407,37.32603],[69.03274,37.25174],[69.25152,37.09426],[69.39529,37.16752],[69.45022,37.23315],[69.36645,37.40462],[69.44954,37.4869],[69.51888,37.5844],[69.80041,37.5746],[69.84435,37.60616],[69.93362,37.61378],[69.95971,37.5659],[70.15015,37.52519],[70.28243,37.66706],[70.27694,37.81258],[70.1863,37.84296],[70.17206,37.93276],[70.4898,38.12546],[70.54673,38.24541],[70.60407,38.28046],[70.61526,38.34774],[70.64966,38.34999],[70.69189,38.37031],[70.6761,38.39144],[70.67438,38.40597],[70.69807,38.41861],[70.72485,38.4131],[70.75455,38.4252],[70.77132,38.45548],[70.78581,38.45502],[70.78702,38.45031],[70.79766,38.44944],[70.80521,38.44447],[70.81697,38.44507],[70.82538,38.45394],[70.84376,38.44688],[70.88719,38.46826],[70.92728,38.43021],[70.98693,38.48862],[71.03545,38.44779],[71.0556,38.40176],[71.09542,38.42517],[71.10592,38.42077],[71.10957,38.40671],[71.1451,38.40106],[71.21291,38.32797],[71.33114,38.30339],[71.33869,38.27335],[71.37803,38.25641],[71.36444,38.15358],[71.29878,38.04429],[71.28922,38.01272],[71.27622,37.99946],[71.27278,37.96496],[71.24969,37.93031],[71.2809,37.91995],[71.296,37.93403],[71.32871,37.88564],[71.51565,37.95349],[71.58843,37.92425],[71.59255,37.79956],[71.55752,37.78677],[71.54324,37.77104],[71.53053,37.76534],[71.55234,37.73209],[71.54186,37.69691],[71.51972,37.61945],[71.5065,37.60912],[71.49693,37.53527],[71.50616,37.50733],[71.5256,37.47971],[71.49612,37.4279],[71.47685,37.40281],[71.4862,37.33405],[71.49821,37.31975],[71.50674,37.31502],[71.48536,37.26017],[71.4824,37.24921],[71.48339,37.23937],[71.47386,37.2269],[71.4555,37.21418],[71.4494,37.18137],[71.44127,37.11856],[71.43097,37.05855],[71.45578,37.03094],[71.46923,36.99925],[71.48481,36.93218],[71.51502,36.89128],[71.57195,36.74943],[71.67083,36.67346],[71.83229,36.68084],[72.31676,36.98115],[72.54095,37.00007],[72.66381,37.02014],[72.79693,37.22222],[73.06884,37.31729],[73.29633,37.46495],[73.77197,37.4417],[73.76647,37.33913],[73.61129,37.27469],[73.64974,37.23643],[73.82552,37.22659],[73.8564,37.26158],[74.20308,37.34208],[74.23339,37.41116],[74.41055,37.3948],[74.56161,37.37734],[74.68383,37.3948],[74.8294,37.3435],[74.88887,37.23275],[75.12328,37.31839],[75.09719,37.37297],[75.15899,37.41443],[75.06011,37.52779],[74.94338,37.55501],[74.8912,37.67576],[75.00935,37.77486],[74.92416,37.83428],[74.9063,38.03033],[74.82665,38.07359],[74.80331,38.19889],[74.69894,38.22155],[74.69619,38.42947],[74.51217,38.47034],[74.17022,38.65504],[73.97933,38.52945],[73.79806,38.61106],[73.80656,38.66449],[73.7033,38.84782],[73.7445,38.93867],[73.82964,38.91517],[73.81728,39.04007],[73.75823,39.023],[73.60638,39.24534],[73.54572,39.27567],[73.55396,39.3543],[73.5004,39.38402],[73.59241,39.40843],[73.59831,39.46425],[73.45096,39.46677],[73.31912,39.38615],[73.18454,39.35536],[72.85934,39.35116],[72.62027,39.39696],[72.33173,39.33093],[72.23834,39.17248],[72.17242,39.2661],[72.09689,39.26823],[72.04059,39.36704],[71.90601,39.27674],[71.79202,39.27355],[71.7522,39.32031],[71.80164,39.40631],[71.76816,39.45456],[71.62688,39.44056],[71.5517,39.45722],[71.55856,39.57588],[71.49814,39.61397],[71.08752,39.50704],[71.06418,39.41586],[70.7854,39.38933],[70.64087,39.58792],[70.44757,39.60128],[70.2869,39.53141],[70.11111,39.58223],[69.87491,39.53882],[69.68677,39.59281],[69.3594,39.52516],[69.26938,39.8127],[69.35649,40.01994],[69.43134,39.98431],[69.43557,39.92877],[69.53615,39.93991],[69.5057,40.03277],[69.53855,40.0887],[69.53794,40.11833],[69.55555,40.12296],[69.57615,40.10524],[69.64704,40.12165],[69.67001,40.10639],[70.01283,40.23288],[70.58297,40.00891],[70.57384,39.99394],[70.47557,39.93216],[70.55033,39.96619],[70.58912,39.95211],[70.65946,39.9878],[70.65827,40.0981],[70.7928,40.12797],[70.80495,40.16813],[70.9818,40.22392],[70.8607,40.217],[70.62342,40.17396],[70.56394,40.26421],[70.57149,40.3442],[70.37511,40.38605],[70.32626,40.45174],[70.49871,40.52503],[70.80009,40.72825],[70.45251,41.04438]]],[[[70.68112,40.90612],[70.6158,40.97661],[70.56077,41.00642],[70.54223,40.98787],[70.57501,40.98941],[70.6721,40.90555],[70.68112,40.90612]]],[[[70.74189,39.86319],[70.53651,39.89155],[70.52631,39.86989],[70.54998,39.85137],[70.59667,39.83542],[70.63105,39.77923],[70.74189,39.86319]]]]}},{type:"Feature",properties:{iso1A2:"TK",iso1A3:"TKL",iso1N3:"772",wikidata:"Q36823",nameEn:"Tokelau",country:"NZ",groups:["061","009"],driveSide:"left",callingCodes:["690"]},geometry:{type:"MultiPolygon",coordinates:[[[[-167.75195,-10.12005],[-167.75329,-7.52784],[-174.18707,-7.54408],[-174.17993,-10.13616],[-167.75195,-10.12005]]]]}},{type:"Feature",properties:{iso1A2:"TL",iso1A3:"TLS",iso1N3:"626",wikidata:"Q574",nameEn:"East Timor",aliases:["Timor-Leste","TP"],groups:["035","142"],driveSide:"left",callingCodes:["670"]},geometry:{type:"MultiPolygon",coordinates:[[[[124.46701,-9.13002],[124.94011,-8.85617],[124.97742,-9.08128],[125.11764,-8.96359],[125.18632,-9.03142],[125.18907,-9.16434],[125.09434,-9.19669],[125.04044,-9.17093],[124.97892,-9.19281],[125.09025,-9.46406],[125.68138,-9.85176],[127.55165,-9.05052],[127.42116,-8.22471],[125.87691,-8.31789],[125.65946,-8.06136],[125.31127,-8.22976],[124.92337,-8.75859],[124.33472,-9.11416],[124.04628,-9.22671],[124.04286,-9.34243],[124.10539,-9.41206],[124.14517,-9.42324],[124.21247,-9.36904],[124.28115,-9.42189],[124.28115,-9.50453],[124.3535,-9.48493],[124.35258,-9.43002],[124.38554,-9.3582],[124.45971,-9.30263],[124.46701,-9.13002]]]]}},{type:"Feature",properties:{iso1A2:"TM",iso1A3:"TKM",iso1N3:"795",wikidata:"Q874",nameEn:"Turkmenistan",groups:["143","142"],callingCodes:["993"]},geometry:{type:"MultiPolygon",coordinates:[[[[60.5078,41.21694],[60.06581,41.4363],[60.18117,41.60082],[60.06032,41.76287],[60.08504,41.80997],[60.33223,41.75058],[59.95046,41.97966],[60.0356,42.01028],[60.04659,42.08982],[59.96419,42.1428],[60.00539,42.212],[59.94633,42.27655],[59.4341,42.29738],[59.2955,42.37064],[59.17317,42.52248],[58.93422,42.5407],[58.6266,42.79314],[58.57991,42.64988],[58.27504,42.69632],[58.14321,42.62159],[58.29427,42.56497],[58.51674,42.30348],[58.40688,42.29535],[58.3492,42.43335],[57.99214,42.50021],[57.90975,42.4374],[57.92897,42.24047],[57.84932,42.18555],[57.6296,42.16519],[57.30275,42.14076],[57.03633,41.92043],[56.96218,41.80383],[57.03359,41.41777],[57.13796,41.36625],[57.03423,41.25435],[56.00314,41.32584],[55.45471,41.25609],[54.95182,41.92424],[54.20635,42.38477],[52.97575,42.1308],[52.47884,41.78034],[52.26048,41.69249],[51.7708,40.29239],[53.89734,37.3464],[54.24565,37.32047],[54.36211,37.34912],[54.58664,37.45809],[54.67247,37.43532],[54.77822,37.51597],[54.81804,37.61285],[54.77684,37.62264],[54.851,37.75739],[55.13412,37.94705],[55.44152,38.08564],[55.76561,38.12238],[55.97847,38.08024],[56.33278,38.08132],[56.32454,38.18502],[56.43303,38.26054],[56.62255,38.24005],[56.73928,38.27887],[57.03453,38.18717],[57.21169,38.28965],[57.37236,38.09321],[57.35042,37.98546],[57.79534,37.89299],[58.21399,37.77281],[58.22999,37.6856],[58.39959,37.63134],[58.47786,37.6433],[58.5479,37.70526],[58.6921,37.64548],[58.9338,37.67374],[59.22905,37.51161],[59.33507,37.53146],[59.39797,37.47892],[59.39385,37.34257],[59.55178,37.13594],[59.74678,37.12499],[60.00768,37.04102],[60.34767,36.63214],[61.14516,36.64644],[61.18187,36.55348],[61.1393,36.38782],[61.22719,36.12759],[61.12007,35.95992],[61.22444,35.92879],[61.26152,35.80749],[61.22719,35.67038],[61.27371,35.61482],[61.58742,35.43803],[61.77693,35.41341],[61.97743,35.4604],[62.05709,35.43803],[62.15871,35.33278],[62.29191,35.25964],[62.29878,35.13312],[62.48006,35.28796],[62.62288,35.22067],[62.74098,35.25432],[62.90853,35.37086],[63.0898,35.43131],[63.12276,35.53196],[63.10079,35.63024],[63.23262,35.67487],[63.10318,35.81782],[63.12276,35.86208],[63.29579,35.85985],[63.53475,35.90881],[63.56496,35.95106],[63.98519,36.03773],[64.05385,36.10433],[64.43288,36.24401],[64.57295,36.34362],[64.62514,36.44311],[64.61141,36.6351],[64.97945,37.21913],[65.51778,37.23881],[65.64263,37.34388],[65.64137,37.45061],[65.72274,37.55438],[66.30993,37.32409],[66.55743,37.35409],[66.52303,37.39827],[66.65761,37.45497],[66.52852,37.58568],[66.53676,37.80084],[66.67684,37.96776],[66.56697,38.0435],[66.41042,38.02403],[66.24013,38.16238],[65.83913,38.25733],[65.55873,38.29052],[64.32576,38.98691],[64.19086,38.95561],[63.70778,39.22349],[63.6913,39.27666],[62.43337,39.98528],[62.34273,40.43206],[62.11751,40.58242],[61.87856,41.12257],[61.4446,41.29407],[61.39732,41.19873],[61.33199,41.14946],[61.22212,41.14946],[61.03261,41.25691],[60.5078,41.21694]]]]}},{type:"Feature",properties:{iso1A2:"TN",iso1A3:"TUN",iso1N3:"788",wikidata:"Q948",nameEn:"Tunisia",groups:["015","002"],callingCodes:["216"]},geometry:{type:"MultiPolygon",coordinates:[[[[11.2718,37.6713],[7.89009,38.19924],[8.59123,37.14286],[8.64044,36.9401],[8.62972,36.86499],[8.67706,36.8364],[8.57613,36.78062],[8.46537,36.7706],[8.47609,36.66607],[8.16167,36.48817],[8.18936,36.44939],[8.40731,36.42208],[8.2626,35.91733],[8.26472,35.73669],[8.35371,35.66373],[8.36086,35.47774],[8.30329,35.29884],[8.47318,35.23376],[8.3555,35.10007],[8.30727,34.95378],[8.25189,34.92009],[8.29655,34.72798],[8.20482,34.57575],[7.86264,34.3987],[7.81242,34.21841],[7.74207,34.16492],[7.66174,34.20167],[7.52851,34.06493],[7.54088,33.7726],[7.73687,33.42114],[7.83028,33.18851],[8.11433,33.10175],[8.1179,33.05086],[8.31895,32.83483],[8.35999,32.50101],[9.07483,32.07865],[9.55544,30.23971],[9.76848,30.34366],[9.88152,30.34074],[10.29516,30.90337],[10.12239,31.42098],[10.31364,31.72648],[10.48497,31.72956],[10.62788,31.96629],[10.7315,31.97235],[11.04234,32.2145],[11.53898,32.4138],[11.57828,32.48013],[11.46037,32.6307],[11.51549,33.09826],[11.55852,33.1409],[11.56255,33.16754],[11.66543,33.34642],[11.2718,37.6713]]]]}},{type:"Feature",properties:{iso1A2:"TO",iso1A3:"TON",iso1N3:"776",wikidata:"Q678",nameEn:"Tonga",groups:["061","009"],driveSide:"left",callingCodes:["676"]},geometry:{type:"MultiPolygon",coordinates:[[[[-176.74538,-22.89767],[-180,-22.90585],[-180,-24.21376],[-173.10761,-24.19665],[-173.11048,-23.23027],[-173.13438,-14.94228],[-174.17905,-14.94502],[-176.76826,-14.95183],[-176.74538,-22.89767]]]]}},{type:"Feature",properties:{iso1A2:"TR",iso1A3:"TUR",iso1N3:"792",wikidata:"Q43",nameEn:"Turkey",groups:["145","142"],callingCodes:["90"]},geometry:{type:"MultiPolygon",coordinates:[[[[41.54366,41.52185],[40.89217,41.72528],[34.8305,42.4581],[28.32297,41.98371],[28.02971,41.98066],[27.91479,41.97902],[27.83492,41.99709],[27.81235,41.94803],[27.69949,41.97515],[27.55191,41.90928],[27.52379,41.93756],[27.45478,41.96591],[27.27411,42.10409],[27.22376,42.10152],[27.19251,42.06028],[27.08486,42.08735],[27.03277,42.0809],[26.95638,42.00741],[26.79143,41.97386],[26.62996,41.97644],[26.56051,41.92995],[26.57961,41.90024],[26.53968,41.82653],[26.36952,41.82265],[26.33589,41.76802],[26.32952,41.73637],[26.35957,41.71149],[26.47958,41.67037],[26.5209,41.62592],[26.59196,41.60491],[26.59742,41.48058],[26.61767,41.42281],[26.62997,41.34613],[26.5837,41.32131],[26.5209,41.33993],[26.39861,41.25053],[26.32259,41.24929],[26.31928,41.07386],[26.3606,41.02027],[26.33297,40.98388],[26.35894,40.94292],[26.32259,40.94042],[26.28623,40.93005],[26.29441,40.89119],[26.26169,40.9168],[26.20856,40.86048],[26.21351,40.83298],[26.15685,40.80709],[26.12854,40.77339],[26.12495,40.74283],[26.08638,40.73214],[26.0754,40.72772],[26.03489,40.73051],[25.94795,40.72797],[26.04292,40.3958],[25.61285,40.17161],[25.94257,39.39358],[26.43357,39.43096],[26.70773,39.0312],[26.61814,38.81372],[26.21136,38.65436],[26.32173,38.48731],[26.24183,38.44695],[26.21136,38.17558],[27.05537,37.9131],[27.16428,37.72343],[26.99377,37.69034],[26.95583,37.64989],[27.14757,37.32],[27.20312,36.94571],[27.45627,36.9008],[27.24613,36.71622],[27.46117,36.53789],[27.89482,36.69898],[27.95037,36.46155],[28.23708,36.56812],[29.30783,36.01033],[29.48192,36.18377],[29.61002,36.1731],[29.61805,36.14179],[29.69611,36.10365],[29.73302,35.92555],[32.82353,35.70297],[35.51152,36.10954],[35.931,35.92109],[35.98499,35.94107],[36.00514,35.94113],[36.01844,35.92403],[35.99829,35.88242],[36.11827,35.85923],[36.13919,35.83692],[36.14029,35.81015],[36.1623,35.80925],[36.17441,35.92076],[36.19973,35.95195],[36.25366,35.96264],[36.27678,35.94839],[36.29769,35.96086],[36.28338,36.00273],[36.30099,36.00985],[36.33956,35.98687],[36.37474,36.01163],[36.39206,36.22088],[36.4617,36.20461],[36.50463,36.2419],[36.6125,36.22592],[36.68672,36.23677],[36.65653,36.33861],[36.6081,36.33772],[36.54206,36.49539],[36.58829,36.58295],[36.57398,36.65186],[36.62681,36.71189],[36.61581,36.74629],[36.66727,36.82901],[36.99557,36.75997],[36.99886,36.74012],[37.04399,36.73483],[37.04619,36.71101],[37.01647,36.69512],[37.02088,36.66422],[37.08279,36.63495],[37.10894,36.6704],[37.16177,36.66069],[37.21988,36.6736],[37.47253,36.63243],[37.49103,36.66904],[37.68048,36.75065],[37.81974,36.76055],[38.21064,36.91842],[38.38859,36.90064],[38.55908,36.84429],[38.74042,36.70629],[39.03217,36.70911],[39.21538,36.66834],[39.81589,36.75538],[40.69136,37.0996],[40.90856,37.13147],[41.21937,37.07665],[41.515,37.08084],[42.00894,37.17209],[42.18225,37.28569],[42.19301,37.31323],[42.2112,37.32491],[42.22257,37.31395],[42.22381,37.30238],[42.20454,37.28715],[42.21548,37.28026],[42.23683,37.2863],[42.26039,37.27017],[42.2824,37.2798],[42.34735,37.22548],[42.32313,37.17814],[42.35724,37.10998],[42.56725,37.14878],[42.78887,37.38615],[42.93705,37.32015],[43.11403,37.37436],[43.30083,37.30629],[43.33508,37.33105],[43.50787,37.24436],[43.56702,37.25675],[43.63085,37.21957],[43.7009,37.23692],[43.8052,37.22825],[43.82699,37.19477],[43.84878,37.22205],[43.90949,37.22453],[44.02002,37.33229],[44.13521,37.32486],[44.2613,37.25055],[44.27998,37.16501],[44.22239,37.15756],[44.18503,37.09551],[44.25975,36.98119],[44.30645,36.97373],[44.35937,37.02843],[44.35315,37.04955],[44.38117,37.05825],[44.42631,37.05825],[44.63179,37.19229],[44.76698,37.16162],[44.78319,37.1431],[44.7868,37.16644],[44.75986,37.21549],[44.81021,37.2915],[44.58449,37.45018],[44.61401,37.60165],[44.56887,37.6429],[44.62096,37.71985],[44.55498,37.783],[44.45948,37.77065],[44.3883,37.85433],[44.22509,37.88859],[44.42476,38.25763],[44.50115,38.33939],[44.44386,38.38295],[44.38309,38.36117],[44.3119,38.37887],[44.3207,38.49799],[44.32058,38.62752],[44.28065,38.6465],[44.26155,38.71427],[44.30322,38.81581],[44.18863,38.93881],[44.20946,39.13975],[44.1043,39.19842],[44.03667,39.39223],[44.22452,39.4169],[44.29818,39.378],[44.37921,39.4131],[44.42832,39.4131],[44.41849,39.56659],[44.48111,39.61579],[44.47298,39.68788],[44.6137,39.78393],[44.65422,39.72163],[44.71806,39.71124],[44.81043,39.62677],[44.80977,39.65768],[44.75779,39.7148],[44.61845,39.8281],[44.46635,39.97733],[44.26973,40.04866],[44.1778,40.02845],[44.1057,40.03555],[43.92307,40.01787],[43.65688,40.11199],[43.65221,40.14889],[43.71136,40.16673],[43.59928,40.34019],[43.60862,40.43267],[43.54791,40.47413],[43.63664,40.54159],[43.7425,40.66805],[43.74872,40.7365],[43.67712,40.84846],[43.67712,40.93084],[43.58683,40.98961],[43.47319,41.02251],[43.44984,41.0988],[43.4717,41.12611],[43.44973,41.17666],[43.36118,41.2028],[43.23096,41.17536],[43.1945,41.25242],[43.13373,41.25503],[43.21707,41.30331],[43.02956,41.37891],[42.8785,41.50516],[42.84899,41.47265],[42.78995,41.50126],[42.84471,41.58912],[42.72794,41.59714],[42.59202,41.58183],[42.51772,41.43606],[42.26387,41.49346],[41.95134,41.52466],[41.81939,41.43621],[41.7124,41.47417],[41.7148,41.4932],[41.54366,41.52185]]]]}},{type:"Feature",properties:{iso1A2:"TT",iso1A3:"TTO",iso1N3:"780",wikidata:"Q754",nameEn:"Trinidad and Tobago",groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 868"]},geometry:{type:"MultiPolygon",coordinates:[[[[-61.62505,11.18974],[-62.08693,10.04435],[-60.89962,9.81445],[-60.07172,11.77667],[-61.62505,11.18974]]]]}},{type:"Feature",properties:{iso1A2:"TV",iso1A3:"TUV",iso1N3:"798",wikidata:"Q672",nameEn:"Tuvalu",groups:["061","009"],driveSide:"left",callingCodes:["688"]},geometry:{type:"MultiPolygon",coordinates:[[[[174,-5],[174,-11.5],[179.99999,-11.5],[179.99999,-5],[174,-5]]]]}},{type:"Feature",properties:{iso1A2:"TW",iso1A3:"TWN",iso1N3:"158",wikidata:"Q865",nameEn:"Taiwan",groups:["030","142"],callingCodes:["886"]},geometry:{type:"MultiPolygon",coordinates:[[[[123.0791,22.07818],[122.26612,25.98197],[120.49232,25.22863],[118.56434,24.49266],[118.42453,24.54644],[118.35291,24.51645],[118.28244,24.51231],[118.11703,24.39734],[120.69238,21.52331],[123.0791,22.07818]]]]}},{type:"Feature",properties:{iso1A2:"TZ",iso1A3:"TZA",iso1N3:"834",wikidata:"Q924",nameEn:"Tanzania",groups:["014","202","002"],driveSide:"left",callingCodes:["255"]},geometry:{type:"MultiPolygon",coordinates:[[[[30.80408,-0.99911],[30.76635,-0.9852],[30.70631,-1.01175],[30.64166,-1.06601],[30.47194,-1.0555],[30.45116,-1.10641],[30.50889,-1.16412],[30.57123,-1.33264],[30.71974,-1.43244],[30.84079,-1.64652],[30.80802,-1.91477],[30.89303,-2.08223],[30.83915,-2.35795],[30.54501,-2.41404],[30.41789,-2.66266],[30.52747,-2.65841],[30.40662,-2.86151],[30.4987,-2.9573],[30.57926,-2.89791],[30.6675,-2.98987],[30.83823,-2.97837],[30.84165,-3.25152],[30.45915,-3.56532],[30.22042,-4.01738],[30.03323,-4.26631],[29.88172,-4.35743],[29.82885,-4.36153],[29.77289,-4.41733],[29.75109,-4.45836],[29.63827,-4.44681],[29.43673,-4.44845],[29.52552,-6.2731],[30.2567,-7.14121],[30.79243,-8.27382],[31.00796,-8.58615],[31.37533,-8.60769],[31.57147,-8.70619],[31.57147,-8.81388],[31.71158,-8.91386],[31.81587,-8.88618],[31.94663,-8.93846],[31.94196,-9.02303],[31.98866,-9.07069],[32.08206,-9.04609],[32.16146,-9.05993],[32.25486,-9.13371],[32.43543,-9.11988],[32.49147,-9.14754],[32.53661,-9.24281],[32.75611,-9.28583],[32.76233,-9.31963],[32.95389,-9.40138],[32.99397,-9.36712],[33.14925,-9.49322],[33.31581,-9.48554],[33.48052,-9.62442],[33.76677,-9.58516],[33.93298,-9.71647],[33.9638,-9.62206],[33.95829,-9.54066],[34.03865,-9.49398],[34.54499,-10.0678],[34.51911,-10.12279],[34.57581,-10.56271],[34.65946,-10.6828],[34.67047,-10.93796],[34.61161,-11.01611],[34.63305,-11.11731],[34.79375,-11.32245],[34.91153,-11.39799],[34.96296,-11.57354],[35.63599,-11.55927],[35.82767,-11.41081],[36.19094,-11.57593],[36.19094,-11.70008],[36.62068,-11.72884],[36.80309,-11.56836],[37.3936,-11.68949],[37.76614,-11.53352],[37.8388,-11.3123],[37.93618,-11.26228],[38.21598,-11.27289],[38.47258,-11.4199],[38.88996,-11.16978],[39.24395,-11.17433],[39.58249,-10.96043],[40.00295,-10.80255],[40.44265,-10.4618],[40.74206,-10.25691],[40.14328,-4.64201],[39.62121,-4.68136],[39.44306,-4.93877],[39.21631,-4.67835],[37.81321,-3.69179],[37.75036,-3.54243],[37.63099,-3.50723],[37.5903,-3.42735],[37.71745,-3.304],[37.67199,-3.06222],[34.0824,-1.02264],[34.03084,-1.05101],[34.02286,-1.00779],[33.93107,-0.99298],[30.80408,-0.99911]]]]}},{type:"Feature",properties:{iso1A2:"UA",iso1A3:"UKR",iso1N3:"804",wikidata:"Q212",nameEn:"Ukraine",groups:["151","150"],callingCodes:["380"]},geometry:{type:"MultiPolygon",coordinates:[[[[33.57318,46.10317],[33.61467,46.13561],[33.63854,46.14147],[33.61517,46.22615],[33.646,46.23028],[33.74047,46.18555],[33.79715,46.20482],[33.85234,46.19863],[33.91549,46.15938],[34.05272,46.10838],[34.07311,46.11769],[34.12929,46.10494],[34.181,46.06804],[34.25111,46.0532],[34.33912,46.06114],[34.41221,46.00245],[34.44155,45.95995],[34.48729,45.94267],[34.52011,45.95097],[34.55889,45.99347],[34.60861,45.99347],[34.66679,45.97136],[34.75479,45.90705],[34.80153,45.90047],[34.79905,45.81009],[34.96015,45.75634],[35.23066,45.79231],[37.62608,46.82615],[38.12112,46.86078],[38.3384,46.98085],[38.22955,47.12069],[38.23049,47.2324],[38.32112,47.2585],[38.33074,47.30508],[38.22225,47.30788],[38.28954,47.39255],[38.28679,47.53552],[38.35062,47.61631],[38.76379,47.69346],[38.79628,47.81109],[38.87979,47.87719],[39.73935,47.82876],[39.82213,47.96396],[39.77544,48.04206],[39.88256,48.04482],[39.83724,48.06501],[39.94847,48.22811],[40.00752,48.22445],[39.99241,48.31768],[39.97325,48.31399],[39.9693,48.29904],[39.95248,48.29972],[39.91465,48.26743],[39.90041,48.3049],[39.84273,48.30947],[39.84136,48.33321],[39.94847,48.35055],[39.88794,48.44226],[39.86196,48.46633],[39.84548,48.57821],[39.79764,48.58668],[39.67226,48.59368],[39.71765,48.68673],[39.73104,48.7325],[39.79466,48.83739],[39.97182,48.79398],[40.08168,48.87443],[40.03636,48.91957],[39.98967,48.86901],[39.78368,48.91596],[39.74874,48.98675],[39.72649,48.9754],[39.71353,48.98959],[39.6683,48.99454],[39.6836,49.05121],[39.93437,49.05709],[40.01988,49.1761],[40.22176,49.25683],[40.18331,49.34996],[40.14912,49.37681],[40.1141,49.38798],[40.03087,49.45452],[40.03636,49.52321],[40.16683,49.56865],[40.13249,49.61672],[39.84548,49.56064],[39.65047,49.61761],[39.59142,49.73758],[39.44496,49.76067],[39.27968,49.75976],[39.1808,49.88911],[38.9391,49.79524],[38.90477,49.86787],[38.73311,49.90238],[38.68677,50.00904],[38.65688,49.97176],[38.35408,50.00664],[38.32524,50.08866],[38.18517,50.08161],[38.21675,49.98104],[38.02999,49.90592],[38.02999,49.94482],[37.90776,50.04194],[37.79515,50.08425],[37.75807,50.07896],[37.61113,50.21976],[37.62879,50.24481],[37.62486,50.29966],[37.47243,50.36277],[37.48204,50.46079],[37.08468,50.34935],[36.91762,50.34963],[36.69377,50.26982],[36.64571,50.218],[36.56655,50.2413],[36.58371,50.28563],[36.47817,50.31457],[36.30101,50.29088],[36.20763,50.3943],[36.06893,50.45205],[35.8926,50.43829],[35.80388,50.41356],[35.73659,50.35489],[35.61711,50.35707],[35.58003,50.45117],[35.47463,50.49247],[35.39464,50.64751],[35.48116,50.66405],[35.47704,50.77274],[35.41367,50.80227],[35.39307,50.92145],[35.32598,50.94524],[35.40837,51.04119],[35.31774,51.08434],[35.20375,51.04723],[35.12685,51.16191],[35.14058,51.23162],[34.97304,51.2342],[34.82472,51.17483],[34.6874,51.18],[34.6613,51.25053],[34.38802,51.2746],[34.31661,51.23936],[34.23009,51.26429],[34.33446,51.363],[34.22048,51.4187],[34.30562,51.5205],[34.17599,51.63253],[34.07765,51.67065],[34.42922,51.72852],[34.41136,51.82793],[34.09413,52.00835],[34.11199,52.14087],[34.05239,52.20132],[33.78789,52.37204],[33.55718,52.30324],[33.48027,52.31499],[33.51323,52.35779],[33.18913,52.3754],[32.89937,52.2461],[32.85405,52.27888],[32.69475,52.25535],[32.54781,52.32423],[32.3528,52.32842],[32.38988,52.24946],[32.33083,52.23685],[32.34044,52.1434],[32.2777,52.10266],[32.23331,52.08085],[32.08813,52.03319],[31.92159,52.05144],[31.96141,52.08015],[31.85018,52.11305],[31.81722,52.09955],[31.7822,52.11406],[31.38326,52.12991],[31.25142,52.04131],[31.13332,52.1004],[30.95589,52.07775],[30.90897,52.00699],[30.76443,51.89739],[30.68804,51.82806],[30.51946,51.59649],[30.64992,51.35014],[30.56203,51.25655],[30.36153,51.33984],[30.34642,51.42555],[30.17888,51.51025],[29.77376,51.4461],[29.7408,51.53417],[29.54372,51.48372],[29.49773,51.39814],[29.42357,51.4187],[29.32881,51.37843],[29.25191,51.49828],[29.25603,51.57089],[29.20659,51.56918],[29.16402,51.64679],[29.1187,51.65872],[28.99098,51.56833],[28.95528,51.59222],[28.81795,51.55552],[28.76027,51.48802],[28.78224,51.45294],[28.75615,51.41442],[28.73143,51.46236],[28.69161,51.44695],[28.64429,51.5664],[28.47051,51.59734],[28.37592,51.54505],[28.23452,51.66988],[28.10658,51.57857],[27.95827,51.56065],[27.91844,51.61952],[27.85253,51.62293],[27.76052,51.47604],[27.67125,51.50854],[27.71932,51.60672],[27.55727,51.63486],[27.51058,51.5854],[27.47212,51.61184],[27.24828,51.60161],[27.26613,51.65957],[27.20948,51.66713],[27.20602,51.77291],[26.99422,51.76933],[26.9489,51.73788],[26.80043,51.75777],[26.69759,51.82284],[26.46962,51.80501],[26.39367,51.87315],[26.19084,51.86781],[26.00408,51.92967],[25.83217,51.92587],[25.80574,51.94556],[25.73673,51.91973],[25.46163,51.92205],[25.20228,51.97143],[24.98784,51.91273],[24.37123,51.88222],[24.29021,51.80841],[24.3163,51.75063],[24.13075,51.66979],[23.99907,51.58369],[23.8741,51.59734],[23.91118,51.63316],[23.7766,51.66809],[23.60906,51.62122],[23.6736,51.50255],[23.62751,51.50512],[23.69905,51.40871],[23.63858,51.32182],[23.80678,51.18405],[23.90376,51.07697],[23.92217,51.00836],[24.04576,50.90196],[24.14524,50.86128],[24.0952,50.83262],[23.99254,50.83847],[23.95925,50.79271],[24.0595,50.71625],[24.0996,50.60752],[24.07048,50.5071],[24.03668,50.44507],[23.99563,50.41289],[23.79445,50.40481],[23.71382,50.38248],[23.67635,50.33385],[23.28221,50.0957],[22.99329,49.84249],[22.83179,49.69875],[22.80261,49.69098],[22.78304,49.65543],[22.64534,49.53094],[22.69444,49.49378],[22.748,49.32759],[22.72009,49.20288],[22.86336,49.10513],[22.89122,49.00725],[22.56155,49.08865],[22.54338,49.01424],[22.48296,48.99172],[22.42934,48.92857],[22.34151,48.68893],[22.21379,48.6218],[22.16023,48.56548],[22.14689,48.4005],[22.2083,48.42534],[22.38133,48.23726],[22.49806,48.25189],[22.59007,48.15121],[22.58733,48.10813],[22.66835,48.09162],[22.73427,48.12005],[22.81804,48.11363],[22.87847,48.04665],[22.84276,47.98602],[22.89849,47.95851],[22.94301,47.96672],[22.92241,48.02002],[23.0158,47.99338],[23.08858,48.00716],[23.1133,48.08061],[23.15999,48.12188],[23.27397,48.08245],[23.33577,48.0237],[23.4979,47.96858],[23.52803,48.01818],[23.5653,48.00499],[23.63894,48.00293],[23.66262,47.98786],[23.75188,47.99705],[23.80904,47.98142],[23.8602,47.9329],[23.89352,47.94512],[23.94192,47.94868],[23.96337,47.96672],[23.98553,47.96076],[24.00801,47.968],[24.02999,47.95087],[24.06466,47.95317],[24.11281,47.91487],[24.22566,47.90231],[24.34926,47.9244],[24.43578,47.97131],[24.61994,47.95062],[24.70632,47.84428],[24.81893,47.82031],[24.88896,47.7234],[25.11144,47.75203],[25.23778,47.89403],[25.63878,47.94924],[25.77723,47.93919],[26.05901,47.9897],[26.17711,47.99246],[26.33504,48.18418],[26.55202,48.22445],[26.62823,48.25804],[26.6839,48.35828],[26.79239,48.29071],[26.82809,48.31629],[26.71274,48.40388],[26.85556,48.41095],[26.93384,48.36558],[27.03821,48.37653],[27.0231,48.42485],[27.08078,48.43214],[27.13434,48.37288],[27.27855,48.37534],[27.32159,48.4434],[27.37604,48.44398],[27.37741,48.41026],[27.44333,48.41209],[27.46942,48.454],[27.5889,48.49224],[27.59027,48.46311],[27.6658,48.44034],[27.74422,48.45926],[27.79225,48.44244],[27.81902,48.41874],[27.87533,48.4037],[27.88391,48.36699],[27.95883,48.32368],[28.04527,48.32661],[28.09873,48.3124],[28.07504,48.23494],[28.17666,48.25963],[28.19314,48.20749],[28.2856,48.23202],[28.32508,48.23384],[28.35519,48.24957],[28.36996,48.20543],[28.34912,48.1787],[28.30586,48.1597],[28.30609,48.14018],[28.34009,48.13147],[28.38712,48.17567],[28.43701,48.15832],[28.42454,48.12047],[28.48428,48.0737],[28.53921,48.17453],[28.69896,48.13106],[28.85232,48.12506],[28.8414,48.03392],[28.9306,47.96255],[29.1723,47.99013],[29.19839,47.89261],[29.27804,47.88893],[29.20663,47.80367],[29.27255,47.79953],[29.22242,47.73607],[29.22414,47.60012],[29.11743,47.55001],[29.18603,47.43387],[29.3261,47.44664],[29.39889,47.30179],[29.47854,47.30366],[29.48678,47.36043],[29.5733,47.36508],[29.59665,47.25521],[29.54996,47.24962],[29.57696,47.13581],[29.49732,47.12878],[29.53044,47.07851],[29.61038,47.09932],[29.62137,47.05069],[29.57056,46.94766],[29.72986,46.92234],[29.75458,46.8604],[29.87405,46.88199],[29.98814,46.82358],[29.94522,46.80055],[29.9743,46.75325],[29.94409,46.56002],[29.88916,46.54302],[30.02511,46.45132],[30.16794,46.40967],[30.09103,46.38694],[29.94114,46.40114],[29.88329,46.35851],[29.74496,46.45605],[29.66359,46.4215],[29.6763,46.36041],[29.5939,46.35472],[29.49914,46.45889],[29.35357,46.49505],[29.24886,46.37912],[29.23547,46.55435],[29.02409,46.49582],[29.01241,46.46177],[28.9306,46.45699],[29.004,46.31495],[28.98478,46.31803],[28.94953,46.25852],[29.06656,46.19716],[28.94643,46.09176],[29.00613,46.04962],[28.98004,46.00385],[28.74383,45.96664],[28.78503,45.83475],[28.69852,45.81753],[28.70401,45.78019],[28.52823,45.73803],[28.47879,45.66994],[28.51587,45.6613],[28.54196,45.58062],[28.49252,45.56716],[28.51449,45.49982],[28.43072,45.48538],[28.41836,45.51715],[28.30201,45.54744],[28.21139,45.46895],[28.28504,45.43907],[28.34554,45.32102],[28.5735,45.24759],[28.71358,45.22631],[28.78911,45.24179],[28.81383,45.3384],[28.94292,45.28045],[28.96077,45.33164],[29.24779,45.43388],[29.42632,45.44545],[29.59798,45.38857],[29.68175,45.26885],[29.65428,45.25629],[29.69272,45.19227],[30.04414,45.08461],[31.62627,45.50633],[33.54017,46.0123],[33.59087,46.06013],[33.57318,46.10317]]]]}},{type:"Feature",properties:{iso1A2:"UG",iso1A3:"UGA",iso1N3:"800",wikidata:"Q1036",nameEn:"Uganda",groups:["014","202","002"],driveSide:"left",callingCodes:["256"]},geometry:{type:"MultiPolygon",coordinates:[[[[33.93107,-0.99298],[33.9264,-0.54188],[33.98449,-0.13079],[33.90936,0.10581],[34.10067,0.36372],[34.08727,0.44713],[34.11408,0.48884],[34.13493,0.58118],[34.20196,0.62289],[34.27345,0.63182],[34.31516,0.75693],[34.40041,0.80266],[34.43349,0.85254],[34.52369,1.10692],[34.57427,1.09868],[34.58029,1.14712],[34.67562,1.21265],[34.80223,1.22754],[34.82606,1.26626],[34.82606,1.30944],[34.7918,1.36752],[34.87819,1.5596],[34.92734,1.56109],[34.9899,1.6668],[34.98692,1.97348],[34.90947,2.42447],[34.95267,2.47209],[34.77244,2.70272],[34.78137,2.76223],[34.73967,2.85447],[34.65774,2.8753],[34.60114,2.93034],[34.56242,3.11478],[34.45815,3.18319],[34.40006,3.37949],[34.41794,3.44342],[34.39112,3.48802],[34.44922,3.51627],[34.45815,3.67385],[34.15429,3.80464],[34.06046,4.15235],[33.9873,4.23316],[33.51264,3.75068],[33.18356,3.77812],[33.02852,3.89296],[32.89746,3.81339],[32.72021,3.77327],[32.41337,3.748],[32.20782,3.6053],[32.19888,3.50867],[32.08866,3.53543],[32.08491,3.56287],[32.05187,3.589],[31.95907,3.57408],[31.96205,3.6499],[31.86821,3.78664],[31.81459,3.82083],[31.72075,3.74354],[31.50776,3.63652],[31.50478,3.67814],[31.29476,3.8015],[31.16666,3.79853],[30.97601,3.693],[30.85153,3.48867],[30.94081,3.50847],[30.93486,3.40737],[30.84251,3.26908],[30.77101,3.04897],[30.8574,2.9508],[30.8857,2.83923],[30.75612,2.5863],[30.74271,2.43601],[30.83059,2.42559],[30.91102,2.33332],[30.96911,2.41071],[31.06593,2.35862],[31.07934,2.30207],[31.12104,2.27676],[31.1985,2.29462],[31.20148,2.2217],[31.28042,2.17853],[31.30127,2.11006],[30.48503,1.21675],[30.24671,1.14974],[30.22139,0.99635],[30.1484,0.89805],[29.98307,0.84295],[29.95477,0.64486],[29.97413,0.52124],[29.87284,0.39166],[29.81922,0.16824],[29.77454,0.16675],[29.7224,0.07291],[29.72687,-0.08051],[29.65091,-0.46777],[29.67474,-0.47969],[29.67176,-0.55714],[29.62708,-0.71055],[29.63006,-0.8997],[29.58388,-0.89821],[29.59061,-1.39016],[29.82657,-1.31187],[29.912,-1.48269],[30.16369,-1.34303],[30.35212,-1.06896],[30.47194,-1.0555],[30.64166,-1.06601],[30.70631,-1.01175],[30.76635,-0.9852],[30.80408,-0.99911],[33.93107,-0.99298]]]]}},{type:"Feature",properties:{iso1A2:"UM",iso1A3:"UMI",iso1N3:"581",wikidata:"Q16645",nameEn:"United States Minor Outlying Islands",country:"US",groups:["057","009"]},geometry:{type:"MultiPolygon",coordinates:[[[[-175.33482,-1.40631],[-175.33167,1.67574],[-177.43928,1.65656],[-177.43039,-1.43294],[-175.33482,-1.40631]]],[[[-161.04969,-1.36251],[-158.62058,-1.35506],[-158.62734,1.1296],[-161.05669,1.11722],[-161.04969,-1.36251]]],[[[-161.06795,5.2462],[-161.0731,7.1291],[-163.24994,7.12322],[-163.24478,5.24198],[-161.06795,5.2462]]],[[[-170.65691,16.57199],[-168.87689,16.01159],[-169.2329,17.4933],[-170.65691,16.57199]]],[[[-176.29741,29.09786],[-177.77531,29.29793],[-177.5224,27.7635],[-176.29741,29.09786]]],[[[-74.7289,18.71009],[-75.71816,18.46438],[-74.76465,18.06252],[-74.7289,18.71009]]],[[[167.34779,18.97692],[166.67967,20.14834],[165.82549,18.97692],[167.34779,18.97692]]]]}},{type:"Feature",properties:{iso1A2:"US",iso1A3:"USA",iso1N3:"840",wikidata:"Q30",nameEn:"United States of America",groups:["021","003","019"],roadSpeedUnit:"mph",callingCodes:["1"]},geometry:{type:"MultiPolygon",coordinates:[[[[-177.8563,29.18961],[-179.49839,27.86265],[-151.6784,9.55515],[-154.05867,45.51124],[-177.5224,27.7635],[-177.8563,29.18961]]],[[[169.34848,52.47228],[180,51.0171],[179.84401,55.10087],[169.34848,52.47228]]],[[[-168.95635,65.98512],[-169.03888,65.48473],[-172.76104,63.77445],[-179.55295,57.62081],[-179.55295,50.81807],[-133.92876,54.62289],[-130.61931,54.70835],[-130.64499,54.76912],[-130.44184,54.85377],[-130.27203,54.97174],[-130.18765,55.07744],[-130.08035,55.21556],[-129.97513,55.28029],[-130.15373,55.74895],[-130.00857,55.91344],[-130.00093,56.00325],[-130.10173,56.12178],[-130.33965,56.10849],[-130.77769,56.36185],[-131.8271,56.62247],[-133.38523,58.42773],[-133.84645,58.73543],[-134.27175,58.8634],[-134.48059,59.13231],[-134.55699,59.1297],[-134.7047,59.2458],[-135.00267,59.28745],[-135.03069,59.56208],[-135.48007,59.79937],[-136.31566,59.59083],[-136.22381,59.55526],[-136.33727,59.44466],[-136.47323,59.46617],[-136.52365,59.16752],[-136.82619,59.16198],[-137.4925,58.89415],[-137.60623,59.24465],[-138.62145,59.76431],[-138.71149,59.90728],[-139.05365,59.99655],[-139.20603,60.08896],[-139.05831,60.35205],[-139.68991,60.33693],[-139.98024,60.18027],[-140.45648,60.30919],[-140.5227,60.22077],[-141.00116,60.30648],[-140.97446,84.39275],[-168.25765,71.99091],[-168.95635,65.98512]]],[[[-97.13927,25.96583],[-96.92418,25.97377],[-82.02215,24.23074],[-79.89631,24.6597],[-79.14818,27.83105],[-61.98255,37.34815],[-67.16117,44.20069],[-66.93432,44.82597],[-66.96824,44.83078],[-66.98249,44.87071],[-66.96824,44.90965],[-67.0216,44.95333],[-67.11316,45.11176],[-67.15965,45.16179],[-67.19603,45.16771],[-67.20349,45.1722],[-67.22751,45.16344],[-67.27039,45.1934],[-67.29748,45.18173],[-67.29754,45.14865],[-67.34927,45.122],[-67.48201,45.27351],[-67.42394,45.37969],[-67.50578,45.48971],[-67.42144,45.50584],[-67.43815,45.59162],[-67.6049,45.60725],[-67.80705,45.69528],[-67.80653,45.80022],[-67.75654,45.82324],[-67.80961,45.87531],[-67.75196,45.91814],[-67.78111,45.9392],[-67.78578,47.06473],[-67.87993,47.10377],[-67.94843,47.1925],[-68.23244,47.35712],[-68.37458,47.35851],[-68.38332,47.28723],[-68.57914,47.28431],[-68.60575,47.24659],[-68.70125,47.24399],[-68.89222,47.1807],[-69.05039,47.2456],[-69.05073,47.30076],[-69.05148,47.42012],[-69.22119,47.46461],[-69.99966,46.69543],[-70.05812,46.41768],[-70.18547,46.35357],[-70.29078,46.18832],[-70.23855,46.1453],[-70.31025,45.96424],[-70.24694,45.95138],[-70.25976,45.89675],[-70.41523,45.79497],[-70.38934,45.73215],[-70.54019,45.67291],[-70.68516,45.56964],[-70.72651,45.49771],[-70.62518,45.42286],[-70.65383,45.37592],[-70.78372,45.43269],[-70.82638,45.39828],[-70.80236,45.37444],[-70.84816,45.22698],[-70.89864,45.2398],[-70.91169,45.29849],[-70.95193,45.33895],[-71.0107,45.34819],[-71.01866,45.31573],[-71.08364,45.30623],[-71.14568,45.24128],[-71.19723,45.25438],[-71.22338,45.25184],[-71.29371,45.29996],[-71.37133,45.24624],[-71.44252,45.2361],[-71.40364,45.21382],[-71.42778,45.12624],[-71.48735,45.07784],[-71.50067,45.01357],[-73.35025,45.00942],[-74.32699,44.99029],[-74.66689,45.00646],[-74.8447,45.00606],[-74.99101,44.98051],[-75.01363,44.95608],[-75.2193,44.87821],[-75.41441,44.76614],[-75.76813,44.51537],[-75.8217,44.43176],[-75.95947,44.34463],[-76.00018,44.34896],[-76.16285,44.28262],[-76.1664,44.23051],[-76.244,44.19643],[-76.31222,44.19894],[-76.35324,44.13493],[-76.43859,44.09393],[-76.79706,43.63099],[-79.25796,43.54052],[-79.06921,43.26183],[-79.05512,43.25375],[-79.05544,43.21224],[-79.05002,43.20133],[-79.05384,43.17418],[-79.04652,43.16396],[-79.0427,43.13934],[-79.06881,43.12029],[-79.05671,43.10937],[-79.07486,43.07845],[-79.01055,43.06659],[-78.99941,43.05612],[-79.02424,43.01983],[-79.02074,42.98444],[-78.98126,42.97],[-78.96312,42.95509],[-78.93224,42.95229],[-78.90905,42.93022],[-78.90712,42.89733],[-78.93684,42.82887],[-82.67862,41.67615],[-83.11184,41.95671],[-83.14962,42.04089],[-83.12724,42.2376],[-83.09837,42.28877],[-83.07837,42.30978],[-83.02253,42.33045],[-82.82964,42.37355],[-82.64242,42.55594],[-82.58873,42.54984],[-82.57583,42.5718],[-82.51858,42.611],[-82.51063,42.66025],[-82.46613,42.76615],[-82.4826,42.8068],[-82.45331,42.93139],[-82.4253,42.95423],[-82.4146,42.97626],[-82.42469,42.992],[-82.48419,45.30225],[-83.59589,45.82131],[-83.43746,45.99749],[-83.57017,46.105],[-83.83329,46.12169],[-83.90453,46.05922],[-83.95399,46.05634],[-84.1096,46.23987],[-84.09756,46.25512],[-84.11615,46.2681],[-84.11254,46.32329],[-84.13451,46.39218],[-84.11196,46.50248],[-84.12885,46.53068],[-84.17723,46.52753],[-84.1945,46.54061],[-84.2264,46.53337],[-84.26351,46.49508],[-84.29893,46.49127],[-84.34174,46.50683],[-84.42101,46.49853],[-84.4481,46.48972],[-84.47607,46.45225],[-84.55635,46.45974],[-84.85871,46.88881],[-88.37033,48.30586],[-89.48837,48.01412],[-89.57972,48.00023],[-89.77248,48.02607],[-89.89974,47.98109],[-90.07418,48.11043],[-90.56312,48.09488],[-90.56444,48.12184],[-90.75045,48.09143],[-90.87588,48.2484],[-91.08016,48.18096],[-91.25025,48.08522],[-91.43248,48.04912],[-91.45829,48.07454],[-91.58025,48.04339],[-91.55649,48.10611],[-91.70451,48.11805],[-91.71231,48.19875],[-91.86125,48.21278],[-91.98929,48.25409],[-92.05339,48.35958],[-92.14732,48.36578],[-92.202,48.35252],[-92.26662,48.35651],[-92.30939,48.31251],[-92.27167,48.25046],[-92.37185,48.22259],[-92.48147,48.36609],[-92.45588,48.40624],[-92.50712,48.44921],[-92.65606,48.43471],[-92.71323,48.46081],[-92.69927,48.49573],[-92.62747,48.50278],[-92.6342,48.54133],[-92.7287,48.54005],[-92.94973,48.60866],[-93.25391,48.64266],[-93.33946,48.62787],[-93.3712,48.60599],[-93.39758,48.60364],[-93.40693,48.60948],[-93.44472,48.59147],[-93.47022,48.54357],[-93.66382,48.51845],[-93.79267,48.51631],[-93.80939,48.52439],[-93.80676,48.58232],[-93.83288,48.62745],[-93.85769,48.63284],[-94.23215,48.65202],[-94.25104,48.65729],[-94.25172,48.68404],[-94.27153,48.70232],[-94.4174,48.71049],[-94.44258,48.69223],[-94.53826,48.70216],[-94.54885,48.71543],[-94.58903,48.71803],[-94.69335,48.77883],[-94.69669,48.80918],[-94.70486,48.82365],[-94.70087,48.8339],[-94.687,48.84077],[-94.75017,49.09931],[-94.77355,49.11998],[-94.82487,49.29483],[-94.8159,49.32299],[-94.85381,49.32492],[-94.95681,49.37035],[-94.99532,49.36579],[-95.01419,49.35647],[-95.05825,49.35311],[-95.12903,49.37056],[-95.15357,49.384],[-95.15355,48.9996],[-97.24024,48.99952],[-101.36198,48.99935],[-104.05004,48.99925],[-110.0051,48.99901],[-114.0683,48.99885],[-116.04938,48.99999],[-117.03266,49.00056],[-123.32163,49.00419],[-123.0093,48.83186],[-123.0093,48.76586],[-123.26565,48.6959],[-123.15614,48.35395],[-123.50039,48.21223],[-125.03842,48.53282],[-133.98258,38.06389],[-118.48109,32.5991],[-117.1243,32.53427],[-115.88053,32.63624],[-114.71871,32.71894],[-114.76736,32.64094],[-114.80584,32.62028],[-114.81141,32.55543],[-114.79524,32.55731],[-114.82011,32.49609],[-112.34553,31.7357],[-111.07523,31.33232],[-109.05235,31.3333],[-108.20979,31.33316],[-108.20899,31.78534],[-106.529,31.784],[-106.52266,31.77509],[-106.51251,31.76922],[-106.50962,31.76155],[-106.50111,31.75714],[-106.48815,31.74769],[-106.47298,31.75054],[-106.46726,31.75998],[-106.45244,31.76523],[-106.43419,31.75478],[-106.41773,31.75196],[-106.38003,31.73151],[-106.3718,31.71165],[-106.34864,31.69663],[-106.33419,31.66303],[-106.30305,31.62154],[-106.28084,31.56173],[-106.24612,31.54193],[-106.23711,31.51262],[-106.20346,31.46305],[-106.09025,31.40569],[-106.00363,31.39181],[-104.77674,30.4236],[-104.5171,29.64671],[-104.3969,29.57105],[-104.39363,29.55396],[-104.37752,29.54255],[-103.15787,28.93865],[-102.60596,29.8192],[-101.47277,29.7744],[-101.05686,29.44738],[-101.01128,29.36947],[-100.96725,29.3477],[-100.94579,29.34523],[-100.94056,29.33371],[-100.87982,29.296],[-100.79696,29.24688],[-100.67294,29.09744],[-100.63689,28.90812],[-100.59809,28.88197],[-100.52313,28.75598],[-100.5075,28.74066],[-100.51222,28.70679],[-100.50029,28.66117],[-99.55409,27.61314],[-99.51478,27.55836],[-99.52955,27.49747],[-99.50208,27.50021],[-99.48045,27.49016],[-99.482,27.47128],[-99.49744,27.43746],[-99.53573,27.30926],[-99.08477,26.39849],[-99.03053,26.41249],[-99.00546,26.3925],[-98.35126,26.15129],[-98.30491,26.10475],[-98.27075,26.09457],[-98.24603,26.07191],[-97.97017,26.05232],[-97.95155,26.0625],[-97.66511,26.01708],[-97.52025,25.88518],[-97.49828,25.89877],[-97.45669,25.86874],[-97.42511,25.83969],[-97.37332,25.83854],[-97.35946,25.92189],[-97.13927,25.96583]]]]}},{type:"Feature",properties:{iso1A2:"UY",iso1A3:"URY",iso1N3:"858",wikidata:"Q77",nameEn:"Uruguay",groups:["005","419","019"],callingCodes:["598"]},geometry:{type:"MultiPolygon",coordinates:[[[[-57.65132,-30.19229],[-57.61478,-30.25165],[-57.64859,-30.35095],[-57.89115,-30.49572],[-57.8024,-30.77193],[-57.89476,-30.95994],[-57.86729,-31.06352],[-57.9908,-31.34924],[-57.98127,-31.3872],[-58.07569,-31.44916],[-58.0023,-31.53084],[-58.00076,-31.65016],[-58.20252,-31.86966],[-58.10036,-32.25338],[-58.22362,-32.52416],[-58.1224,-32.98842],[-58.40475,-33.11777],[-58.44442,-33.84033],[-58.34425,-34.15035],[-57.83001,-34.69099],[-54.78916,-36.21945],[-52.83257,-34.01481],[-53.37138,-33.74313],[-53.39593,-33.75169],[-53.44031,-33.69344],[-53.52794,-33.68908],[-53.53459,-33.16843],[-53.1111,-32.71147],[-53.37671,-32.57005],[-53.39572,-32.58596],[-53.76024,-32.0751],[-54.17384,-31.86168],[-55.50821,-30.91349],[-55.50841,-30.9027],[-55.51862,-30.89828],[-55.52712,-30.89997],[-55.53276,-30.90218],[-55.53431,-30.89714],[-55.54572,-30.89051],[-55.55218,-30.88193],[-55.55373,-30.8732],[-55.5634,-30.8686],[-55.58866,-30.84117],[-55.87388,-31.05053],[-56.4619,-30.38457],[-56.4795,-30.3899],[-56.49267,-30.39471],[-56.90236,-30.02578],[-57.22502,-30.26121],[-57.65132,-30.19229]]]]}},{type:"Feature",properties:{iso1A2:"UZ",iso1A3:"UZB",iso1N3:"860",wikidata:"Q265",nameEn:"Uzbekistan",groups:["143","142"],callingCodes:["998"]},geometry:{type:"MultiPolygon",coordinates:[[[[65.85194,42.85481],[65.53277,43.31856],[65.18666,43.48835],[64.96464,43.74748],[64.53885,43.56941],[63.34656,43.64003],[62.01711,43.51008],[61.01475,44.41383],[58.59711,45.58671],[55.97842,44.99622],[55.97832,44.99622],[55.97822,44.99617],[55.97811,44.99617],[55.97801,44.99612],[55.97801,44.99607],[55.97791,44.99607],[55.9778,44.99607],[55.9777,44.99601],[55.9777,44.99596],[55.9776,44.99591],[55.97749,44.99591],[55.97739,44.99591],[55.97739,44.99586],[55.97729,44.99586],[55.97718,44.99581],[55.97708,44.99576],[55.97698,44.9957],[55.97698,44.99565],[55.97687,44.9956],[55.97677,44.9956],[55.97677,44.99555],[55.97677,44.9955],[55.97667,44.99545],[55.97656,44.99539],[55.97646,44.99534],[55.97646,44.99529],[55.97636,44.99524],[55.97636,44.99519],[55.97625,44.99514],[55.97615,44.99508],[55.97615,44.99503],[55.97615,44.99498],[55.97615,44.99493],[55.97615,44.99483],[55.97615,44.99477],[55.97605,44.99477],[55.97605,44.99467],[55.97605,44.99462],[55.97605,44.99457],[55.97605,44.99452],[55.97594,44.99446],[55.97584,44.99441],[55.97584,44.99436],[55.97584,44.99431],[55.97584,44.99426],[55.97584,44.99421],[55.97584,44.99415],[55.97584,44.99405],[55.97584,44.994],[55.97584,44.9939],[55.97584,44.99384],[55.97584,44.99374],[55.97584,44.99369],[55.97584,44.99359],[55.97584,44.99353],[55.97584,44.99348],[55.97584,44.99343],[55.97584,44.99338],[55.97584,44.99328],[55.97584,44.99322],[56.00314,41.32584],[57.03423,41.25435],[57.13796,41.36625],[57.03359,41.41777],[56.96218,41.80383],[57.03633,41.92043],[57.30275,42.14076],[57.6296,42.16519],[57.84932,42.18555],[57.92897,42.24047],[57.90975,42.4374],[57.99214,42.50021],[58.3492,42.43335],[58.40688,42.29535],[58.51674,42.30348],[58.29427,42.56497],[58.14321,42.62159],[58.27504,42.69632],[58.57991,42.64988],[58.6266,42.79314],[58.93422,42.5407],[59.17317,42.52248],[59.2955,42.37064],[59.4341,42.29738],[59.94633,42.27655],[60.00539,42.212],[59.96419,42.1428],[60.04659,42.08982],[60.0356,42.01028],[59.95046,41.97966],[60.33223,41.75058],[60.08504,41.80997],[60.06032,41.76287],[60.18117,41.60082],[60.06581,41.4363],[60.5078,41.21694],[61.03261,41.25691],[61.22212,41.14946],[61.33199,41.14946],[61.39732,41.19873],[61.4446,41.29407],[61.87856,41.12257],[62.11751,40.58242],[62.34273,40.43206],[62.43337,39.98528],[63.6913,39.27666],[63.70778,39.22349],[64.19086,38.95561],[64.32576,38.98691],[65.55873,38.29052],[65.83913,38.25733],[66.24013,38.16238],[66.41042,38.02403],[66.56697,38.0435],[66.67684,37.96776],[66.53676,37.80084],[66.52852,37.58568],[66.65761,37.45497],[66.52303,37.39827],[66.55743,37.35409],[66.64699,37.32958],[66.95598,37.40162],[67.08232,37.35469],[67.13039,37.27168],[67.2224,37.24545],[67.2581,37.17216],[67.51868,37.26102],[67.78329,37.1834],[67.8474,37.31594],[67.81566,37.43107],[68.12635,37.93],[68.27159,37.91477],[68.40343,38.19484],[68.13289,38.40822],[68.06274,38.39435],[68.11366,38.47169],[68.05873,38.56087],[68.0807,38.64136],[68.05598,38.71641],[68.12877,38.73677],[68.06948,38.82115],[68.19743,38.85985],[68.09704,39.02589],[67.68915,39.00775],[67.67833,39.14479],[67.33226,39.23739],[67.36522,39.31287],[67.45998,39.315],[67.46822,39.46146],[67.39681,39.52505],[67.46547,39.53564],[67.44899,39.57799],[67.62889,39.60234],[67.70992,39.66156],[68.12053,39.56317],[68.54166,39.53929],[68.61972,39.68905],[68.63071,39.85265],[68.88889,39.87163],[68.93695,39.91167],[68.84906,40.04952],[68.96579,40.06949],[69.01935,40.11466],[69.01523,40.15771],[68.62796,40.07789],[68.52771,40.11676],[68.5332,40.14826],[68.77902,40.20492],[68.79276,40.17555],[68.84357,40.18604],[68.85832,40.20885],[69.04544,40.22904],[69.15659,40.2162],[69.2074,40.21488],[69.30448,40.18774],[69.30104,40.24502],[69.25229,40.26362],[69.24817,40.30357],[69.30808,40.2821],[69.32833,40.29794],[69.33794,40.34819],[69.30774,40.36102],[69.28525,40.41894],[69.27066,40.49274],[69.21063,40.54469],[69.2643,40.57506],[69.3455,40.57988],[69.32834,40.70233],[69.38327,40.7918],[69.53021,40.77621],[69.59441,40.70181],[69.69434,40.62615],[70.36655,40.90296],[70.38028,41.02014],[70.45251,41.04438],[70.80009,40.72825],[70.49871,40.52503],[70.32626,40.45174],[70.37511,40.38605],[70.57149,40.3442],[70.56394,40.26421],[70.62342,40.17396],[70.8607,40.217],[70.9818,40.22392],[70.95789,40.28761],[71.05901,40.28765],[71.13042,40.34106],[71.36663,40.31593],[71.4246,40.28619],[71.51215,40.26943],[71.51549,40.22986],[71.61725,40.20615],[71.61931,40.26775],[71.68386,40.26984],[71.70569,40.20391],[71.69621,40.18492],[71.71719,40.17886],[71.73054,40.14818],[71.82646,40.21872],[71.85002,40.25647],[72.05464,40.27586],[71.96401,40.31907],[72.18648,40.49893],[72.24368,40.46091],[72.40346,40.4007],[72.44191,40.48222],[72.41513,40.50856],[72.38384,40.51535],[72.41714,40.55736],[72.34406,40.60144],[72.40517,40.61917],[72.47795,40.5532],[72.66713,40.5219],[72.66713,40.59076],[72.69579,40.59778],[72.73995,40.58409],[72.74768,40.58051],[72.74862,40.57131],[72.75982,40.57273],[72.74894,40.59592],[72.74866,40.60873],[72.80137,40.67856],[72.84754,40.67229],[72.85372,40.7116],[72.8722,40.71111],[72.93296,40.73089],[72.99133,40.76457],[73.0612,40.76678],[73.13412,40.79122],[73.13267,40.83512],[73.01869,40.84681],[72.94454,40.8094],[72.84291,40.85512],[72.68157,40.84942],[72.59136,40.86947],[72.55109,40.96046],[72.48742,40.97136],[72.45206,41.03018],[72.38511,41.02785],[72.36138,41.04384],[72.34757,41.06104],[72.34026,41.04539],[72.324,41.03381],[72.18339,40.99571],[72.17594,41.02377],[72.21061,41.05607],[72.1792,41.10621],[72.14864,41.13363],[72.17594,41.15522],[72.16433,41.16483],[72.10745,41.15483],[72.07249,41.11739],[71.85964,41.19081],[71.91457,41.2982],[71.83914,41.3546],[71.76625,41.4466],[71.71132,41.43012],[71.73054,41.54713],[71.65914,41.49599],[71.6787,41.42111],[71.57227,41.29175],[71.46688,41.31883],[71.43814,41.19644],[71.46148,41.13958],[71.40198,41.09436],[71.34877,41.16807],[71.27187,41.11015],[71.25813,41.18796],[71.11806,41.15359],[71.02193,41.19494],[70.9615,41.16393],[70.86263,41.23833],[70.77885,41.24813],[70.78572,41.36419],[70.67586,41.47953],[70.48909,41.40335],[70.17682,41.5455],[70.69777,41.92554],[71.28719,42.18033],[71.13263,42.28356],[70.94483,42.26238],[69.49545,41.545],[69.45751,41.56863],[69.39485,41.51518],[69.45081,41.46246],[69.37468,41.46555],[69.35554,41.47211],[69.29778,41.43673],[69.25059,41.46693],[69.23332,41.45847],[69.22671,41.46298],[69.20439,41.45391],[69.18528,41.45175],[69.17701,41.43769],[69.15137,41.43078],[69.05006,41.36183],[69.01308,41.22804],[68.7217,41.05025],[68.73945,40.96989],[68.65662,40.93861],[68.62221,41.03019],[68.49983,40.99669],[68.58444,40.91447],[68.63,40.59358],[68.49983,40.56437],[67.96736,40.83798],[68.1271,41.0324],[68.08273,41.08148],[67.98511,41.02794],[67.9644,41.14611],[66.69129,41.1311],[66.53302,41.87388],[66.00546,41.94455],[66.09482,42.93426],[65.85194,42.85481]],[[70.68112,40.90612],[70.6721,40.90555],[70.57501,40.98941],[70.54223,40.98787],[70.56077,41.00642],[70.6158,40.97661],[70.68112,40.90612]]],[[[71.21139,40.03369],[71.12218,40.03052],[71.06305,40.1771],[71.00236,40.18154],[71.01035,40.05481],[71.11037,40.01984],[71.11668,39.99291],[71.09063,39.99],[71.10501,39.95568],[71.04979,39.89808],[71.10531,39.91354],[71.16101,39.88423],[71.23067,39.93581],[71.1427,39.95026],[71.21139,40.03369]]],[[[71.86463,39.98598],[71.78838,40.01404],[71.71511,39.96348],[71.7504,39.93701],[71.84316,39.95582],[71.86463,39.98598]]]]}},{type:"Feature",properties:{iso1A2:"VA",iso1A3:"VAT",iso1N3:"336",wikidata:"Q237",nameEn:"Vatican City",aliases:["Holy See"],groups:["039","150"],callingCodes:["379","39 06"]},geometry:{type:"MultiPolygon",coordinates:[[[[12.45181,41.90056],[12.45446,41.90028],[12.45435,41.90143],[12.45626,41.90172],[12.45691,41.90125],[12.4577,41.90115],[12.45834,41.90174],[12.45826,41.90281],[12.45755,41.9033],[12.45762,41.9058],[12.45561,41.90629],[12.45543,41.90738],[12.45091,41.90625],[12.44984,41.90545],[12.44815,41.90326],[12.44582,41.90194],[12.44834,41.90095],[12.45181,41.90056]]]]}},{type:"Feature",properties:{iso1A2:"VC",iso1A3:"VCT",iso1N3:"670",wikidata:"Q757",nameEn:"St. Vincent and the Grenadines",aliases:["WV"],groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 784"]},geometry:{type:"MultiPolygon",coordinates:[[[[-61.73897,12.61191],[-61.38256,12.52991],[-61.13395,12.51526],[-60.70539,13.41452],[-61.43129,13.68336],[-61.73897,12.61191]]]]}},{type:"Feature",properties:{iso1A2:"VE",iso1A3:"VEN",iso1N3:"862",wikidata:"Q717",nameEn:"Venezuela",aliases:["YV"],groups:["005","419","019"],callingCodes:["58"]},geometry:{type:"MultiPolygon",coordinates:[[[[-71.22331,13.01387],[-70.92579,11.96275],[-71.3275,11.85],[-71.9675,11.65536],[-72.24983,11.14138],[-72.4767,11.1117],[-72.88002,10.44309],[-72.98085,9.85253],[-73.36905,9.16636],[-73.02119,9.27584],[-72.94052,9.10663],[-72.77415,9.10165],[-72.65474,8.61428],[-72.4042,8.36513],[-72.36987,8.19976],[-72.35163,8.01163],[-72.39137,8.03534],[-72.47213,7.96106],[-72.48801,7.94329],[-72.48183,7.92909],[-72.47042,7.92306],[-72.45806,7.91141],[-72.46183,7.90682],[-72.44454,7.86031],[-72.46763,7.79518],[-72.47827,7.65604],[-72.45321,7.57232],[-72.47415,7.48928],[-72.43132,7.40034],[-72.19437,7.37034],[-72.04895,7.03837],[-71.82441,7.04314],[-71.44118,7.02116],[-71.42212,7.03854],[-71.37234,7.01588],[-71.03941,6.98163],[-70.7596,7.09799],[-70.10716,6.96516],[-69.41843,6.1072],[-67.60654,6.2891],[-67.4625,6.20625],[-67.43513,5.98835],[-67.58558,5.84537],[-67.63914,5.64963],[-67.59141,5.5369],[-67.83341,5.31104],[-67.85358,4.53249],[-67.62671,3.74303],[-67.50067,3.75812],[-67.30945,3.38393],[-67.85862,2.86727],[-67.85862,2.79173],[-67.65696,2.81691],[-67.21967,2.35778],[-66.85795,1.22998],[-66.28507,0.74585],[-65.6727,1.01353],[-65.50158,0.92086],[-65.57288,0.62856],[-65.11657,1.12046],[-64.38932,1.5125],[-64.34654,1.35569],[-64.08274,1.64792],[-64.06135,1.94722],[-63.39827,2.16098],[-63.39114,2.4317],[-64.0257,2.48156],[-64.02908,2.79797],[-64.48379,3.7879],[-64.84028,4.24665],[-64.72977,4.28931],[-64.57648,4.12576],[-64.14512,4.12932],[-63.99183,3.90172],[-63.86082,3.94796],[-63.70218,3.91417],[-63.67099,4.01731],[-63.50611,3.83592],[-63.42233,3.89995],[-63.4464,3.9693],[-63.21111,3.96219],[-62.98296,3.59935],[-62.7655,3.73099],[-62.74411,4.03331],[-62.57656,4.04754],[-62.44822,4.18621],[-62.13094,4.08309],[-61.54629,4.2822],[-61.48569,4.43149],[-61.29675,4.44216],[-61.31457,4.54167],[-61.15703,4.49839],[-60.98303,4.54167],[-60.86539,4.70512],[-60.5802,4.94312],[-60.73204,5.20931],[-61.4041,5.95304],[-61.15058,6.19558],[-61.20762,6.58174],[-61.13632,6.70922],[-60.54873,6.8631],[-60.39419,6.94847],[-60.28074,7.1162],[-60.44116,7.20817],[-60.54098,7.14804],[-60.63367,7.25061],[-60.59802,7.33194],[-60.71923,7.55817],[-60.64793,7.56877],[-60.51959,7.83373],[-60.38056,7.8302],[-60.02407,8.04557],[-59.97059,8.20791],[-59.83156,8.23261],[-59.80661,8.28906],[-59.85562,8.35213],[-59.98508,8.53046],[-59.54058,8.6862],[-60.89962,9.81445],[-62.08693,10.04435],[-61.62505,11.18974],[-63.73917,11.92623],[-63.19938,16.44103],[-67.89186,12.4116],[-68.01417,11.77722],[-68.33524,11.78151],[-68.99639,11.79035],[-71.22331,13.01387]]]]}},{type:"Feature",properties:{iso1A2:"VG",iso1A3:"VGB",iso1N3:"092",wikidata:"Q25305",nameEn:"British Virgin Islands",country:"GB",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 284"]},geometry:{type:"MultiPolygon",coordinates:[[[[-64.03057,18.08241],[-63.75633,19.39745],[-65.02435,18.73231],[-64.86027,18.39056],[-64.64067,18.36478],[-64.646,18.10286],[-64.03057,18.08241]]]]}},{type:"Feature",properties:{iso1A2:"VI",iso1A3:"VIR",iso1N3:"850",wikidata:"Q11703",nameEn:"United States Virgin Islands",country:"US",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 340"]},geometry:{type:"MultiPolygon",coordinates:[[[[-65.02435,18.73231],[-65.27974,17.56928],[-64.35558,17.48384],[-64.646,18.10286],[-64.64067,18.36478],[-64.86027,18.39056],[-65.02435,18.73231]]]]}},{type:"Feature",properties:{iso1A2:"VN",iso1A3:"VNM",iso1N3:"704",wikidata:"Q881",nameEn:"Vietnam",groups:["035","142"],callingCodes:["84"]},geometry:{type:"MultiPolygon",coordinates:[[[[108.10003,21.47338],[108.0569,21.53604],[108.02926,21.54997],[107.97932,21.54503],[107.97383,21.53961],[107.97074,21.54072],[107.96774,21.53601],[107.95232,21.5388],[107.92652,21.58906],[107.90006,21.5905],[107.86114,21.65128],[107.80355,21.66141],[107.66967,21.60787],[107.56537,21.61945],[107.54047,21.5934],[107.49065,21.59774],[107.49532,21.62958],[107.47197,21.6672],[107.41593,21.64839],[107.38636,21.59774],[107.35989,21.60063],[107.35834,21.6672],[107.29296,21.74674],[107.24625,21.7077],[107.20734,21.71493],[107.10771,21.79879],[107.02615,21.81981],[107.00964,21.85948],[107.06101,21.88982],[107.05634,21.92303],[106.99252,21.95191],[106.97228,21.92592],[106.92714,21.93459],[106.9178,21.97357],[106.81038,21.97934],[106.74345,22.00965],[106.72551,21.97923],[106.69276,21.96013],[106.68274,21.99811],[106.70142,22.02409],[106.6983,22.15102],[106.67495,22.1885],[106.69986,22.22309],[106.6516,22.33977],[106.55976,22.34841],[106.57221,22.37],[106.55665,22.46498],[106.58395,22.474],[106.61269,22.60301],[106.65316,22.5757],[106.71698,22.58432],[106.72321,22.63606],[106.76293,22.73491],[106.82404,22.7881],[106.83685,22.8098],[106.81271,22.8226],[106.78422,22.81532],[106.71128,22.85982],[106.71387,22.88296],[106.6734,22.89587],[106.6516,22.86862],[106.60179,22.92884],[106.55976,22.92311],[106.51306,22.94891],[106.49749,22.91164],[106.34961,22.86718],[106.27022,22.87722],[106.19705,22.98475],[106.00179,22.99049],[105.99568,22.94178],[105.90119,22.94168],[105.8726,22.92756],[105.72382,23.06641],[105.57594,23.075],[105.56037,23.16806],[105.49966,23.20669],[105.42805,23.30824],[105.40782,23.28107],[105.32376,23.39684],[105.22569,23.27249],[105.17276,23.28679],[105.11672,23.25247],[105.07002,23.26248],[104.98712,23.19176],[104.96532,23.20463],[104.9486,23.17235],[104.91435,23.18666],[104.87992,23.17141],[104.87382,23.12854],[104.79478,23.12934],[104.8334,23.01484],[104.86765,22.95178],[104.84942,22.93631],[104.77114,22.90017],[104.72755,22.81984],[104.65283,22.83419],[104.60457,22.81841],[104.58122,22.85571],[104.47225,22.75813],[104.35593,22.69353],[104.25683,22.76534],[104.27084,22.8457],[104.11384,22.80363],[104.03734,22.72945],[104.01088,22.51823],[103.99247,22.51958],[103.97384,22.50634],[103.96783,22.51173],[103.96352,22.50584],[103.95191,22.5134],[103.94513,22.52553],[103.93286,22.52703],[103.87904,22.56683],[103.64506,22.79979],[103.56255,22.69499],[103.57812,22.65764],[103.52675,22.59155],[103.43646,22.70648],[103.43179,22.75816],[103.32282,22.8127],[103.28079,22.68063],[103.18895,22.64471],[103.15782,22.59873],[103.17961,22.55705],[103.07843,22.50097],[103.0722,22.44775],[102.9321,22.48659],[102.8636,22.60735],[102.60675,22.73376],[102.57095,22.7036],[102.51802,22.77969],[102.46665,22.77108],[102.42618,22.69212],[102.38415,22.67919],[102.41061,22.64184],[102.25339,22.4607],[102.26428,22.41321],[102.16621,22.43336],[102.14099,22.40092],[102.18712,22.30403],[102.51734,22.02676],[102.49092,21.99002],[102.62301,21.91447],[102.67145,21.65894],[102.74189,21.66713],[102.82115,21.73667],[102.81894,21.83888],[102.85637,21.84501],[102.86077,21.71213],[102.97965,21.74076],[102.98846,21.58936],[102.86297,21.4255],[102.94223,21.46034],[102.88939,21.3107],[102.80794,21.25736],[102.89825,21.24707],[102.97745,21.05821],[103.03469,21.05821],[103.12055,20.89994],[103.21497,20.89832],[103.38032,20.79501],[103.45737,20.82382],[103.68633,20.66324],[103.73478,20.6669],[103.82282,20.8732],[103.98024,20.91531],[104.11121,20.96779],[104.27412,20.91433],[104.63957,20.6653],[104.38199,20.47155],[104.40621,20.3849],[104.47886,20.37459],[104.66158,20.47774],[104.72102,20.40554],[104.62195,20.36633],[104.61315,20.24452],[104.86852,20.14121],[104.91695,20.15567],[104.9874,20.09573],[104.8465,19.91783],[104.8355,19.80395],[104.68359,19.72729],[104.64837,19.62365],[104.53169,19.61743],[104.41281,19.70035],[104.23229,19.70242],[104.06498,19.66926],[104.05617,19.61743],[104.10832,19.51575],[104.06058,19.43484],[103.87125,19.31854],[104.5361,18.97747],[104.64617,18.85668],[105.12829,18.70453],[105.19654,18.64196],[105.1327,18.58355],[105.10408,18.43533],[105.15942,18.38691],[105.38366,18.15315],[105.46292,18.22008],[105.64784,17.96687],[105.60381,17.89356],[105.76612,17.67147],[105.85744,17.63221],[106.09019,17.36399],[106.18991,17.28227],[106.24444,17.24714],[106.29287,17.3018],[106.31929,17.20509],[106.43597,17.01362],[106.50862,16.9673],[106.55045,17.0031],[106.54824,16.92729],[106.51963,16.92097],[106.52183,16.87884],[106.55265,16.86831],[106.55485,16.68704],[106.59013,16.62259],[106.58267,16.6012],[106.61477,16.60713],[106.66052,16.56892],[106.65832,16.47816],[106.74418,16.41904],[106.84104,16.55415],[106.88727,16.52671],[106.88067,16.43594],[106.96638,16.34938],[106.97385,16.30204],[107.02597,16.31132],[107.09091,16.3092],[107.15035,16.26271],[107.14595,16.17816],[107.25822,16.13587],[107.33968,16.05549],[107.44975,16.08511],[107.46296,16.01106],[107.39471,15.88829],[107.34188,15.89464],[107.21419,15.83747],[107.21859,15.74638],[107.27143,15.71459],[107.27583,15.62769],[107.34408,15.62345],[107.3815,15.49832],[107.50699,15.48771],[107.53341,15.40496],[107.62367,15.42193],[107.60605,15.37524],[107.62587,15.2266],[107.58844,15.20111],[107.61926,15.13949],[107.61486,15.0566],[107.46516,15.00982],[107.48277,14.93751],[107.59285,14.87795],[107.51579,14.79282],[107.54361,14.69092],[107.55371,14.628],[107.52102,14.59034],[107.52569,14.54665],[107.48521,14.40346],[107.44941,14.41552],[107.39493,14.32655],[107.40427,14.24509],[107.33577,14.11832],[107.37158,14.07906],[107.35757,14.02319],[107.38247,13.99147],[107.44318,13.99751],[107.46498,13.91593],[107.45252,13.78897],[107.53503,13.73908],[107.61909,13.52577],[107.62843,13.3668],[107.49144,13.01215],[107.49611,12.88926],[107.55993,12.7982],[107.5755,12.52177],[107.55059,12.36824],[107.4463,12.29373],[107.42917,12.24657],[107.34511,12.33327],[107.15831,12.27547],[106.99953,12.08983],[106.92325,12.06548],[106.79405,12.0807],[106.70687,11.96956],[106.4111,11.97413],[106.4687,11.86751],[106.44068,11.86294],[106.44535,11.8279],[106.41577,11.76999],[106.45158,11.68616],[106.44691,11.66787],[106.37219,11.69836],[106.30525,11.67549],[106.26478,11.72122],[106.18539,11.75171],[106.13158,11.73283],[106.06708,11.77761],[106.02038,11.77457],[106.00792,11.7197],[105.95188,11.63738],[105.88962,11.67854],[105.8507,11.66635],[105.80867,11.60536],[105.81645,11.56876],[105.87328,11.55953],[105.88962,11.43605],[105.86782,11.28343],[106.10444,11.07879],[106.1527,11.10476],[106.1757,11.07301],[106.20095,10.97795],[106.14301,10.98176],[106.18539,10.79451],[106.06708,10.8098],[105.94535,10.9168],[105.93403,10.83853],[105.84603,10.85873],[105.86376,10.89839],[105.77751,11.03671],[105.50045,10.94586],[105.42884,10.96878],[105.34011,10.86179],[105.11449,10.96332],[105.08326,10.95656],[105.02722,10.89236],[105.09571,10.72722],[104.95094,10.64003],[104.87933,10.52833],[104.59018,10.53073],[104.49869,10.4057],[104.47963,10.43046],[104.43778,10.42386],[103.99198,10.48391],[102.47649,9.66162],[104.81582,8.03101],[109.55486,8.10026],[111.60491,13.57105],[108.00365,17.98159],[108.10003,21.47338]]]]}},{type:"Feature",properties:{iso1A2:"VU",iso1A3:"VUT",iso1N3:"548",wikidata:"Q686",nameEn:"Vanuatu",groups:["054","009"],callingCodes:["678"]},geometry:{type:"MultiPolygon",coordinates:[[[[162.93363,-17.28904],[173.26254,-22.69968],[168.21179,-12.88558],[166.02864,-12.9396],[162.93363,-17.28904]]]]}},{type:"Feature",properties:{iso1A2:"WF",iso1A3:"WLF",iso1N3:"876",wikidata:"Q35555",nameEn:"Wallis and Futuna",country:"FR",groups:["061","009"],callingCodes:["681"]},geometry:{type:"MultiPolygon",coordinates:[[[[-178.60161,-14.95666],[-176.76826,-14.95183],[-174.17905,-14.94502],[-174.18596,-12.48057],[-178.60852,-12.49232],[-178.60161,-14.95666]]]]}},{type:"Feature",properties:{iso1A2:"WS",iso1A3:"WSM",iso1N3:"882",wikidata:"Q683",nameEn:"Samoa",groups:["061","009"],driveSide:"left",callingCodes:["685"]},geometry:{type:"MultiPolygon",coordinates:[[[[-174.17905,-14.94502],[-173.13438,-14.94228],[-171.14262,-14.93704],[-171.14953,-12.4725],[-174.18596,-12.48057],[-174.17905,-14.94502]]]]}},{type:"Feature",properties:{iso1A2:"XK",iso1A3:"XKX",wikidata:"Q1246",nameEn:"Kosovo",aliases:["KV"],groups:["039","150"],isoStatus:"usrAssn",callingCodes:["383"]},geometry:{type:"MultiPolygon",coordinates:[[[[21.39045,42.74888],[21.44047,42.87276],[21.36941,42.87397],[21.32974,42.90424],[21.2719,42.8994],[21.23534,42.95523],[21.23877,43.00848],[21.2041,43.02277],[21.16734,42.99694],[21.14465,43.11089],[21.08952,43.13471],[21.05378,43.10707],[21.00749,43.13984],[20.96287,43.12416],[20.83727,43.17842],[20.88685,43.21697],[20.82145,43.26769],[20.73811,43.25068],[20.68688,43.21335],[20.59929,43.20492],[20.69515,43.09641],[20.64557,43.00826],[20.59929,43.01067],[20.48692,42.93208],[20.53484,42.8885],[20.43734,42.83157],[20.40594,42.84853],[20.35692,42.8335],[20.27869,42.81945],[20.2539,42.76245],[20.04898,42.77701],[20.02088,42.74789],[20.02915,42.71147],[20.0969,42.65559],[20.07761,42.55582],[20.17127,42.50469],[20.21797,42.41237],[20.24399,42.32168],[20.34479,42.32656],[20.3819,42.3029],[20.48857,42.25444],[20.56955,42.12097],[20.55633,42.08173],[20.59434,42.03879],[20.63069,41.94913],[20.57946,41.91593],[20.59524,41.8818],[20.68523,41.85318],[20.76786,41.91839],[20.75464,42.05229],[21.11491,42.20794],[21.16614,42.19815],[21.22728,42.08909],[21.31983,42.10993],[21.29913,42.13954],[21.30496,42.1418],[21.38428,42.24465],[21.43882,42.23609],[21.43882,42.2789],[21.50823,42.27156],[21.52145,42.24465],[21.58992,42.25915],[21.56772,42.30946],[21.5264,42.33634],[21.53467,42.36809],[21.57021,42.3647],[21.59029,42.38042],[21.62887,42.37664],[21.64209,42.41081],[21.62556,42.45106],[21.7035,42.51899],[21.70522,42.54176],[21.7327,42.55041],[21.75672,42.62695],[21.79413,42.65923],[21.75025,42.70125],[21.6626,42.67813],[21.58755,42.70418],[21.59154,42.72643],[21.47498,42.74695],[21.39045,42.74888]]]]}},{type:"Feature",properties:{iso1A2:"YE",iso1A3:"YEM",iso1N3:"887",wikidata:"Q805",nameEn:"Yemen",groups:["145","142"],callingCodes:["967"]},geometry:{type:"MultiPolygon",coordinates:[[[[53.32998,16.16312],[53.09917,16.67084],[52.81185,17.28568],[52.74267,17.29519],[52.78009,17.35124],[52.00311,19.00083],[49.04884,18.59899],[48.19996,18.20584],[47.58351,17.50366],[47.48245,17.10808],[47.00571,16.94765],[46.76494,17.29151],[46.31018,17.20464],[44.50126,17.47475],[43.70631,17.35762],[43.43005,17.56148],[43.29185,17.53224],[43.22533,17.38343],[43.32653,17.31179],[43.20156,17.25901],[43.17787,17.14717],[43.23967,17.03428],[43.18233,17.02673],[43.1813,16.98438],[43.19328,16.94703],[43.1398,16.90696],[43.18338,16.84852],[43.22012,16.83932],[43.22956,16.80613],[43.24801,16.80613],[43.26303,16.79479],[43.25857,16.75304],[43.21325,16.74416],[43.22066,16.65179],[43.15274,16.67248],[43.11601,16.53166],[42.97215,16.51093],[42.94351,16.49467],[42.94625,16.39721],[42.76801,16.40371],[42.15205,16.40211],[41.37609,16.19728],[41.29956,15.565],[42.63806,13.58268],[43.29075,12.79154],[43.32909,12.59711],[43.90659,12.3823],[50.51849,13.0483],[51.12877,12.56479],[52.253,11.68582],[55.69862,12.12478],[53.32998,16.16312]]]]}},{type:"Feature",properties:{iso1A2:"YT",iso1A3:"MYT",iso1N3:"175",wikidata:"Q17063",nameEn:"Mayotte",country:"FR",groups:["EU","014","202","002"],callingCodes:["262"]},geometry:{type:"MultiPolygon",coordinates:[[[[43.83794,-13.66915],[45.54824,-13.22353],[45.50237,-11.90315],[43.83794,-13.66915]]]]}},{type:"Feature",properties:{iso1A2:"ZA",iso1A3:"ZAF",iso1N3:"710",wikidata:"Q258",nameEn:"South Africa",groups:["018","202","002"],driveSide:"left",callingCodes:["27"]},geometry:{type:"MultiPolygon",coordinates:[[[[31.30611,-22.422],[31.16344,-22.32599],[31.08932,-22.34884],[30.86696,-22.28907],[30.6294,-22.32599],[30.48686,-22.31368],[30.38614,-22.34533],[30.28351,-22.35587],[30.2265,-22.2961],[30.13147,-22.30841],[29.92242,-22.19408],[29.76848,-22.14128],[29.64609,-22.12917],[29.37703,-22.19581],[29.21955,-22.17771],[29.18974,-22.18599],[29.15268,-22.21399],[29.10881,-22.21202],[29.0151,-22.22907],[28.91889,-22.44299],[28.63287,-22.55887],[28.34874,-22.5694],[28.04562,-22.8394],[28.04752,-22.90243],[27.93729,-22.96194],[27.93539,-23.04941],[27.74154,-23.2137],[27.6066,-23.21894],[27.52393,-23.37952],[27.33768,-23.40917],[26.99749,-23.65486],[26.84165,-24.24885],[26.51667,-24.47219],[26.46346,-24.60358],[26.39409,-24.63468],[25.8515,-24.75727],[25.84295,-24.78661],[25.88571,-24.87802],[25.72702,-25.25503],[25.69661,-25.29284],[25.6643,-25.4491],[25.58543,-25.6343],[25.33076,-25.76616],[25.12266,-25.75931],[25.01718,-25.72507],[24.8946,-25.80723],[24.67319,-25.81749],[24.44703,-25.73021],[24.36531,-25.773],[24.18287,-25.62916],[23.9244,-25.64286],[23.47588,-25.29971],[23.03497,-25.29971],[22.86012,-25.50572],[22.70808,-25.99186],[22.56365,-26.19668],[22.41921,-26.23078],[22.21206,-26.3773],[22.06192,-26.61882],[21.90703,-26.66808],[21.83291,-26.65959],[21.77114,-26.69015],[21.7854,-26.79199],[21.69322,-26.86152],[21.37869,-26.82083],[21.13353,-26.86661],[20.87031,-26.80047],[20.68596,-26.9039],[20.63275,-26.78181],[20.61754,-26.4692],[20.86081,-26.14892],[20.64795,-25.47827],[20.29826,-24.94869],[20.03678,-24.81004],[20.02809,-24.78725],[19.99817,-24.76768],[19.99882,-28.42622],[18.99885,-28.89165],[17.4579,-28.68718],[17.15405,-28.08573],[16.90446,-28.057],[16.59922,-28.53246],[16.46592,-28.57126],[16.45332,-28.63117],[12.51595,-32.27486],[38.88176,-48.03306],[34.51034,-26.91792],[32.35222,-26.86027],[32.29584,-26.852],[32.22302,-26.84136],[32.19409,-26.84032],[32.13315,-26.84345],[32.09664,-26.80721],[32.00893,-26.8096],[31.97463,-27.11057],[31.97592,-27.31675],[31.49834,-27.31549],[31.15027,-27.20151],[30.96088,-27.0245],[30.97757,-26.92706],[30.88826,-26.79622],[30.81101,-26.84722],[30.78927,-26.48271],[30.95819,-26.26303],[31.13073,-25.91558],[31.31237,-25.7431],[31.4175,-25.71886],[31.86881,-25.99973],[31.974,-25.95387],[31.92649,-25.84216],[32.00631,-25.65044],[31.97875,-25.46356],[32.01676,-25.38117],[32.03196,-25.10785],[31.9835,-24.29983],[31.90368,-24.18892],[31.87707,-23.95293],[31.77445,-23.90082],[31.70223,-23.72695],[31.67942,-23.60858],[31.56539,-23.47268],[31.55779,-23.176],[31.30611,-22.422]],[[29.33204,-29.45598],[29.28545,-29.58456],[29.12553,-29.76266],[29.16548,-29.91706],[28.9338,-30.05072],[28.80222,-30.10579],[28.68627,-30.12885],[28.399,-30.1592],[28.2319,-30.28476],[28.12073,-30.68072],[27.74814,-30.60635],[27.69467,-30.55862],[27.67819,-30.53437],[27.6521,-30.51707],[27.62137,-30.50509],[27.56781,-30.44562],[27.56901,-30.42504],[27.45452,-30.32239],[27.38108,-30.33456],[27.36649,-30.27246],[27.37293,-30.19401],[27.40778,-30.14577],[27.32555,-30.14785],[27.29603,-30.05473],[27.22719,-30.00718],[27.09489,-29.72796],[27.01016,-29.65439],[27.33464,-29.48161],[27.4358,-29.33465],[27.47254,-29.31968],[27.45125,-29.29708],[27.48679,-29.29349],[27.54258,-29.25575],[27.5158,-29.2261],[27.55974,-29.18954],[27.75458,-28.89839],[27.8907,-28.91612],[27.88933,-28.88156],[27.9392,-28.84864],[27.98675,-28.8787],[28.02503,-28.85991],[28.1317,-28.7293],[28.2348,-28.69471],[28.30518,-28.69531],[28.40612,-28.6215],[28.65091,-28.57025],[28.68043,-28.58744],[29.40524,-29.21246],[29.44883,-29.3772],[29.33204,-29.45598]]]]}},{type:"Feature",properties:{iso1A2:"ZM",iso1A3:"ZMB",iso1N3:"894",wikidata:"Q953",nameEn:"Zambia",groups:["014","202","002"],driveSide:"left",callingCodes:["260"]},geometry:{type:"MultiPolygon",coordinates:[[[[32.95389,-9.40138],[32.76233,-9.31963],[32.75611,-9.28583],[32.53661,-9.24281],[32.49147,-9.14754],[32.43543,-9.11988],[32.25486,-9.13371],[32.16146,-9.05993],[32.08206,-9.04609],[31.98866,-9.07069],[31.94196,-9.02303],[31.94663,-8.93846],[31.81587,-8.88618],[31.71158,-8.91386],[31.57147,-8.81388],[31.57147,-8.70619],[31.37533,-8.60769],[31.00796,-8.58615],[30.79243,-8.27382],[28.88917,-8.4831],[28.9711,-8.66935],[28.38526,-9.23393],[28.36562,-9.30091],[28.52636,-9.35379],[28.51627,-9.44726],[28.56208,-9.49122],[28.68532,-9.78],[28.62795,-9.92942],[28.65032,-10.65133],[28.37241,-11.57848],[28.48357,-11.87532],[29.18592,-12.37921],[29.4992,-12.43843],[29.48404,-12.23604],[29.8139,-12.14898],[29.81551,-13.44683],[29.65078,-13.41844],[29.60531,-13.21685],[29.01918,-13.41353],[28.33199,-12.41375],[27.59932,-12.22123],[27.21025,-11.76157],[27.22541,-11.60323],[27.04351,-11.61312],[26.88687,-12.01868],[26.01777,-11.91488],[25.33058,-11.65767],[25.34069,-11.19707],[24.42612,-11.44975],[24.34528,-11.06816],[24.00027,-10.89356],[24.02603,-11.15368],[23.98804,-12.13149],[24.06672,-12.29058],[23.90937,-12.844],[24.03339,-12.99091],[21.97988,-13.00148],[22.00323,-16.18028],[22.17217,-16.50269],[23.20038,-17.47563],[23.47474,-17.62877],[24.23619,-17.47489],[24.32811,-17.49082],[24.38712,-17.46818],[24.5621,-17.52963],[24.70864,-17.49501],[25.00198,-17.58221],[25.26433,-17.79571],[25.51646,-17.86232],[25.6827,-17.81987],[25.85738,-17.91403],[25.85892,-17.97726],[26.08925,-17.98168],[26.0908,-17.93021],[26.21601,-17.88608],[26.55918,-17.99638],[26.68403,-18.07411],[26.74314,-18.0199],[26.89926,-17.98756],[27.14196,-17.81398],[27.30736,-17.60487],[27.61377,-17.34378],[27.62795,-17.24365],[27.83141,-16.96274],[28.73725,-16.5528],[28.76199,-16.51575],[28.81454,-16.48611],[28.8501,-16.04537],[28.9243,-15.93987],[29.01298,-15.93805],[29.21955,-15.76589],[29.4437,-15.68702],[29.8317,-15.6126],[30.35574,-15.6513],[30.41902,-15.62269],[30.22098,-14.99447],[33.24249,-14.00019],[33.16749,-13.93992],[33.07568,-13.98447],[33.02977,-14.05022],[32.99042,-13.95689],[32.88985,-13.82956],[32.79015,-13.80755],[32.76962,-13.77224],[32.84528,-13.71576],[32.7828,-13.64805],[32.68654,-13.64268],[32.66468,-13.60019],[32.68436,-13.55769],[32.73683,-13.57682],[32.84176,-13.52794],[32.86113,-13.47292],[33.0078,-13.19492],[32.98289,-13.12671],[33.02181,-12.88707],[32.96733,-12.88251],[32.94397,-12.76868],[33.05917,-12.59554],[33.18837,-12.61377],[33.28177,-12.54692],[33.37517,-12.54085],[33.54485,-12.35996],[33.47636,-12.32498],[33.3705,-12.34931],[33.25998,-12.14242],[33.33937,-11.91252],[33.32692,-11.59248],[33.24252,-11.59302],[33.23663,-11.40637],[33.29267,-11.43536],[33.29267,-11.3789],[33.39697,-11.15296],[33.25998,-10.88862],[33.28022,-10.84428],[33.47636,-10.78465],[33.70675,-10.56896],[33.54797,-10.36077],[33.53863,-10.20148],[33.31297,-10.05133],[33.37902,-9.9104],[33.36581,-9.81063],[33.31517,-9.82364],[33.2095,-9.61099],[33.12144,-9.58929],[33.10163,-9.66525],[33.05485,-9.61316],[33.00256,-9.63053],[33.00476,-9.5133],[32.95389,-9.40138]]]]}},{type:"Feature",properties:{iso1A2:"ZW",iso1A3:"ZWE",iso1N3:"716",wikidata:"Q954",nameEn:"Zimbabwe",groups:["014","202","002"],driveSide:"left",callingCodes:["263"]},geometry:{type:"MultiPolygon",coordinates:[[[[30.41902,-15.62269],[30.35574,-15.6513],[29.8317,-15.6126],[29.4437,-15.68702],[29.21955,-15.76589],[29.01298,-15.93805],[28.9243,-15.93987],[28.8501,-16.04537],[28.81454,-16.48611],[28.76199,-16.51575],[28.73725,-16.5528],[27.83141,-16.96274],[27.62795,-17.24365],[27.61377,-17.34378],[27.30736,-17.60487],[27.14196,-17.81398],[26.89926,-17.98756],[26.74314,-18.0199],[26.68403,-18.07411],[26.55918,-17.99638],[26.21601,-17.88608],[26.0908,-17.93021],[26.08925,-17.98168],[25.85892,-17.97726],[25.85738,-17.91403],[25.6827,-17.81987],[25.51646,-17.86232],[25.26433,-17.79571],[25.23909,-17.90832],[25.31799,-18.07091],[25.39972,-18.12691],[25.53465,-18.39041],[25.68859,-18.56165],[25.79217,-18.6355],[25.82353,-18.82808],[25.94326,-18.90362],[25.99837,-19.02943],[25.96226,-19.08152],[26.17227,-19.53709],[26.72246,-19.92707],[27.21278,-20.08244],[27.29831,-20.28935],[27.28865,-20.49873],[27.69361,-20.48531],[27.72972,-20.51735],[27.69171,-21.08409],[27.91407,-21.31621],[28.01669,-21.57624],[28.29416,-21.59037],[28.49942,-21.66634],[28.58114,-21.63455],[29.07763,-21.81877],[29.04023,-21.85864],[29.02191,-21.90647],[29.02191,-21.95665],[29.04108,-22.00563],[29.08495,-22.04867],[29.14501,-22.07275],[29.1974,-22.07472],[29.24648,-22.05967],[29.3533,-22.18363],[29.37703,-22.19581],[29.64609,-22.12917],[29.76848,-22.14128],[29.92242,-22.19408],[30.13147,-22.30841],[30.2265,-22.2961],[30.28351,-22.35587],[30.38614,-22.34533],[30.48686,-22.31368],[30.6294,-22.32599],[30.86696,-22.28907],[31.08932,-22.34884],[31.16344,-22.32599],[31.30611,-22.422],[31.38336,-22.36919],[32.41234,-21.31246],[32.48236,-21.32873],[32.37115,-21.133],[32.51644,-20.91929],[32.48122,-20.63319],[32.55167,-20.56312],[32.66174,-20.56106],[32.85987,-20.27841],[32.85987,-20.16686],[32.93032,-20.03868],[33.01178,-20.02007],[33.06461,-19.77787],[32.95013,-19.67219],[32.84666,-19.68462],[32.84446,-19.48343],[32.78282,-19.47513],[32.77966,-19.36098],[32.85107,-19.29238],[32.87088,-19.09279],[32.84006,-19.0262],[32.72118,-19.02204],[32.69917,-18.94293],[32.73439,-18.92628],[32.70137,-18.84712],[32.82465,-18.77419],[32.9017,-18.7992],[32.95013,-18.69079],[32.88629,-18.58023],[32.88629,-18.51344],[33.02278,-18.4696],[33.03159,-18.35054],[32.94133,-17.99705],[33.0492,-17.60298],[32.98536,-17.55891],[32.96554,-17.48964],[33.0426,-17.3468],[33.00517,-17.30477],[32.96554,-17.11971],[32.84113,-16.92259],[32.91051,-16.89446],[32.97655,-16.70689],[32.78943,-16.70267],[32.69917,-16.66893],[32.71017,-16.59932],[32.42838,-16.4727],[32.28529,-16.43892],[32.02772,-16.43892],[31.91324,-16.41569],[31.90223,-16.34388],[31.67988,-16.19595],[31.42451,-16.15154],[31.30563,-16.01193],[31.13171,-15.98019],[30.97761,-16.05848],[30.91597,-15.99924],[30.42568,-15.9962],[30.41902,-15.62269]]]]}}];
60463         var rawBorders = {
60464         type: type,
60465         features: features
60466         };
60467
60468         var borders = rawBorders;
60469         var whichPolygonGetter = {};
60470         var featuresByCode = {};
60471         var idFilterRegex = /\bThe\b|\bthe\b|\band\b|\bof\b|[-_ .,()&[\]/]/g;
60472         var levels = [
60473           'subterritory',
60474           'territory',
60475           'country',
60476           'intermediateRegion',
60477           'subregion',
60478           'region',
60479           'union',
60480           'world'
60481         ];
60482         loadDerivedDataAndCaches(borders);
60483         function loadDerivedDataAndCaches(borders) {
60484           var identifierProps = ['iso1A2', 'iso1A3', 'm49', 'wikidata', 'emojiFlag', 'nameEn'];
60485           var geometryFeatures = [];
60486           for (var i in borders.features) {
60487             var feature = borders.features[i];
60488             feature.properties.id = feature.properties.iso1A2 || feature.properties.m49;
60489             loadM49(feature);
60490             loadIsoStatus(feature);
60491             loadLevel(feature);
60492             loadGroups(feature);
60493             loadRoadSpeedUnit(feature);
60494             loadDriveSide(feature);
60495             loadFlag(feature);
60496             cacheFeatureByIDs(feature);
60497             if (feature.geometry) { geometryFeatures.push(feature); }
60498           }
60499           for (var i$1 in borders.features) {
60500             var feature$1 = borders.features[i$1];
60501             feature$1.properties.groups.sort(function(groupID1, groupID2) {
60502               return (
60503                 levels.indexOf(featuresByCode[groupID1].properties.level) -
60504                 levels.indexOf(featuresByCode[groupID2].properties.level)
60505               );
60506             });
60507             loadMembersForGroupsOf(feature$1);
60508           }
60509           var geometryOnlyCollection = {
60510             type: 'RegionFeatureCollection',
60511             features: geometryFeatures
60512           };
60513           whichPolygonGetter = whichPolygon_1(geometryOnlyCollection);
60514           function loadGroups(feature) {
60515             var props = feature.properties;
60516             if (!props.groups) {
60517               props.groups = [];
60518             }
60519             if (props.country) {
60520               props.groups.push(props.country);
60521             }
60522             if (props.m49 !== '001') {
60523               props.groups.push('001');
60524             }
60525           }
60526           function loadM49(feature) {
60527             var props = feature.properties;
60528             if (!props.m49 && props.iso1N3) {
60529               props.m49 = props.iso1N3;
60530             }
60531           }
60532           function loadIsoStatus(feature) {
60533             var props = feature.properties;
60534             if (!props.isoStatus && props.iso1A2) {
60535               props.isoStatus = 'official';
60536             }
60537           }
60538           function loadLevel(feature) {
60539             var props = feature.properties;
60540             if (props.level) { return; }
60541             if (!props.country) {
60542               props.level = 'country';
60543             } else if (props.isoStatus === 'official') {
60544               props.level = 'territory';
60545             } else {
60546               props.level = 'subterritory';
60547             }
60548           }
60549           function loadRoadSpeedUnit(feature) {
60550             var props = feature.properties;
60551             if (props.roadSpeedUnit === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
60552               props.roadSpeedUnit = 'km/h';
60553             }
60554           }
60555           function loadDriveSide(feature) {
60556             var props = feature.properties;
60557             if (props.driveSide === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
60558               props.driveSide = 'right';
60559             }
60560           }
60561           function loadFlag(feature) {
60562             if (!feature.properties.iso1A2) { return; }
60563             var flag = feature.properties.iso1A2.replace(/./g, function(char) {
60564               return String.fromCodePoint(char.charCodeAt(0) + 127397);
60565             });
60566             feature.properties.emojiFlag = flag;
60567           }
60568           function loadMembersForGroupsOf(feature) {
60569             var featureID = feature.properties.id;
60570             var standardizedGroupIDs = [];
60571             for (var j in feature.properties.groups) {
60572               var groupID = feature.properties.groups[j];
60573               var groupFeature = featuresByCode[groupID];
60574               standardizedGroupIDs.push(groupFeature.properties.id);
60575               if (groupFeature.properties.members) {
60576                 groupFeature.properties.members.push(featureID);
60577               } else {
60578                 groupFeature.properties.members = [featureID];
60579               }
60580             }
60581             feature.properties.groups = standardizedGroupIDs;
60582           }
60583           function cacheFeatureByIDs(feature) {
60584             for (var k in identifierProps) {
60585               var prop = identifierProps[k];
60586               var id = prop && feature.properties[prop];
60587               if (id) {
60588                 id = id.replace(idFilterRegex, '').toUpperCase();
60589                 featuresByCode[id] = feature;
60590               }
60591             }
60592             if (feature.properties.aliases) {
60593               for (var j in feature.properties.aliases) {
60594                 var alias = feature.properties.aliases[j].replace(idFilterRegex, '').toUpperCase();
60595                 featuresByCode[alias] = feature;
60596               }
60597             }
60598           }
60599         }
60600         function locArray(loc) {
60601           if (Array.isArray(loc)) {
60602             return loc;
60603           } else if (loc.coordinates) {
60604             return loc.coordinates;
60605           }
60606           return loc.geometry.coordinates;
60607         }
60608         function smallestFeature(loc) {
60609           var query = locArray(loc);
60610           var featureProperties = whichPolygonGetter(query);
60611           if (!featureProperties) { return null; }
60612           return featuresByCode[featureProperties.id];
60613         }
60614         function countryFeature(loc) {
60615           var feature = smallestFeature(loc);
60616           if (!feature) { return null; }
60617           var countryCode = feature.properties.country || feature.properties.iso1A2;
60618           return featuresByCode[countryCode];
60619         }
60620         function featureForLoc(loc, opts) {
60621           if (opts && opts.level && opts.level !== 'country') {
60622             var features = featuresContaining(loc);
60623             var targetLevel = opts.level;
60624             var targetLevelIndex = levels.indexOf(targetLevel);
60625             if (targetLevelIndex === -1) { return null; }
60626             for (var i in features) {
60627               var feature = features[i];
60628               if (
60629                 feature.properties.level === targetLevel ||
60630                 levels.indexOf(feature.properties.level) > targetLevelIndex
60631               ) {
60632                 return feature;
60633               }
60634             }
60635             return null;
60636           }
60637           return countryFeature(loc);
60638         }
60639         function featureForID(id) {
60640           var stringID;
60641           if (typeof id === 'number') {
60642             stringID = id.toString();
60643             if (stringID.length === 1) {
60644               stringID = '00' + stringID;
60645             } else if (stringID.length === 2) {
60646               stringID = '0' + stringID;
60647             }
60648           } else {
60649             stringID = id.replace(idFilterRegex, '').toUpperCase();
60650           }
60651           return featuresByCode[stringID] || null;
60652         }
60653         function smallestOrMatchingFeature(query) {
60654           if (typeof query === 'object') {
60655             return smallestFeature(query);
60656           }
60657           return featureForID(query);
60658         }
60659         function feature(query, opts) {
60660           if (typeof query === 'object') {
60661             return featureForLoc(query, opts);
60662           }
60663           return featureForID(query);
60664         }
60665         function iso1A2Code(query, opts) {
60666           var match = feature(query, opts);
60667           if (!match) { return null; }
60668           return match.properties.iso1A2 || null;
60669         }
60670         function featuresContaining(query, strict) {
60671           var feature = smallestOrMatchingFeature(query);
60672           if (!feature) { return []; }
60673           var features = [];
60674           if (!strict || typeof query === 'object') {
60675             features.push(feature);
60676           }
60677           var properties = feature.properties;
60678           for (var i in properties.groups) {
60679             var groupID = properties.groups[i];
60680             features.push(featuresByCode[groupID]);
60681           }
60682           return features;
60683         }
60684         function roadSpeedUnit(query) {
60685           var feature = smallestOrMatchingFeature(query);
60686           return (feature && feature.properties.roadSpeedUnit) || null;
60687         }
60688
60689         var _dataDeprecated;
60690         var _nsi;
60691
60692         function validationOutdatedTags() {
60693           var type = 'outdated_tags';
60694           var nsiKeys = ['amenity', 'shop', 'tourism', 'leisure', 'office'];
60695
60696           // A concern here in switching to async data means that `_dataDeprecated`
60697           // and `_nsi` will not be available at first, so the data on early tiles
60698           // may not have tags validated fully.
60699
60700           // initialize deprecated tags array
60701           _mainFileFetcher.get('deprecated')
60702             .then(function (d) { return _dataDeprecated = d; })
60703             .catch(function () { /* ignore */ });
60704
60705           _mainFileFetcher.get('nsi_brands')
60706             .then(function (d) {
60707               _nsi = {
60708                 brands: d.brands,
60709                 matcher: matcher$1(),
60710                 wikidata: {},
60711                 wikipedia: {}
60712               };
60713
60714               // initialize name-suggestion-index matcher
60715               _nsi.matcher.buildMatchIndex(d.brands);
60716
60717               // index all known wikipedia and wikidata tags
60718               Object.keys(d.brands).forEach(function (kvnd) {
60719                 var brand = d.brands[kvnd];
60720                 var wd = brand.tags['brand:wikidata'];
60721                 var wp = brand.tags['brand:wikipedia'];
60722                 if (wd) { _nsi.wikidata[wd] = kvnd; }
60723                 if (wp) { _nsi.wikipedia[wp] = kvnd; }
60724               });
60725
60726               return _nsi;
60727             })
60728             .catch(function () { /* ignore */ });
60729
60730
60731           function oldTagIssues(entity, graph) {
60732             var oldTags = Object.assign({}, entity.tags);  // shallow copy
60733             var preset = _mainPresetIndex.match(entity, graph);
60734             var subtype = 'deprecated_tags';
60735             if (!preset) { return []; }
60736
60737             // upgrade preset..
60738             if (preset.replacement) {
60739               var newPreset = _mainPresetIndex.item(preset.replacement);
60740               graph = actionChangePreset(entity.id, preset, newPreset, true /* skip field defaults */)(graph);
60741               entity = graph.entity(entity.id);
60742               preset = newPreset;
60743             }
60744
60745             // upgrade tags..
60746             if (_dataDeprecated) {
60747               var deprecatedTags = entity.deprecatedTags(_dataDeprecated);
60748               if (deprecatedTags.length) {
60749                 deprecatedTags.forEach(function (tag) {
60750                   graph = actionUpgradeTags(entity.id, tag.old, tag.replace)(graph);
60751                 });
60752                 entity = graph.entity(entity.id);
60753               }
60754             }
60755
60756             // add missing addTags..
60757             var newTags = Object.assign({}, entity.tags);  // shallow copy
60758             if (preset.tags !== preset.addTags) {
60759               Object.keys(preset.addTags).forEach(function (k) {
60760                 if (!newTags[k]) {
60761                   if (preset.addTags[k] === '*') {
60762                     newTags[k] = 'yes';
60763                   } else {
60764                     newTags[k] = preset.addTags[k];
60765                   }
60766                 }
60767               });
60768             }
60769
60770             if (_nsi) {
60771               // Do `wikidata` or `wikipedia` identify this entity as a brand?  #6416
60772               // If so, these tags can be swapped to `brand:wikidata`/`brand:wikipedia`
60773               var isBrand;
60774               if (newTags.wikidata) {                 // try matching `wikidata`
60775                 isBrand = _nsi.wikidata[newTags.wikidata];
60776               }
60777               if (!isBrand && newTags.wikipedia) {    // fallback to `wikipedia`
60778                 isBrand = _nsi.wikipedia[newTags.wikipedia];
60779               }
60780               if (isBrand && !newTags.office) {       // but avoid doing this for corporate offices
60781                 if (newTags.wikidata) {
60782                   newTags['brand:wikidata'] = newTags.wikidata;
60783                   delete newTags.wikidata;
60784                 }
60785                 if (newTags.wikipedia) {
60786                   newTags['brand:wikipedia'] = newTags.wikipedia;
60787                   delete newTags.wikipedia;
60788                 }
60789                 // I considered setting `name` and other tags here, but they aren't unique per wikidata
60790                 // (Q2759586 -> in USA "Papa John's", in Russia "Папа Джонс")
60791                 // So users will really need to use a preset or assign `name` themselves.
60792               }
60793
60794               // try key/value|name match against name-suggestion-index
60795               if (newTags.name) {
60796                 for (var i = 0; i < nsiKeys.length; i++) {
60797                   var k = nsiKeys[i];
60798                   if (!newTags[k]) { continue; }
60799
60800                   var center = entity.extent(graph).center();
60801                   var countryCode = iso1A2Code(center);
60802                   var match = _nsi.matcher.matchKVN(k, newTags[k], newTags.name, countryCode && countryCode.toLowerCase());
60803                   if (!match) { continue; }
60804
60805                   // for now skip ambiguous matches (like Target~(USA) vs Target~(Australia))
60806                   if (match.d) { continue; }
60807
60808                   var brand = _nsi.brands[match.kvnd];
60809                   if (brand && brand.tags['brand:wikidata'] &&
60810                     brand.tags['brand:wikidata'] !== entity.tags['not:brand:wikidata']) {
60811                     subtype = 'noncanonical_brand';
60812
60813                     var keepTags = ['takeaway'].reduce(function (acc, k) {
60814                       if (newTags[k]) {
60815                         acc[k] = newTags[k];
60816                       }
60817                       return acc;
60818                     }, {});
60819
60820                     nsiKeys.forEach(function (k) { return delete newTags[k]; });
60821                     Object.assign(newTags, brand.tags, keepTags);
60822                     break;
60823                   }
60824                 }
60825               }
60826             }
60827
60828             // determine diff
60829             var tagDiff = utilTagDiff(oldTags, newTags);
60830             if (!tagDiff.length) { return []; }
60831
60832             var isOnlyAddingTags = tagDiff.every(function (d) { return d.type === '+'; });
60833
60834             var prefix = '';
60835             if (subtype === 'noncanonical_brand') {
60836               prefix = 'noncanonical_brand.';
60837             } else if (subtype === 'deprecated_tags' && isOnlyAddingTags) {
60838               subtype = 'incomplete_tags';
60839               prefix = 'incomplete.';
60840             }
60841
60842             // don't allow autofixing brand tags
60843             var autoArgs = subtype !== 'noncanonical_brand' ? [doUpgrade, _t('issues.fix.upgrade_tags.annotation')] : null;
60844
60845             return [new validationIssue({
60846               type: type,
60847               subtype: subtype,
60848               severity: 'warning',
60849               message: showMessage,
60850               reference: showReference,
60851               entityIds: [entity.id],
60852               hash: JSON.stringify(tagDiff),
60853               dynamicFixes: function () {
60854                 return [
60855                   new validationIssueFix({
60856                     autoArgs: autoArgs,
60857                     title: _t('issues.fix.upgrade_tags.title'),
60858                     onClick: function (context) {
60859                       context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
60860                     }
60861                   })
60862                 ];
60863               }
60864             })];
60865
60866
60867             function doUpgrade(graph) {
60868               var currEntity = graph.hasEntity(entity.id);
60869               if (!currEntity) { return graph; }
60870
60871               var newTags = Object.assign({}, currEntity.tags);  // shallow copy
60872               tagDiff.forEach(function (diff) {
60873                 if (diff.type === '-') {
60874                   delete newTags[diff.key];
60875                 } else if (diff.type === '+') {
60876                   newTags[diff.key] = diff.newVal;
60877                 }
60878               });
60879
60880               return actionChangeTags(currEntity.id, newTags)(graph);
60881             }
60882
60883
60884             function showMessage(context) {
60885               var currEntity = context.hasEntity(entity.id);
60886               if (!currEntity) { return ''; }
60887
60888               var messageID = "issues.outdated_tags." + prefix + "message";
60889               if (subtype === 'noncanonical_brand' && isOnlyAddingTags) {
60890                 messageID += '_incomplete';
60891               }
60892               return _t(messageID, { feature: utilDisplayLabel(currEntity, context.graph()) });
60893             }
60894
60895
60896             function showReference(selection) {
60897               var enter = selection.selectAll('.issue-reference')
60898                 .data([0])
60899                 .enter();
60900
60901               enter
60902                 .append('div')
60903                 .attr('class', 'issue-reference')
60904                 .text(_t(("issues.outdated_tags." + prefix + "reference")));
60905
60906               enter
60907                 .append('strong')
60908                 .text(_t('issues.suggested'));
60909
60910               enter
60911                 .append('table')
60912                 .attr('class', 'tagDiff-table')
60913                 .selectAll('.tagDiff-row')
60914                 .data(tagDiff)
60915                 .enter()
60916                 .append('tr')
60917                 .attr('class', 'tagDiff-row')
60918                 .append('td')
60919                 .attr('class', function (d) {
60920                   var klass = d.type === '+' ? 'add' : 'remove';
60921                   return ("tagDiff-cell tagDiff-cell-" + klass);
60922                 })
60923                 .text(function (d) { return d.display; });
60924             }
60925           }
60926
60927
60928           function oldMultipolygonIssues(entity, graph) {
60929             var multipolygon, outerWay;
60930             if (entity.type === 'relation') {
60931               outerWay = osmOldMultipolygonOuterMemberOfRelation(entity, graph);
60932               multipolygon = entity;
60933             } else if (entity.type === 'way') {
60934               multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
60935               outerWay = entity;
60936             } else {
60937               return [];
60938             }
60939
60940             if (!multipolygon || !outerWay) { return []; }
60941
60942             return [new validationIssue({
60943               type: type,
60944               subtype: 'old_multipolygon',
60945               severity: 'warning',
60946               message: showMessage,
60947               reference: showReference,
60948               entityIds: [outerWay.id, multipolygon.id],
60949               dynamicFixes: function () {
60950                 return [
60951                   new validationIssueFix({
60952                     autoArgs: [doUpgrade, _t('issues.fix.move_tags.annotation')],
60953                     title: _t('issues.fix.move_tags.title'),
60954                     onClick: function (context) {
60955                       context.perform(doUpgrade, _t('issues.fix.move_tags.annotation'));
60956                     }
60957                   })
60958                 ];
60959               }
60960             })];
60961
60962
60963             function doUpgrade(graph) {
60964               var currMultipolygon = graph.hasEntity(multipolygon.id);
60965               var currOuterWay = graph.hasEntity(outerWay.id);
60966               if (!currMultipolygon || !currOuterWay) { return graph; }
60967
60968               currMultipolygon = currMultipolygon.mergeTags(currOuterWay.tags);
60969               graph = graph.replace(currMultipolygon);
60970               return actionChangeTags(currOuterWay.id, {})(graph);
60971             }
60972
60973
60974             function showMessage(context) {
60975               var currMultipolygon = context.hasEntity(multipolygon.id);
60976               if (!currMultipolygon) { return ''; }
60977
60978               return _t('issues.old_multipolygon.message',
60979                   { multipolygon: utilDisplayLabel(currMultipolygon, context.graph()) }
60980               );
60981             }
60982
60983
60984             function showReference(selection) {
60985               selection.selectAll('.issue-reference')
60986                 .data([0])
60987                 .enter()
60988                 .append('div')
60989                 .attr('class', 'issue-reference')
60990                 .text(_t('issues.old_multipolygon.reference'));
60991             }
60992           }
60993
60994
60995           var validation = function checkOutdatedTags(entity, graph) {
60996             var issues = oldMultipolygonIssues(entity, graph);
60997             if (!issues.length) { issues = oldTagIssues(entity, graph); }
60998             return issues;
60999           };
61000
61001
61002           validation.type = type;
61003
61004           return validation;
61005         }
61006
61007         function validationPrivateData() {
61008             var type = 'private_data';
61009
61010             // assume that some buildings are private
61011             var privateBuildingValues = {
61012                 detached: true,
61013                 farm: true,
61014                 house: true,
61015                 houseboat: true,
61016                 residential: true,
61017                 semidetached_house: true,
61018                 static_caravan: true
61019             };
61020
61021             // but they might be public if they have one of these other tags
61022             var publicKeys = {
61023                 amenity: true,
61024                 craft: true,
61025                 historic: true,
61026                 leisure: true,
61027                 office: true,
61028                 shop: true,
61029                 tourism: true
61030             };
61031
61032             // these tags may contain personally identifying info
61033             var personalTags = {
61034                 'contact:email': true,
61035                 'contact:fax': true,
61036                 'contact:phone': true,
61037                 email: true,
61038                 fax: true,
61039                 phone: true
61040             };
61041
61042
61043             var validation = function checkPrivateData(entity) {
61044                 var tags = entity.tags;
61045                 if (!tags.building || !privateBuildingValues[tags.building]) { return []; }
61046
61047                 var keepTags = {};
61048                 for (var k in tags) {
61049                     if (publicKeys[k]) { return []; }  // probably a public feature
61050                     if (!personalTags[k]) {
61051                         keepTags[k] = tags[k];
61052                     }
61053                 }
61054
61055                 var tagDiff = utilTagDiff(tags, keepTags);
61056                 if (!tagDiff.length) { return []; }
61057
61058                 var fixID = tagDiff.length === 1 ? 'remove_tag' : 'remove_tags';
61059
61060                 return [new validationIssue({
61061                     type: type,
61062                     severity: 'warning',
61063                     message: showMessage,
61064                     reference: showReference,
61065                     entityIds: [entity.id],
61066                     dynamicFixes: function() {
61067                         return [
61068                             new validationIssueFix({
61069                                 icon: 'iD-operation-delete',
61070                                 title: _t('issues.fix.' + fixID + '.title'),
61071                                 onClick: function(context) {
61072                                     context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
61073                                 }
61074                             })
61075                         ];
61076                     }
61077                 })];
61078
61079
61080                 function doUpgrade(graph) {
61081                     var currEntity = graph.hasEntity(entity.id);
61082                     if (!currEntity) { return graph; }
61083
61084                     var newTags = Object.assign({}, currEntity.tags);  // shallow copy
61085                     tagDiff.forEach(function(diff) {
61086                         if (diff.type === '-') {
61087                             delete newTags[diff.key];
61088                         } else if (diff.type === '+') {
61089                             newTags[diff.key] = diff.newVal;
61090                         }
61091                     });
61092
61093                     return actionChangeTags(currEntity.id, newTags)(graph);
61094                 }
61095
61096
61097                 function showMessage(context) {
61098                     var currEntity = context.hasEntity(this.entityIds[0]);
61099                     if (!currEntity) { return ''; }
61100
61101                     return _t('issues.private_data.contact.message',
61102                         { feature: utilDisplayLabel(currEntity, context.graph()) }
61103                     );
61104                 }
61105
61106
61107                 function showReference(selection) {
61108                     var enter = selection.selectAll('.issue-reference')
61109                         .data([0])
61110                         .enter();
61111
61112                     enter
61113                         .append('div')
61114                         .attr('class', 'issue-reference')
61115                         .text(_t('issues.private_data.reference'));
61116
61117                     enter
61118                         .append('strong')
61119                         .text(_t('issues.suggested'));
61120
61121                     enter
61122                         .append('table')
61123                         .attr('class', 'tagDiff-table')
61124                         .selectAll('.tagDiff-row')
61125                         .data(tagDiff)
61126                         .enter()
61127                         .append('tr')
61128                         .attr('class', 'tagDiff-row')
61129                         .append('td')
61130                         .attr('class', function(d) {
61131                             var klass = d.type === '+' ? 'add' : 'remove';
61132                             return 'tagDiff-cell tagDiff-cell-' + klass;
61133                         })
61134                         .text(function(d) { return d.display; });
61135                 }
61136             };
61137
61138
61139             validation.type = type;
61140
61141             return validation;
61142         }
61143
61144         var _discardNameRegexes = [];
61145
61146         function validationSuspiciousName() {
61147           var type = 'suspicious_name';
61148           var keysToTestForGenericValues = [
61149             'aerialway', 'aeroway', 'amenity', 'building', 'craft', 'highway',
61150             'leisure', 'railway', 'man_made', 'office', 'shop', 'tourism', 'waterway'
61151           ];
61152
61153           // A concern here in switching to async data means that `_nsiFilters` will not
61154           // be available at first, so the data on early tiles may not have tags validated fully.
61155
61156           _mainFileFetcher.get('nsi_filters')
61157             .then(function (filters) {
61158               // known list of generic names (e.g. "bar")
61159               _discardNameRegexes = filters.discardNames
61160                 .map(function (discardName) { return new RegExp(discardName, 'i'); });
61161             })
61162             .catch(function () { /* ignore */ });
61163
61164
61165           function isDiscardedSuggestionName(lowercaseName) {
61166             return _discardNameRegexes.some(function (regex) { return regex.test(lowercaseName); });
61167           }
61168
61169           // test if the name is just the key or tag value (e.g. "park")
61170           function nameMatchesRawTag(lowercaseName, tags) {
61171             for (var i = 0; i < keysToTestForGenericValues.length; i++) {
61172               var key = keysToTestForGenericValues[i];
61173               var val = tags[key];
61174               if (val) {
61175                 val = val.toLowerCase();
61176                 if (key === lowercaseName ||
61177                   val === lowercaseName ||
61178                   key.replace(/\_/g, ' ') === lowercaseName ||
61179                   val.replace(/\_/g, ' ') === lowercaseName) {
61180                   return true;
61181                 }
61182               }
61183             }
61184             return false;
61185           }
61186
61187           function isGenericName(name, tags) {
61188             name = name.toLowerCase();
61189             return nameMatchesRawTag(name, tags) || isDiscardedSuggestionName(name);
61190           }
61191
61192           function makeGenericNameIssue(entityId, nameKey, genericName, langCode) {
61193             return new validationIssue({
61194               type: type,
61195               subtype: 'generic_name',
61196               severity: 'warning',
61197               message: function(context) {
61198                 var entity = context.hasEntity(this.entityIds[0]);
61199                 if (!entity) { return ''; }
61200                 var preset = _mainPresetIndex.match(entity, context.graph());
61201                 var langName = langCode && _mainLocalizer.languageName(langCode);
61202                 return _t('issues.generic_name.message' + (langName ? '_language' : ''),
61203                   { feature: preset.name(), name: genericName, language: langName }
61204                 );
61205               },
61206               reference: showReference,
61207               entityIds: [entityId],
61208               hash: nameKey + '=' + genericName,
61209               dynamicFixes: function() {
61210                 return [
61211                   new validationIssueFix({
61212                     icon: 'iD-operation-delete',
61213                     title: _t('issues.fix.remove_the_name.title'),
61214                     onClick: function(context) {
61215                       var entityId = this.issue.entityIds[0];
61216                       var entity = context.entity(entityId);
61217                       var tags = Object.assign({}, entity.tags);   // shallow copy
61218                       delete tags[nameKey];
61219                       context.perform(
61220                         actionChangeTags(entityId, tags), _t('issues.fix.remove_generic_name.annotation')
61221                       );
61222                     }
61223                   })
61224                 ];
61225               }
61226             });
61227
61228             function showReference(selection) {
61229               selection.selectAll('.issue-reference')
61230                 .data([0])
61231                 .enter()
61232                 .append('div')
61233                 .attr('class', 'issue-reference')
61234                 .text(_t('issues.generic_name.reference'));
61235             }
61236           }
61237
61238           function makeIncorrectNameIssue(entityId, nameKey, incorrectName, langCode) {
61239             return new validationIssue({
61240               type: type,
61241               subtype: 'not_name',
61242               severity: 'warning',
61243               message: function(context) {
61244                 var entity = context.hasEntity(this.entityIds[0]);
61245                 if (!entity) { return ''; }
61246                 var preset = _mainPresetIndex.match(entity, context.graph());
61247                 var langName = langCode && _mainLocalizer.languageName(langCode);
61248                 return _t('issues.incorrect_name.message' + (langName ? '_language' : ''),
61249                   { feature: preset.name(), name: incorrectName, language: langName }
61250                 );
61251               },
61252               reference: showReference,
61253               entityIds: [entityId],
61254               hash: nameKey + '=' + incorrectName,
61255               dynamicFixes: function() {
61256                 return [
61257                   new validationIssueFix({
61258                     icon: 'iD-operation-delete',
61259                     title: _t('issues.fix.remove_the_name.title'),
61260                     onClick: function(context) {
61261                       var entityId = this.issue.entityIds[0];
61262                       var entity = context.entity(entityId);
61263                       var tags = Object.assign({}, entity.tags);   // shallow copy
61264                       delete tags[nameKey];
61265                       context.perform(
61266                         actionChangeTags(entityId, tags), _t('issues.fix.remove_mistaken_name.annotation')
61267                       );
61268                     }
61269                   })
61270                 ];
61271               }
61272             });
61273
61274             function showReference(selection) {
61275               selection.selectAll('.issue-reference')
61276                 .data([0])
61277                 .enter()
61278                 .append('div')
61279                 .attr('class', 'issue-reference')
61280                 .text(_t('issues.generic_name.reference'));
61281             }
61282           }
61283
61284
61285           var validation = function checkGenericName(entity) {
61286             // a generic name is okay if it's a known brand or entity
61287             if (entity.hasWikidata()) { return []; }
61288
61289             var issues = [];
61290             var notNames = (entity.tags['not:name'] || '').split(';');
61291
61292             for (var key in entity.tags) {
61293               var m = key.match(/^name(?:(?::)([a-zA-Z_-]+))?$/);
61294               if (!m) { continue; }
61295
61296               var langCode = m.length >= 2 ? m[1] : null;
61297               var value = entity.tags[key];
61298               if (notNames.length) {
61299                 for (var i in notNames) {
61300                   var notName = notNames[i];
61301                   if (notName && value === notName) {
61302                     issues.push(makeIncorrectNameIssue(entity.id, key, value, langCode));
61303                     continue;
61304                   }
61305                 }
61306               }
61307               if (isGenericName(value, entity.tags)) {
61308                 issues.push(makeGenericNameIssue(entity.id, key, value, langCode));
61309               }
61310             }
61311
61312             return issues;
61313           };
61314
61315
61316           validation.type = type;
61317
61318           return validation;
61319         }
61320
61321         function validationUnsquareWay(context) {
61322             var type = 'unsquare_way';
61323             var DEFAULT_DEG_THRESHOLD = 5;   // see also issues.js
61324
61325             // use looser epsilon for detection to reduce warnings of buildings that are essentially square already
61326             var epsilon = 0.05;
61327             var nodeThreshold = 10;
61328
61329             function isBuilding(entity, graph) {
61330                 if (entity.type !== 'way' || entity.geometry(graph) !== 'area') { return false; }
61331                 return entity.tags.building && entity.tags.building !== 'no';
61332             }
61333
61334
61335             var validation = function checkUnsquareWay(entity, graph) {
61336
61337                 if (!isBuilding(entity, graph)) { return []; }
61338
61339                 // don't flag ways marked as physically unsquare
61340                 if (entity.tags.nonsquare === 'yes') { return []; }
61341
61342                 var isClosed = entity.isClosed();
61343                 if (!isClosed) { return []; }        // this building has bigger problems
61344
61345                 // don't flag ways with lots of nodes since they are likely detail-mapped
61346                 var nodes = graph.childNodes(entity).slice();    // shallow copy
61347                 if (nodes.length > nodeThreshold + 1) { return []; }   // +1 because closing node appears twice
61348
61349                 // ignore if not all nodes are fully downloaded
61350                 var osm = services.osm;
61351                 if (!osm || nodes.some(function(node) { return !osm.isDataLoaded(node.loc); })) { return []; }
61352
61353                 // don't flag connected ways to avoid unresolvable unsquare loops
61354                 var hasConnectedSquarableWays = nodes.some(function(node) {
61355                     return graph.parentWays(node).some(function(way) {
61356                         if (way.id === entity.id) { return false; }
61357                         if (isBuilding(way, graph)) { return true; }
61358                         return graph.parentRelations(way).some(function(parentRelation) {
61359                             return parentRelation.isMultipolygon() &&
61360                                 parentRelation.tags.building &&
61361                                 parentRelation.tags.building !== 'no';
61362                         });
61363                     });
61364                 });
61365                 if (hasConnectedSquarableWays) { return []; }
61366
61367
61368                 // user-configurable square threshold
61369                 var storedDegreeThreshold = corePreferences('validate-square-degrees');
61370                 var degreeThreshold = isNaN(storedDegreeThreshold) ? DEFAULT_DEG_THRESHOLD : parseFloat(storedDegreeThreshold);
61371
61372                 var points = nodes.map(function(node) { return context.projection(node.loc); });
61373                 if (!geoOrthoCanOrthogonalize(points, isClosed, epsilon, degreeThreshold, true)) { return []; }
61374
61375                 var autoArgs;
61376                 // don't allow autosquaring features linked to wikidata
61377                 if (!entity.tags.wikidata) {
61378                     // use same degree threshold as for detection
61379                     var autoAction = actionOrthogonalize(entity.id, context.projection, undefined, degreeThreshold);
61380                     autoAction.transitionable = false;  // when autofixing, do it instantly
61381                     autoArgs = [autoAction, _t('operations.orthogonalize.annotation.feature.single')];
61382                 }
61383
61384                 return [new validationIssue({
61385                     type: type,
61386                     subtype: 'building',
61387                     severity: 'warning',
61388                     message: function(context) {
61389                         var entity = context.hasEntity(this.entityIds[0]);
61390                         return entity ? _t('issues.unsquare_way.message', { feature: utilDisplayLabel(entity, context.graph()) }) : '';
61391                     },
61392                     reference: showReference,
61393                     entityIds: [entity.id],
61394                     hash: JSON.stringify(autoArgs !== undefined) + degreeThreshold,
61395                     dynamicFixes: function() {
61396                         return [
61397                             new validationIssueFix({
61398                                 icon: 'iD-operation-orthogonalize',
61399                                 title: _t('issues.fix.square_feature.title'),
61400                                 autoArgs: autoArgs,
61401                                 onClick: function(context, completionHandler) {
61402                                     var entityId = this.issue.entityIds[0];
61403                                     // use same degree threshold as for detection
61404                                     context.perform(
61405                                         actionOrthogonalize(entityId, context.projection, undefined, degreeThreshold),
61406                                         _t('operations.orthogonalize.annotation.feature.single')
61407                                     );
61408                                     // run after the squaring transition (currently 150ms)
61409                                     window.setTimeout(function() { completionHandler(); }, 175);
61410                                 }
61411                             }) ];
61412                     }
61413                 })];
61414
61415                 function showReference(selection) {
61416                     selection.selectAll('.issue-reference')
61417                         .data([0])
61418                         .enter()
61419                         .append('div')
61420                         .attr('class', 'issue-reference')
61421                         .text(_t('issues.unsquare_way.buildings.reference'));
61422                 }
61423             };
61424
61425             validation.type = type;
61426
61427             return validation;
61428         }
61429
61430         var Validations = /*#__PURE__*/Object.freeze({
61431                 __proto__: null,
61432                 validationAlmostJunction: validationAlmostJunction,
61433                 validationCloseNodes: validationCloseNodes,
61434                 validationCrossingWays: validationCrossingWays,
61435                 validationDisconnectedWay: validationDisconnectedWay,
61436                 validationFormatting: validationFormatting,
61437                 validationHelpRequest: validationHelpRequest,
61438                 validationImpossibleOneway: validationImpossibleOneway,
61439                 validationIncompatibleSource: validationIncompatibleSource,
61440                 validationMaprules: validationMaprules,
61441                 validationMismatchedGeometry: validationMismatchedGeometry,
61442                 validationMissingRole: validationMissingRole,
61443                 validationMissingTag: validationMissingTag,
61444                 validationOutdatedTags: validationOutdatedTags,
61445                 validationPrivateData: validationPrivateData,
61446                 validationSuspiciousName: validationSuspiciousName,
61447                 validationUnsquareWay: validationUnsquareWay
61448         });
61449
61450         function coreValidator(context) {
61451             var dispatch$1 = dispatch('validated', 'focusedIssue');
61452             var validator = utilRebind({}, dispatch$1, 'on');
61453
61454             var _rules = {};
61455             var _disabledRules = {};
61456
61457             var _ignoredIssueIDs = {};          // issue.id -> true
61458             var _baseCache = validationCache(); // issues before any user edits
61459             var _headCache = validationCache(); // issues after all user edits
61460             var _validatedGraph = null;
61461             var _deferred = new Set();
61462
61463             //
61464             // initialize the validator rulesets
61465             //
61466             validator.init = function() {
61467                 Object.values(Validations).forEach(function(validation) {
61468                     if (typeof validation !== 'function') { return; }
61469
61470                     var fn = validation(context);
61471                     var key = fn.type;
61472                     _rules[key] = fn;
61473                 });
61474
61475                 var disabledRules = corePreferences('validate-disabledRules');
61476                 if (disabledRules) {
61477                     disabledRules.split(',')
61478                         .forEach(function(key) { _disabledRules[key] = true; });
61479                 }
61480             };
61481
61482
61483             //
61484             // clear caches, called whenever iD resets after a save
61485             //
61486             validator.reset = function() {
61487                 Array.from(_deferred).forEach(function(handle) {
61488                     window.cancelIdleCallback(handle);
61489                     _deferred.delete(handle);
61490                 });
61491
61492                 // clear caches
61493                 _ignoredIssueIDs = {};
61494                 _baseCache = validationCache();
61495                 _headCache = validationCache();
61496                 _validatedGraph = null;
61497             };
61498
61499             validator.resetIgnoredIssues = function() {
61500                 _ignoredIssueIDs = {};
61501                 // reload UI
61502                 dispatch$1.call('validated');
61503             };
61504
61505
61506             // must update issues when the user changes the unsquare thereshold
61507             validator.reloadUnsquareIssues = function() {
61508
61509                 reloadUnsquareIssues(_headCache, context.graph());
61510                 reloadUnsquareIssues(_baseCache, context.history().base());
61511
61512                 dispatch$1.call('validated');
61513             };
61514
61515             function reloadUnsquareIssues(cache, graph) {
61516
61517                 var checkUnsquareWay = _rules.unsquare_way;
61518                 if (typeof checkUnsquareWay !== 'function') { return; }
61519
61520                 // uncache existing
61521                 cache.uncacheIssuesOfType('unsquare_way');
61522
61523                 var buildings = context.history().tree().intersects(geoExtent([-180,-90],[180, 90]), graph)  // everywhere
61524                     .filter(function(entity) {
61525                         return entity.type === 'way' && entity.tags.building && entity.tags.building !== 'no';
61526                     });
61527
61528                 // rerun for all buildings
61529                 buildings.forEach(function(entity) {
61530                     var detected = checkUnsquareWay(entity, graph);
61531                     if (detected.length !== 1) { return; }
61532                     var issue = detected[0];
61533                     if (!cache.issuesByEntityID[entity.id]) {
61534                         cache.issuesByEntityID[entity.id] = new Set();
61535                     }
61536                     cache.issuesByEntityID[entity.id].add(issue.id);
61537                     cache.issuesByIssueID[issue.id] = issue;
61538                 });
61539             }
61540
61541             // options = {
61542             //     what: 'all',     // 'all' or 'edited'
61543             //     where: 'all',   // 'all' or 'visible'
61544             //     includeIgnored: false   // true, false, or 'only'
61545             //     includeDisabledRules: false   // true, false, or 'only'
61546             // };
61547             validator.getIssues = function(options) {
61548                 var opts = Object.assign({ what: 'all', where: 'all', includeIgnored: false, includeDisabledRules: false }, options);
61549                 var issues = Object.values(_headCache.issuesByIssueID);
61550                 var view = context.map().extent();
61551
61552                 return issues.filter(function(issue) {
61553                     if (!issue) { return false; }
61554                     if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) { return false; }
61555                     if (!opts.includeDisabledRules && _disabledRules[issue.type]) { return false; }
61556
61557                     if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) { return false; }
61558                     if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) { return false; }
61559
61560                     // Sanity check:  This issue may be for an entity that not longer exists.
61561                     // If we detect this, uncache and return false so it is not included..
61562                     var entityIds = issue.entityIds || [];
61563                     for (var i = 0; i < entityIds.length; i++) {
61564                         var entityId = entityIds[i];
61565                         if (!context.hasEntity(entityId)) {
61566                             delete _headCache.issuesByEntityID[entityId];
61567                             delete _headCache.issuesByIssueID[issue.id];
61568                             return false;
61569                         }
61570                     }
61571
61572                     if (opts.what === 'edited' && _baseCache.issuesByIssueID[issue.id]) { return false; }
61573
61574                     if (opts.where === 'visible') {
61575                         var extent = issue.extent(context.graph());
61576                         if (!view.intersects(extent)) { return false; }
61577                     }
61578
61579                     return true;
61580                 });
61581             };
61582
61583             validator.getResolvedIssues = function() {
61584                 var baseIssues = Object.values(_baseCache.issuesByIssueID);
61585                 return baseIssues.filter(function(issue) {
61586                     return !_headCache.issuesByIssueID[issue.id];
61587                 });
61588             };
61589
61590             validator.focusIssue = function(issue) {
61591                 var extent = issue.extent(context.graph());
61592
61593                 if (extent) {
61594                     var setZoom = Math.max(context.map().zoom(), 19);
61595                     context.map().unobscuredCenterZoomEase(extent.center(), setZoom);
61596
61597                     // select the first entity
61598                     if (issue.entityIds && issue.entityIds.length) {
61599                         window.setTimeout(function() {
61600                             var ids = issue.entityIds;
61601                             context.enter(modeSelect(context, [ids[0]]));
61602                             dispatch$1.call('focusedIssue', this, issue);
61603                         }, 250);  // after ease
61604                     }
61605                 }
61606             };
61607
61608
61609             validator.getIssuesBySeverity = function(options) {
61610                 var groups = utilArrayGroupBy(validator.getIssues(options), 'severity');
61611                 groups.error = groups.error || [];
61612                 groups.warning = groups.warning || [];
61613                 return groups;
61614             };
61615
61616             // show some issue types in a particular order
61617             var orderedIssueTypes = [
61618                 // flag missing data first
61619                 'missing_tag', 'missing_role',
61620                 // then flag identity issues
61621                 'outdated_tags', 'mismatched_geometry',
61622                 // flag geometry issues where fixing them might solve connectivity issues
61623                 'crossing_ways', 'almost_junction',
61624                 // then flag connectivity issues
61625                 'disconnected_way', 'impossible_oneway'
61626             ];
61627
61628             // returns the issues that the given entity IDs have in common, matching the given options
61629             validator.getSharedEntityIssues = function(entityIDs, options) {
61630                 var cache = _headCache;
61631
61632                 // gather the issues that are common to all the entities
61633                 var issueIDs = entityIDs.reduce(function(acc, entityID) {
61634                     var entityIssueIDs = cache.issuesByEntityID[entityID] || new Set();
61635                     if (!acc) {
61636                         return new Set(entityIssueIDs);
61637                     }
61638                     return new Set([].concat( acc ).filter(function(elem) {
61639                         return entityIssueIDs.has(elem);
61640                     }));
61641                 }, null) || [];
61642
61643                 var opts = options || {};
61644
61645                 return Array.from(issueIDs)
61646                     .map(function(id) { return cache.issuesByIssueID[id]; })
61647                     .filter(function(issue) {
61648                         if (!issue) { return false; }
61649                         if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) { return false; }
61650                         if (!opts.includeDisabledRules && _disabledRules[issue.type]) { return false; }
61651
61652                         if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) { return false; }
61653                         if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) { return false; }
61654
61655                         return true;
61656                     }).sort(function(issue1, issue2) {
61657                         if (issue1.type === issue2.type) {
61658                             // issues of the same type, sort deterministically
61659                             return issue1.id < issue2.id ? -1 : 1;
61660                         }
61661                         var index1 = orderedIssueTypes.indexOf(issue1.type);
61662                         var index2 = orderedIssueTypes.indexOf(issue2.type);
61663                         if (index1 !== -1 && index2 !== -1) {
61664                             // both issue types have explicit sort orders
61665                             return index1 - index2;
61666                         } else if (index1 === -1 && index2 === -1) {
61667                             // neither issue type has an explicit sort order, sort by type
61668                             return issue1.type < issue2.type ? -1 : 1;
61669                         } else {
61670                             // order explicit types before everything else
61671                             return index1 !== -1 ? -1 : 1;
61672                         }
61673                     });
61674             };
61675
61676
61677             validator.getEntityIssues = function(entityID, options) {
61678                 return validator.getSharedEntityIssues([entityID], options);
61679             };
61680
61681
61682             validator.getRuleKeys = function() {
61683                 return Object.keys(_rules);
61684             };
61685
61686
61687             validator.isRuleEnabled = function(key) {
61688                 return !_disabledRules[key];
61689             };
61690
61691
61692             validator.toggleRule = function(key) {
61693                 if (_disabledRules[key]) {
61694                     delete _disabledRules[key];
61695                 } else {
61696                     _disabledRules[key] = true;
61697                 }
61698
61699                 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
61700                 validator.validate();
61701             };
61702
61703
61704             validator.disableRules = function(keys) {
61705                 _disabledRules = {};
61706                 keys.forEach(function(k) {
61707                     _disabledRules[k] = true;
61708                 });
61709
61710                 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
61711                 validator.validate();
61712             };
61713
61714
61715             validator.ignoreIssue = function(id) {
61716                 _ignoredIssueIDs[id] = true;
61717             };
61718
61719
61720             //
61721             // Run validation on a single entity for the given graph
61722             //
61723             function validateEntity(entity, graph) {
61724                 var entityIssues = [];
61725
61726                 // runs validation and appends resulting issues
61727                 function runValidation(key) {
61728
61729                     var fn = _rules[key];
61730                     if (typeof fn !== 'function') {
61731                         console.error('no such validation rule = ' + key);  // eslint-disable-line no-console
61732                         return;
61733                     }
61734
61735                     var detected = fn(entity, graph);
61736                     entityIssues = entityIssues.concat(detected);
61737                 }
61738
61739                 // run all rules
61740                 Object.keys(_rules).forEach(runValidation);
61741
61742                 return entityIssues;
61743             }
61744
61745             function entityIDsToValidate(entityIDs, graph) {
61746                 var processedIDs = new Set();
61747                 return entityIDs.reduce(function(acc, entityID) {
61748                     // keep redundancy check separate from `acc` because an `entityID`
61749                     // could have been added to `acc` as a related entity through an earlier pass
61750                     if (processedIDs.has(entityID)) { return acc; }
61751                     processedIDs.add(entityID);
61752
61753                     var entity = graph.hasEntity(entityID);
61754                     if (!entity) { return acc; }
61755
61756                     acc.add(entityID);
61757
61758                     var checkParentRels = [entity];
61759
61760                     if (entity.type === 'node') {
61761                         graph.parentWays(entity).forEach(function(parentWay) {
61762                             acc.add(parentWay.id); // include parent ways
61763                             checkParentRels.push(parentWay);
61764                         });
61765                     } else if (entity.type === 'relation') {
61766                         entity.members.forEach(function(member) {
61767                             acc.add(member.id); // include members
61768                         });
61769                     } else if (entity.type === 'way') {
61770                         entity.nodes.forEach(function(nodeID) {
61771                             acc.add(nodeID); // include child nodes
61772                             graph._parentWays[nodeID].forEach(function(wayID) {
61773                                 acc.add(wayID); // include connected ways
61774                             });
61775                         });
61776                     }
61777
61778                     checkParentRels.forEach(function(entity) {   // include parent relations
61779                         if (entity.type !== 'relation') {        // but not super-relations
61780                             graph.parentRelations(entity).forEach(function(parentRelation) {
61781                                 acc.add(parentRelation.id);
61782                             });
61783                         }
61784                     });
61785
61786                     return acc;
61787
61788                 }, new Set());
61789             }
61790
61791             //
61792             // Run validation for several entities, supplied `entityIDs`,
61793             // against `graph` for the given `cache`
61794             //
61795             function validateEntities(entityIDs, graph, cache) {
61796
61797                 // clear caches for existing issues related to these entities
61798                 entityIDs.forEach(cache.uncacheEntityID);
61799
61800                 // detect new issues and update caches
61801                 entityIDs.forEach(function(entityID) {
61802                     var entity = graph.hasEntity(entityID);
61803                     // don't validate deleted entities
61804                     if (!entity) { return; }
61805
61806                     var issues = validateEntity(entity, graph);
61807                     cache.cacheIssues(issues);
61808                 });
61809             }
61810
61811
61812             //
61813             // Validates anything that has changed since the last time it was run.
61814             // Also updates the "validatedGraph" to be the current graph
61815             // and dispatches a `validated` event when finished.
61816             //
61817             validator.validate = function() {
61818
61819                 var currGraph = context.graph();
61820                 _validatedGraph = _validatedGraph || context.history().base();
61821                 if (currGraph === _validatedGraph) {
61822                     dispatch$1.call('validated');
61823                     return;
61824                 }
61825                 var oldGraph = _validatedGraph;
61826                 var difference = coreDifference(oldGraph, currGraph);
61827                 _validatedGraph = currGraph;
61828
61829                 var createdAndModifiedEntityIDs = difference.extantIDs(true);   // created/modified (true = w/relation members)
61830                 var entityIDsToCheck = entityIDsToValidate(createdAndModifiedEntityIDs, currGraph);
61831
61832                 // check modified and deleted entities against the old graph in order to update their related entities
61833                 // (e.g. deleting the only highway connected to a road should create a disconnected highway issue)
61834                 var modifiedAndDeletedEntityIDs = difference.deleted().concat(difference.modified())
61835                     .map(function(entity) { return entity.id; });
61836                 var entityIDsToCheckForOldGraph = entityIDsToValidate(modifiedAndDeletedEntityIDs, oldGraph);
61837
61838                 // concat the sets
61839                 entityIDsToCheckForOldGraph.forEach(entityIDsToCheck.add, entityIDsToCheck);
61840
61841                 validateEntities(entityIDsToCheck, context.graph(), _headCache);
61842
61843                 dispatch$1.call('validated');
61844             };
61845
61846
61847             // WHEN TO RUN VALIDATION:
61848             // When graph changes:
61849             context.history()
61850                 .on('restore.validator', validator.validate)   // restore saved history
61851                 .on('undone.validator', validator.validate)    // undo
61852                 .on('redone.validator', validator.validate);   // redo
61853                 // but not on 'change' (e.g. while drawing)
61854
61855             // When user chages editing modes:
61856             context
61857                 .on('exit.validator', validator.validate);
61858
61859             // When merging fetched data:
61860             context.history()
61861                 .on('merge.validator', function(entities) {
61862                     if (!entities) { return; }
61863                     var handle = window.requestIdleCallback(function() {
61864                         var entityIDs = entities.map(function(entity) { return entity.id; });
61865                         var headGraph = context.graph();
61866                         validateEntities(entityIDsToValidate(entityIDs, headGraph), headGraph, _headCache);
61867
61868                         var baseGraph = context.history().base();
61869                         validateEntities(entityIDsToValidate(entityIDs, baseGraph), baseGraph, _baseCache);
61870
61871                         dispatch$1.call('validated');
61872                     });
61873                     _deferred.add(handle);
61874                 });
61875
61876
61877             return validator;
61878         }
61879
61880
61881         function validationCache() {
61882
61883             var cache = {
61884                 issuesByIssueID: {},  // issue.id -> issue
61885                 issuesByEntityID: {} // entity.id -> set(issue.id)
61886             };
61887
61888             cache.cacheIssues = function(issues) {
61889                 issues.forEach(function(issue) {
61890                     var entityIds = issue.entityIds || [];
61891                     entityIds.forEach(function(entityId) {
61892                         if (!cache.issuesByEntityID[entityId]) {
61893                             cache.issuesByEntityID[entityId] = new Set();
61894                         }
61895                         cache.issuesByEntityID[entityId].add(issue.id);
61896                     });
61897                     cache.issuesByIssueID[issue.id] = issue;
61898                 });
61899             };
61900
61901             cache.uncacheIssue = function(issue) {
61902                 // When multiple entities are involved (e.g. crossing_ways),
61903                 // remove this issue from the other entity caches too..
61904                 var entityIds = issue.entityIds || [];
61905                 entityIds.forEach(function(entityId) {
61906                     if (cache.issuesByEntityID[entityId]) {
61907                         cache.issuesByEntityID[entityId].delete(issue.id);
61908                     }
61909                 });
61910                 delete cache.issuesByIssueID[issue.id];
61911             };
61912
61913             cache.uncacheIssues = function(issues) {
61914                 issues.forEach(cache.uncacheIssue);
61915             };
61916
61917             cache.uncacheIssuesOfType = function(type) {
61918                 var issuesOfType = Object.values(cache.issuesByIssueID)
61919                     .filter(function(issue) { return issue.type === type; });
61920                 cache.uncacheIssues(issuesOfType);
61921             };
61922
61923             //
61924             // Remove a single entity and all its related issues from the caches
61925             //
61926             cache.uncacheEntityID = function(entityID) {
61927                 var issueIDs = cache.issuesByEntityID[entityID];
61928                 if (!issueIDs) { return; }
61929
61930                 issueIDs.forEach(function(issueID) {
61931                     var issue = cache.issuesByIssueID[issueID];
61932                     if (issue) {
61933                         cache.uncacheIssue(issue);
61934                     } else {
61935                         delete cache.issuesByIssueID[issueID];
61936                     }
61937                 });
61938
61939                 delete cache.issuesByEntityID[entityID];
61940             };
61941
61942             return cache;
61943         }
61944
61945         function coreUploader(context) {
61946
61947             var dispatch$1 = dispatch(
61948                 // Start and end events are dispatched exactly once each per legitimate outside call to `save`
61949                 'saveStarted', // dispatched as soon as a call to `save` has been deemed legitimate
61950                 'saveEnded',   // dispatched after the result event has been dispatched
61951
61952                 'willAttemptUpload', // dispatched before the actual upload call occurs, if it will
61953                 'progressChanged',
61954
61955                 // Each save results in one of these outcomes:
61956                 'resultNoChanges', // upload wasn't attempted since there were no edits
61957                 'resultErrors',    // upload failed due to errors
61958                 'resultConflicts', // upload failed due to data conflicts
61959                 'resultSuccess'    // upload completed without errors
61960             );
61961
61962             var _isSaving = false;
61963
61964             var _conflicts = [];
61965             var _errors = [];
61966             var _origChanges;
61967
61968             var _discardTags = {};
61969             _mainFileFetcher.get('discarded')
61970                 .then(function(d) { _discardTags = d; })
61971                 .catch(function() { /* ignore */ });
61972
61973             var uploader = utilRebind({}, dispatch$1, 'on');
61974
61975             uploader.isSaving = function() {
61976                 return _isSaving;
61977             };
61978
61979             uploader.save = function(changeset, tryAgain, checkConflicts) {
61980                 // Guard against accidentally entering save code twice - #4641
61981                 if (_isSaving && !tryAgain) {
61982                     return;
61983                 }
61984
61985                 var osm = context.connection();
61986                 if (!osm) { return; }
61987
61988                 // If user somehow got logged out mid-save, try to reauthenticate..
61989                 // This can happen if they were logged in from before, but the tokens are no longer valid.
61990                 if (!osm.authenticated()) {
61991                     osm.authenticate(function(err) {
61992                         if (!err) {
61993                             uploader.save(changeset, tryAgain, checkConflicts);  // continue where we left off..
61994                         }
61995                     });
61996                     return;
61997                 }
61998
61999                 if (!_isSaving) {
62000                     _isSaving = true;
62001                     dispatch$1.call('saveStarted', this);
62002                 }
62003
62004                 var history = context.history();
62005
62006                 _conflicts = [];
62007                 _errors = [];
62008
62009                 // Store original changes, in case user wants to download them as an .osc file
62010                 _origChanges = history.changes(actionDiscardTags(history.difference(), _discardTags));
62011
62012                 // First time, `history.perform` a no-op action.
62013                 // Any conflict resolutions will be done as `history.replace`
62014                 // Remember to pop this later if needed
62015                 if (!tryAgain) {
62016                     history.perform(actionNoop());
62017                 }
62018
62019                 // Attempt a fast upload.. If there are conflicts, re-enter with `checkConflicts = true`
62020                 if (!checkConflicts) {
62021                     upload(changeset);
62022
62023                 // Do the full (slow) conflict check..
62024                 } else {
62025                     performFullConflictCheck(changeset);
62026                 }
62027
62028             };
62029
62030
62031             function performFullConflictCheck(changeset) {
62032
62033                 var osm = context.connection();
62034                 if (!osm) { return; }
62035
62036                 var history = context.history();
62037
62038                 var localGraph = context.graph();
62039                 var remoteGraph = coreGraph(history.base(), true);
62040
62041                 var summary = history.difference().summary();
62042                 var _toCheck = [];
62043                 for (var i = 0; i < summary.length; i++) {
62044                     var item = summary[i];
62045                     if (item.changeType === 'modified') {
62046                         _toCheck.push(item.entity.id);
62047                     }
62048                 }
62049
62050                 var _toLoad = withChildNodes(_toCheck, localGraph);
62051                 var _loaded = {};
62052                 var _toLoadCount = 0;
62053                 var _toLoadTotal = _toLoad.length;
62054
62055                 if (_toCheck.length) {
62056                     dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
62057                     _toLoad.forEach(function(id) { _loaded[id] = false; });
62058                     osm.loadMultiple(_toLoad, loaded);
62059                 } else {
62060                     upload(changeset);
62061                 }
62062
62063                 return;
62064
62065                 function withChildNodes(ids, graph) {
62066                     var s = new Set(ids);
62067                     ids.forEach(function(id) {
62068                         var entity = graph.entity(id);
62069                         if (entity.type !== 'way') { return; }
62070
62071                         graph.childNodes(entity).forEach(function(child) {
62072                             if (child.version !== undefined) {
62073                                 s.add(child.id);
62074                             }
62075                         });
62076                     });
62077
62078                     return Array.from(s);
62079                 }
62080
62081
62082                 // Reload modified entities into an alternate graph and check for conflicts..
62083                 function loaded(err, result) {
62084                     if (_errors.length) { return; }
62085
62086                     if (err) {
62087                         _errors.push({
62088                             msg: err.message || err.responseText,
62089                             details: [ _t('save.status_code', { code: err.status }) ]
62090                         });
62091                         didResultInErrors();
62092
62093                     } else {
62094                         var loadMore = [];
62095
62096                         result.data.forEach(function(entity) {
62097                             remoteGraph.replace(entity);
62098                             _loaded[entity.id] = true;
62099                             _toLoad = _toLoad.filter(function(val) { return val !== entity.id; });
62100
62101                             if (!entity.visible) { return; }
62102
62103                             // Because loadMultiple doesn't download /full like loadEntity,
62104                             // need to also load children that aren't already being checked..
62105                             var i, id;
62106                             if (entity.type === 'way') {
62107                                 for (i = 0; i < entity.nodes.length; i++) {
62108                                     id = entity.nodes[i];
62109                                     if (_loaded[id] === undefined) {
62110                                         _loaded[id] = false;
62111                                         loadMore.push(id);
62112                                     }
62113                                 }
62114                             } else if (entity.type === 'relation' && entity.isMultipolygon()) {
62115                                 for (i = 0; i < entity.members.length; i++) {
62116                                     id = entity.members[i].id;
62117                                     if (_loaded[id] === undefined) {
62118                                         _loaded[id] = false;
62119                                         loadMore.push(id);
62120                                     }
62121                                 }
62122                             }
62123                         });
62124
62125                         _toLoadCount += result.data.length;
62126                         _toLoadTotal += loadMore.length;
62127                         dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
62128
62129                         if (loadMore.length) {
62130                             _toLoad.push.apply(_toLoad, loadMore);
62131                             osm.loadMultiple(loadMore, loaded);
62132                         }
62133
62134                         if (!_toLoad.length) {
62135                             detectConflicts();
62136                             upload(changeset);
62137                         }
62138                     }
62139                 }
62140
62141
62142                 function detectConflicts() {
62143                     function choice(id, text, action) {
62144                         return {
62145                             id: id,
62146                             text: text,
62147                             action: function() {
62148                                 history.replace(action);
62149                             }
62150                         };
62151                     }
62152                     function formatUser(d) {
62153                         return '<a href="' + osm.userURL(d) + '" target="_blank">' + d + '</a>';
62154                     }
62155                     function entityName(entity) {
62156                         return utilDisplayName(entity) || (utilDisplayType(entity.id) + ' ' + entity.id);
62157                     }
62158
62159                     function sameVersions(local, remote) {
62160                         if (local.version !== remote.version) { return false; }
62161
62162                         if (local.type === 'way') {
62163                             var children = utilArrayUnion(local.nodes, remote.nodes);
62164                             for (var i = 0; i < children.length; i++) {
62165                                 var a = localGraph.hasEntity(children[i]);
62166                                 var b = remoteGraph.hasEntity(children[i]);
62167                                 if (a && b && a.version !== b.version) { return false; }
62168                             }
62169                         }
62170
62171                         return true;
62172                     }
62173
62174                     _toCheck.forEach(function(id) {
62175                         var local = localGraph.entity(id);
62176                         var remote = remoteGraph.entity(id);
62177
62178                         if (sameVersions(local, remote)) { return; }
62179
62180                         var merge = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags, formatUser);
62181
62182                         history.replace(merge);
62183
62184                         var mergeConflicts = merge.conflicts();
62185                         if (!mergeConflicts.length) { return; }  // merged safely
62186
62187                         var forceLocal = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_local');
62188                         var forceRemote = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_remote');
62189                         var keepMine = _t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore'));
62190                         var keepTheirs = _t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
62191
62192                         _conflicts.push({
62193                             id: id,
62194                             name: entityName(local),
62195                             details: mergeConflicts,
62196                             chosen: 1,
62197                             choices: [
62198                                 choice(id, keepMine, forceLocal),
62199                                 choice(id, keepTheirs, forceRemote)
62200                             ]
62201                         });
62202                     });
62203                 }
62204             }
62205
62206
62207             function upload(changeset) {
62208                 var osm = context.connection();
62209                 if (!osm) {
62210                     _errors.push({ msg: 'No OSM Service' });
62211                 }
62212
62213                 if (_conflicts.length) {
62214                     didResultInConflicts(changeset);
62215
62216                 } else if (_errors.length) {
62217                     didResultInErrors();
62218
62219                 } else {
62220                     var history = context.history();
62221                     var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
62222                     if (changes.modified.length || changes.created.length || changes.deleted.length) {
62223
62224                         dispatch$1.call('willAttemptUpload', this);
62225
62226                         osm.putChangeset(changeset, changes, uploadCallback);
62227
62228                     } else {
62229                         // changes were insignificant or reverted by user
62230                         didResultInNoChanges();
62231                     }
62232                 }
62233             }
62234
62235
62236             function uploadCallback(err, changeset) {
62237                 if (err) {
62238                     if (err.status === 409) {  // 409 Conflict
62239                         uploader.save(changeset, true, true);  // tryAgain = true, checkConflicts = true
62240                     } else {
62241                         _errors.push({
62242                             msg: err.message || err.responseText,
62243                             details: [ _t('save.status_code', { code: err.status }) ]
62244                         });
62245                         didResultInErrors();
62246                     }
62247
62248                 } else {
62249                     didResultInSuccess(changeset);
62250                 }
62251             }
62252
62253             function didResultInNoChanges() {
62254
62255                 dispatch$1.call('resultNoChanges', this);
62256
62257                 endSave();
62258
62259                 context.flush(); // reset iD
62260             }
62261
62262             function didResultInErrors() {
62263
62264                 context.history().pop();
62265
62266                 dispatch$1.call('resultErrors', this, _errors);
62267
62268                 endSave();
62269             }
62270
62271
62272             function didResultInConflicts(changeset) {
62273
62274                 _conflicts.sort(function(a, b) { return b.id.localeCompare(a.id); });
62275
62276                 dispatch$1.call('resultConflicts', this, changeset, _conflicts, _origChanges);
62277
62278                 endSave();
62279             }
62280
62281
62282             function didResultInSuccess(changeset) {
62283
62284                 // delete the edit stack cached to local storage
62285                 context.history().clearSaved();
62286
62287                 dispatch$1.call('resultSuccess', this, changeset);
62288
62289                 // Add delay to allow for postgres replication #1646 #2678
62290                 window.setTimeout(function() {
62291
62292                     endSave();
62293
62294                     context.flush(); // reset iD
62295                 }, 2500);
62296             }
62297
62298
62299             function endSave() {
62300                 _isSaving = false;
62301
62302                 dispatch$1.call('saveEnded', this);
62303             }
62304
62305
62306             uploader.cancelConflictResolution = function() {
62307                 context.history().pop();
62308             };
62309
62310
62311             uploader.processResolvedConflicts = function(changeset) {
62312                 var history = context.history();
62313
62314                 for (var i = 0; i < _conflicts.length; i++) {
62315                     if (_conflicts[i].chosen === 1) {  // user chose "use theirs"
62316                         var entity = context.hasEntity(_conflicts[i].id);
62317                         if (entity && entity.type === 'way') {
62318                             var children = utilArrayUniq(entity.nodes);
62319                             for (var j = 0; j < children.length; j++) {
62320                                 history.replace(actionRevert(children[j]));
62321                             }
62322                         }
62323                         history.replace(actionRevert(_conflicts[i].id));
62324                     }
62325                 }
62326
62327                 uploader.save(changeset, true, false);  // tryAgain = true, checkConflicts = false
62328             };
62329
62330
62331             uploader.reset = function() {
62332
62333             };
62334
62335
62336             return uploader;
62337         }
62338
62339         var isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2;
62340
62341         // listen for DPI change, e.g. when dragging a browser window from a retina to non-retina screen
62342         window.matchMedia("\n        (-webkit-min-device-pixel-ratio: 2), /* Safari */\n        (min-resolution: 2dppx),             /* standard */\n        (min-resolution: 192dpi)             /* fallback */\n    ").addListener(function() {
62343
62344             isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2;
62345         });
62346
62347
62348         function localeDateString(s) {
62349             if (!s) { return null; }
62350             var options = { day: 'numeric', month: 'short', year: 'numeric' };
62351             var d = new Date(s);
62352             if (isNaN(d.getTime())) { return null; }
62353             return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
62354         }
62355
62356         function vintageRange(vintage) {
62357             var s;
62358             if (vintage.start || vintage.end) {
62359                 s = (vintage.start || '?');
62360                 if (vintage.start !== vintage.end) {
62361                     s += ' - ' + (vintage.end || '?');
62362                 }
62363             }
62364             return s;
62365         }
62366
62367
62368         function rendererBackgroundSource(data) {
62369             var source = Object.assign({}, data);   // shallow copy
62370             var _offset = [0, 0];
62371             var _name = source.name;
62372             var _description = source.description;
62373             var _best = !!source.best;
62374             var _template = source.encrypted ? utilAesDecrypt(source.template) : source.template;
62375
62376             source.tileSize = data.tileSize || 256;
62377             source.zoomExtent = data.zoomExtent || [0, 22];
62378             source.overzoom = data.overzoom !== false;
62379
62380             source.offset = function(val) {
62381                 if (!arguments.length) { return _offset; }
62382                 _offset = val;
62383                 return source;
62384             };
62385
62386
62387             source.nudge = function(val, zoomlevel) {
62388                 _offset[0] += val[0] / Math.pow(2, zoomlevel);
62389                 _offset[1] += val[1] / Math.pow(2, zoomlevel);
62390                 return source;
62391             };
62392
62393
62394             source.name = function() {
62395                 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
62396                 return _t('imagery.' + id_safe + '.name', { default: _name });
62397             };
62398
62399
62400             source.description = function() {
62401                 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
62402                 return _t('imagery.' + id_safe + '.description', { default: _description });
62403             };
62404
62405
62406             source.best = function() {
62407                 return _best;
62408             };
62409
62410
62411             source.area = function() {
62412                 if (!data.polygon) { return Number.MAX_VALUE; }  // worldwide
62413                 var area = d3_geoArea({ type: 'MultiPolygon', coordinates: [ data.polygon ] });
62414                 return isNaN(area) ? 0 : area;
62415             };
62416
62417
62418             source.imageryUsed = function() {
62419                 return name || source.id;
62420             };
62421
62422
62423             source.template = function(val) {
62424                 if (!arguments.length) { return _template; }
62425                 if (source.id === 'custom') {
62426                     _template = val;
62427                 }
62428                 return source;
62429             };
62430
62431
62432             source.url = function(coord) {
62433                 var result = _template;
62434                 if (result === '') { return result; }   // source 'none'
62435
62436
62437                 // Guess a type based on the tokens present in the template
62438                 // (This is for 'custom' source, where we don't know)
62439                 if (!source.type) {
62440                     if (/SERVICE=WMS|\{(proj|wkid|bbox)\}/.test(_template)) {
62441                         source.type = 'wms';
62442                         source.projection = 'EPSG:3857';  // guess
62443                     } else if (/\{(x|y)\}/.test(_template)) {
62444                         source.type = 'tms';
62445                     } else if (/\{u\}/.test(_template)) {
62446                         source.type = 'bing';
62447                     }
62448                 }
62449
62450
62451                 if (source.type === 'wms') {
62452                     var tileToProjectedCoords = (function(x, y, z) {
62453                         //polyfill for IE11, PhantomJS
62454                         var sinh = Math.sinh || function(x) {
62455                             var y = Math.exp(x);
62456                             return (y - 1 / y) / 2;
62457                         };
62458
62459                         var zoomSize = Math.pow(2, z);
62460                         var lon = x / zoomSize * Math.PI * 2 - Math.PI;
62461                         var lat = Math.atan(sinh(Math.PI * (1 - 2 * y / zoomSize)));
62462
62463                         switch (source.projection) {
62464                             case 'EPSG:4326':
62465                                 return {
62466                                     x: lon * 180 / Math.PI,
62467                                     y: lat * 180 / Math.PI
62468                                 };
62469                             default: // EPSG:3857 and synonyms
62470                                 var mercCoords = mercatorRaw(lon, lat);
62471                                 return {
62472                                     x: 20037508.34 / Math.PI * mercCoords[0],
62473                                     y: 20037508.34 / Math.PI * mercCoords[1]
62474                                 };
62475                         }
62476                     });
62477
62478                     var tileSize = source.tileSize;
62479                     var projection = source.projection;
62480                     var minXmaxY = tileToProjectedCoords(coord[0], coord[1], coord[2]);
62481                     var maxXminY = tileToProjectedCoords(coord[0]+1, coord[1]+1, coord[2]);
62482
62483                     result = result.replace(/\{(\w+)\}/g, function (token, key) {
62484                       switch (key) {
62485                         case 'width':
62486                         case 'height':
62487                             return tileSize;
62488                         case 'proj':
62489                             return projection;
62490                         case 'wkid':
62491                             return projection.replace(/^EPSG:/, '');
62492                         case 'bbox':
62493                             // WMS 1.3 flips x/y for some coordinate systems including EPSG:4326 - #7557
62494                             if (projection === 'EPSG:4326' &&
62495                                 // The CRS parameter implies version 1.3 (prior versions use SRS)
62496                                 /VERSION=1.3|CRS={proj}/.test(source.template())) {
62497                                 return maxXminY.y + ',' + minXmaxY.x + ',' + minXmaxY.y + ',' + maxXminY.x;
62498                             } else {
62499                                 return minXmaxY.x + ',' + maxXminY.y + ',' + maxXminY.x + ',' + minXmaxY.y;
62500                             }
62501                         case 'w':
62502                             return minXmaxY.x;
62503                         case 's':
62504                             return maxXminY.y;
62505                         case 'n':
62506                             return maxXminY.x;
62507                         case 'e':
62508                             return minXmaxY.y;
62509                         default:
62510                             return token;
62511                       }
62512                     });
62513
62514                 } else if (source.type === 'tms') {
62515                     result = result
62516                         .replace('{x}', coord[0])
62517                         .replace('{y}', coord[1])
62518                         // TMS-flipped y coordinate
62519                         .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1)
62520                         .replace(/\{z(oom)?\}/, coord[2])
62521                         // only fetch retina tiles for retina screens
62522                         .replace(/\{@2x\}|\{r\}/, isRetina ? '@2x' : '');
62523
62524                 } else if (source.type === 'bing') {
62525                     result = result
62526                         .replace('{u}', function() {
62527                             var u = '';
62528                             for (var zoom = coord[2]; zoom > 0; zoom--) {
62529                                 var b = 0;
62530                                 var mask = 1 << (zoom - 1);
62531                                 if ((coord[0] & mask) !== 0) { b++; }
62532                                 if ((coord[1] & mask) !== 0) { b += 2; }
62533                                 u += b.toString();
62534                             }
62535                             return u;
62536                         });
62537                 }
62538
62539                 // these apply to any type..
62540                 result = result.replace(/\{switch:([^}]+)\}/, function(s, r) {
62541                     var subdomains = r.split(',');
62542                     return subdomains[(coord[0] + coord[1]) % subdomains.length];
62543                 });
62544
62545
62546                 return result;
62547             };
62548
62549
62550             source.validZoom = function(z) {
62551                 return source.zoomExtent[0] <= z &&
62552                     (source.overzoom || source.zoomExtent[1] > z);
62553             };
62554
62555
62556             source.isLocatorOverlay = function() {
62557                 return source.id === 'mapbox_locator_overlay';
62558             };
62559
62560
62561             /* hides a source from the list, but leaves it available for use */
62562             source.isHidden = function() {
62563                 return source.id === 'DigitalGlobe-Premium-vintage' ||
62564                     source.id === 'DigitalGlobe-Standard-vintage';
62565             };
62566
62567
62568             source.copyrightNotices = function() {};
62569
62570
62571             source.getMetadata = function(center, tileCoord, callback) {
62572                 var vintage = {
62573                     start: localeDateString(source.startDate),
62574                     end: localeDateString(source.endDate)
62575                 };
62576                 vintage.range = vintageRange(vintage);
62577
62578                 var metadata = { vintage: vintage };
62579                 callback(null, metadata);
62580             };
62581
62582
62583             return source;
62584         }
62585
62586
62587         rendererBackgroundSource.Bing = function(data, dispatch) {
62588             // http://msdn.microsoft.com/en-us/library/ff701716.aspx
62589             // http://msdn.microsoft.com/en-us/library/ff701701.aspx
62590
62591             data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
62592
62593             var bing = rendererBackgroundSource(data);
62594             // var key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU'; // P2, JOSM, etc
62595             var key = 'Ak5oTE46TUbjRp08OFVcGpkARErDobfpuyNKa-W2mQ8wbt1K1KL8p1bIRwWwcF-Q';    // iD
62596
62597
62598             var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' + key;
62599             var cache = {};
62600             var inflight = {};
62601             var providers = [];
62602
62603             d3_json(url)
62604                 .then(function(json) {
62605                     providers = json.resourceSets[0].resources[0].imageryProviders.map(function(provider) {
62606                         return {
62607                             attribution: provider.attribution,
62608                             areas: provider.coverageAreas.map(function(area) {
62609                                 return {
62610                                     zoom: [area.zoomMin, area.zoomMax],
62611                                     extent: geoExtent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
62612                                 };
62613                             })
62614                         };
62615                     });
62616                     dispatch.call('change');
62617                 })
62618                 .catch(function() {
62619                     /* ignore */
62620                 });
62621
62622
62623             bing.copyrightNotices = function(zoom, extent) {
62624                 zoom = Math.min(zoom, 21);
62625                 return providers.filter(function(provider) {
62626                     return provider.areas.some(function(area) {
62627                         return extent.intersects(area.extent) &&
62628                             area.zoom[0] <= zoom &&
62629                             area.zoom[1] >= zoom;
62630                     });
62631                 }).map(function(provider) {
62632                     return provider.attribution;
62633                 }).join(', ');
62634             };
62635
62636
62637             bing.getMetadata = function(center, tileCoord, callback) {
62638                 var tileID = tileCoord.slice(0, 3).join('/');
62639                 var zoom = Math.min(tileCoord[2], 21);
62640                 var centerPoint = center[1] + ',' + center[0];  // lat,lng
62641                 var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint +
62642                         '?zl=' + zoom + '&key=' + key;
62643
62644                 if (inflight[tileID]) { return; }
62645
62646                 if (!cache[tileID]) {
62647                     cache[tileID] = {};
62648                 }
62649                 if (cache[tileID] && cache[tileID].metadata) {
62650                     return callback(null, cache[tileID].metadata);
62651                 }
62652
62653                 inflight[tileID] = true;
62654                 d3_json(url)
62655                     .then(function(result) {
62656                         delete inflight[tileID];
62657                         if (!result) {
62658                             throw new Error('Unknown Error');
62659                         }
62660                         var vintage = {
62661                             start: localeDateString(result.resourceSets[0].resources[0].vintageStart),
62662                             end: localeDateString(result.resourceSets[0].resources[0].vintageEnd)
62663                         };
62664                         vintage.range = vintageRange(vintage);
62665
62666                         var metadata = { vintage: vintage };
62667                         cache[tileID].metadata = metadata;
62668                         if (callback) { callback(null, metadata); }
62669                     })
62670                     .catch(function(err) {
62671                         delete inflight[tileID];
62672                         if (callback) { callback(err.message); }
62673                     });
62674             };
62675
62676
62677             bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
62678
62679
62680             return bing;
62681         };
62682
62683
62684
62685         rendererBackgroundSource.Esri = function(data) {
62686             // in addition to using the tilemap at zoom level 20, overzoom real tiles - #4327 (deprecated technique, but it works)
62687             if (data.template.match(/blankTile/) === null) {
62688                 data.template = data.template + '?blankTile=false';
62689             }
62690
62691             var esri = rendererBackgroundSource(data);
62692             var cache = {};
62693             var inflight = {};
62694             var _prevCenter;
62695
62696             // use a tilemap service to set maximum zoom for esri tiles dynamically
62697             // https://developers.arcgis.com/documentation/tiled-elevation-service/
62698             esri.fetchTilemap = function(center) {
62699                 // skip if we have already fetched a tilemap within 5km
62700                 if (_prevCenter && geoSphericalDistance(center, _prevCenter) < 5000) { return; }
62701                 _prevCenter = center;
62702
62703                 // tiles are available globally to zoom level 19, afterward they may or may not be present
62704                 var z = 20;
62705
62706                 // first generate a random url using the template
62707                 var dummyUrl = esri.url([1,2,3]);
62708
62709                 // calculate url z/y/x from the lat/long of the center of the map
62710                 var x = (Math.floor((center[0] + 180) / 360 * Math.pow(2, z)));
62711                 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)));
62712
62713                 // fetch an 8x8 grid to leverage cache
62714                 var tilemapUrl = dummyUrl.replace(/tile\/[0-9]+\/[0-9]+\/[0-9]+\?blankTile=false/, 'tilemap') + '/' + z + '/' + y + '/' + x + '/8/8';
62715
62716                 // make the request and introspect the response from the tilemap server
62717                 d3_json(tilemapUrl)
62718                     .then(function(tilemap) {
62719                         if (!tilemap) {
62720                             throw new Error('Unknown Error');
62721                         }
62722                         var hasTiles = true;
62723                         for (var i = 0; i < tilemap.data.length; i++) {
62724                             // 0 means an individual tile in the grid doesn't exist
62725                             if (!tilemap.data[i]) {
62726                                 hasTiles = false;
62727                                 break;
62728                             }
62729                         }
62730
62731                         // if any tiles are missing at level 20 we restrict maxZoom to 19
62732                         esri.zoomExtent[1] = (hasTiles ? 22 : 19);
62733                     })
62734                     .catch(function() {
62735                         /* ignore */
62736                     });
62737             };
62738
62739
62740             esri.getMetadata = function(center, tileCoord, callback) {
62741                 var tileID = tileCoord.slice(0, 3).join('/');
62742                 var zoom = Math.min(tileCoord[2], esri.zoomExtent[1]);
62743                 var centerPoint = center[0] + ',' + center[1];  // long, lat (as it should be)
62744                 var unknown = _t('info_panels.background.unknown');
62745                 var metadataLayer;
62746                 var vintage = {};
62747                 var metadata = {};
62748
62749                 if (inflight[tileID]) { return; }
62750
62751                 switch (true) {
62752                     case (zoom >= 20 && esri.id === 'EsriWorldImageryClarity'):
62753                         metadataLayer = 4;
62754                         break;
62755                     case zoom >= 19:
62756                         metadataLayer = 3;
62757                         break;
62758                     case zoom >= 17:
62759                         metadataLayer = 2;
62760                         break;
62761                     case zoom >= 13:
62762                         metadataLayer = 0;
62763                         break;
62764                     default:
62765                         metadataLayer = 99;
62766                 }
62767
62768                 var url;
62769                 // build up query using the layer appropriate to the current zoom
62770                 if (esri.id === 'EsriWorldImagery') {
62771                     url = 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/';
62772                 } else if (esri.id === 'EsriWorldImageryClarity') {
62773                     url = 'https://serviceslab.arcgisonline.com/arcgis/rest/services/Clarity_World_Imagery/MapServer/';
62774                 }
62775
62776                 url += metadataLayer + '/query?returnGeometry=false&geometry=' + centerPoint + '&inSR=4326&geometryType=esriGeometryPoint&outFields=*&f=json';
62777
62778                 if (!cache[tileID]) {
62779                     cache[tileID] = {};
62780                 }
62781                 if (cache[tileID] && cache[tileID].metadata) {
62782                     return callback(null, cache[tileID].metadata);
62783                 }
62784
62785                 // accurate metadata is only available >= 13
62786                 if (metadataLayer === 99) {
62787                     vintage = {
62788                         start: null,
62789                         end: null,
62790                         range: null
62791                     };
62792                     metadata = {
62793                         vintage: null,
62794                         source: unknown,
62795                         description: unknown,
62796                         resolution: unknown,
62797                         accuracy: unknown
62798                     };
62799
62800                     callback(null, metadata);
62801
62802                 } else {
62803                     inflight[tileID] = true;
62804                     d3_json(url)
62805                         .then(function(result) {
62806                             delete inflight[tileID];
62807                             if (!result) {
62808                                 throw new Error('Unknown Error');
62809                             } else if (result.features && result.features.length < 1) {
62810                                 throw new Error('No Results');
62811                             } else if (result.error && result.error.message) {
62812                                 throw new Error(result.error.message);
62813                             }
62814
62815                             // pass through the discrete capture date from metadata
62816                             var captureDate = localeDateString(result.features[0].attributes.SRC_DATE2);
62817                             vintage = {
62818                                 start: captureDate,
62819                                 end: captureDate,
62820                                 range: captureDate
62821                             };
62822                             metadata = {
62823                                 vintage: vintage,
62824                                 source: clean(result.features[0].attributes.NICE_NAME),
62825                                 description: clean(result.features[0].attributes.NICE_DESC),
62826                                 resolution: clean(+parseFloat(result.features[0].attributes.SRC_RES).toFixed(4)),
62827                                 accuracy: clean(+parseFloat(result.features[0].attributes.SRC_ACC).toFixed(4))
62828                             };
62829
62830                             // append units - meters
62831                             if (isFinite(metadata.resolution)) {
62832                                 metadata.resolution += ' m';
62833                             }
62834                             if (isFinite(metadata.accuracy)) {
62835                                 metadata.accuracy += ' m';
62836                             }
62837
62838                             cache[tileID].metadata = metadata;
62839                             if (callback) { callback(null, metadata); }
62840                         })
62841                         .catch(function(err) {
62842                             delete inflight[tileID];
62843                             if (callback) { callback(err.message); }
62844                         });
62845                 }
62846
62847
62848                 function clean(val) {
62849                     return String(val).trim() || unknown;
62850                 }
62851             };
62852
62853             return esri;
62854         };
62855
62856
62857         rendererBackgroundSource.None = function() {
62858             var source = rendererBackgroundSource({ id: 'none', template: '' });
62859
62860
62861             source.name = function() {
62862                 return _t('background.none');
62863             };
62864
62865
62866             source.imageryUsed = function() {
62867                 return null;
62868             };
62869
62870
62871             source.area = function() {
62872                 return -1;  // sources in background pane are sorted by area
62873             };
62874
62875
62876             return source;
62877         };
62878
62879
62880         rendererBackgroundSource.Custom = function(template) {
62881             var source = rendererBackgroundSource({ id: 'custom', template: template });
62882
62883
62884             source.name = function() {
62885                 return _t('background.custom');
62886             };
62887
62888
62889             source.imageryUsed = function() {
62890                 // sanitize personal connection tokens - #6801
62891                 var cleaned = source.template();
62892
62893                 // from query string parameters
62894                 if (cleaned.indexOf('?') !== -1) {
62895                     var parts = cleaned.split('?', 2);
62896                     var qs = utilStringQs(parts[1]);
62897
62898                     ['access_token', 'connectId', 'token'].forEach(function(param) {
62899                         if (qs[param]) {
62900                             qs[param] = '{apikey}';
62901                         }
62902                     });
62903                     cleaned = parts[0] + '?' + utilQsString(qs, true);  // true = soft encode
62904                 }
62905
62906                 // from wms/wmts api path parameters
62907                 cleaned = cleaned.replace(/token\/(\w+)/, 'token/{apikey}');
62908
62909                 return 'Custom (' + cleaned + ' )';
62910             };
62911
62912
62913             source.area = function() {
62914                 return -2;  // sources in background pane are sorted by area
62915             };
62916
62917
62918             return source;
62919         };
62920
62921         function rendererTileLayer(context) {
62922             var transformProp = utilPrefixCSSProperty('Transform');
62923             var tiler = utilTiler();
62924
62925             var _tileSize = 256;
62926             var _projection;
62927             var _cache = {};
62928             var _tileOrigin;
62929             var _zoom;
62930             var _source;
62931
62932
62933             function tileSizeAtZoom(d, z) {
62934                 var EPSILON = 0.002;    // close seams
62935                 return ((_tileSize * Math.pow(2, z - d[2])) / _tileSize) + EPSILON;
62936             }
62937
62938
62939             function atZoom(t, distance) {
62940                 var power = Math.pow(2, distance);
62941                 return [
62942                     Math.floor(t[0] * power),
62943                     Math.floor(t[1] * power),
62944                     t[2] + distance
62945                 ];
62946             }
62947
62948
62949             function lookUp(d) {
62950                 for (var up = -1; up > -d[2]; up--) {
62951                     var tile = atZoom(d, up);
62952                     if (_cache[_source.url(tile)] !== false) {
62953                         return tile;
62954                     }
62955                 }
62956             }
62957
62958
62959             function uniqueBy(a, n) {
62960                 var o = [];
62961                 var seen = {};
62962                 for (var i = 0; i < a.length; i++) {
62963                     if (seen[a[i][n]] === undefined) {
62964                         o.push(a[i]);
62965                         seen[a[i][n]] = true;
62966                     }
62967                 }
62968                 return o;
62969             }
62970
62971
62972             function addSource(d) {
62973                 d.push(_source.url(d));
62974                 return d;
62975             }
62976
62977
62978             // Update tiles based on current state of `projection`.
62979             function background(selection) {
62980                 _zoom = geoScaleToZoom(_projection.scale(), _tileSize);
62981
62982                 var pixelOffset;
62983                 if (_source) {
62984                     pixelOffset = [
62985                         _source.offset()[0] * Math.pow(2, _zoom),
62986                         _source.offset()[1] * Math.pow(2, _zoom)
62987                     ];
62988                 } else {
62989                     pixelOffset = [0, 0];
62990                 }
62991
62992                 var translate = [
62993                     _projection.translate()[0] + pixelOffset[0],
62994                     _projection.translate()[1] + pixelOffset[1]
62995                 ];
62996
62997                 tiler
62998                     .scale(_projection.scale() * 2 * Math.PI)
62999                     .translate(translate);
63000
63001                 _tileOrigin = [
63002                     _projection.scale() * Math.PI - translate[0],
63003                     _projection.scale() * Math.PI - translate[1]
63004                 ];
63005
63006                 render(selection);
63007             }
63008
63009
63010             // Derive the tiles onscreen, remove those offscreen and position them.
63011             // Important that this part not depend on `_projection` because it's
63012             // rentered when tiles load/error (see #644).
63013             function render(selection) {
63014                 if (!_source) { return; }
63015                 var requests = [];
63016                 var showDebug = context.getDebug('tile') && !_source.overlay;
63017
63018                 if (_source.validZoom(_zoom)) {
63019                     tiler.skipNullIsland(!!_source.overlay);
63020
63021                     tiler().forEach(function(d) {
63022                         addSource(d);
63023                         if (d[3] === '') { return; }
63024                         if (typeof d[3] !== 'string') { return; } // Workaround for #2295
63025                         requests.push(d);
63026                         if (_cache[d[3]] === false && lookUp(d)) {
63027                             requests.push(addSource(lookUp(d)));
63028                         }
63029                     });
63030
63031                     requests = uniqueBy(requests, 3).filter(function(r) {
63032                         // don't re-request tiles which have failed in the past
63033                         return _cache[r[3]] !== false;
63034                     });
63035                 }
63036
63037                 function load(d) {
63038                     _cache[d[3]] = true;
63039                     select(this)
63040                         .on('error', null)
63041                         .on('load', null)
63042                         .classed('tile-loaded', true);
63043                     render(selection);
63044                 }
63045
63046                 function error(d) {
63047                     _cache[d[3]] = false;
63048                     select(this)
63049                         .on('error', null)
63050                         .on('load', null)
63051                         .remove();
63052                     render(selection);
63053                 }
63054
63055                 function imageTransform(d) {
63056                     var ts = _tileSize * Math.pow(2, _zoom - d[2]);
63057                     var scale = tileSizeAtZoom(d, _zoom);
63058                     return 'translate(' +
63059                         ((d[0] * ts) - _tileOrigin[0]) + 'px,' +
63060                         ((d[1] * ts) - _tileOrigin[1]) + 'px) ' +
63061                         'scale(' + scale + ',' + scale + ')';
63062                 }
63063
63064                 function tileCenter(d) {
63065                     var ts = _tileSize * Math.pow(2, _zoom - d[2]);
63066                     return [
63067                         ((d[0] * ts) - _tileOrigin[0] + (ts / 2)),
63068                         ((d[1] * ts) - _tileOrigin[1] + (ts / 2))
63069                     ];
63070                 }
63071
63072                 function debugTransform(d) {
63073                     var coord = tileCenter(d);
63074                     return 'translate(' + coord[0] + 'px,' + coord[1] + 'px)';
63075                 }
63076
63077
63078                 // Pick a representative tile near the center of the viewport
63079                 // (This is useful for sampling the imagery vintage)
63080                 var dims = tiler.size();
63081                 var mapCenter = [dims[0] / 2, dims[1] / 2];
63082                 var minDist = Math.max(dims[0], dims[1]);
63083                 var nearCenter;
63084
63085                 requests.forEach(function(d) {
63086                     var c = tileCenter(d);
63087                     var dist = geoVecLength(c, mapCenter);
63088                     if (dist < minDist) {
63089                         minDist = dist;
63090                         nearCenter = d;
63091                     }
63092                 });
63093
63094
63095                 var image = selection.selectAll('img')
63096                     .data(requests, function(d) { return d[3]; });
63097
63098                 image.exit()
63099                     .style(transformProp, imageTransform)
63100                     .classed('tile-removing', true)
63101                     .classed('tile-center', false)
63102                     .each(function() {
63103                         var tile = select(this);
63104                         window.setTimeout(function() {
63105                             if (tile.classed('tile-removing')) {
63106                                 tile.remove();
63107                             }
63108                         }, 300);
63109                     });
63110
63111                 image.enter()
63112                   .append('img')
63113                     .attr('class', 'tile')
63114                     .attr('draggable', 'false')
63115                     .style('width', _tileSize + 'px')
63116                     .style('height', _tileSize + 'px')
63117                     .attr('src', function(d) { return d[3]; })
63118                     .on('error', error)
63119                     .on('load', load)
63120                   .merge(image)
63121                     .style(transformProp, imageTransform)
63122                     .classed('tile-debug', showDebug)
63123                     .classed('tile-removing', false)
63124                     .classed('tile-center', function(d) { return d === nearCenter; });
63125
63126
63127
63128                 var debug = selection.selectAll('.tile-label-debug')
63129                     .data(showDebug ? requests : [], function(d) { return d[3]; });
63130
63131                 debug.exit()
63132                     .remove();
63133
63134                 if (showDebug) {
63135                     var debugEnter = debug.enter()
63136                         .append('div')
63137                         .attr('class', 'tile-label-debug');
63138
63139                     debugEnter
63140                         .append('div')
63141                         .attr('class', 'tile-label-debug-coord');
63142
63143                     debugEnter
63144                         .append('div')
63145                         .attr('class', 'tile-label-debug-vintage');
63146
63147                     debug = debug.merge(debugEnter);
63148
63149                     debug
63150                         .style(transformProp, debugTransform);
63151
63152                     debug
63153                         .selectAll('.tile-label-debug-coord')
63154                         .text(function(d) { return d[2] + ' / ' + d[0] + ' / ' + d[1]; });
63155
63156                     debug
63157                         .selectAll('.tile-label-debug-vintage')
63158                         .each(function(d) {
63159                             var span = select(this);
63160                             var center = context.projection.invert(tileCenter(d));
63161                             _source.getMetadata(center, d, function(err, result) {
63162                                 span.text((result && result.vintage && result.vintage.range) ||
63163                                     _t('info_panels.background.vintage') + ': ' + _t('info_panels.background.unknown')
63164                                 );
63165                             });
63166                         });
63167                 }
63168
63169             }
63170
63171
63172             background.projection = function(val) {
63173                 if (!arguments.length) { return _projection; }
63174                 _projection = val;
63175                 return background;
63176             };
63177
63178
63179             background.dimensions = function(val) {
63180                 if (!arguments.length) { return tiler.size(); }
63181                 tiler.size(val);
63182                 return background;
63183             };
63184
63185
63186             background.source = function(val) {
63187                 if (!arguments.length) { return _source; }
63188                 _source = val;
63189                 _tileSize = _source.tileSize;
63190                 _cache = {};
63191                 tiler.tileSize(_source.tileSize).zoomExtent(_source.zoomExtent);
63192                 return background;
63193             };
63194
63195
63196             return background;
63197         }
63198
63199         var _imageryIndex = null;
63200
63201         function rendererBackground(context) {
63202           var dispatch$1 = dispatch('change');
63203           var detected = utilDetect();
63204           var baseLayer = rendererTileLayer(context).projection(context.projection);
63205           var _isValid = true;
63206           var _overlayLayers = [];
63207           var _brightness = 1;
63208           var _contrast = 1;
63209           var _saturation = 1;
63210           var _sharpness = 1;
63211
63212
63213           function ensureImageryIndex() {
63214             return _mainFileFetcher.get('imagery')
63215               .then(function (sources) {
63216                 if (_imageryIndex) { return _imageryIndex; }
63217
63218                 _imageryIndex = {
63219                   imagery: sources,
63220                   features: {}
63221                 };
63222
63223                 // use which-polygon to support efficient index and querying for imagery
63224                 var features = sources.map(function (source) {
63225                   if (!source.polygon) { return null; }
63226                   // workaround for editor-layer-index weirdness..
63227                   // Add an extra array nest to each element in `source.polygon`
63228                   // so the rings are not treated as a bunch of holes:
63229                   // what we have: [ [[outer],[hole],[hole]] ]
63230                   // what we want: [ [[outer]],[[outer]],[[outer]] ]
63231                   var rings = source.polygon.map(function (ring) { return [ring]; });
63232
63233                   var feature = {
63234                     type: 'Feature',
63235                     properties: { id: source.id },
63236                     geometry: { type: 'MultiPolygon', coordinates: rings }
63237                   };
63238
63239                   _imageryIndex.features[source.id] = feature;
63240                   return feature;
63241
63242                 }).filter(Boolean);
63243
63244                 _imageryIndex.query = whichPolygon_1({ type: 'FeatureCollection', features: features });
63245
63246
63247                 // Instantiate `rendererBackgroundSource` objects for each source
63248                 _imageryIndex.backgrounds = sources.map(function (source) {
63249                   if (source.type === 'bing') {
63250                     return rendererBackgroundSource.Bing(source, dispatch$1);
63251                   } else if (/^EsriWorldImagery/.test(source.id)) {
63252                     return rendererBackgroundSource.Esri(source);
63253                   } else {
63254                     return rendererBackgroundSource(source);
63255                   }
63256                 });
63257
63258                 // Add 'None'
63259                 _imageryIndex.backgrounds.unshift(rendererBackgroundSource.None());
63260
63261                 // Add 'Custom'
63262                 var template = corePreferences('background-custom-template') || '';
63263                 var custom = rendererBackgroundSource.Custom(template);
63264                 _imageryIndex.backgrounds.unshift(custom);
63265
63266                 return _imageryIndex;
63267               });
63268           }
63269
63270
63271           function background(selection) {
63272             var currSource = baseLayer.source();
63273
63274             // If we are displaying an Esri basemap at high zoom,
63275             // check its tilemap to see how high the zoom can go
63276             if (context.map().zoom() > 18) {
63277               if (currSource && /^EsriWorldImagery/.test(currSource.id)) {
63278                 var center = context.map().center();
63279                 currSource.fetchTilemap(center);
63280               }
63281             }
63282
63283             // Is the imagery valid here? - #4827
63284             var sources = background.sources(context.map().extent());
63285             var wasValid = _isValid;
63286             _isValid = !!sources.filter(function (d) { return d === currSource; }).length;
63287
63288             if (wasValid !== _isValid) {      // change in valid status
63289               background.updateImagery();
63290             }
63291
63292
63293             var baseFilter = '';
63294             if (detected.cssfilters) {
63295               if (_brightness !== 1) {
63296                 baseFilter += " brightness(" + _brightness + ")";
63297               }
63298               if (_contrast !== 1) {
63299                 baseFilter += " contrast(" + _contrast + ")";
63300               }
63301               if (_saturation !== 1) {
63302                 baseFilter += " saturate(" + _saturation + ")";
63303               }
63304               if (_sharpness < 1) {  // gaussian blur
63305                 var blur = d3_interpolateNumber(0.5, 5)(1 - _sharpness);
63306                 baseFilter += " blur(" + blur + "px)";
63307               }
63308             }
63309
63310             var base = selection.selectAll('.layer-background')
63311               .data([0]);
63312
63313             base = base.enter()
63314               .insert('div', '.layer-data')
63315               .attr('class', 'layer layer-background')
63316               .merge(base);
63317
63318             if (detected.cssfilters) {
63319               base.style('filter', baseFilter || null);
63320             } else {
63321               base.style('opacity', _brightness);
63322             }
63323
63324
63325             var imagery = base.selectAll('.layer-imagery')
63326               .data([0]);
63327
63328             imagery.enter()
63329               .append('div')
63330               .attr('class', 'layer layer-imagery')
63331               .merge(imagery)
63332               .call(baseLayer);
63333
63334
63335             var maskFilter = '';
63336             var mixBlendMode = '';
63337             if (detected.cssfilters && _sharpness > 1) {  // apply unsharp mask
63338               mixBlendMode = 'overlay';
63339               maskFilter = 'saturate(0) blur(3px) invert(1)';
63340
63341               var contrast = _sharpness - 1;
63342               maskFilter += " contrast(" + contrast + ")";
63343
63344               var brightness = d3_interpolateNumber(1, 0.85)(_sharpness - 1);
63345               maskFilter += " brightness(" + brightness + ")";
63346             }
63347
63348             var mask = base.selectAll('.layer-unsharp-mask')
63349               .data(detected.cssfilters && _sharpness > 1 ? [0] : []);
63350
63351             mask.exit()
63352               .remove();
63353
63354             mask.enter()
63355               .append('div')
63356               .attr('class', 'layer layer-mask layer-unsharp-mask')
63357               .merge(mask)
63358               .call(baseLayer)
63359               .style('filter', maskFilter || null)
63360               .style('mix-blend-mode', mixBlendMode || null);
63361
63362
63363             var overlays = selection.selectAll('.layer-overlay')
63364               .data(_overlayLayers, function (d) { return d.source().name(); });
63365
63366             overlays.exit()
63367               .remove();
63368
63369             overlays.enter()
63370               .insert('div', '.layer-data')
63371               .attr('class', 'layer layer-overlay')
63372               .merge(overlays)
63373               .each(function (layer, i, nodes) { return select(nodes[i]).call(layer); });
63374           }
63375
63376
63377           background.updateImagery = function() {
63378             var currSource = baseLayer.source();
63379             if (context.inIntro() || !currSource) { return; }
63380
63381             var o = _overlayLayers
63382               .filter(function (d) { return !d.source().isLocatorOverlay() && !d.source().isHidden(); })
63383               .map(function (d) { return d.source().id; })
63384               .join(',');
63385
63386             var meters = geoOffsetToMeters(currSource.offset());
63387             var EPSILON = 0.01;
63388             var x = +meters[0].toFixed(2);
63389             var y = +meters[1].toFixed(2);
63390             var hash = utilStringQs(window.location.hash);
63391
63392             var id = currSource.id;
63393             if (id === 'custom') {
63394               id = "custom:" + (currSource.template());
63395             }
63396
63397             if (id) {
63398               hash.background = id;
63399             } else {
63400               delete hash.background;
63401             }
63402
63403             if (o) {
63404               hash.overlays = o;
63405             } else {
63406               delete hash.overlays;
63407             }
63408
63409             if (Math.abs(x) > EPSILON || Math.abs(y) > EPSILON) {
63410               hash.offset = x + "," + y;
63411             } else {
63412               delete hash.offset;
63413             }
63414
63415             if (!window.mocha) {
63416               window.location.replace('#' + utilQsString(hash, true));
63417             }
63418
63419             var imageryUsed = [];
63420             var photoOverlaysUsed = [];
63421
63422             var currUsed = currSource.imageryUsed();
63423             if (currUsed && _isValid) {
63424               imageryUsed.push(currUsed);
63425             }
63426
63427             _overlayLayers
63428               .filter(function (d) { return !d.source().isLocatorOverlay() && !d.source().isHidden(); })
63429               .forEach(function (d) { return imageryUsed.push(d.source().imageryUsed()); });
63430
63431             var dataLayer = context.layers().layer('data');
63432             if (dataLayer && dataLayer.enabled() && dataLayer.hasData()) {
63433               imageryUsed.push(dataLayer.getSrc());
63434             }
63435
63436             var photoOverlayLayers = {
63437               streetside: 'Bing Streetside',
63438               mapillary: 'Mapillary Images',
63439               'mapillary-map-features': 'Mapillary Map Features',
63440               'mapillary-signs': 'Mapillary Signs',
63441               openstreetcam: 'OpenStreetCam Images'
63442             };
63443
63444             for (var layerID in photoOverlayLayers) {
63445               var layer = context.layers().layer(layerID);
63446               if (layer && layer.enabled()) {
63447                 photoOverlaysUsed.push(layerID);
63448                 imageryUsed.push(photoOverlayLayers[layerID]);
63449               }
63450             }
63451
63452             context.history().imageryUsed(imageryUsed);
63453             context.history().photoOverlaysUsed(photoOverlaysUsed);
63454           };
63455
63456
63457           background.sources = function (extent, zoom, includeCurrent) {
63458             if (!_imageryIndex) { return []; }   // called before init()?
63459
63460             var visible = {};
63461             (_imageryIndex.query.bbox(extent.rectangle(), true) || [])
63462               .forEach(function (d) { return visible[d.id] = true; });
63463
63464             var currSource = baseLayer.source();
63465
63466             return _imageryIndex.backgrounds.filter(function (source) {
63467               if (!source.polygon) { return true; }                          // always include imagery with worldwide coverage
63468               if (includeCurrent && currSource === source) { return true; }  // optionally include the current imagery
63469               if (zoom && zoom < 6) { return false; }                        // optionally exclude local imagery at low zooms
63470               return visible[source.id];                                 // include imagery visible in given extent
63471             });
63472           };
63473
63474
63475           background.dimensions = function (val) {
63476             if (!val) { return; }
63477             baseLayer.dimensions(val);
63478             _overlayLayers.forEach(function (layer) { return layer.dimensions(val); });
63479           };
63480
63481
63482           background.baseLayerSource = function(d) {
63483             if (!arguments.length) { return baseLayer.source(); }
63484
63485             // test source against OSM imagery blacklists..
63486             var osm = context.connection();
63487             if (!osm) { return background; }
63488
63489             var blacklists = osm.imageryBlacklists();
63490             var template = d.template();
63491             var fail = false;
63492             var tested = 0;
63493             var regex;
63494
63495             for (var i = 0; i < blacklists.length; i++) {
63496               try {
63497                 regex = new RegExp(blacklists[i]);
63498                 fail = regex.test(template);
63499                 tested++;
63500                 if (fail) { break; }
63501               } catch (e) {
63502                 /* noop */
63503               }
63504             }
63505
63506             // ensure at least one test was run.
63507             if (!tested) {
63508               regex = new RegExp('.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*');
63509               fail = regex.test(template);
63510             }
63511
63512             baseLayer.source(!fail ? d : background.findSource('none'));
63513             dispatch$1.call('change');
63514             background.updateImagery();
63515             return background;
63516           };
63517
63518
63519           background.findSource = function (id) {
63520             if (!id || !_imageryIndex) { return null; }   // called before init()?
63521             return _imageryIndex.backgrounds.find(function (d) { return d.id && d.id === id; });
63522           };
63523
63524
63525           background.bing = function () {
63526             background.baseLayerSource(background.findSource('Bing'));
63527           };
63528
63529
63530           background.showsLayer = function (d) {
63531             var currSource = baseLayer.source();
63532             if (!d || !currSource) { return false; }
63533             return d.id === currSource.id || _overlayLayers.some(function (layer) { return d.id === layer.source().id; });
63534           };
63535
63536
63537           background.overlayLayerSources = function () {
63538             return _overlayLayers.map(function (layer) { return layer.source(); });
63539           };
63540
63541
63542           background.toggleOverlayLayer = function (d) {
63543             var layer;
63544             for (var i = 0; i < _overlayLayers.length; i++) {
63545               layer = _overlayLayers[i];
63546               if (layer.source() === d) {
63547                 _overlayLayers.splice(i, 1);
63548                 dispatch$1.call('change');
63549                 background.updateImagery();
63550                 return;
63551               }
63552             }
63553
63554             layer = rendererTileLayer(context)
63555               .source(d)
63556               .projection(context.projection)
63557               .dimensions(baseLayer.dimensions()
63558             );
63559
63560             _overlayLayers.push(layer);
63561             dispatch$1.call('change');
63562             background.updateImagery();
63563           };
63564
63565
63566           background.nudge = function (d, zoom) {
63567             var currSource = baseLayer.source();
63568             if (currSource) {
63569               currSource.nudge(d, zoom);
63570               dispatch$1.call('change');
63571               background.updateImagery();
63572             }
63573             return background;
63574           };
63575
63576
63577           background.offset = function(d) {
63578             var currSource = baseLayer.source();
63579             if (!arguments.length) {
63580               return (currSource && currSource.offset()) || [0, 0];
63581             }
63582             if (currSource) {
63583               currSource.offset(d);
63584               dispatch$1.call('change');
63585               background.updateImagery();
63586             }
63587             return background;
63588           };
63589
63590
63591           background.brightness = function(d) {
63592             if (!arguments.length) { return _brightness; }
63593             _brightness = d;
63594             if (context.mode()) { dispatch$1.call('change'); }
63595             return background;
63596           };
63597
63598
63599           background.contrast = function(d) {
63600             if (!arguments.length) { return _contrast; }
63601             _contrast = d;
63602             if (context.mode()) { dispatch$1.call('change'); }
63603             return background;
63604           };
63605
63606
63607           background.saturation = function(d) {
63608             if (!arguments.length) { return _saturation; }
63609             _saturation = d;
63610             if (context.mode()) { dispatch$1.call('change'); }
63611             return background;
63612           };
63613
63614
63615           background.sharpness = function(d) {
63616             if (!arguments.length) { return _sharpness; }
63617             _sharpness = d;
63618             if (context.mode()) { dispatch$1.call('change'); }
63619             return background;
63620           };
63621
63622           var _loadPromise;
63623
63624           background.ensureLoaded = function () {
63625
63626             if (_loadPromise) { return _loadPromise; }
63627
63628             function parseMapParams(qmap) {
63629               if (!qmap) { return false; }
63630               var params = qmap.split('/').map(Number);
63631               if (params.length < 3 || params.some(isNaN)) { return false; }
63632               return geoExtent([params[2], params[1]]);  // lon,lat
63633             }
63634
63635             var hash = utilStringQs(window.location.hash);
63636             var requested = hash.background || hash.layer;
63637             var extent = parseMapParams(hash.map);
63638
63639             return _loadPromise = ensureImageryIndex()
63640               .then(function (imageryIndex) {
63641                 var first = imageryIndex.backgrounds.length && imageryIndex.backgrounds[0];
63642
63643                 var best;
63644                 if (!requested && extent) {
63645                   best = background.sources(extent).find(function (s) { return s.best(); });
63646                 }
63647
63648                 // Decide which background layer to display
63649                 if (requested && requested.indexOf('custom:') === 0) {
63650                   var template = requested.replace(/^custom:/, '');
63651                   var custom = background.findSource('custom');
63652                   background.baseLayerSource(custom.template(template));
63653                   corePreferences('background-custom-template', template);
63654                 } else {
63655                   background.baseLayerSource(
63656                     background.findSource(requested) ||
63657                     best ||
63658                     background.findSource(corePreferences('background-last-used')) ||
63659                     background.findSource('Bing') ||
63660                     first ||
63661                     background.findSource('none')
63662                   );
63663                 }
63664
63665                 var locator = imageryIndex.backgrounds.find(function (d) { return d.overlay && d.default; });
63666                 if (locator) {
63667                   background.toggleOverlayLayer(locator);
63668                 }
63669
63670                 var overlays = (hash.overlays || '').split(',');
63671                 overlays.forEach(function (overlay) {
63672                   overlay = background.findSource(overlay);
63673                   if (overlay) {
63674                     background.toggleOverlayLayer(overlay);
63675                   }
63676                 });
63677
63678                 if (hash.gpx) {
63679                   var gpx = context.layers().layer('data');
63680                   if (gpx) {
63681                     gpx.url(hash.gpx, '.gpx');
63682                   }
63683                 }
63684
63685                 if (hash.offset) {
63686                   var offset = hash.offset
63687                     .replace(/;/g, ',')
63688                     .split(',')
63689                     .map(function (n) { return !isNaN(n) && n; });
63690
63691                   if (offset.length === 2) {
63692                     background.offset(geoMetersToOffset(offset));
63693                   }
63694                 }
63695               })
63696               .catch(function () { /* ignore */ });
63697           };
63698
63699
63700           return utilRebind(background, dispatch$1, 'on');
63701         }
63702
63703         function rendererFeatures(context) {
63704             var dispatch$1 = dispatch('change', 'redraw');
63705             var features = utilRebind({}, dispatch$1, 'on');
63706             var _deferred = new Set();
63707
63708             var traffic_roads = {
63709                 'motorway': true,
63710                 'motorway_link': true,
63711                 'trunk': true,
63712                 'trunk_link': true,
63713                 'primary': true,
63714                 'primary_link': true,
63715                 'secondary': true,
63716                 'secondary_link': true,
63717                 'tertiary': true,
63718                 'tertiary_link': true,
63719                 'residential': true,
63720                 'unclassified': true,
63721                 'living_street': true
63722             };
63723
63724             var service_roads = {
63725                 'service': true,
63726                 'road': true,
63727                 'track': true
63728             };
63729
63730             var paths = {
63731                 'path': true,
63732                 'footway': true,
63733                 'cycleway': true,
63734                 'bridleway': true,
63735                 'steps': true,
63736                 'pedestrian': true
63737             };
63738
63739             var past_futures = {
63740                 'proposed': true,
63741                 'construction': true,
63742                 'abandoned': true,
63743                 'dismantled': true,
63744                 'disused': true,
63745                 'razed': true,
63746                 'demolished': true,
63747                 'obliterated': true
63748             };
63749
63750             var _cullFactor = 1;
63751             var _cache = {};
63752             var _rules = {};
63753             var _stats = {};
63754             var _keys = [];
63755             var _hidden = [];
63756             var _forceVisible = {};
63757
63758
63759             function update() {
63760                 if (!window.mocha) {
63761                     var hash = utilStringQs(window.location.hash);
63762                     var disabled = features.disabled();
63763                     if (disabled.length) {
63764                         hash.disable_features = disabled.join(',');
63765                     } else {
63766                         delete hash.disable_features;
63767                     }
63768                     window.location.replace('#' + utilQsString(hash, true));
63769                     corePreferences('disabled-features', disabled.join(','));
63770                 }
63771                 _hidden = features.hidden();
63772                 dispatch$1.call('change');
63773                 dispatch$1.call('redraw');
63774             }
63775
63776
63777             function defineRule(k, filter, max) {
63778                 var isEnabled = true;
63779
63780                 _keys.push(k);
63781                 _rules[k] = {
63782                     filter: filter,
63783                     enabled: isEnabled,   // whether the user wants it enabled..
63784                     count: 0,
63785                     currentMax: (max || Infinity),
63786                     defaultMax: (max || Infinity),
63787                     enable: function() { this.enabled = true; this.currentMax = this.defaultMax; },
63788                     disable: function() { this.enabled = false; this.currentMax = 0; },
63789                     hidden: function() {
63790                         return (this.count === 0 && !this.enabled) ||
63791                             this.count > this.currentMax * _cullFactor;
63792                     },
63793                     autoHidden: function() { return this.hidden() && this.currentMax > 0; }
63794                 };
63795             }
63796
63797
63798             defineRule('points', function isPoint(tags, geometry) {
63799                 return geometry === 'point';
63800             }, 200);
63801
63802             defineRule('traffic_roads', function isTrafficRoad(tags) {
63803                 return traffic_roads[tags.highway];
63804             });
63805
63806             defineRule('service_roads', function isServiceRoad(tags) {
63807                 return service_roads[tags.highway];
63808             });
63809
63810             defineRule('paths', function isPath(tags) {
63811                 return paths[tags.highway];
63812             });
63813
63814             defineRule('buildings', function isBuilding(tags) {
63815                 return (
63816                     (!!tags.building && tags.building !== 'no') ||
63817                     tags.parking === 'multi-storey' ||
63818                     tags.parking === 'sheds' ||
63819                     tags.parking === 'carports' ||
63820                     tags.parking === 'garage_boxes'
63821                 );
63822             }, 250);
63823
63824             defineRule('building_parts', function isBuildingPart(tags) {
63825                 return tags['building:part'];
63826             });
63827
63828             defineRule('indoor', function isIndoor(tags) {
63829                 return tags.indoor;
63830             });
63831
63832             defineRule('landuse', function isLanduse(tags, geometry) {
63833                 return geometry === 'area' &&
63834                     !_rules.buildings.filter(tags) &&
63835                     !_rules.building_parts.filter(tags) &&
63836                     !_rules.indoor.filter(tags) &&
63837                     !_rules.water.filter(tags) &&
63838                     !_rules.pistes.filter(tags);
63839             });
63840
63841             defineRule('boundaries', function isBoundary(tags) {
63842                 return (
63843                     !!tags.boundary
63844                 ) && !(
63845                     traffic_roads[tags.highway] ||
63846                     service_roads[tags.highway] ||
63847                     paths[tags.highway] ||
63848                     tags.waterway ||
63849                     tags.railway ||
63850                     tags.landuse ||
63851                     tags.natural ||
63852                     tags.building ||
63853                     tags.power
63854                 );
63855             });
63856
63857             defineRule('water', function isWater(tags) {
63858                 return (
63859                     !!tags.waterway ||
63860                     tags.natural === 'water' ||
63861                     tags.natural === 'coastline' ||
63862                     tags.natural === 'bay' ||
63863                     tags.landuse === 'pond' ||
63864                     tags.landuse === 'basin' ||
63865                     tags.landuse === 'reservoir' ||
63866                     tags.landuse === 'salt_pond'
63867                 );
63868             });
63869
63870             defineRule('rail', function isRail(tags) {
63871                 return (
63872                     !!tags.railway ||
63873                     tags.landuse === 'railway'
63874                 ) && !(
63875                     traffic_roads[tags.highway] ||
63876                     service_roads[tags.highway] ||
63877                     paths[tags.highway]
63878                 );
63879             });
63880
63881             defineRule('pistes', function isPiste(tags) {
63882                 return tags['piste:type'];
63883             });
63884
63885             defineRule('aerialways', function isPiste(tags) {
63886                 return tags.aerialway &&
63887                     tags.aerialway !== 'yes' &&
63888                     tags.aerialway !== 'station';
63889             });
63890
63891             defineRule('power', function isPower(tags) {
63892                 return !!tags.power;
63893             });
63894
63895             // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
63896             defineRule('past_future', function isPastFuture(tags) {
63897                 if (
63898                     traffic_roads[tags.highway] ||
63899                     service_roads[tags.highway] ||
63900                     paths[tags.highway]
63901                 ) { return false; }
63902
63903                 var strings = Object.keys(tags);
63904
63905                 for (var i = 0; i < strings.length; i++) {
63906                     var s = strings[i];
63907                     if (past_futures[s] || past_futures[tags[s]]) { return true; }
63908                 }
63909                 return false;
63910             });
63911
63912             // Lines or areas that don't match another feature filter.
63913             // IMPORTANT: The 'others' feature must be the last one defined,
63914             //   so that code in getMatches can skip this test if `hasMatch = true`
63915             defineRule('others', function isOther(tags, geometry) {
63916                 return (geometry === 'line' || geometry === 'area');
63917             });
63918
63919
63920
63921             features.features = function() {
63922                 return _rules;
63923             };
63924
63925
63926             features.keys = function() {
63927                 return _keys;
63928             };
63929
63930
63931             features.enabled = function(k) {
63932                 if (!arguments.length) {
63933                     return _keys.filter(function(k) { return _rules[k].enabled; });
63934                 }
63935                 return _rules[k] && _rules[k].enabled;
63936             };
63937
63938
63939             features.disabled = function(k) {
63940                 if (!arguments.length) {
63941                     return _keys.filter(function(k) { return !_rules[k].enabled; });
63942                 }
63943                 return _rules[k] && !_rules[k].enabled;
63944             };
63945
63946
63947             features.hidden = function(k) {
63948                 if (!arguments.length) {
63949                     return _keys.filter(function(k) { return _rules[k].hidden(); });
63950                 }
63951                 return _rules[k] && _rules[k].hidden();
63952             };
63953
63954
63955             features.autoHidden = function(k) {
63956                 if (!arguments.length) {
63957                     return _keys.filter(function(k) { return _rules[k].autoHidden(); });
63958                 }
63959                 return _rules[k] && _rules[k].autoHidden();
63960             };
63961
63962
63963             features.enable = function(k) {
63964                 if (_rules[k] && !_rules[k].enabled) {
63965                     _rules[k].enable();
63966                     update();
63967                 }
63968             };
63969
63970             features.enableAll = function() {
63971                 var didEnable = false;
63972                 for (var k in _rules) {
63973                     if (!_rules[k].enabled) {
63974                         didEnable = true;
63975                         _rules[k].enable();
63976                     }
63977                 }
63978                 if (didEnable) { update(); }
63979             };
63980
63981
63982             features.disable = function(k) {
63983                 if (_rules[k] && _rules[k].enabled) {
63984                     _rules[k].disable();
63985                     update();
63986                 }
63987             };
63988
63989             features.disableAll = function() {
63990                 var didDisable = false;
63991                 for (var k in _rules) {
63992                     if (_rules[k].enabled) {
63993                         didDisable = true;
63994                         _rules[k].disable();
63995                     }
63996                 }
63997                 if (didDisable) { update(); }
63998             };
63999
64000
64001             features.toggle = function(k) {
64002                 if (_rules[k]) {
64003                     (function(f) { return f.enabled ? f.disable() : f.enable(); }(_rules[k]));
64004                     update();
64005                 }
64006             };
64007
64008
64009             features.resetStats = function() {
64010                 for (var i = 0; i < _keys.length; i++) {
64011                     _rules[_keys[i]].count = 0;
64012                 }
64013                 dispatch$1.call('change');
64014             };
64015
64016
64017             features.gatherStats = function(d, resolver, dimensions) {
64018                 var needsRedraw = false;
64019                 var types = utilArrayGroupBy(d, 'type');
64020                 var entities = [].concat(types.relation || [], types.way || [], types.node || []);
64021                 var currHidden, geometry, matches, i, j;
64022
64023                 for (i = 0; i < _keys.length; i++) {
64024                     _rules[_keys[i]].count = 0;
64025                 }
64026
64027                 // adjust the threshold for point/building culling based on viewport size..
64028                 // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
64029                 _cullFactor = dimensions[0] * dimensions[1] / 1000000;
64030
64031                 for (i = 0; i < entities.length; i++) {
64032                     geometry = entities[i].geometry(resolver);
64033                     matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
64034                     for (j = 0; j < matches.length; j++) {
64035                         _rules[matches[j]].count++;
64036                     }
64037                 }
64038
64039                 currHidden = features.hidden();
64040                 if (currHidden !== _hidden) {
64041                     _hidden = currHidden;
64042                     needsRedraw = true;
64043                     dispatch$1.call('change');
64044                 }
64045
64046                 return needsRedraw;
64047             };
64048
64049
64050             features.stats = function() {
64051                 for (var i = 0; i < _keys.length; i++) {
64052                     _stats[_keys[i]] = _rules[_keys[i]].count;
64053                 }
64054
64055                 return _stats;
64056             };
64057
64058
64059             features.clear = function(d) {
64060                 for (var i = 0; i < d.length; i++) {
64061                     features.clearEntity(d[i]);
64062                 }
64063             };
64064
64065
64066             features.clearEntity = function(entity) {
64067                 delete _cache[osmEntity.key(entity)];
64068             };
64069
64070
64071             features.reset = function() {
64072                 Array.from(_deferred).forEach(function(handle) {
64073                     window.cancelIdleCallback(handle);
64074                     _deferred.delete(handle);
64075                 });
64076
64077                 _cache = {};
64078             };
64079
64080             // only certain relations are worth checking
64081             function relationShouldBeChecked(relation) {
64082                 // multipolygon features have `area` geometry and aren't checked here
64083                 return relation.tags.type === 'boundary';
64084             }
64085
64086             features.getMatches = function(entity, resolver, geometry) {
64087                 if (geometry === 'vertex' ||
64088                     (geometry === 'relation' && !relationShouldBeChecked(entity))) { return {}; }
64089
64090                 var ent = osmEntity.key(entity);
64091                 if (!_cache[ent]) {
64092                     _cache[ent] = {};
64093                 }
64094
64095                 if (!_cache[ent].matches) {
64096                     var matches = {};
64097                     var hasMatch = false;
64098
64099                     for (var i = 0; i < _keys.length; i++) {
64100                         if (_keys[i] === 'others') {
64101                             if (hasMatch) { continue; }
64102
64103                             // If an entity...
64104                             //   1. is a way that hasn't matched other 'interesting' feature rules,
64105                             if (entity.type === 'way') {
64106                                 var parents = features.getParents(entity, resolver, geometry);
64107
64108                                 //   2a. belongs only to a single multipolygon relation
64109                                 if ((parents.length === 1 && parents[0].isMultipolygon()) ||
64110                                     // 2b. or belongs only to boundary relations
64111                                     (parents.length > 0 && parents.every(function(parent) { return parent.tags.type === 'boundary'; }))) {
64112
64113                                     // ...then match whatever feature rules the parent relation has matched.
64114                                     // see #2548, #2887
64115                                     //
64116                                     // IMPORTANT:
64117                                     // For this to work, getMatches must be called on relations before ways.
64118                                     //
64119                                     var pkey = osmEntity.key(parents[0]);
64120                                     if (_cache[pkey] && _cache[pkey].matches) {
64121                                         matches = Object.assign({}, _cache[pkey].matches);  // shallow copy
64122                                         continue;
64123                                     }
64124                                 }
64125                             }
64126                         }
64127
64128                         if (_rules[_keys[i]].filter(entity.tags, geometry)) {
64129                             matches[_keys[i]] = hasMatch = true;
64130                         }
64131                     }
64132                     _cache[ent].matches = matches;
64133                 }
64134
64135                 return _cache[ent].matches;
64136             };
64137
64138
64139             features.getParents = function(entity, resolver, geometry) {
64140                 if (geometry === 'point') { return []; }
64141
64142                 var ent = osmEntity.key(entity);
64143                 if (!_cache[ent]) {
64144                     _cache[ent] = {};
64145                 }
64146
64147                 if (!_cache[ent].parents) {
64148                     var parents = [];
64149                     if (geometry === 'vertex') {
64150                         parents = resolver.parentWays(entity);
64151                     } else {   // 'line', 'area', 'relation'
64152                         parents = resolver.parentRelations(entity);
64153                     }
64154                     _cache[ent].parents = parents;
64155                 }
64156                 return _cache[ent].parents;
64157             };
64158
64159
64160             features.isHiddenPreset = function(preset, geometry) {
64161                 if (!_hidden.length) { return false; }
64162                 if (!preset.tags) { return false; }
64163
64164                 var test = preset.setTags({}, geometry);
64165                 for (var key in _rules) {
64166                     if (_rules[key].filter(test, geometry)) {
64167                         if (_hidden.indexOf(key) !== -1) {
64168                             return key;
64169                         }
64170                         return false;
64171                     }
64172                 }
64173                 return false;
64174             };
64175
64176
64177             features.isHiddenFeature = function(entity, resolver, geometry) {
64178                 if (!_hidden.length) { return false; }
64179                 if (!entity.version) { return false; }
64180                 if (_forceVisible[entity.id]) { return false; }
64181
64182                 var matches = Object.keys(features.getMatches(entity, resolver, geometry));
64183                 return matches.length && matches.every(function(k) { return features.hidden(k); });
64184             };
64185
64186
64187             features.isHiddenChild = function(entity, resolver, geometry) {
64188                 if (!_hidden.length) { return false; }
64189                 if (!entity.version || geometry === 'point') { return false; }
64190                 if (_forceVisible[entity.id]) { return false; }
64191
64192                 var parents = features.getParents(entity, resolver, geometry);
64193                 if (!parents.length) { return false; }
64194
64195                 for (var i = 0; i < parents.length; i++) {
64196                     if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
64197                         return false;
64198                     }
64199                 }
64200                 return true;
64201             };
64202
64203
64204             features.hasHiddenConnections = function(entity, resolver) {
64205                 if (!_hidden.length) { return false; }
64206
64207                 var childNodes, connections;
64208                 if (entity.type === 'midpoint') {
64209                     childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
64210                     connections = [];
64211                 } else {
64212                     childNodes = entity.nodes ? resolver.childNodes(entity) : [];
64213                     connections = features.getParents(entity, resolver, entity.geometry(resolver));
64214                 }
64215
64216                 // gather ways connected to child nodes..
64217                 connections = childNodes.reduce(function(result, e) {
64218                     return resolver.isShared(e) ? utilArrayUnion(result, resolver.parentWays(e)) : result;
64219                 }, connections);
64220
64221                 return connections.some(function(e) {
64222                     return features.isHidden(e, resolver, e.geometry(resolver));
64223                 });
64224             };
64225
64226
64227             features.isHidden = function(entity, resolver, geometry) {
64228                 if (!_hidden.length) { return false; }
64229                 if (!entity.version) { return false; }
64230
64231                 var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature);
64232                 return fn(entity, resolver, geometry);
64233             };
64234
64235
64236             features.filter = function(d, resolver) {
64237                 if (!_hidden.length) { return d; }
64238
64239                 var result = [];
64240                 for (var i = 0; i < d.length; i++) {
64241                     var entity = d[i];
64242                     if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
64243                         result.push(entity);
64244                     }
64245                 }
64246                 return result;
64247             };
64248
64249
64250             features.forceVisible = function(entityIDs) {
64251                 if (!arguments.length) { return Object.keys(_forceVisible); }
64252
64253                 _forceVisible = {};
64254                 for (var i = 0; i < entityIDs.length; i++) {
64255                     _forceVisible[entityIDs[i]] = true;
64256                     var entity = context.hasEntity(entityIDs[i]);
64257                     if (entity && entity.type === 'relation') {
64258                         // also show relation members (one level deep)
64259                         for (var j in entity.members) {
64260                             _forceVisible[entity.members[j].id] = true;
64261                         }
64262                     }
64263                 }
64264                 return features;
64265             };
64266
64267
64268             features.init = function() {
64269                 var storage = corePreferences('disabled-features');
64270                 if (storage) {
64271                     var storageDisabled = storage.replace(/;/g, ',').split(',');
64272                     storageDisabled.forEach(features.disable);
64273                 }
64274
64275                 var hash = utilStringQs(window.location.hash);
64276                 if (hash.disable_features) {
64277                     var hashDisabled = hash.disable_features.replace(/;/g, ',').split(',');
64278                     hashDisabled.forEach(features.disable);
64279                 }
64280             };
64281
64282
64283             // warm up the feature matching cache upon merging fetched data
64284             context.history().on('merge.features', function(newEntities) {
64285                 if (!newEntities) { return; }
64286                 var handle = window.requestIdleCallback(function() {
64287                     var graph = context.graph();
64288                     var types = utilArrayGroupBy(newEntities, 'type');
64289                     // ensure that getMatches is called on relations before ways
64290                     var entities = [].concat(types.relation || [], types.way || [], types.node || []);
64291                     for (var i = 0; i < entities.length; i++) {
64292                         var geometry = entities[i].geometry(graph);
64293                         features.getMatches(entities[i], graph, geometry);
64294                     }
64295                 });
64296                 _deferred.add(handle);
64297             });
64298
64299
64300             return features;
64301         }
64302
64303         // Touch targets control which other vertices we can drag a vertex onto.
64304         //
64305         // - the activeID - nope
64306         // - 1 away (adjacent) to the activeID - yes (vertices will be merged)
64307         // - 2 away from the activeID - nope (would create a self intersecting segment)
64308         // - all others on a linear way - yes
64309         // - all others on a closed way - nope (would create a self intersecting polygon)
64310         //
64311         // returns
64312         // 0 = active vertex - no touch/connect
64313         // 1 = passive vertex - yes touch/connect
64314         // 2 = adjacent vertex - yes but pay attention segmenting a line here
64315         //
64316         function svgPassiveVertex(node, graph, activeID) {
64317             if (!activeID) { return 1; }
64318             if (activeID === node.id) { return 0; }
64319
64320             var parents = graph.parentWays(node);
64321
64322             var i, j, nodes, isClosed, ix1, ix2, ix3, ix4, max;
64323
64324             for (i = 0; i < parents.length; i++) {
64325                 nodes = parents[i].nodes;
64326                 isClosed = parents[i].isClosed();
64327                 for (j = 0; j < nodes.length; j++) {   // find this vertex, look nearby
64328                     if (nodes[j] === node.id) {
64329                         ix1 = j - 2;
64330                         ix2 = j - 1;
64331                         ix3 = j + 1;
64332                         ix4 = j + 2;
64333
64334                         if (isClosed) {  // wraparound if needed
64335                             max = nodes.length - 1;
64336                             if (ix1 < 0)   { ix1 = max + ix1; }
64337                             if (ix2 < 0)   { ix2 = max + ix2; }
64338                             if (ix3 > max) { ix3 = ix3 - max; }
64339                             if (ix4 > max) { ix4 = ix4 - max; }
64340                         }
64341
64342                         if (nodes[ix1] === activeID) { return 0; }        // no - prevent self intersect
64343                         else if (nodes[ix2] === activeID) { return 2; }   // ok - adjacent
64344                         else if (nodes[ix3] === activeID) { return 2; }   // ok - adjacent
64345                         else if (nodes[ix4] === activeID) { return 0; }   // no - prevent self intersect
64346                         else if (isClosed && nodes.indexOf(activeID) !== -1) { return 0; }  // no - prevent self intersect
64347                     }
64348                 }
64349             }
64350
64351             return 1;   // ok
64352         }
64353
64354
64355         function svgMarkerSegments(projection, graph, dt,
64356                                           shouldReverse,
64357                                           bothDirections) {
64358             return function(entity) {
64359                 var i = 0;
64360                 var offset = dt;
64361                 var segments = [];
64362                 var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
64363                 var coordinates = graph.childNodes(entity).map(function(n) { return n.loc; });
64364                 var a, b;
64365
64366                 if (shouldReverse(entity)) {
64367                     coordinates.reverse();
64368                 }
64369
64370                 d3_geoStream({
64371                     type: 'LineString',
64372                     coordinates: coordinates
64373                 }, projection.stream(clip({
64374                     lineStart: function() {},
64375                     lineEnd: function() { a = null; },
64376                     point: function(x, y) {
64377                         b = [x, y];
64378
64379                         if (a) {
64380                             var span = geoVecLength(a, b) - offset;
64381
64382                             if (span >= 0) {
64383                                 var heading = geoVecAngle(a, b);
64384                                 var dx = dt * Math.cos(heading);
64385                                 var dy = dt * Math.sin(heading);
64386                                 var p = [
64387                                     a[0] + offset * Math.cos(heading),
64388                                     a[1] + offset * Math.sin(heading)
64389                                 ];
64390
64391                                 // gather coordinates
64392                                 var coord = [a, p];
64393                                 for (span -= dt; span >= 0; span -= dt) {
64394                                     p = geoVecAdd(p, [dx, dy]);
64395                                     coord.push(p);
64396                                 }
64397                                 coord.push(b);
64398
64399                                 // generate svg paths
64400                                 var segment = '';
64401                                 var j;
64402
64403                                 for (j = 0; j < coord.length; j++) {
64404                                     segment += (j === 0 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
64405                                 }
64406                                 segments.push({ id: entity.id, index: i++, d: segment });
64407
64408                                 if (bothDirections(entity)) {
64409                                     segment = '';
64410                                     for (j = coord.length - 1; j >= 0; j--) {
64411                                         segment += (j === coord.length - 1 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
64412                                     }
64413                                     segments.push({ id: entity.id, index: i++, d: segment });
64414                                 }
64415                             }
64416
64417                             offset = -span;
64418                         }
64419
64420                         a = b;
64421                     }
64422                 })));
64423
64424                 return segments;
64425             };
64426         }
64427
64428
64429         function svgPath(projection, graph, isArea) {
64430
64431             // Explanation of magic numbers:
64432             // "padding" here allows space for strokes to extend beyond the viewport,
64433             // so that the stroke isn't drawn along the edge of the viewport when
64434             // the shape is clipped.
64435             //
64436             // When drawing lines, pad viewport by 5px.
64437             // When drawing areas, pad viewport by 65px in each direction to allow
64438             // for 60px area fill stroke (see ".fill-partial path.fill" css rule)
64439
64440             var cache = {};
64441             var padding = isArea ? 65 : 5;
64442             var viewport = projection.clipExtent();
64443             var paddedExtent = [
64444                 [viewport[0][0] - padding, viewport[0][1] - padding],
64445                 [viewport[1][0] + padding, viewport[1][1] + padding]
64446             ];
64447             var clip = d3_geoIdentity().clipExtent(paddedExtent).stream;
64448             var project = projection.stream;
64449             var path = d3_geoPath()
64450                 .projection({stream: function(output) { return project(clip(output)); }});
64451
64452             var svgpath = function(entity) {
64453                 if (entity.id in cache) {
64454                     return cache[entity.id];
64455                 } else {
64456                     return cache[entity.id] = path(entity.asGeoJSON(graph));
64457                 }
64458             };
64459
64460             svgpath.geojson = function(d) {
64461                 if (d.__featurehash__ !== undefined) {
64462                     if (d.__featurehash__ in cache) {
64463                         return cache[d.__featurehash__];
64464                     } else {
64465                         return cache[d.__featurehash__] = path(d);
64466                     }
64467                 } else {
64468                     return path(d);
64469                 }
64470             };
64471
64472             return svgpath;
64473         }
64474
64475
64476         function svgPointTransform(projection) {
64477             var svgpoint = function(entity) {
64478                 // http://jsperf.com/short-array-join
64479                 var pt = projection(entity.loc);
64480                 return 'translate(' + pt[0] + ',' + pt[1] + ')';
64481             };
64482
64483             svgpoint.geojson = function(d) {
64484                 return svgpoint(d.properties.entity);
64485             };
64486
64487             return svgpoint;
64488         }
64489
64490
64491         function svgRelationMemberTags(graph) {
64492             return function(entity) {
64493                 var tags = entity.tags;
64494                 var shouldCopyMultipolygonTags = !entity.hasInterestingTags();
64495                 graph.parentRelations(entity).forEach(function(relation) {
64496                     var type = relation.tags.type;
64497                     if ((type === 'multipolygon' && shouldCopyMultipolygonTags) || type === 'boundary') {
64498                         tags = Object.assign({}, relation.tags, tags);
64499                     }
64500                 });
64501                 return tags;
64502             };
64503         }
64504
64505
64506         function svgSegmentWay(way, graph, activeID) {
64507             // When there is no activeID, we can memoize this expensive computation
64508             if (activeID === undefined) {
64509                 return graph.transient(way, 'waySegments', getWaySegments);
64510             } else {
64511                 return getWaySegments();
64512             }
64513
64514             function getWaySegments() {
64515                 var isActiveWay = (way.nodes.indexOf(activeID) !== -1);
64516                 var features = { passive: [], active: [] };
64517                 var start = {};
64518                 var end = {};
64519                 var node, type;
64520
64521                 for (var i = 0; i < way.nodes.length; i++) {
64522                     node = graph.entity(way.nodes[i]);
64523                     type = svgPassiveVertex(node, graph, activeID);
64524                     end = { node: node, type: type };
64525
64526                     if (start.type !== undefined) {
64527                         if (start.node.id === activeID || end.node.id === activeID) ; else if (isActiveWay && (start.type === 2 || end.type === 2)) {   // one adjacent vertex
64528                             pushActive(start, end, i);
64529                         } else if (start.type === 0 && end.type === 0) {   // both active vertices
64530                             pushActive(start, end, i);
64531                         } else {
64532                             pushPassive(start, end, i);
64533                         }
64534                     }
64535
64536                     start = end;
64537                 }
64538
64539                 return features;
64540
64541                 function pushActive(start, end, index) {
64542                     features.active.push({
64543                         type: 'Feature',
64544                         id: way.id + '-' + index + '-nope',
64545                         properties: {
64546                             nope: true,
64547                             target: true,
64548                             entity: way,
64549                             nodes: [start.node, end.node],
64550                             index: index
64551                         },
64552                         geometry: {
64553                             type: 'LineString',
64554                             coordinates: [start.node.loc, end.node.loc]
64555                         }
64556                     });
64557                 }
64558
64559                 function pushPassive(start, end, index) {
64560                     features.passive.push({
64561                         type: 'Feature',
64562                         id: way.id + '-' + index,
64563                         properties: {
64564                             target: true,
64565                             entity: way,
64566                             nodes: [start.node, end.node],
64567                             index: index
64568                         },
64569                         geometry: {
64570                             type: 'LineString',
64571                             coordinates: [start.node.loc, end.node.loc]
64572                         }
64573                     });
64574                 }
64575             }
64576         }
64577
64578         function svgTagClasses() {
64579             var primaries = [
64580                 'building', 'highway', 'railway', 'waterway', 'aeroway', 'aerialway',
64581                 'piste:type', 'boundary', 'power', 'amenity', 'natural', 'landuse',
64582                 'leisure', 'military', 'place', 'man_made', 'route', 'attraction',
64583                 'building:part', 'indoor'
64584             ];
64585             var statuses = [
64586                 // nonexistent, might be built
64587                 'proposed', 'planned',
64588                 // under maintentance or between groundbreaking and opening
64589                 'construction',
64590                 // existent but not functional
64591                 'disused',
64592                 // dilapidated to nonexistent
64593                 'abandoned',
64594                 // nonexistent, still may appear in imagery
64595                 'dismantled', 'razed', 'demolished', 'obliterated',
64596                 // existent occasionally, e.g. stormwater drainage basin
64597                 'intermittent'
64598             ];
64599             var secondaries = [
64600                 'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier',
64601                 'surface', 'tracktype', 'footway', 'crossing', 'service', 'sport',
64602                 'public_transport', 'location', 'parking', 'golf', 'type', 'leisure',
64603                 'man_made', 'indoor'
64604             ];
64605             var _tags = function(entity) { return entity.tags; };
64606
64607
64608             var tagClasses = function(selection) {
64609                 selection.each(function tagClassesEach(entity) {
64610                     var value = this.className;
64611
64612                     if (value.baseVal !== undefined) {
64613                         value = value.baseVal;
64614                     }
64615
64616                     var t = _tags(entity);
64617
64618                     var computed = tagClasses.getClassesString(t, value);
64619
64620                     if (computed !== value) {
64621                         select(this).attr('class', computed);
64622                     }
64623                 });
64624             };
64625
64626
64627             tagClasses.getClassesString = function(t, value) {
64628                 var primary, status;
64629                 var i, j, k, v;
64630
64631                 // in some situations we want to render perimeter strokes a certain way
64632                 var overrideGeometry;
64633                 if (/\bstroke\b/.test(value)) {
64634                     if (!!t.barrier && t.barrier !== 'no') {
64635                         overrideGeometry = 'line';
64636                     }
64637                 }
64638
64639                 // preserve base classes (nothing with `tag-`)
64640                 var classes = value.trim().split(/\s+/)
64641                     .filter(function(klass) {
64642                         return klass.length && !/^tag-/.test(klass);
64643                     })
64644                     .map(function(klass) {  // special overrides for some perimeter strokes
64645                         return (klass === 'line' || klass === 'area') ? (overrideGeometry || klass) : klass;
64646                     });
64647
64648                 // pick at most one primary classification tag..
64649                 for (i = 0; i < primaries.length; i++) {
64650                     k = primaries[i];
64651                     v = t[k];
64652                     if (!v || v === 'no') { continue; }
64653
64654                     if (k === 'piste:type') {  // avoid a ':' in the class name
64655                         k = 'piste';
64656                     } else if (k === 'building:part') {  // avoid a ':' in the class name
64657                         k = 'building_part';
64658                     }
64659
64660                     primary = k;
64661                     if (statuses.indexOf(v) !== -1) {   // e.g. `railway=abandoned`
64662                         status = v;
64663                         classes.push('tag-' + k);
64664                     } else {
64665                         classes.push('tag-' + k);
64666                         classes.push('tag-' + k + '-' + v);
64667                     }
64668
64669                     break;
64670                 }
64671
64672                 if (!primary) {
64673                     for (i = 0; i < statuses.length; i++) {
64674                         for (j = 0; j < primaries.length; j++) {
64675                             k = statuses[i] + ':' + primaries[j];  // e.g. `demolished:building=yes`
64676                             v = t[k];
64677                             if (!v || v === 'no') { continue; }
64678
64679                             status = statuses[i];
64680                             break;
64681                         }
64682                     }
64683                 }
64684
64685                 // add at most one status tag, only if relates to primary tag..
64686                 if (!status) {
64687                     for (i = 0; i < statuses.length; i++) {
64688                         k = statuses[i];
64689                         v = t[k];
64690                         if (!v || v === 'no') { continue; }
64691
64692                         if (v === 'yes') {   // e.g. `railway=rail + abandoned=yes`
64693                             status = k;
64694                         }
64695                         else if (primary && primary === v) {  // e.g. `railway=rail + abandoned=railway`
64696                             status = k;
64697                         } else if (!primary && primaries.indexOf(v) !== -1) {  // e.g. `abandoned=railway`
64698                             status = k;
64699                             primary = v;
64700                             classes.push('tag-' + v);
64701                         }  // else ignore e.g.  `highway=path + abandoned=railway`
64702
64703                         if (status) { break; }
64704                     }
64705                 }
64706
64707                 if (status) {
64708                     classes.push('tag-status');
64709                     classes.push('tag-status-' + status);
64710                 }
64711
64712                 // add any secondary tags
64713                 for (i = 0; i < secondaries.length; i++) {
64714                     k = secondaries[i];
64715                     v = t[k];
64716                     if (!v || v === 'no' || k === primary) { continue; }
64717                     classes.push('tag-' + k);
64718                     classes.push('tag-' + k + '-' + v);
64719                 }
64720
64721                 // For highways, look for surface tagging..
64722                 if ((primary === 'highway' && !osmPathHighwayTagValues[t.highway]) || primary === 'aeroway') {
64723                     var surface = t.highway === 'track' ? 'unpaved' : 'paved';
64724                     for (k in t) {
64725                         v = t[k];
64726                         if (k in osmPavedTags) {
64727                             surface = osmPavedTags[k][v] ? 'paved' : 'unpaved';
64728                         }
64729                         if (k in osmSemipavedTags && !!osmSemipavedTags[k][v]) {
64730                             surface = 'semipaved';
64731                         }
64732                     }
64733                     classes.push('tag-' + surface);
64734                 }
64735
64736                 // If this is a wikidata-tagged item, add a class for that..
64737                 if (t.wikidata || t['brand:wikidata']) {
64738                     classes.push('tag-wikidata');
64739                 }
64740
64741                 return classes.join(' ').trim();
64742             };
64743
64744
64745             tagClasses.tags = function(val) {
64746                 if (!arguments.length) { return _tags; }
64747                 _tags = val;
64748                 return tagClasses;
64749             };
64750
64751             return tagClasses;
64752         }
64753
64754         // Patterns only work in Firefox when set directly on element.
64755         // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
64756         var patterns = {
64757             // tag - pattern name
64758             // -or-
64759             // tag - value - pattern name
64760             // -or-
64761             // tag - value - rules (optional tag-values, pattern name)
64762             // (matches earlier rules first, so fallback should be last entry)
64763             amenity: {
64764                 grave_yard: 'cemetery',
64765                 fountain: 'water_standing'
64766             },
64767             landuse: {
64768                 cemetery: [
64769                     { religion: 'christian', pattern: 'cemetery_christian' },
64770                     { religion: 'buddhist', pattern: 'cemetery_buddhist' },
64771                     { religion: 'muslim', pattern: 'cemetery_muslim' },
64772                     { religion: 'jewish', pattern: 'cemetery_jewish' },
64773                     { pattern: 'cemetery' }
64774                 ],
64775                 construction: 'construction',
64776                 farmland: 'farmland',
64777                 farmyard: 'farmyard',
64778                 forest: [
64779                     { leaf_type: 'broadleaved', pattern: 'forest_broadleaved' },
64780                     { leaf_type: 'needleleaved', pattern: 'forest_needleleaved' },
64781                     { leaf_type: 'leafless', pattern: 'forest_leafless' },
64782                     { pattern: 'forest' } // same as 'leaf_type:mixed'
64783                 ],
64784                 grave_yard: 'cemetery',
64785                 grass: [
64786                     { golf: 'green', pattern: 'golf_green' },
64787                     { pattern: 'grass' } ],
64788                 landfill: 'landfill',
64789                 meadow: 'meadow',
64790                 military: 'construction',
64791                 orchard: 'orchard',
64792                 quarry: 'quarry',
64793                 vineyard: 'vineyard'
64794             },
64795             natural: {
64796                 beach: 'beach',
64797                 grassland: 'grass',
64798                 sand: 'beach',
64799                 scrub: 'scrub',
64800                 water: [
64801                     { water: 'pond', pattern: 'pond' },
64802                     { water: 'reservoir', pattern: 'water_standing' },
64803                     { pattern: 'waves' }
64804                 ],
64805                 wetland: [
64806                     { wetland: 'marsh', pattern: 'wetland_marsh' },
64807                     { wetland: 'swamp', pattern: 'wetland_swamp' },
64808                     { wetland: 'bog', pattern: 'wetland_bog' },
64809                     { wetland: 'reedbed', pattern: 'wetland_reedbed' },
64810                     { pattern: 'wetland' }
64811                 ],
64812                 wood: [
64813                     { leaf_type: 'broadleaved', pattern: 'forest_broadleaved' },
64814                     { leaf_type: 'needleleaved', pattern: 'forest_needleleaved' },
64815                     { leaf_type: 'leafless', pattern: 'forest_leafless' },
64816                     { pattern: 'forest' } // same as 'leaf_type:mixed'
64817                 ]
64818             },
64819             traffic_calming: {
64820                 island: [
64821                     { surface: 'grass', pattern: 'grass' } ],
64822                 chicane: [
64823                     { surface: 'grass', pattern: 'grass' } ],
64824                 choker: [
64825                     { surface: 'grass', pattern: 'grass' } ]
64826             }
64827         };
64828
64829         function svgTagPattern(tags) {
64830             // Skip pattern filling if this is a building (buildings don't get patterns applied)
64831             if (tags.building && tags.building !== 'no') {
64832                 return null;
64833             }
64834
64835             for (var tag in patterns) {
64836                 var entityValue = tags[tag];
64837                 if (!entityValue) { continue; }
64838
64839                 if (typeof patterns[tag] === 'string') { // extra short syntax (just tag) - pattern name
64840                     return 'pattern-' + patterns[tag];
64841                 } else {
64842                     var values = patterns[tag];
64843                     for (var value in values) {
64844                         if (entityValue !== value) { continue; }
64845
64846                         var rules = values[value];
64847                         if (typeof rules === 'string') { // short syntax - pattern name
64848                             return 'pattern-' + rules;
64849                         }
64850
64851                         // long syntax - rule array
64852                         for (var ruleKey in rules) {
64853                             var rule = rules[ruleKey];
64854
64855                             var pass = true;
64856                             for (var criterion in rule) {
64857                                 if (criterion !== 'pattern') { // reserved for pattern name
64858                                     // The only rule is a required tag-value pair
64859                                     var v = tags[criterion];
64860                                     if (!v || v !== rule[criterion]) {
64861                                         pass = false;
64862                                         break;
64863                                     }
64864                                 }
64865                             }
64866
64867                             if (pass) {
64868                                 return 'pattern-' + rule.pattern;
64869                             }
64870                         }
64871                     }
64872                 }
64873             }
64874
64875             return null;
64876         }
64877
64878         function svgAreas(projection, context) {
64879
64880
64881             function getPatternStyle(tags) {
64882                 var imageID = svgTagPattern(tags);
64883                 if (imageID) {
64884                     return 'url("#ideditor-' + imageID + '")';
64885                 }
64886                 return '';
64887             }
64888
64889
64890             function drawTargets(selection, graph, entities, filter) {
64891                 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
64892                 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
64893                 var getPath = svgPath(projection).geojson;
64894                 var activeID = context.activeID();
64895                 var base = context.history().base();
64896
64897                 // The targets and nopes will be MultiLineString sub-segments of the ways
64898                 var data = { targets: [], nopes: [] };
64899
64900                 entities.forEach(function(way) {
64901                     var features = svgSegmentWay(way, graph, activeID);
64902                     data.targets.push.apply(data.targets, features.passive);
64903                     data.nopes.push.apply(data.nopes, features.active);
64904                 });
64905
64906
64907                 // Targets allow hover and vertex snapping
64908                 var targetData = data.targets.filter(getPath);
64909                 var targets = selection.selectAll('.area.target-allowed')
64910                     .filter(function(d) { return filter(d.properties.entity); })
64911                     .data(targetData, function key(d) { return d.id; });
64912
64913                 // exit
64914                 targets.exit()
64915                     .remove();
64916
64917                 var segmentWasEdited = function(d) {
64918                     var wayID = d.properties.entity.id;
64919                     // if the whole line was edited, don't draw segment changes
64920                     if (!base.entities[wayID] ||
64921                         !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
64922                         return false;
64923                     }
64924                     return d.properties.nodes.some(function(n) {
64925                         return !base.entities[n.id] ||
64926                                !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
64927                     });
64928                 };
64929
64930                 // enter/update
64931                 targets.enter()
64932                     .append('path')
64933                     .merge(targets)
64934                     .attr('d', getPath)
64935                     .attr('class', function(d) { return 'way area target target-allowed ' + targetClass + d.id; })
64936                     .classed('segment-edited', segmentWasEdited);
64937
64938
64939                 // NOPE
64940                 var nopeData = data.nopes.filter(getPath);
64941                 var nopes = selection.selectAll('.area.target-nope')
64942                     .filter(function(d) { return filter(d.properties.entity); })
64943                     .data(nopeData, function key(d) { return d.id; });
64944
64945                 // exit
64946                 nopes.exit()
64947                     .remove();
64948
64949                 // enter/update
64950                 nopes.enter()
64951                     .append('path')
64952                     .merge(nopes)
64953                     .attr('d', getPath)
64954                     .attr('class', function(d) { return 'way area target target-nope ' + nopeClass + d.id; })
64955                     .classed('segment-edited', segmentWasEdited);
64956             }
64957
64958
64959             function drawAreas(selection, graph, entities, filter) {
64960                 var path = svgPath(projection, graph, true);
64961                 var areas = {};
64962                 var multipolygon;
64963                 var base = context.history().base();
64964
64965                 for (var i = 0; i < entities.length; i++) {
64966                     var entity = entities[i];
64967                     if (entity.geometry(graph) !== 'area') { continue; }
64968
64969                     multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
64970                     if (multipolygon) {
64971                         areas[multipolygon.id] = {
64972                             entity: multipolygon.mergeTags(entity.tags),
64973                             area: Math.abs(entity.area(graph))
64974                         };
64975                     } else if (!areas[entity.id]) {
64976                         areas[entity.id] = {
64977                             entity: entity,
64978                             area: Math.abs(entity.area(graph))
64979                         };
64980                     }
64981                 }
64982
64983                 var fills = Object.values(areas).filter(function hasPath(a) { return path(a.entity); });
64984                 fills.sort(function areaSort(a, b) { return b.area - a.area; });
64985                 fills = fills.map(function(a) { return a.entity; });
64986
64987                 var strokes = fills.filter(function(area) { return area.type === 'way'; });
64988
64989                 var data = {
64990                     clip: fills,
64991                     shadow: strokes,
64992                     stroke: strokes,
64993                     fill: fills
64994                 };
64995
64996                 var clipPaths = context.surface().selectAll('defs').selectAll('.clipPath-osm')
64997                    .filter(filter)
64998                    .data(data.clip, osmEntity.key);
64999
65000                 clipPaths.exit()
65001                    .remove();
65002
65003                 var clipPathsEnter = clipPaths.enter()
65004                    .append('clipPath')
65005                    .attr('class', 'clipPath-osm')
65006                    .attr('id', function(entity) { return 'ideditor-' + entity.id + '-clippath'; });
65007
65008                 clipPathsEnter
65009                    .append('path');
65010
65011                 clipPaths.merge(clipPathsEnter)
65012                    .selectAll('path')
65013                    .attr('d', path);
65014
65015
65016                 var drawLayer = selection.selectAll('.layer-osm.areas');
65017                 var touchLayer = selection.selectAll('.layer-touch.areas');
65018
65019                 // Draw areas..
65020                 var areagroup = drawLayer
65021                     .selectAll('g.areagroup')
65022                     .data(['fill', 'shadow', 'stroke']);
65023
65024                 areagroup = areagroup.enter()
65025                     .append('g')
65026                     .attr('class', function(d) { return 'areagroup area-' + d; })
65027                     .merge(areagroup);
65028
65029                 var paths = areagroup
65030                     .selectAll('path')
65031                     .filter(filter)
65032                     .data(function(layer) { return data[layer]; }, osmEntity.key);
65033
65034                 paths.exit()
65035                     .remove();
65036
65037
65038                 var fillpaths = selection.selectAll('.area-fill path.area').nodes();
65039                 var bisect = d3_bisector(function(node) { return -node.__data__.area(graph); }).left;
65040
65041                 function sortedByArea(entity) {
65042                     if (this._parent.__data__ === 'fill') {
65043                         return fillpaths[bisect(fillpaths, -entity.area(graph))];
65044                     }
65045                 }
65046
65047                 paths = paths.enter()
65048                     .insert('path', sortedByArea)
65049                     .merge(paths)
65050                     .each(function(entity) {
65051                         var layer = this.parentNode.__data__;
65052                         this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
65053
65054                         if (layer === 'fill') {
65055                             this.setAttribute('clip-path', 'url(#ideditor-' + entity.id + '-clippath)');
65056                             this.style.fill = this.style.stroke = getPatternStyle(entity.tags);
65057                         }
65058                     })
65059                     .classed('added', function(d) {
65060                         return !base.entities[d.id];
65061                     })
65062                     .classed('geometry-edited', function(d) {
65063                         return graph.entities[d.id] &&
65064                             base.entities[d.id] &&
65065                             !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
65066                     })
65067                     .classed('retagged', function(d) {
65068                         return graph.entities[d.id] &&
65069                             base.entities[d.id] &&
65070                             !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
65071                     })
65072                     .call(svgTagClasses())
65073                     .attr('d', path);
65074
65075
65076                 // Draw touch targets..
65077                 touchLayer
65078                     .call(drawTargets, graph, data.stroke, filter);
65079             }
65080
65081             return drawAreas;
65082         }
65083
65084         //[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]
65085         //[4a]          NameChar           ::=          NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
65086         //[5]           Name       ::=          NameStartChar (NameChar)*
65087         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
65088         var nameChar = new RegExp("[\\-\\.0-9"+nameStartChar.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]");
65089         var tagNamePattern = new RegExp('^'+nameStartChar.source+nameChar.source+'*(?:\:'+nameStartChar.source+nameChar.source+'*)?$');
65090         //var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/
65091         //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(',')
65092
65093         //S_TAG,        S_ATTR, S_EQ,   S_ATTR_NOQUOT_VALUE
65094         //S_ATTR_SPACE, S_ATTR_END,     S_TAG_SPACE, S_TAG_CLOSE
65095         var S_TAG = 0;//tag name offerring
65096         var S_ATTR = 1;//attr name offerring 
65097         var S_ATTR_SPACE=2;//attr name end and space offer
65098         var S_EQ = 3;//=space?
65099         var S_ATTR_NOQUOT_VALUE = 4;//attr value(no quot value only)
65100         var S_ATTR_END = 5;//attr value end and no space(quot end)
65101         var S_TAG_SPACE = 6;//(attr value end || tag end ) && (space offer)
65102         var S_TAG_CLOSE = 7;//closed el<el />
65103
65104         function XMLReader(){
65105                 
65106         }
65107
65108         XMLReader.prototype = {
65109                 parse:function(source,defaultNSMap,entityMap){
65110                         var domBuilder = this.domBuilder;
65111                         domBuilder.startDocument();
65112                         _copy(defaultNSMap ,defaultNSMap = {});
65113                         parse(source,defaultNSMap,entityMap,
65114                                         domBuilder,this.errorHandler);
65115                         domBuilder.endDocument();
65116                 }
65117         };
65118         function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){
65119                 function fixedFromCharCode(code) {
65120                         // String.prototype.fromCharCode does not supports
65121                         // > 2 bytes unicode chars directly
65122                         if (code > 0xffff) {
65123                                 code -= 0x10000;
65124                                 var surrogate1 = 0xd800 + (code >> 10)
65125                                         , surrogate2 = 0xdc00 + (code & 0x3ff);
65126
65127                                 return String.fromCharCode(surrogate1, surrogate2);
65128                         } else {
65129                                 return String.fromCharCode(code);
65130                         }
65131                 }
65132                 function entityReplacer(a){
65133                         var k = a.slice(1,-1);
65134                         if(k in entityMap){
65135                                 return entityMap[k]; 
65136                         }else if(k.charAt(0) === '#'){
65137                                 return fixedFromCharCode(parseInt(k.substr(1).replace('x','0x')))
65138                         }else {
65139                                 errorHandler.error('entity not found:'+a);
65140                                 return a;
65141                         }
65142                 }
65143                 function appendText(end){//has some bugs
65144                         if(end>start){
65145                                 var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer);
65146                                 locator&&position(start);
65147                                 domBuilder.characters(xt,0,end-start);
65148                                 start = end;
65149                         }
65150                 }
65151                 function position(p,m){
65152                         while(p>=lineEnd && (m = linePattern.exec(source))){
65153                                 lineStart = m.index;
65154                                 lineEnd = lineStart + m[0].length;
65155                                 locator.lineNumber++;
65156                                 //console.log('line++:',locator,startPos,endPos)
65157                         }
65158                         locator.columnNumber = p-lineStart+1;
65159                 }
65160                 var lineStart = 0;
65161                 var lineEnd = 0;
65162                 var linePattern = /.*(?:\r\n?|\n)|.*$/g;
65163                 var locator = domBuilder.locator;
65164                 
65165                 var parseStack = [{currentNSMap:defaultNSMapCopy}];
65166                 var closeMap = {};
65167                 var start = 0;
65168                 while(true){
65169                         try{
65170                                 var tagStart = source.indexOf('<',start);
65171                                 if(tagStart<0){
65172                                         if(!source.substr(start).match(/^\s*$/)){
65173                                                 var doc = domBuilder.doc;
65174                                         var text = doc.createTextNode(source.substr(start));
65175                                         doc.appendChild(text);
65176                                         domBuilder.currentElement = text;
65177                                         }
65178                                         return;
65179                                 }
65180                                 if(tagStart>start){
65181                                         appendText(tagStart);
65182                                 }
65183                                 switch(source.charAt(tagStart+1)){
65184                                 case '/':
65185                                         var end = source.indexOf('>',tagStart+3);
65186                                         var tagName = source.substring(tagStart+2,end);
65187                                         var config = parseStack.pop();
65188                                         if(end<0){
65189                                                 
65190                                         tagName = source.substring(tagStart+2).replace(/[\s<].*/,'');
65191                                         //console.error('#@@@@@@'+tagName)
65192                                         errorHandler.error("end tag name: "+tagName+' is not complete:'+config.tagName);
65193                                         end = tagStart+1+tagName.length;
65194                                 }else if(tagName.match(/\s</)){
65195                                         tagName = tagName.replace(/[\s<].*/,'');
65196                                         errorHandler.error("end tag name: "+tagName+' maybe not complete');
65197                                         end = tagStart+1+tagName.length;
65198                                         }
65199                                         //console.error(parseStack.length,parseStack)
65200                                         //console.error(config);
65201                                         var localNSMap = config.localNSMap;
65202                                         var endMatch = config.tagName == tagName;
65203                                         var endIgnoreCaseMach = endMatch || config.tagName&&config.tagName.toLowerCase() == tagName.toLowerCase();
65204                                 if(endIgnoreCaseMach){
65205                                         domBuilder.endElement(config.uri,config.localName,tagName);
65206                                                 if(localNSMap){
65207                                                         for(var prefix in localNSMap){
65208                                                                 domBuilder.endPrefixMapping(prefix) ;
65209                                                         }
65210                                                 }
65211                                                 if(!endMatch){
65212                                         errorHandler.fatalError("end tag name: "+tagName+' is not match the current start tagName:'+config.tagName );
65213                                                 }
65214                                 }else {
65215                                         parseStack.push(config);
65216                                 }
65217                                         
65218                                         end++;
65219                                         break;
65220                                         // end elment
65221                                 case '?':// <?...?>
65222                                         locator&&position(tagStart);
65223                                         end = parseInstruction(source,tagStart,domBuilder);
65224                                         break;
65225                                 case '!':// <!doctype,<![CDATA,<!--
65226                                         locator&&position(tagStart);
65227                                         end = parseDCC(source,tagStart,domBuilder,errorHandler);
65228                                         break;
65229                                 default:
65230                                         locator&&position(tagStart);
65231                                         var el = new ElementAttributes();
65232                                         var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
65233                                         //elStartEnd
65234                                         var end = parseElementStartPart(source,tagStart,el,currentNSMap,entityReplacer,errorHandler);
65235                                         var len = el.length;
65236                                         
65237                                         
65238                                         if(!el.closed && fixSelfClosed(source,end,el.tagName,closeMap)){
65239                                                 el.closed = true;
65240                                                 if(!entityMap.nbsp){
65241                                                         errorHandler.warning('unclosed xml attribute');
65242                                                 }
65243                                         }
65244                                         if(locator && len){
65245                                                 var locator2 = copyLocator(locator,{});
65246                                                 //try{//attribute position fixed
65247                                                 for(var i = 0;i<len;i++){
65248                                                         var a = el[i];
65249                                                         position(a.offset);
65250                                                         a.locator = copyLocator(locator,{});
65251                                                 }
65252                                                 //}catch(e){console.error('@@@@@'+e)}
65253                                                 domBuilder.locator = locator2;
65254                                                 if(appendElement(el,domBuilder,currentNSMap)){
65255                                                         parseStack.push(el);
65256                                                 }
65257                                                 domBuilder.locator = locator;
65258                                         }else {
65259                                                 if(appendElement(el,domBuilder,currentNSMap)){
65260                                                         parseStack.push(el);
65261                                                 }
65262                                         }
65263                                         
65264                                         
65265                                         
65266                                         if(el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed){
65267                                                 end = parseHtmlSpecialContent(source,end,el.tagName,entityReplacer,domBuilder);
65268                                         }else {
65269                                                 end++;
65270                                         }
65271                                 }
65272                         }catch(e){
65273                                 errorHandler.error('element parse error: '+e);
65274                                 //errorHandler.error('element parse error: '+e);
65275                                 end = -1;
65276                                 //throw e;
65277                         }
65278                         if(end>start){
65279                                 start = end;
65280                         }else {
65281                                 //TODO: 这里有可能sax回退,有位置错误风险
65282                                 appendText(Math.max(tagStart,start)+1);
65283                         }
65284                 }
65285         }
65286         function copyLocator(f,t){
65287                 t.lineNumber = f.lineNumber;
65288                 t.columnNumber = f.columnNumber;
65289                 return t;
65290         }
65291
65292         /**
65293          * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
65294          * @return end of the elementStartPart(end of elementEndPart for selfClosed el)
65295          */
65296         function parseElementStartPart(source,start,el,currentNSMap,entityReplacer,errorHandler){
65297                 var attrName;
65298                 var value;
65299                 var p = ++start;
65300                 var s = S_TAG;//status
65301                 while(true){
65302                         var c = source.charAt(p);
65303                         switch(c){
65304                         case '=':
65305                                 if(s === S_ATTR){//attrName
65306                                         attrName = source.slice(start,p);
65307                                         s = S_EQ;
65308                                 }else if(s === S_ATTR_SPACE){
65309                                         s = S_EQ;
65310                                 }else {
65311                                         //fatalError: equal must after attrName or space after attrName
65312                                         throw new Error('attribute equal must after attrName');
65313                                 }
65314                                 break;
65315                         case '\'':
65316                         case '"':
65317                                 if(s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE
65318                                         ){//equal
65319                                         if(s === S_ATTR){
65320                                                 errorHandler.warning('attribute value must after "="');
65321                                                 attrName = source.slice(start,p);
65322                                         }
65323                                         start = p+1;
65324                                         p = source.indexOf(c,start);
65325                                         if(p>0){
65326                                                 value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
65327                                                 el.add(attrName,value,start-1);
65328                                                 s = S_ATTR_END;
65329                                         }else {
65330                                                 //fatalError: no end quot match
65331                                                 throw new Error('attribute value no end \''+c+'\' match');
65332                                         }
65333                                 }else if(s == S_ATTR_NOQUOT_VALUE){
65334                                         value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
65335                                         //console.log(attrName,value,start,p)
65336                                         el.add(attrName,value,start);
65337                                         //console.dir(el)
65338                                         errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!');
65339                                         start = p+1;
65340                                         s = S_ATTR_END;
65341                                 }else {
65342                                         //fatalError: no equal before
65343                                         throw new Error('attribute value must after "="');
65344                                 }
65345                                 break;
65346                         case '/':
65347                                 switch(s){
65348                                 case S_TAG:
65349                                         el.setTagName(source.slice(start,p));
65350                                 case S_ATTR_END:
65351                                 case S_TAG_SPACE:
65352                                 case S_TAG_CLOSE:
65353                                         s =S_TAG_CLOSE;
65354                                         el.closed = true;
65355                                 case S_ATTR_NOQUOT_VALUE:
65356                                 case S_ATTR:
65357                                 case S_ATTR_SPACE:
65358                                         break;
65359                                 //case S_EQ:
65360                                 default:
65361                                         throw new Error("attribute invalid close char('/')")
65362                                 }
65363                                 break;
65364                         case ''://end document
65365                                 //throw new Error('unexpected end of input')
65366                                 errorHandler.error('unexpected end of input');
65367                                 if(s == S_TAG){
65368                                         el.setTagName(source.slice(start,p));
65369                                 }
65370                                 return p;
65371                         case '>':
65372                                 switch(s){
65373                                 case S_TAG:
65374                                         el.setTagName(source.slice(start,p));
65375                                 case S_ATTR_END:
65376                                 case S_TAG_SPACE:
65377                                 case S_TAG_CLOSE:
65378                                         break;//normal
65379                                 case S_ATTR_NOQUOT_VALUE://Compatible state
65380                                 case S_ATTR:
65381                                         value = source.slice(start,p);
65382                                         if(value.slice(-1) === '/'){
65383                                                 el.closed  = true;
65384                                                 value = value.slice(0,-1);
65385                                         }
65386                                 case S_ATTR_SPACE:
65387                                         if(s === S_ATTR_SPACE){
65388                                                 value = attrName;
65389                                         }
65390                                         if(s == S_ATTR_NOQUOT_VALUE){
65391                                                 errorHandler.warning('attribute "'+value+'" missed quot(")!!');
65392                                                 el.add(attrName,value.replace(/&#?\w+;/g,entityReplacer),start);
65393                                         }else {
65394                                                 if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)){
65395                                                         errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!');
65396                                                 }
65397                                                 el.add(value,value,start);
65398                                         }
65399                                         break;
65400                                 case S_EQ:
65401                                         throw new Error('attribute value missed!!');
65402                                 }
65403         //                      console.log(tagName,tagNamePattern,tagNamePattern.test(tagName))
65404                                 return p;
65405                         /*xml space '\x20' | #x9 | #xD | #xA; */
65406                         case '\u0080':
65407                                 c = ' ';
65408                         default:
65409                                 if(c<= ' '){//space
65410                                         switch(s){
65411                                         case S_TAG:
65412                                                 el.setTagName(source.slice(start,p));//tagName
65413                                                 s = S_TAG_SPACE;
65414                                                 break;
65415                                         case S_ATTR:
65416                                                 attrName = source.slice(start,p);
65417                                                 s = S_ATTR_SPACE;
65418                                                 break;
65419                                         case S_ATTR_NOQUOT_VALUE:
65420                                                 var value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
65421                                                 errorHandler.warning('attribute "'+value+'" missed quot(")!!');
65422                                                 el.add(attrName,value,start);
65423                                         case S_ATTR_END:
65424                                                 s = S_TAG_SPACE;
65425                                                 break;
65426                                         //case S_TAG_SPACE:
65427                                         //case S_EQ:
65428                                         //case S_ATTR_SPACE:
65429                                         //      void();break;
65430                                         //case S_TAG_CLOSE:
65431                                                 //ignore warning
65432                                         }
65433                                 }else {//not space
65434         //S_TAG,        S_ATTR, S_EQ,   S_ATTR_NOQUOT_VALUE
65435         //S_ATTR_SPACE, S_ATTR_END,     S_TAG_SPACE, S_TAG_CLOSE
65436                                         switch(s){
65437                                         //case S_TAG:void();break;
65438                                         //case S_ATTR:void();break;
65439                                         //case S_ATTR_NOQUOT_VALUE:void();break;
65440                                         case S_ATTR_SPACE:
65441                                                 var tagName =  el.tagName;
65442                                                 if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)){
65443                                                         errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead2!!');
65444                                                 }
65445                                                 el.add(attrName,attrName,start);
65446                                                 start = p;
65447                                                 s = S_ATTR;
65448                                                 break;
65449                                         case S_ATTR_END:
65450                                                 errorHandler.warning('attribute space is required"'+attrName+'"!!');
65451                                         case S_TAG_SPACE:
65452                                                 s = S_ATTR;
65453                                                 start = p;
65454                                                 break;
65455                                         case S_EQ:
65456                                                 s = S_ATTR_NOQUOT_VALUE;
65457                                                 start = p;
65458                                                 break;
65459                                         case S_TAG_CLOSE:
65460                                                 throw new Error("elements closed character '/' and '>' must be connected to");
65461                                         }
65462                                 }
65463                         }//end outer switch
65464                         //console.log('p++',p)
65465                         p++;
65466                 }
65467         }
65468         /**
65469          * @return true if has new namespace define
65470          */
65471         function appendElement(el,domBuilder,currentNSMap){
65472                 var tagName = el.tagName;
65473                 var localNSMap = null;
65474                 //var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
65475                 var i = el.length;
65476                 while(i--){
65477                         var a = el[i];
65478                         var qName = a.qName;
65479                         var value = a.value;
65480                         var nsp = qName.indexOf(':');
65481                         if(nsp>0){
65482                                 var prefix = a.prefix = qName.slice(0,nsp);
65483                                 var localName = qName.slice(nsp+1);
65484                                 var nsPrefix = prefix === 'xmlns' && localName;
65485                         }else {
65486                                 localName = qName;
65487                                 prefix = null;
65488                                 nsPrefix = qName === 'xmlns' && '';
65489                         }
65490                         //can not set prefix,because prefix !== ''
65491                         a.localName = localName ;
65492                         //prefix == null for no ns prefix attribute 
65493                         if(nsPrefix !== false){//hack!!
65494                                 if(localNSMap == null){
65495                                         localNSMap = {};
65496                                         //console.log(currentNSMap,0)
65497                                         _copy(currentNSMap,currentNSMap={});
65498                                         //console.log(currentNSMap,1)
65499                                 }
65500                                 currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value;
65501                                 a.uri = 'http://www.w3.org/2000/xmlns/';
65502                                 domBuilder.startPrefixMapping(nsPrefix, value); 
65503                         }
65504                 }
65505                 var i = el.length;
65506                 while(i--){
65507                         a = el[i];
65508                         var prefix = a.prefix;
65509                         if(prefix){//no prefix attribute has no namespace
65510                                 if(prefix === 'xml'){
65511                                         a.uri = 'http://www.w3.org/XML/1998/namespace';
65512                                 }if(prefix !== 'xmlns'){
65513                                         a.uri = currentNSMap[prefix || ''];
65514                                         
65515                                         //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)}
65516                                 }
65517                         }
65518                 }
65519                 var nsp = tagName.indexOf(':');
65520                 if(nsp>0){
65521                         prefix = el.prefix = tagName.slice(0,nsp);
65522                         localName = el.localName = tagName.slice(nsp+1);
65523                 }else {
65524                         prefix = null;//important!!
65525                         localName = el.localName = tagName;
65526                 }
65527                 //no prefix element has default namespace
65528                 var ns = el.uri = currentNSMap[prefix || ''];
65529                 domBuilder.startElement(ns,localName,tagName,el);
65530                 //endPrefixMapping and startPrefixMapping have not any help for dom builder
65531                 //localNSMap = null
65532                 if(el.closed){
65533                         domBuilder.endElement(ns,localName,tagName);
65534                         if(localNSMap){
65535                                 for(prefix in localNSMap){
65536                                         domBuilder.endPrefixMapping(prefix); 
65537                                 }
65538                         }
65539                 }else {
65540                         el.currentNSMap = currentNSMap;
65541                         el.localNSMap = localNSMap;
65542                         //parseStack.push(el);
65543                         return true;
65544                 }
65545         }
65546         function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){
65547                 if(/^(?:script|textarea)$/i.test(tagName)){
65548                         var elEndStart =  source.indexOf('</'+tagName+'>',elStartEnd);
65549                         var text = source.substring(elStartEnd+1,elEndStart);
65550                         if(/[&<]/.test(text)){
65551                                 if(/^script$/i.test(tagName)){
65552                                         //if(!/\]\]>/.test(text)){
65553                                                 //lexHandler.startCDATA();
65554                                                 domBuilder.characters(text,0,text.length);
65555                                                 //lexHandler.endCDATA();
65556                                                 return elEndStart;
65557                                         //}
65558                                 }//}else{//text area
65559                                         text = text.replace(/&#?\w+;/g,entityReplacer);
65560                                         domBuilder.characters(text,0,text.length);
65561                                         return elEndStart;
65562                                 //}
65563                                 
65564                         }
65565                 }
65566                 return elStartEnd+1;
65567         }
65568         function fixSelfClosed(source,elStartEnd,tagName,closeMap){
65569                 //if(tagName in closeMap){
65570                 var pos = closeMap[tagName];
65571                 if(pos == null){
65572                         //console.log(tagName)
65573                         pos =  source.lastIndexOf('</'+tagName+'>');
65574                         if(pos<elStartEnd){//忘记闭合
65575                                 pos = source.lastIndexOf('</'+tagName);
65576                         }
65577                         closeMap[tagName] =pos;
65578                 }
65579                 return pos<elStartEnd;
65580                 //} 
65581         }
65582         function _copy(source,target){
65583                 for(var n in source){target[n] = source[n];}
65584         }
65585         function parseDCC(source,start,domBuilder,errorHandler){//sure start with '<!'
65586                 var next= source.charAt(start+2);
65587                 switch(next){
65588                 case '-':
65589                         if(source.charAt(start + 3) === '-'){
65590                                 var end = source.indexOf('-->',start+4);
65591                                 //append comment source.substring(4,end)//<!--
65592                                 if(end>start){
65593                                         domBuilder.comment(source,start+4,end-start-4);
65594                                         return end+3;
65595                                 }else {
65596                                         errorHandler.error("Unclosed comment");
65597                                         return -1;
65598                                 }
65599                         }else {
65600                                 //error
65601                                 return -1;
65602                         }
65603                 default:
65604                         if(source.substr(start+3,6) == 'CDATA['){
65605                                 var end = source.indexOf(']]>',start+9);
65606                                 domBuilder.startCDATA();
65607                                 domBuilder.characters(source,start+9,end-start-9);
65608                                 domBuilder.endCDATA(); 
65609                                 return end+3;
65610                         }
65611                         //<!DOCTYPE
65612                         //startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId) 
65613                         var matchs = split(source,start);
65614                         var len = matchs.length;
65615                         if(len>1 && /!doctype/i.test(matchs[0][0])){
65616                                 var name = matchs[1][0];
65617                                 var pubid = len>3 && /^public$/i.test(matchs[2][0]) && matchs[3][0];
65618                                 var sysid = len>4 && matchs[4][0];
65619                                 var lastMatch = matchs[len-1];
65620                                 domBuilder.startDTD(name,pubid && pubid.replace(/^(['"])(.*?)\1$/,'$2'),
65621                                                 sysid && sysid.replace(/^(['"])(.*?)\1$/,'$2'));
65622                                 domBuilder.endDTD();
65623                                 
65624                                 return lastMatch.index+lastMatch[0].length
65625                         }
65626                 }
65627                 return -1;
65628         }
65629
65630
65631
65632         function parseInstruction(source,start,domBuilder){
65633                 var end = source.indexOf('?>',start);
65634                 if(end){
65635                         var match = source.substring(start,end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);
65636                         if(match){
65637                                 var len = match[0].length;
65638                                 domBuilder.processingInstruction(match[1], match[2]) ;
65639                                 return end+2;
65640                         }else {//error
65641                                 return -1;
65642                         }
65643                 }
65644                 return -1;
65645         }
65646
65647         /**
65648          * @param source
65649          */
65650         function ElementAttributes(source){
65651                 
65652         }
65653         ElementAttributes.prototype = {
65654                 setTagName:function(tagName){
65655                         if(!tagNamePattern.test(tagName)){
65656                                 throw new Error('invalid tagName:'+tagName)
65657                         }
65658                         this.tagName = tagName;
65659                 },
65660                 add:function(qName,value,offset){
65661                         if(!tagNamePattern.test(qName)){
65662                                 throw new Error('invalid attribute:'+qName)
65663                         }
65664                         this[this.length++] = {qName:qName,value:value,offset:offset};
65665                 },
65666                 length:0,
65667                 getLocalName:function(i){return this[i].localName},
65668                 getLocator:function(i){return this[i].locator},
65669                 getQName:function(i){return this[i].qName},
65670                 getURI:function(i){return this[i].uri},
65671                 getValue:function(i){return this[i].value}
65672         //      ,getIndex:function(uri, localName)){
65673         //              if(localName){
65674         //                      
65675         //              }else{
65676         //                      var qName = uri
65677         //              }
65678         //      },
65679         //      getValue:function(){return this.getValue(this.getIndex.apply(this,arguments))},
65680         //      getType:function(uri,localName){}
65681         //      getType:function(i){},
65682         };
65683
65684
65685
65686
65687         function _set_proto_(thiz,parent){
65688                 thiz.__proto__ = parent;
65689                 return thiz;
65690         }
65691         if(!(_set_proto_({},_set_proto_.prototype) instanceof _set_proto_)){
65692                 _set_proto_ = function(thiz,parent){
65693                         function p(){}          p.prototype = parent;
65694                         p = new p();
65695                         for(parent in thiz){
65696                                 p[parent] = thiz[parent];
65697                         }
65698                         return p;
65699                 };
65700         }
65701
65702         function split(source,start){
65703                 var match;
65704                 var buf = [];
65705                 var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;
65706                 reg.lastIndex = start;
65707                 reg.exec(source);//skip <
65708                 while(match = reg.exec(source)){
65709                         buf.push(match);
65710                         if(match[1]){ return buf; }
65711                 }
65712         }
65713
65714         var XMLReader_1 = XMLReader;
65715
65716         var sax = {
65717                 XMLReader: XMLReader_1
65718         };
65719
65720         /*
65721          * DOM Level 2
65722          * Object DOMException
65723          * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
65724          * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html
65725          */
65726
65727         function copy$2(src,dest){
65728                 for(var p in src){
65729                         dest[p] = src[p];
65730                 }
65731         }
65732         /**
65733         ^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));?
65734         ^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));?
65735          */
65736         function _extends(Class,Super){
65737                 var pt = Class.prototype;
65738                 if(Object.create){
65739                         var ppt = Object.create(Super.prototype);
65740                         pt.__proto__ = ppt;
65741                 }
65742                 if(!(pt instanceof Super)){
65743                         function t(){}          t.prototype = Super.prototype;
65744                         t = new t();
65745                         copy$2(pt,t);
65746                         Class.prototype = pt = t;
65747                 }
65748                 if(pt.constructor != Class){
65749                         if(typeof Class != 'function'){
65750                                 console.error("unknow Class:"+Class);
65751                         }
65752                         pt.constructor = Class;
65753                 }
65754         }
65755         var htmlns = 'http://www.w3.org/1999/xhtml' ;
65756         // Node Types
65757         var NodeType = {};
65758         var ELEMENT_NODE                = NodeType.ELEMENT_NODE                = 1;
65759         var ATTRIBUTE_NODE              = NodeType.ATTRIBUTE_NODE              = 2;
65760         var TEXT_NODE                   = NodeType.TEXT_NODE                   = 3;
65761         var CDATA_SECTION_NODE          = NodeType.CDATA_SECTION_NODE          = 4;
65762         var ENTITY_REFERENCE_NODE       = NodeType.ENTITY_REFERENCE_NODE       = 5;
65763         var ENTITY_NODE                 = NodeType.ENTITY_NODE                 = 6;
65764         var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7;
65765         var COMMENT_NODE                = NodeType.COMMENT_NODE                = 8;
65766         var DOCUMENT_NODE               = NodeType.DOCUMENT_NODE               = 9;
65767         var DOCUMENT_TYPE_NODE          = NodeType.DOCUMENT_TYPE_NODE          = 10;
65768         var DOCUMENT_FRAGMENT_NODE      = NodeType.DOCUMENT_FRAGMENT_NODE      = 11;
65769         var NOTATION_NODE               = NodeType.NOTATION_NODE               = 12;
65770
65771         // ExceptionCode
65772         var ExceptionCode = {};
65773         var ExceptionMessage = {};
65774         var INDEX_SIZE_ERR              = ExceptionCode.INDEX_SIZE_ERR              = ((ExceptionMessage[1]="Index size error"),1);
65775         var DOMSTRING_SIZE_ERR          = ExceptionCode.DOMSTRING_SIZE_ERR          = ((ExceptionMessage[2]="DOMString size error"),2);
65776         var HIERARCHY_REQUEST_ERR       = ExceptionCode.HIERARCHY_REQUEST_ERR       = ((ExceptionMessage[3]="Hierarchy request error"),3);
65777         var WRONG_DOCUMENT_ERR          = ExceptionCode.WRONG_DOCUMENT_ERR          = ((ExceptionMessage[4]="Wrong document"),4);
65778         var INVALID_CHARACTER_ERR       = ExceptionCode.INVALID_CHARACTER_ERR       = ((ExceptionMessage[5]="Invalid character"),5);
65779         var NO_DATA_ALLOWED_ERR         = ExceptionCode.NO_DATA_ALLOWED_ERR         = ((ExceptionMessage[6]="No data allowed"),6);
65780         var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = ((ExceptionMessage[7]="No modification allowed"),7);
65781         var NOT_FOUND_ERR               = ExceptionCode.NOT_FOUND_ERR               = ((ExceptionMessage[8]="Not found"),8);
65782         var NOT_SUPPORTED_ERR           = ExceptionCode.NOT_SUPPORTED_ERR           = ((ExceptionMessage[9]="Not supported"),9);
65783         var INUSE_ATTRIBUTE_ERR         = ExceptionCode.INUSE_ATTRIBUTE_ERR         = ((ExceptionMessage[10]="Attribute in use"),10);
65784         //level2
65785         var INVALID_STATE_ERR           = ExceptionCode.INVALID_STATE_ERR               = ((ExceptionMessage[11]="Invalid state"),11);
65786         var SYNTAX_ERR                  = ExceptionCode.SYNTAX_ERR                      = ((ExceptionMessage[12]="Syntax error"),12);
65787         var INVALID_MODIFICATION_ERR    = ExceptionCode.INVALID_MODIFICATION_ERR        = ((ExceptionMessage[13]="Invalid modification"),13);
65788         var NAMESPACE_ERR               = ExceptionCode.NAMESPACE_ERR                   = ((ExceptionMessage[14]="Invalid namespace"),14);
65789         var INVALID_ACCESS_ERR          = ExceptionCode.INVALID_ACCESS_ERR              = ((ExceptionMessage[15]="Invalid access"),15);
65790
65791
65792         function DOMException$2(code, message) {
65793                 if(message instanceof Error){
65794                         var error = message;
65795                 }else {
65796                         error = this;
65797                         Error.call(this, ExceptionMessage[code]);
65798                         this.message = ExceptionMessage[code];
65799                         if(Error.captureStackTrace) { Error.captureStackTrace(this, DOMException$2); }
65800                 }
65801                 error.code = code;
65802                 if(message) { this.message = this.message + ": " + message; }
65803                 return error;
65804         }DOMException$2.prototype = Error.prototype;
65805         copy$2(ExceptionCode,DOMException$2);
65806         /**
65807          * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
65808          * 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.
65809          * The items in the NodeList are accessible via an integral index, starting from 0.
65810          */
65811         function NodeList() {
65812         }NodeList.prototype = {
65813                 /**
65814                  * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive.
65815                  * @standard level1
65816                  */
65817                 length:0, 
65818                 /**
65819                  * 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.
65820                  * @standard level1
65821                  * @param index  unsigned long 
65822                  *   Index into the collection.
65823                  * @return Node
65824                  *      The node at the indexth position in the NodeList, or null if that is not a valid index. 
65825                  */
65826                 item: function(index) {
65827                         return this[index] || null;
65828                 },
65829                 toString:function(isHTML,nodeFilter){
65830                         for(var buf = [], i = 0;i<this.length;i++){
65831                                 serializeToString(this[i],buf,isHTML,nodeFilter);
65832                         }
65833                         return buf.join('');
65834                 }
65835         };
65836         function LiveNodeList(node,refresh){
65837                 this._node = node;
65838                 this._refresh = refresh;
65839                 _updateLiveList(this);
65840         }
65841         function _updateLiveList(list){
65842                 var inc = list._node._inc || list._node.ownerDocument._inc;
65843                 if(list._inc != inc){
65844                         var ls = list._refresh(list._node);
65845                         //console.log(ls.length)
65846                         __set__(list,'length',ls.length);
65847                         copy$2(ls,list);
65848                         list._inc = inc;
65849                 }
65850         }
65851         LiveNodeList.prototype.item = function(i){
65852                 _updateLiveList(this);
65853                 return this[i];
65854         };
65855
65856         _extends(LiveNodeList,NodeList);
65857         /**
65858          * 
65859          * 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.
65860          * NamedNodeMap objects in the DOM are live.
65861          * used for attributes or DocumentType entities 
65862          */
65863         function NamedNodeMap() {
65864         }
65865         function _findNodeIndex(list,node){
65866                 var i = list.length;
65867                 while(i--){
65868                         if(list[i] === node){return i}
65869                 }
65870         }
65871
65872         function _addNamedNode(el,list,newAttr,oldAttr){
65873                 if(oldAttr){
65874                         list[_findNodeIndex(list,oldAttr)] = newAttr;
65875                 }else {
65876                         list[list.length++] = newAttr;
65877                 }
65878                 if(el){
65879                         newAttr.ownerElement = el;
65880                         var doc = el.ownerDocument;
65881                         if(doc){
65882                                 oldAttr && _onRemoveAttribute(doc,el,oldAttr);
65883                                 _onAddAttribute(doc,el,newAttr);
65884                         }
65885                 }
65886         }
65887         function _removeNamedNode(el,list,attr){
65888                 //console.log('remove attr:'+attr)
65889                 var i = _findNodeIndex(list,attr);
65890                 if(i>=0){
65891                         var lastIndex = list.length-1;
65892                         while(i<lastIndex){
65893                                 list[i] = list[++i];
65894                         }
65895                         list.length = lastIndex;
65896                         if(el){
65897                                 var doc = el.ownerDocument;
65898                                 if(doc){
65899                                         _onRemoveAttribute(doc,el,attr);
65900                                         attr.ownerElement = null;
65901                                 }
65902                         }
65903                 }else {
65904                         throw DOMException$2(NOT_FOUND_ERR,new Error(el.tagName+'@'+attr))
65905                 }
65906         }
65907         NamedNodeMap.prototype = {
65908                 length:0,
65909                 item:NodeList.prototype.item,
65910                 getNamedItem: function(key) {
65911         //              if(key.indexOf(':')>0 || key == 'xmlns'){
65912         //                      return null;
65913         //              }
65914                         //console.log()
65915                         var i = this.length;
65916                         while(i--){
65917                                 var attr = this[i];
65918                                 //console.log(attr.nodeName,key)
65919                                 if(attr.nodeName == key){
65920                                         return attr;
65921                                 }
65922                         }
65923                 },
65924                 setNamedItem: function(attr) {
65925                         var el = attr.ownerElement;
65926                         if(el && el!=this._ownerElement){
65927                                 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
65928                         }
65929                         var oldAttr = this.getNamedItem(attr.nodeName);
65930                         _addNamedNode(this._ownerElement,this,attr,oldAttr);
65931                         return oldAttr;
65932                 },
65933                 /* returns Node */
65934                 setNamedItemNS: function(attr) {// raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR
65935                         var el = attr.ownerElement, oldAttr;
65936                         if(el && el!=this._ownerElement){
65937                                 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
65938                         }
65939                         oldAttr = this.getNamedItemNS(attr.namespaceURI,attr.localName);
65940                         _addNamedNode(this._ownerElement,this,attr,oldAttr);
65941                         return oldAttr;
65942                 },
65943
65944                 /* returns Node */
65945                 removeNamedItem: function(key) {
65946                         var attr = this.getNamedItem(key);
65947                         _removeNamedNode(this._ownerElement,this,attr);
65948                         return attr;
65949                         
65950                         
65951                 },// raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR
65952                 
65953                 //for level2
65954                 removeNamedItemNS:function(namespaceURI,localName){
65955                         var attr = this.getNamedItemNS(namespaceURI,localName);
65956                         _removeNamedNode(this._ownerElement,this,attr);
65957                         return attr;
65958                 },
65959                 getNamedItemNS: function(namespaceURI, localName) {
65960                         var i = this.length;
65961                         while(i--){
65962                                 var node = this[i];
65963                                 if(node.localName == localName && node.namespaceURI == namespaceURI){
65964                                         return node;
65965                                 }
65966                         }
65967                         return null;
65968                 }
65969         };
65970         /**
65971          * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490
65972          */
65973         function DOMImplementation(/* Object */ features) {
65974                 this._features = {};
65975                 if (features) {
65976                         for (var feature in features) {
65977                                  this._features = features[feature];
65978                         }
65979                 }
65980         }
65981         DOMImplementation.prototype = {
65982                 hasFeature: function(/* string */ feature, /* string */ version) {
65983                         var versions = this._features[feature.toLowerCase()];
65984                         if (versions && (!version || version in versions)) {
65985                                 return true;
65986                         } else {
65987                                 return false;
65988                         }
65989                 },
65990                 // Introduced in DOM Level 2:
65991                 createDocument:function(namespaceURI,  qualifiedName, doctype){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR
65992                         var doc = new Document();
65993                         doc.implementation = this;
65994                         doc.childNodes = new NodeList();
65995                         doc.doctype = doctype;
65996                         if(doctype){
65997                                 doc.appendChild(doctype);
65998                         }
65999                         if(qualifiedName){
66000                                 var root = doc.createElementNS(namespaceURI,qualifiedName);
66001                                 doc.appendChild(root);
66002                         }
66003                         return doc;
66004                 },
66005                 // Introduced in DOM Level 2:
66006                 createDocumentType:function(qualifiedName, publicId, systemId){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR
66007                         var node = new DocumentType();
66008                         node.name = qualifiedName;
66009                         node.nodeName = qualifiedName;
66010                         node.publicId = publicId;
66011                         node.systemId = systemId;
66012                         // Introduced in DOM Level 2:
66013                         //readonly attribute DOMString        internalSubset;
66014                         
66015                         //TODO:..
66016                         //  readonly attribute NamedNodeMap     entities;
66017                         //  readonly attribute NamedNodeMap     notations;
66018                         return node;
66019                 }
66020         };
66021
66022
66023         /**
66024          * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
66025          */
66026
66027         function Node() {
66028         }
66029         Node.prototype = {
66030                 firstChild : null,
66031                 lastChild : null,
66032                 previousSibling : null,
66033                 nextSibling : null,
66034                 attributes : null,
66035                 parentNode : null,
66036                 childNodes : null,
66037                 ownerDocument : null,
66038                 nodeValue : null,
66039                 namespaceURI : null,
66040                 prefix : null,
66041                 localName : null,
66042                 // Modified in DOM Level 2:
66043                 insertBefore:function(newChild, refChild){//raises 
66044                         return _insertBefore(this,newChild,refChild);
66045                 },
66046                 replaceChild:function(newChild, oldChild){//raises 
66047                         this.insertBefore(newChild,oldChild);
66048                         if(oldChild){
66049                                 this.removeChild(oldChild);
66050                         }
66051                 },
66052                 removeChild:function(oldChild){
66053                         return _removeChild(this,oldChild);
66054                 },
66055                 appendChild:function(newChild){
66056                         return this.insertBefore(newChild,null);
66057                 },
66058                 hasChildNodes:function(){
66059                         return this.firstChild != null;
66060                 },
66061                 cloneNode:function(deep){
66062                         return cloneNode(this.ownerDocument||this,this,deep);
66063                 },
66064                 // Modified in DOM Level 2:
66065                 normalize:function(){
66066                         var child = this.firstChild;
66067                         while(child){
66068                                 var next = child.nextSibling;
66069                                 if(next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE){
66070                                         this.removeChild(next);
66071                                         child.appendData(next.data);
66072                                 }else {
66073                                         child.normalize();
66074                                         child = next;
66075                                 }
66076                         }
66077                 },
66078                 // Introduced in DOM Level 2:
66079                 isSupported:function(feature, version){
66080                         return this.ownerDocument.implementation.hasFeature(feature,version);
66081                 },
66082             // Introduced in DOM Level 2:
66083             hasAttributes:function(){
66084                 return this.attributes.length>0;
66085             },
66086             lookupPrefix:function(namespaceURI){
66087                 var el = this;
66088                 while(el){
66089                         var map = el._nsMap;
66090                         //console.dir(map)
66091                         if(map){
66092                                 for(var n in map){
66093                                         if(map[n] == namespaceURI){
66094                                                 return n;
66095                                         }
66096                                 }
66097                         }
66098                         el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
66099                 }
66100                 return null;
66101             },
66102             // Introduced in DOM Level 3:
66103             lookupNamespaceURI:function(prefix){
66104                 var el = this;
66105                 while(el){
66106                         var map = el._nsMap;
66107                         //console.dir(map)
66108                         if(map){
66109                                 if(prefix in map){
66110                                         return map[prefix] ;
66111                                 }
66112                         }
66113                         el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
66114                 }
66115                 return null;
66116             },
66117             // Introduced in DOM Level 3:
66118             isDefaultNamespace:function(namespaceURI){
66119                 var prefix = this.lookupPrefix(namespaceURI);
66120                 return prefix == null;
66121             }
66122         };
66123
66124
66125         function _xmlEncoder(c){
66126                 return c == '<' && '&lt;' ||
66127                  c == '>' && '&gt;' ||
66128                  c == '&' && '&amp;' ||
66129                  c == '"' && '&quot;' ||
66130                  '&#'+c.charCodeAt()+';'
66131         }
66132
66133
66134         copy$2(NodeType,Node);
66135         copy$2(NodeType,Node.prototype);
66136
66137         /**
66138          * @param callback return true for continue,false for break
66139          * @return boolean true: break visit;
66140          */
66141         function _visitNode(node,callback){
66142                 if(callback(node)){
66143                         return true;
66144                 }
66145                 if(node = node.firstChild){
66146                         do{
66147                                 if(_visitNode(node,callback)){return true}
66148                 }while(node=node.nextSibling)
66149             }
66150         }
66151
66152
66153
66154         function Document(){
66155         }
66156         function _onAddAttribute(doc,el,newAttr){
66157                 doc && doc._inc++;
66158                 var ns = newAttr.namespaceURI ;
66159                 if(ns == 'http://www.w3.org/2000/xmlns/'){
66160                         //update namespace
66161                         el._nsMap[newAttr.prefix?newAttr.localName:''] = newAttr.value;
66162                 }
66163         }
66164         function _onRemoveAttribute(doc,el,newAttr,remove){
66165                 doc && doc._inc++;
66166                 var ns = newAttr.namespaceURI ;
66167                 if(ns == 'http://www.w3.org/2000/xmlns/'){
66168                         //update namespace
66169                         delete el._nsMap[newAttr.prefix?newAttr.localName:''];
66170                 }
66171         }
66172         function _onUpdateChild(doc,el,newChild){
66173                 if(doc && doc._inc){
66174                         doc._inc++;
66175                         //update childNodes
66176                         var cs = el.childNodes;
66177                         if(newChild){
66178                                 cs[cs.length++] = newChild;
66179                         }else {
66180                                 //console.log(1)
66181                                 var child = el.firstChild;
66182                                 var i = 0;
66183                                 while(child){
66184                                         cs[i++] = child;
66185                                         child =child.nextSibling;
66186                                 }
66187                                 cs.length = i;
66188                         }
66189                 }
66190         }
66191
66192         /**
66193          * attributes;
66194          * children;
66195          * 
66196          * writeable properties:
66197          * nodeValue,Attr:value,CharacterData:data
66198          * prefix
66199          */
66200         function _removeChild(parentNode,child){
66201                 var previous = child.previousSibling;
66202                 var next = child.nextSibling;
66203                 if(previous){
66204                         previous.nextSibling = next;
66205                 }else {
66206                         parentNode.firstChild = next;
66207                 }
66208                 if(next){
66209                         next.previousSibling = previous;
66210                 }else {
66211                         parentNode.lastChild = previous;
66212                 }
66213                 _onUpdateChild(parentNode.ownerDocument,parentNode);
66214                 return child;
66215         }
66216         /**
66217          * preformance key(refChild == null)
66218          */
66219         function _insertBefore(parentNode,newChild,nextChild){
66220                 var cp = newChild.parentNode;
66221                 if(cp){
66222                         cp.removeChild(newChild);//remove and update
66223                 }
66224                 if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
66225                         var newFirst = newChild.firstChild;
66226                         if (newFirst == null) {
66227                                 return newChild;
66228                         }
66229                         var newLast = newChild.lastChild;
66230                 }else {
66231                         newFirst = newLast = newChild;
66232                 }
66233                 var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild;
66234
66235                 newFirst.previousSibling = pre;
66236                 newLast.nextSibling = nextChild;
66237                 
66238                 
66239                 if(pre){
66240                         pre.nextSibling = newFirst;
66241                 }else {
66242                         parentNode.firstChild = newFirst;
66243                 }
66244                 if(nextChild == null){
66245                         parentNode.lastChild = newLast;
66246                 }else {
66247                         nextChild.previousSibling = newLast;
66248                 }
66249                 do{
66250                         newFirst.parentNode = parentNode;
66251                 }while(newFirst !== newLast && (newFirst= newFirst.nextSibling))
66252                 _onUpdateChild(parentNode.ownerDocument||parentNode,parentNode);
66253                 //console.log(parentNode.lastChild.nextSibling == null)
66254                 if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
66255                         newChild.firstChild = newChild.lastChild = null;
66256                 }
66257                 return newChild;
66258         }
66259         function _appendSingleChild(parentNode,newChild){
66260                 var cp = newChild.parentNode;
66261                 if(cp){
66262                         var pre = parentNode.lastChild;
66263                         cp.removeChild(newChild);//remove and update
66264                         var pre = parentNode.lastChild;
66265                 }
66266                 var pre = parentNode.lastChild;
66267                 newChild.parentNode = parentNode;
66268                 newChild.previousSibling = pre;
66269                 newChild.nextSibling = null;
66270                 if(pre){
66271                         pre.nextSibling = newChild;
66272                 }else {
66273                         parentNode.firstChild = newChild;
66274                 }
66275                 parentNode.lastChild = newChild;
66276                 _onUpdateChild(parentNode.ownerDocument,parentNode,newChild);
66277                 return newChild;
66278                 //console.log("__aa",parentNode.lastChild.nextSibling == null)
66279         }
66280         Document.prototype = {
66281                 //implementation : null,
66282                 nodeName :  '#document',
66283                 nodeType :  DOCUMENT_NODE,
66284                 doctype :  null,
66285                 documentElement :  null,
66286                 _inc : 1,
66287                 
66288                 insertBefore :  function(newChild, refChild){//raises 
66289                         if(newChild.nodeType == DOCUMENT_FRAGMENT_NODE){
66290                                 var child = newChild.firstChild;
66291                                 while(child){
66292                                         var next = child.nextSibling;
66293                                         this.insertBefore(child,refChild);
66294                                         child = next;
66295                                 }
66296                                 return newChild;
66297                         }
66298                         if(this.documentElement == null && newChild.nodeType == ELEMENT_NODE){
66299                                 this.documentElement = newChild;
66300                         }
66301                         
66302                         return _insertBefore(this,newChild,refChild),(newChild.ownerDocument = this),newChild;
66303                 },
66304                 removeChild :  function(oldChild){
66305                         if(this.documentElement == oldChild){
66306                                 this.documentElement = null;
66307                         }
66308                         return _removeChild(this,oldChild);
66309                 },
66310                 // Introduced in DOM Level 2:
66311                 importNode : function(importedNode,deep){
66312                         return importNode(this,importedNode,deep);
66313                 },
66314                 // Introduced in DOM Level 2:
66315                 getElementById :        function(id){
66316                         var rtv = null;
66317                         _visitNode(this.documentElement,function(node){
66318                                 if(node.nodeType == ELEMENT_NODE){
66319                                         if(node.getAttribute('id') == id){
66320                                                 rtv = node;
66321                                                 return true;
66322                                         }
66323                                 }
66324                         });
66325                         return rtv;
66326                 },
66327                 
66328                 //document factory method:
66329                 createElement : function(tagName){
66330                         var node = new Element();
66331                         node.ownerDocument = this;
66332                         node.nodeName = tagName;
66333                         node.tagName = tagName;
66334                         node.childNodes = new NodeList();
66335                         var attrs       = node.attributes = new NamedNodeMap();
66336                         attrs._ownerElement = node;
66337                         return node;
66338                 },
66339                 createDocumentFragment :        function(){
66340                         var node = new DocumentFragment();
66341                         node.ownerDocument = this;
66342                         node.childNodes = new NodeList();
66343                         return node;
66344                 },
66345                 createTextNode :        function(data){
66346                         var node = new Text();
66347                         node.ownerDocument = this;
66348                         node.appendData(data);
66349                         return node;
66350                 },
66351                 createComment : function(data){
66352                         var node = new Comment();
66353                         node.ownerDocument = this;
66354                         node.appendData(data);
66355                         return node;
66356                 },
66357                 createCDATASection :    function(data){
66358                         var node = new CDATASection();
66359                         node.ownerDocument = this;
66360                         node.appendData(data);
66361                         return node;
66362                 },
66363                 createProcessingInstruction :   function(target,data){
66364                         var node = new ProcessingInstruction();
66365                         node.ownerDocument = this;
66366                         node.tagName = node.target = target;
66367                         node.nodeValue= node.data = data;
66368                         return node;
66369                 },
66370                 createAttribute :       function(name){
66371                         var node = new Attr();
66372                         node.ownerDocument      = this;
66373                         node.name = name;
66374                         node.nodeName   = name;
66375                         node.localName = name;
66376                         node.specified = true;
66377                         return node;
66378                 },
66379                 createEntityReference : function(name){
66380                         var node = new EntityReference();
66381                         node.ownerDocument      = this;
66382                         node.nodeName   = name;
66383                         return node;
66384                 },
66385                 // Introduced in DOM Level 2:
66386                 createElementNS :       function(namespaceURI,qualifiedName){
66387                         var node = new Element();
66388                         var pl = qualifiedName.split(':');
66389                         var attrs       = node.attributes = new NamedNodeMap();
66390                         node.childNodes = new NodeList();
66391                         node.ownerDocument = this;
66392                         node.nodeName = qualifiedName;
66393                         node.tagName = qualifiedName;
66394                         node.namespaceURI = namespaceURI;
66395                         if(pl.length == 2){
66396                                 node.prefix = pl[0];
66397                                 node.localName = pl[1];
66398                         }else {
66399                                 //el.prefix = null;
66400                                 node.localName = qualifiedName;
66401                         }
66402                         attrs._ownerElement = node;
66403                         return node;
66404                 },
66405                 // Introduced in DOM Level 2:
66406                 createAttributeNS :     function(namespaceURI,qualifiedName){
66407                         var node = new Attr();
66408                         var pl = qualifiedName.split(':');
66409                         node.ownerDocument = this;
66410                         node.nodeName = qualifiedName;
66411                         node.name = qualifiedName;
66412                         node.namespaceURI = namespaceURI;
66413                         node.specified = true;
66414                         if(pl.length == 2){
66415                                 node.prefix = pl[0];
66416                                 node.localName = pl[1];
66417                         }else {
66418                                 //el.prefix = null;
66419                                 node.localName = qualifiedName;
66420                         }
66421                         return node;
66422                 }
66423         };
66424         _extends(Document,Node);
66425
66426
66427         function Element() {
66428                 this._nsMap = {};
66429         }Element.prototype = {
66430                 nodeType : ELEMENT_NODE,
66431                 hasAttribute : function(name){
66432                         return this.getAttributeNode(name)!=null;
66433                 },
66434                 getAttribute : function(name){
66435                         var attr = this.getAttributeNode(name);
66436                         return attr && attr.value || '';
66437                 },
66438                 getAttributeNode : function(name){
66439                         return this.attributes.getNamedItem(name);
66440                 },
66441                 setAttribute : function(name, value){
66442                         var attr = this.ownerDocument.createAttribute(name);
66443                         attr.value = attr.nodeValue = "" + value;
66444                         this.setAttributeNode(attr);
66445                 },
66446                 removeAttribute : function(name){
66447                         var attr = this.getAttributeNode(name);
66448                         attr && this.removeAttributeNode(attr);
66449                 },
66450                 
66451                 //four real opeartion method
66452                 appendChild:function(newChild){
66453                         if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
66454                                 return this.insertBefore(newChild,null);
66455                         }else {
66456                                 return _appendSingleChild(this,newChild);
66457                         }
66458                 },
66459                 setAttributeNode : function(newAttr){
66460                         return this.attributes.setNamedItem(newAttr);
66461                 },
66462                 setAttributeNodeNS : function(newAttr){
66463                         return this.attributes.setNamedItemNS(newAttr);
66464                 },
66465                 removeAttributeNode : function(oldAttr){
66466                         //console.log(this == oldAttr.ownerElement)
66467                         return this.attributes.removeNamedItem(oldAttr.nodeName);
66468                 },
66469                 //get real attribute name,and remove it by removeAttributeNode
66470                 removeAttributeNS : function(namespaceURI, localName){
66471                         var old = this.getAttributeNodeNS(namespaceURI, localName);
66472                         old && this.removeAttributeNode(old);
66473                 },
66474                 
66475                 hasAttributeNS : function(namespaceURI, localName){
66476                         return this.getAttributeNodeNS(namespaceURI, localName)!=null;
66477                 },
66478                 getAttributeNS : function(namespaceURI, localName){
66479                         var attr = this.getAttributeNodeNS(namespaceURI, localName);
66480                         return attr && attr.value || '';
66481                 },
66482                 setAttributeNS : function(namespaceURI, qualifiedName, value){
66483                         var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
66484                         attr.value = attr.nodeValue = "" + value;
66485                         this.setAttributeNode(attr);
66486                 },
66487                 getAttributeNodeNS : function(namespaceURI, localName){
66488                         return this.attributes.getNamedItemNS(namespaceURI, localName);
66489                 },
66490                 
66491                 getElementsByTagName : function(tagName){
66492                         return new LiveNodeList(this,function(base){
66493                                 var ls = [];
66494                                 _visitNode(base,function(node){
66495                                         if(node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)){
66496                                                 ls.push(node);
66497                                         }
66498                                 });
66499                                 return ls;
66500                         });
66501                 },
66502                 getElementsByTagNameNS : function(namespaceURI, localName){
66503                         return new LiveNodeList(this,function(base){
66504                                 var ls = [];
66505                                 _visitNode(base,function(node){
66506                                         if(node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)){
66507                                                 ls.push(node);
66508                                         }
66509                                 });
66510                                 return ls;
66511                                 
66512                         });
66513                 }
66514         };
66515         Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
66516         Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
66517
66518
66519         _extends(Element,Node);
66520         function Attr() {
66521         }Attr.prototype.nodeType = ATTRIBUTE_NODE;
66522         _extends(Attr,Node);
66523
66524
66525         function CharacterData() {
66526         }CharacterData.prototype = {
66527                 data : '',
66528                 substringData : function(offset, count) {
66529                         return this.data.substring(offset, offset+count);
66530                 },
66531                 appendData: function(text) {
66532                         text = this.data+text;
66533                         this.nodeValue = this.data = text;
66534                         this.length = text.length;
66535                 },
66536                 insertData: function(offset,text) {
66537                         this.replaceData(offset,0,text);
66538                 
66539                 },
66540                 appendChild:function(newChild){
66541                         throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR])
66542                 },
66543                 deleteData: function(offset, count) {
66544                         this.replaceData(offset,count,"");
66545                 },
66546                 replaceData: function(offset, count, text) {
66547                         var start = this.data.substring(0,offset);
66548                         var end = this.data.substring(offset+count);
66549                         text = start + text + end;
66550                         this.nodeValue = this.data = text;
66551                         this.length = text.length;
66552                 }
66553         };
66554         _extends(CharacterData,Node);
66555         function Text() {
66556         }Text.prototype = {
66557                 nodeName : "#text",
66558                 nodeType : TEXT_NODE,
66559                 splitText : function(offset) {
66560                         var text = this.data;
66561                         var newText = text.substring(offset);
66562                         text = text.substring(0, offset);
66563                         this.data = this.nodeValue = text;
66564                         this.length = text.length;
66565                         var newNode = this.ownerDocument.createTextNode(newText);
66566                         if(this.parentNode){
66567                                 this.parentNode.insertBefore(newNode, this.nextSibling);
66568                         }
66569                         return newNode;
66570                 }
66571         };
66572         _extends(Text,CharacterData);
66573         function Comment() {
66574         }Comment.prototype = {
66575                 nodeName : "#comment",
66576                 nodeType : COMMENT_NODE
66577         };
66578         _extends(Comment,CharacterData);
66579
66580         function CDATASection() {
66581         }CDATASection.prototype = {
66582                 nodeName : "#cdata-section",
66583                 nodeType : CDATA_SECTION_NODE
66584         };
66585         _extends(CDATASection,CharacterData);
66586
66587
66588         function DocumentType() {
66589         }DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
66590         _extends(DocumentType,Node);
66591
66592         function Notation() {
66593         }Notation.prototype.nodeType = NOTATION_NODE;
66594         _extends(Notation,Node);
66595
66596         function Entity() {
66597         }Entity.prototype.nodeType = ENTITY_NODE;
66598         _extends(Entity,Node);
66599
66600         function EntityReference() {
66601         }EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
66602         _extends(EntityReference,Node);
66603
66604         function DocumentFragment() {
66605         }DocumentFragment.prototype.nodeName =  "#document-fragment";
66606         DocumentFragment.prototype.nodeType =   DOCUMENT_FRAGMENT_NODE;
66607         _extends(DocumentFragment,Node);
66608
66609
66610         function ProcessingInstruction() {
66611         }
66612         ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
66613         _extends(ProcessingInstruction,Node);
66614         function XMLSerializer$1(){}
66615         XMLSerializer$1.prototype.serializeToString = function(node,isHtml,nodeFilter){
66616                 return nodeSerializeToString.call(node,isHtml,nodeFilter);
66617         };
66618         Node.prototype.toString = nodeSerializeToString;
66619         function nodeSerializeToString(isHtml,nodeFilter){
66620                 var buf = [];
66621                 var refNode = this.nodeType == 9?this.documentElement:this;
66622                 var prefix = refNode.prefix;
66623                 var uri = refNode.namespaceURI;
66624                 
66625                 if(uri && prefix == null){
66626                         //console.log(prefix)
66627                         var prefix = refNode.lookupPrefix(uri);
66628                         if(prefix == null){
66629                                 //isHTML = true;
66630                                 var visibleNamespaces=[
66631                                 {namespace:uri,prefix:null} ];
66632                         }
66633                 }
66634                 serializeToString(this,buf,isHtml,nodeFilter,visibleNamespaces);
66635                 //console.log('###',this.nodeType,uri,prefix,buf.join(''))
66636                 return buf.join('');
66637         }
66638         function needNamespaceDefine(node,isHTML, visibleNamespaces) {
66639                 var prefix = node.prefix||'';
66640                 var uri = node.namespaceURI;
66641                 if (!prefix && !uri){
66642                         return false;
66643                 }
66644                 if (prefix === "xml" && uri === "http://www.w3.org/XML/1998/namespace" 
66645                         || uri == 'http://www.w3.org/2000/xmlns/'){
66646                         return false;
66647                 }
66648                 
66649                 var i = visibleNamespaces.length; 
66650                 //console.log('@@@@',node.tagName,prefix,uri,visibleNamespaces)
66651                 while (i--) {
66652                         var ns = visibleNamespaces[i];
66653                         // get namespace prefix
66654                         //console.log(node.nodeType,node.tagName,ns.prefix,prefix)
66655                         if (ns.prefix == prefix){
66656                                 return ns.namespace != uri;
66657                         }
66658                 }
66659                 //console.log(isHTML,uri,prefix=='')
66660                 //if(isHTML && prefix ==null && uri == 'http://www.w3.org/1999/xhtml'){
66661                 //      return false;
66662                 //}
66663                 //node.flag = '11111'
66664                 //console.error(3,true,node.flag,node.prefix,node.namespaceURI)
66665                 return true;
66666         }
66667         function serializeToString(node,buf,isHTML,nodeFilter,visibleNamespaces){
66668                 if(nodeFilter){
66669                         node = nodeFilter(node);
66670                         if(node){
66671                                 if(typeof node == 'string'){
66672                                         buf.push(node);
66673                                         return;
66674                                 }
66675                         }else {
66676                                 return;
66677                         }
66678                         //buf.sort.apply(attrs, attributeSorter);
66679                 }
66680                 switch(node.nodeType){
66681                 case ELEMENT_NODE:
66682                         if (!visibleNamespaces) { visibleNamespaces = []; }
66683                         var startVisibleNamespaces = visibleNamespaces.length;
66684                         var attrs = node.attributes;
66685                         var len = attrs.length;
66686                         var child = node.firstChild;
66687                         var nodeName = node.tagName;
66688                         
66689                         isHTML =  (htmlns === node.namespaceURI) ||isHTML; 
66690                         buf.push('<',nodeName);
66691                         
66692                         
66693                         
66694                         for(var i=0;i<len;i++){
66695                                 // add namespaces for attributes
66696                                 var attr = attrs.item(i);
66697                                 if (attr.prefix == 'xmlns') {
66698                                         visibleNamespaces.push({ prefix: attr.localName, namespace: attr.value });
66699                                 }else if(attr.nodeName == 'xmlns'){
66700                                         visibleNamespaces.push({ prefix: '', namespace: attr.value });
66701                                 }
66702                         }
66703                         for(var i=0;i<len;i++){
66704                                 var attr = attrs.item(i);
66705                                 if (needNamespaceDefine(attr,isHTML, visibleNamespaces)) {
66706                                         var prefix = attr.prefix||'';
66707                                         var uri = attr.namespaceURI;
66708                                         var ns = prefix ? ' xmlns:' + prefix : " xmlns";
66709                                         buf.push(ns, '="' , uri , '"');
66710                                         visibleNamespaces.push({ prefix: prefix, namespace:uri });
66711                                 }
66712                                 serializeToString(attr,buf,isHTML,nodeFilter,visibleNamespaces);
66713                         }
66714                         // add namespace for current node               
66715                         if (needNamespaceDefine(node,isHTML, visibleNamespaces)) {
66716                                 var prefix = node.prefix||'';
66717                                 var uri = node.namespaceURI;
66718                                 var ns = prefix ? ' xmlns:' + prefix : " xmlns";
66719                                 buf.push(ns, '="' , uri , '"');
66720                                 visibleNamespaces.push({ prefix: prefix, namespace:uri });
66721                         }
66722                         
66723                         if(child || isHTML && !/^(?:meta|link|img|br|hr|input)$/i.test(nodeName)){
66724                                 buf.push('>');
66725                                 //if is cdata child node
66726                                 if(isHTML && /^script$/i.test(nodeName)){
66727                                         while(child){
66728                                                 if(child.data){
66729                                                         buf.push(child.data);
66730                                                 }else {
66731                                                         serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
66732                                                 }
66733                                                 child = child.nextSibling;
66734                                         }
66735                                 }else
66736                                 {
66737                                         while(child){
66738                                                 serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
66739                                                 child = child.nextSibling;
66740                                         }
66741                                 }
66742                                 buf.push('</',nodeName,'>');
66743                         }else {
66744                                 buf.push('/>');
66745                         }
66746                         // remove added visible namespaces
66747                         //visibleNamespaces.length = startVisibleNamespaces;
66748                         return;
66749                 case DOCUMENT_NODE:
66750                 case DOCUMENT_FRAGMENT_NODE:
66751                         var child = node.firstChild;
66752                         while(child){
66753                                 serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
66754                                 child = child.nextSibling;
66755                         }
66756                         return;
66757                 case ATTRIBUTE_NODE:
66758                         return buf.push(' ',node.name,'="',node.value.replace(/[<&"]/g,_xmlEncoder),'"');
66759                 case TEXT_NODE:
66760                         return buf.push(node.data.replace(/[<&]/g,_xmlEncoder));
66761                 case CDATA_SECTION_NODE:
66762                         return buf.push( '<![CDATA[',node.data,']]>');
66763                 case COMMENT_NODE:
66764                         return buf.push( "<!--",node.data,"-->");
66765                 case DOCUMENT_TYPE_NODE:
66766                         var pubid = node.publicId;
66767                         var sysid = node.systemId;
66768                         buf.push('<!DOCTYPE ',node.name);
66769                         if(pubid){
66770                                 buf.push(' PUBLIC "',pubid);
66771                                 if (sysid && sysid!='.') {
66772                                         buf.push( '" "',sysid);
66773                                 }
66774                                 buf.push('">');
66775                         }else if(sysid && sysid!='.'){
66776                                 buf.push(' SYSTEM "',sysid,'">');
66777                         }else {
66778                                 var sub = node.internalSubset;
66779                                 if(sub){
66780                                         buf.push(" [",sub,"]");
66781                                 }
66782                                 buf.push(">");
66783                         }
66784                         return;
66785                 case PROCESSING_INSTRUCTION_NODE:
66786                         return buf.push( "<?",node.target," ",node.data,"?>");
66787                 case ENTITY_REFERENCE_NODE:
66788                         return buf.push( '&',node.nodeName,';');
66789                 //case ENTITY_NODE:
66790                 //case NOTATION_NODE:
66791                 default:
66792                         buf.push('??',node.nodeName);
66793                 }
66794         }
66795         function importNode(doc,node,deep){
66796                 var node2;
66797                 switch (node.nodeType) {
66798                 case ELEMENT_NODE:
66799                         node2 = node.cloneNode(false);
66800                         node2.ownerDocument = doc;
66801                         //var attrs = node2.attributes;
66802                         //var len = attrs.length;
66803                         //for(var i=0;i<len;i++){
66804                                 //node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep));
66805                         //}
66806                 case DOCUMENT_FRAGMENT_NODE:
66807                         break;
66808                 case ATTRIBUTE_NODE:
66809                         deep = true;
66810                         break;
66811                 //case ENTITY_REFERENCE_NODE:
66812                 //case PROCESSING_INSTRUCTION_NODE:
66813                 ////case TEXT_NODE:
66814                 //case CDATA_SECTION_NODE:
66815                 //case COMMENT_NODE:
66816                 //      deep = false;
66817                 //      break;
66818                 //case DOCUMENT_NODE:
66819                 //case DOCUMENT_TYPE_NODE:
66820                 //cannot be imported.
66821                 //case ENTITY_NODE:
66822                 //case NOTATION_NODE:
66823                 //can not hit in level3
66824                 //default:throw e;
66825                 }
66826                 if(!node2){
66827                         node2 = node.cloneNode(false);//false
66828                 }
66829                 node2.ownerDocument = doc;
66830                 node2.parentNode = null;
66831                 if(deep){
66832                         var child = node.firstChild;
66833                         while(child){
66834                                 node2.appendChild(importNode(doc,child,deep));
66835                                 child = child.nextSibling;
66836                         }
66837                 }
66838                 return node2;
66839         }
66840         //
66841         //var _relationMap = {firstChild:1,lastChild:1,previousSibling:1,nextSibling:1,
66842         //                                      attributes:1,childNodes:1,parentNode:1,documentElement:1,doctype,};
66843         function cloneNode(doc,node,deep){
66844                 var node2 = new node.constructor();
66845                 for(var n in node){
66846                         var v = node[n];
66847                         if(typeof v != 'object' ){
66848                                 if(v != node2[n]){
66849                                         node2[n] = v;
66850                                 }
66851                         }
66852                 }
66853                 if(node.childNodes){
66854                         node2.childNodes = new NodeList();
66855                 }
66856                 node2.ownerDocument = doc;
66857                 switch (node2.nodeType) {
66858                 case ELEMENT_NODE:
66859                         var attrs       = node.attributes;
66860                         var attrs2      = node2.attributes = new NamedNodeMap();
66861                         var len = attrs.length;
66862                         attrs2._ownerElement = node2;
66863                         for(var i=0;i<len;i++){
66864                                 node2.setAttributeNode(cloneNode(doc,attrs.item(i),true));
66865                         }
66866                         break;  case ATTRIBUTE_NODE:
66867                         deep = true;
66868                 }
66869                 if(deep){
66870                         var child = node.firstChild;
66871                         while(child){
66872                                 node2.appendChild(cloneNode(doc,child,deep));
66873                                 child = child.nextSibling;
66874                         }
66875                 }
66876                 return node2;
66877         }
66878
66879         function __set__(object,key,value){
66880                 object[key] = value;
66881         }
66882         //do dynamic
66883         try{
66884                 if(Object.defineProperty){
66885                         Object.defineProperty(LiveNodeList.prototype,'length',{
66886                                 get:function(){
66887                                         _updateLiveList(this);
66888                                         return this.$$length;
66889                                 }
66890                         });
66891                         Object.defineProperty(Node.prototype,'textContent',{
66892                                 get:function(){
66893                                         return getTextContent(this);
66894                                 },
66895                                 set:function(data){
66896                                         switch(this.nodeType){
66897                                         case ELEMENT_NODE:
66898                                         case DOCUMENT_FRAGMENT_NODE:
66899                                                 while(this.firstChild){
66900                                                         this.removeChild(this.firstChild);
66901                                                 }
66902                                                 if(data || String(data)){
66903                                                         this.appendChild(this.ownerDocument.createTextNode(data));
66904                                                 }
66905                                                 break;
66906                                         default:
66907                                                 //TODO:
66908                                                 this.data = data;
66909                                                 this.value = data;
66910                                                 this.nodeValue = data;
66911                                         }
66912                                 }
66913                         });
66914                         
66915                         function getTextContent(node){
66916                                 switch(node.nodeType){
66917                                 case ELEMENT_NODE:
66918                                 case DOCUMENT_FRAGMENT_NODE:
66919                                         var buf = [];
66920                                         node = node.firstChild;
66921                                         while(node){
66922                                                 if(node.nodeType!==7 && node.nodeType !==8){
66923                                                         buf.push(getTextContent(node));
66924                                                 }
66925                                                 node = node.nextSibling;
66926                                         }
66927                                         return buf.join('');
66928                                 default:
66929                                         return node.nodeValue;
66930                                 }
66931                         }
66932                         __set__ = function(object,key,value){
66933                                 //console.log(value)
66934                                 object['$$'+key] = value;
66935                         };
66936                 }
66937         }catch(e){//ie8
66938         }
66939
66940         //if(typeof require == 'function'){
66941                 var DOMImplementation_1 = DOMImplementation;
66942                 var XMLSerializer_1 = XMLSerializer$1;
66943         //}
66944
66945         var dom = {
66946                 DOMImplementation: DOMImplementation_1,
66947                 XMLSerializer: XMLSerializer_1
66948         };
66949
66950         var domParser = createCommonjsModule(function (module, exports) {
66951         function DOMParser(options){
66952                 this.options = options ||{locator:{}};
66953                 
66954         }
66955         DOMParser.prototype.parseFromString = function(source,mimeType){
66956                 var options = this.options;
66957                 var sax =  new XMLReader();
66958                 var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler
66959                 var errorHandler = options.errorHandler;
66960                 var locator = options.locator;
66961                 var defaultNSMap = options.xmlns||{};
66962                 var entityMap = {'lt':'<','gt':'>','amp':'&','quot':'"','apos':"'"};
66963                 if(locator){
66964                         domBuilder.setDocumentLocator(locator);
66965                 }
66966                 
66967                 sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator);
66968                 sax.domBuilder = options.domBuilder || domBuilder;
66969                 if(/\/x?html?$/.test(mimeType)){
66970                         entityMap.nbsp = '\xa0';
66971                         entityMap.copy = '\xa9';
66972                         defaultNSMap['']= 'http://www.w3.org/1999/xhtml';
66973                 }
66974                 defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace';
66975                 if(source){
66976                         sax.parse(source,defaultNSMap,entityMap);
66977                 }else {
66978                         sax.errorHandler.error("invalid doc source");
66979                 }
66980                 return domBuilder.doc;
66981         };
66982         function buildErrorHandler(errorImpl,domBuilder,locator){
66983                 if(!errorImpl){
66984                         if(domBuilder instanceof DOMHandler){
66985                                 return domBuilder;
66986                         }
66987                         errorImpl = domBuilder ;
66988                 }
66989                 var errorHandler = {};
66990                 var isCallback = errorImpl instanceof Function;
66991                 locator = locator||{};
66992                 function build(key){
66993                         var fn = errorImpl[key];
66994                         if(!fn && isCallback){
66995                                 fn = errorImpl.length == 2?function(msg){errorImpl(key,msg);}:errorImpl;
66996                         }
66997                         errorHandler[key] = fn && function(msg){
66998                                 fn('[xmldom '+key+']\t'+msg+_locator(locator));
66999                         }||function(){};
67000                 }
67001                 build('warning');
67002                 build('error');
67003                 build('fatalError');
67004                 return errorHandler;
67005         }
67006
67007         //console.log('#\n\n\n\n\n\n\n####')
67008         /**
67009          * +ContentHandler+ErrorHandler
67010          * +LexicalHandler+EntityResolver2
67011          * -DeclHandler-DTDHandler 
67012          * 
67013          * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
67014          * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
67015          * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
67016          */
67017         function DOMHandler() {
67018             this.cdata = false;
67019         }
67020         function position(locator,node){
67021                 node.lineNumber = locator.lineNumber;
67022                 node.columnNumber = locator.columnNumber;
67023         }
67024         /**
67025          * @see org.xml.sax.ContentHandler#startDocument
67026          * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
67027          */ 
67028         DOMHandler.prototype = {
67029                 startDocument : function() {
67030                 this.doc = new DOMImplementation().createDocument(null, null, null);
67031                 if (this.locator) {
67032                         this.doc.documentURI = this.locator.systemId;
67033                 }
67034                 },
67035                 startElement:function(namespaceURI, localName, qName, attrs) {
67036                         var doc = this.doc;
67037                     var el = doc.createElementNS(namespaceURI, qName||localName);
67038                     var len = attrs.length;
67039                     appendElement(this, el);
67040                     this.currentElement = el;
67041                     
67042                         this.locator && position(this.locator,el);
67043                     for (var i = 0 ; i < len; i++) {
67044                         var namespaceURI = attrs.getURI(i);
67045                         var value = attrs.getValue(i);
67046                         var qName = attrs.getQName(i);
67047                                 var attr = doc.createAttributeNS(namespaceURI, qName);
67048                                 this.locator &&position(attrs.getLocator(i),attr);
67049                                 attr.value = attr.nodeValue = value;
67050                                 el.setAttributeNode(attr);
67051                     }
67052                 },
67053                 endElement:function(namespaceURI, localName, qName) {
67054                         var current = this.currentElement;
67055                         var tagName = current.tagName;
67056                         this.currentElement = current.parentNode;
67057                 },
67058                 startPrefixMapping:function(prefix, uri) {
67059                 },
67060                 endPrefixMapping:function(prefix) {
67061                 },
67062                 processingInstruction:function(target, data) {
67063                     var ins = this.doc.createProcessingInstruction(target, data);
67064                     this.locator && position(this.locator,ins);
67065                     appendElement(this, ins);
67066                 },
67067                 ignorableWhitespace:function(ch, start, length) {
67068                 },
67069                 characters:function(chars, start, length) {
67070                         chars = _toString.apply(this,arguments);
67071                         //console.log(chars)
67072                         if(chars){
67073                                 if (this.cdata) {
67074                                         var charNode = this.doc.createCDATASection(chars);
67075                                 } else {
67076                                         var charNode = this.doc.createTextNode(chars);
67077                                 }
67078                                 if(this.currentElement){
67079                                         this.currentElement.appendChild(charNode);
67080                                 }else if(/^\s*$/.test(chars)){
67081                                         this.doc.appendChild(charNode);
67082                                         //process xml
67083                                 }
67084                                 this.locator && position(this.locator,charNode);
67085                         }
67086                 },
67087                 skippedEntity:function(name) {
67088                 },
67089                 endDocument:function() {
67090                         this.doc.normalize();
67091                 },
67092                 setDocumentLocator:function (locator) {
67093                     if(this.locator = locator){// && !('lineNumber' in locator)){
67094                         locator.lineNumber = 0;
67095                     }
67096                 },
67097                 //LexicalHandler
67098                 comment:function(chars, start, length) {
67099                         chars = _toString.apply(this,arguments);
67100                     var comm = this.doc.createComment(chars);
67101                     this.locator && position(this.locator,comm);
67102                     appendElement(this, comm);
67103                 },
67104                 
67105                 startCDATA:function() {
67106                     //used in characters() methods
67107                     this.cdata = true;
67108                 },
67109                 endCDATA:function() {
67110                     this.cdata = false;
67111                 },
67112                 
67113                 startDTD:function(name, publicId, systemId) {
67114                         var impl = this.doc.implementation;
67115                     if (impl && impl.createDocumentType) {
67116                         var dt = impl.createDocumentType(name, publicId, systemId);
67117                         this.locator && position(this.locator,dt);
67118                         appendElement(this, dt);
67119                     }
67120                 },
67121                 /**
67122                  * @see org.xml.sax.ErrorHandler
67123                  * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
67124                  */
67125                 warning:function(error) {
67126                         console.warn('[xmldom warning]\t'+error,_locator(this.locator));
67127                 },
67128                 error:function(error) {
67129                         console.error('[xmldom error]\t'+error,_locator(this.locator));
67130                 },
67131                 fatalError:function(error) {
67132                         console.error('[xmldom fatalError]\t'+error,_locator(this.locator));
67133                     throw error;
67134                 }
67135         };
67136         function _locator(l){
67137                 if(l){
67138                         return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']'
67139                 }
67140         }
67141         function _toString(chars,start,length){
67142                 if(typeof chars == 'string'){
67143                         return chars.substr(start,length)
67144                 }else {//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
67145                         if(chars.length >= start+length || start){
67146                                 return new java.lang.String(chars,start,length)+'';
67147                         }
67148                         return chars;
67149                 }
67150         }
67151
67152         /*
67153          * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
67154          * used method of org.xml.sax.ext.LexicalHandler:
67155          *  #comment(chars, start, length)
67156          *  #startCDATA()
67157          *  #endCDATA()
67158          *  #startDTD(name, publicId, systemId)
67159          *
67160          *
67161          * IGNORED method of org.xml.sax.ext.LexicalHandler:
67162          *  #endDTD()
67163          *  #startEntity(name)
67164          *  #endEntity(name)
67165          *
67166          *
67167          * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
67168          * IGNORED method of org.xml.sax.ext.DeclHandler
67169          *      #attributeDecl(eName, aName, type, mode, value)
67170          *  #elementDecl(name, model)
67171          *  #externalEntityDecl(name, publicId, systemId)
67172          *  #internalEntityDecl(name, value)
67173          * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
67174          * IGNORED method of org.xml.sax.EntityResolver2
67175          *  #resolveEntity(String name,String publicId,String baseURI,String systemId)
67176          *  #resolveEntity(publicId, systemId)
67177          *  #getExternalSubset(name, baseURI)
67178          * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
67179          * IGNORED method of org.xml.sax.DTDHandler
67180          *  #notationDecl(name, publicId, systemId) {};
67181          *  #unparsedEntityDecl(name, publicId, systemId, notationName) {};
67182          */
67183         "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){
67184                 DOMHandler.prototype[key] = function(){return null};
67185         });
67186
67187         /* 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 */
67188         function appendElement (hander,node) {
67189             if (!hander.currentElement) {
67190                 hander.doc.appendChild(node);
67191             } else {
67192                 hander.currentElement.appendChild(node);
67193             }
67194         }//appendChild and setAttributeNS are preformance key
67195
67196         //if(typeof require == 'function'){
67197                 var XMLReader = sax.XMLReader;
67198                 var DOMImplementation = exports.DOMImplementation = dom.DOMImplementation;
67199                 exports.XMLSerializer = dom.XMLSerializer ;
67200                 exports.DOMParser = DOMParser;
67201         //}
67202         });
67203
67204         var togeojson = createCommonjsModule(function (module, exports) {
67205         var toGeoJSON = (function() {
67206
67207             var removeSpace = /\s*/g,
67208                 trimSpace = /^\s*|\s*$/g,
67209                 splitSpace = /\s+/;
67210             // generate a short, numeric hash of a string
67211             function okhash(x) {
67212                 if (!x || !x.length) { return 0; }
67213                 for (var i = 0, h = 0; i < x.length; i++) {
67214                     h = ((h << 5) - h) + x.charCodeAt(i) | 0;
67215                 } return h;
67216             }
67217             // all Y children of X
67218             function get(x, y) { return x.getElementsByTagName(y); }
67219             function attr(x, y) { return x.getAttribute(y); }
67220             function attrf(x, y) { return parseFloat(attr(x, y)); }
67221             // one Y child of X, if any, otherwise null
67222             function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; }
67223             // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
67224             function norm(el) { if (el.normalize) { el.normalize(); } return el; }
67225             // cast array x into numbers
67226             function numarray(x) {
67227                 for (var j = 0, o = []; j < x.length; j++) { o[j] = parseFloat(x[j]); }
67228                 return o;
67229             }
67230             // get the content of a text node, if any
67231             function nodeVal(x) {
67232                 if (x) { norm(x); }
67233                 return (x && x.textContent) || '';
67234             }
67235             // get the contents of multiple text nodes, if present
67236             function getMulti(x, ys) {
67237                 var o = {}, n, k;
67238                 for (k = 0; k < ys.length; k++) {
67239                     n = get1(x, ys[k]);
67240                     if (n) { o[ys[k]] = nodeVal(n); }
67241                 }
67242                 return o;
67243             }
67244             // add properties of Y to X, overwriting if present in both
67245             function extend(x, y) { for (var k in y) { x[k] = y[k]; } }
67246             // get one coordinate from a coordinate array, if any
67247             function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); }
67248             // get all coordinates from a coordinate array as [[],[]]
67249             function coord(v) {
67250                 var coords = v.replace(trimSpace, '').split(splitSpace),
67251                     o = [];
67252                 for (var i = 0; i < coords.length; i++) {
67253                     o.push(coord1(coords[i]));
67254                 }
67255                 return o;
67256             }
67257             function coordPair(x) {
67258                 var ll = [attrf(x, 'lon'), attrf(x, 'lat')],
67259                     ele = get1(x, 'ele'),
67260                     // handle namespaced attribute in browser
67261                     heartRate = get1(x, 'gpxtpx:hr') || get1(x, 'hr'),
67262                     time = get1(x, 'time'),
67263                     e;
67264                 if (ele) {
67265                     e = parseFloat(nodeVal(ele));
67266                     if (!isNaN(e)) {
67267                         ll.push(e);
67268                     }
67269                 }
67270                 return {
67271                     coordinates: ll,
67272                     time: time ? nodeVal(time) : null,
67273                     heartRate: heartRate ? parseFloat(nodeVal(heartRate)) : null
67274                 };
67275             }
67276
67277             // create a new feature collection parent object
67278             function fc() {
67279                 return {
67280                     type: 'FeatureCollection',
67281                     features: []
67282                 };
67283             }
67284
67285             var serializer;
67286             if (typeof XMLSerializer !== 'undefined') {
67287                 /* istanbul ignore next */
67288                 serializer = new XMLSerializer();
67289             // only require xmldom in a node environment
67290             } else if ( typeof process === 'object' && !process.browser) {
67291                 serializer = new (domParser.XMLSerializer)();
67292             }
67293             function xml2str(str) {
67294                 // IE9 will create a new XMLSerializer but it'll crash immediately.
67295                 // This line is ignored because we don't run coverage tests in IE9
67296                 /* istanbul ignore next */
67297                 if (str.xml !== undefined) { return str.xml; }
67298                 return serializer.serializeToString(str);
67299             }
67300
67301             var t = {
67302                 kml: function(doc) {
67303
67304                     var gj = fc(),
67305                         // styleindex keeps track of hashed styles in order to match features
67306                         styleIndex = {}, styleByHash = {},
67307                         // stylemapindex keeps track of style maps to expose in properties
67308                         styleMapIndex = {},
67309                         // atomic geospatial types supported by KML - MultiGeometry is
67310                         // handled separately
67311                         geotypes = ['Polygon', 'LineString', 'Point', 'Track', 'gx:Track'],
67312                         // all root placemarks in the file
67313                         placemarks = get(doc, 'Placemark'),
67314                         styles = get(doc, 'Style'),
67315                         styleMaps = get(doc, 'StyleMap');
67316
67317                     for (var k = 0; k < styles.length; k++) {
67318                         var hash = okhash(xml2str(styles[k])).toString(16);
67319                         styleIndex['#' + attr(styles[k], 'id')] = hash;
67320                         styleByHash[hash] = styles[k];
67321                     }
67322                     for (var l = 0; l < styleMaps.length; l++) {
67323                         styleIndex['#' + attr(styleMaps[l], 'id')] = okhash(xml2str(styleMaps[l])).toString(16);
67324                         var pairs = get(styleMaps[l], 'Pair');
67325                         var pairsMap = {};
67326                         for (var m = 0; m < pairs.length; m++) {
67327                             pairsMap[nodeVal(get1(pairs[m], 'key'))] = nodeVal(get1(pairs[m], 'styleUrl'));
67328                         }
67329                         styleMapIndex['#' + attr(styleMaps[l], 'id')] = pairsMap;
67330
67331                     }
67332                     for (var j = 0; j < placemarks.length; j++) {
67333                         gj.features = gj.features.concat(getPlacemark(placemarks[j]));
67334                     }
67335                     function kmlColor(v) {
67336                         var color, opacity;
67337                         v = v || '';
67338                         if (v.substr(0, 1) === '#') { v = v.substr(1); }
67339                         if (v.length === 6 || v.length === 3) { color = v; }
67340                         if (v.length === 8) {
67341                             opacity = parseInt(v.substr(0, 2), 16) / 255;
67342                             color = '#' + v.substr(6, 2) +
67343                                 v.substr(4, 2) +
67344                                 v.substr(2, 2);
67345                         }
67346                         return [color, isNaN(opacity) ? undefined : opacity];
67347                     }
67348                     function gxCoord(v) { return numarray(v.split(' ')); }
67349                     function gxCoords(root) {
67350                         var elems = get(root, 'coord'), coords = [], times = [];
67351                         if (elems.length === 0) { elems = get(root, 'gx:coord'); }
67352                         for (var i = 0; i < elems.length; i++) { coords.push(gxCoord(nodeVal(elems[i]))); }
67353                         var timeElems = get(root, 'when');
67354                         for (var j = 0; j < timeElems.length; j++) { times.push(nodeVal(timeElems[j])); }
67355                         return {
67356                             coords: coords,
67357                             times: times
67358                         };
67359                     }
67360                     function getGeometry(root) {
67361                         var geomNode, geomNodes, i, j, k, geoms = [], coordTimes = [];
67362                         if (get1(root, 'MultiGeometry')) { return getGeometry(get1(root, 'MultiGeometry')); }
67363                         if (get1(root, 'MultiTrack')) { return getGeometry(get1(root, 'MultiTrack')); }
67364                         if (get1(root, 'gx:MultiTrack')) { return getGeometry(get1(root, 'gx:MultiTrack')); }
67365                         for (i = 0; i < geotypes.length; i++) {
67366                             geomNodes = get(root, geotypes[i]);
67367                             if (geomNodes) {
67368                                 for (j = 0; j < geomNodes.length; j++) {
67369                                     geomNode = geomNodes[j];
67370                                     if (geotypes[i] === 'Point') {
67371                                         geoms.push({
67372                                             type: 'Point',
67373                                             coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
67374                                         });
67375                                     } else if (geotypes[i] === 'LineString') {
67376                                         geoms.push({
67377                                             type: 'LineString',
67378                                             coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
67379                                         });
67380                                     } else if (geotypes[i] === 'Polygon') {
67381                                         var rings = get(geomNode, 'LinearRing'),
67382                                             coords = [];
67383                                         for (k = 0; k < rings.length; k++) {
67384                                             coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
67385                                         }
67386                                         geoms.push({
67387                                             type: 'Polygon',
67388                                             coordinates: coords
67389                                         });
67390                                     } else if (geotypes[i] === 'Track' ||
67391                                         geotypes[i] === 'gx:Track') {
67392                                         var track = gxCoords(geomNode);
67393                                         geoms.push({
67394                                             type: 'LineString',
67395                                             coordinates: track.coords
67396                                         });
67397                                         if (track.times.length) { coordTimes.push(track.times); }
67398                                     }
67399                                 }
67400                             }
67401                         }
67402                         return {
67403                             geoms: geoms,
67404                             coordTimes: coordTimes
67405                         };
67406                     }
67407                     function getPlacemark(root) {
67408                         var geomsAndTimes = getGeometry(root), i, properties = {},
67409                             name = nodeVal(get1(root, 'name')),
67410                             address = nodeVal(get1(root, 'address')),
67411                             styleUrl = nodeVal(get1(root, 'styleUrl')),
67412                             description = nodeVal(get1(root, 'description')),
67413                             timeSpan = get1(root, 'TimeSpan'),
67414                             timeStamp = get1(root, 'TimeStamp'),
67415                             extendedData = get1(root, 'ExtendedData'),
67416                             lineStyle = get1(root, 'LineStyle'),
67417                             polyStyle = get1(root, 'PolyStyle'),
67418                             visibility = get1(root, 'visibility');
67419
67420                         if (!geomsAndTimes.geoms.length) { return []; }
67421                         if (name) { properties.name = name; }
67422                         if (address) { properties.address = address; }
67423                         if (styleUrl) {
67424                             if (styleUrl[0] !== '#') {
67425                                 styleUrl = '#' + styleUrl;
67426                             }
67427
67428                             properties.styleUrl = styleUrl;
67429                             if (styleIndex[styleUrl]) {
67430                                 properties.styleHash = styleIndex[styleUrl];
67431                             }
67432                             if (styleMapIndex[styleUrl]) {
67433                                 properties.styleMapHash = styleMapIndex[styleUrl];
67434                                 properties.styleHash = styleIndex[styleMapIndex[styleUrl].normal];
67435                             }
67436                             // Try to populate the lineStyle or polyStyle since we got the style hash
67437                             var style = styleByHash[properties.styleHash];
67438                             if (style) {
67439                                 if (!lineStyle) { lineStyle = get1(style, 'LineStyle'); }
67440                                 if (!polyStyle) { polyStyle = get1(style, 'PolyStyle'); }
67441                             }
67442                         }
67443                         if (description) { properties.description = description; }
67444                         if (timeSpan) {
67445                             var begin = nodeVal(get1(timeSpan, 'begin'));
67446                             var end = nodeVal(get1(timeSpan, 'end'));
67447                             properties.timespan = { begin: begin, end: end };
67448                         }
67449                         if (timeStamp) {
67450                             properties.timestamp = nodeVal(get1(timeStamp, 'when'));
67451                         }
67452                         if (lineStyle) {
67453                             var linestyles = kmlColor(nodeVal(get1(lineStyle, 'color'))),
67454                                 color = linestyles[0],
67455                                 opacity = linestyles[1],
67456                                 width = parseFloat(nodeVal(get1(lineStyle, 'width')));
67457                             if (color) { properties.stroke = color; }
67458                             if (!isNaN(opacity)) { properties['stroke-opacity'] = opacity; }
67459                             if (!isNaN(width)) { properties['stroke-width'] = width; }
67460                         }
67461                         if (polyStyle) {
67462                             var polystyles = kmlColor(nodeVal(get1(polyStyle, 'color'))),
67463                                 pcolor = polystyles[0],
67464                                 popacity = polystyles[1],
67465                                 fill = nodeVal(get1(polyStyle, 'fill')),
67466                                 outline = nodeVal(get1(polyStyle, 'outline'));
67467                             if (pcolor) { properties.fill = pcolor; }
67468                             if (!isNaN(popacity)) { properties['fill-opacity'] = popacity; }
67469                             if (fill) { properties['fill-opacity'] = fill === '1' ? properties['fill-opacity'] || 1 : 0; }
67470                             if (outline) { properties['stroke-opacity'] = outline === '1' ? properties['stroke-opacity'] || 1 : 0; }
67471                         }
67472                         if (extendedData) {
67473                             var datas = get(extendedData, 'Data'),
67474                                 simpleDatas = get(extendedData, 'SimpleData');
67475
67476                             for (i = 0; i < datas.length; i++) {
67477                                 properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
67478                             }
67479                             for (i = 0; i < simpleDatas.length; i++) {
67480                                 properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
67481                             }
67482                         }
67483                         if (visibility) {
67484                             properties.visibility = nodeVal(visibility);
67485                         }
67486                         if (geomsAndTimes.coordTimes.length) {
67487                             properties.coordTimes = (geomsAndTimes.coordTimes.length === 1) ?
67488                                 geomsAndTimes.coordTimes[0] : geomsAndTimes.coordTimes;
67489                         }
67490                         var feature = {
67491                             type: 'Feature',
67492                             geometry: (geomsAndTimes.geoms.length === 1) ? geomsAndTimes.geoms[0] : {
67493                                 type: 'GeometryCollection',
67494                                 geometries: geomsAndTimes.geoms
67495                             },
67496                             properties: properties
67497                         };
67498                         if (attr(root, 'id')) { feature.id = attr(root, 'id'); }
67499                         return [feature];
67500                     }
67501                     return gj;
67502                 },
67503                 gpx: function(doc) {
67504                     var i,
67505                         tracks = get(doc, 'trk'),
67506                         routes = get(doc, 'rte'),
67507                         waypoints = get(doc, 'wpt'),
67508                         // a feature collection
67509                         gj = fc(),
67510                         feature;
67511                     for (i = 0; i < tracks.length; i++) {
67512                         feature = getTrack(tracks[i]);
67513                         if (feature) { gj.features.push(feature); }
67514                     }
67515                     for (i = 0; i < routes.length; i++) {
67516                         feature = getRoute(routes[i]);
67517                         if (feature) { gj.features.push(feature); }
67518                     }
67519                     for (i = 0; i < waypoints.length; i++) {
67520                         gj.features.push(getPoint(waypoints[i]));
67521                     }
67522                     function getPoints(node, pointname) {
67523                         var pts = get(node, pointname),
67524                             line = [],
67525                             times = [],
67526                             heartRates = [],
67527                             l = pts.length;
67528                         if (l < 2) { return {}; }  // Invalid line in GeoJSON
67529                         for (var i = 0; i < l; i++) {
67530                             var c = coordPair(pts[i]);
67531                             line.push(c.coordinates);
67532                             if (c.time) { times.push(c.time); }
67533                             if (c.heartRate) { heartRates.push(c.heartRate); }
67534                         }
67535                         return {
67536                             line: line,
67537                             times: times,
67538                             heartRates: heartRates
67539                         };
67540                     }
67541                     function getTrack(node) {
67542                         var segments = get(node, 'trkseg'),
67543                             track = [],
67544                             times = [],
67545                             heartRates = [],
67546                             line;
67547                         for (var i = 0; i < segments.length; i++) {
67548                             line = getPoints(segments[i], 'trkpt');
67549                             if (line) {
67550                                 if (line.line) { track.push(line.line); }
67551                                 if (line.times && line.times.length) { times.push(line.times); }
67552                                 if (line.heartRates && line.heartRates.length) { heartRates.push(line.heartRates); }
67553                             }
67554                         }
67555                         if (track.length === 0) { return; }
67556                         var properties = getProperties(node);
67557                         extend(properties, getLineStyle(get1(node, 'extensions')));
67558                         if (times.length) { properties.coordTimes = track.length === 1 ? times[0] : times; }
67559                         if (heartRates.length) { properties.heartRates = track.length === 1 ? heartRates[0] : heartRates; }
67560                         return {
67561                             type: 'Feature',
67562                             properties: properties,
67563                             geometry: {
67564                                 type: track.length === 1 ? 'LineString' : 'MultiLineString',
67565                                 coordinates: track.length === 1 ? track[0] : track
67566                             }
67567                         };
67568                     }
67569                     function getRoute(node) {
67570                         var line = getPoints(node, 'rtept');
67571                         if (!line.line) { return; }
67572                         var prop = getProperties(node);
67573                         extend(prop, getLineStyle(get1(node, 'extensions')));
67574                         var routeObj = {
67575                             type: 'Feature',
67576                             properties: prop,
67577                             geometry: {
67578                                 type: 'LineString',
67579                                 coordinates: line.line
67580                             }
67581                         };
67582                         return routeObj;
67583                     }
67584                     function getPoint(node) {
67585                         var prop = getProperties(node);
67586                         extend(prop, getMulti(node, ['sym']));
67587                         return {
67588                             type: 'Feature',
67589                             properties: prop,
67590                             geometry: {
67591                                 type: 'Point',
67592                                 coordinates: coordPair(node).coordinates
67593                             }
67594                         };
67595                     }
67596                     function getLineStyle(extensions) {
67597                         var style = {};
67598                         if (extensions) {
67599                             var lineStyle = get1(extensions, 'line');
67600                             if (lineStyle) {
67601                                 var color = nodeVal(get1(lineStyle, 'color')),
67602                                     opacity = parseFloat(nodeVal(get1(lineStyle, 'opacity'))),
67603                                     width = parseFloat(nodeVal(get1(lineStyle, 'width')));
67604                                 if (color) { style.stroke = color; }
67605                                 if (!isNaN(opacity)) { style['stroke-opacity'] = opacity; }
67606                                 // GPX width is in mm, convert to px with 96 px per inch
67607                                 if (!isNaN(width)) { style['stroke-width'] = width * 96 / 25.4; }
67608                             }
67609                         }
67610                         return style;
67611                     }
67612                     function getProperties(node) {
67613                         var prop = getMulti(node, ['name', 'cmt', 'desc', 'type', 'time', 'keywords']),
67614                             links = get(node, 'link');
67615                         if (links.length) { prop.links = []; }
67616                         for (var i = 0, link; i < links.length; i++) {
67617                             link = { href: attr(links[i], 'href') };
67618                             extend(link, getMulti(links[i], ['text', 'type']));
67619                             prop.links.push(link);
67620                         }
67621                         return prop;
67622                     }
67623                     return gj;
67624                 }
67625             };
67626             return t;
67627         })();
67628
67629         { module.exports = toGeoJSON; }
67630         });
67631
67632         var _initialized = false;
67633         var _enabled = false;
67634         var _geojson;
67635
67636
67637         function svgData(projection, context, dispatch) {
67638             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
67639             var _showLabels = true;
67640             var detected = utilDetect();
67641             var layer = select(null);
67642             var _vtService;
67643             var _fileList;
67644             var _template;
67645             var _src;
67646
67647
67648             function init() {
67649                 if (_initialized) { return; }  // run once
67650
67651                 _geojson = {};
67652                 _enabled = true;
67653
67654                 function over() {
67655                     event.stopPropagation();
67656                     event.preventDefault();
67657                     event.dataTransfer.dropEffect = 'copy';
67658                 }
67659
67660                 context.container()
67661                     .attr('dropzone', 'copy')
67662                     .on('drop.svgData', function() {
67663                         event.stopPropagation();
67664                         event.preventDefault();
67665                         if (!detected.filedrop) { return; }
67666                         drawData.fileList(event.dataTransfer.files);
67667                     })
67668                     .on('dragenter.svgData', over)
67669                     .on('dragexit.svgData', over)
67670                     .on('dragover.svgData', over);
67671
67672                 _initialized = true;
67673             }
67674
67675
67676             function getService() {
67677                 if (services.vectorTile && !_vtService) {
67678                     _vtService = services.vectorTile;
67679                     _vtService.event.on('loadedData', throttledRedraw);
67680                 } else if (!services.vectorTile && _vtService) {
67681                     _vtService = null;
67682                 }
67683
67684                 return _vtService;
67685             }
67686
67687
67688             function showLayer() {
67689                 layerOn();
67690
67691                 layer
67692                     .style('opacity', 0)
67693                     .transition()
67694                     .duration(250)
67695                     .style('opacity', 1)
67696                     .on('end', function () { dispatch.call('change'); });
67697             }
67698
67699
67700             function hideLayer() {
67701                 throttledRedraw.cancel();
67702
67703                 layer
67704                     .transition()
67705                     .duration(250)
67706                     .style('opacity', 0)
67707                     .on('end', layerOff);
67708             }
67709
67710
67711             function layerOn() {
67712                 layer.style('display', 'block');
67713             }
67714
67715
67716             function layerOff() {
67717                 layer.selectAll('.viewfield-group').remove();
67718                 layer.style('display', 'none');
67719             }
67720
67721
67722             // ensure that all geojson features in a collection have IDs
67723             function ensureIDs(gj) {
67724                 if (!gj) { return null; }
67725
67726                 if (gj.type === 'FeatureCollection') {
67727                     for (var i = 0; i < gj.features.length; i++) {
67728                         ensureFeatureID(gj.features[i]);
67729                     }
67730                 } else {
67731                     ensureFeatureID(gj);
67732                 }
67733                 return gj;
67734             }
67735
67736
67737             // ensure that each single Feature object has a unique ID
67738             function ensureFeatureID(feature) {
67739                 if (!feature) { return; }
67740                 feature.__featurehash__ = utilHashcode(fastJsonStableStringify(feature));
67741                 return feature;
67742             }
67743
67744
67745             // Prefer an array of Features instead of a FeatureCollection
67746             function getFeatures(gj) {
67747                 if (!gj) { return []; }
67748
67749                 if (gj.type === 'FeatureCollection') {
67750                     return gj.features;
67751                 } else {
67752                     return [gj];
67753                 }
67754             }
67755
67756
67757             function featureKey(d) {
67758                 return d.__featurehash__;
67759             }
67760
67761
67762             function isPolygon(d) {
67763                 return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon';
67764             }
67765
67766
67767             function clipPathID(d) {
67768                 return 'ideditor-data-' + d.__featurehash__ + '-clippath';
67769             }
67770
67771
67772             function featureClasses(d) {
67773                 return [
67774                     'data' + d.__featurehash__,
67775                     d.geometry.type,
67776                     isPolygon(d) ? 'area' : '',
67777                     d.__layerID__ || ''
67778                 ].filter(Boolean).join(' ');
67779             }
67780
67781
67782             function drawData(selection) {
67783                 var vtService = getService();
67784                 var getPath = svgPath(projection).geojson;
67785                 var getAreaPath = svgPath(projection, null, true).geojson;
67786                 var hasData = drawData.hasData();
67787
67788                 layer = selection.selectAll('.layer-mapdata')
67789                     .data(_enabled && hasData ? [0] : []);
67790
67791                 layer.exit()
67792                     .remove();
67793
67794                 layer = layer.enter()
67795                     .append('g')
67796                     .attr('class', 'layer-mapdata')
67797                     .merge(layer);
67798
67799                 var surface = context.surface();
67800                 if (!surface || surface.empty()) { return; }  // not ready to draw yet, starting up
67801
67802
67803                 // Gather data
67804                 var geoData, polygonData;
67805                 if (_template && vtService) {   // fetch data from vector tile service
67806                     var sourceID = _template;
67807                     vtService.loadTiles(sourceID, _template, projection);
67808                     geoData = vtService.data(sourceID, projection);
67809                 } else {
67810                     geoData = getFeatures(_geojson);
67811                 }
67812                 geoData = geoData.filter(getPath);
67813                 polygonData = geoData.filter(isPolygon);
67814
67815
67816                 // Draw clip paths for polygons
67817                 var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data')
67818                    .data(polygonData, featureKey);
67819
67820                 clipPaths.exit()
67821                    .remove();
67822
67823                 var clipPathsEnter = clipPaths.enter()
67824                    .append('clipPath')
67825                    .attr('class', 'clipPath-data')
67826                    .attr('id', clipPathID);
67827
67828                 clipPathsEnter
67829                    .append('path');
67830
67831                 clipPaths.merge(clipPathsEnter)
67832                    .selectAll('path')
67833                    .attr('d', getAreaPath);
67834
67835
67836                 // Draw fill, shadow, stroke layers
67837                 var datagroups = layer
67838                     .selectAll('g.datagroup')
67839                     .data(['fill', 'shadow', 'stroke']);
67840
67841                 datagroups = datagroups.enter()
67842                     .append('g')
67843                     .attr('class', function(d) { return 'datagroup datagroup-' + d; })
67844                     .merge(datagroups);
67845
67846
67847                 // Draw paths
67848                 var pathData = {
67849                     fill: polygonData,
67850                     shadow: geoData,
67851                     stroke: geoData
67852                 };
67853
67854                 var paths = datagroups
67855                     .selectAll('path')
67856                     .data(function(layer) { return pathData[layer]; }, featureKey);
67857
67858                 // exit
67859                 paths.exit()
67860                     .remove();
67861
67862                 // enter/update
67863                 paths = paths.enter()
67864                     .append('path')
67865                     .attr('class', function(d) {
67866                         var datagroup = this.parentNode.__data__;
67867                         return 'pathdata ' + datagroup + ' ' + featureClasses(d);
67868                     })
67869                     .attr('clip-path', function(d) {
67870                         var datagroup = this.parentNode.__data__;
67871                         return datagroup === 'fill' ? ('url(#' + clipPathID(d) + ')') : null;
67872                     })
67873                     .merge(paths)
67874                     .attr('d', function(d) {
67875                         var datagroup = this.parentNode.__data__;
67876                         return datagroup === 'fill' ? getAreaPath(d) : getPath(d);
67877                     });
67878
67879
67880                 // Draw labels
67881                 layer
67882                     .call(drawLabels, 'label-halo', geoData)
67883                     .call(drawLabels, 'label', geoData);
67884
67885
67886                 function drawLabels(selection, textClass, data) {
67887                     var labelPath = d3_geoPath(projection);
67888                     var labelData = data.filter(function(d) {
67889                         return _showLabels && d.properties && (d.properties.desc || d.properties.name);
67890                     });
67891
67892                     var labels = selection.selectAll('text.' + textClass)
67893                         .data(labelData, featureKey);
67894
67895                     // exit
67896                     labels.exit()
67897                         .remove();
67898
67899                     // enter/update
67900                     labels = labels.enter()
67901                         .append('text')
67902                         .attr('class', function(d) { return textClass + ' ' + featureClasses(d); })
67903                         .merge(labels)
67904                         .text(function(d) {
67905                             return d.properties.desc || d.properties.name;
67906                         })
67907                         .attr('x', function(d) {
67908                             var centroid = labelPath.centroid(d);
67909                             return centroid[0] + 11;
67910                         })
67911                         .attr('y', function(d) {
67912                             var centroid = labelPath.centroid(d);
67913                             return centroid[1];
67914                         });
67915                 }
67916             }
67917
67918
67919             function getExtension(fileName) {
67920                 if (!fileName) { return; }
67921
67922                 var re = /\.(gpx|kml|(geo)?json)$/i;
67923                 var match = fileName.toLowerCase().match(re);
67924                 return match && match.length && match[0];
67925             }
67926
67927
67928             function xmlToDom(textdata) {
67929                 return (new DOMParser()).parseFromString(textdata, 'text/xml');
67930             }
67931
67932
67933             drawData.setFile = function(extension, data) {
67934                 _template = null;
67935                 _fileList = null;
67936                 _geojson = null;
67937                 _src = null;
67938
67939                 var gj;
67940                 switch (extension) {
67941                     case '.gpx':
67942                         gj = togeojson.gpx(xmlToDom(data));
67943                         break;
67944                     case '.kml':
67945                         gj = togeojson.kml(xmlToDom(data));
67946                         break;
67947                     case '.geojson':
67948                     case '.json':
67949                         gj = JSON.parse(data);
67950                         break;
67951                 }
67952
67953                 gj = gj || {};
67954                 if (Object.keys(gj).length) {
67955                     _geojson = ensureIDs(gj);
67956                     _src = extension + ' data file';
67957                     this.fitZoom();
67958                 }
67959
67960                 dispatch.call('change');
67961                 return this;
67962             };
67963
67964
67965             drawData.showLabels = function(val) {
67966                 if (!arguments.length) { return _showLabels; }
67967
67968                 _showLabels = val;
67969                 return this;
67970             };
67971
67972
67973             drawData.enabled = function(val) {
67974                 if (!arguments.length) { return _enabled; }
67975
67976                 _enabled = val;
67977                 if (_enabled) {
67978                     showLayer();
67979                 } else {
67980                     hideLayer();
67981                 }
67982
67983                 dispatch.call('change');
67984                 return this;
67985             };
67986
67987
67988             drawData.hasData = function() {
67989                 var gj = _geojson || {};
67990                 return !!(_template || Object.keys(gj).length);
67991             };
67992
67993
67994             drawData.template = function(val, src) {
67995                 if (!arguments.length) { return _template; }
67996
67997                 // test source against OSM imagery blacklists..
67998                 var osm = context.connection();
67999                 if (osm) {
68000                     var blacklists = osm.imageryBlacklists();
68001                     var fail = false;
68002                     var tested = 0;
68003                     var regex;
68004
68005                     for (var i = 0; i < blacklists.length; i++) {
68006                         try {
68007                             regex = new RegExp(blacklists[i]);
68008                             fail = regex.test(val);
68009                             tested++;
68010                             if (fail) { break; }
68011                         } catch (e) {
68012                             /* noop */
68013                         }
68014                     }
68015
68016                     // ensure at least one test was run.
68017                     if (!tested) {
68018                         regex = new RegExp('.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*');
68019                         fail = regex.test(val);
68020                     }
68021                 }
68022
68023                 _template = val;
68024                 _fileList = null;
68025                 _geojson = null;
68026
68027                 // strip off the querystring/hash from the template,
68028                 // it often includes the access token
68029                 _src = src || ('vectortile:' + val.split(/[?#]/)[0]);
68030
68031                 dispatch.call('change');
68032                 return this;
68033             };
68034
68035
68036             drawData.geojson = function(gj, src) {
68037                 if (!arguments.length) { return _geojson; }
68038
68039                 _template = null;
68040                 _fileList = null;
68041                 _geojson = null;
68042                 _src = null;
68043
68044                 gj = gj || {};
68045                 if (Object.keys(gj).length) {
68046                     _geojson = ensureIDs(gj);
68047                     _src = src || 'unknown.geojson';
68048                 }
68049
68050                 dispatch.call('change');
68051                 return this;
68052             };
68053
68054
68055             drawData.fileList = function(fileList) {
68056                 if (!arguments.length) { return _fileList; }
68057
68058                 _template = null;
68059                 _fileList = fileList;
68060                 _geojson = null;
68061                 _src = null;
68062
68063                 if (!fileList || !fileList.length) { return this; }
68064                 var f = fileList[0];
68065                 var extension = getExtension(f.name);
68066                 var reader = new FileReader();
68067                 reader.onload = (function() {
68068                     return function(e) {
68069                         drawData.setFile(extension, e.target.result);
68070                     };
68071                 })();
68072
68073                 reader.readAsText(f);
68074
68075                 return this;
68076             };
68077
68078
68079             drawData.url = function(url, defaultExtension) {
68080                 _template = null;
68081                 _fileList = null;
68082                 _geojson = null;
68083                 _src = null;
68084
68085                 // strip off any querystring/hash from the url before checking extension
68086                 var testUrl = url.split(/[?#]/)[0];
68087                 var extension = getExtension(testUrl) || defaultExtension;
68088                 if (extension) {
68089                     _template = null;
68090                     d3_text(url)
68091                         .then(function(data) {
68092                             drawData.setFile(extension, data);
68093                         })
68094                         .catch(function() {
68095                             /* ignore */
68096                         });
68097
68098                 } else {
68099                     drawData.template(url);
68100                 }
68101
68102                 return this;
68103             };
68104
68105
68106             drawData.getSrc = function() {
68107                 return _src || '';
68108             };
68109
68110
68111             drawData.fitZoom = function() {
68112                 var features = getFeatures(_geojson);
68113                 if (!features.length) { return; }
68114
68115                 var map = context.map();
68116                 var viewport = map.trimmedExtent().polygon();
68117                 var coords = features.reduce(function(coords, feature) {
68118                     var geom = feature.geometry;
68119                     if (!geom) { return coords; }
68120
68121                     var c = geom.coordinates;
68122
68123                     /* eslint-disable no-fallthrough */
68124                     switch (geom.type) {
68125                         case 'Point':
68126                             c = [c];
68127                         case 'MultiPoint':
68128                         case 'LineString':
68129                             break;
68130
68131                         case 'MultiPolygon':
68132                             c = utilArrayFlatten(c);
68133                         case 'Polygon':
68134                         case 'MultiLineString':
68135                             c = utilArrayFlatten(c);
68136                             break;
68137                     }
68138                     /* eslint-enable no-fallthrough */
68139
68140                     return utilArrayUnion(coords, c);
68141                 }, []);
68142
68143                 if (!geoPolygonIntersectsPolygon(viewport, coords, true)) {
68144                     var extent = geoExtent(d3_geoBounds({ type: 'LineString', coordinates: coords }));
68145                     map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
68146                 }
68147
68148                 return this;
68149             };
68150
68151
68152             init();
68153             return drawData;
68154         }
68155
68156         function svgDebug(projection, context) {
68157
68158           function drawDebug(selection) {
68159             var showTile = context.getDebug('tile');
68160             var showCollision = context.getDebug('collision');
68161             var showImagery = context.getDebug('imagery');
68162             var showTouchTargets = context.getDebug('target');
68163             var showDownloaded = context.getDebug('downloaded');
68164
68165             var debugData = [];
68166             if (showTile) {
68167               debugData.push({ class: 'red', label: 'tile' });
68168             }
68169             if (showCollision) {
68170               debugData.push({ class: 'yellow', label: 'collision' });
68171             }
68172             if (showImagery) {
68173               debugData.push({ class: 'orange', label: 'imagery' });
68174             }
68175             if (showTouchTargets) {
68176               debugData.push({ class: 'pink', label: 'touchTargets' });
68177             }
68178             if (showDownloaded) {
68179               debugData.push({ class: 'purple', label: 'downloaded' });
68180             }
68181
68182
68183             var legend = context.container().select('.main-content')
68184               .selectAll('.debug-legend')
68185               .data(debugData.length ? [0] : []);
68186
68187             legend.exit()
68188               .remove();
68189
68190             legend = legend.enter()
68191               .append('div')
68192               .attr('class', 'fillD debug-legend')
68193               .merge(legend);
68194
68195
68196             var legendItems = legend.selectAll('.debug-legend-item')
68197               .data(debugData, function (d) { return d.label; });
68198
68199             legendItems.exit()
68200               .remove();
68201
68202             legendItems.enter()
68203               .append('span')
68204               .attr('class', function (d) { return ("debug-legend-item " + (d.class)); })
68205               .text(function (d) { return d.label; });
68206
68207
68208             var layer = selection.selectAll('.layer-debug')
68209               .data(showImagery || showDownloaded ? [0] : []);
68210
68211             layer.exit()
68212               .remove();
68213
68214             layer = layer.enter()
68215               .append('g')
68216               .attr('class', 'layer-debug')
68217               .merge(layer);
68218
68219
68220             // imagery
68221             var extent = context.map().extent();
68222             _mainFileFetcher.get('imagery')
68223               .then(function (d) {
68224                 var hits = (showImagery && d.query.bbox(extent.rectangle(), true)) || [];
68225                 var features = hits.map(function (d) { return d.features[d.id]; });
68226
68227                 var imagery = layer.selectAll('path.debug-imagery')
68228                   .data(features);
68229
68230                 imagery.exit()
68231                   .remove();
68232
68233                 imagery.enter()
68234                   .append('path')
68235                   .attr('class', 'debug-imagery debug orange');
68236               })
68237               .catch(function () { /* ignore */ });
68238
68239             // downloaded
68240             var osm = context.connection();
68241             var dataDownloaded = [];
68242             if (osm && showDownloaded) {
68243               var rtree = osm.caches('get').tile.rtree;
68244               dataDownloaded = rtree.all().map(function (bbox) {
68245                 return {
68246                   type: 'Feature',
68247                   properties: { id: bbox.id },
68248                   geometry: {
68249                     type: 'Polygon',
68250                     coordinates: [[
68251                       [ bbox.minX, bbox.minY ],
68252                       [ bbox.minX, bbox.maxY ],
68253                       [ bbox.maxX, bbox.maxY ],
68254                       [ bbox.maxX, bbox.minY ],
68255                       [ bbox.minX, bbox.minY ]
68256                     ]]
68257                   }
68258                 };
68259               });
68260             }
68261
68262             var downloaded = layer
68263               .selectAll('path.debug-downloaded')
68264               .data(showDownloaded ? dataDownloaded : []);
68265
68266             downloaded.exit()
68267               .remove();
68268
68269             downloaded.enter()
68270               .append('path')
68271               .attr('class', 'debug-downloaded debug purple');
68272
68273             // update
68274             layer.selectAll('path')
68275               .attr('d', svgPath(projection).geojson);
68276           }
68277
68278
68279           // This looks strange because `enabled` methods on other layers are
68280           // chainable getter/setters, and this one is just a getter.
68281           drawDebug.enabled = function() {
68282             if (!arguments.length) {
68283               return context.getDebug('tile') ||
68284                 context.getDebug('collision') ||
68285                 context.getDebug('imagery') ||
68286                 context.getDebug('target') ||
68287                 context.getDebug('downloaded');
68288             } else {
68289                 return this;
68290             }
68291           };
68292
68293
68294           return drawDebug;
68295         }
68296
68297         var _layerEnabled = false;
68298         var _qaService;
68299
68300         function svgKeepRight(projection, context, dispatch) {
68301           var throttledRedraw = throttle(function () { return dispatch.call('change'); }, 1000);
68302           var minZoom = 12;
68303
68304           var touchLayer = select(null);
68305           var drawLayer = select(null);
68306           var layerVisible = false;
68307
68308           function markerPath(selection, klass) {
68309             selection
68310               .attr('class', klass)
68311               .attr('transform', 'translate(-4, -24)')
68312               .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');
68313           }
68314
68315           // Loosely-coupled keepRight service for fetching issues.
68316           function getService() {
68317             if (services.keepRight && !_qaService) {
68318               _qaService = services.keepRight;
68319               _qaService.on('loaded', throttledRedraw);
68320             } else if (!services.keepRight && _qaService) {
68321               _qaService = null;
68322             }
68323
68324             return _qaService;
68325           }
68326
68327           // Show the markers
68328           function editOn() {
68329             if (!layerVisible) {
68330               layerVisible = true;
68331               drawLayer
68332                 .style('display', 'block');
68333             }
68334           }
68335
68336           // Immediately remove the markers and their touch targets
68337           function editOff() {
68338             if (layerVisible) {
68339               layerVisible = false;
68340               drawLayer
68341                 .style('display', 'none');
68342               drawLayer.selectAll('.qaItem.keepRight')
68343                 .remove();
68344               touchLayer.selectAll('.qaItem.keepRight')
68345                 .remove();
68346             }
68347           }
68348
68349           // Enable the layer.  This shows the markers and transitions them to visible.
68350           function layerOn() {
68351             editOn();
68352
68353             drawLayer
68354               .style('opacity', 0)
68355               .transition()
68356               .duration(250)
68357               .style('opacity', 1)
68358               .on('end interrupt', function () { return dispatch.call('change'); });
68359           }
68360
68361           // Disable the layer.  This transitions the layer invisible and then hides the markers.
68362           function layerOff() {
68363             throttledRedraw.cancel();
68364             drawLayer.interrupt();
68365             touchLayer.selectAll('.qaItem.keepRight')
68366               .remove();
68367
68368             drawLayer
68369               .transition()
68370               .duration(250)
68371               .style('opacity', 0)
68372               .on('end interrupt', function () {
68373                 editOff();
68374                 dispatch.call('change');
68375               });
68376           }
68377
68378           // Update the issue markers
68379           function updateMarkers() {
68380             if (!layerVisible || !_layerEnabled) { return; }
68381
68382             var service = getService();
68383             var selectedID = context.selectedErrorID();
68384             var data = (service ? service.getItems(projection) : []);
68385             var getTransform = svgPointTransform(projection);
68386
68387             // Draw markers..
68388             var markers = drawLayer.selectAll('.qaItem.keepRight')
68389               .data(data, function (d) { return d.id; });
68390
68391             // exit
68392             markers.exit()
68393               .remove();
68394
68395             // enter
68396             var markersEnter = markers.enter()
68397               .append('g')
68398                 .attr('class', function (d) { return ("qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.parentIssueType)); });
68399
68400             markersEnter
68401               .append('ellipse')
68402                 .attr('cx', 0.5)
68403                 .attr('cy', 1)
68404                 .attr('rx', 6.5)
68405                 .attr('ry', 3)
68406                 .attr('class', 'stroke');
68407
68408             markersEnter
68409               .append('path')
68410                 .call(markerPath, 'shadow');
68411
68412             markersEnter
68413               .append('use')
68414                 .attr('class', 'qaItem-fill')
68415                 .attr('width', '20px')
68416                 .attr('height', '20px')
68417                 .attr('x', '-8px')
68418                 .attr('y', '-22px')
68419                 .attr('xlink:href', '#iD-icon-bolt');
68420
68421             // update
68422             markers
68423               .merge(markersEnter)
68424               .sort(sortY)
68425                 .classed('selected', function (d) { return d.id === selectedID; })
68426                 .attr('transform', getTransform);
68427
68428
68429             // Draw targets..
68430             if (touchLayer.empty()) { return; }
68431             var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
68432
68433             var targets = touchLayer.selectAll('.qaItem.keepRight')
68434               .data(data, function (d) { return d.id; });
68435
68436             // exit
68437             targets.exit()
68438               .remove();
68439
68440             // enter/update
68441             targets.enter()
68442               .append('rect')
68443                 .attr('width', '20px')
68444                 .attr('height', '20px')
68445                 .attr('x', '-8px')
68446                 .attr('y', '-22px')
68447               .merge(targets)
68448               .sort(sortY)
68449                 .attr('class', function (d) { return ("qaItem " + (d.service) + " target " + fillClass + " itemId-" + (d.id)); })
68450                 .attr('transform', getTransform);
68451
68452
68453             function sortY(a, b) {
68454               return (a.id === selectedID) ? 1
68455                 : (b.id === selectedID) ? -1
68456                 : (a.severity === 'error' && b.severity !== 'error') ? 1
68457                 : (b.severity === 'error' && a.severity !== 'error') ? -1
68458                 : b.loc[1] - a.loc[1];
68459             }
68460           }
68461
68462           // Draw the keepRight layer and schedule loading issues and updating markers.
68463           function drawKeepRight(selection) {
68464             var service = getService();
68465
68466             var surface = context.surface();
68467             if (surface && !surface.empty()) {
68468               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
68469             }
68470
68471             drawLayer = selection.selectAll('.layer-keepRight')
68472               .data(service ? [0] : []);
68473
68474             drawLayer.exit()
68475               .remove();
68476
68477             drawLayer = drawLayer.enter()
68478               .append('g')
68479                 .attr('class', 'layer-keepRight')
68480                 .style('display', _layerEnabled ? 'block' : 'none')
68481               .merge(drawLayer);
68482
68483             if (_layerEnabled) {
68484               if (service && ~~context.map().zoom() >= minZoom) {
68485                 editOn();
68486                 service.loadIssues(projection);
68487                 updateMarkers();
68488               } else {
68489                 editOff();
68490               }
68491             }
68492           }
68493
68494           // Toggles the layer on and off
68495           drawKeepRight.enabled = function(val) {
68496             if (!arguments.length) { return _layerEnabled; }
68497
68498             _layerEnabled = val;
68499             if (_layerEnabled) {
68500               layerOn();
68501             } else {
68502               layerOff();
68503               if (context.selectedErrorID()) {
68504                 context.enter(modeBrowse(context));
68505               }
68506             }
68507
68508             dispatch.call('change');
68509             return this;
68510           };
68511
68512           drawKeepRight.supported = function () { return !!getService(); };
68513
68514           return drawKeepRight;
68515         }
68516
68517         function svgGeolocate(projection) {
68518             var layer = select(null);
68519             var _position;
68520
68521
68522             function init() {
68523                 if (svgGeolocate.initialized) { return; }  // run once
68524                 svgGeolocate.enabled = false;
68525                 svgGeolocate.initialized = true;
68526             }
68527
68528             function showLayer() {
68529                 layer.style('display', 'block');
68530             }
68531
68532
68533             function hideLayer() {
68534                 layer
68535                     .transition()
68536                     .duration(250)
68537                     .style('opacity', 0);
68538             }
68539
68540             function layerOn() {
68541                 layer
68542                     .style('opacity', 0)
68543                     .transition()
68544                     .duration(250)
68545                     .style('opacity', 1);
68546
68547             }
68548
68549             function layerOff() {
68550                 layer.style('display', 'none');
68551             }
68552
68553             function transform(d) {
68554                 return svgPointTransform(projection)(d);
68555             }
68556
68557             function accuracy(accuracy, loc) { // converts accuracy to pixels...
68558                 var degreesRadius = geoMetersToLat(accuracy),
68559                     tangentLoc = [loc[0], loc[1] + degreesRadius],
68560                     projectedTangent = projection(tangentLoc),
68561                     projectedLoc = projection([loc[0], loc[1]]);
68562
68563                 // southern most point will have higher pixel value...
68564                return Math.round(projectedLoc[1] - projectedTangent[1]).toString();
68565             }
68566
68567             function update() {
68568                 var geolocation = { loc: [_position.coords.longitude, _position.coords.latitude] };
68569
68570                 var groups = layer.selectAll('.geolocations').selectAll('.geolocation')
68571                     .data([geolocation]);
68572
68573                 groups.exit()
68574                     .remove();
68575
68576                 var pointsEnter = groups.enter()
68577                     .append('g')
68578                     .attr('class', 'geolocation');
68579
68580                 pointsEnter
68581                     .append('circle')
68582                     .attr('class', 'geolocate-radius')
68583                     .attr('dx', '0')
68584                     .attr('dy', '0')
68585                     .attr('fill', 'rgb(15,128,225)')
68586                     .attr('fill-opacity', '0.3')
68587                     .attr('r', '0');
68588
68589                 pointsEnter
68590                     .append('circle')
68591                     .attr('dx', '0')
68592                     .attr('dy', '0')
68593                     .attr('fill', 'rgb(15,128,225)')
68594                     .attr('stroke', 'white')
68595                     .attr('stroke-width', '1.5')
68596                     .attr('r', '6');
68597
68598                 groups.merge(pointsEnter)
68599                     .attr('transform', transform);
68600
68601                 layer.select('.geolocate-radius').attr('r', accuracy(_position.coords.accuracy, geolocation.loc));
68602             }
68603
68604             function drawLocation(selection) {
68605                 var enabled = svgGeolocate.enabled;
68606
68607                 layer = selection.selectAll('.layer-geolocate')
68608                     .data([0]);
68609
68610                 layer.exit()
68611                     .remove();
68612
68613                 var layerEnter = layer.enter()
68614                     .append('g')
68615                     .attr('class', 'layer-geolocate')
68616                     .style('display', enabled ? 'block' : 'none');
68617
68618                 layerEnter
68619                     .append('g')
68620                     .attr('class', 'geolocations');
68621
68622                 layer = layerEnter
68623                     .merge(layer);
68624
68625                 if (enabled) {
68626                     update();
68627                 } else {
68628                     layerOff();
68629                 }
68630             }
68631
68632             drawLocation.enabled = function (position, enabled) {
68633                 if (!arguments.length) { return svgGeolocate.enabled; }
68634                 _position = position;
68635                 svgGeolocate.enabled = enabled;
68636                 if (svgGeolocate.enabled) {
68637                     showLayer();
68638                     layerOn();
68639                 } else {
68640                     hideLayer();
68641                 }
68642                 return this;
68643             };
68644
68645             init();
68646             return drawLocation;
68647         }
68648
68649         function svgLabels(projection, context) {
68650             var path = d3_geoPath(projection);
68651             var detected = utilDetect();
68652             var baselineHack = (detected.ie ||
68653                 detected.browser.toLowerCase() === 'edge' ||
68654                 (detected.browser.toLowerCase() === 'firefox' && detected.version >= 70));
68655             var _rdrawn = new RBush();
68656             var _rskipped = new RBush();
68657             var _textWidthCache = {};
68658             var _entitybboxes = {};
68659
68660             // Listed from highest to lowest priority
68661             var labelStack = [
68662                 ['line', 'aeroway', '*', 12],
68663                 ['line', 'highway', 'motorway', 12],
68664                 ['line', 'highway', 'trunk', 12],
68665                 ['line', 'highway', 'primary', 12],
68666                 ['line', 'highway', 'secondary', 12],
68667                 ['line', 'highway', 'tertiary', 12],
68668                 ['line', 'highway', '*', 12],
68669                 ['line', 'railway', '*', 12],
68670                 ['line', 'waterway', '*', 12],
68671                 ['area', 'aeroway', '*', 12],
68672                 ['area', 'amenity', '*', 12],
68673                 ['area', 'building', '*', 12],
68674                 ['area', 'historic', '*', 12],
68675                 ['area', 'leisure', '*', 12],
68676                 ['area', 'man_made', '*', 12],
68677                 ['area', 'natural', '*', 12],
68678                 ['area', 'shop', '*', 12],
68679                 ['area', 'tourism', '*', 12],
68680                 ['area', 'camp_site', '*', 12],
68681                 ['point', 'aeroway', '*', 10],
68682                 ['point', 'amenity', '*', 10],
68683                 ['point', 'building', '*', 10],
68684                 ['point', 'historic', '*', 10],
68685                 ['point', 'leisure', '*', 10],
68686                 ['point', 'man_made', '*', 10],
68687                 ['point', 'natural', '*', 10],
68688                 ['point', 'shop', '*', 10],
68689                 ['point', 'tourism', '*', 10],
68690                 ['point', 'camp_site', '*', 10],
68691                 ['line', 'name', '*', 12],
68692                 ['area', 'name', '*', 12],
68693                 ['point', 'name', '*', 10]
68694             ];
68695
68696
68697             function shouldSkipIcon(preset) {
68698                 var noIcons = ['building', 'landuse', 'natural'];
68699                 return noIcons.some(function(s) {
68700                     return preset.id.indexOf(s) >= 0;
68701                 });
68702             }
68703
68704
68705             function get(array, prop) {
68706                 return function(d, i) { return array[i][prop]; };
68707             }
68708
68709
68710             function textWidth(text, size, elem) {
68711                 var c = _textWidthCache[size];
68712                 if (!c) { c = _textWidthCache[size] = {}; }
68713
68714                 if (c[text]) {
68715                     return c[text];
68716
68717                 } else if (elem) {
68718                     c[text] = elem.getComputedTextLength();
68719                     return c[text];
68720
68721                 } else {
68722                     var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
68723                     if (str === null) {
68724                         return size / 3 * 2 * text.length;
68725                     } else {
68726                         return size / 3 * (2 * text.length + str.length);
68727                     }
68728                 }
68729             }
68730
68731
68732             function drawLinePaths(selection, entities, filter, classes, labels) {
68733                 var paths = selection.selectAll('path')
68734                     .filter(filter)
68735                     .data(entities, osmEntity.key);
68736
68737                 // exit
68738                 paths.exit()
68739                     .remove();
68740
68741                 // enter/update
68742                 paths.enter()
68743                     .append('path')
68744                     .style('stroke-width', get(labels, 'font-size'))
68745                     .attr('id', function(d) { return 'ideditor-labelpath-' + d.id; })
68746                     .attr('class', classes)
68747                     .merge(paths)
68748                     .attr('d', get(labels, 'lineString'));
68749             }
68750
68751
68752             function drawLineLabels(selection, entities, filter, classes, labels) {
68753                 var texts = selection.selectAll('text.' + classes)
68754                     .filter(filter)
68755                     .data(entities, osmEntity.key);
68756
68757                 // exit
68758                 texts.exit()
68759                     .remove();
68760
68761                 // enter
68762                 texts.enter()
68763                     .append('text')
68764                     .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; })
68765                     .attr('dy', baselineHack ? '0.35em' : null)
68766                     .append('textPath')
68767                     .attr('class', 'textpath');
68768
68769                 // update
68770                 selection.selectAll('text.' + classes).selectAll('.textpath')
68771                     .filter(filter)
68772                     .data(entities, osmEntity.key)
68773                     .attr('startOffset', '50%')
68774                     .attr('xlink:href', function(d) { return '#ideditor-labelpath-' + d.id; })
68775                     .text(utilDisplayNameForPath);
68776             }
68777
68778
68779             function drawPointLabels(selection, entities, filter, classes, labels) {
68780                 var texts = selection.selectAll('text.' + classes)
68781                     .filter(filter)
68782                     .data(entities, osmEntity.key);
68783
68784                 // exit
68785                 texts.exit()
68786                     .remove();
68787
68788                 // enter/update
68789                 texts.enter()
68790                     .append('text')
68791                     .attr('class', function(d, i) {
68792                         return classes + ' ' + labels[i].classes + ' ' + d.id;
68793                     })
68794                     .merge(texts)
68795                     .attr('x', get(labels, 'x'))
68796                     .attr('y', get(labels, 'y'))
68797                     .style('text-anchor', get(labels, 'textAnchor'))
68798                     .text(utilDisplayName)
68799                     .each(function(d, i) {
68800                         textWidth(utilDisplayName(d), labels[i].height, this);
68801                     });
68802             }
68803
68804
68805             function drawAreaLabels(selection, entities, filter, classes, labels) {
68806                 entities = entities.filter(hasText);
68807                 labels = labels.filter(hasText);
68808                 drawPointLabels(selection, entities, filter, classes, labels);
68809
68810                 function hasText(d, i) {
68811                     return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
68812                 }
68813             }
68814
68815
68816             function drawAreaIcons(selection, entities, filter, classes, labels) {
68817                 var icons = selection.selectAll('use.' + classes)
68818                     .filter(filter)
68819                     .data(entities, osmEntity.key);
68820
68821                 // exit
68822                 icons.exit()
68823                     .remove();
68824
68825                 // enter/update
68826                 icons.enter()
68827                     .append('use')
68828                     .attr('class', 'icon ' + classes)
68829                     .attr('width', '17px')
68830                     .attr('height', '17px')
68831                     .merge(icons)
68832                     .attr('transform', get(labels, 'transform'))
68833                     .attr('xlink:href', function(d) {
68834                         var preset = _mainPresetIndex.match(d, context.graph());
68835                         var picon = preset && preset.icon;
68836
68837                         if (!picon) {
68838                             return '';
68839                         } else {
68840                             var isMaki = /^maki-/.test(picon);
68841                             return '#' + picon + (isMaki ? '-15' : '');
68842                         }
68843                     });
68844             }
68845
68846
68847             function drawCollisionBoxes(selection, rtree, which) {
68848                 var classes = 'debug ' + which + ' ' + (which === 'debug-skipped' ? 'orange' : 'yellow');
68849
68850                 var gj = [];
68851                 if (context.getDebug('collision')) {
68852                     gj = rtree.all().map(function(d) {
68853                         return { type: 'Polygon', coordinates: [[
68854                             [d.minX, d.minY],
68855                             [d.maxX, d.minY],
68856                             [d.maxX, d.maxY],
68857                             [d.minX, d.maxY],
68858                             [d.minX, d.minY]
68859                         ]]};
68860                     });
68861                 }
68862
68863                 var boxes = selection.selectAll('.' + which)
68864                     .data(gj);
68865
68866                 // exit
68867                 boxes.exit()
68868                     .remove();
68869
68870                 // enter/update
68871                 boxes.enter()
68872                     .append('path')
68873                     .attr('class', classes)
68874                     .merge(boxes)
68875                     .attr('d', d3_geoPath());
68876             }
68877
68878
68879             function drawLabels(selection, graph, entities, filter, dimensions, fullRedraw) {
68880                 var wireframe = context.surface().classed('fill-wireframe');
68881                 var zoom = geoScaleToZoom(projection.scale());
68882
68883                 var labelable = [];
68884                 var renderNodeAs = {};
68885                 var i, j, k, entity, geometry;
68886
68887                 for (i = 0; i < labelStack.length; i++) {
68888                     labelable.push([]);
68889                 }
68890
68891                 if (fullRedraw) {
68892                     _rdrawn.clear();
68893                     _rskipped.clear();
68894                     _entitybboxes = {};
68895
68896                 } else {
68897                     for (i = 0; i < entities.length; i++) {
68898                         entity = entities[i];
68899                         var toRemove = []
68900                             .concat(_entitybboxes[entity.id] || [])
68901                             .concat(_entitybboxes[entity.id + 'I'] || []);
68902
68903                         for (j = 0; j < toRemove.length; j++) {
68904                             _rdrawn.remove(toRemove[j]);
68905                             _rskipped.remove(toRemove[j]);
68906                         }
68907                     }
68908                 }
68909
68910                 // Loop through all the entities to do some preprocessing
68911                 for (i = 0; i < entities.length; i++) {
68912                     entity = entities[i];
68913                     geometry = entity.geometry(graph);
68914
68915                     // Insert collision boxes around interesting points/vertices
68916                     if (geometry === 'point' || (geometry === 'vertex' && isInterestingVertex(entity))) {
68917                         var hasDirections = entity.directions(graph, projection).length;
68918                         var markerPadding;
68919
68920                         if (!wireframe && geometry === 'point' && !(zoom >= 18 && hasDirections)) {
68921                             renderNodeAs[entity.id] = 'point';
68922                             markerPadding = 20;   // extra y for marker height
68923                         } else {
68924                             renderNodeAs[entity.id] = 'vertex';
68925                             markerPadding = 0;
68926                         }
68927
68928                         var coord = projection(entity.loc);
68929                         var nodePadding = 10;
68930                         var bbox = {
68931                             minX: coord[0] - nodePadding,
68932                             minY: coord[1] - nodePadding - markerPadding,
68933                             maxX: coord[0] + nodePadding,
68934                             maxY: coord[1] + nodePadding
68935                         };
68936
68937                         doInsert(bbox, entity.id + 'P');
68938                     }
68939
68940                     // From here on, treat vertices like points
68941                     if (geometry === 'vertex') {
68942                         geometry = 'point';
68943                     }
68944
68945                     // Determine which entities are label-able
68946                     var preset = geometry === 'area' && _mainPresetIndex.match(entity, graph);
68947                     var icon = preset && !shouldSkipIcon(preset) && preset.icon;
68948
68949                     if (!icon && !utilDisplayName(entity))
68950                         { continue; }
68951
68952                     for (k = 0; k < labelStack.length; k++) {
68953                         var matchGeom = labelStack[k][0];
68954                         var matchKey = labelStack[k][1];
68955                         var matchVal = labelStack[k][2];
68956                         var hasVal = entity.tags[matchKey];
68957
68958                         if (geometry === matchGeom && hasVal && (matchVal === '*' || matchVal === hasVal)) {
68959                             labelable[k].push(entity);
68960                             break;
68961                         }
68962                     }
68963                 }
68964
68965                 var positions = {
68966                     point: [],
68967                     line: [],
68968                     area: []
68969                 };
68970
68971                 var labelled = {
68972                     point: [],
68973                     line: [],
68974                     area: []
68975                 };
68976
68977                 // Try and find a valid label for labellable entities
68978                 for (k = 0; k < labelable.length; k++) {
68979                     var fontSize = labelStack[k][3];
68980
68981                     for (i = 0; i < labelable[k].length; i++) {
68982                         entity = labelable[k][i];
68983                         geometry = entity.geometry(graph);
68984
68985                         var getName = (geometry === 'line') ? utilDisplayNameForPath : utilDisplayName;
68986                         var name = getName(entity);
68987                         var width = name && textWidth(name, fontSize);
68988                         var p = null;
68989
68990                         if (geometry === 'point' || geometry === 'vertex') {
68991                             // no point or vertex labels in wireframe mode
68992                             // no vertex labels at low zooms (vertices have no icons)
68993                             if (wireframe) { continue; }
68994                             var renderAs = renderNodeAs[entity.id];
68995                             if (renderAs === 'vertex' && zoom < 17) { continue; }
68996
68997                             p = getPointLabel(entity, width, fontSize, renderAs);
68998
68999                         } else if (geometry === 'line') {
69000                             p = getLineLabel(entity, width, fontSize);
69001
69002                         } else if (geometry === 'area') {
69003                             p = getAreaLabel(entity, width, fontSize);
69004                         }
69005
69006                         if (p) {
69007                             if (geometry === 'vertex') { geometry = 'point'; }  // treat vertex like point
69008                             p.classes = geometry + ' tag-' + labelStack[k][1];
69009                             positions[geometry].push(p);
69010                             labelled[geometry].push(entity);
69011                         }
69012                     }
69013                 }
69014
69015
69016                 function isInterestingVertex(entity) {
69017                     var selectedIDs = context.selectedIDs();
69018
69019                     return entity.hasInterestingTags() ||
69020                         entity.isEndpoint(graph) ||
69021                         entity.isConnected(graph) ||
69022                         selectedIDs.indexOf(entity.id) !== -1 ||
69023                         graph.parentWays(entity).some(function(parent) {
69024                             return selectedIDs.indexOf(parent.id) !== -1;
69025                         });
69026                 }
69027
69028
69029                 function getPointLabel(entity, width, height, geometry) {
69030                     var y = (geometry === 'point' ? -12 : 0);
69031                     var pointOffsets = {
69032                         ltr: [15, y, 'start'],
69033                         rtl: [-15, y, 'end']
69034                     };
69035
69036                     var textDirection = _mainLocalizer.textDirection();
69037
69038                     var coord = projection(entity.loc);
69039                     var textPadding = 2;
69040                     var offset = pointOffsets[textDirection];
69041                     var p = {
69042                         height: height,
69043                         width: width,
69044                         x: coord[0] + offset[0],
69045                         y: coord[1] + offset[1],
69046                         textAnchor: offset[2]
69047                     };
69048
69049                     // insert a collision box for the text label..
69050                     var bbox;
69051                     if (textDirection === 'rtl') {
69052                         bbox = {
69053                             minX: p.x - width - textPadding,
69054                             minY: p.y - (height / 2) - textPadding,
69055                             maxX: p.x + textPadding,
69056                             maxY: p.y + (height / 2) + textPadding
69057                         };
69058                     } else {
69059                         bbox = {
69060                             minX: p.x - textPadding,
69061                             minY: p.y - (height / 2) - textPadding,
69062                             maxX: p.x + width + textPadding,
69063                             maxY: p.y + (height / 2) + textPadding
69064                         };
69065                     }
69066
69067                     if (tryInsert([bbox], entity.id, true)) {
69068                         return p;
69069                     }
69070                 }
69071
69072
69073                 function getLineLabel(entity, width, height) {
69074                     var viewport = geoExtent(context.projection.clipExtent()).polygon();
69075                     var points = graph.childNodes(entity)
69076                         .map(function(node) { return projection(node.loc); });
69077                     var length = geoPathLength(points);
69078
69079                     if (length < width + 20) { return; }
69080
69081                     // % along the line to attempt to place the label
69082                     var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70,
69083                                        25, 75, 20, 80, 15, 95, 10, 90, 5, 95];
69084                     var padding = 3;
69085
69086                     for (var i = 0; i < lineOffsets.length; i++) {
69087                         var offset = lineOffsets[i];
69088                         var middle = offset / 100 * length;
69089                         var start = middle - width / 2;
69090
69091                         if (start < 0 || start + width > length) { continue; }
69092
69093                         // generate subpath and ignore paths that are invalid or don't cross viewport.
69094                         var sub = subpath(points, start, start + width);
69095                         if (!sub || !geoPolygonIntersectsPolygon(viewport, sub, true)) {
69096                             continue;
69097                         }
69098
69099                         var isReverse = reverse(sub);
69100                         if (isReverse) {
69101                             sub = sub.reverse();
69102                         }
69103
69104                         var bboxes = [];
69105                         var boxsize = (height + 2) / 2;
69106
69107                         for (var j = 0; j < sub.length - 1; j++) {
69108                             var a = sub[j];
69109                             var b = sub[j + 1];
69110
69111                             // split up the text into small collision boxes
69112                             var num = Math.max(1, Math.floor(geoVecLength(a, b) / boxsize / 2));
69113
69114                             for (var box = 0; box < num; box++) {
69115                                 var p = geoVecInterp(a, b, box / num);
69116                                 var x0 = p[0] - boxsize - padding;
69117                                 var y0 = p[1] - boxsize - padding;
69118                                 var x1 = p[0] + boxsize + padding;
69119                                 var y1 = p[1] + boxsize + padding;
69120
69121                                 bboxes.push({
69122                                     minX: Math.min(x0, x1),
69123                                     minY: Math.min(y0, y1),
69124                                     maxX: Math.max(x0, x1),
69125                                     maxY: Math.max(y0, y1)
69126                                 });
69127                             }
69128                         }
69129
69130                         if (tryInsert(bboxes, entity.id, false)) {   // accept this one
69131                             return {
69132                                 'font-size': height + 2,
69133                                 lineString: lineString(sub),
69134                                 startOffset: offset + '%'
69135                             };
69136                         }
69137                     }
69138
69139                     function reverse(p) {
69140                         var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
69141                         return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > -Math.PI/2);
69142                     }
69143
69144                     function lineString(points) {
69145                         return 'M' + points.join('L');
69146                     }
69147
69148                     function subpath(points, from, to) {
69149                         var sofar = 0;
69150                         var start, end, i0, i1;
69151
69152                         for (var i = 0; i < points.length - 1; i++) {
69153                             var a = points[i];
69154                             var b = points[i + 1];
69155                             var current = geoVecLength(a, b);
69156                             var portion;
69157                             if (!start && sofar + current >= from) {
69158                                 portion = (from - sofar) / current;
69159                                 start = [
69160                                     a[0] + portion * (b[0] - a[0]),
69161                                     a[1] + portion * (b[1] - a[1])
69162                                 ];
69163                                 i0 = i + 1;
69164                             }
69165                             if (!end && sofar + current >= to) {
69166                                 portion = (to - sofar) / current;
69167                                 end = [
69168                                     a[0] + portion * (b[0] - a[0]),
69169                                     a[1] + portion * (b[1] - a[1])
69170                                 ];
69171                                 i1 = i + 1;
69172                             }
69173                             sofar += current;
69174                         }
69175
69176                         var result = points.slice(i0, i1);
69177                         result.unshift(start);
69178                         result.push(end);
69179                         return result;
69180                     }
69181                 }
69182
69183
69184                 function getAreaLabel(entity, width, height) {
69185                     var centroid = path.centroid(entity.asGeoJSON(graph, true));
69186                     var extent = entity.extent(graph);
69187                     var areaWidth = projection(extent[1])[0] - projection(extent[0])[0];
69188
69189                     if (isNaN(centroid[0]) || areaWidth < 20) { return; }
69190
69191                     var preset = _mainPresetIndex.match(entity, context.graph());
69192                     var picon = preset && preset.icon;
69193                     var iconSize = 17;
69194                     var padding = 2;
69195                     var p = {};
69196
69197                     if (picon) {  // icon and label..
69198                         if (addIcon()) {
69199                             addLabel(iconSize + padding);
69200                             return p;
69201                         }
69202                     } else {   // label only..
69203                         if (addLabel(0)) {
69204                             return p;
69205                         }
69206                     }
69207
69208
69209                     function addIcon() {
69210                         var iconX = centroid[0] - (iconSize / 2);
69211                         var iconY = centroid[1] - (iconSize / 2);
69212                         var bbox = {
69213                             minX: iconX,
69214                             minY: iconY,
69215                             maxX: iconX + iconSize,
69216                             maxY: iconY + iconSize
69217                         };
69218
69219                         if (tryInsert([bbox], entity.id + 'I', true)) {
69220                             p.transform = 'translate(' + iconX + ',' + iconY + ')';
69221                             return true;
69222                         }
69223                         return false;
69224                     }
69225
69226                     function addLabel(yOffset) {
69227                         if (width && areaWidth >= width + 20) {
69228                             var labelX = centroid[0];
69229                             var labelY = centroid[1] + yOffset;
69230                             var bbox = {
69231                                 minX: labelX - (width / 2) - padding,
69232                                 minY: labelY - (height / 2) - padding,
69233                                 maxX: labelX + (width / 2) + padding,
69234                                 maxY: labelY + (height / 2) + padding
69235                             };
69236
69237                             if (tryInsert([bbox], entity.id, true)) {
69238                                 p.x = labelX;
69239                                 p.y = labelY;
69240                                 p.textAnchor = 'middle';
69241                                 p.height = height;
69242                                 return true;
69243                             }
69244                         }
69245                         return false;
69246                     }
69247                 }
69248
69249
69250                 // force insert a singular bounding box
69251                 // singular box only, no array, id better be unique
69252                 function doInsert(bbox, id) {
69253                     bbox.id = id;
69254
69255                     var oldbox = _entitybboxes[id];
69256                     if (oldbox) {
69257                         _rdrawn.remove(oldbox);
69258                     }
69259                     _entitybboxes[id] = bbox;
69260                     _rdrawn.insert(bbox);
69261                 }
69262
69263
69264                 function tryInsert(bboxes, id, saveSkipped) {
69265                     var skipped = false;
69266
69267                     for (var i = 0; i < bboxes.length; i++) {
69268                         var bbox = bboxes[i];
69269                         bbox.id = id;
69270
69271                         // Check that label is visible
69272                         if (bbox.minX < 0 || bbox.minY < 0 || bbox.maxX > dimensions[0] || bbox.maxY > dimensions[1]) {
69273                             skipped = true;
69274                             break;
69275                         }
69276                         if (_rdrawn.collides(bbox)) {
69277                             skipped = true;
69278                             break;
69279                         }
69280                     }
69281
69282                     _entitybboxes[id] = bboxes;
69283
69284                     if (skipped) {
69285                         if (saveSkipped) {
69286                             _rskipped.load(bboxes);
69287                         }
69288                     } else {
69289                         _rdrawn.load(bboxes);
69290                     }
69291
69292                     return !skipped;
69293                 }
69294
69295
69296                 var layer = selection.selectAll('.layer-osm.labels');
69297                 layer.selectAll('.labels-group')
69298                     .data(['halo', 'label', 'debug'])
69299                     .enter()
69300                     .append('g')
69301                     .attr('class', function(d) { return 'labels-group ' + d; });
69302
69303                 var halo = layer.selectAll('.labels-group.halo');
69304                 var label = layer.selectAll('.labels-group.label');
69305                 var debug = layer.selectAll('.labels-group.debug');
69306
69307                 // points
69308                 drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
69309                 drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point);
69310
69311                 // lines
69312                 drawLinePaths(layer, labelled.line, filter, '', positions.line);
69313                 drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
69314                 drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line);
69315
69316                 // areas
69317                 drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
69318                 drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
69319                 drawAreaIcons(label, labelled.area, filter, 'areaicon', positions.area);
69320                 drawAreaIcons(halo, labelled.area, filter, 'areaicon-halo', positions.area);
69321
69322                 // debug
69323                 drawCollisionBoxes(debug, _rskipped, 'debug-skipped');
69324                 drawCollisionBoxes(debug, _rdrawn, 'debug-drawn');
69325
69326                 layer.call(filterLabels);
69327             }
69328
69329
69330             function filterLabels(selection) {
69331                 var drawLayer = selection.selectAll('.layer-osm.labels');
69332                 var layers = drawLayer.selectAll('.labels-group.halo, .labels-group.label');
69333
69334                 layers.selectAll('.nolabel')
69335                     .classed('nolabel', false);
69336
69337                 var mouse = context.map().mouse();
69338                 var graph = context.graph();
69339                 var selectedIDs = context.selectedIDs();
69340                 var ids = [];
69341                 var pad, bbox;
69342
69343                 // hide labels near the mouse
69344                 if (mouse) {
69345                     pad = 20;
69346                     bbox = { minX: mouse[0] - pad, minY: mouse[1] - pad, maxX: mouse[0] + pad, maxY: mouse[1] + pad };
69347                     var nearMouse = _rdrawn.search(bbox).map(function(entity) { return entity.id; });
69348                     ids.push.apply(ids, nearMouse);
69349                 }
69350
69351                 // hide labels on selected nodes (they look weird when dragging / haloed)
69352                 for (var i = 0; i < selectedIDs.length; i++) {
69353                     var entity = graph.hasEntity(selectedIDs[i]);
69354                     if (entity && entity.type === 'node') {
69355                         ids.push(selectedIDs[i]);
69356                     }
69357                 }
69358
69359                 layers.selectAll(utilEntitySelector(ids))
69360                     .classed('nolabel', true);
69361
69362
69363                 // draw the mouse bbox if debugging is on..
69364                 var debug = selection.selectAll('.labels-group.debug');
69365                 var gj = [];
69366                 if (context.getDebug('collision')) {
69367                     gj = bbox ? [{
69368                         type: 'Polygon',
69369                         coordinates: [[
69370                             [bbox.minX, bbox.minY],
69371                             [bbox.maxX, bbox.minY],
69372                             [bbox.maxX, bbox.maxY],
69373                             [bbox.minX, bbox.maxY],
69374                             [bbox.minX, bbox.minY]
69375                         ]]
69376                     }] : [];
69377                 }
69378
69379                 var box = debug.selectAll('.debug-mouse')
69380                     .data(gj);
69381
69382                 // exit
69383                 box.exit()
69384                     .remove();
69385
69386                 // enter/update
69387                 box.enter()
69388                     .append('path')
69389                     .attr('class', 'debug debug-mouse yellow')
69390                     .merge(box)
69391                     .attr('d', d3_geoPath());
69392             }
69393
69394
69395             var throttleFilterLabels = throttle(filterLabels, 100);
69396
69397
69398             drawLabels.observe = function(selection) {
69399                 var listener = function() { throttleFilterLabels(selection); };
69400                 selection.on('mousemove.hidelabels', listener);
69401                 context.on('enter.hidelabels', listener);
69402             };
69403
69404
69405             drawLabels.off = function(selection) {
69406                 throttleFilterLabels.cancel();
69407                 selection.on('mousemove.hidelabels', null);
69408                 context.on('enter.hidelabels', null);
69409             };
69410
69411
69412             return drawLabels;
69413         }
69414
69415         var _layerEnabled$1 = false;
69416         var _qaService$1;
69417
69418         function svgImproveOSM(projection, context, dispatch) {
69419           var throttledRedraw = throttle(function () { return dispatch.call('change'); }, 1000);
69420           var minZoom = 12;
69421
69422           var touchLayer = select(null);
69423           var drawLayer = select(null);
69424           var layerVisible = false;
69425
69426           function markerPath(selection, klass) {
69427             selection
69428               .attr('class', klass)
69429               .attr('transform', 'translate(-10, -28)')
69430               .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');
69431           }
69432
69433           // Loosely-coupled improveOSM service for fetching issues
69434           function getService() {
69435             if (services.improveOSM && !_qaService$1) {
69436               _qaService$1 = services.improveOSM;
69437               _qaService$1.on('loaded', throttledRedraw);
69438             } else if (!services.improveOSM && _qaService$1) {
69439               _qaService$1 = null;
69440             }
69441
69442             return _qaService$1;
69443           }
69444
69445           // Show the markers
69446           function editOn() {
69447             if (!layerVisible) {
69448               layerVisible = true;
69449               drawLayer
69450                 .style('display', 'block');
69451             }
69452           }
69453
69454           // Immediately remove the markers and their touch targets
69455           function editOff() {
69456             if (layerVisible) {
69457               layerVisible = false;
69458               drawLayer
69459                 .style('display', 'none');
69460               drawLayer.selectAll('.qaItem.improveOSM')
69461                 .remove();
69462               touchLayer.selectAll('.qaItem.improveOSM')
69463                 .remove();
69464             }
69465           }
69466
69467           // Enable the layer.  This shows the markers and transitions them to visible.
69468           function layerOn() {
69469             editOn();
69470
69471             drawLayer
69472               .style('opacity', 0)
69473               .transition()
69474               .duration(250)
69475               .style('opacity', 1)
69476               .on('end interrupt', function () { return dispatch.call('change'); });
69477           }
69478
69479           // Disable the layer.  This transitions the layer invisible and then hides the markers.
69480           function layerOff() {
69481             throttledRedraw.cancel();
69482             drawLayer.interrupt();
69483             touchLayer.selectAll('.qaItem.improveOSM')
69484               .remove();
69485
69486             drawLayer
69487               .transition()
69488               .duration(250)
69489               .style('opacity', 0)
69490               .on('end interrupt', function () {
69491                 editOff();
69492                 dispatch.call('change');
69493               });
69494           }
69495
69496           // Update the issue markers
69497           function updateMarkers() {
69498             if (!layerVisible || !_layerEnabled$1) { return; }
69499
69500             var service = getService();
69501             var selectedID = context.selectedErrorID();
69502             var data = (service ? service.getItems(projection) : []);
69503             var getTransform = svgPointTransform(projection);
69504
69505             // Draw markers..
69506             var markers = drawLayer.selectAll('.qaItem.improveOSM')
69507               .data(data, function (d) { return d.id; });
69508
69509             // exit
69510             markers.exit()
69511               .remove();
69512
69513             // enter
69514             var markersEnter = markers.enter()
69515               .append('g')
69516                 .attr('class', function (d) { return ("qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.itemType)); });
69517
69518             markersEnter
69519               .append('polygon')
69520                 .call(markerPath, 'shadow');
69521
69522             markersEnter
69523               .append('ellipse')
69524                 .attr('cx', 0)
69525                 .attr('cy', 0)
69526                 .attr('rx', 4.5)
69527                 .attr('ry', 2)
69528                 .attr('class', 'stroke');
69529
69530             markersEnter
69531               .append('polygon')
69532                 .attr('fill', 'currentColor')
69533                 .call(markerPath, 'qaItem-fill');
69534
69535             markersEnter
69536               .append('use')
69537                 .attr('transform', 'translate(-6.5, -23)')
69538                 .attr('class', 'icon-annotation')
69539                 .attr('width', '13px')
69540                 .attr('height', '13px')
69541                 .attr('xlink:href', function (d) {
69542                   var picon = d.icon;
69543
69544                   if (!picon) {
69545                   return '';
69546                   } else {
69547                   var isMaki = /^maki-/.test(picon);
69548                   return ("#" + picon + (isMaki ? '-11' : ''));
69549                   }
69550                 });
69551
69552             // update
69553             markers
69554               .merge(markersEnter)
69555               .sort(sortY)
69556                 .classed('selected', function (d) { return d.id === selectedID; })
69557                 .attr('transform', getTransform);
69558
69559
69560             // Draw targets..
69561             if (touchLayer.empty()) { return; }
69562             var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
69563
69564             var targets = touchLayer.selectAll('.qaItem.improveOSM')
69565               .data(data, function (d) { return d.id; });
69566
69567             // exit
69568             targets.exit()
69569               .remove();
69570
69571             // enter/update
69572             targets.enter()
69573               .append('rect')
69574                 .attr('width', '20px')
69575                 .attr('height', '30px')
69576                 .attr('x', '-10px')
69577                 .attr('y', '-28px')
69578               .merge(targets)
69579               .sort(sortY)
69580                 .attr('class', function (d) { return ("qaItem " + (d.service) + " target " + fillClass + " itemId-" + (d.id)); })
69581                 .attr('transform', getTransform);
69582
69583             function sortY(a, b) {
69584               return (a.id === selectedID) ? 1
69585                 : (b.id === selectedID) ? -1
69586                 : b.loc[1] - a.loc[1];
69587             }
69588           }
69589
69590           // Draw the ImproveOSM layer and schedule loading issues and updating markers.
69591           function drawImproveOSM(selection) {
69592             var service = getService();
69593
69594             var surface = context.surface();
69595             if (surface && !surface.empty()) {
69596               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
69597             }
69598
69599             drawLayer = selection.selectAll('.layer-improveOSM')
69600               .data(service ? [0] : []);
69601
69602             drawLayer.exit()
69603               .remove();
69604
69605             drawLayer = drawLayer.enter()
69606               .append('g')
69607                 .attr('class', 'layer-improveOSM')
69608                 .style('display', _layerEnabled$1 ? 'block' : 'none')
69609               .merge(drawLayer);
69610
69611             if (_layerEnabled$1) {
69612               if (service && ~~context.map().zoom() >= minZoom) {
69613                 editOn();
69614                 service.loadIssues(projection);
69615                 updateMarkers();
69616               } else {
69617                 editOff();
69618               }
69619             }
69620           }
69621
69622           // Toggles the layer on and off
69623           drawImproveOSM.enabled = function(val) {
69624             if (!arguments.length) { return _layerEnabled$1; }
69625
69626             _layerEnabled$1 = val;
69627             if (_layerEnabled$1) {
69628               layerOn();
69629             } else {
69630               layerOff();
69631               if (context.selectedErrorID()) {
69632                 context.enter(modeBrowse(context));
69633               }
69634             }
69635
69636             dispatch.call('change');
69637             return this;
69638           };
69639
69640           drawImproveOSM.supported = function () { return !!getService(); };
69641
69642           return drawImproveOSM;
69643         }
69644
69645         var _layerEnabled$2 = false;
69646         var _qaService$2;
69647
69648         function svgOsmose(projection, context, dispatch) {
69649           var throttledRedraw = throttle(function () { return dispatch.call('change'); }, 1000);
69650           var minZoom = 12;
69651
69652           var touchLayer = select(null);
69653           var drawLayer = select(null);
69654           var layerVisible = false;
69655
69656           function markerPath(selection, klass) {
69657             selection
69658               .attr('class', klass)
69659               .attr('transform', 'translate(-10, -28)')
69660               .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');
69661           }
69662
69663           // Loosely-coupled osmose service for fetching issues
69664           function getService() {
69665             if (services.osmose && !_qaService$2) {
69666               _qaService$2 = services.osmose;
69667               _qaService$2.on('loaded', throttledRedraw);
69668             } else if (!services.osmose && _qaService$2) {
69669               _qaService$2 = null;
69670             }
69671
69672             return _qaService$2;
69673           }
69674
69675           // Show the markers
69676           function editOn() {
69677             if (!layerVisible) {
69678               layerVisible = true;
69679               drawLayer
69680                 .style('display', 'block');
69681             }
69682           }
69683
69684           // Immediately remove the markers and their touch targets
69685           function editOff() {
69686             if (layerVisible) {
69687               layerVisible = false;
69688               drawLayer
69689                 .style('display', 'none');
69690               drawLayer.selectAll('.qaItem.osmose')
69691                 .remove();
69692               touchLayer.selectAll('.qaItem.osmose')
69693                 .remove();
69694             }
69695           }
69696
69697           // Enable the layer.  This shows the markers and transitions them to visible.
69698           function layerOn() {
69699             editOn();
69700
69701             drawLayer
69702               .style('opacity', 0)
69703               .transition()
69704               .duration(250)
69705               .style('opacity', 1)
69706               .on('end interrupt', function () { return dispatch.call('change'); });
69707           }
69708
69709           // Disable the layer.  This transitions the layer invisible and then hides the markers.
69710           function layerOff() {
69711             throttledRedraw.cancel();
69712             drawLayer.interrupt();
69713             touchLayer.selectAll('.qaItem.osmose')
69714               .remove();
69715
69716             drawLayer
69717               .transition()
69718               .duration(250)
69719               .style('opacity', 0)
69720               .on('end interrupt', function () {
69721                 editOff();
69722                 dispatch.call('change');
69723               });
69724           }
69725
69726           // Update the issue markers
69727           function updateMarkers() {
69728             if (!layerVisible || !_layerEnabled$2) { return; }
69729
69730             var service = getService();
69731             var selectedID = context.selectedErrorID();
69732             var data = (service ? service.getItems(projection) : []);
69733             var getTransform = svgPointTransform(projection);
69734
69735             // Draw markers..
69736             var markers = drawLayer.selectAll('.qaItem.osmose')
69737               .data(data, function (d) { return d.id; });
69738
69739             // exit
69740             markers.exit()
69741               .remove();
69742
69743             // enter
69744             var markersEnter = markers.enter()
69745               .append('g')
69746                 .attr('class', function (d) { return ("qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.itemType)); });
69747
69748             markersEnter
69749               .append('polygon')
69750                 .call(markerPath, 'shadow');
69751
69752             markersEnter
69753               .append('ellipse')
69754                 .attr('cx', 0)
69755                 .attr('cy', 0)
69756                 .attr('rx', 4.5)
69757                 .attr('ry', 2)
69758                 .attr('class', 'stroke');
69759
69760             markersEnter
69761               .append('polygon')
69762                 .attr('fill', function (d) { return service.getColor(d.item); })
69763                 .call(markerPath, 'qaItem-fill');
69764
69765             markersEnter
69766               .append('use')
69767                 .attr('transform', 'translate(-6.5, -23)')
69768                 .attr('class', 'icon-annotation')
69769                 .attr('width', '13px')
69770                 .attr('height', '13px')
69771                 .attr('xlink:href', function (d) {
69772                   var picon = d.icon;
69773
69774                   if (!picon) {
69775                     return '';
69776                   } else {
69777                     var isMaki = /^maki-/.test(picon);
69778                     return ("#" + picon + (isMaki ? '-11' : ''));
69779                   }
69780                 });
69781
69782             // update
69783             markers
69784               .merge(markersEnter)
69785               .sort(sortY)
69786                 .classed('selected', function (d) { return d.id === selectedID; })
69787                 .attr('transform', getTransform);
69788
69789             // Draw targets..
69790             if (touchLayer.empty()) { return; }
69791             var fillClass = context.getDebug('target') ? 'pink' : 'nocolor';
69792
69793             var targets = touchLayer.selectAll('.qaItem.osmose')
69794               .data(data, function (d) { return d.id; });
69795
69796             // exit
69797             targets.exit()
69798               .remove();
69799
69800             // enter/update
69801             targets.enter()
69802               .append('rect')
69803                 .attr('width', '20px')
69804                 .attr('height', '30px')
69805                 .attr('x', '-10px')
69806                 .attr('y', '-28px')
69807               .merge(targets)
69808               .sort(sortY)
69809                 .attr('class', function (d) { return ("qaItem " + (d.service) + " target " + fillClass + " itemId-" + (d.id)); })
69810                 .attr('transform', getTransform);
69811
69812             function sortY(a, b) {
69813               return (a.id === selectedID) ? 1
69814                 : (b.id === selectedID) ? -1
69815                 : b.loc[1] - a.loc[1];
69816             }
69817           }
69818
69819           // Draw the Osmose layer and schedule loading issues and updating markers.
69820           function drawOsmose(selection) {
69821             var service = getService();
69822
69823             var surface = context.surface();
69824             if (surface && !surface.empty()) {
69825               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
69826             }
69827
69828             drawLayer = selection.selectAll('.layer-osmose')
69829               .data(service ? [0] : []);
69830
69831             drawLayer.exit()
69832               .remove();
69833
69834             drawLayer = drawLayer.enter()
69835               .append('g')
69836                 .attr('class', 'layer-osmose')
69837                 .style('display', _layerEnabled$2 ? 'block' : 'none')
69838               .merge(drawLayer);
69839
69840             if (_layerEnabled$2) {
69841               if (service && ~~context.map().zoom() >= minZoom) {
69842                 editOn();
69843                 service.loadIssues(projection);
69844                 updateMarkers();
69845               } else {
69846                 editOff();
69847               }
69848             }
69849           }
69850
69851           // Toggles the layer on and off
69852           drawOsmose.enabled = function(val) {
69853             if (!arguments.length) { return _layerEnabled$2; }
69854
69855             _layerEnabled$2 = val;
69856             if (_layerEnabled$2) {
69857               // Strings supplied by Osmose fetched before showing layer for first time
69858               // NOTE: Currently no way to change locale in iD at runtime, would need to re-call this method if that's ever implemented
69859               // Also, If layer is toggled quickly multiple requests are sent
69860               getService().loadStrings()
69861                 .then(layerOn)
69862                 .catch(function (err) {
69863                   console.log(err); // eslint-disable-line no-console
69864                 });
69865             } else {
69866               layerOff();
69867               if (context.selectedErrorID()) {
69868                 context.enter(modeBrowse(context));
69869               }
69870             }
69871
69872             dispatch.call('change');
69873             return this;
69874           };
69875
69876           drawOsmose.supported = function () { return !!getService(); };
69877
69878           return drawOsmose;
69879         }
69880
69881         function svgStreetside(projection, context, dispatch) {
69882             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
69883             var minZoom = 14;
69884             var minMarkerZoom = 16;
69885             var minViewfieldZoom = 18;
69886             var layer = select(null);
69887             var _viewerYaw = 0;
69888             var _selectedSequence = null;
69889             var _streetside;
69890
69891             /**
69892              * init().
69893              */
69894             function init() {
69895                 if (svgStreetside.initialized) { return; }  // run once
69896                 svgStreetside.enabled = false;
69897                 svgStreetside.initialized = true;
69898             }
69899
69900             /**
69901              * getService().
69902              */
69903             function getService() {
69904                 if (services.streetside && !_streetside) {
69905                     _streetside = services.streetside;
69906                     _streetside.event
69907                         .on('viewerChanged.svgStreetside', viewerChanged)
69908                         .on('loadedBubbles.svgStreetside', throttledRedraw);
69909                 } else if (!services.streetside && _streetside) {
69910                     _streetside = null;
69911                 }
69912
69913                 return _streetside;
69914             }
69915
69916             /**
69917              * showLayer().
69918              */
69919             function showLayer() {
69920                 var service = getService();
69921                 if (!service) { return; }
69922
69923                 editOn();
69924
69925                 layer
69926                     .style('opacity', 0)
69927                     .transition()
69928                     .duration(250)
69929                     .style('opacity', 1)
69930                     .on('end', function () { dispatch.call('change'); });
69931             }
69932
69933             /**
69934              * hideLayer().
69935              */
69936             function hideLayer() {
69937                 throttledRedraw.cancel();
69938
69939                 layer
69940                     .transition()
69941                     .duration(250)
69942                     .style('opacity', 0)
69943                     .on('end', editOff);
69944             }
69945
69946             /**
69947              * editOn().
69948              */
69949             function editOn() {
69950                 layer.style('display', 'block');
69951             }
69952
69953             /**
69954              * editOff().
69955              */
69956             function editOff() {
69957                 layer.selectAll('.viewfield-group').remove();
69958                 layer.style('display', 'none');
69959             }
69960
69961             /**
69962              * click() Handles 'bubble' point click event.
69963              */
69964             function click(d) {
69965                 var service = getService();
69966                 if (!service) { return; }
69967
69968                 // try to preserve the viewer rotation when staying on the same sequence
69969                 if (d.sequenceKey !== _selectedSequence) {
69970                     _viewerYaw = 0;  // reset
69971                 }
69972                 _selectedSequence = d.sequenceKey;
69973
69974                 service
69975                     .selectImage(context, d)
69976                     .then(function (response) {
69977                         if (response.status === 'ok'){
69978                             service.showViewer(context, _viewerYaw);
69979                         }
69980                     });
69981
69982
69983                 context.map().centerEase(d.loc);
69984             }
69985
69986             /**
69987              * mouseover().
69988              */
69989             function mouseover(d) {
69990                 var service = getService();
69991                 if (service) { service.setStyles(context, d); }
69992             }
69993
69994             /**
69995              * mouseout().
69996              */
69997             function mouseout() {
69998                 var service = getService();
69999                 if (service) { service.setStyles(context, null); }
70000             }
70001
70002             /**
70003              * transform().
70004              */
70005             function transform(d) {
70006                 var t = svgPointTransform(projection)(d);
70007                 var rot = d.ca + _viewerYaw;
70008                 if (rot) {
70009                     t += ' rotate(' + Math.floor(rot) + ',0,0)';
70010                 }
70011                 return t;
70012             }
70013
70014
70015             function viewerChanged() {
70016                 var service = getService();
70017                 if (!service) { return; }
70018
70019                 var viewer = service.viewer();
70020                 if (!viewer) { return; }
70021
70022                 // update viewfield rotation
70023                 _viewerYaw = viewer.getYaw();
70024
70025                 // avoid updating if the map is currently transformed
70026                 // e.g. during drags or easing.
70027                 if (context.map().isTransformed()) { return; }
70028
70029                 layer.selectAll('.viewfield-group.currentView')
70030                     .attr('transform', transform);
70031             }
70032
70033
70034             context.photos().on('change.streetside', update);
70035
70036             /**
70037              * update().
70038              */
70039             function update() {
70040                 var viewer = context.container().select('.photoviewer');
70041                 var selected = viewer.empty() ? undefined : viewer.datum();
70042                 var z = ~~context.map().zoom();
70043                 var showMarkers = (z >= minMarkerZoom);
70044                 var showViewfields = (z >= minViewfieldZoom);
70045                 var service = getService();
70046
70047                 var sequences = [];
70048                 var bubbles = [];
70049
70050                 if (context.photos().showsPanoramic()) {
70051                     sequences = (service ? service.sequences(projection) : []);
70052                     bubbles = (service && showMarkers ? service.bubbles(projection) : []);
70053                 }
70054
70055                 var traces = layer.selectAll('.sequences').selectAll('.sequence')
70056                     .data(sequences, function(d) { return d.properties.key; });
70057
70058                 // exit
70059                 traces.exit()
70060                     .remove();
70061
70062                 // enter/update
70063                 traces = traces.enter()
70064                     .append('path')
70065                     .attr('class', 'sequence')
70066                     .merge(traces)
70067                     .attr('d', svgPath(projection).geojson);
70068
70069
70070                 var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
70071                     .data(bubbles, function(d) {
70072                         // force reenter once bubbles are attached to a sequence
70073                         return d.key + (d.sequenceKey ? 'v1' : 'v0');
70074                     });
70075
70076                 // exit
70077                 groups.exit()
70078                     .remove();
70079
70080                 // enter
70081                 var groupsEnter = groups.enter()
70082                     .append('g')
70083                     .attr('class', 'viewfield-group')
70084                     .on('mouseenter', mouseover)
70085                     .on('mouseleave', mouseout)
70086                     .on('click', click);
70087
70088                 groupsEnter
70089                     .append('g')
70090                     .attr('class', 'viewfield-scale');
70091
70092                 // update
70093                 var markers = groups
70094                     .merge(groupsEnter)
70095                     .sort(function(a, b) {
70096                         return (a === selected) ? 1
70097                             : (b === selected) ? -1
70098                             : b.loc[1] - a.loc[1];
70099                     })
70100                     .attr('transform', transform)
70101                     .select('.viewfield-scale');
70102
70103
70104                 markers.selectAll('circle')
70105                     .data([0])
70106                     .enter()
70107                     .append('circle')
70108                     .attr('dx', '0')
70109                     .attr('dy', '0')
70110                     .attr('r', '6');
70111
70112                 var viewfields = markers.selectAll('.viewfield')
70113                     .data(showViewfields ? [0] : []);
70114
70115                 viewfields.exit()
70116                     .remove();
70117
70118                 // viewfields may or may not be drawn...
70119                 // but if they are, draw below the circles
70120                 viewfields.enter()
70121                     .insert('path', 'circle')
70122                     .attr('class', 'viewfield')
70123                     .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
70124                     .attr('d', viewfieldPath);
70125
70126                 function viewfieldPath() {
70127                     var d = this.parentNode.__data__;
70128                     if (d.pano) {
70129                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
70130                     } else {
70131                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
70132                     }
70133                 }
70134
70135             }
70136
70137             /**
70138              * drawImages()
70139              * drawImages is the method that is returned (and that runs) everytime 'svgStreetside()' is called.
70140              * 'svgStreetside()' is called from index.js
70141              */
70142             function drawImages(selection) {
70143                 var enabled = svgStreetside.enabled;
70144                 var service = getService();
70145
70146                 layer = selection.selectAll('.layer-streetside-images')
70147                     .data(service ? [0] : []);
70148
70149                 layer.exit()
70150                     .remove();
70151
70152                 var layerEnter = layer.enter()
70153                     .append('g')
70154                     .attr('class', 'layer-streetside-images')
70155                     .style('display', enabled ? 'block' : 'none');
70156
70157                 layerEnter
70158                     .append('g')
70159                     .attr('class', 'sequences');
70160
70161                 layerEnter
70162                     .append('g')
70163                     .attr('class', 'markers');
70164
70165                 layer = layerEnter
70166                     .merge(layer);
70167
70168                 if (enabled) {
70169                     if (service && ~~context.map().zoom() >= minZoom) {
70170                         editOn();
70171                         update();
70172                         service.loadBubbles(projection);
70173                     } else {
70174                         editOff();
70175                     }
70176                 }
70177             }
70178
70179
70180             /**
70181              * drawImages.enabled().
70182              */
70183             drawImages.enabled = function(_) {
70184                 if (!arguments.length) { return svgStreetside.enabled; }
70185                 svgStreetside.enabled = _;
70186                 if (svgStreetside.enabled) {
70187                     showLayer();
70188                 } else {
70189                     hideLayer();
70190                 }
70191                 dispatch.call('change');
70192                 return this;
70193             };
70194
70195             /**
70196              * drawImages.supported().
70197              */
70198             drawImages.supported = function() {
70199                 return !!getService();
70200             };
70201
70202             init();
70203
70204             return drawImages;
70205         }
70206
70207         function svgMapillaryImages(projection, context, dispatch) {
70208             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70209             var minZoom = 12;
70210             var minMarkerZoom = 16;
70211             var minViewfieldZoom = 18;
70212             var layer = select(null);
70213             var _mapillary;
70214             var viewerCompassAngle;
70215
70216
70217             function init() {
70218                 if (svgMapillaryImages.initialized) { return; }  // run once
70219                 svgMapillaryImages.enabled = false;
70220                 svgMapillaryImages.initialized = true;
70221             }
70222
70223
70224             function getService() {
70225                 if (services.mapillary && !_mapillary) {
70226                     _mapillary = services.mapillary;
70227                     _mapillary.event.on('loadedImages', throttledRedraw);
70228                     _mapillary.event.on('bearingChanged', function(e) {
70229                         viewerCompassAngle = e;
70230
70231                         // avoid updating if the map is currently transformed
70232                         // e.g. during drags or easing.
70233                         if (context.map().isTransformed()) { return; }
70234
70235                         layer.selectAll('.viewfield-group.currentView')
70236                             .filter(function(d) {
70237                                 return d.pano;
70238                             })
70239                             .attr('transform', transform);
70240                     });
70241                 } else if (!services.mapillary && _mapillary) {
70242                     _mapillary = null;
70243                 }
70244
70245                 return _mapillary;
70246             }
70247
70248
70249             function showLayer() {
70250                 var service = getService();
70251                 if (!service) { return; }
70252
70253                 editOn();
70254
70255                 layer
70256                     .style('opacity', 0)
70257                     .transition()
70258                     .duration(250)
70259                     .style('opacity', 1)
70260                     .on('end', function () { dispatch.call('change'); });
70261             }
70262
70263
70264             function hideLayer() {
70265                 throttledRedraw.cancel();
70266
70267                 layer
70268                     .transition()
70269                     .duration(250)
70270                     .style('opacity', 0)
70271                     .on('end', editOff);
70272             }
70273
70274
70275             function editOn() {
70276                 layer.style('display', 'block');
70277             }
70278
70279
70280             function editOff() {
70281                 layer.selectAll('.viewfield-group').remove();
70282                 layer.style('display', 'none');
70283             }
70284
70285
70286             function click(d) {
70287                 var service = getService();
70288                 if (!service) { return; }
70289
70290                 service
70291                     .selectImage(context, d.key)
70292                     .updateViewer(context, d.key)
70293                     .showViewer(context);
70294
70295                 context.map().centerEase(d.loc);
70296             }
70297
70298
70299             function mouseover(d) {
70300                 var service = getService();
70301                 if (service) { service.setStyles(context, d); }
70302             }
70303
70304
70305             function mouseout() {
70306                 var service = getService();
70307                 if (service) { service.setStyles(context, null); }
70308             }
70309
70310
70311             function transform(d) {
70312                 var t = svgPointTransform(projection)(d);
70313                 if (d.pano && viewerCompassAngle !== null && isFinite(viewerCompassAngle)) {
70314                     t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)';
70315                 } else if (d.ca) {
70316                     t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
70317                 }
70318                 return t;
70319             }
70320
70321             context.photos().on('change.mapillary_images', update);
70322
70323             function filterImages(images) {
70324                 var showsPano = context.photos().showsPanoramic();
70325                 var showsFlat = context.photos().showsFlat();
70326                 if (!showsPano || !showsFlat) {
70327                     images = images.filter(function(image) {
70328                         if (image.pano) { return showsPano; }
70329                         return showsFlat;
70330                     });
70331                 }
70332                 return images;
70333             }
70334
70335             function filterSequences(sequences, service) {
70336                 var showsPano = context.photos().showsPanoramic();
70337                 var showsFlat = context.photos().showsFlat();
70338                 if (!showsPano || !showsFlat) {
70339                     sequences = sequences.filter(function(sequence) {
70340                         if (sequence.properties.hasOwnProperty('pano')) {
70341                             if (sequence.properties.pano) { return showsPano; }
70342                             return showsFlat;
70343                         } else {
70344                             // if the sequence doesn't specify pano or not, search its images
70345                             var cProps = sequence.properties.coordinateProperties;
70346                             if (cProps && cProps.image_keys && cProps.image_keys.length > 0) {
70347                                 for (var index in cProps.image_keys) {
70348                                     var imageKey = cProps.image_keys[index];
70349                                     var image = service.cachedImage(imageKey);
70350                                     if (image && image.hasOwnProperty('pano')) {
70351                                         if (image.pano) { return showsPano; }
70352                                         return showsFlat;
70353                                     }
70354                                 }
70355                             }
70356                         }
70357                     });
70358                 }
70359                 return sequences;
70360             }
70361
70362             function update() {
70363
70364                 var z = ~~context.map().zoom();
70365                 var showMarkers = (z >= minMarkerZoom);
70366                 var showViewfields = (z >= minViewfieldZoom);
70367
70368                 var service = getService();
70369                 var selectedKey = service && service.getSelectedImageKey();
70370                 var sequences = (service ? service.sequences(projection) : []);
70371                 var images = (service && showMarkers ? service.images(projection) : []);
70372
70373                 images = filterImages(images);
70374                 sequences = filterSequences(sequences, service);
70375
70376                 var traces = layer.selectAll('.sequences').selectAll('.sequence')
70377                     .data(sequences, function(d) { return d.properties.key; });
70378
70379                 // exit
70380                 traces.exit()
70381                     .remove();
70382
70383                 // enter/update
70384                 traces = traces.enter()
70385                     .append('path')
70386                     .attr('class', 'sequence')
70387                     .merge(traces)
70388                     .attr('d', svgPath(projection).geojson);
70389
70390
70391                 var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
70392                     .data(images, function(d) { return d.key; });
70393
70394                 // exit
70395                 groups.exit()
70396                     .remove();
70397
70398                 // enter
70399                 var groupsEnter = groups.enter()
70400                     .append('g')
70401                     .attr('class', 'viewfield-group')
70402                     .on('mouseenter', mouseover)
70403                     .on('mouseleave', mouseout)
70404                     .on('click', click);
70405
70406                 groupsEnter
70407                     .append('g')
70408                     .attr('class', 'viewfield-scale');
70409
70410                 // update
70411                 var markers = groups
70412                     .merge(groupsEnter)
70413                     .sort(function(a, b) {
70414                         return (a.key === selectedKey) ? 1
70415                             : (b.key === selectedKey) ? -1
70416                             : b.loc[1] - a.loc[1];  // sort Y
70417                     })
70418                     .attr('transform', transform)
70419                     .select('.viewfield-scale');
70420
70421
70422                 markers.selectAll('circle')
70423                     .data([0])
70424                     .enter()
70425                     .append('circle')
70426                     .attr('dx', '0')
70427                     .attr('dy', '0')
70428                     .attr('r', '6');
70429
70430                 var viewfields = markers.selectAll('.viewfield')
70431                     .data(showViewfields ? [0] : []);
70432
70433                 viewfields.exit()
70434                     .remove();
70435
70436                 viewfields.enter()               // viewfields may or may not be drawn...
70437                     .insert('path', 'circle')    // but if they are, draw below the circles
70438                     .attr('class', 'viewfield')
70439                     .classed('pano', function() { return this.parentNode.__data__.pano; })
70440                     .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
70441                     .attr('d', viewfieldPath);
70442
70443                 function viewfieldPath() {
70444                     var d = this.parentNode.__data__;
70445                     if (d.pano) {
70446                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
70447                     } else {
70448                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
70449                     }
70450                 }
70451             }
70452
70453
70454             function drawImages(selection) {
70455                 var enabled = svgMapillaryImages.enabled;
70456                 var service = getService();
70457
70458                 layer = selection.selectAll('.layer-mapillary')
70459                     .data(service ? [0] : []);
70460
70461                 layer.exit()
70462                     .remove();
70463
70464                 var layerEnter = layer.enter()
70465                     .append('g')
70466                     .attr('class', 'layer-mapillary')
70467                     .style('display', enabled ? 'block' : 'none');
70468
70469                 layerEnter
70470                     .append('g')
70471                     .attr('class', 'sequences');
70472
70473                 layerEnter
70474                     .append('g')
70475                     .attr('class', 'markers');
70476
70477                 layer = layerEnter
70478                     .merge(layer);
70479
70480                 if (enabled) {
70481                     if (service && ~~context.map().zoom() >= minZoom) {
70482                         editOn();
70483                         update();
70484                         service.loadImages(projection);
70485                     } else {
70486                         editOff();
70487                     }
70488                 }
70489             }
70490
70491
70492             drawImages.enabled = function(_) {
70493                 if (!arguments.length) { return svgMapillaryImages.enabled; }
70494                 svgMapillaryImages.enabled = _;
70495                 if (svgMapillaryImages.enabled) {
70496                     showLayer();
70497                 } else {
70498                     hideLayer();
70499                 }
70500                 dispatch.call('change');
70501                 return this;
70502             };
70503
70504
70505             drawImages.supported = function() {
70506                 return !!getService();
70507             };
70508
70509
70510             init();
70511             return drawImages;
70512         }
70513
70514         function svgMapillarySigns(projection, context, dispatch) {
70515             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70516             var minZoom = 12;
70517             var layer = select(null);
70518             var _mapillary;
70519
70520
70521             function init() {
70522                 if (svgMapillarySigns.initialized) { return; }  // run once
70523                 svgMapillarySigns.enabled = false;
70524                 svgMapillarySigns.initialized = true;
70525             }
70526
70527
70528             function getService() {
70529                 if (services.mapillary && !_mapillary) {
70530                     _mapillary = services.mapillary;
70531                     _mapillary.event.on('loadedSigns', throttledRedraw);
70532                 } else if (!services.mapillary && _mapillary) {
70533                     _mapillary = null;
70534                 }
70535                 return _mapillary;
70536             }
70537
70538
70539             function showLayer() {
70540                 var service = getService();
70541                 if (!service) { return; }
70542
70543                 editOn();
70544             }
70545
70546
70547             function hideLayer() {
70548                 throttledRedraw.cancel();
70549                 editOff();
70550             }
70551
70552
70553             function editOn() {
70554                 layer.style('display', 'block');
70555             }
70556
70557
70558             function editOff() {
70559                 layer.selectAll('.icon-sign').remove();
70560                 layer.style('display', 'none');
70561             }
70562
70563
70564             function click(d) {
70565                 var service = getService();
70566                 if (!service) { return; }
70567
70568                 context.map().centerEase(d.loc);
70569
70570                 var selectedImageKey = service.getSelectedImageKey();
70571                 var imageKey;
70572
70573                 // Pick one of the images the sign was detected in,
70574                 // preference given to an image already selected.
70575                 d.detections.forEach(function(detection) {
70576                     if (!imageKey || selectedImageKey === detection.image_key) {
70577                         imageKey = detection.image_key;
70578                     }
70579                 });
70580
70581                 service
70582                     .selectImage(context, imageKey)
70583                     .updateViewer(context, imageKey)
70584                     .showViewer(context);
70585             }
70586
70587
70588             function update() {
70589                 var service = getService();
70590                 var data = (service ? service.signs(projection) : []);
70591                 var selectedImageKey = service.getSelectedImageKey();
70592                 var transform = svgPointTransform(projection);
70593
70594                 var signs = layer.selectAll('.icon-sign')
70595                     .data(data, function(d) { return d.key; });
70596
70597                 // exit
70598                 signs.exit()
70599                     .remove();
70600
70601                 // enter
70602                 var enter = signs.enter()
70603                     .append('g')
70604                     .attr('class', 'icon-sign icon-detected')
70605                     .on('click', click);
70606
70607                 enter
70608                     .append('use')
70609                     .attr('width', '24px')
70610                     .attr('height', '24px')
70611                     .attr('x', '-12px')
70612                     .attr('y', '-12px')
70613                     .attr('xlink:href', function(d) { return '#' + d.value; });
70614
70615                 enter
70616                     .append('rect')
70617                     .attr('width', '24px')
70618                     .attr('height', '24px')
70619                     .attr('x', '-12px')
70620                     .attr('y', '-12px');
70621
70622                 // update
70623                 signs
70624                     .merge(enter)
70625                     .attr('transform', transform)
70626                     .classed('currentView', function(d) {
70627                         return d.detections.some(function(detection) {
70628                             return detection.image_key === selectedImageKey;
70629                         });
70630                     })
70631                     .sort(function(a, b) {
70632                         var aSelected = a.detections.some(function(detection) {
70633                             return detection.image_key === selectedImageKey;
70634                         });
70635                         var bSelected = b.detections.some(function(detection) {
70636                             return detection.image_key === selectedImageKey;
70637                         });
70638                         if (aSelected === bSelected) {
70639                             return b.loc[1] - a.loc[1]; // sort Y
70640                         } else if (aSelected) {
70641                             return 1;
70642                         }
70643                         return -1;
70644                     });
70645             }
70646
70647
70648             function drawSigns(selection) {
70649                 var enabled = svgMapillarySigns.enabled;
70650                 var service = getService();
70651
70652                 layer = selection.selectAll('.layer-mapillary-signs')
70653                     .data(service ? [0] : []);
70654
70655                 layer.exit()
70656                     .remove();
70657
70658                 layer = layer.enter()
70659                     .append('g')
70660                     .attr('class', 'layer-mapillary-signs layer-mapillary-detections')
70661                     .style('display', enabled ? 'block' : 'none')
70662                     .merge(layer);
70663
70664                 if (enabled) {
70665                     if (service && ~~context.map().zoom() >= minZoom) {
70666                         editOn();
70667                         update();
70668                         service.loadSigns(projection);
70669                     } else {
70670                         editOff();
70671                     }
70672                 }
70673             }
70674
70675
70676             drawSigns.enabled = function(_) {
70677                 if (!arguments.length) { return svgMapillarySigns.enabled; }
70678                 svgMapillarySigns.enabled = _;
70679                 if (svgMapillarySigns.enabled) {
70680                     showLayer();
70681                 } else {
70682                     hideLayer();
70683                 }
70684                 dispatch.call('change');
70685                 return this;
70686             };
70687
70688
70689             drawSigns.supported = function() {
70690                 return !!getService();
70691             };
70692
70693
70694             init();
70695             return drawSigns;
70696         }
70697
70698         function svgMapillaryMapFeatures(projection, context, dispatch) {
70699             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70700             var minZoom = 12;
70701             var layer = select(null);
70702             var _mapillary;
70703
70704
70705             function init() {
70706                 if (svgMapillaryMapFeatures.initialized) { return; }  // run once
70707                 svgMapillaryMapFeatures.enabled = false;
70708                 svgMapillaryMapFeatures.initialized = true;
70709             }
70710
70711
70712             function getService() {
70713                 if (services.mapillary && !_mapillary) {
70714                     _mapillary = services.mapillary;
70715                     _mapillary.event.on('loadedMapFeatures', throttledRedraw);
70716                 } else if (!services.mapillary && _mapillary) {
70717                     _mapillary = null;
70718                 }
70719                 return _mapillary;
70720             }
70721
70722
70723             function showLayer() {
70724                 var service = getService();
70725                 if (!service) { return; }
70726
70727                 editOn();
70728             }
70729
70730
70731             function hideLayer() {
70732                 throttledRedraw.cancel();
70733                 editOff();
70734             }
70735
70736
70737             function editOn() {
70738                 layer.style('display', 'block');
70739             }
70740
70741
70742             function editOff() {
70743                 layer.selectAll('.icon-map-feature').remove();
70744                 layer.style('display', 'none');
70745             }
70746
70747
70748             function click(d) {
70749                 var service = getService();
70750                 if (!service) { return; }
70751
70752                 context.map().centerEase(d.loc);
70753
70754                 var selectedImageKey = service.getSelectedImageKey();
70755                 var imageKey;
70756
70757                 // Pick one of the images the map feature was detected in,
70758                 // preference given to an image already selected.
70759                 d.detections.forEach(function(detection) {
70760                     if (!imageKey || selectedImageKey === detection.image_key) {
70761                         imageKey = detection.image_key;
70762                     }
70763                 });
70764
70765                 service
70766                     .selectImage(context, imageKey)
70767                     .updateViewer(context, imageKey)
70768                     .showViewer(context);
70769             }
70770
70771
70772             function update() {
70773                 var service = getService();
70774                 var data = (service ? service.mapFeatures(projection) : []);
70775                 var selectedImageKey = service && service.getSelectedImageKey();
70776                 var transform = svgPointTransform(projection);
70777
70778                 var mapFeatures = layer.selectAll('.icon-map-feature')
70779                     .data(data, function(d) { return d.key; });
70780
70781                 // exit
70782                 mapFeatures.exit()
70783                     .remove();
70784
70785                 // enter
70786                 var enter = mapFeatures.enter()
70787                     .append('g')
70788                     .attr('class', 'icon-map-feature icon-detected')
70789                     .on('click', click);
70790
70791                 enter
70792                     .append('title')
70793                     .text(function(d) {
70794                         var id = d.value.replace(/--/g, '.').replace(/-/g, '_');
70795                         return _t('mapillary_map_features.' + id);
70796                     });
70797
70798                 enter
70799                     .append('use')
70800                     .attr('width', '24px')
70801                     .attr('height', '24px')
70802                     .attr('x', '-12px')
70803                     .attr('y', '-12px')
70804                     .attr('xlink:href', function(d) {
70805                         if (d.value === 'object--billboard') {
70806                             // no billboard icon right now, so use the advertisement icon
70807                             return '#object--sign--advertisement';
70808                         }
70809                         return '#' + d.value;
70810                     });
70811
70812                 enter
70813                     .append('rect')
70814                     .attr('width', '24px')
70815                     .attr('height', '24px')
70816                     .attr('x', '-12px')
70817                     .attr('y', '-12px');
70818
70819                 // update
70820                 mapFeatures
70821                     .merge(enter)
70822                     .attr('transform', transform)
70823                     .classed('currentView', function(d) {
70824                         return d.detections.some(function(detection) {
70825                             return detection.image_key === selectedImageKey;
70826                         });
70827                     })
70828                     .sort(function(a, b) {
70829                         var aSelected = a.detections.some(function(detection) {
70830                             return detection.image_key === selectedImageKey;
70831                         });
70832                         var bSelected = b.detections.some(function(detection) {
70833                             return detection.image_key === selectedImageKey;
70834                         });
70835                         if (aSelected === bSelected) {
70836                             return b.loc[1] - a.loc[1]; // sort Y
70837                         } else if (aSelected) {
70838                             return 1;
70839                         }
70840                         return -1;
70841                     });
70842             }
70843
70844
70845             function drawMapFeatures(selection) {
70846                 var enabled = svgMapillaryMapFeatures.enabled;
70847                 var service = getService();
70848
70849                 layer = selection.selectAll('.layer-mapillary-map-features')
70850                     .data(service ? [0] : []);
70851
70852                 layer.exit()
70853                     .remove();
70854
70855                 layer = layer.enter()
70856                     .append('g')
70857                     .attr('class', 'layer-mapillary-map-features layer-mapillary-detections')
70858                     .style('display', enabled ? 'block' : 'none')
70859                     .merge(layer);
70860
70861                 if (enabled) {
70862                     if (service && ~~context.map().zoom() >= minZoom) {
70863                         editOn();
70864                         update();
70865                         service.loadMapFeatures(projection);
70866                     } else {
70867                         editOff();
70868                     }
70869                 }
70870             }
70871
70872
70873             drawMapFeatures.enabled = function(_) {
70874                 if (!arguments.length) { return svgMapillaryMapFeatures.enabled; }
70875                 svgMapillaryMapFeatures.enabled = _;
70876                 if (svgMapillaryMapFeatures.enabled) {
70877                     showLayer();
70878                 } else {
70879                     hideLayer();
70880                 }
70881                 dispatch.call('change');
70882                 return this;
70883             };
70884
70885
70886             drawMapFeatures.supported = function() {
70887                 return !!getService();
70888             };
70889
70890
70891             init();
70892             return drawMapFeatures;
70893         }
70894
70895         function svgOpenstreetcamImages(projection, context, dispatch) {
70896             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70897             var minZoom = 12;
70898             var minMarkerZoom = 16;
70899             var minViewfieldZoom = 18;
70900             var layer = select(null);
70901             var _openstreetcam;
70902
70903
70904             function init() {
70905                 if (svgOpenstreetcamImages.initialized) { return; }  // run once
70906                 svgOpenstreetcamImages.enabled = false;
70907                 svgOpenstreetcamImages.initialized = true;
70908             }
70909
70910
70911             function getService() {
70912                 if (services.openstreetcam && !_openstreetcam) {
70913                     _openstreetcam = services.openstreetcam;
70914                     _openstreetcam.event.on('loadedImages', throttledRedraw);
70915                 } else if (!services.openstreetcam && _openstreetcam) {
70916                     _openstreetcam = null;
70917                 }
70918
70919                 return _openstreetcam;
70920             }
70921
70922
70923             function showLayer() {
70924                 var service = getService();
70925                 if (!service) { return; }
70926
70927                 editOn();
70928
70929                 layer
70930                     .style('opacity', 0)
70931                     .transition()
70932                     .duration(250)
70933                     .style('opacity', 1)
70934                     .on('end', function () { dispatch.call('change'); });
70935             }
70936
70937
70938             function hideLayer() {
70939                 throttledRedraw.cancel();
70940
70941                 layer
70942                     .transition()
70943                     .duration(250)
70944                     .style('opacity', 0)
70945                     .on('end', editOff);
70946             }
70947
70948
70949             function editOn() {
70950                 layer.style('display', 'block');
70951             }
70952
70953
70954             function editOff() {
70955                 layer.selectAll('.viewfield-group').remove();
70956                 layer.style('display', 'none');
70957             }
70958
70959
70960             function click(d) {
70961                 var service = getService();
70962                 if (!service) { return; }
70963
70964                 service
70965                     .selectImage(context, d)
70966                     .updateViewer(context, d)
70967                     .showViewer(context);
70968
70969                 context.map().centerEase(d.loc);
70970             }
70971
70972
70973             function mouseover(d) {
70974                 var service = getService();
70975                 if (service) { service.setStyles(context, d); }
70976             }
70977
70978
70979             function mouseout() {
70980                 var service = getService();
70981                 if (service) { service.setStyles(context, null); }
70982             }
70983
70984
70985             function transform(d) {
70986                 var t = svgPointTransform(projection)(d);
70987                 if (d.ca) {
70988                     t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
70989                 }
70990                 return t;
70991             }
70992
70993
70994             context.photos().on('change.openstreetcam_images', update);
70995
70996             function update() {
70997                 var viewer = context.container().select('.photoviewer');
70998                 var selected = viewer.empty() ? undefined : viewer.datum();
70999
71000                 var z = ~~context.map().zoom();
71001                 var showMarkers = (z >= minMarkerZoom);
71002                 var showViewfields = (z >= minViewfieldZoom);
71003
71004                 var service = getService();
71005                 var sequences = [];
71006                 var images = [];
71007
71008                 if (context.photos().showsFlat()) {
71009                     sequences = (service ? service.sequences(projection) : []);
71010                     images = (service && showMarkers ? service.images(projection) : []);
71011                 }
71012
71013                 var traces = layer.selectAll('.sequences').selectAll('.sequence')
71014                     .data(sequences, function(d) { return d.properties.key; });
71015
71016                 // exit
71017                 traces.exit()
71018                     .remove();
71019
71020                 // enter/update
71021                 traces = traces.enter()
71022                     .append('path')
71023                     .attr('class', 'sequence')
71024                     .merge(traces)
71025                     .attr('d', svgPath(projection).geojson);
71026
71027
71028                 var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
71029                     .data(images, function(d) { return d.key; });
71030
71031                 // exit
71032                 groups.exit()
71033                     .remove();
71034
71035                 // enter
71036                 var groupsEnter = groups.enter()
71037                     .append('g')
71038                     .attr('class', 'viewfield-group')
71039                     .on('mouseenter', mouseover)
71040                     .on('mouseleave', mouseout)
71041                     .on('click', click);
71042
71043                 groupsEnter
71044                     .append('g')
71045                     .attr('class', 'viewfield-scale');
71046
71047                 // update
71048                 var markers = groups
71049                     .merge(groupsEnter)
71050                     .sort(function(a, b) {
71051                         return (a === selected) ? 1
71052                             : (b === selected) ? -1
71053                             : b.loc[1] - a.loc[1];  // sort Y
71054                     })
71055                     .attr('transform', transform)
71056                     .select('.viewfield-scale');
71057
71058
71059                 markers.selectAll('circle')
71060                     .data([0])
71061                     .enter()
71062                     .append('circle')
71063                     .attr('dx', '0')
71064                     .attr('dy', '0')
71065                     .attr('r', '6');
71066
71067                 var viewfields = markers.selectAll('.viewfield')
71068                     .data(showViewfields ? [0] : []);
71069
71070                 viewfields.exit()
71071                     .remove();
71072
71073                 viewfields.enter()               // viewfields may or may not be drawn...
71074                     .insert('path', 'circle')    // but if they are, draw below the circles
71075                     .attr('class', 'viewfield')
71076                     .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
71077                     .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');
71078             }
71079
71080
71081             function drawImages(selection) {
71082                 var enabled = svgOpenstreetcamImages.enabled,
71083                     service = getService();
71084
71085                 layer = selection.selectAll('.layer-openstreetcam')
71086                     .data(service ? [0] : []);
71087
71088                 layer.exit()
71089                     .remove();
71090
71091                 var layerEnter = layer.enter()
71092                     .append('g')
71093                     .attr('class', 'layer-openstreetcam')
71094                     .style('display', enabled ? 'block' : 'none');
71095
71096                 layerEnter
71097                     .append('g')
71098                     .attr('class', 'sequences');
71099
71100                 layerEnter
71101                     .append('g')
71102                     .attr('class', 'markers');
71103
71104                 layer = layerEnter
71105                     .merge(layer);
71106
71107                 if (enabled) {
71108                     if (service && ~~context.map().zoom() >= minZoom) {
71109                         editOn();
71110                         update();
71111                         service.loadImages(projection);
71112                     } else {
71113                         editOff();
71114                     }
71115                 }
71116             }
71117
71118
71119             drawImages.enabled = function(_) {
71120                 if (!arguments.length) { return svgOpenstreetcamImages.enabled; }
71121                 svgOpenstreetcamImages.enabled = _;
71122                 if (svgOpenstreetcamImages.enabled) {
71123                     showLayer();
71124                 } else {
71125                     hideLayer();
71126                 }
71127                 dispatch.call('change');
71128                 return this;
71129             };
71130
71131
71132             drawImages.supported = function() {
71133                 return !!getService();
71134             };
71135
71136
71137             init();
71138             return drawImages;
71139         }
71140
71141         function svgOsm(projection, context, dispatch) {
71142             var enabled = true;
71143
71144
71145             function drawOsm(selection) {
71146                 selection.selectAll('.layer-osm')
71147                     .data(['covered', 'areas', 'lines', 'points', 'labels'])
71148                     .enter()
71149                     .append('g')
71150                     .attr('class', function(d) { return 'layer-osm ' + d; });
71151
71152                 selection.selectAll('.layer-osm.points').selectAll('.points-group')
71153                     .data(['points', 'midpoints', 'vertices', 'turns'])
71154                     .enter()
71155                     .append('g')
71156                     .attr('class', function(d) { return 'points-group ' + d; });
71157             }
71158
71159
71160             function showLayer() {
71161                 var layer = context.surface().selectAll('.data-layer.osm');
71162                 layer.interrupt();
71163
71164                 layer
71165                     .classed('disabled', false)
71166                     .style('opacity', 0)
71167                     .transition()
71168                     .duration(250)
71169                     .style('opacity', 1)
71170                     .on('end interrupt', function () {
71171                         dispatch.call('change');
71172                     });
71173             }
71174
71175
71176             function hideLayer() {
71177                 var layer = context.surface().selectAll('.data-layer.osm');
71178                 layer.interrupt();
71179
71180                 layer
71181                     .transition()
71182                     .duration(250)
71183                     .style('opacity', 0)
71184                     .on('end interrupt', function () {
71185                         layer.classed('disabled', true);
71186                         dispatch.call('change');
71187                     });
71188             }
71189
71190
71191             drawOsm.enabled = function(val) {
71192                 if (!arguments.length) { return enabled; }
71193                 enabled = val;
71194
71195                 if (enabled) {
71196                     showLayer();
71197                 } else {
71198                     hideLayer();
71199                 }
71200
71201                 dispatch.call('change');
71202                 return this;
71203             };
71204
71205
71206             return drawOsm;
71207         }
71208
71209         var _notesEnabled = false;
71210         var _osmService;
71211
71212
71213         function svgNotes(projection, context, dispatch$1) {
71214             if (!dispatch$1) { dispatch$1 = dispatch('change'); }
71215             var throttledRedraw = throttle(function () { dispatch$1.call('change'); }, 1000);
71216             var minZoom = 12;
71217             var touchLayer = select(null);
71218             var drawLayer = select(null);
71219             var _notesVisible = false;
71220
71221
71222             function markerPath(selection, klass) {
71223                 selection
71224                     .attr('class', klass)
71225                     .attr('transform', 'translate(-8, -22)')
71226                     .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');
71227             }
71228
71229
71230             // Loosely-coupled osm service for fetching notes.
71231             function getService() {
71232                 if (services.osm && !_osmService) {
71233                     _osmService = services.osm;
71234                     _osmService.on('loadedNotes', throttledRedraw);
71235                 } else if (!services.osm && _osmService) {
71236                     _osmService = null;
71237                 }
71238
71239                 return _osmService;
71240             }
71241
71242
71243             // Show the notes
71244             function editOn() {
71245                 if (!_notesVisible) {
71246                     _notesVisible = true;
71247                     drawLayer
71248                         .style('display', 'block');
71249                 }
71250             }
71251
71252
71253             // Immediately remove the notes and their touch targets
71254             function editOff() {
71255                 if (_notesVisible) {
71256                     _notesVisible = false;
71257                     drawLayer
71258                         .style('display', 'none');
71259                     drawLayer.selectAll('.note')
71260                         .remove();
71261                     touchLayer.selectAll('.note')
71262                         .remove();
71263                 }
71264             }
71265
71266
71267             // Enable the layer.  This shows the notes and transitions them to visible.
71268             function layerOn() {
71269                 editOn();
71270
71271                 drawLayer
71272                     .style('opacity', 0)
71273                     .transition()
71274                     .duration(250)
71275                     .style('opacity', 1)
71276                     .on('end interrupt', function () {
71277                         dispatch$1.call('change');
71278                     });
71279             }
71280
71281
71282             // Disable the layer.  This transitions the layer invisible and then hides the notes.
71283             function layerOff() {
71284                 throttledRedraw.cancel();
71285                 drawLayer.interrupt();
71286                 touchLayer.selectAll('.note')
71287                     .remove();
71288
71289                 drawLayer
71290                     .transition()
71291                     .duration(250)
71292                     .style('opacity', 0)
71293                     .on('end interrupt', function () {
71294                         editOff();
71295                         dispatch$1.call('change');
71296                     });
71297             }
71298
71299
71300             // Update the note markers
71301             function updateMarkers() {
71302                 if (!_notesVisible || !_notesEnabled) { return; }
71303
71304                 var service = getService();
71305                 var selectedID = context.selectedNoteID();
71306                 var data = (service ? service.notes(projection) : []);
71307                 var getTransform = svgPointTransform(projection);
71308
71309                 // Draw markers..
71310                 var notes = drawLayer.selectAll('.note')
71311                     .data(data, function(d) { return d.status + d.id; });
71312
71313                 // exit
71314                 notes.exit()
71315                     .remove();
71316
71317                 // enter
71318                 var notesEnter = notes.enter()
71319                     .append('g')
71320                     .attr('class', function(d) { return 'note note-' + d.id + ' ' + d.status; })
71321                     .classed('new', function(d) { return d.id < 0; });
71322
71323                 notesEnter
71324                     .append('ellipse')
71325                     .attr('cx', 0.5)
71326                     .attr('cy', 1)
71327                     .attr('rx', 6.5)
71328                     .attr('ry', 3)
71329                     .attr('class', 'stroke');
71330
71331                 notesEnter
71332                     .append('path')
71333                     .call(markerPath, 'shadow');
71334
71335                 notesEnter
71336                     .append('use')
71337                     .attr('class', 'note-fill')
71338                     .attr('width', '20px')
71339                     .attr('height', '20px')
71340                     .attr('x', '-8px')
71341                     .attr('y', '-22px')
71342                     .attr('xlink:href', '#iD-icon-note');
71343
71344                 notesEnter.selectAll('.icon-annotation')
71345                     .data(function(d) { return [d]; })
71346                     .enter()
71347                     .append('use')
71348                     .attr('class', 'icon-annotation')
71349                     .attr('width', '10px')
71350                     .attr('height', '10px')
71351                     .attr('x', '-3px')
71352                     .attr('y', '-19px')
71353                     .attr('xlink:href', function(d) {
71354                         return '#iD-icon-' + (d.id < 0 ? 'plus' : (d.status === 'open' ? 'close' : 'apply'));
71355                     });
71356
71357                 // update
71358                 notes
71359                     .merge(notesEnter)
71360                     .sort(sortY)
71361                     .classed('selected', function(d) {
71362                         var mode = context.mode();
71363                         var isMoving = mode && mode.id === 'drag-note';  // no shadows when dragging
71364                         return !isMoving && d.id === selectedID;
71365                     })
71366                     .attr('transform', getTransform);
71367
71368
71369                 // Draw targets..
71370                 if (touchLayer.empty()) { return; }
71371                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71372
71373                 var targets = touchLayer.selectAll('.note')
71374                     .data(data, function(d) { return d.id; });
71375
71376                 // exit
71377                 targets.exit()
71378                     .remove();
71379
71380                 // enter/update
71381                 targets.enter()
71382                     .append('rect')
71383                     .attr('width', '20px')
71384                     .attr('height', '20px')
71385                     .attr('x', '-8px')
71386                     .attr('y', '-22px')
71387                     .merge(targets)
71388                     .sort(sortY)
71389                     .attr('class', function(d) {
71390                         var newClass = (d.id < 0 ? 'new' : '');
71391                         return 'note target note-' + d.id + ' ' + fillClass + newClass;
71392                     })
71393                     .attr('transform', getTransform);
71394
71395
71396                 function sortY(a, b) {
71397                     return (a.id === selectedID) ? 1 : (b.id === selectedID) ? -1 : b.loc[1] - a.loc[1];
71398                 }
71399             }
71400
71401
71402             // Draw the notes layer and schedule loading notes and updating markers.
71403             function drawNotes(selection) {
71404                 var service = getService();
71405
71406                 var surface = context.surface();
71407                 if (surface && !surface.empty()) {
71408                     touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
71409                 }
71410
71411                 drawLayer = selection.selectAll('.layer-notes')
71412                     .data(service ? [0] : []);
71413
71414                 drawLayer.exit()
71415                     .remove();
71416
71417                 drawLayer = drawLayer.enter()
71418                     .append('g')
71419                     .attr('class', 'layer-notes')
71420                     .style('display', _notesEnabled ? 'block' : 'none')
71421                     .merge(drawLayer);
71422
71423                 if (_notesEnabled) {
71424                     if (service && ~~context.map().zoom() >= minZoom) {
71425                         editOn();
71426                         service.loadNotes(projection);
71427                         updateMarkers();
71428                     } else {
71429                         editOff();
71430                     }
71431                 }
71432             }
71433
71434
71435             // Toggles the layer on and off
71436             drawNotes.enabled = function(val) {
71437                 if (!arguments.length) { return _notesEnabled; }
71438
71439                 _notesEnabled = val;
71440                 if (_notesEnabled) {
71441                     layerOn();
71442                 } else {
71443                     layerOff();
71444                     if (context.selectedNoteID()) {
71445                         context.enter(modeBrowse(context));
71446                     }
71447                 }
71448
71449                 dispatch$1.call('change');
71450                 return this;
71451             };
71452
71453
71454             return drawNotes;
71455         }
71456
71457         function svgTouch() {
71458
71459             function drawTouch(selection) {
71460                 selection.selectAll('.layer-touch')
71461                     .data(['areas', 'lines', 'points', 'turns', 'markers'])
71462                     .enter()
71463                     .append('g')
71464                     .attr('class', function(d) { return 'layer-touch ' + d; });
71465             }
71466
71467             return drawTouch;
71468         }
71469
71470         function refresh(selection, node) {
71471             var cr = node.getBoundingClientRect();
71472             var prop = [cr.width, cr.height];
71473             selection.property('__dimensions__', prop);
71474             return prop;
71475         }
71476
71477         function utilGetDimensions(selection, force) {
71478             if (!selection || selection.empty()) {
71479                 return [0, 0];
71480             }
71481             var node = selection.node(),
71482                 cached = selection.property('__dimensions__');
71483             return (!cached || force) ? refresh(selection, node) : cached;
71484         }
71485
71486
71487         function utilSetDimensions(selection, dimensions) {
71488             if (!selection || selection.empty()) {
71489                 return selection;
71490             }
71491             var node = selection.node();
71492             if (dimensions === null) {
71493                 refresh(selection, node);
71494                 return selection;
71495             }
71496             return selection
71497                 .property('__dimensions__', [dimensions[0], dimensions[1]])
71498                 .attr('width', dimensions[0])
71499                 .attr('height', dimensions[1]);
71500         }
71501
71502         function svgLayers(projection, context) {
71503             var dispatch$1 = dispatch('change');
71504             var svg = select(null);
71505             var _layers = [
71506                 { id: 'osm', layer: svgOsm(projection, context, dispatch$1) },
71507                 { id: 'notes', layer: svgNotes(projection, context, dispatch$1) },
71508                 { id: 'data', layer: svgData(projection, context, dispatch$1) },
71509                 { id: 'keepRight', layer: svgKeepRight(projection, context, dispatch$1) },
71510                 { id: 'improveOSM', layer: svgImproveOSM(projection, context, dispatch$1) },
71511                 { id: 'osmose', layer: svgOsmose(projection, context, dispatch$1) },
71512                 { id: 'streetside', layer: svgStreetside(projection, context, dispatch$1)},
71513                 { id: 'mapillary', layer: svgMapillaryImages(projection, context, dispatch$1) },
71514                 { id: 'mapillary-map-features',  layer: svgMapillaryMapFeatures(projection, context, dispatch$1) },
71515                 { id: 'mapillary-signs',  layer: svgMapillarySigns(projection, context, dispatch$1) },
71516                 { id: 'openstreetcam', layer: svgOpenstreetcamImages(projection, context, dispatch$1) },
71517                 { id: 'debug', layer: svgDebug(projection, context) },
71518                 { id: 'geolocate', layer: svgGeolocate(projection) },
71519                 { id: 'touch', layer: svgTouch() }
71520             ];
71521
71522
71523             function drawLayers(selection) {
71524                 svg = selection.selectAll('.surface')
71525                     .data([0]);
71526
71527                 svg = svg.enter()
71528                     .append('svg')
71529                     .attr('class', 'surface')
71530                     .merge(svg);
71531
71532                 var defs = svg.selectAll('.surface-defs')
71533                     .data([0]);
71534
71535                 defs.enter()
71536                     .append('defs')
71537                     .attr('class', 'surface-defs');
71538
71539                 var groups = svg.selectAll('.data-layer')
71540                     .data(_layers);
71541
71542                 groups.exit()
71543                     .remove();
71544
71545                 groups.enter()
71546                     .append('g')
71547                     .attr('class', function(d) { return 'data-layer ' + d.id; })
71548                     .merge(groups)
71549                     .each(function(d) { select(this).call(d.layer); });
71550             }
71551
71552
71553             drawLayers.all = function() {
71554                 return _layers;
71555             };
71556
71557
71558             drawLayers.layer = function(id) {
71559                 var obj = _layers.find(function(o) { return o.id === id; });
71560                 return obj && obj.layer;
71561             };
71562
71563
71564             drawLayers.only = function(what) {
71565                 var arr = [].concat(what);
71566                 var all = _layers.map(function(layer) { return layer.id; });
71567                 return drawLayers.remove(utilArrayDifference(all, arr));
71568             };
71569
71570
71571             drawLayers.remove = function(what) {
71572                 var arr = [].concat(what);
71573                 arr.forEach(function(id) {
71574                     _layers = _layers.filter(function(o) { return o.id !== id; });
71575                 });
71576                 dispatch$1.call('change');
71577                 return this;
71578             };
71579
71580
71581             drawLayers.add = function(what) {
71582                 var arr = [].concat(what);
71583                 arr.forEach(function(obj) {
71584                     if ('id' in obj && 'layer' in obj) {
71585                         _layers.push(obj);
71586                     }
71587                 });
71588                 dispatch$1.call('change');
71589                 return this;
71590             };
71591
71592
71593             drawLayers.dimensions = function(val) {
71594                 if (!arguments.length) { return utilGetDimensions(svg); }
71595                 utilSetDimensions(svg, val);
71596                 return this;
71597             };
71598
71599
71600             return utilRebind(drawLayers, dispatch$1, 'on');
71601         }
71602
71603         function svgLines(projection, context) {
71604             var detected = utilDetect();
71605
71606             var highway_stack = {
71607                 motorway: 0,
71608                 motorway_link: 1,
71609                 trunk: 2,
71610                 trunk_link: 3,
71611                 primary: 4,
71612                 primary_link: 5,
71613                 secondary: 6,
71614                 tertiary: 7,
71615                 unclassified: 8,
71616                 residential: 9,
71617                 service: 10,
71618                 footway: 11
71619             };
71620
71621
71622             function drawTargets(selection, graph, entities, filter) {
71623                 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71624                 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
71625                 var getPath = svgPath(projection).geojson;
71626                 var activeID = context.activeID();
71627                 var base = context.history().base();
71628
71629                 // The targets and nopes will be MultiLineString sub-segments of the ways
71630                 var data = { targets: [], nopes: [] };
71631
71632                 entities.forEach(function(way) {
71633                     var features = svgSegmentWay(way, graph, activeID);
71634                     data.targets.push.apply(data.targets, features.passive);
71635                     data.nopes.push.apply(data.nopes, features.active);
71636                 });
71637
71638
71639                 // Targets allow hover and vertex snapping
71640                 var targetData = data.targets.filter(getPath);
71641                 var targets = selection.selectAll('.line.target-allowed')
71642                     .filter(function(d) { return filter(d.properties.entity); })
71643                     .data(targetData, function key(d) { return d.id; });
71644
71645                 // exit
71646                 targets.exit()
71647                     .remove();
71648
71649                 var segmentWasEdited = function(d) {
71650                     var wayID = d.properties.entity.id;
71651                     // if the whole line was edited, don't draw segment changes
71652                     if (!base.entities[wayID] ||
71653                         !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
71654                         return false;
71655                     }
71656                     return d.properties.nodes.some(function(n) {
71657                         return !base.entities[n.id] ||
71658                                !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
71659                     });
71660                 };
71661
71662                 // enter/update
71663                 targets.enter()
71664                     .append('path')
71665                     .merge(targets)
71666                     .attr('d', getPath)
71667                     .attr('class', function(d) {
71668                         return 'way line target target-allowed ' + targetClass + d.id;
71669                     })
71670                     .classed('segment-edited', segmentWasEdited);
71671
71672                 // NOPE
71673                 var nopeData = data.nopes.filter(getPath);
71674                 var nopes = selection.selectAll('.line.target-nope')
71675                     .filter(function(d) { return filter(d.properties.entity); })
71676                     .data(nopeData, function key(d) { return d.id; });
71677
71678                 // exit
71679                 nopes.exit()
71680                     .remove();
71681
71682                 // enter/update
71683                 nopes.enter()
71684                     .append('path')
71685                     .merge(nopes)
71686                     .attr('d', getPath)
71687                     .attr('class', function(d) {
71688                         return 'way line target target-nope ' + nopeClass + d.id;
71689                     })
71690                     .classed('segment-edited', segmentWasEdited);
71691             }
71692
71693
71694             function drawLines(selection, graph, entities, filter) {
71695                 var base = context.history().base();
71696
71697                 function waystack(a, b) {
71698                     var selected = context.selectedIDs();
71699                     var scoreA = selected.indexOf(a.id) !== -1 ? 20 : 0;
71700                     var scoreB = selected.indexOf(b.id) !== -1 ? 20 : 0;
71701
71702                     if (a.tags.highway) { scoreA -= highway_stack[a.tags.highway]; }
71703                     if (b.tags.highway) { scoreB -= highway_stack[b.tags.highway]; }
71704                     return scoreA - scoreB;
71705                 }
71706
71707
71708                 function drawLineGroup(selection, klass, isSelected) {
71709                     // Note: Don't add `.selected` class in draw modes
71710                     var mode = context.mode();
71711                     var isDrawing = mode && /^draw/.test(mode.id);
71712                     var selectedClass = (!isDrawing && isSelected) ? 'selected ' : '';
71713
71714                     var lines = selection
71715                         .selectAll('path')
71716                         .filter(filter)
71717                         .data(getPathData(isSelected), osmEntity.key);
71718
71719                     lines.exit()
71720                         .remove();
71721
71722                     // Optimization: Call expensive TagClasses only on enter selection. This
71723                     // works because osmEntity.key is defined to include the entity v attribute.
71724                     lines.enter()
71725                         .append('path')
71726                         .attr('class', function(d) {
71727
71728                             var prefix = 'way line';
71729
71730                             // if this line isn't styled by its own tags
71731                             if (!d.hasInterestingTags()) {
71732
71733                                 var parentRelations = graph.parentRelations(d);
71734                                 var parentMultipolygons = parentRelations.filter(function(relation) {
71735                                     return relation.isMultipolygon();
71736                                 });
71737
71738                                 // and if it's a member of at least one multipolygon relation
71739                                 if (parentMultipolygons.length > 0 &&
71740                                     // and only multipolygon relations
71741                                     parentRelations.length === parentMultipolygons.length) {
71742                                     // then fudge the classes to style this as an area edge
71743                                     prefix = 'relation area';
71744                                 }
71745                             }
71746
71747                             var oldMPClass = oldMultiPolygonOuters[d.id] ? 'old-multipolygon ' : '';
71748                             return prefix + ' ' + klass + ' ' + selectedClass + oldMPClass + d.id;
71749                         })
71750                         .classed('added', function(d) {
71751                             return !base.entities[d.id];
71752                         })
71753                         .classed('geometry-edited', function(d) {
71754                             return graph.entities[d.id] &&
71755                                 base.entities[d.id] &&
71756                                 !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
71757                         })
71758                         .classed('retagged', function(d) {
71759                             return graph.entities[d.id] &&
71760                                 base.entities[d.id] &&
71761                                 !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
71762                         })
71763                         .call(svgTagClasses())
71764                         .merge(lines)
71765                         .sort(waystack)
71766                         .attr('d', getPath)
71767                         .call(svgTagClasses().tags(svgRelationMemberTags(graph)));
71768
71769                     return selection;
71770                 }
71771
71772
71773                 function getPathData(isSelected) {
71774                     return function() {
71775                         var layer = this.parentNode.__data__;
71776                         var data = pathdata[layer] || [];
71777                         return data.filter(function(d) {
71778                             if (isSelected)
71779                                 { return context.selectedIDs().indexOf(d.id) !== -1; }
71780                             else
71781                                 { return context.selectedIDs().indexOf(d.id) === -1; }
71782                         });
71783                     };
71784                 }
71785
71786                 function addMarkers(layergroup, pathclass, groupclass, groupdata, marker) {
71787                     var markergroup = layergroup
71788                         .selectAll('g.' + groupclass)
71789                         .data([pathclass]);
71790
71791                     markergroup = markergroup.enter()
71792                         .append('g')
71793                         .attr('class', groupclass)
71794                         .merge(markergroup);
71795
71796                     var markers = markergroup
71797                         .selectAll('path')
71798                         .filter(filter)
71799                         .data(
71800                             function data() { return groupdata[this.parentNode.__data__] || []; },
71801                             function key(d) { return [d.id, d.index]; }
71802                         );
71803
71804                     markers.exit()
71805                         .remove();
71806
71807                     markers = markers.enter()
71808                         .append('path')
71809                         .attr('class', pathclass)
71810                         .merge(markers)
71811                         .attr('marker-mid', marker)
71812                         .attr('d', function(d) { return d.d; });
71813
71814                     if (detected.ie) {
71815                         markers.each(function() { this.parentNode.insertBefore(this, this); });
71816                     }
71817                 }
71818
71819
71820                 var getPath = svgPath(projection, graph);
71821                 var ways = [];
71822                 var onewaydata = {};
71823                 var sideddata = {};
71824                 var oldMultiPolygonOuters = {};
71825
71826                 for (var i = 0; i < entities.length; i++) {
71827                     var entity = entities[i];
71828                     var outer = osmOldMultipolygonOuterMember(entity, graph);
71829                     if (outer) {
71830                         ways.push(entity.mergeTags(outer.tags));
71831                         oldMultiPolygonOuters[outer.id] = true;
71832                     } else if (entity.geometry(graph) === 'line') {
71833                         ways.push(entity);
71834                     }
71835                 }
71836
71837                 ways = ways.filter(getPath);
71838                 var pathdata = utilArrayGroupBy(ways, function(way) { return way.layer(); });
71839
71840                 Object.keys(pathdata).forEach(function(k) {
71841                     var v = pathdata[k];
71842                     var onewayArr = v.filter(function(d) { return d.isOneWay(); });
71843                     var onewaySegments = svgMarkerSegments(
71844                         projection, graph, 35,
71845                         function shouldReverse(entity) { return entity.tags.oneway === '-1'; },
71846                         function bothDirections(entity) {
71847                             return entity.tags.oneway === 'reversible' || entity.tags.oneway === 'alternating';
71848                         }
71849                     );
71850                     onewaydata[k] = utilArrayFlatten(onewayArr.map(onewaySegments));
71851
71852                     var sidedArr = v.filter(function(d) { return d.isSided(); });
71853                     var sidedSegments = svgMarkerSegments(
71854                         projection, graph, 30,
71855                         function shouldReverse() { return false; },
71856                         function bothDirections() { return false; }
71857                     );
71858                     sideddata[k] = utilArrayFlatten(sidedArr.map(sidedSegments));
71859                 });
71860
71861
71862                 var covered = selection.selectAll('.layer-osm.covered');     // under areas
71863                 var uncovered = selection.selectAll('.layer-osm.lines');     // over areas
71864                 var touchLayer = selection.selectAll('.layer-touch.lines');
71865
71866                 // Draw lines..
71867                 [covered, uncovered].forEach(function(selection) {
71868                     var range = (selection === covered ? range$1(-10,0) : range$1(0,11));
71869                     var layergroup = selection
71870                         .selectAll('g.layergroup')
71871                         .data(range);
71872
71873                     layergroup = layergroup.enter()
71874                         .append('g')
71875                         .attr('class', function(d) { return 'layergroup layer' + String(d); })
71876                         .merge(layergroup);
71877
71878                     layergroup
71879                         .selectAll('g.linegroup')
71880                         .data(['shadow', 'casing', 'stroke', 'shadow-highlighted', 'casing-highlighted', 'stroke-highlighted'])
71881                         .enter()
71882                         .append('g')
71883                         .attr('class', function(d) { return 'linegroup line-' + d; });
71884
71885                     layergroup.selectAll('g.line-shadow')
71886                         .call(drawLineGroup, 'shadow', false);
71887                     layergroup.selectAll('g.line-casing')
71888                         .call(drawLineGroup, 'casing', false);
71889                     layergroup.selectAll('g.line-stroke')
71890                         .call(drawLineGroup, 'stroke', false);
71891
71892                     layergroup.selectAll('g.line-shadow-highlighted')
71893                         .call(drawLineGroup, 'shadow', true);
71894                     layergroup.selectAll('g.line-casing-highlighted')
71895                         .call(drawLineGroup, 'casing', true);
71896                     layergroup.selectAll('g.line-stroke-highlighted')
71897                         .call(drawLineGroup, 'stroke', true);
71898
71899                     addMarkers(layergroup, 'oneway', 'onewaygroup', onewaydata, 'url(#ideditor-oneway-marker)');
71900                     addMarkers(layergroup, 'sided', 'sidedgroup', sideddata,
71901                         function marker(d) {
71902                             var category = graph.entity(d.id).sidednessIdentifier();
71903                             return 'url(#ideditor-sided-marker-' + category + ')';
71904                         }
71905                     );
71906                 });
71907
71908                 // Draw touch targets..
71909                 touchLayer
71910                     .call(drawTargets, graph, ways, filter);
71911             }
71912
71913
71914             return drawLines;
71915         }
71916
71917         function svgMidpoints(projection, context) {
71918             var targetRadius = 8;
71919
71920             function drawTargets(selection, graph, entities, filter) {
71921                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71922                 var getTransform = svgPointTransform(projection).geojson;
71923
71924                 var data = entities.map(function(midpoint) {
71925                     return {
71926                         type: 'Feature',
71927                         id: midpoint.id,
71928                         properties: {
71929                             target: true,
71930                             entity: midpoint
71931                         },
71932                         geometry: {
71933                             type: 'Point',
71934                             coordinates: midpoint.loc
71935                         }
71936                     };
71937                 });
71938
71939                 var targets = selection.selectAll('.midpoint.target')
71940                     .filter(function(d) { return filter(d.properties.entity); })
71941                     .data(data, function key(d) { return d.id; });
71942
71943                 // exit
71944                 targets.exit()
71945                     .remove();
71946
71947                 // enter/update
71948                 targets.enter()
71949                     .append('circle')
71950                     .attr('r', targetRadius)
71951                     .merge(targets)
71952                     .attr('class', function(d) { return 'node midpoint target ' + fillClass + d.id; })
71953                     .attr('transform', getTransform);
71954             }
71955
71956
71957             function drawMidpoints(selection, graph, entities, filter, extent) {
71958                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.midpoints');
71959                 var touchLayer = selection.selectAll('.layer-touch.points');
71960
71961                 var mode = context.mode();
71962                 if ((mode && mode.id !== 'select') || !context.map().withinEditableZoom()) {
71963                     drawLayer.selectAll('.midpoint').remove();
71964                     touchLayer.selectAll('.midpoint.target').remove();
71965                     return;
71966                 }
71967
71968                 var poly = extent.polygon();
71969                 var midpoints = {};
71970
71971                 for (var i = 0; i < entities.length; i++) {
71972                     var entity = entities[i];
71973
71974                     if (entity.type !== 'way') { continue; }
71975                     if (!filter(entity)) { continue; }
71976                     if (context.selectedIDs().indexOf(entity.id) < 0) { continue; }
71977
71978                     var nodes = graph.childNodes(entity);
71979                     for (var j = 0; j < nodes.length - 1; j++) {
71980                         var a = nodes[j];
71981                         var b = nodes[j + 1];
71982                         var id = [a.id, b.id].sort().join('-');
71983
71984                         if (midpoints[id]) {
71985                             midpoints[id].parents.push(entity);
71986                         } else if (geoVecLength(projection(a.loc), projection(b.loc)) > 40) {
71987                             var point = geoVecInterp(a.loc, b.loc, 0.5);
71988                             var loc = null;
71989
71990                             if (extent.intersects(point)) {
71991                                 loc = point;
71992                             } else {
71993                                 for (var k = 0; k < 4; k++) {
71994                                     point = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]);
71995                                     if (point &&
71996                                         geoVecLength(projection(a.loc), projection(point)) > 20 &&
71997                                         geoVecLength(projection(b.loc), projection(point)) > 20)
71998                                     {
71999                                         loc = point;
72000                                         break;
72001                                     }
72002                                 }
72003                             }
72004
72005                             if (loc) {
72006                                 midpoints[id] = {
72007                                     type: 'midpoint',
72008                                     id: id,
72009                                     loc: loc,
72010                                     edge: [a.id, b.id],
72011                                     parents: [entity]
72012                                 };
72013                             }
72014                         }
72015                     }
72016                 }
72017
72018
72019                 function midpointFilter(d) {
72020                     if (midpoints[d.id])
72021                         { return true; }
72022
72023                     for (var i = 0; i < d.parents.length; i++) {
72024                         if (filter(d.parents[i])) {
72025                             return true;
72026                         }
72027                     }
72028
72029                     return false;
72030                 }
72031
72032
72033                 var groups = drawLayer.selectAll('.midpoint')
72034                     .filter(midpointFilter)
72035                     .data(Object.values(midpoints), function(d) { return d.id; });
72036
72037                 groups.exit()
72038                     .remove();
72039
72040                 var enter = groups.enter()
72041                     .insert('g', ':first-child')
72042                     .attr('class', 'midpoint');
72043
72044                 enter
72045                     .append('polygon')
72046                     .attr('points', '-6,8 10,0 -6,-8')
72047                     .attr('class', 'shadow');
72048
72049                 enter
72050                     .append('polygon')
72051                     .attr('points', '-3,4 5,0 -3,-4')
72052                     .attr('class', 'fill');
72053
72054                 groups = groups
72055                     .merge(enter)
72056                     .attr('transform', function(d) {
72057                         var translate = svgPointTransform(projection);
72058                         var a = graph.entity(d.edge[0]);
72059                         var b = graph.entity(d.edge[1]);
72060                         var angle = geoAngle(a, b, projection) * (180 / Math.PI);
72061                         return translate(d) + ' rotate(' + angle + ')';
72062                     })
72063                     .call(svgTagClasses().tags(
72064                         function(d) { return d.parents[0].tags; }
72065                     ));
72066
72067                 // Propagate data bindings.
72068                 groups.select('polygon.shadow');
72069                 groups.select('polygon.fill');
72070
72071
72072                 // Draw touch targets..
72073                 touchLayer
72074                     .call(drawTargets, graph, Object.values(midpoints), midpointFilter);
72075             }
72076
72077             return drawMidpoints;
72078         }
72079
72080         function svgPoints(projection, context) {
72081
72082             function markerPath(selection, klass) {
72083                 selection
72084                     .attr('class', klass)
72085                     .attr('transform', 'translate(-8, -23)')
72086                     .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');
72087             }
72088
72089             function sortY(a, b) {
72090                 return b.loc[1] - a.loc[1];
72091             }
72092
72093
72094             // Avoid exit/enter if we're just moving stuff around.
72095             // The node will get a new version but we only need to run the update selection.
72096             function fastEntityKey(d) {
72097                 var mode = context.mode();
72098                 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
72099                 return isMoving ? d.id : osmEntity.key(d);
72100             }
72101
72102
72103             function drawTargets(selection, graph, entities, filter) {
72104                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72105                 var getTransform = svgPointTransform(projection).geojson;
72106                 var activeID = context.activeID();
72107                 var data = [];
72108
72109                 entities.forEach(function(node) {
72110                     if (activeID === node.id) { return; }   // draw no target on the activeID
72111
72112                     data.push({
72113                         type: 'Feature',
72114                         id: node.id,
72115                         properties: {
72116                             target: true,
72117                             entity: node
72118                         },
72119                         geometry: node.asGeoJSON()
72120                     });
72121                 });
72122
72123                 var targets = selection.selectAll('.point.target')
72124                     .filter(function(d) { return filter(d.properties.entity); })
72125                     .data(data, function key(d) { return d.id; });
72126
72127                 // exit
72128                 targets.exit()
72129                     .remove();
72130
72131                 // enter/update
72132                 targets.enter()
72133                     .append('rect')
72134                     .attr('x', -10)
72135                     .attr('y', -26)
72136                     .attr('width', 20)
72137                     .attr('height', 30)
72138                     .merge(targets)
72139                     .attr('class', function(d) { return 'node point target ' + fillClass + d.id; })
72140                     .attr('transform', getTransform);
72141             }
72142
72143
72144             function drawPoints(selection, graph, entities, filter) {
72145                 var wireframe = context.surface().classed('fill-wireframe');
72146                 var zoom = geoScaleToZoom(projection.scale());
72147                 var base = context.history().base();
72148
72149                 // Points with a direction will render as vertices at higher zooms..
72150                 function renderAsPoint(entity) {
72151                     return entity.geometry(graph) === 'point' &&
72152                         !(zoom >= 18 && entity.directions(graph, projection).length);
72153                 }
72154
72155                 // All points will render as vertices in wireframe mode too..
72156                 var points = wireframe ? [] : entities.filter(renderAsPoint);
72157                 points.sort(sortY);
72158
72159
72160                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.points');
72161                 var touchLayer = selection.selectAll('.layer-touch.points');
72162
72163                 // Draw points..
72164                 var groups = drawLayer.selectAll('g.point')
72165                     .filter(filter)
72166                     .data(points, fastEntityKey);
72167
72168                 groups.exit()
72169                     .remove();
72170
72171                 var enter = groups.enter()
72172                     .append('g')
72173                     .attr('class', function(d) { return 'node point ' + d.id; })
72174                     .order();
72175
72176                 enter
72177                     .append('path')
72178                     .call(markerPath, 'shadow');
72179
72180                 enter
72181                     .append('ellipse')
72182                     .attr('cx', 0.5)
72183                     .attr('cy', 1)
72184                     .attr('rx', 6.5)
72185                     .attr('ry', 3)
72186                     .attr('class', 'stroke');
72187
72188                 enter
72189                     .append('path')
72190                     .call(markerPath, 'stroke');
72191
72192                 enter
72193                     .append('use')
72194                     .attr('transform', 'translate(-5, -19)')
72195                     .attr('class', 'icon')
72196                     .attr('width', '11px')
72197                     .attr('height', '11px');
72198
72199                 groups = groups
72200                     .merge(enter)
72201                     .attr('transform', svgPointTransform(projection))
72202                     .classed('added', function(d) {
72203                         return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
72204                     })
72205                     .classed('moved', function(d) {
72206                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
72207                     })
72208                     .classed('retagged', function(d) {
72209                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
72210                     })
72211                     .call(svgTagClasses());
72212
72213                 groups.select('.shadow');   // propagate bound data
72214                 groups.select('.stroke');   // propagate bound data
72215                 groups.select('.icon')      // propagate bound data
72216                     .attr('xlink:href', function(entity) {
72217                         var preset = _mainPresetIndex.match(entity, graph);
72218                         var picon = preset && preset.icon;
72219
72220                         if (!picon) {
72221                             return '';
72222                         } else {
72223                             var isMaki = /^maki-/.test(picon);
72224                             return '#' + picon + (isMaki ? '-11' : '');
72225                         }
72226                     });
72227
72228
72229                 // Draw touch targets..
72230                 touchLayer
72231                     .call(drawTargets, graph, points, filter);
72232             }
72233
72234
72235             return drawPoints;
72236         }
72237
72238         function svgTurns(projection, context) {
72239
72240             function icon(turn) {
72241                 var u = turn.u ? '-u' : '';
72242                 if (turn.no) { return '#iD-turn-no' + u; }
72243                 if (turn.only) { return '#iD-turn-only' + u; }
72244                 return '#iD-turn-yes' + u;
72245             }
72246
72247             function drawTurns(selection, graph, turns) {
72248
72249                 function turnTransform(d) {
72250                     var pxRadius = 50;
72251                     var toWay = graph.entity(d.to.way);
72252                     var toPoints = graph.childNodes(toWay)
72253                         .map(function (n) { return n.loc; })
72254                         .map(projection);
72255                     var toLength = geoPathLength(toPoints);
72256                     var mid = toLength / 2;    // midpoint of destination way
72257
72258                     var toNode = graph.entity(d.to.node);
72259                     var toVertex = graph.entity(d.to.vertex);
72260                     var a = geoAngle(toVertex, toNode, projection);
72261                     var o = projection(toVertex.loc);
72262                     var r = d.u ? 0                  // u-turn: no radius
72263                         : !toWay.__via ? pxRadius    // leaf way: put marker at pxRadius
72264                         : Math.min(mid, pxRadius);   // via way: prefer pxRadius, fallback to mid for very short ways
72265
72266                     return 'translate(' + (r * Math.cos(a) + o[0]) + ',' + (r * Math.sin(a) + o[1]) + ') ' +
72267                         'rotate(' + a * 180 / Math.PI + ')';
72268                 }
72269
72270
72271                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.turns');
72272                 var touchLayer = selection.selectAll('.layer-touch.turns');
72273
72274                 // Draw turns..
72275                 var groups = drawLayer.selectAll('g.turn')
72276                     .data(turns, function(d) { return d.key; });
72277
72278                 // exit
72279                 groups.exit()
72280                     .remove();
72281
72282                 // enter
72283                 var groupsEnter = groups.enter()
72284                     .append('g')
72285                     .attr('class', function(d) { return 'turn ' + d.key; });
72286
72287                 var turnsEnter = groupsEnter
72288                     .filter(function(d) { return !d.u; });
72289
72290                 turnsEnter.append('rect')
72291                     .attr('transform', 'translate(-22, -12)')
72292                     .attr('width', '44')
72293                     .attr('height', '24');
72294
72295                 turnsEnter.append('use')
72296                     .attr('transform', 'translate(-22, -12)')
72297                     .attr('width', '44')
72298                     .attr('height', '24');
72299
72300                 var uEnter = groupsEnter
72301                     .filter(function(d) { return d.u; });
72302
72303                 uEnter.append('circle')
72304                     .attr('r', '16');
72305
72306                 uEnter.append('use')
72307                     .attr('transform', 'translate(-16, -16)')
72308                     .attr('width', '32')
72309                     .attr('height', '32');
72310
72311                 // update
72312                 groups = groups
72313                     .merge(groupsEnter)
72314                     .attr('opacity', function(d) { return d.direct === false ? '0.7' : null; })
72315                     .attr('transform', turnTransform);
72316
72317                 groups.select('use')
72318                     .attr('xlink:href', icon);
72319
72320                 groups.select('rect');      // propagate bound data
72321                 groups.select('circle');    // propagate bound data
72322
72323
72324                 // Draw touch targets..
72325                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72326                 groups = touchLayer.selectAll('g.turn')
72327                     .data(turns, function(d) { return d.key; });
72328
72329                 // exit
72330                 groups.exit()
72331                     .remove();
72332
72333                 // enter
72334                 groupsEnter = groups.enter()
72335                     .append('g')
72336                     .attr('class', function(d) { return 'turn ' + d.key; });
72337
72338                 turnsEnter = groupsEnter
72339                     .filter(function(d) { return !d.u; });
72340
72341                 turnsEnter.append('rect')
72342                     .attr('class', 'target ' + fillClass)
72343                     .attr('transform', 'translate(-22, -12)')
72344                     .attr('width', '44')
72345                     .attr('height', '24');
72346
72347                 uEnter = groupsEnter
72348                     .filter(function(d) { return d.u; });
72349
72350                 uEnter.append('circle')
72351                     .attr('class', 'target ' + fillClass)
72352                     .attr('r', '16');
72353
72354                 // update
72355                 groups = groups
72356                     .merge(groupsEnter)
72357                     .attr('transform', turnTransform);
72358
72359                 groups.select('rect');      // propagate bound data
72360                 groups.select('circle');    // propagate bound data
72361
72362
72363                 return this;
72364             }
72365
72366             return drawTurns;
72367         }
72368
72369         function svgVertices(projection, context) {
72370             var radiuses = {
72371                 //       z16-, z17,   z18+,  w/icon
72372                 shadow: [6,    7.5,   7.5,   12],
72373                 stroke: [2.5,  3.5,   3.5,   8],
72374                 fill:   [1,    1.5,   1.5,   1.5]
72375             };
72376
72377             var _currHoverTarget;
72378             var _currPersistent = {};
72379             var _currHover = {};
72380             var _prevHover = {};
72381             var _currSelected = {};
72382             var _prevSelected = {};
72383             var _radii = {};
72384
72385
72386             function sortY(a, b) {
72387                 return b.loc[1] - a.loc[1];
72388             }
72389
72390             // Avoid exit/enter if we're just moving stuff around.
72391             // The node will get a new version but we only need to run the update selection.
72392             function fastEntityKey(d) {
72393                 var mode = context.mode();
72394                 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
72395                 return isMoving ? d.id : osmEntity.key(d);
72396             }
72397
72398
72399             function draw(selection, graph, vertices, sets, filter) {
72400                 sets = sets || { selected: {}, important: {}, hovered: {} };
72401
72402                 var icons = {};
72403                 var directions = {};
72404                 var wireframe = context.surface().classed('fill-wireframe');
72405                 var zoom = geoScaleToZoom(projection.scale());
72406                 var z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2);
72407                 var activeID = context.activeID();
72408                 var base = context.history().base();
72409
72410
72411                 function getIcon(d) {
72412                     // always check latest entity, as fastEntityKey avoids enter/exit now
72413                     var entity = graph.entity(d.id);
72414                     if (entity.id in icons) { return icons[entity.id]; }
72415
72416                     icons[entity.id] =
72417                         entity.hasInterestingTags() &&
72418                         _mainPresetIndex.match(entity, graph).icon;
72419
72420                     return icons[entity.id];
72421                 }
72422
72423
72424                 // memoize directions results, return false for empty arrays (for use in filter)
72425                 function getDirections(entity) {
72426                     if (entity.id in directions) { return directions[entity.id]; }
72427
72428                     var angles = entity.directions(graph, projection);
72429                     directions[entity.id] = angles.length ? angles : false;
72430                     return angles;
72431                 }
72432
72433
72434                 function updateAttributes(selection) {
72435                     ['shadow', 'stroke', 'fill'].forEach(function(klass) {
72436                         var rads = radiuses[klass];
72437                         selection.selectAll('.' + klass)
72438                             .each(function(entity) {
72439                                 var i = z && getIcon(entity);
72440                                 var r = rads[i ? 3 : z];
72441
72442                                 // slightly increase the size of unconnected endpoints #3775
72443                                 if (entity.id !== activeID && entity.isEndpoint(graph) && !entity.isConnected(graph)) {
72444                                     r += 1.5;
72445                                 }
72446
72447                                 if (klass === 'shadow') {   // remember this value, so we don't need to
72448                                     _radii[entity.id] = r;  // recompute it when we draw the touch targets
72449                                 }
72450
72451                                 select(this)
72452                                     .attr('r', r)
72453                                     .attr('visibility', (i && klass === 'fill') ? 'hidden' : null);
72454                             });
72455                     });
72456                 }
72457
72458                 vertices.sort(sortY);
72459
72460                 var groups = selection.selectAll('g.vertex')
72461                     .filter(filter)
72462                     .data(vertices, fastEntityKey);
72463
72464                 // exit
72465                 groups.exit()
72466                     .remove();
72467
72468                 // enter
72469                 var enter = groups.enter()
72470                     .append('g')
72471                     .attr('class', function(d) { return 'node vertex ' + d.id; })
72472                     .order();
72473
72474                 enter
72475                     .append('circle')
72476                     .attr('class', 'shadow');
72477
72478                 enter
72479                     .append('circle')
72480                     .attr('class', 'stroke');
72481
72482                 // Vertices with tags get a fill.
72483                 enter.filter(function(d) { return d.hasInterestingTags(); })
72484                     .append('circle')
72485                     .attr('class', 'fill');
72486
72487                 // update
72488                 groups = groups
72489                     .merge(enter)
72490                     .attr('transform', svgPointTransform(projection))
72491                     .classed('sibling', function(d) { return d.id in sets.selected; })
72492                     .classed('shared', function(d) { return graph.isShared(d); })
72493                     .classed('endpoint', function(d) { return d.isEndpoint(graph); })
72494                     .classed('added', function(d) {
72495                         return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
72496                     })
72497                     .classed('moved', function(d) {
72498                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
72499                     })
72500                     .classed('retagged', function(d) {
72501                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
72502                     })
72503                     .call(updateAttributes);
72504
72505                 // Vertices with icons get a `use`.
72506                 var iconUse = groups
72507                     .selectAll('.icon')
72508                     .data(function data(d) { return zoom >= 17 && getIcon(d) ? [d] : []; }, fastEntityKey);
72509
72510                 // exit
72511                 iconUse.exit()
72512                     .remove();
72513
72514                 // enter
72515                 iconUse.enter()
72516                     .append('use')
72517                     .attr('class', 'icon')
72518                     .attr('width', '11px')
72519                     .attr('height', '11px')
72520                     .attr('transform', 'translate(-5.5, -5.5)')
72521                     .attr('xlink:href', function(d) {
72522                         var picon = getIcon(d);
72523                         var isMaki = /^maki-/.test(picon);
72524                         return '#' + picon + (isMaki ? '-11' : '');
72525                     });
72526
72527
72528                 // Vertices with directions get viewfields
72529                 var dgroups = groups
72530                     .selectAll('.viewfieldgroup')
72531                     .data(function data(d) { return zoom >= 18 && getDirections(d) ? [d] : []; }, fastEntityKey);
72532
72533                 // exit
72534                 dgroups.exit()
72535                     .remove();
72536
72537                 // enter/update
72538                 dgroups = dgroups.enter()
72539                     .insert('g', '.shadow')
72540                     .attr('class', 'viewfieldgroup')
72541                     .merge(dgroups);
72542
72543                 var viewfields = dgroups.selectAll('.viewfield')
72544                     .data(getDirections, function key(d) { return osmEntity.key(d); });
72545
72546                 // exit
72547                 viewfields.exit()
72548                     .remove();
72549
72550                 // enter/update
72551                 viewfields.enter()
72552                     .append('path')
72553                     .attr('class', 'viewfield')
72554                     .attr('d', 'M0,0H0')
72555                     .merge(viewfields)
72556                     .attr('marker-start', 'url(#ideditor-viewfield-marker' + (wireframe ? '-wireframe' : '') + ')')
72557                     .attr('transform', function(d) { return 'rotate(' + d + ')'; });
72558             }
72559
72560
72561             function drawTargets(selection, graph, entities, filter) {
72562                 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72563                 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
72564                 var getTransform = svgPointTransform(projection).geojson;
72565                 var activeID = context.activeID();
72566                 var data = { targets: [], nopes: [] };
72567
72568                 entities.forEach(function(node) {
72569                     if (activeID === node.id) { return; }   // draw no target on the activeID
72570
72571                     var vertexType = svgPassiveVertex(node, graph, activeID);
72572                     if (vertexType !== 0) {     // passive or adjacent - allow to connect
72573                         data.targets.push({
72574                             type: 'Feature',
72575                             id: node.id,
72576                             properties: {
72577                                 target: true,
72578                                 entity: node
72579                             },
72580                             geometry: node.asGeoJSON()
72581                         });
72582                     } else {
72583                         data.nopes.push({
72584                             type: 'Feature',
72585                             id: node.id + '-nope',
72586                             properties: {
72587                                 nope: true,
72588                                 target: true,
72589                                 entity: node
72590                             },
72591                             geometry: node.asGeoJSON()
72592                         });
72593                     }
72594                 });
72595
72596                 // Targets allow hover and vertex snapping
72597                 var targets = selection.selectAll('.vertex.target-allowed')
72598                     .filter(function(d) { return filter(d.properties.entity); })
72599                     .data(data.targets, function key(d) { return d.id; });
72600
72601                 // exit
72602                 targets.exit()
72603                     .remove();
72604
72605                 // enter/update
72606                 targets.enter()
72607                     .append('circle')
72608                     .attr('r', function(d) {
72609                         return _radii[d.id]
72610                           || radiuses.shadow[3];
72611                     })
72612                     .merge(targets)
72613                     .attr('class', function(d) {
72614                         return 'node vertex target target-allowed '
72615                         + targetClass + d.id;
72616                     })
72617                     .attr('transform', getTransform);
72618
72619
72620                 // NOPE
72621                 var nopes = selection.selectAll('.vertex.target-nope')
72622                     .filter(function(d) { return filter(d.properties.entity); })
72623                     .data(data.nopes, function key(d) { return d.id; });
72624
72625                 // exit
72626                 nopes.exit()
72627                     .remove();
72628
72629                 // enter/update
72630                 nopes.enter()
72631                     .append('circle')
72632                     .attr('r', function(d) { return (_radii[d.properties.entity.id] || radiuses.shadow[3]); })
72633                     .merge(nopes)
72634                     .attr('class', function(d) { return 'node vertex target target-nope ' + nopeClass + d.id; })
72635                     .attr('transform', getTransform);
72636             }
72637
72638
72639             // Points can also render as vertices:
72640             // 1. in wireframe mode or
72641             // 2. at higher zooms if they have a direction
72642             function renderAsVertex(entity, graph, wireframe, zoom) {
72643                 var geometry = entity.geometry(graph);
72644                 return geometry === 'vertex' || (geometry === 'point' && (
72645                     wireframe || (zoom >= 18 && entity.directions(graph, projection).length)
72646                 ));
72647             }
72648
72649
72650             function isEditedNode(node, base, head) {
72651                 var baseNode = base.entities[node.id];
72652                 var headNode = head.entities[node.id];
72653                 return !headNode ||
72654                     !baseNode ||
72655                     !fastDeepEqual(headNode.tags, baseNode.tags) ||
72656                     !fastDeepEqual(headNode.loc, baseNode.loc);
72657             }
72658
72659
72660             function getSiblingAndChildVertices(ids, graph, wireframe, zoom) {
72661                 var results = {};
72662
72663                 var seenIds = {};
72664
72665                 function addChildVertices(entity) {
72666
72667                     // avoid redunant work and infinite recursion of circular relations
72668                     if (seenIds[entity.id]) { return; }
72669                     seenIds[entity.id] = true;
72670
72671                     var geometry = entity.geometry(graph);
72672                     if (!context.features().isHiddenFeature(entity, graph, geometry)) {
72673                         var i;
72674                         if (entity.type === 'way') {
72675                             for (i = 0; i < entity.nodes.length; i++) {
72676                                 var child = graph.hasEntity(entity.nodes[i]);
72677                                 if (child) {
72678                                     addChildVertices(child);
72679                                 }
72680                             }
72681                         } else if (entity.type === 'relation') {
72682                             for (i = 0; i < entity.members.length; i++) {
72683                                 var member = graph.hasEntity(entity.members[i].id);
72684                                 if (member) {
72685                                     addChildVertices(member);
72686                                 }
72687                             }
72688                         } else if (renderAsVertex(entity, graph, wireframe, zoom)) {
72689                             results[entity.id] = entity;
72690                         }
72691                     }
72692                 }
72693
72694                 ids.forEach(function(id) {
72695                     var entity = graph.hasEntity(id);
72696                     if (!entity) { return; }
72697
72698                     if (entity.type === 'node') {
72699                         if (renderAsVertex(entity, graph, wireframe, zoom)) {
72700                             results[entity.id] = entity;
72701                             graph.parentWays(entity).forEach(function(entity) {
72702                                 addChildVertices(entity);
72703                             });
72704                         }
72705                     } else {  // way, relation
72706                         addChildVertices(entity);
72707                     }
72708                 });
72709
72710                 return results;
72711             }
72712
72713
72714             function drawVertices(selection, graph, entities, filter, extent, fullRedraw) {
72715                 var wireframe = context.surface().classed('fill-wireframe');
72716                 var visualDiff = context.surface().classed('highlight-edited');
72717                 var zoom = geoScaleToZoom(projection.scale());
72718                 var mode = context.mode();
72719                 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
72720                 var base = context.history().base();
72721
72722                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.vertices');
72723                 var touchLayer = selection.selectAll('.layer-touch.points');
72724
72725                 if (fullRedraw) {
72726                     _currPersistent = {};
72727                     _radii = {};
72728                 }
72729
72730                 // Collect important vertices from the `entities` list..
72731                 // (during a paritial redraw, it will not contain everything)
72732                 for (var i = 0; i < entities.length; i++) {
72733                     var entity = entities[i];
72734                     var geometry = entity.geometry(graph);
72735                     var keep = false;
72736
72737                     // a point that looks like a vertex..
72738                     if ((geometry === 'point') && renderAsVertex(entity, graph, wireframe, zoom)) {
72739                         _currPersistent[entity.id] = entity;
72740                         keep = true;
72741
72742                     // a vertex of some importance..
72743                     } else if (geometry === 'vertex' &&
72744                         (entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph)
72745                         || (visualDiff && isEditedNode(entity, base, graph)))) {
72746                         _currPersistent[entity.id] = entity;
72747                         keep = true;
72748                     }
72749
72750                     // whatever this is, it's not a persistent vertex..
72751                     if (!keep && !fullRedraw) {
72752                         delete _currPersistent[entity.id];
72753                     }
72754                 }
72755
72756                 // 3 sets of vertices to consider:
72757                 var sets = {
72758                     persistent: _currPersistent,  // persistent = important vertices (render always)
72759                     selected: _currSelected,      // selected + siblings of selected (render always)
72760                     hovered: _currHover           // hovered + siblings of hovered (render only in draw modes)
72761                 };
72762
72763                 var all = Object.assign({}, (isMoving ? _currHover : {}), _currSelected, _currPersistent);
72764
72765                 // Draw the vertices..
72766                 // The filter function controls the scope of what objects d3 will touch (exit/enter/update)
72767                 // Adjust the filter function to expand the scope beyond whatever entities were passed in.
72768                 var filterRendered = function(d) {
72769                     return d.id in _currPersistent || d.id in _currSelected || d.id in _currHover || filter(d);
72770                 };
72771                 drawLayer
72772                     .call(draw, graph, currentVisible(all), sets, filterRendered);
72773
72774                 // Draw touch targets..
72775                 // When drawing, render all targets (not just those affected by a partial redraw)
72776                 var filterTouch = function(d) {
72777                     return isMoving ? true : filterRendered(d);
72778                 };
72779                 touchLayer
72780                     .call(drawTargets, graph, currentVisible(all), filterTouch);
72781
72782
72783                 function currentVisible(which) {
72784                     return Object.keys(which)
72785                         .map(graph.hasEntity, graph)     // the current version of this entity
72786                         .filter(function (entity) { return entity && entity.intersects(extent, graph); });
72787                 }
72788             }
72789
72790
72791             // partial redraw - only update the selected items..
72792             drawVertices.drawSelected = function(selection, graph, extent) {
72793                 var wireframe = context.surface().classed('fill-wireframe');
72794                 var zoom = geoScaleToZoom(projection.scale());
72795
72796                 _prevSelected = _currSelected || {};
72797                 if (context.map().isInWideSelection()) {
72798                     _currSelected = {};
72799                     context.selectedIDs().forEach(function(id) {
72800                         var entity = graph.hasEntity(id);
72801                         if (!entity) { return; }
72802
72803                         if (entity.type === 'node') {
72804                             if (renderAsVertex(entity, graph, wireframe, zoom)) {
72805                                 _currSelected[entity.id] = entity;
72806                             }
72807                         }
72808                     });
72809
72810                 } else {
72811                     _currSelected = getSiblingAndChildVertices(context.selectedIDs(), graph, wireframe, zoom);
72812                 }
72813
72814                 // note that drawVertices will add `_currSelected` automatically if needed..
72815                 var filter = function(d) { return d.id in _prevSelected; };
72816                 drawVertices(selection, graph, Object.values(_prevSelected), filter, extent, false);
72817             };
72818
72819
72820             // partial redraw - only update the hovered items..
72821             drawVertices.drawHover = function(selection, graph, target, extent) {
72822                 if (target === _currHoverTarget) { return; }  // continue only if something changed
72823
72824                 var wireframe = context.surface().classed('fill-wireframe');
72825                 var zoom = geoScaleToZoom(projection.scale());
72826
72827                 _prevHover = _currHover || {};
72828                 _currHoverTarget = target;
72829                 var entity = target && target.properties && target.properties.entity;
72830
72831                 if (entity) {
72832                     _currHover = getSiblingAndChildVertices([entity.id], graph, wireframe, zoom);
72833                 } else {
72834                     _currHover = {};
72835                 }
72836
72837                 // note that drawVertices will add `_currHover` automatically if needed..
72838                 var filter = function(d) { return d.id in _prevHover; };
72839                 drawVertices(selection, graph, Object.values(_prevHover), filter, extent, false);
72840             };
72841
72842             return drawVertices;
72843         }
72844
72845         function utilBindOnce(target, type, listener, capture) {
72846             var typeOnce = type + '.once';
72847             function one() {
72848                 target.on(typeOnce, null);
72849                 listener.apply(this, arguments);
72850             }
72851             target.on(typeOnce, one, capture);
72852             return this;
72853         }
72854
72855         // Adapted from d3-zoom to handle pointer events.
72856
72857         // Ignore right-click, since that should open the context menu.
72858         function defaultFilter$2() {
72859           return !event.ctrlKey && !event.button;
72860         }
72861
72862         function defaultExtent$1() {
72863           var e = this;
72864           if (e instanceof SVGElement) {
72865             e = e.ownerSVGElement || e;
72866             if (e.hasAttribute('viewBox')) {
72867               e = e.viewBox.baseVal;
72868               return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
72869             }
72870             return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
72871           }
72872           return [[0, 0], [e.clientWidth, e.clientHeight]];
72873         }
72874
72875         function defaultWheelDelta$1() {
72876           return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002);
72877         }
72878
72879         function defaultConstrain$1(transform, extent, translateExtent) {
72880           var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
72881               dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
72882               dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
72883               dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
72884           return transform.translate(
72885             dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),
72886             dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)
72887           );
72888         }
72889
72890         function utilZoomPan() {
72891           var filter = defaultFilter$2,
72892               extent = defaultExtent$1,
72893               constrain = defaultConstrain$1,
72894               wheelDelta = defaultWheelDelta$1,
72895               scaleExtent = [0, Infinity],
72896               translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
72897               interpolate = interpolateZoom,
72898               listeners = dispatch('start', 'zoom', 'end'),
72899               _wheelDelay = 150,
72900               _transform = identity$2,
72901               _activeGesture;
72902
72903           function zoom(selection) {
72904             selection
72905                 .on('pointerdown.zoom', pointerdown)
72906                 .on('wheel.zoom', wheeled)
72907                 .style('touch-action', 'none')
72908                 .style('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
72909
72910             select(window)
72911                 .on('pointermove.zoompan', pointermove)
72912                 .on('pointerup.zoompan pointercancel.zoompan', pointerup);
72913           }
72914
72915           zoom.transform = function(collection, transform, point) {
72916             var selection = collection.selection ? collection.selection() : collection;
72917             if (collection !== selection) {
72918               schedule(collection, transform, point);
72919             } else {
72920               selection.interrupt().each(function() {
72921                 gesture(this, arguments)
72922                     .start()
72923                     .zoom(null, typeof transform === 'function' ? transform.apply(this, arguments) : transform)
72924                     .end();
72925               });
72926             }
72927           };
72928
72929           zoom.scaleBy = function(selection, k, p) {
72930             zoom.scaleTo(selection, function() {
72931               var k0 = _transform.k,
72932                   k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
72933               return k0 * k1;
72934             }, p);
72935           };
72936
72937           zoom.scaleTo = function(selection, k, p) {
72938             zoom.transform(selection, function() {
72939               var e = extent.apply(this, arguments),
72940                   t0 = _transform,
72941                   p0 = p == null ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p,
72942                   p1 = t0.invert(p0),
72943                   k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
72944               return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
72945             }, p);
72946           };
72947
72948           zoom.translateBy = function(selection, x, y) {
72949             zoom.transform(selection, function() {
72950               return constrain(_transform.translate(
72951                 typeof x === 'function' ? x.apply(this, arguments) : x,
72952                 typeof y === 'function' ? y.apply(this, arguments) : y
72953               ), extent.apply(this, arguments), translateExtent);
72954             });
72955           };
72956
72957           zoom.translateTo = function(selection, x, y, p) {
72958             zoom.transform(selection, function() {
72959               var e = extent.apply(this, arguments),
72960                   t = _transform,
72961                   p0 = p == null ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p;
72962               return constrain(identity$2.translate(p0[0], p0[1]).scale(t.k).translate(
72963                 typeof x === 'function' ? -x.apply(this, arguments) : -x,
72964                 typeof y === 'function' ? -y.apply(this, arguments) : -y
72965               ), e, translateExtent);
72966             }, p);
72967           };
72968
72969           function scale(transform, k) {
72970             k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
72971             return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
72972           }
72973
72974           function translate(transform, p0, p1) {
72975             var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k;
72976             return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
72977           }
72978
72979           function centroid(extent) {
72980             return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
72981           }
72982
72983           function schedule(transition, transform, point) {
72984             transition
72985                 .on('start.zoom', function() { gesture(this, arguments).start(); })
72986                 .on('interrupt.zoom end.zoom', function() { gesture(this, arguments).end(); })
72987                 .tween('zoom', function() {
72988                   var that = this,
72989                       args = arguments,
72990                       g = gesture(that, args),
72991                       e = extent.apply(that, args),
72992                       p = point == null ? centroid(e) : typeof point === 'function' ? point.apply(that, args) : point,
72993                       w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
72994                       a = _transform,
72995                       b = typeof transform === 'function' ? transform.apply(that, args) : transform,
72996                       i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
72997                   return function(t) {
72998                     if (t === 1) { t = b; } // Avoid rounding error on end.
72999                     else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); }
73000                     g.zoom(null, t);
73001                   };
73002                 });
73003           }
73004
73005           function gesture(that, args, clean) {
73006             return (!clean && _activeGesture) || new Gesture(that, args);
73007           }
73008
73009           function Gesture(that, args) {
73010             this.that = that;
73011             this.args = args;
73012             this.active = 0;
73013             this.extent = extent.apply(that, args);
73014           }
73015
73016           Gesture.prototype = {
73017             start: function() {
73018               if (++this.active === 1) {
73019                 _activeGesture = this;
73020                 this.emit('start');
73021               }
73022               return this;
73023             },
73024             zoom: function(key, transform) {
73025               if (this.mouse && key !== 'mouse') { this.mouse[1] = transform.invert(this.mouse[0]); }
73026               if (this.pointer0 && key !== 'touch') { this.pointer0[1] = transform.invert(this.pointer0[0]); }
73027               if (this.pointer1 && key !== 'touch') { this.pointer1[1] = transform.invert(this.pointer1[0]); }
73028               _transform = transform;
73029               this.emit('zoom');
73030               return this;
73031             },
73032             end: function() {
73033               if (--this.active === 0) {
73034                 _activeGesture = null;
73035                 this.emit('end');
73036               }
73037               return this;
73038             },
73039             emit: function(type) {
73040               customEvent(new ZoomEvent(zoom, type, _transform), listeners.apply, listeners, [type, this.that, this.args]);
73041             }
73042           };
73043
73044           function wheeled() {
73045             if (!filter.apply(this, arguments)) { return; }
73046             var g = gesture(this, arguments),
73047                 t = _transform,
73048                 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
73049                 p = utilFastMouse(this)(event);
73050
73051             // If the mouse is in the same location as before, reuse it.
73052             // If there were recent wheel events, reset the wheel idle timeout.
73053             if (g.wheel) {
73054               if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
73055                 g.mouse[1] = t.invert(g.mouse[0] = p);
73056               }
73057               clearTimeout(g.wheel);
73058
73059             // Otherwise, capture the mouse point and location at the start.
73060             } else {
73061               g.mouse = [p, t.invert(p)];
73062               interrupt(this);
73063               g.start();
73064             }
73065
73066             event.preventDefault();
73067             event.stopImmediatePropagation();
73068             g.wheel = setTimeout(wheelidled, _wheelDelay);
73069             g.zoom('mouse', constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
73070
73071             function wheelidled() {
73072               g.wheel = null;
73073               g.end();
73074             }
73075           }
73076
73077           var _downPointerIDs = new Set();
73078           var _pointerLocGetter;
73079
73080           function pointerdown() {
73081             _downPointerIDs.add(event.pointerId);
73082
73083             if (!filter.apply(this, arguments)) { return; }
73084
73085             var g = gesture(this, arguments, _downPointerIDs.size === 1);
73086             var started;
73087
73088             event.stopImmediatePropagation();
73089             _pointerLocGetter = utilFastMouse(this);
73090             var loc = _pointerLocGetter(event);
73091             var p = [loc, _transform.invert(loc), event.pointerId];
73092             if (!g.pointer0) {
73093                g.pointer0 = p;
73094                started = true;
73095
73096             } else if (!g.pointer1 && g.pointer0[2] !== p[2]) {
73097                g.pointer1 = p;
73098             }
73099
73100             if (started) {
73101               interrupt(this);
73102               g.start();
73103             }
73104           }
73105
73106           function pointermove() {
73107             if (!_downPointerIDs.has(event.pointerId)) { return; }
73108
73109             if (!_activeGesture || !_pointerLocGetter) { return; }
73110
73111             var g = gesture(this, arguments);
73112
73113             var isPointer0 = g.pointer0 && g.pointer0[2] === event.pointerId;
73114             var isPointer1 = !isPointer0 && g.pointer1 && g.pointer1[2] === event.pointerId;
73115
73116             if ((isPointer0 || isPointer1) && 'buttons' in event && !event.buttons) {
73117               // The pointer went up without ending the gesture somehow, e.g.
73118               // a down mouse was moved off the map and released. End it here.
73119               if (g.pointer0) { _downPointerIDs.delete(g.pointer0[2]); }
73120               if (g.pointer1) { _downPointerIDs.delete(g.pointer1[2]); }
73121               g.end();
73122               return;
73123             }
73124
73125             event.preventDefault();
73126             event.stopImmediatePropagation();
73127
73128             var loc = _pointerLocGetter(event);
73129             var t, p, l;
73130
73131             if (isPointer0) { g.pointer0[0] = loc; }
73132             else if (isPointer1) { g.pointer1[0] = loc; }
73133
73134             t = _transform;
73135             if (g.pointer1) {
73136               var p0 = g.pointer0[0], l0 = g.pointer0[1],
73137                   p1 = g.pointer1[0], l1 = g.pointer1[1],
73138                   dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
73139                   dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
73140               t = scale(t, Math.sqrt(dp / dl));
73141               p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
73142               l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
73143             } else if (g.pointer0) {
73144               p = g.pointer0[0];
73145               l = g.pointer0[1];
73146             }
73147             else { return; }
73148             g.zoom('touch', constrain(translate(t, p, l), g.extent, translateExtent));
73149           }
73150
73151           function pointerup() {
73152             if (!_downPointerIDs.has(event.pointerId)) { return; }
73153
73154             _downPointerIDs.delete(event.pointerId);
73155
73156             if (!_activeGesture) { return; }
73157
73158             var g = gesture(this, arguments);
73159
73160             event.stopImmediatePropagation();
73161
73162             if (g.pointer0 && g.pointer0[2] === event.pointerId) { delete g.pointer0; }
73163             else if (g.pointer1 && g.pointer1[2] === event.pointerId) { delete g.pointer1; }
73164
73165             if (g.pointer1 && !g.pointer0) {
73166               g.pointer0 = g.pointer1;
73167               delete g.pointer1;
73168             }
73169             if (g.pointer0) { g.pointer0[1] = _transform.invert(g.pointer0[0]); }
73170             else {
73171               g.end();
73172             }
73173           }
73174
73175           zoom.wheelDelta = function(_) {
73176             return arguments.length ? (wheelDelta = utilFunctor(+_), zoom) : wheelDelta;
73177           };
73178
73179           zoom.filter = function(_) {
73180             return arguments.length ? (filter = utilFunctor(!!_), zoom) : filter;
73181           };
73182
73183           zoom.extent = function(_) {
73184             return arguments.length ? (extent = utilFunctor([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
73185           };
73186
73187           zoom.scaleExtent = function(_) {
73188             return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
73189           };
73190
73191           zoom.translateExtent = function(_) {
73192             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]]];
73193           };
73194
73195           zoom.constrain = function(_) {
73196             return arguments.length ? (constrain = _, zoom) : constrain;
73197           };
73198
73199           zoom.interpolate = function(_) {
73200             return arguments.length ? (interpolate = _, zoom) : interpolate;
73201           };
73202
73203           zoom._transform = function(_) {
73204             return arguments.length ? (_transform = _, zoom) : _transform;
73205           };
73206
73207           zoom.on = function() {
73208             var value = listeners.on.apply(listeners, arguments);
73209             return value === listeners ? zoom : value;
73210           };
73211
73212           return zoom;
73213         }
73214
73215         // A custom double-click / double-tap event detector that works on touch devices
73216         // if pointer events are supported. Falls back to default `dblclick` event.
73217         function utilDoubleUp() {
73218
73219             var dispatch$1 = dispatch('doubleUp');
73220
73221             var _maxTimespan = 500; // milliseconds
73222             var _maxDistance = 20; // web pixels; be somewhat generous to account for touch devices
73223             var _pointer; // object representing the pointer that could trigger double up
73224
73225             function pointerIsValidFor(loc) {
73226                 // second pointerup must occur within a small timeframe after the first pointerdown
73227                 return new Date().getTime() - _pointer.startTime <= _maxTimespan &&
73228                     // all pointer events must occur within a small distance of the first pointerdown
73229                     geoVecLength(_pointer.startLoc, loc) <= _maxDistance;
73230             }
73231
73232             function pointerdown() {
73233
73234                 // ignore right-click
73235                 if (event.ctrlKey || event.button === 2) { return; }
73236
73237                 var loc = [event.clientX, event.clientY];
73238
73239                 // Don't rely on pointerId here since it can change between pointerdown
73240                 // events on touch devices
73241                 if (_pointer && !pointerIsValidFor(loc)) {
73242                     // if this pointer is no longer valid, clear it so another can be started
73243                     _pointer = undefined;
73244                 }
73245
73246                 if (!_pointer) {
73247                     _pointer = {
73248                         startLoc: loc,
73249                         startTime: new Date().getTime(),
73250                         upCount: 0,
73251                         pointerId: event.pointerId
73252                     };
73253                 } else { // double down
73254                     _pointer.pointerId = event.pointerId;
73255                 }
73256             }
73257
73258             function pointerup() {
73259
73260                 // ignore right-click
73261                 if (event.ctrlKey || event.button === 2) { return; }
73262
73263                 if (!_pointer || _pointer.pointerId !== event.pointerId) { return; }
73264
73265                 _pointer.upCount += 1;
73266
73267                 if (_pointer.upCount === 2) { // double up!
73268                     var loc = [event.clientX, event.clientY];
73269                     if (pointerIsValidFor(loc)) {
73270                         var locInThis = utilFastMouse(this)(event);
73271                         dispatch$1.call('doubleUp', this, locInThis);
73272                     }
73273                     // clear the pointer info in any case
73274                     _pointer = undefined;
73275                 }
73276             }
73277
73278             function doubleUp(selection) {
73279                 if ('PointerEvent' in window) {
73280                     // dblclick isn't well supported on touch devices so manually use
73281                     // pointer events if they're available
73282                     selection
73283                         .on('pointerdown.doubleUp', pointerdown)
73284                         .on('pointerup.doubleUp', pointerup);
73285                 } else {
73286                     // fallback to dblclick
73287                     selection
73288                         .on('dblclick.doubleUp', function() {
73289                             dispatch$1.call('doubleUp', this, utilFastMouse(this)(event));
73290                         });
73291                 }
73292             }
73293
73294             doubleUp.off = function(selection) {
73295                 selection
73296                     .on('pointerdown.doubleUp', null)
73297                     .on('pointerup.doubleUp', null)
73298                     .on('dblclick.doubleUp', null);
73299             };
73300
73301             return utilRebind(doubleUp, dispatch$1, 'on');
73302         }
73303
73304         // constants
73305         var TILESIZE = 256;
73306         var minZoom = 2;
73307         var maxZoom = 24;
73308         var kMin = geoZoomToScale(minZoom, TILESIZE);
73309         var kMax = geoZoomToScale(maxZoom, TILESIZE);
73310
73311         function clamp(num, min, max) {
73312             return Math.max(min, Math.min(num, max));
73313         }
73314
73315
73316         function rendererMap(context) {
73317             var dispatch$1 = dispatch(
73318                 'move', 'drawn',
73319                 'crossEditableZoom', 'hitMinZoom',
73320                 'changeHighlighting', 'changeAreaFill'
73321             );
73322             var projection = context.projection;
73323             var curtainProjection = context.curtainProjection;
73324             var drawLayers;
73325             var drawPoints;
73326             var drawVertices;
73327             var drawLines;
73328             var drawAreas;
73329             var drawMidpoints;
73330             var drawLabels;
73331
73332             var _selection = select(null);
73333             var supersurface = select(null);
73334             var wrapper = select(null);
73335             var surface = select(null);
73336
73337             var _dimensions = [1, 1];
73338             var _dblClickZoomEnabled = true;
73339             var _redrawEnabled = true;
73340             var _gestureTransformStart;
73341             var _transformStart = projection.transform();
73342             var _transformLast;
73343             var _isTransformed = false;
73344             var _minzoom = 0;
73345             var _getMouseCoords;
73346             var _lastPointerEvent;
73347             var _lastWithinEditableZoom;
73348
73349             // whether a pointerdown event started the zoom
73350             var _pointerDown = false;
73351
73352             // use pointer events on supported platforms; fallback to mouse events
73353             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
73354
73355             // use pointer event interaction if supported; fallback to touch/mouse events in d3-zoom
73356             var _zoomerPannerFunction = 'PointerEvent' in window ? utilZoomPan : d3_zoom;
73357
73358             var _zoomerPanner = _zoomerPannerFunction()
73359                 .scaleExtent([kMin, kMax])
73360                 .interpolate(interpolate)
73361                 .filter(zoomEventFilter)
73362                 .on('zoom.map', zoomPan)
73363                 .on('start.map', function() {
73364                     _pointerDown = event.sourceEvent && event.sourceEvent.type === 'pointerdown';
73365                 })
73366                 .on('end.map', function() {
73367                     _pointerDown = false;
73368                 });
73369             var _doubleUpHandler = utilDoubleUp();
73370
73371             var scheduleRedraw = throttle(redraw, 750);
73372             // var isRedrawScheduled = false;
73373             // var pendingRedrawCall;
73374             // function scheduleRedraw() {
73375             //     // Only schedule the redraw if one has not already been set.
73376             //     if (isRedrawScheduled) return;
73377             //     isRedrawScheduled = true;
73378             //     var that = this;
73379             //     var args = arguments;
73380             //     pendingRedrawCall = window.requestIdleCallback(function () {
73381             //         // Reset the boolean so future redraws can be set.
73382             //         isRedrawScheduled = false;
73383             //         redraw.apply(that, args);
73384             //     }, { timeout: 1400 });
73385             // }
73386
73387             function cancelPendingRedraw() {
73388                 scheduleRedraw.cancel();
73389                 // isRedrawScheduled = false;
73390                 // window.cancelIdleCallback(pendingRedrawCall);
73391             }
73392
73393
73394             function map(selection) {
73395                 _selection = selection;
73396
73397                 context
73398                     .on('change.map', immediateRedraw);
73399
73400                 var osm = context.connection();
73401                 if (osm) {
73402                     osm.on('change.map', immediateRedraw);
73403                 }
73404
73405                 function didUndoOrRedo(targetTransform) {
73406                     var mode = context.mode().id;
73407                     if (mode !== 'browse' && mode !== 'select') { return; }
73408                     if (targetTransform) {
73409                         map.transformEase(targetTransform);
73410                     }
73411                 }
73412
73413                 context.history()
73414                     .on('merge.map', function() { scheduleRedraw(); })
73415                     .on('change.map', immediateRedraw)
73416                     .on('undone.map', function(stack, fromStack) {
73417                         didUndoOrRedo(fromStack.transform);
73418                     })
73419                     .on('redone.map', function(stack) {
73420                         didUndoOrRedo(stack.transform);
73421                     });
73422
73423                 context.background()
73424                     .on('change.map', immediateRedraw);
73425
73426                 context.features()
73427                     .on('redraw.map', immediateRedraw);
73428
73429                 drawLayers
73430                     .on('change.map', function() {
73431                         context.background().updateImagery();
73432                         immediateRedraw();
73433                     });
73434
73435                 selection
73436                     .on('wheel.map mousewheel.map', function() {
73437                         // disable swipe-to-navigate browser pages on trackpad/magic mouse – #5552
73438                         event.preventDefault();
73439                     })
73440                     .call(_zoomerPanner)
73441                     .call(_zoomerPanner.transform, projection.transform())
73442                     .on('dblclick.zoom', null); // override d3-zoom dblclick handling
73443
73444                 map.supersurface = supersurface = selection.append('div')
73445                     .attr('class', 'supersurface')
73446                     .call(utilSetTransform, 0, 0);
73447
73448                 // Need a wrapper div because Opera can't cope with an absolutely positioned
73449                 // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
73450                 wrapper = supersurface
73451                     .append('div')
73452                     .attr('class', 'layer layer-data');
73453
73454                 map.surface = surface = wrapper
73455                     .call(drawLayers)
73456                     .selectAll('.surface');
73457
73458                 surface
73459                     .call(drawLabels.observe)
73460                     .call(_doubleUpHandler)
73461                     .on(_pointerPrefix + 'down.zoom', function() {
73462                         _lastPointerEvent = event;
73463                         if (event.button === 2) {
73464                             event.stopPropagation();
73465                         }
73466                     }, true)
73467                     .on(_pointerPrefix + 'up.zoom', function() {
73468                         _lastPointerEvent = event;
73469                         if (resetTransform()) {
73470                             immediateRedraw();
73471                         }
73472                     })
73473                     .on(_pointerPrefix + 'move.map', function() {
73474                         _lastPointerEvent = event;
73475                     })
73476                     .on(_pointerPrefix + 'over.vertices', function() {
73477                         if (map.editableDataEnabled() && !_isTransformed) {
73478                             var hover = event.target.__data__;
73479                             surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
73480                             dispatch$1.call('drawn', this, { full: false });
73481                         }
73482                     })
73483                     .on(_pointerPrefix + 'out.vertices', function() {
73484                         if (map.editableDataEnabled() && !_isTransformed) {
73485                             var hover = event.relatedTarget && event.relatedTarget.__data__;
73486                             surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
73487                             dispatch$1.call('drawn', this, { full: false });
73488                         }
73489                     });
73490
73491                 var detected = utilDetect();
73492
73493                 // only WebKit supports gesture events
73494                 if ('GestureEvent' in window &&
73495                     // Listening for gesture events on iOS 13.4+ breaks double-tapping,
73496                     // but we only need to do this on desktop Safari anyway. – #7694
73497                     !detected.isMobileWebKit) {
73498
73499                     // Desktop Safari sends gesture events for multitouch trackpad pinches.
73500                     // We can listen for these and translate them into map zooms.
73501                     surface
73502                         .on('gesturestart.surface', function() {
73503                             event.preventDefault();
73504                             _gestureTransformStart = projection.transform();
73505                         })
73506                         .on('gesturechange.surface', gestureChange);
73507                 }
73508
73509                 // must call after surface init
73510                 updateAreaFill();
73511
73512                 _doubleUpHandler.on('doubleUp.map', function(p0) {
73513                     if (!_dblClickZoomEnabled) { return; }
73514
73515                     // don't zoom if targeting something other than the map itself
73516                     if (typeof event.target.__data__ === 'object' &&
73517                         // or area fills
73518                         !select(event.target).classed('fill')) { return; }
73519
73520                     var zoomOut = event.shiftKey;
73521
73522                     var t = projection.transform();
73523
73524                     var p1 = t.invert(p0);
73525
73526                     t = t.scale(zoomOut ? 0.5 : 2);
73527
73528                     t.x = p0[0] - p1[0] * t.k;
73529                     t.y = p0[1] - p1[1] * t.k;
73530
73531                     map.transformEase(t);
73532                 });
73533
73534                 context.on('enter.map',  function() {
73535                     if (!map.editableDataEnabled(true /* skip zoom check */)) { return; }
73536
73537                     // redraw immediately any objects affected by a change in selectedIDs.
73538                     var graph = context.graph();
73539                     var selectedAndParents = {};
73540                     context.selectedIDs().forEach(function(id) {
73541                         var entity = graph.hasEntity(id);
73542                         if (entity) {
73543                             selectedAndParents[entity.id] = entity;
73544                             if (entity.type === 'node') {
73545                                 graph.parentWays(entity).forEach(function(parent) {
73546                                     selectedAndParents[parent.id] = parent;
73547                                 });
73548                             }
73549                         }
73550                     });
73551                     var data = Object.values(selectedAndParents);
73552                     var filter = function(d) { return d.id in selectedAndParents; };
73553
73554                     data = context.features().filter(data, graph);
73555
73556                     surface
73557                         .call(drawVertices.drawSelected, graph, map.extent())
73558                         .call(drawLines, graph, data, filter)
73559                         .call(drawAreas, graph, data, filter)
73560                         .call(drawMidpoints, graph, data, filter, map.trimmedExtent());
73561
73562                     dispatch$1.call('drawn', this, { full: false });
73563
73564                     // redraw everything else later
73565                     scheduleRedraw();
73566                 });
73567
73568                 map.dimensions(utilGetDimensions(selection));
73569             }
73570
73571
73572             function zoomEventFilter() {
73573                 // Fix for #2151, (see also d3/d3-zoom#60, d3/d3-brush#18)
73574                 // Intercept `mousedown` and check if there is an orphaned zoom gesture.
73575                 // This can happen if a previous `mousedown` occurred without a `mouseup`.
73576                 // If we detect this, dispatch `mouseup` to complete the orphaned gesture,
73577                 // so that d3-zoom won't stop propagation of new `mousedown` events.
73578                 if (event.type === 'mousedown') {
73579                     var hasOrphan = false;
73580                     var listeners = window.__on;
73581                     for (var i = 0; i < listeners.length; i++) {
73582                         var listener = listeners[i];
73583                         if (listener.name === 'zoom' && listener.type === 'mouseup') {
73584                             hasOrphan = true;
73585                             break;
73586                         }
73587                     }
73588                     if (hasOrphan) {
73589                         var event$1 = window.CustomEvent;
73590                         if (event$1) {
73591                             event$1 = new event$1('mouseup');
73592                         } else {
73593                             event$1 = window.document.createEvent('Event');
73594                             event$1.initEvent('mouseup', false, false);
73595                         }
73596                         // Event needs to be dispatched with an event.view property.
73597                         event$1.view = window;
73598                         window.dispatchEvent(event$1);
73599                     }
73600                 }
73601
73602                 return event.button !== 2;   // ignore right clicks
73603             }
73604
73605
73606             function pxCenter() {
73607                 return [_dimensions[0] / 2, _dimensions[1] / 2];
73608             }
73609
73610
73611             function drawEditable(difference, extent) {
73612                 var mode = context.mode();
73613                 var graph = context.graph();
73614                 var features = context.features();
73615                 var all = context.history().intersects(map.extent());
73616                 var fullRedraw = false;
73617                 var data;
73618                 var set;
73619                 var filter;
73620                 var applyFeatureLayerFilters = true;
73621
73622                 if (map.isInWideSelection()) {
73623                     data = [];
73624                     utilEntityAndDeepMemberIDs(mode.selectedIDs(), context.graph()).forEach(function(id) {
73625                         var entity = context.hasEntity(id);
73626                         if (entity) { data.push(entity); }
73627                     });
73628                     fullRedraw = true;
73629                     filter = utilFunctor(true);
73630                     // selected features should always be visible, so we can skip filtering
73631                     applyFeatureLayerFilters = false;
73632
73633                 } else if (difference) {
73634                     var complete = difference.complete(map.extent());
73635                     data = Object.values(complete).filter(Boolean);
73636                     set = new Set(Object.keys(complete));
73637                     filter = function(d) { return set.has(d.id); };
73638                     features.clear(data);
73639
73640                 } else {
73641                     // force a full redraw if gatherStats detects that a feature
73642                     // should be auto-hidden (e.g. points or buildings)..
73643                     if (features.gatherStats(all, graph, _dimensions)) {
73644                         extent = undefined;
73645                     }
73646
73647                     if (extent) {
73648                         data = context.history().intersects(map.extent().intersection(extent));
73649                         set = new Set(data.map(function(entity) { return entity.id; }));
73650                         filter = function(d) { return set.has(d.id); };
73651
73652                     } else {
73653                         data = all;
73654                         fullRedraw = true;
73655                         filter = utilFunctor(true);
73656                     }
73657                 }
73658
73659                 if (applyFeatureLayerFilters) {
73660                     data = features.filter(data, graph);
73661                 } else {
73662                     context.features().resetStats();
73663                 }
73664
73665                 if (mode && mode.id === 'select') {
73666                     // update selected vertices - the user might have just double-clicked a way,
73667                     // creating a new vertex, triggering a partial redraw without a mode change
73668                     surface.call(drawVertices.drawSelected, graph, map.extent());
73669                 }
73670
73671                 surface
73672                     .call(drawVertices, graph, data, filter, map.extent(), fullRedraw)
73673                     .call(drawLines, graph, data, filter)
73674                     .call(drawAreas, graph, data, filter)
73675                     .call(drawMidpoints, graph, data, filter, map.trimmedExtent())
73676                     .call(drawLabels, graph, data, filter, _dimensions, fullRedraw)
73677                     .call(drawPoints, graph, data, filter);
73678
73679                 dispatch$1.call('drawn', this, {full: true});
73680             }
73681
73682             map.init = function() {
73683                 drawLayers = svgLayers(projection, context);
73684                 drawPoints = svgPoints(projection, context);
73685                 drawVertices = svgVertices(projection, context);
73686                 drawLines = svgLines(projection, context);
73687                 drawAreas = svgAreas(projection, context);
73688                 drawMidpoints = svgMidpoints(projection, context);
73689                 drawLabels = svgLabels(projection, context);
73690             };
73691
73692             function editOff() {
73693                 context.features().resetStats();
73694                 surface.selectAll('.layer-osm *').remove();
73695                 surface.selectAll('.layer-touch:not(.markers) *').remove();
73696
73697                 var allowed = {
73698                     'browse': true,
73699                     'save': true,
73700                     'select-note': true,
73701                     'select-data': true,
73702                     'select-error': true
73703                 };
73704
73705                 var mode = context.mode();
73706                 if (mode && !allowed[mode.id]) {
73707                     context.enter(modeBrowse(context));
73708                 }
73709
73710                 dispatch$1.call('drawn', this, {full: true});
73711             }
73712
73713
73714
73715
73716
73717             function gestureChange() {
73718                 // Remap Safari gesture events to wheel events - #5492
73719                 // We want these disabled most places, but enabled for zoom/unzoom on map surface
73720                 // https://developer.mozilla.org/en-US/docs/Web/API/GestureEvent
73721                 var e = event;
73722                 e.preventDefault();
73723
73724                 var props = {
73725                     deltaMode: 0,    // dummy values to ignore in zoomPan
73726                     deltaY: 1,       // dummy values to ignore in zoomPan
73727                     clientX: e.clientX,
73728                     clientY: e.clientY,
73729                     screenX: e.screenX,
73730                     screenY: e.screenY,
73731                     x: e.x,
73732                     y: e.y
73733                 };
73734
73735                 var e2 = new WheelEvent('wheel', props);
73736                 e2._scale = e.scale;         // preserve the original scale
73737                 e2._rotation = e.rotation;   // preserve the original rotation
73738
73739                 _selection.node().dispatchEvent(e2);
73740             }
73741
73742
73743             function zoomPan(manualEvent) {
73744                 var event$1 = (manualEvent || event);
73745                 var source = event$1.sourceEvent;
73746                 var eventTransform = event$1.transform;
73747                 var x = eventTransform.x;
73748                 var y = eventTransform.y;
73749                 var k = eventTransform.k;
73750
73751                 // Special handling of 'wheel' events:
73752                 // They might be triggered by the user scrolling the mouse wheel,
73753                 // or 2-finger pinch/zoom gestures, the transform may need adjustment.
73754                 if (source && source.type === 'wheel') {
73755
73756                     // assume that the gesture is already handled by pointer events
73757                     if (_pointerDown) { return; }
73758
73759                     var detected = utilDetect();
73760                     var dX = source.deltaX;
73761                     var dY = source.deltaY;
73762                     var x2 = x;
73763                     var y2 = y;
73764                     var k2 = k;
73765                     var t0, p0, p1;
73766
73767                     // Normalize mousewheel scroll speed (Firefox) - #3029
73768                     // If wheel delta is provided in LINE units, recalculate it in PIXEL units
73769                     // We are essentially redoing the calculations that occur here:
73770                     //   https://github.com/d3/d3-zoom/blob/78563a8348aa4133b07cac92e2595c2227ca7cd7/src/zoom.js#L203
73771                     // See this for more info:
73772                     //   https://github.com/basilfx/normalize-wheel/blob/master/src/normalizeWheel.js
73773                     if (source.deltaMode === 1 /* LINE */) {
73774                         // Convert from lines to pixels, more if the user is scrolling fast.
73775                         // (I made up the exp function to roughly match Firefox to what Chrome does)
73776                         // These numbers should be floats, because integers are treated as pan gesture below.
73777                         var lines = Math.abs(source.deltaY);
73778                         var sign = (source.deltaY > 0) ? 1 : -1;
73779                         dY = sign * clamp(
73780                             Math.exp((lines - 1) * 0.75) * 4.000244140625,
73781                             4.000244140625,    // min
73782                             350.000244140625   // max
73783                         );
73784
73785                         // On Firefox Windows and Linux we always get +/- the scroll line amount (default 3)
73786                         // There doesn't seem to be any scroll accelleration.
73787                         // This multiplier increases the speed a little bit - #5512
73788                         if (detected.os !== 'mac') {
73789                             dY *= 5;
73790                         }
73791
73792                         // recalculate x2,y2,k2
73793                         t0 = _isTransformed ? _transformLast : _transformStart;
73794                         p0 = _getMouseCoords(source);
73795                         p1 = t0.invert(p0);
73796                         k2 = t0.k * Math.pow(2, -dY / 500);
73797                         k2 = clamp(k2, kMin, kMax);
73798                         x2 = p0[0] - p1[0] * k2;
73799                         y2 = p0[1] - p1[1] * k2;
73800
73801                     // 2 finger map pinch zooming (Safari) - #5492
73802                     // These are fake `wheel` events we made from Safari `gesturechange` events..
73803                     } else if (source._scale) {
73804                         // recalculate x2,y2,k2
73805                         t0 = _gestureTransformStart;
73806                         p0 = _getMouseCoords(source);
73807                         p1 = t0.invert(p0);
73808                         k2 = t0.k * source._scale;
73809                         k2 = clamp(k2, kMin, kMax);
73810                         x2 = p0[0] - p1[0] * k2;
73811                         y2 = p0[1] - p1[1] * k2;
73812
73813                     // 2 finger map pinch zooming (all browsers except Safari) - #5492
73814                     // Pinch zooming via the `wheel` event will always have:
73815                     // - `ctrlKey = true`
73816                     // - `deltaY` is not round integer pixels (ignore `deltaX`)
73817                     } else if (source.ctrlKey && !isInteger(dY)) {
73818                         dY *= 6;   // slightly scale up whatever the browser gave us
73819
73820                         // recalculate x2,y2,k2
73821                         t0 = _isTransformed ? _transformLast : _transformStart;
73822                         p0 = _getMouseCoords(source);
73823                         p1 = t0.invert(p0);
73824                         k2 = t0.k * Math.pow(2, -dY / 500);
73825                         k2 = clamp(k2, kMin, kMax);
73826                         x2 = p0[0] - p1[0] * k2;
73827                         y2 = p0[1] - p1[1] * k2;
73828
73829                     // Trackpad scroll zooming with shift or alt/option key down
73830                     } else if ((source.altKey || source.shiftKey) && isInteger(dY)) {
73831                         // recalculate x2,y2,k2
73832                         t0 = _isTransformed ? _transformLast : _transformStart;
73833                         p0 = _getMouseCoords(source);
73834                         p1 = t0.invert(p0);
73835                         k2 = t0.k * Math.pow(2, -dY / 500);
73836                         k2 = clamp(k2, kMin, kMax);
73837                         x2 = p0[0] - p1[0] * k2;
73838                         y2 = p0[1] - p1[1] * k2;
73839
73840                     // 2 finger map panning (Mac only, all browsers) - #5492, #5512
73841                     // Panning via the `wheel` event will always have:
73842                     // - `ctrlKey = false`
73843                     // - `deltaX`,`deltaY` are round integer pixels
73844                     } else if (detected.os === 'mac' && !source.ctrlKey && isInteger(dX) && isInteger(dY)) {
73845                         p1 = projection.translate();
73846                         x2 = p1[0] - dX;
73847                         y2 = p1[1] - dY;
73848                         k2 = projection.scale();
73849                         k2 = clamp(k2, kMin, kMax);
73850                     }
73851
73852                     // something changed - replace the event transform
73853                     if (x2 !== x || y2 !== y || k2 !== k) {
73854                         x = x2;
73855                         y = y2;
73856                         k = k2;
73857                         eventTransform = identity$2.translate(x2, y2).scale(k2);
73858                         if (_zoomerPanner._transform) {
73859                             // utilZoomPan interface
73860                             _zoomerPanner._transform(eventTransform);
73861                         } else {
73862                             // d3_zoom interface
73863                             _selection.node().__zoom = eventTransform;
73864                         }
73865                     }
73866
73867                 }
73868
73869                 if (_transformStart.x === x &&
73870                     _transformStart.y === y &&
73871                     _transformStart.k === k) {
73872                     return;  // no change
73873                 }
73874
73875                 var withinEditableZoom = map.withinEditableZoom();
73876                 if (_lastWithinEditableZoom !== withinEditableZoom) {
73877                     if (_lastWithinEditableZoom !== undefined) {
73878                         // notify that the map zoomed in or out over the editable zoom threshold
73879                         dispatch$1.call('crossEditableZoom', this, withinEditableZoom);
73880                     }
73881                     _lastWithinEditableZoom = withinEditableZoom;
73882                 }
73883
73884                 if (geoScaleToZoom(k, TILESIZE) < _minzoom) {
73885                     surface.interrupt();
73886                     dispatch$1.call('hitMinZoom', this, map);
73887                     setCenterZoom(map.center(), context.minEditableZoom(), 0, true);
73888                     scheduleRedraw();
73889                     dispatch$1.call('move', this, map);
73890                     return;
73891                 }
73892
73893                 projection.transform(eventTransform);
73894
73895                 var scale = k / _transformStart.k;
73896                 var tX = (x / scale - _transformStart.x) * scale;
73897                 var tY = (y / scale - _transformStart.y) * scale;
73898
73899                 if (context.inIntro()) {
73900                     curtainProjection.transform({
73901                         x: x - tX,
73902                         y: y - tY,
73903                         k: k
73904                     });
73905                 }
73906
73907                 if (source) {
73908                     _lastPointerEvent = event$1;
73909                 }
73910                 _isTransformed = true;
73911                 _transformLast = eventTransform;
73912                 utilSetTransform(supersurface, tX, tY, scale);
73913                 scheduleRedraw();
73914
73915                 dispatch$1.call('move', this, map);
73916
73917
73918                 function isInteger(val) {
73919                     return typeof val === 'number' && isFinite(val) && Math.floor(val) === val;
73920                 }
73921             }
73922
73923
73924             function resetTransform() {
73925                 if (!_isTransformed) { return false; }
73926
73927                 utilSetTransform(supersurface, 0, 0);
73928                 _isTransformed = false;
73929                 if (context.inIntro()) {
73930                     curtainProjection.transform(projection.transform());
73931                 }
73932                 return true;
73933             }
73934
73935
73936             function redraw(difference, extent) {
73937                 if (surface.empty() || !_redrawEnabled) { return; }
73938
73939                 // If we are in the middle of a zoom/pan, we can't do differenced redraws.
73940                 // It would result in artifacts where differenced entities are redrawn with
73941                 // one transform and unchanged entities with another.
73942                 if (resetTransform()) {
73943                     difference = extent = undefined;
73944                 }
73945
73946                 var zoom = map.zoom();
73947                 var z = String(~~zoom);
73948
73949                 if (surface.attr('data-zoom') !== z) {
73950                     surface.attr('data-zoom', z);
73951                 }
73952
73953                 // class surface as `lowzoom` around z17-z18.5 (based on latitude)
73954                 var lat = map.center()[1];
73955                 var lowzoom = linear$2()
73956                     .domain([-60, 0, 60])
73957                     .range([17, 18.5, 17])
73958                     .clamp(true);
73959
73960                 surface
73961                     .classed('low-zoom', zoom <= lowzoom(lat));
73962
73963
73964                 if (!difference) {
73965                     supersurface.call(context.background());
73966                     wrapper.call(drawLayers);
73967                 }
73968
73969                 // OSM
73970                 if (map.editableDataEnabled() || map.isInWideSelection()) {
73971                     context.loadTiles(projection);
73972                     drawEditable(difference, extent);
73973                 } else {
73974                     editOff();
73975                 }
73976
73977                 _transformStart = projection.transform();
73978
73979                 return map;
73980             }
73981
73982
73983
73984             var immediateRedraw = function(difference, extent) {
73985                 if (!difference && !extent) { cancelPendingRedraw(); }
73986                 redraw(difference, extent);
73987             };
73988
73989
73990             map.lastPointerEvent = function() {
73991                 return _lastPointerEvent;
73992             };
73993
73994
73995             map.mouse = function() {
73996                 var event$1 = _lastPointerEvent || event;
73997                 if (event$1) {
73998                     var s;
73999                     while ((s = event$1.sourceEvent)) { event$1 = s; }
74000                     return _getMouseCoords(event$1);
74001                 }
74002                 return null;
74003             };
74004
74005
74006             // returns Lng/Lat
74007             map.mouseCoordinates = function() {
74008                 var coord = map.mouse() || pxCenter();
74009                 return projection.invert(coord);
74010             };
74011
74012
74013             map.dblclickZoomEnable = function(val) {
74014                 if (!arguments.length) { return _dblClickZoomEnabled; }
74015                 _dblClickZoomEnabled = val;
74016                 return map;
74017             };
74018
74019
74020             map.redrawEnable = function(val) {
74021                 if (!arguments.length) { return _redrawEnabled; }
74022                 _redrawEnabled = val;
74023                 return map;
74024             };
74025
74026
74027             map.isTransformed = function() {
74028                 return _isTransformed;
74029             };
74030
74031
74032             function setTransform(t2, duration, force) {
74033                 var t = projection.transform();
74034                 if (!force && t2.k === t.k && t2.x === t.x && t2.y === t.y) { return false; }
74035
74036                 if (duration) {
74037                     _selection
74038                         .transition()
74039                         .duration(duration)
74040                         .on('start', function() { map.startEase(); })
74041                         .call(_zoomerPanner.transform, identity$2.translate(t2.x, t2.y).scale(t2.k));
74042                 } else {
74043                     projection.transform(t2);
74044                     _transformStart = t2;
74045                     _selection.call(_zoomerPanner.transform, _transformStart);
74046                 }
74047
74048                 return true;
74049             }
74050
74051
74052             function setCenterZoom(loc2, z2, duration, force) {
74053                 var c = map.center();
74054                 var z = map.zoom();
74055                 if (loc2[0] === c[0] && loc2[1] === c[1] && z2 === z && !force) { return false; }
74056
74057                 var proj = geoRawMercator().transform(projection.transform());  // copy projection
74058
74059                 var k2 = clamp(geoZoomToScale(z2, TILESIZE), kMin, kMax);
74060                 proj.scale(k2);
74061
74062                 var t = proj.translate();
74063                 var point = proj(loc2);
74064
74065                 var center = pxCenter();
74066                 t[0] += center[0] - point[0];
74067                 t[1] += center[1] - point[1];
74068
74069                 return setTransform(identity$2.translate(t[0], t[1]).scale(k2), duration, force);
74070             }
74071
74072
74073             map.pan = function(delta, duration) {
74074                 var t = projection.translate();
74075                 var k = projection.scale();
74076
74077                 t[0] += delta[0];
74078                 t[1] += delta[1];
74079
74080                 if (duration) {
74081                     _selection
74082                         .transition()
74083                         .duration(duration)
74084                         .on('start', function() { map.startEase(); })
74085                         .call(_zoomerPanner.transform, identity$2.translate(t[0], t[1]).scale(k));
74086                 } else {
74087                     projection.translate(t);
74088                     _transformStart = projection.transform();
74089                     _selection.call(_zoomerPanner.transform, _transformStart);
74090                     dispatch$1.call('move', this, map);
74091                     immediateRedraw();
74092                 }
74093
74094                 return map;
74095             };
74096
74097
74098             map.dimensions = function(val) {
74099                 if (!arguments.length) { return _dimensions; }
74100
74101                 _dimensions = val;
74102                 drawLayers.dimensions(_dimensions);
74103                 context.background().dimensions(_dimensions);
74104                 projection.clipExtent([[0, 0], _dimensions]);
74105                 _getMouseCoords = utilFastMouse(supersurface.node());
74106
74107                 scheduleRedraw();
74108                 return map;
74109             };
74110
74111
74112             function zoomIn(delta) {
74113                 setCenterZoom(map.center(), ~~map.zoom() + delta, 250, true);
74114             }
74115
74116             function zoomOut(delta) {
74117                 setCenterZoom(map.center(), ~~map.zoom() - delta, 250, true);
74118             }
74119
74120             map.zoomIn = function() { zoomIn(1); };
74121             map.zoomInFurther = function() { zoomIn(4); };
74122             map.canZoomIn = function() { return map.zoom() < maxZoom; };
74123
74124             map.zoomOut = function() { zoomOut(1); };
74125             map.zoomOutFurther = function() { zoomOut(4); };
74126             map.canZoomOut = function() { return map.zoom() > minZoom; };
74127
74128             map.center = function(loc2) {
74129                 if (!arguments.length) {
74130                     return projection.invert(pxCenter());
74131                 }
74132
74133                 if (setCenterZoom(loc2, map.zoom())) {
74134                     dispatch$1.call('move', this, map);
74135                 }
74136
74137                 scheduleRedraw();
74138                 return map;
74139             };
74140
74141             map.unobscuredCenterZoomEase = function(loc, zoom) {
74142                 var offset = map.unobscuredOffsetPx();
74143
74144                 var proj = geoRawMercator().transform(projection.transform());  // copy projection
74145                 // use the target zoom to calculate the offset center
74146                 proj.scale(geoZoomToScale(zoom, TILESIZE));
74147
74148                 var locPx = proj(loc);
74149                 var offsetLocPx = [locPx[0] + offset[0], locPx[1] + offset[1]];
74150                 var offsetLoc = proj.invert(offsetLocPx);
74151
74152                 map.centerZoomEase(offsetLoc, zoom);
74153             };
74154
74155             map.unobscuredOffsetPx = function() {
74156                 var openPane = context.container().select('.map-panes .map-pane.shown');
74157                 if (!openPane.empty()) {
74158                     return [openPane.node().offsetWidth/2, 0];
74159                 }
74160                 return [0, 0];
74161             };
74162
74163             map.zoom = function(z2) {
74164                 if (!arguments.length) {
74165                     return Math.max(geoScaleToZoom(projection.scale(), TILESIZE), 0);
74166                 }
74167
74168                 if (z2 < _minzoom) {
74169                     surface.interrupt();
74170                     dispatch$1.call('hitMinZoom', this, map);
74171                     z2 = context.minEditableZoom();
74172                 }
74173
74174                 if (setCenterZoom(map.center(), z2)) {
74175                     dispatch$1.call('move', this, map);
74176                 }
74177
74178                 scheduleRedraw();
74179                 return map;
74180             };
74181
74182
74183             map.centerZoom = function(loc2, z2) {
74184                 if (setCenterZoom(loc2, z2)) {
74185                     dispatch$1.call('move', this, map);
74186                 }
74187
74188                 scheduleRedraw();
74189                 return map;
74190             };
74191
74192
74193             map.zoomTo = function(entity) {
74194                 var extent = entity.extent(context.graph());
74195                 if (!isFinite(extent.area())) { return map; }
74196
74197                 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
74198                 return map.centerZoom(extent.center(), z2);
74199             };
74200
74201
74202             map.centerEase = function(loc2, duration) {
74203                 duration = duration || 250;
74204                 setCenterZoom(loc2, map.zoom(), duration);
74205                 return map;
74206             };
74207
74208
74209             map.zoomEase = function(z2, duration) {
74210                 duration = duration || 250;
74211                 setCenterZoom(map.center(), z2, duration, false);
74212                 return map;
74213             };
74214
74215
74216             map.centerZoomEase = function(loc2, z2, duration) {
74217                 duration = duration || 250;
74218                 setCenterZoom(loc2, z2, duration, false);
74219                 return map;
74220             };
74221
74222
74223             map.transformEase = function(t2, duration) {
74224                 duration = duration || 250;
74225                 setTransform(t2, duration, false /* don't force */);
74226                 return map;
74227             };
74228
74229
74230             map.zoomToEase = function(obj, duration) {
74231                 var extent;
74232                 if (Array.isArray(obj)) {
74233                     obj.forEach(function(entity) {
74234                         var entityExtent = entity.extent(context.graph());
74235                         if (!extent) {
74236                             extent = entityExtent;
74237                         } else {
74238                             extent = extent.extend(entityExtent);
74239                         }
74240                     });
74241                 } else {
74242                     extent = obj.extent(context.graph());
74243                 }
74244                 if (!isFinite(extent.area())) { return map; }
74245
74246                 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
74247                 return map.centerZoomEase(extent.center(), z2, duration);
74248             };
74249
74250
74251             map.startEase = function() {
74252                 utilBindOnce(surface, _pointerPrefix + 'down.ease', function() {
74253                     map.cancelEase();
74254                 });
74255                 return map;
74256             };
74257
74258
74259             map.cancelEase = function() {
74260                 _selection.interrupt();
74261                 return map;
74262             };
74263
74264
74265             map.extent = function(val) {
74266                 if (!arguments.length) {
74267                     return new geoExtent(
74268                         projection.invert([0, _dimensions[1]]),
74269                         projection.invert([_dimensions[0], 0])
74270                     );
74271                 } else {
74272                     var extent = geoExtent(val);
74273                     map.centerZoom(extent.center(), map.extentZoom(extent));
74274                 }
74275             };
74276
74277
74278             map.trimmedExtent = function(val) {
74279                 if (!arguments.length) {
74280                     var headerY = 71;
74281                     var footerY = 30;
74282                     var pad = 10;
74283                     return new geoExtent(
74284                         projection.invert([pad, _dimensions[1] - footerY - pad]),
74285                         projection.invert([_dimensions[0] - pad, headerY + pad])
74286                     );
74287                 } else {
74288                     var extent = geoExtent(val);
74289                     map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
74290                 }
74291             };
74292
74293
74294             function calcExtentZoom(extent, dim) {
74295                 var tl = projection([extent[0][0], extent[1][1]]);
74296                 var br = projection([extent[1][0], extent[0][1]]);
74297
74298                 // Calculate maximum zoom that fits extent
74299                 var hFactor = (br[0] - tl[0]) / dim[0];
74300                 var vFactor = (br[1] - tl[1]) / dim[1];
74301                 var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
74302                 var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
74303                 var newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
74304
74305                 return newZoom;
74306             }
74307
74308
74309             map.extentZoom = function(val) {
74310                 return calcExtentZoom(geoExtent(val), _dimensions);
74311             };
74312
74313
74314             map.trimmedExtentZoom = function(val) {
74315                 var trimY = 120;
74316                 var trimX = 40;
74317                 var trimmed = [_dimensions[0] - trimX, _dimensions[1] - trimY];
74318                 return calcExtentZoom(geoExtent(val), trimmed);
74319             };
74320
74321
74322             map.withinEditableZoom = function() {
74323                 return map.zoom() >= context.minEditableZoom();
74324             };
74325
74326
74327             map.isInWideSelection = function() {
74328                 return !map.withinEditableZoom() && context.selectedIDs().length;
74329             };
74330
74331
74332             map.editableDataEnabled = function(skipZoomCheck) {
74333
74334                 var layer = context.layers().layer('osm');
74335                 if (!layer || !layer.enabled()) { return false; }
74336
74337                 return skipZoomCheck || map.withinEditableZoom();
74338             };
74339
74340
74341             map.notesEditable = function() {
74342                 var layer = context.layers().layer('notes');
74343                 if (!layer || !layer.enabled()) { return false; }
74344
74345                 return map.withinEditableZoom();
74346             };
74347
74348
74349             map.minzoom = function(val) {
74350                 if (!arguments.length) { return _minzoom; }
74351                 _minzoom = val;
74352                 return map;
74353             };
74354
74355
74356             map.toggleHighlightEdited = function() {
74357                 surface.classed('highlight-edited', !surface.classed('highlight-edited'));
74358                 map.pan([0,0]);  // trigger a redraw
74359                 dispatch$1.call('changeHighlighting', this);
74360             };
74361
74362
74363             map.areaFillOptions = ['wireframe', 'partial', 'full'];
74364
74365             map.activeAreaFill = function(val) {
74366                 if (!arguments.length) { return corePreferences('area-fill') || 'partial'; }
74367
74368                 corePreferences('area-fill', val);
74369                 if (val !== 'wireframe') {
74370                     corePreferences('area-fill-toggle', val);
74371                 }
74372                 updateAreaFill();
74373                 map.pan([0,0]);  // trigger a redraw
74374                 dispatch$1.call('changeAreaFill', this);
74375                 return map;
74376             };
74377
74378             map.toggleWireframe = function() {
74379
74380                 var activeFill = map.activeAreaFill();
74381
74382                 if (activeFill === 'wireframe') {
74383                     activeFill = corePreferences('area-fill-toggle') || 'partial';
74384                 } else {
74385                     activeFill = 'wireframe';
74386                 }
74387
74388                 map.activeAreaFill(activeFill);
74389             };
74390
74391             function updateAreaFill() {
74392                 var activeFill = map.activeAreaFill();
74393                 map.areaFillOptions.forEach(function(opt) {
74394                     surface.classed('fill-' + opt, Boolean(opt === activeFill));
74395                 });
74396             }
74397
74398
74399             map.layers = function () { return drawLayers; };
74400
74401
74402             map.doubleUpHandler = function() {
74403                 return _doubleUpHandler;
74404             };
74405
74406
74407             return utilRebind(map, dispatch$1, 'on');
74408         }
74409
74410         function rendererPhotos(context) {
74411             var dispatch$1 = dispatch('change');
74412             var _layerIDs = ['streetside', 'mapillary', 'mapillary-map-features', 'mapillary-signs', 'openstreetcam'];
74413             var _allPhotoTypes = ['flat', 'panoramic'];
74414             var _shownPhotoTypes = _allPhotoTypes.slice();   // shallow copy
74415
74416             function photos() {}
74417
74418             function updateStorage() {
74419                 if (window.mocha) { return; }
74420
74421                 var hash = utilStringQs(window.location.hash);
74422                 var enabled = context.layers().all().filter(function(d) {
74423                     return _layerIDs.indexOf(d.id) !== -1 && d.layer && d.layer.supported() && d.layer.enabled();
74424                 }).map(function(d) {
74425                     return d.id;
74426                 });
74427                 if (enabled.length) {
74428                     hash.photo_overlay = enabled.join(',');
74429                 } else {
74430                     delete hash.photo_overlay;
74431                 }
74432                 window.location.replace('#' + utilQsString(hash, true));
74433             }
74434
74435             photos.overlayLayerIDs = function() {
74436                 return _layerIDs;
74437             };
74438
74439             photos.allPhotoTypes = function() {
74440                 return _allPhotoTypes;
74441             };
74442
74443             function showsLayer(id) {
74444                 var layer = context.layers().layer(id);
74445                 return layer && layer.supported() && layer.enabled();
74446             }
74447
74448             photos.shouldFilterByPhotoType = function() {
74449                 return showsLayer('mapillary') ||
74450                     (showsLayer('streetside') && showsLayer('openstreetcam'));
74451             };
74452
74453             photos.showsPhotoType = function(val) {
74454                 if (!photos.shouldFilterByPhotoType()) { return true; }
74455
74456                 return _shownPhotoTypes.indexOf(val) !== -1;
74457             };
74458
74459             photos.showsFlat = function() {
74460                 return photos.showsPhotoType('flat');
74461             };
74462
74463             photos.showsPanoramic = function() {
74464                 return photos.showsPhotoType('panoramic');
74465             };
74466
74467             photos.togglePhotoType = function(val) {
74468                 var index = _shownPhotoTypes.indexOf(val);
74469                 if (index !== -1) {
74470                     _shownPhotoTypes.splice(index, 1);
74471                 } else {
74472                     _shownPhotoTypes.push(val);
74473                 }
74474                 dispatch$1.call('change', this);
74475                 return photos;
74476             };
74477
74478             photos.init = function() {
74479                 var hash = utilStringQs(window.location.hash);
74480                 if (hash.photo_overlay) {
74481                     var hashOverlayIDs = hash.photo_overlay.replace(/;/g, ',').split(',');
74482                     hashOverlayIDs.forEach(function(id) {
74483                         var layer = context.layers().layer(id);
74484                         if (layer) { layer.enabled(true); }
74485                     });
74486                 }
74487
74488                 context.layers().on('change.rendererPhotos', updateStorage);
74489             };
74490
74491             return utilRebind(photos, dispatch$1, 'on');
74492         }
74493
74494         function uiAccount(context) {
74495             var osm = context.connection();
74496
74497
74498             function update(selection) {
74499                 if (!osm) { return; }
74500
74501                 if (!osm.authenticated()) {
74502                     selection.selectAll('.userLink, .logoutLink')
74503                         .classed('hide', true);
74504                     return;
74505                 }
74506
74507                 osm.userDetails(function(err, details) {
74508                     var userLink = selection.select('.userLink'),
74509                         logoutLink = selection.select('.logoutLink');
74510
74511                     userLink.html('');
74512                     logoutLink.html('');
74513
74514                     if (err || !details) { return; }
74515
74516                     selection.selectAll('.userLink, .logoutLink')
74517                         .classed('hide', false);
74518
74519                     // Link
74520                     userLink.append('a')
74521                         .attr('href', osm.userURL(details.display_name))
74522                         .attr('target', '_blank');
74523
74524                     // Add thumbnail or dont
74525                     if (details.image_url) {
74526                         userLink.append('img')
74527                             .attr('class', 'icon pre-text user-icon')
74528                             .attr('src', details.image_url);
74529                     } else {
74530                         userLink
74531                             .call(svgIcon('#iD-icon-avatar', 'pre-text light'));
74532                     }
74533
74534                     // Add user name
74535                     userLink.append('span')
74536                         .attr('class', 'label')
74537                         .text(details.display_name);
74538
74539                     logoutLink.append('a')
74540                         .attr('class', 'logout')
74541                         .attr('href', '#')
74542                         .text(_t('logout'))
74543                         .on('click.logout', function() {
74544                             event.preventDefault();
74545                             osm.logout();
74546                         });
74547                 });
74548             }
74549
74550
74551             return function(selection) {
74552                 selection.append('li')
74553                     .attr('class', 'logoutLink')
74554                     .classed('hide', true);
74555
74556                 selection.append('li')
74557                     .attr('class', 'userLink')
74558                     .classed('hide', true);
74559
74560                 if (osm) {
74561                     osm.on('change.account', function() { update(selection); });
74562                     update(selection);
74563                 }
74564             };
74565         }
74566
74567         function uiAttribution(context) {
74568           var _selection = select(null);
74569
74570
74571           function render(selection, data, klass) {
74572             var div = selection.selectAll(("." + klass))
74573               .data([0]);
74574
74575             div = div.enter()
74576               .append('div')
74577               .attr('class', klass)
74578               .merge(div);
74579
74580
74581             var attributions = div.selectAll('.attribution')
74582               .data(data, function (d) { return d.id; });
74583
74584             attributions.exit()
74585               .remove();
74586
74587             attributions = attributions.enter()
74588               .append('span')
74589               .attr('class', 'attribution')
74590               .each(function (d, i, nodes) {
74591                 var attribution = select(nodes[i]);
74592
74593                 if (d.terms_html) {
74594                   attribution.html(d.terms_html);
74595                   return;
74596                 }
74597
74598                 if (d.terms_url) {
74599                   attribution = attribution
74600                     .append('a')
74601                     .attr('href', d.terms_url)
74602                     .attr('target', '_blank');
74603                 }
74604
74605                 var sourceID = d.id.replace(/\./g, '<TX_DOT>');
74606                 var terms_text = _t(("imagery." + sourceID + ".attribution.text"),
74607                   { default: d.terms_text || d.id || d.name() }
74608                 );
74609
74610                 if (d.icon && !d.overlay) {
74611                   attribution
74612                     .append('img')
74613                     .attr('class', 'source-image')
74614                     .attr('src', d.icon);
74615                 }
74616
74617                 attribution
74618                   .append('span')
74619                   .attr('class', 'attribution-text')
74620                   .text(terms_text);
74621               })
74622               .merge(attributions);
74623
74624
74625             var copyright = attributions.selectAll('.copyright-notice')
74626               .data(function (d) {
74627                 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
74628                 return notice ? [notice] : [];
74629               });
74630
74631             copyright.exit()
74632               .remove();
74633
74634             copyright = copyright.enter()
74635               .append('span')
74636               .attr('class', 'copyright-notice')
74637               .merge(copyright);
74638
74639             copyright
74640               .text(String);
74641           }
74642
74643
74644           function update() {
74645             var baselayer = context.background().baseLayerSource();
74646             _selection
74647               .call(render, (baselayer ? [baselayer] : []), 'base-layer-attribution');
74648
74649             var z = context.map().zoom();
74650             var overlays = context.background().overlayLayerSources() || [];
74651             _selection
74652               .call(render, overlays.filter(function (s) { return s.validZoom(z); }), 'overlay-layer-attribution');
74653           }
74654
74655
74656           return function(selection) {
74657             _selection = selection;
74658
74659             context.background()
74660               .on('change.attribution', update);
74661
74662             context.map()
74663               .on('move.attribution', throttle(update, 400, { leading: false }));
74664
74665             update();
74666           };
74667         }
74668
74669         function uiContributors(context) {
74670             var osm = context.connection(),
74671                 debouncedUpdate = debounce(function() { update(); }, 1000),
74672                 limit = 4,
74673                 hidden = false,
74674                 wrap = select(null);
74675
74676
74677             function update() {
74678                 if (!osm) { return; }
74679
74680                 var users = {},
74681                     entities = context.history().intersects(context.map().extent());
74682
74683                 entities.forEach(function(entity) {
74684                     if (entity && entity.user) { users[entity.user] = true; }
74685                 });
74686
74687                 var u = Object.keys(users),
74688                     subset = u.slice(0, u.length > limit ? limit - 1 : limit);
74689
74690                 wrap.html('')
74691                     .call(svgIcon('#iD-icon-nearby', 'pre-text light'));
74692
74693                 var userList = select(document.createElement('span'));
74694
74695                 userList.selectAll()
74696                     .data(subset)
74697                     .enter()
74698                     .append('a')
74699                     .attr('class', 'user-link')
74700                     .attr('href', function(d) { return osm.userURL(d); })
74701                     .attr('target', '_blank')
74702                     .text(String);
74703
74704                 if (u.length > limit) {
74705                     var count = select(document.createElement('span'));
74706
74707                     count.append('a')
74708                         .attr('target', '_blank')
74709                         .attr('href', function() {
74710                             return osm.changesetsURL(context.map().center(), context.map().zoom());
74711                         })
74712                         .text(u.length - limit + 1);
74713
74714                     wrap.append('span')
74715                         .html(_t('contributors.truncated_list', { users: userList.html(), count: count.html() }));
74716
74717                 } else {
74718                     wrap.append('span')
74719                         .html(_t('contributors.list', { users: userList.html() }));
74720                 }
74721
74722                 if (!u.length) {
74723                     hidden = true;
74724                     wrap
74725                         .transition()
74726                         .style('opacity', 0);
74727
74728                 } else if (hidden) {
74729                     wrap
74730                         .transition()
74731                         .style('opacity', 1);
74732                 }
74733             }
74734
74735
74736             return function(selection) {
74737                 if (!osm) { return; }
74738                 wrap = selection;
74739                 update();
74740
74741                 osm.on('loaded.contributors', debouncedUpdate);
74742                 context.map().on('move.contributors', debouncedUpdate);
74743             };
74744         }
74745
74746         var _popoverID = 0;
74747
74748         function uiPopover(klass) {
74749             var _id = _popoverID++;
74750             var _anchorSelection = select(null);
74751             var popover = function(selection) {
74752                 _anchorSelection = selection;
74753                 selection.each(setup);
74754             };
74755             var _animation = utilFunctor(false);
74756             var _placement = utilFunctor('top'); // top, bottom, left, right
74757             var _alignment = utilFunctor('center');  // leading, center, trailing
74758             var _scrollContainer = utilFunctor(select(null));
74759             var _content;
74760             var _displayType = utilFunctor('');
74761             var _hasArrow = utilFunctor(true);
74762
74763             // use pointer events on supported platforms; fallback to mouse events
74764             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
74765
74766             popover.displayType = function(val) {
74767                 if (arguments.length) {
74768                     _displayType = utilFunctor(val);
74769                     return popover;
74770                 } else {
74771                     return _displayType;
74772                 }
74773             };
74774
74775             popover.hasArrow = function(val) {
74776                 if (arguments.length) {
74777                     _hasArrow = utilFunctor(val);
74778                     return popover;
74779                 } else {
74780                     return _hasArrow;
74781                 }
74782             };
74783
74784             popover.placement = function(val) {
74785                 if (arguments.length) {
74786                     _placement = utilFunctor(val);
74787                     return popover;
74788                 } else {
74789                     return _placement;
74790                 }
74791             };
74792
74793             popover.alignment = function(val) {
74794                 if (arguments.length) {
74795                     _alignment = utilFunctor(val);
74796                     return popover;
74797                 } else {
74798                     return _alignment;
74799                 }
74800             };
74801
74802             popover.scrollContainer = function(val) {
74803                 if (arguments.length) {
74804                     _scrollContainer = utilFunctor(val);
74805                     return popover;
74806                 } else {
74807                     return _scrollContainer;
74808                 }
74809             };
74810
74811             popover.content = function(val) {
74812                 if (arguments.length) {
74813                     _content = val;
74814                     return popover;
74815                 } else {
74816                     return _content;
74817                 }
74818             };
74819
74820             popover.isShown = function() {
74821                 var popoverSelection = _anchorSelection.select('.popover-' + _id);
74822                 return !popoverSelection.empty() && popoverSelection.classed('in');
74823             };
74824
74825             popover.show = function() {
74826                 _anchorSelection.each(show);
74827             };
74828
74829             popover.updateContent = function() {
74830                 _anchorSelection.each(updateContent);
74831             };
74832
74833             popover.hide = function() {
74834                 _anchorSelection.each(hide);
74835             };
74836
74837             popover.toggle = function() {
74838                 _anchorSelection.each(toggle);
74839             };
74840
74841             popover.destroy = function(selection, selector) {
74842                 // by default, just destroy the current popover
74843                 selector = selector || '.popover-' + _id;
74844
74845                 selection
74846                     .on(_pointerPrefix + 'enter.popover', null)
74847                     .on(_pointerPrefix + 'leave.popover', null)
74848                     .on(_pointerPrefix + 'up.popover', null)
74849                     .on(_pointerPrefix + 'down.popover', null)
74850                     .on('click.popover', null)
74851                     .attr('title', function() {
74852                         return this.getAttribute('data-original-title') || this.getAttribute('title');
74853                     })
74854                     .attr('data-original-title', null)
74855                     .selectAll(selector)
74856                     .remove();
74857             };
74858
74859
74860             popover.destroyAny = function(selection) {
74861                 selection.call(popover.destroy, '.popover');
74862             };
74863
74864             function setup() {
74865                 var anchor = select(this);
74866                 var animate = _animation.apply(this, arguments);
74867                 var popoverSelection = anchor.selectAll('.popover-' + _id)
74868                     .data([0]);
74869
74870
74871                 var enter = popoverSelection.enter()
74872                     .append('div')
74873                     .attr('class', 'popover popover-' + _id + ' ' + (klass ? klass : ''))
74874                     .classed('arrowed', _hasArrow.apply(this, arguments));
74875
74876                 enter
74877                     .append('div')
74878                     .attr('class', 'popover-arrow');
74879
74880                 enter
74881                     .append('div')
74882                     .attr('class', 'popover-inner');
74883
74884                 popoverSelection = enter
74885                     .merge(popoverSelection);
74886
74887                 if (animate) {
74888                     popoverSelection.classed('fade', true);
74889                 }
74890
74891                 var display = _displayType.apply(this, arguments);
74892
74893                 if (display === 'hover') {
74894                     var _lastNonMouseEnterTime;
74895                     anchor.on(_pointerPrefix + 'enter.popover', function() {
74896
74897                         if (event.pointerType) {
74898                             if (event.pointerType !== 'mouse') {
74899                                 _lastNonMouseEnterTime = event.timeStamp;
74900                                 // only allow hover behavior for mouse input
74901                                 return;
74902                             } else if (_lastNonMouseEnterTime &&
74903                                 event.timeStamp - _lastNonMouseEnterTime < 1500) {
74904                                 // HACK: iOS 13.4 sends an erroneous `mouse` type pointerenter
74905                                 // event for non-mouse interactions right after sending
74906                                 // the correct type pointerenter event. Workaround by discarding
74907                                 // any mouse event that occurs immediately after a non-mouse event.
74908                                 return;
74909                             }
74910                         }
74911
74912                         // don't show if buttons are pressed, e.g. during click and drag of map
74913                         if (event.buttons !== 0) { return; }
74914
74915                         show.apply(this, arguments);
74916                     });
74917                     anchor.on(_pointerPrefix + 'leave.popover', function() {
74918                         hide.apply(this, arguments);
74919                     });
74920
74921                 } else if (display === 'clickFocus') {
74922                     anchor
74923                         .on(_pointerPrefix + 'down.popover', function() {
74924                             event.preventDefault();
74925                             event.stopPropagation();
74926                         })
74927                         .on(_pointerPrefix + 'up.popover', function() {
74928                             event.preventDefault();
74929                             event.stopPropagation();
74930                         })
74931                         .on('click.popover', toggle);
74932
74933                     popoverSelection
74934                         .attr('tabindex', 0)
74935                         .on('blur.popover', function() {
74936                             anchor.each(function() {
74937                                 hide.apply(this, arguments);
74938                             });
74939                         });
74940                 }
74941             }
74942
74943
74944             function show() {
74945                 var anchor = select(this);
74946                 var popoverSelection = anchor.selectAll('.popover-' + _id);
74947
74948                 if (popoverSelection.empty()) {
74949                     // popover was removed somehow, put it back
74950                     anchor.call(popover.destroy);
74951                     anchor.each(setup);
74952                     popoverSelection = anchor.selectAll('.popover-' + _id);
74953                 }
74954
74955                 popoverSelection.classed('in', true);
74956
74957                 var displayType = _displayType.apply(this, arguments);
74958                 if (displayType === 'clickFocus') {
74959                     anchor.classed('active', true);
74960                     popoverSelection.node().focus();
74961                 }
74962
74963                 anchor.each(updateContent);
74964             }
74965
74966             function updateContent() {
74967                 var anchor = select(this);
74968
74969                 if (_content) {
74970                     anchor.selectAll('.popover-' + _id + ' > .popover-inner')
74971                         .call(_content.apply(this, arguments));
74972                 }
74973
74974                 updatePosition.apply(this, arguments);
74975                 // hack: update multiple times to fix instances where the absolute offset is
74976                 // set before the dynamic popover size is calculated by the browser
74977                 updatePosition.apply(this, arguments);
74978                 updatePosition.apply(this, arguments);
74979             }
74980
74981
74982             function updatePosition() {
74983
74984                 var anchor = select(this);
74985                 var popoverSelection = anchor.selectAll('.popover-' + _id);
74986
74987                 var scrollContainer = _scrollContainer && _scrollContainer.apply(this, arguments);
74988                 var scrollNode = scrollContainer && !scrollContainer.empty() && scrollContainer.node();
74989                 var scrollLeft = scrollNode ? scrollNode.scrollLeft : 0;
74990                 var scrollTop = scrollNode ? scrollNode.scrollTop : 0;
74991
74992                 var placement = _placement.apply(this, arguments);
74993                 popoverSelection
74994                     .classed('left', false)
74995                     .classed('right', false)
74996                     .classed('top', false)
74997                     .classed('bottom', false)
74998                     .classed(placement, true);
74999
75000                 var alignment = _alignment.apply(this, arguments);
75001                 var alignFactor = 0.5;
75002                 if (alignment === 'leading') {
75003                     alignFactor = 0;
75004                 } else if (alignment === 'trailing') {
75005                     alignFactor = 1;
75006                 }
75007                 var anchorFrame = getFrame(anchor.node());
75008                 var popoverFrame = getFrame(popoverSelection.node());
75009                 var position;
75010
75011                 switch (placement) {
75012                     case 'top':
75013                     position = {
75014                         x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
75015                         y: anchorFrame.y - popoverFrame.h
75016                     };
75017                     break;
75018                     case 'bottom':
75019                     position = {
75020                         x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
75021                         y: anchorFrame.y + anchorFrame.h
75022                     };
75023                     break;
75024                     case 'left':
75025                     position = {
75026                         x: anchorFrame.x - popoverFrame.w,
75027                         y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
75028                     };
75029                     break;
75030                     case 'right':
75031                     position = {
75032                         x: anchorFrame.x + anchorFrame.w,
75033                         y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
75034                     };
75035                     break;
75036                 }
75037
75038                 if (position) {
75039
75040                     if (scrollNode && (placement === 'top' || placement === 'bottom')) {
75041
75042                         var initialPosX = position.x;
75043
75044                         if (position.x + popoverFrame.w > scrollNode.offsetWidth - 10) {
75045                             position.x = scrollNode.offsetWidth - 10 - popoverFrame.w;
75046                         } else if (position.x < 10) {
75047                             position.x = 10;
75048                         }
75049
75050                         var arrow = anchor.selectAll('.popover-' + _id + ' > .popover-arrow');
75051                         // keep the arrow centered on the button, or as close as possible
75052                         var arrowPosX = Math.min(Math.max(popoverFrame.w / 2 - (position.x - initialPosX), 10), popoverFrame.w - 10);
75053                         arrow.style('left', ~~arrowPosX + 'px');
75054                     }
75055
75056                     popoverSelection.style('left', ~~position.x + 'px').style('top', ~~position.y + 'px');
75057                 } else {
75058                     popoverSelection.style('left', null).style('top', null);
75059                 }
75060
75061                 function getFrame(node) {
75062                     var positionStyle = select(node).style('position');
75063                     if (positionStyle === 'absolute' || positionStyle === 'static') {
75064                         return {
75065                             x: node.offsetLeft - scrollLeft,
75066                             y: node.offsetTop - scrollTop,
75067                             w: node.offsetWidth,
75068                             h: node.offsetHeight
75069                         };
75070                     } else {
75071                         return {
75072                             x: 0,
75073                             y: 0,
75074                             w: node.offsetWidth,
75075                             h: node.offsetHeight
75076                         };
75077                     }
75078                 }
75079             }
75080
75081
75082             function hide() {
75083                 var anchor = select(this);
75084                 if (_displayType.apply(this, arguments) === 'clickFocus') {
75085                     anchor.classed('active', false);
75086                 }
75087                 anchor.selectAll('.popover-' + _id).classed('in', false);
75088             }
75089
75090
75091             function toggle() {
75092                 if (select(this).select('.popover-' + _id).classed('in')) {
75093                     hide.apply(this, arguments);
75094                 } else {
75095                     show.apply(this, arguments);
75096                 }
75097             }
75098
75099
75100             return popover;
75101         }
75102
75103         function uiTooltip(klass) {
75104
75105             var tooltip = uiPopover((klass || '') + ' tooltip')
75106                 .displayType('hover');
75107
75108             var _title = function() {
75109                 var title = this.getAttribute('data-original-title');
75110                 if (title) {
75111                     return title;
75112                 } else {
75113                     title = this.getAttribute('title');
75114                     this.removeAttribute('title');
75115                     this.setAttribute('data-original-title', title);
75116                 }
75117                 return title;
75118             };
75119
75120             var _heading = utilFunctor(null);
75121             var _keys = utilFunctor(null);
75122
75123             tooltip.title = function(val) {
75124                 if (!arguments.length) { return _title; }
75125                 _title = utilFunctor(val);
75126                 return tooltip;
75127             };
75128
75129             tooltip.heading = function(val) {
75130                 if (!arguments.length) { return _heading; }
75131                 _heading = utilFunctor(val);
75132                 return tooltip;
75133             };
75134
75135             tooltip.keys = function(val) {
75136                 if (!arguments.length) { return _keys; }
75137                 _keys = utilFunctor(val);
75138                 return tooltip;
75139             };
75140
75141             tooltip.content(function() {
75142                 var heading = _heading.apply(this, arguments);
75143                 var text = _title.apply(this, arguments);
75144                 var keys = _keys.apply(this, arguments);
75145
75146                 return function(selection) {
75147
75148                     var headingSelect = selection
75149                         .selectAll('.tooltip-heading')
75150                         .data(heading ? [heading] :[]);
75151
75152                     headingSelect.exit()
75153                         .remove();
75154
75155                     headingSelect.enter()
75156                         .append('div')
75157                         .attr('class', 'tooltip-heading')
75158                         .merge(headingSelect)
75159                         .html(heading);
75160
75161                     var textSelect = selection
75162                         .selectAll('.tooltip-text')
75163                         .data(text ? [text] :[]);
75164
75165                     textSelect.exit()
75166                         .remove();
75167
75168                     textSelect.enter()
75169                         .append('div')
75170                         .attr('class', 'tooltip-text')
75171                         .merge(textSelect)
75172                         .html(text);
75173
75174                     var keyhintWrap = selection
75175                         .selectAll('.keyhint-wrap')
75176                         .data(keys && keys.length ? [0] : []);
75177
75178                     keyhintWrap.exit()
75179                         .remove();
75180
75181                     var keyhintWrapEnter = keyhintWrap.enter()
75182                         .append('div')
75183                         .attr('class', 'keyhint-wrap');
75184
75185                     keyhintWrapEnter
75186                         .append('span')
75187                         .html(_t('tooltip_keyhint'));
75188
75189                     keyhintWrap = keyhintWrapEnter.merge(keyhintWrap);
75190
75191                     keyhintWrap.selectAll('kbd.shortcut')
75192                         .data(keys && keys.length ? keys : [])
75193                         .enter()
75194                         .append('kbd')
75195                         .attr('class', 'shortcut')
75196                         .html(function(d) {
75197                             return d;
75198                         });
75199                 };
75200             });
75201
75202             return tooltip;
75203         }
75204
75205         function uiEditMenu(context) {
75206             var dispatch$1 = dispatch('toggled');
75207
75208             var _menu = select(null);
75209             var _operations = [];
75210             // the position the menu should be displayed relative to
75211             var _anchorLoc = [0, 0];
75212             var _anchorLocLonLat = [0, 0];
75213             // a string indicating how the menu was opened
75214             var _triggerType = '';
75215
75216             var _vpTopMargin = 85; // viewport top margin
75217             var _vpBottomMargin = 45; // viewport bottom margin
75218             var _vpSideMargin = 35;   // viewport side margin
75219
75220             var _menuTop = false;
75221             var _menuHeight;
75222             var _menuWidth;
75223
75224             // hardcode these values to make menu positioning easier
75225             var _verticalPadding = 4;
75226
75227             // see also `.edit-menu .tooltip` CSS; include margin
75228             var _tooltipWidth = 210;
75229
75230             // offset the menu slightly from the target location
75231             var _menuSideMargin = 10;
75232
75233             var _tooltips = [];
75234
75235             var editMenu = function(selection) {
75236
75237                 var isTouchMenu = _triggerType.includes('touch') || _triggerType.includes('pen');
75238
75239                 var ops = _operations.filter(function(op) {
75240                     return !isTouchMenu || !op.mouseOnly;
75241                 });
75242
75243                 if (!ops.length) { return; }
75244
75245                 _tooltips = [];
75246
75247                 // Position the menu above the anchor for stylus and finger input
75248                 // since the mapper's hand likely obscures the screen below the anchor
75249                 _menuTop = isTouchMenu;
75250
75251                 // Show labels for touch input since there aren't hover tooltips
75252                 var showLabels = isTouchMenu;
75253
75254                 var buttonHeight = showLabels ? 32 : 34;
75255                 if (showLabels) {
75256                     // Get a general idea of the width based on the length of the label
75257                     _menuWidth = 52 + Math.min(120, 6 * Math.max.apply(Math, ops.map(function(op) {
75258                         return op.title.length;
75259                     })));
75260                 } else {
75261                     _menuWidth = 44;
75262                 }
75263
75264                 _menuHeight = _verticalPadding * 2 + ops.length * buttonHeight;
75265
75266                 _menu = selection
75267                     .append('div')
75268                     .attr('class', 'edit-menu')
75269                     .classed('touch-menu', isTouchMenu)
75270                     .style('padding', _verticalPadding + 'px 0');
75271
75272                 var buttons = _menu.selectAll('.edit-menu-item')
75273                     .data(ops);
75274
75275                 // enter
75276                 var buttonsEnter = buttons.enter()
75277                     .append('button')
75278                     .attr('class', function (d) { return 'edit-menu-item edit-menu-item-' + d.id; })
75279                     .style('height', buttonHeight + 'px')
75280                     .on('click', click)
75281                     // don't listen for `mouseup` because we only care about non-mouse pointer types
75282                     .on('pointerup', pointerup)
75283                     .on('pointerdown mousedown', function pointerdown() {
75284                         // don't let button presses also act as map input - #1869
75285                         event.stopPropagation();
75286                     });
75287
75288                 buttonsEnter.each(function(d) {
75289                     var tooltip = uiTooltip()
75290                         .heading(d.title)
75291                         .title(d.tooltip())
75292                         .keys([d.keys[0]]);
75293
75294                     _tooltips.push(tooltip);
75295
75296                     select(this)
75297                         .call(tooltip)
75298                         .append('div')
75299                         .attr('class', 'icon-wrap')
75300                         .call(svgIcon('#iD-operation-' + d.id, 'operation'));
75301                 });
75302
75303                 if (showLabels) {
75304                     buttonsEnter.append('span')
75305                         .attr('class', 'label')
75306                         .text(function(d) {
75307                             return d.title;
75308                         });
75309                 }
75310
75311                 // update
75312                 buttons = buttonsEnter
75313                     .merge(buttons)
75314                     .classed('disabled', function(d) { return d.disabled(); });
75315
75316                 updatePosition();
75317
75318                 var initialScale = context.projection.scale();
75319                 context.map()
75320                     .on('move.edit-menu', function() {
75321                         if (initialScale !== context.projection.scale()) {
75322                             editMenu.close();
75323                         }
75324                     })
75325                     .on('drawn.edit-menu', function(info) {
75326                         if (info.full) { updatePosition(); }
75327                     });
75328
75329                 var lastPointerUpType;
75330                 // `pointerup` is always called before `click`
75331                 function pointerup() {
75332                     lastPointerUpType = event.pointerType;
75333                 }
75334
75335                 function click(operation) {
75336                     event.stopPropagation();
75337                     if (operation.disabled()) {
75338                         if (lastPointerUpType === 'touch' ||
75339                             lastPointerUpType === 'pen') {
75340                             // there are no tooltips for touch interactions so flash feedback instead
75341                             context.ui().flash
75342                                 .duration(4000)
75343                                 .iconName('#iD-operation-' + operation.id)
75344                                 .iconClass('operation disabled')
75345                                 .text(operation.tooltip)();
75346                         }
75347                     } else {
75348                         if (lastPointerUpType === 'touch' ||
75349                             lastPointerUpType === 'pen') {
75350                             context.ui().flash
75351                                 .duration(2000)
75352                                 .iconName('#iD-operation-' + operation.id)
75353                                 .iconClass('operation')
75354                                 .text(operation.annotation() || operation.title)();
75355                         }
75356
75357                         operation();
75358                         editMenu.close();
75359                     }
75360                     lastPointerUpType = null;
75361                 }
75362
75363                 dispatch$1.call('toggled', this, true);
75364             };
75365
75366             function updatePosition() {
75367
75368                 if (!_menu || _menu.empty()) { return; }
75369
75370                 var anchorLoc = context.projection(_anchorLocLonLat);
75371
75372                 var viewport = context.surfaceRect();
75373
75374                 if (anchorLoc[0] < 0 ||
75375                     anchorLoc[0] > viewport.width ||
75376                     anchorLoc[1] < 0 ||
75377                     anchorLoc[1] > viewport.height) {
75378                     // close the menu if it's gone offscreen
75379
75380                     editMenu.close();
75381                     return;
75382                 }
75383
75384                 var menuLeft = displayOnLeft(viewport);
75385
75386                 var offset = [0, 0];
75387
75388                 offset[0] = menuLeft ? -1 * (_menuSideMargin + _menuWidth) : _menuSideMargin;
75389
75390                 if (_menuTop) {
75391                     if (anchorLoc[1] - _menuHeight < _vpTopMargin) {
75392                         // menu is near top viewport edge, shift downward
75393                         offset[1] = -anchorLoc[1] + _vpTopMargin;
75394                     } else {
75395                         offset[1] = -_menuHeight;
75396                     }
75397                 } else {
75398                     if (anchorLoc[1] + _menuHeight > (viewport.height - _vpBottomMargin)) {
75399                         // menu is near bottom viewport edge, shift upwards
75400                         offset[1] = -anchorLoc[1] - _menuHeight + viewport.height - _vpBottomMargin;
75401                     } else {
75402                         offset[1] = 0;
75403                     }
75404                 }
75405
75406                 var origin = geoVecAdd(anchorLoc, offset);
75407
75408                 _menu
75409                     .style('left', origin[0] + 'px')
75410                     .style('top', origin[1] + 'px');
75411
75412                 var tooltipSide = tooltipPosition(viewport, menuLeft);
75413                 _tooltips.forEach(function(tooltip) {
75414                     tooltip.placement(tooltipSide);
75415                 });
75416
75417                 function displayOnLeft(viewport) {
75418                     if (_mainLocalizer.textDirection() === 'ltr') {
75419                         if ((anchorLoc[0] + _menuSideMargin + _menuWidth) > (viewport.width - _vpSideMargin)) {
75420                             // right menu would be too close to the right viewport edge, go left
75421                             return true;
75422                         }
75423                         // prefer right menu
75424                         return false;
75425
75426                     } else { // rtl
75427                         if ((anchorLoc[0] - _menuSideMargin - _menuWidth) < _vpSideMargin) {
75428                             // left menu would be too close to the left viewport edge, go right
75429                             return false;
75430                         }
75431                         // prefer left menu
75432                         return true;
75433                     }
75434                 }
75435
75436                 function tooltipPosition(viewport, menuLeft) {
75437                     if (_mainLocalizer.textDirection() === 'ltr') {
75438                         if (menuLeft) {
75439                             // if there's not room for a right-side menu then there definitely
75440                             // isn't room for right-side tooltips
75441                             return 'left';
75442                         }
75443                         if ((anchorLoc[0] + _menuSideMargin + _menuWidth + _tooltipWidth) > (viewport.width - _vpSideMargin)) {
75444                             // right tooltips would be too close to the right viewport edge, go left
75445                             return 'left';
75446                         }
75447                         // prefer right tooltips
75448                         return 'right';
75449
75450                     } else { // rtl
75451                         if (!menuLeft) {
75452                             return 'right';
75453                         }
75454                         if ((anchorLoc[0] - _menuSideMargin - _menuWidth - _tooltipWidth) < _vpSideMargin) {
75455                             // left tooltips would be too close to the left viewport edge, go right
75456                             return 'right';
75457                         }
75458                         // prefer left tooltips
75459                         return 'left';
75460                     }
75461                 }
75462             }
75463
75464             editMenu.close = function () {
75465
75466                 context.map()
75467                     .on('move.edit-menu', null)
75468                     .on('drawn.edit-menu', null);
75469
75470                 _menu.remove();
75471                 _tooltips = [];
75472
75473                 dispatch$1.call('toggled', this, false);
75474             };
75475
75476             editMenu.anchorLoc = function(val) {
75477                 if (!arguments.length) { return _anchorLoc; }
75478                 _anchorLoc = val;
75479                 _anchorLocLonLat = context.projection.invert(_anchorLoc);
75480                 return editMenu;
75481             };
75482
75483             editMenu.triggerType = function(val) {
75484                 if (!arguments.length) { return _triggerType; }
75485                 _triggerType = val;
75486                 return editMenu;
75487             };
75488
75489             editMenu.operations = function(val) {
75490                 if (!arguments.length) { return _operations; }
75491                 _operations = val;
75492                 return editMenu;
75493             };
75494
75495             return utilRebind(editMenu, dispatch$1, 'on');
75496         }
75497
75498         function uiFeatureInfo(context) {
75499             function update(selection) {
75500                 var features = context.features();
75501                 var stats = features.stats();
75502                 var count = 0;
75503                 var hiddenList = features.hidden().map(function(k) {
75504                     if (stats[k]) {
75505                         count += stats[k];
75506                         return String(stats[k]) + ' ' + _t('feature.' + k + '.description');
75507                     }
75508                 }).filter(Boolean);
75509
75510                 selection.html('');
75511
75512                 if (hiddenList.length) {
75513                     var tooltipBehavior = uiTooltip()
75514                         .placement('top')
75515                         .title(function() {
75516                             return hiddenList.join('<br/>');
75517                         });
75518
75519                     selection.append('a')
75520                         .attr('class', 'chip')
75521                         .attr('href', '#')
75522                         .attr('tabindex', -1)
75523                         .html(_t('feature_info.hidden_warning', { count: count }))
75524                         .call(tooltipBehavior)
75525                         .on('click', function() {
75526                             tooltipBehavior.hide();
75527                             event.preventDefault();
75528                             // open the Map Data pane
75529                             context.ui().togglePanes(context.container().select('.map-panes .map-data-pane'));
75530                         });
75531                 }
75532
75533                 selection
75534                     .classed('hide', !hiddenList.length);
75535             }
75536
75537
75538             return function(selection) {
75539                 update(selection);
75540
75541                 context.features().on('change.feature_info', function() {
75542                     update(selection);
75543                 });
75544             };
75545         }
75546
75547         function uiFlash(context) {
75548             var _flashTimer;
75549
75550             var _duration = 2000;
75551             var _iconName = '#iD-icon-no';
75552             var _iconClass = 'disabled';
75553             var _text = '';
75554             var _textClass;
75555
75556             function flash() {
75557                 if (_flashTimer) {
75558                     _flashTimer.stop();
75559                 }
75560
75561                 context.container().select('.main-footer-wrap')
75562                     .classed('footer-hide', true)
75563                     .classed('footer-show', false);
75564                 context.container().select('.flash-wrap')
75565                     .classed('footer-hide', false)
75566                     .classed('footer-show', true);
75567
75568                 var content = context.container().select('.flash-wrap').selectAll('.flash-content')
75569                     .data([0]);
75570
75571                 // Enter
75572                 var contentEnter = content.enter()
75573                     .append('div')
75574                     .attr('class', 'flash-content');
75575
75576                 var iconEnter = contentEnter
75577                     .append('svg')
75578                     .attr('class', 'flash-icon icon')
75579                     .append('g')
75580                     .attr('transform', 'translate(10,10)');
75581
75582                 iconEnter
75583                     .append('circle')
75584                     .attr('r', 9);
75585
75586                 iconEnter
75587                     .append('use')
75588                     .attr('transform', 'translate(-7,-7)')
75589                     .attr('width', '14')
75590                     .attr('height', '14');
75591
75592                 contentEnter
75593                     .append('div')
75594                     .attr('class', 'flash-text');
75595
75596
75597                 // Update
75598                 content = content
75599                     .merge(contentEnter);
75600
75601                 content
75602                     .selectAll('.flash-icon')
75603                     .attr('class', 'icon flash-icon ' + (_iconClass || ''));
75604
75605                 content
75606                     .selectAll('.flash-icon use')
75607                     .attr('xlink:href', _iconName);
75608
75609                 content
75610                     .selectAll('.flash-text')
75611                     .attr('class', 'flash-text ' + (_textClass || ''))
75612                     .text(_text);
75613
75614
75615                 _flashTimer = d3_timeout(function() {
75616                     _flashTimer = null;
75617                     context.container().select('.main-footer-wrap')
75618                         .classed('footer-hide', false)
75619                         .classed('footer-show', true);
75620                     context.container().select('.flash-wrap')
75621                         .classed('footer-hide', true)
75622                         .classed('footer-show', false);
75623                 }, _duration);
75624
75625                 return content;
75626             }
75627
75628
75629             flash.duration = function(_) {
75630                 if (!arguments.length) { return _duration; }
75631                 _duration = _;
75632                 return flash;
75633             };
75634
75635             flash.text = function(_) {
75636                 if (!arguments.length) { return _text; }
75637                 _text = _;
75638                 return flash;
75639             };
75640
75641             flash.textClass = function(_) {
75642                 if (!arguments.length) { return _textClass; }
75643                 _textClass = _;
75644                 return flash;
75645             };
75646
75647             flash.iconName = function(_) {
75648                 if (!arguments.length) { return _iconName; }
75649                 _iconName = _;
75650                 return flash;
75651             };
75652
75653             flash.iconClass = function(_) {
75654                 if (!arguments.length) { return _iconClass; }
75655                 _iconClass = _;
75656                 return flash;
75657             };
75658
75659             return flash;
75660         }
75661
75662         function uiFullScreen(context) {
75663             var element = context.container().node();
75664             // var button = d3_select(null);
75665
75666
75667             function getFullScreenFn() {
75668                 if (element.requestFullscreen) {
75669                     return element.requestFullscreen;
75670                 } else if (element.msRequestFullscreen) {
75671                     return element.msRequestFullscreen;
75672                 } else if (element.mozRequestFullScreen) {
75673                     return element.mozRequestFullScreen;
75674                 } else if (element.webkitRequestFullscreen) {
75675                     return element.webkitRequestFullscreen;
75676                 }
75677             }
75678
75679
75680             function getExitFullScreenFn() {
75681                 if (document.exitFullscreen) {
75682                     return document.exitFullscreen;
75683                 } else if (document.msExitFullscreen) {
75684                     return document.msExitFullscreen;
75685                 } else if (document.mozCancelFullScreen) {
75686                     return document.mozCancelFullScreen;
75687                 } else if (document.webkitExitFullscreen) {
75688                     return document.webkitExitFullscreen;
75689                 }
75690             }
75691
75692
75693             function isFullScreen() {
75694                 return document.fullscreenElement ||
75695                     document.mozFullScreenElement ||
75696                     document.webkitFullscreenElement ||
75697                     document.msFullscreenElement;
75698             }
75699
75700
75701             function isSupported() {
75702                 return !!getFullScreenFn();
75703             }
75704
75705
75706             function fullScreen() {
75707                 event.preventDefault();
75708                 if (!isFullScreen()) {
75709                     // button.classed('active', true);
75710                     getFullScreenFn().apply(element);
75711                 } else {
75712                     // button.classed('active', false);
75713                     getExitFullScreenFn().apply(document);
75714                 }
75715             }
75716
75717
75718             return function() { // selection) {
75719                 if (!isSupported()) { return; }
75720
75721                 // button = selection.append('button')
75722                 //     .attr('title', t('full_screen'))
75723                 //     .attr('tabindex', -1)
75724                 //     .on('click', fullScreen)
75725                 //     .call(tooltip);
75726
75727                 // button.append('span')
75728                 //     .attr('class', 'icon full-screen');
75729
75730                 var detected = utilDetect();
75731                 var keys = (detected.os === 'mac' ? [uiCmd('⌃⌘F'), 'f11'] : ['f11']);
75732                 context.keybinding().on(keys, fullScreen);
75733             };
75734         }
75735
75736         function uiGeolocate(context) {
75737             var _geolocationOptions = {
75738                 // prioritize speed and power usage over precision
75739                 enableHighAccuracy: false,
75740                 // don't hang indefinitely getting the location
75741                 timeout: 6000 // 6sec
75742             };
75743             var _locating = uiLoading(context).message(_t('geolocate.locating')).blocking(true);
75744             var _layer = context.layers().layer('geolocate');
75745             var _position;
75746             var _extent;
75747             var _timeoutID;
75748             var _button = select(null);
75749
75750             function click() {
75751                 if (context.inIntro()) { return; }
75752                 if (!_layer.enabled() && !_locating.isShown()) {
75753
75754                     // This timeout ensures that we still call finish() even if
75755                     // the user declines to share their location in Firefox
75756                     _timeoutID = setTimeout(error, 10000 /* 10sec */ );
75757
75758                     context.container().call(_locating);
75759                     // get the latest position even if we already have one
75760                     navigator.geolocation.getCurrentPosition(success, error, _geolocationOptions);
75761                 } else {
75762                     _locating.close();
75763                     _layer.enabled(null, false);
75764                     updateButtonState();
75765                 }
75766             }
75767
75768             function zoomTo() {
75769                 context.enter(modeBrowse(context));
75770
75771                 var map = context.map();
75772                 _layer.enabled(_position, true);
75773                 updateButtonState();
75774                 map.centerZoomEase(_extent.center(), Math.min(20, map.extentZoom(_extent)));
75775             }
75776
75777             function success(geolocation) {
75778                 _position = geolocation;
75779                 var coords = _position.coords;
75780                 _extent = geoExtent([coords.longitude, coords.latitude]).padByMeters(coords.accuracy);
75781                 zoomTo();
75782                 finish();
75783             }
75784
75785             function error() {
75786                 if (_position) {
75787                     // use the position from a previous call if we have one
75788                     zoomTo();
75789                 } else {
75790                     context.ui().flash
75791                         .text(_t('geolocate.location_unavailable'))
75792                         .iconName('#iD-icon-geolocate')();
75793                 }
75794
75795                 finish();
75796             }
75797
75798             function finish() {
75799                 _locating.close();  // unblock ui
75800                 if (_timeoutID) { clearTimeout(_timeoutID); }
75801                 _timeoutID = undefined;
75802             }
75803
75804             function updateButtonState() {
75805                 _button.classed('active', _layer.enabled());
75806             }
75807
75808             return function(selection) {
75809                 if (!navigator.geolocation || !navigator.geolocation.getCurrentPosition) { return; }
75810
75811                 _button = selection
75812                     .append('button')
75813                     .on('click', click)
75814                     .call(svgIcon('#iD-icon-geolocate', 'light'))
75815                     .call(uiTooltip()
75816                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
75817                         .title(_t('geolocate.title'))
75818                         .keys([_t('geolocate.key')])
75819                     );
75820
75821                 context.keybinding().on(_t('geolocate.key'), click);
75822             };
75823         }
75824
75825         function uiPanelBackground(context) {
75826             var background = context.background();
75827             var currSourceName = null;
75828             var metadata = {};
75829             var metadataKeys = [
75830                 'zoom', 'vintage', 'source', 'description', 'resolution', 'accuracy'
75831             ];
75832
75833             var debouncedRedraw = debounce(redraw, 250);
75834
75835             function redraw(selection) {
75836                 var source = background.baseLayerSource();
75837                 if (!source) { return; }
75838
75839                 var isDG = (source.id.match(/^DigitalGlobe/i) !== null);
75840
75841                 if (currSourceName !== source.name()) {
75842                     currSourceName = source.name();
75843                     metadata = {};
75844                 }
75845
75846                 selection.html('');
75847
75848                 var list = selection
75849                     .append('ul')
75850                     .attr('class', 'background-info');
75851
75852                 list
75853                     .append('li')
75854                     .text(currSourceName);
75855
75856                 metadataKeys.forEach(function(k) {
75857                     // DigitalGlobe vintage is available in raster layers for now.
75858                     if (isDG && k === 'vintage') { return; }
75859
75860                     list
75861                         .append('li')
75862                         .attr('class', 'background-info-list-' + k)
75863                         .classed('hide', !metadata[k])
75864                         .text(_t('info_panels.background.' + k) + ':')
75865                         .append('span')
75866                         .attr('class', 'background-info-span-' + k)
75867                         .text(metadata[k]);
75868                 });
75869
75870                 debouncedGetMetadata(selection);
75871
75872                 var toggleTiles = context.getDebug('tile') ? 'hide_tiles' : 'show_tiles';
75873
75874                 selection
75875                     .append('a')
75876                     .text(_t('info_panels.background.' + toggleTiles))
75877                     .attr('href', '#')
75878                     .attr('class', 'button button-toggle-tiles')
75879                     .on('click', function() {
75880                         event.preventDefault();
75881                         context.setDebug('tile', !context.getDebug('tile'));
75882                         selection.call(redraw);
75883                     });
75884
75885                 if (isDG) {
75886                     var key = source.id + '-vintage';
75887                     var sourceVintage = context.background().findSource(key);
75888                     var showsVintage = context.background().showsLayer(sourceVintage);
75889                     var toggleVintage = showsVintage ? 'hide_vintage' : 'show_vintage';
75890                     selection
75891                         .append('a')
75892                         .text(_t('info_panels.background.' + toggleVintage))
75893                         .attr('href', '#')
75894                         .attr('class', 'button button-toggle-vintage')
75895                         .on('click', function() {
75896                             event.preventDefault();
75897                             context.background().toggleOverlayLayer(sourceVintage);
75898                             selection.call(redraw);
75899                         });
75900                 }
75901
75902                 // disable if necessary
75903                 ['DigitalGlobe-Premium', 'DigitalGlobe-Standard'].forEach(function(layerId) {
75904                     if (source.id !== layerId) {
75905                         var key = layerId + '-vintage';
75906                         var sourceVintage = context.background().findSource(key);
75907                         if (context.background().showsLayer(sourceVintage)) {
75908                             context.background().toggleOverlayLayer(sourceVintage);
75909                         }
75910                     }
75911                 });
75912             }
75913
75914
75915             var debouncedGetMetadata = debounce(getMetadata, 250);
75916
75917             function getMetadata(selection) {
75918                 var tile = context.container().select('.layer-background img.tile-center');   // tile near viewport center
75919                 if (tile.empty()) { return; }
75920
75921                 var sourceName = currSourceName;
75922                 var d = tile.datum();
75923                 var zoom = (d && d.length >= 3 && d[2]) || Math.floor(context.map().zoom());
75924                 var center = context.map().center();
75925
75926                 // update zoom
75927                 metadata.zoom = String(zoom);
75928                 selection.selectAll('.background-info-list-zoom')
75929                     .classed('hide', false)
75930                     .selectAll('.background-info-span-zoom')
75931                     .text(metadata.zoom);
75932
75933                 if (!d || !d.length >= 3) { return; }
75934
75935                 background.baseLayerSource().getMetadata(center, d, function(err, result) {
75936                     if (err || currSourceName !== sourceName) { return; }
75937
75938                     // update vintage
75939                     var vintage = result.vintage;
75940                     metadata.vintage = (vintage && vintage.range) || _t('info_panels.background.unknown');
75941                     selection.selectAll('.background-info-list-vintage')
75942                         .classed('hide', false)
75943                         .selectAll('.background-info-span-vintage')
75944                         .text(metadata.vintage);
75945
75946                     // update other metdata
75947                     metadataKeys.forEach(function(k) {
75948                         if (k === 'zoom' || k === 'vintage') { return; }  // done already
75949                         var val = result[k];
75950                         metadata[k] = val;
75951                         selection.selectAll('.background-info-list-' + k)
75952                             .classed('hide', !val)
75953                             .selectAll('.background-info-span-' + k)
75954                             .text(val);
75955                     });
75956                 });
75957             }
75958
75959
75960             var panel = function(selection) {
75961                 selection.call(redraw);
75962
75963                 context.map()
75964                     .on('drawn.info-background', function() {
75965                         selection.call(debouncedRedraw);
75966                     })
75967                     .on('move.info-background', function() {
75968                         selection.call(debouncedGetMetadata);
75969                     });
75970
75971             };
75972
75973             panel.off = function() {
75974                 context.map()
75975                     .on('drawn.info-background', null)
75976                     .on('move.info-background', null);
75977             };
75978
75979             panel.id = 'background';
75980             panel.title = _t('info_panels.background.title');
75981             panel.key = _t('info_panels.background.key');
75982
75983
75984             return panel;
75985         }
75986
75987         function uiPanelHistory(context) {
75988             var osm;
75989
75990             function displayTimestamp(timestamp) {
75991                 if (!timestamp) { return _t('info_panels.history.unknown'); }
75992                 var options = {
75993                     day: 'numeric', month: 'short', year: 'numeric',
75994                     hour: 'numeric', minute: 'numeric', second: 'numeric'
75995                 };
75996                 var d = new Date(timestamp);
75997                 if (isNaN(d.getTime())) { return _t('info_panels.history.unknown'); }
75998                 return d.toLocaleString(_mainLocalizer.localeCode(), options);
75999             }
76000
76001
76002             function displayUser(selection, userName) {
76003                 if (!userName) {
76004                     selection
76005                         .append('span')
76006                         .text(_t('info_panels.history.unknown'));
76007                     return;
76008                 }
76009
76010                 selection
76011                     .append('span')
76012                     .attr('class', 'user-name')
76013                     .text(userName);
76014
76015                 var links = selection
76016                     .append('div')
76017                     .attr('class', 'links');
76018
76019                 if (osm) {
76020                     links
76021                         .append('a')
76022                         .attr('class', 'user-osm-link')
76023                         .attr('href', osm.userURL(userName))
76024                         .attr('target', '_blank')
76025                         .attr('tabindex', -1)
76026                         .text('OSM');
76027                 }
76028
76029                 links
76030                     .append('a')
76031                     .attr('class', 'user-hdyc-link')
76032                     .attr('href', 'https://hdyc.neis-one.org/?' + userName)
76033                     .attr('target', '_blank')
76034                     .attr('tabindex', -1)
76035                     .text('HDYC');
76036             }
76037
76038
76039             function displayChangeset(selection, changeset) {
76040                 if (!changeset) {
76041                     selection
76042                         .append('span')
76043                         .text(_t('info_panels.history.unknown'));
76044                     return;
76045                 }
76046
76047                 selection
76048                     .append('span')
76049                     .attr('class', 'changeset-id')
76050                     .text(changeset);
76051
76052                 var links = selection
76053                     .append('div')
76054                     .attr('class', 'links');
76055
76056                 if (osm) {
76057                     links
76058                         .append('a')
76059                         .attr('class', 'changeset-osm-link')
76060                         .attr('href', osm.changesetURL(changeset))
76061                         .attr('target', '_blank')
76062                         .attr('tabindex', -1)
76063                         .text('OSM');
76064                 }
76065
76066                 links
76067                     .append('a')
76068                     .attr('class', 'changeset-osmcha-link')
76069                     .attr('href', 'https://osmcha.org/changesets/' + changeset)
76070                     .attr('target', '_blank')
76071                     .attr('tabindex', -1)
76072                     .text('OSMCha');
76073
76074                 links
76075                     .append('a')
76076                     .attr('class', 'changeset-achavi-link')
76077                     .attr('href', 'https://overpass-api.de/achavi/?changeset=' + changeset)
76078                     .attr('target', '_blank')
76079                     .attr('tabindex', -1)
76080                     .text('Achavi');
76081             }
76082
76083
76084             function redraw(selection) {
76085                 var selectedNoteID = context.selectedNoteID();
76086                 osm = context.connection();
76087
76088                 var selected, note, entity;
76089                 if (selectedNoteID && osm) {       // selected 1 note
76090                     selected = [ _t('note.note') + ' ' + selectedNoteID ];
76091                     note = osm.getNote(selectedNoteID);
76092                 } else {                           // selected 1..n entities
76093                     selected = context.selectedIDs()
76094                         .filter(function(e) { return context.hasEntity(e); });
76095                     if (selected.length) {
76096                         entity = context.entity(selected[0]);
76097                     }
76098                 }
76099
76100                 var singular = selected.length === 1 ? selected[0] : null;
76101
76102                 selection.html('');
76103
76104                 selection
76105                     .append('h4')
76106                     .attr('class', 'history-heading')
76107                     .text(singular || _t('info_panels.history.selected', { n: selected.length }));
76108
76109                 if (!singular) { return; }
76110
76111                 if (entity) {
76112                     selection.call(redrawEntity, entity);
76113                 } else if (note) {
76114                     selection.call(redrawNote, note);
76115                 }
76116             }
76117
76118
76119             function redrawNote(selection, note) {
76120                 if (!note || note.isNew()) {
76121                     selection
76122                         .append('div')
76123                         .text(_t('info_panels.history.note_no_history'));
76124                     return;
76125                 }
76126
76127                 var list = selection
76128                     .append('ul');
76129
76130                 list
76131                     .append('li')
76132                     .text(_t('info_panels.history.note_comments') + ':')
76133                     .append('span')
76134                     .text(note.comments.length);
76135
76136                 if (note.comments.length) {
76137                     list
76138                         .append('li')
76139                         .text(_t('info_panels.history.note_created_date') + ':')
76140                         .append('span')
76141                         .text(displayTimestamp(note.comments[0].date));
76142
76143                     list
76144                         .append('li')
76145                         .text(_t('info_panels.history.note_created_user') + ':')
76146                         .call(displayUser, note.comments[0].user);
76147                 }
76148
76149                 if (osm) {
76150                     selection
76151                         .append('a')
76152                         .attr('class', 'view-history-on-osm')
76153                         .attr('target', '_blank')
76154                         .attr('tabindex', -1)
76155                         .attr('href', osm.noteURL(note))
76156                         .call(svgIcon('#iD-icon-out-link', 'inline'))
76157                         .append('span')
76158                         .text(_t('info_panels.history.note_link_text'));
76159                 }
76160             }
76161
76162
76163             function redrawEntity(selection, entity) {
76164                 if (!entity || entity.isNew()) {
76165                     selection
76166                         .append('div')
76167                         .text(_t('info_panels.history.no_history'));
76168                     return;
76169                 }
76170
76171                 var links = selection
76172                     .append('div')
76173                     .attr('class', 'links');
76174
76175                 if (osm) {
76176                     links
76177                         .append('a')
76178                         .attr('class', 'view-history-on-osm')
76179                         .attr('href', osm.historyURL(entity))
76180                         .attr('target', '_blank')
76181                         .attr('tabindex', -1)
76182                         .attr('title', _t('info_panels.history.link_text'))
76183                         .text('OSM');
76184                 }
76185                 links
76186                     .append('a')
76187                     .attr('class', 'pewu-history-viewer-link')
76188                     .attr('href', 'https://pewu.github.io/osm-history/#/' + entity.type + '/' + entity.osmId())
76189                     .attr('target', '_blank')
76190                     .attr('tabindex', -1)
76191                     .text('PeWu');
76192
76193                 var list = selection
76194                     .append('ul');
76195
76196                 list
76197                     .append('li')
76198                     .text(_t('info_panels.history.version') + ':')
76199                     .append('span')
76200                     .text(entity.version);
76201
76202                 list
76203                     .append('li')
76204                     .text(_t('info_panels.history.last_edit') + ':')
76205                     .append('span')
76206                     .text(displayTimestamp(entity.timestamp));
76207
76208                 list
76209                     .append('li')
76210                     .text(_t('info_panels.history.edited_by') + ':')
76211                     .call(displayUser, entity.user);
76212
76213                 list
76214                     .append('li')
76215                     .text(_t('info_panels.history.changeset') + ':')
76216                     .call(displayChangeset, entity.changeset);
76217             }
76218
76219
76220             var panel = function(selection) {
76221                 selection.call(redraw);
76222
76223                 context.map()
76224                     .on('drawn.info-history', function() {
76225                         selection.call(redraw);
76226                     });
76227
76228                 context
76229                     .on('enter.info-history', function() {
76230                         selection.call(redraw);
76231                     });
76232             };
76233
76234             panel.off = function() {
76235                 context.map().on('drawn.info-history', null);
76236                 context.on('enter.info-history', null);
76237             };
76238
76239             panel.id = 'history';
76240             panel.title = _t('info_panels.history.title');
76241             panel.key = _t('info_panels.history.key');
76242
76243
76244             return panel;
76245         }
76246
76247         var OSM_PRECISION = 7;
76248
76249         /**
76250          * Returns a localized representation of the given length measurement.
76251          *
76252          * @param {Number} m area in meters
76253          * @param {Boolean} isImperial true for U.S. customary units; false for metric
76254          */
76255         function displayLength(m, isImperial) {
76256             var d = m * (isImperial ? 3.28084 : 1);
76257             var unit;
76258
76259             if (isImperial) {
76260                 if (d >= 5280) {
76261                     d /= 5280;
76262                     unit = 'miles';
76263                 } else {
76264                     unit = 'feet';
76265                 }
76266             } else {
76267                 if (d >= 1000) {
76268                     d /= 1000;
76269                     unit = 'kilometers';
76270                 } else {
76271                     unit = 'meters';
76272                 }
76273             }
76274
76275             return _t('units.' + unit, {
76276                 quantity: d.toLocaleString(_mainLocalizer.localeCode(), {
76277                     maximumSignificantDigits: 4
76278                 })
76279             });
76280         }
76281
76282         /**
76283          * Returns a localized representation of the given area measurement.
76284          *
76285          * @param {Number} m2 area in square meters
76286          * @param {Boolean} isImperial true for U.S. customary units; false for metric
76287          */
76288         function displayArea(m2, isImperial) {
76289             var locale = _mainLocalizer.localeCode();
76290             var d = m2 * (isImperial ? 10.7639111056 : 1);
76291             var d1, d2, area;
76292             var unit1 = '';
76293             var unit2 = '';
76294
76295             if (isImperial) {
76296                 if (d >= 6969600) { // > 0.25mi² show mi²
76297                     d1 = d / 27878400;
76298                     unit1 = 'square_miles';
76299                 } else {
76300                     d1 = d;
76301                     unit1 = 'square_feet';
76302                 }
76303
76304                 if (d > 4356 && d < 43560000) { // 0.1 - 1000 acres
76305                     d2 = d / 43560;
76306                     unit2 = 'acres';
76307                 }
76308
76309             } else {
76310                 if (d >= 250000) { // > 0.25km² show km²
76311                     d1 = d / 1000000;
76312                     unit1 = 'square_kilometers';
76313                 } else {
76314                     d1 = d;
76315                     unit1 = 'square_meters';
76316                 }
76317
76318                 if (d > 1000 && d < 10000000) { // 0.1 - 1000 hectares
76319                     d2 = d / 10000;
76320                     unit2 = 'hectares';
76321                 }
76322             }
76323
76324             area = _t('units.' + unit1, {
76325                 quantity: d1.toLocaleString(locale, {
76326                     maximumSignificantDigits: 4
76327                 })
76328             });
76329
76330             if (d2) {
76331                 return _t('units.area_pair', {
76332                     area1: area,
76333                     area2: _t('units.' + unit2, {
76334                         quantity: d2.toLocaleString(locale, {
76335                             maximumSignificantDigits: 2
76336                         })
76337                     })
76338                 });
76339             } else {
76340                 return area;
76341             }
76342         }
76343
76344         function wrap(x, min, max) {
76345             var d = max - min;
76346             return ((x - min) % d + d) % d + min;
76347         }
76348
76349         function clamp$1(x, min, max) {
76350             return Math.max(min, Math.min(x, max));
76351         }
76352
76353         function displayCoordinate(deg, pos, neg) {
76354             var locale = _mainLocalizer.localeCode();
76355             var min = (Math.abs(deg) - Math.floor(Math.abs(deg))) * 60;
76356             var sec = (min - Math.floor(min)) * 60;
76357             var displayDegrees = _t('units.arcdegrees', {
76358                 quantity: Math.floor(Math.abs(deg)).toLocaleString(locale)
76359             });
76360             var displayCoordinate;
76361
76362             if (Math.floor(sec) > 0) {
76363                 displayCoordinate = displayDegrees +
76364                     _t('units.arcminutes', {
76365                         quantity: Math.floor(min).toLocaleString(locale)
76366                     }) +
76367                     _t('units.arcseconds', {
76368                         quantity: Math.round(sec).toLocaleString(locale)
76369                     });
76370             } else if (Math.floor(min) > 0) {
76371                 displayCoordinate = displayDegrees +
76372                     _t('units.arcminutes', {
76373                         quantity: Math.round(min).toLocaleString(locale)
76374                     });
76375             } else {
76376                 displayCoordinate = _t('units.arcdegrees', {
76377                     quantity: Math.round(Math.abs(deg)).toLocaleString(locale)
76378                 });
76379             }
76380
76381             if (deg === 0) {
76382                 return displayCoordinate;
76383             } else {
76384                 return _t('units.coordinate', {
76385                     coordinate: displayCoordinate,
76386                     direction: _t('units.' + (deg > 0 ? pos : neg))
76387                 });
76388             }
76389         }
76390
76391         /**
76392          * Returns given coordinate pair in degree-minute-second format.
76393          *
76394          * @param {Array<Number>} coord longitude and latitude
76395          */
76396         function dmsCoordinatePair(coord) {
76397             return _t('units.coordinate_pair', {
76398                 latitude: displayCoordinate(clamp$1(coord[1], -90, 90), 'north', 'south'),
76399                 longitude: displayCoordinate(wrap(coord[0], -180, 180), 'east', 'west')
76400             });
76401         }
76402
76403         /**
76404          * Returns the given coordinate pair in decimal format.
76405          * note: unlocalized to avoid comma ambiguity - see #4765
76406          *
76407          * @param {Array<Number>} coord longitude and latitude
76408          */
76409         function decimalCoordinatePair(coord) {
76410             return _t('units.coordinate_pair', {
76411                 latitude: clamp$1(coord[1], -90, 90).toFixed(OSM_PRECISION),
76412                 longitude: wrap(coord[0], -180, 180).toFixed(OSM_PRECISION)
76413             });
76414         }
76415
76416         function uiPanelLocation(context) {
76417             var currLocation = '';
76418
76419
76420             function redraw(selection) {
76421                 selection.html('');
76422
76423                 var list = selection
76424                     .append('ul');
76425
76426                 // Mouse coordinates
76427                 var coord = context.map().mouseCoordinates();
76428                 if (coord.some(isNaN)) {
76429                     coord = context.map().center();
76430                 }
76431
76432                 list
76433                     .append('li')
76434                     .text(dmsCoordinatePair(coord))
76435                     .append('li')
76436                     .text(decimalCoordinatePair(coord));
76437
76438                 // Location Info
76439                 selection
76440                     .append('div')
76441                     .attr('class', 'location-info')
76442                     .text(currLocation || ' ');
76443
76444                 debouncedGetLocation(selection, coord);
76445             }
76446
76447
76448             var debouncedGetLocation = debounce(getLocation, 250);
76449             function getLocation(selection, coord) {
76450                 if (!services.geocoder) {
76451                     currLocation = _t('info_panels.location.unknown_location');
76452                     selection.selectAll('.location-info')
76453                         .text(currLocation);
76454                 } else {
76455                     services.geocoder.reverse(coord, function(err, result) {
76456                         currLocation = result ? result.display_name : _t('info_panels.location.unknown_location');
76457                         selection.selectAll('.location-info')
76458                             .text(currLocation);
76459                     });
76460                 }
76461             }
76462
76463
76464             var panel = function(selection) {
76465                 selection.call(redraw);
76466
76467                 context.surface()
76468                     .on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'move.info-location', function() {
76469                         selection.call(redraw);
76470                     });
76471             };
76472
76473             panel.off = function() {
76474                 context.surface()
76475                     .on('.info-location', null);
76476             };
76477
76478             panel.id = 'location';
76479             panel.title = _t('info_panels.location.title');
76480             panel.key = _t('info_panels.location.key');
76481
76482
76483             return panel;
76484         }
76485
76486         function uiPanelMeasurement(context) {
76487             var locale = _mainLocalizer.localeCode();
76488             var isImperial = !_mainLocalizer.usesMetric();
76489
76490
76491             function radiansToMeters(r) {
76492                 // using WGS84 authalic radius (6371007.1809 m)
76493                 return r * 6371007.1809;
76494             }
76495
76496             function steradiansToSqmeters(r) {
76497                 // http://gis.stackexchange.com/a/124857/40446
76498                 return r / (4 * Math.PI) * 510065621724000;
76499             }
76500
76501
76502             function toLineString(feature) {
76503                 if (feature.type === 'LineString') { return feature; }
76504
76505                 var result = { type: 'LineString', coordinates: [] };
76506                 if (feature.type === 'Polygon') {
76507                     result.coordinates = feature.coordinates[0];
76508                 } else if (feature.type === 'MultiPolygon') {
76509                     result.coordinates = feature.coordinates[0][0];
76510                 }
76511
76512                 return result;
76513             }
76514
76515
76516             function redraw(selection) {
76517                 var graph = context.graph();
76518                 var selectedNoteID = context.selectedNoteID();
76519                 var osm = services.osm;
76520
76521                 var heading;
76522                 var center, location, centroid;
76523                 var closed, geometry;
76524                 var totalNodeCount, length = 0, area = 0;
76525
76526                 if (selectedNoteID && osm) {       // selected 1 note
76527
76528                     var note = osm.getNote(selectedNoteID);
76529                     heading = _t('note.note') + ' ' + selectedNoteID;
76530                     location = note.loc;
76531                     geometry = 'note';
76532
76533                 } else {                           // selected 1..n entities
76534                     var selectedIDs = context.selectedIDs().filter(function(id) {
76535                         return context.hasEntity(id);
76536                     });
76537                     var selected = selectedIDs.map(function(id) {
76538                         return context.entity(id);
76539                     });
76540
76541                     heading = selected.length === 1 ? selected[0].id :
76542                         _t('info_panels.measurement.selected', { n: selected.length.toLocaleString(locale) });
76543
76544                     if (selected.length) {
76545                         var extent = geoExtent();
76546                         for (var i in selected) {
76547                             var entity = selected[i];
76548                             extent._extend(entity.extent(graph));
76549
76550                             geometry = entity.geometry(graph);
76551                             if (geometry === 'line' || geometry === 'area') {
76552                                 closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate());
76553                                 var feature = entity.asGeoJSON(graph);
76554                                 length += radiansToMeters(d3_geoLength(toLineString(feature)));
76555                                 centroid = d3_geoCentroid(feature);
76556                                 if (closed) {
76557                                     area += steradiansToSqmeters(entity.area(graph));
76558                                 }
76559                             }
76560                         }
76561
76562                         if (selected.length > 1) {
76563                             geometry = null;
76564                             closed = null;
76565                             centroid = null;
76566                         }
76567
76568                         if (selected.length === 1 && selected[0].type === 'node') {
76569                             location = selected[0].loc;
76570                         } else {
76571                             totalNodeCount = utilGetAllNodes(selectedIDs, context.graph()).length;
76572                         }
76573
76574                         if (!location && !centroid) {
76575                             center = extent.center();
76576                         }
76577                     }
76578                 }
76579
76580                 selection.html('');
76581
76582                 if (heading) {
76583                     selection
76584                         .append('h4')
76585                         .attr('class', 'measurement-heading')
76586                         .text(heading);
76587                 }
76588
76589                 var list = selection
76590                     .append('ul');
76591                 var coordItem;
76592
76593                 if (geometry) {
76594                     list
76595                         .append('li')
76596                         .text(_t('info_panels.measurement.geometry') + ':')
76597                         .append('span')
76598                         .text(
76599                             closed ? _t('info_panels.measurement.closed_' + geometry) : _t('geometry.' + geometry)
76600                         );
76601                 }
76602
76603                 if (totalNodeCount) {
76604                     list
76605                         .append('li')
76606                         .text(_t('info_panels.measurement.node_count') + ':')
76607                         .append('span')
76608                         .text(totalNodeCount.toLocaleString(locale));
76609                 }
76610
76611                 if (area) {
76612                     list
76613                         .append('li')
76614                         .text(_t('info_panels.measurement.area') + ':')
76615                         .append('span')
76616                         .text(displayArea(area, isImperial));
76617                 }
76618
76619                 if (length) {
76620                     var lengthLabel = _t('info_panels.measurement.' + (closed ? 'perimeter' : 'length'));
76621                     list
76622                         .append('li')
76623                         .text(lengthLabel + ':')
76624                         .append('span')
76625                         .text(displayLength(length, isImperial));
76626                 }
76627
76628                 if (location) {
76629                     coordItem = list
76630                         .append('li')
76631                         .text(_t('info_panels.measurement.location') + ':');
76632                     coordItem.append('span')
76633                         .text(dmsCoordinatePair(location));
76634                     coordItem.append('span')
76635                         .text(decimalCoordinatePair(location));
76636                 }
76637
76638                 if (centroid) {
76639                     coordItem = list
76640                         .append('li')
76641                         .text(_t('info_panels.measurement.centroid') + ':');
76642                     coordItem.append('span')
76643                         .text(dmsCoordinatePair(centroid));
76644                     coordItem.append('span')
76645                         .text(decimalCoordinatePair(centroid));
76646                 }
76647
76648                 if (center) {
76649                     coordItem = list
76650                         .append('li')
76651                         .text(_t('info_panels.measurement.center') + ':');
76652                     coordItem.append('span')
76653                         .text(dmsCoordinatePair(center));
76654                     coordItem.append('span')
76655                         .text(decimalCoordinatePair(center));
76656                 }
76657
76658                 if (length || area) {
76659                     var toggle  = isImperial ? 'imperial' : 'metric';
76660                     selection
76661                         .append('a')
76662                         .text(_t('info_panels.measurement.' + toggle))
76663                         .attr('href', '#')
76664                         .attr('class', 'button button-toggle-units')
76665                         .on('click', function() {
76666                             event.preventDefault();
76667                             isImperial = !isImperial;
76668                             selection.call(redraw);
76669                         });
76670                 }
76671             }
76672
76673
76674             var panel = function(selection) {
76675                 selection.call(redraw);
76676
76677                 context.map()
76678                     .on('drawn.info-measurement', function() {
76679                         selection.call(redraw);
76680                     });
76681
76682                 context
76683                     .on('enter.info-measurement', function() {
76684                         selection.call(redraw);
76685                     });
76686             };
76687
76688             panel.off = function() {
76689                 context.map().on('drawn.info-measurement', null);
76690                 context.on('enter.info-measurement', null);
76691             };
76692
76693             panel.id = 'measurement';
76694             panel.title = _t('info_panels.measurement.title');
76695             panel.key = _t('info_panels.measurement.key');
76696
76697
76698             return panel;
76699         }
76700
76701         var uiInfoPanels = {
76702             background: uiPanelBackground,
76703             history: uiPanelHistory,
76704             location: uiPanelLocation,
76705             measurement: uiPanelMeasurement,
76706         };
76707
76708         function uiInfo(context) {
76709             var ids = Object.keys(uiInfoPanels);
76710             var wasActive = ['measurement'];
76711             var panels = {};
76712             var active = {};
76713
76714             // create panels
76715             ids.forEach(function(k) {
76716                 if (!panels[k]) {
76717                     panels[k] = uiInfoPanels[k](context);
76718                     active[k] = false;
76719                 }
76720             });
76721
76722
76723             function info(selection) {
76724
76725                 function redraw() {
76726                     var activeids = ids.filter(function(k) { return active[k]; }).sort();
76727
76728                     var containers = infoPanels.selectAll('.panel-container')
76729                         .data(activeids, function(k) { return k; });
76730
76731                     containers.exit()
76732                         .style('opacity', 1)
76733                         .transition()
76734                         .duration(200)
76735                         .style('opacity', 0)
76736                         .on('end', function(d) {
76737                             select(this)
76738                                 .call(panels[d].off)
76739                                 .remove();
76740                         });
76741
76742                     var enter = containers.enter()
76743                         .append('div')
76744                         .attr('class', function(d) { return 'fillD2 panel-container panel-container-' + d; });
76745
76746                     enter
76747                         .style('opacity', 0)
76748                         .transition()
76749                         .duration(200)
76750                         .style('opacity', 1);
76751
76752                     var title = enter
76753                         .append('div')
76754                         .attr('class', 'panel-title fillD2');
76755
76756                     title
76757                         .append('h3')
76758                         .text(function(d) { return panels[d].title; });
76759
76760                     title
76761                         .append('button')
76762                         .attr('class', 'close')
76763                         .on('click', function (d) { info.toggle(d); })
76764                         .call(svgIcon('#iD-icon-close'));
76765
76766                     enter
76767                         .append('div')
76768                         .attr('class', function(d) { return 'panel-content panel-content-' + d; });
76769
76770
76771                     // redraw the panels
76772                     infoPanels.selectAll('.panel-content')
76773                         .each(function(d) {
76774                             select(this).call(panels[d]);
76775                         });
76776                 }
76777
76778
76779                 info.toggle = function(which) {
76780                     if (event) {
76781                         event.stopImmediatePropagation();
76782                         event.preventDefault();
76783                     }
76784
76785                     var activeids = ids.filter(function(k) { return active[k]; });
76786
76787                     if (which) {  // toggle one
76788                         active[which] = !active[which];
76789                         if (activeids.length === 1 && activeids[0] === which) {  // none active anymore
76790                             wasActive = [which];
76791                         }
76792
76793                         context.container().select('.' + which + '-panel-toggle-item')
76794                             .classed('active', active[which])
76795                             .select('input')
76796                             .property('checked', active[which]);
76797
76798                     } else {      // toggle all
76799                         if (activeids.length) {
76800                             wasActive = activeids;
76801                             activeids.forEach(function(k) { active[k] = false; });
76802                         } else {
76803                             wasActive.forEach(function(k) { active[k] = true; });
76804                         }
76805                     }
76806
76807                     redraw();
76808                 };
76809
76810
76811                 var infoPanels = selection.selectAll('.info-panels')
76812                     .data([0]);
76813
76814                 infoPanels = infoPanels.enter()
76815                     .append('div')
76816                     .attr('class', 'info-panels')
76817                     .merge(infoPanels);
76818
76819                 redraw();
76820
76821                 context.keybinding()
76822                     .on(uiCmd('⌘' + _t('info_panels.key')), info.toggle);
76823
76824                 ids.forEach(function(k) {
76825                     var key = _t('info_panels.' + k + '.key', { default: null });
76826                     if (!key) { return; }
76827                     context.keybinding()
76828                         .on(uiCmd('⌘⇧' + key), function() { info.toggle(k); });
76829                 });
76830             }
76831
76832             return info;
76833         }
76834
76835         function pointBox(loc, context) {
76836             var rect = context.surfaceRect();
76837             var point = context.curtainProjection(loc);
76838             return {
76839                 left: point[0] + rect.left - 40,
76840                 top: point[1] + rect.top - 60,
76841                 width: 80,
76842                 height: 90
76843             };
76844         }
76845
76846
76847         function pad(locOrBox, padding, context) {
76848             var box;
76849             if (locOrBox instanceof Array) {
76850                 var rect = context.surfaceRect();
76851                 var point = context.curtainProjection(locOrBox);
76852                 box = {
76853                     left: point[0] + rect.left,
76854                     top: point[1] + rect.top
76855                 };
76856             } else {
76857                 box = locOrBox;
76858             }
76859
76860             return {
76861                 left: box.left - padding,
76862                 top: box.top - padding,
76863                 width: (box.width || 0) + 2 * padding,
76864                 height: (box.width || 0) + 2 * padding
76865             };
76866         }
76867
76868
76869         function icon(name, svgklass, useklass) {
76870             return '<svg class="icon ' + (svgklass || '') + '">' +
76871                  '<use xlink:href="' + name + '"' +
76872                  (useklass ? ' class="' + useklass + '"' : '') + '></use></svg>';
76873         }
76874
76875         var helpStringReplacements;
76876
76877         // Returns the localized string for `id` with a standardized set of icon, key, and
76878         // label replacements suitable for tutorials and documentation. Optionally supplemented
76879         // with custom `replacements`
76880         function helpString(id, replacements) {
76881             // only load these the first time
76882             if (!helpStringReplacements) { helpStringReplacements = {
76883                 // insert icons corresponding to various UI elements
76884                 point_icon: icon('#iD-icon-point', 'pre-text'),
76885                 line_icon: icon('#iD-icon-line', 'pre-text'),
76886                 area_icon: icon('#iD-icon-area', 'pre-text'),
76887                 note_icon: icon('#iD-icon-note', 'pre-text add-note'),
76888                 plus: icon('#iD-icon-plus', 'pre-text'),
76889                 minus: icon('#iD-icon-minus', 'pre-text'),
76890                 move_icon: icon('#iD-operation-move', 'pre-text operation'),
76891                 merge_icon: icon('#iD-operation-merge', 'pre-text operation'),
76892                 delete_icon: icon('#iD-operation-delete', 'pre-text operation'),
76893                 circularize_icon: icon('#iD-operation-circularize', 'pre-text operation'),
76894                 split_icon: icon('#iD-operation-split', 'pre-text operation'),
76895                 orthogonalize_icon: icon('#iD-operation-orthogonalize', 'pre-text operation'),
76896                 disconnect_icon: icon('#iD-operation-disconnect', 'pre-text operation'),
76897                 layers_icon: icon('#iD-icon-layers', 'pre-text'),
76898                 data_icon: icon('#iD-icon-data', 'pre-text'),
76899                 inspect: icon('#iD-icon-inspect', 'pre-text'),
76900                 help_icon: icon('#iD-icon-help', 'pre-text'),
76901                 undo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo', 'pre-text'),
76902                 redo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-undo' : '#iD-icon-redo', 'pre-text'),
76903                 save_icon: icon('#iD-icon-save', 'pre-text'),
76904                 leftclick: icon('#iD-walkthrough-mouse-left', 'pre-text operation'),
76905                 rightclick: icon('#iD-walkthrough-mouse-right', 'pre-text operation'),
76906                 mousewheel_icon: icon('#iD-walkthrough-mousewheel', 'pre-text operation'),
76907                 tap_icon: icon('#iD-walkthrough-tap', 'pre-text operation'),
76908                 doubletap_icon: icon('#iD-walkthrough-doubletap', 'pre-text operation'),
76909                 longpress_icon: icon('#iD-walkthrough-longpress', 'pre-text operation'),
76910                 touchdrag_icon: icon('#iD-walkthrough-touchdrag', 'pre-text operation'),
76911                 pinch_icon: icon('#iD-walkthrough-pinch-apart', 'pre-text operation'),
76912
76913                 // insert keys; may be localized and platform-dependent
76914                 shift: uiCmd.display('⇧'),
76915                 alt: uiCmd.display('⌥'),
76916                 return: uiCmd.display('↵'),
76917                 esc: _t('shortcuts.key.esc'),
76918                 space: _t('shortcuts.key.space'),
76919                 add_note_key: _t('modes.add_note.key'),
76920                 help_key: _t('help.key'),
76921                 shortcuts_key: _t('shortcuts.toggle.key'),
76922
76923                 // reference localized UI labels directly so that they'll always match
76924                 save: _t('save.title'),
76925                 undo: _t('undo.title'),
76926                 redo: _t('redo.title'),
76927                 upload: _t('commit.save'),
76928                 point: _t('modes.add_point.title'),
76929                 line: _t('modes.add_line.title'),
76930                 area: _t('modes.add_area.title'),
76931                 note: _t('modes.add_note.label'),
76932                 delete: _t('operations.delete.title'),
76933                 move: _t('operations.move.title'),
76934                 orthogonalize: _t('operations.orthogonalize.title'),
76935                 circularize: _t('operations.circularize.title'),
76936                 merge: _t('operations.merge.title'),
76937                 disconnect: _t('operations.disconnect.title'),
76938                 split: _t('operations.split.title'),
76939                 map_data: _t('map_data.title'),
76940                 osm_notes: _t('map_data.layers.notes.title'),
76941                 fields: _t('inspector.fields'),
76942                 tags: _t('inspector.tags'),
76943                 relations: _t('inspector.relations'),
76944                 new_relation: _t('inspector.new_relation'),
76945                 turn_restrictions: _t('presets.fields.restrictions.label'),
76946                 background_settings: _t('background.description'),
76947                 imagery_offset: _t('background.fix_misalignment'),
76948                 start_the_walkthrough: _t('splash.walkthrough'),
76949                 help: _t('help.title'),
76950                 ok: _t('intro.ok')
76951             }; }
76952
76953             var reps;
76954             if (replacements) {
76955                 reps = Object.assign(replacements, helpStringReplacements);
76956             } else {
76957                 reps = helpStringReplacements;
76958             }
76959
76960             return _t(id, reps)
76961                  // use keyboard key styling for shortcuts
76962                 .replace(/\`(.*?)\`/g, '<kbd>$1</kbd>');
76963         }
76964
76965
76966         function slugify(text) {
76967             return text.toString().toLowerCase()
76968                 .replace(/\s+/g, '-')           // Replace spaces with -
76969                 .replace(/[^\w\-]+/g, '')       // Remove all non-word chars
76970                 .replace(/\-\-+/g, '-')         // Replace multiple - with single -
76971                 .replace(/^-+/, '')             // Trim - from start of text
76972                 .replace(/-+$/, '');            // Trim - from end of text
76973         }
76974
76975
76976         // console warning for missing walkthrough names
76977         var missingStrings = {};
76978         function checkKey(key, text) {
76979             if (_t(key, { default: undefined}) === undefined) {
76980                 if (missingStrings.hasOwnProperty(key)) { return; }  // warn once
76981                 missingStrings[key] = text;
76982                 var missing = key + ': ' + text;
76983                 if (typeof console !== 'undefined') { console.log(missing); } // eslint-disable-line
76984             }
76985         }
76986
76987
76988         function localize(obj) {
76989             var key;
76990
76991             // Assign name if entity has one..
76992             var name = obj.tags && obj.tags.name;
76993             if (name) {
76994                 key = 'intro.graph.name.' + slugify(name);
76995                 obj.tags.name = _t(key, { default: name });
76996                 checkKey(key, name);
76997             }
76998
76999             // Assign street name if entity has one..
77000             var street = obj.tags && obj.tags['addr:street'];
77001             if (street) {
77002                 key = 'intro.graph.name.' + slugify(street);
77003                 obj.tags['addr:street'] = _t(key, { default: street });
77004                 checkKey(key, street);
77005
77006                 // Add address details common across walkthrough..
77007                 var addrTags = [
77008                     'block_number', 'city', 'county', 'district', 'hamlet', 'neighbourhood',
77009                     'postcode', 'province', 'quarter', 'state', 'subdistrict', 'suburb'
77010                 ];
77011                 addrTags.forEach(function(k) {
77012                     var key = 'intro.graph.' + k;
77013                     var tag = 'addr:' + k;
77014                     var val = obj.tags && obj.tags[tag];
77015                     var str = _t(key, { default: val });
77016
77017                     if (str) {
77018                         if (str.match(/^<.*>$/) !== null) {
77019                             delete obj.tags[tag];
77020                         } else {
77021                             obj.tags[tag] = str;
77022                         }
77023                     }
77024                 });
77025             }
77026
77027             return obj;
77028         }
77029
77030
77031         // Used to detect squareness.. some duplicataion of code from actionOrthogonalize.
77032         function isMostlySquare(points) {
77033             // note: uses 15 here instead of the 12 from actionOrthogonalize because
77034             // actionOrthogonalize can actually straighten some larger angles as it iterates
77035             var threshold = 15; // degrees within right or straight
77036             var lowerBound = Math.cos((90 - threshold) * Math.PI / 180);  // near right
77037             var upperBound = Math.cos(threshold * Math.PI / 180);         // near straight
77038
77039             for (var i = 0; i < points.length; i++) {
77040                 var a = points[(i - 1 + points.length) % points.length];
77041                 var origin = points[i];
77042                 var b = points[(i + 1) % points.length];
77043
77044                 var dotp = geoVecNormalizedDot(a, b, origin);
77045                 var mag = Math.abs(dotp);
77046                 if (mag > lowerBound && mag < upperBound) {
77047                     return false;
77048                 }
77049             }
77050
77051             return true;
77052         }
77053
77054
77055         function selectMenuItem(context, operation) {
77056             return context.container().select('.edit-menu .edit-menu-item-' + operation);
77057         }
77058
77059
77060         function transitionTime(point1, point2) {
77061             var distance = geoSphericalDistance(point1, point2);
77062             if (distance === 0)
77063                 { return 0; }
77064             else if (distance < 80)
77065                 { return 500; }
77066             else
77067                 { return 1000; }
77068         }
77069
77070         // Tooltips and svg mask used to highlight certain features
77071         function uiCurtain(containerNode) {
77072
77073             var surface = select(null),
77074                 tooltip = select(null),
77075                 darkness = select(null);
77076
77077             function curtain(selection) {
77078                 surface = selection
77079                     .append('svg')
77080                     .attr('class', 'curtain')
77081                     .style('top', 0)
77082                     .style('left', 0);
77083
77084                 darkness = surface.append('path')
77085                     .attr('x', 0)
77086                     .attr('y', 0)
77087                     .attr('class', 'curtain-darkness');
77088
77089                 select(window).on('resize.curtain', resize);
77090
77091                 tooltip = selection.append('div')
77092                     .attr('class', 'tooltip');
77093
77094                 tooltip
77095                     .append('div')
77096                     .attr('class', 'popover-arrow');
77097
77098                 tooltip
77099                     .append('div')
77100                     .attr('class', 'popover-inner');
77101
77102                 resize();
77103
77104
77105                 function resize() {
77106                     surface
77107                         .attr('width', containerNode.clientWidth)
77108                         .attr('height', containerNode.clientHeight);
77109                     curtain.cut(darkness.datum());
77110                 }
77111             }
77112
77113
77114             /**
77115              * Reveal cuts the curtain to highlight the given box,
77116              * and shows a tooltip with instructions next to the box.
77117              *
77118              * @param  {String|ClientRect} [box]   box used to cut the curtain
77119              * @param  {String}    [text]          text for a tooltip
77120              * @param  {Object}    [options]
77121              * @param  {string}    [options.tooltipClass]    optional class to add to the tooltip
77122              * @param  {integer}   [options.duration]        transition time in milliseconds
77123              * @param  {string}    [options.buttonText]      if set, create a button with this text label
77124              * @param  {function}  [options.buttonCallback]  if set, the callback for the button
77125              * @param  {function}  [options.padding]         extra margin in px to put around bbox
77126              * @param  {String|ClientRect} [options.tooltipBox]  box for tooltip position, if different from box for the curtain
77127              */
77128             curtain.reveal = function(box, text, options) {
77129                 options = options || {};
77130
77131                 if (typeof box === 'string') {
77132                     box = select(box).node();
77133                 }
77134                 if (box && box.getBoundingClientRect) {
77135                     box = copyBox(box.getBoundingClientRect());
77136                     var containerRect = containerNode.getBoundingClientRect();
77137                     box.top -= containerRect.top;
77138                     box.left -= containerRect.left;
77139                 }
77140                 if (box && options.padding) {
77141                     box.top -= options.padding;
77142                     box.left -= options.padding;
77143                     box.bottom += options.padding;
77144                     box.right += options.padding;
77145                     box.height += options.padding * 2;
77146                     box.width += options.padding * 2;
77147                 }
77148
77149                 var tooltipBox;
77150                 if (options.tooltipBox) {
77151                     tooltipBox = options.tooltipBox;
77152                     if (typeof tooltipBox === 'string') {
77153                         tooltipBox = select(tooltipBox).node();
77154                     }
77155                     if (tooltipBox && tooltipBox.getBoundingClientRect) {
77156                         tooltipBox = copyBox(tooltipBox.getBoundingClientRect());
77157                     }
77158                 } else {
77159                     tooltipBox = box;
77160                 }
77161
77162                 if (tooltipBox && text) {
77163                     // pseudo markdown bold text for the instruction section..
77164                     var parts = text.split('**');
77165                     var html = parts[0] ? '<span>' + parts[0] + '</span>' : '';
77166                     if (parts[1]) {
77167                         html += '<span class="instruction">' + parts[1] + '</span>';
77168                     }
77169
77170                     html = html.replace(/\*(.*?)\*/g, '<em>$1</em>');   // emphasis
77171                     html = html.replace(/\{br\}/g, '<br/><br/>');       // linebreak
77172
77173                     if (options.buttonText && options.buttonCallback) {
77174                         html += '<div class="button-section">' +
77175                             '<button href="#" class="button action">' + options.buttonText + '</button></div>';
77176                     }
77177
77178                     var classes = 'curtain-tooltip popover tooltip arrowed in ' + (options.tooltipClass || '');
77179                     tooltip
77180                         .classed(classes, true)
77181                         .selectAll('.popover-inner')
77182                         .html(html);
77183
77184                     if (options.buttonText && options.buttonCallback) {
77185                         var button = tooltip.selectAll('.button-section .button.action');
77186                         button
77187                             .on('click', function() {
77188                                 event.preventDefault();
77189                                 options.buttonCallback();
77190                             });
77191                     }
77192
77193                     var tip = copyBox(tooltip.node().getBoundingClientRect()),
77194                         w = containerNode.clientWidth,
77195                         h = containerNode.clientHeight,
77196                         tooltipWidth = 200,
77197                         tooltipArrow = 5,
77198                         side, pos;
77199
77200
77201                     // hack: this will have bottom placement,
77202                     // so need to reserve extra space for the tooltip illustration.
77203                     if (options.tooltipClass === 'intro-mouse') {
77204                         tip.height += 80;
77205                     }
77206
77207                     // trim box dimensions to just the portion that fits in the container..
77208                     if (tooltipBox.top + tooltipBox.height > h) {
77209                         tooltipBox.height -= (tooltipBox.top + tooltipBox.height - h);
77210                     }
77211                     if (tooltipBox.left + tooltipBox.width > w) {
77212                         tooltipBox.width -= (tooltipBox.left + tooltipBox.width - w);
77213                     }
77214
77215                     // determine tooltip placement..
77216
77217                     if (tooltipBox.top + tooltipBox.height < 100) {
77218                         // tooltip below box..
77219                         side = 'bottom';
77220                         pos = [
77221                             tooltipBox.left + tooltipBox.width / 2 - tip.width / 2,
77222                             tooltipBox.top + tooltipBox.height
77223                         ];
77224
77225                     } else if (tooltipBox.top > h - 140) {
77226                         // tooltip above box..
77227                         side = 'top';
77228                         pos = [
77229                             tooltipBox.left + tooltipBox.width / 2 - tip.width / 2,
77230                             tooltipBox.top - tip.height
77231                         ];
77232
77233                     } else {
77234                         // tooltip to the side of the tooltipBox..
77235                         var tipY = tooltipBox.top + tooltipBox.height / 2 - tip.height / 2;
77236
77237                         if (_mainLocalizer.textDirection() === 'rtl') {
77238                             if (tooltipBox.left - tooltipWidth - tooltipArrow < 70) {
77239                                 side = 'right';
77240                                 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
77241
77242                             } else {
77243                                 side = 'left';
77244                                 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
77245                             }
77246
77247                         } else {
77248                             if (tooltipBox.left + tooltipBox.width + tooltipArrow + tooltipWidth > w - 70) {
77249                                 side = 'left';
77250                                 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
77251                             }
77252                             else {
77253                                 side = 'right';
77254                                 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
77255                             }
77256                         }
77257                     }
77258
77259                     if (options.duration !== 0 || !tooltip.classed(side)) {
77260                         tooltip.call(uiToggle(true));
77261                     }
77262
77263                     tooltip
77264                         .style('top', pos[1] + 'px')
77265                         .style('left', pos[0] + 'px')
77266                         .attr('class', classes + ' ' + side);
77267
77268
77269                     // shift popover-inner if it is very close to the top or bottom edge
77270                     // (doesn't affect the placement of the popover-arrow)
77271                     var shiftY = 0;
77272                     if (side === 'left' || side === 'right') {
77273                         if (pos[1] < 60) {
77274                             shiftY = 60 - pos[1];
77275                         }
77276                         else if (pos[1] + tip.height > h - 100) {
77277                             shiftY = h - pos[1] - tip.height - 100;
77278                         }
77279                     }
77280                     tooltip.selectAll('.popover-inner')
77281                         .style('top', shiftY + 'px');
77282
77283                 } else {
77284                     tooltip
77285                         .classed('in', false)
77286                         .call(uiToggle(false));
77287                 }
77288
77289                 curtain.cut(box, options.duration);
77290
77291                 return tooltip;
77292             };
77293
77294
77295             curtain.cut = function(datum, duration) {
77296                 darkness.datum(datum)
77297                     .interrupt();
77298
77299                 var selection;
77300                 if (duration === 0) {
77301                     selection = darkness;
77302                 } else {
77303                     selection = darkness
77304                         .transition()
77305                         .duration(duration || 600)
77306                         .ease(linear$1);
77307                 }
77308
77309                 selection
77310                     .attr('d', function(d) {
77311                         var containerWidth = containerNode.clientWidth;
77312                         var containerHeight = containerNode.clientHeight;
77313                         var string = 'M 0,0 L 0,' + containerHeight + ' L ' +
77314                             containerWidth + ',' + containerHeight + 'L' +
77315                             containerWidth + ',0 Z';
77316
77317                         if (!d) { return string; }
77318                         return string + 'M' +
77319                             d.left + ',' + d.top + 'L' +
77320                             d.left + ',' + (d.top + d.height) + 'L' +
77321                             (d.left + d.width) + ',' + (d.top + d.height) + 'L' +
77322                             (d.left + d.width) + ',' + (d.top) + 'Z';
77323
77324                     });
77325             };
77326
77327
77328             curtain.remove = function() {
77329                 surface.remove();
77330                 tooltip.remove();
77331                 select(window).on('resize.curtain', null);
77332             };
77333
77334
77335             // ClientRects are immutable, so copy them to an object,
77336             // in case we need to trim the height/width.
77337             function copyBox(src) {
77338                 return {
77339                     top: src.top,
77340                     right: src.right,
77341                     bottom: src.bottom,
77342                     left: src.left,
77343                     width: src.width,
77344                     height: src.height
77345                 };
77346             }
77347
77348
77349             return curtain;
77350         }
77351
77352         function uiIntroWelcome(context, reveal) {
77353             var dispatch$1 = dispatch('done');
77354
77355             var chapter = {
77356                 title: 'intro.welcome.title'
77357             };
77358
77359
77360             function welcome() {
77361                 context.map().centerZoom([-85.63591, 41.94285], 19);
77362                 reveal('.intro-nav-wrap .chapter-welcome',
77363                     helpString('intro.welcome.welcome'),
77364                     { buttonText: _t('intro.ok'), buttonCallback: practice }
77365                 );
77366             }
77367
77368             function practice() {
77369                 reveal('.intro-nav-wrap .chapter-welcome',
77370                     helpString('intro.welcome.practice'),
77371                     { buttonText: _t('intro.ok'), buttonCallback: words }
77372                 );
77373             }
77374
77375             function words() {
77376                 reveal('.intro-nav-wrap .chapter-welcome',
77377                     helpString('intro.welcome.words'),
77378                     { buttonText: _t('intro.ok'), buttonCallback: chapters }
77379                 );
77380             }
77381
77382
77383             function chapters() {
77384                 dispatch$1.call('done');
77385                 reveal('.intro-nav-wrap .chapter-navigation',
77386                     helpString('intro.welcome.chapters', { next: _t('intro.navigation.title') })
77387                 );
77388             }
77389
77390
77391             chapter.enter = function() {
77392                 welcome();
77393             };
77394
77395
77396             chapter.exit = function() {
77397                 context.container().select('.curtain-tooltip.intro-mouse')
77398                     .selectAll('.counter')
77399                     .remove();
77400             };
77401
77402
77403             chapter.restart = function() {
77404                 chapter.exit();
77405                 chapter.enter();
77406             };
77407
77408
77409             return utilRebind(chapter, dispatch$1, 'on');
77410         }
77411
77412         function uiIntroNavigation(context, reveal) {
77413             var dispatch$1 = dispatch('done');
77414             var timeouts = [];
77415             var hallId = 'n2061';
77416             var townHall = [-85.63591, 41.94285];
77417             var springStreetId = 'w397';
77418             var springStreetEndId = 'n1834';
77419             var springStreet = [-85.63582, 41.94255];
77420             var onewayField = _mainPresetIndex.field('oneway');
77421             var maxspeedField = _mainPresetIndex.field('maxspeed');
77422
77423
77424             var chapter = {
77425                 title: 'intro.navigation.title'
77426             };
77427
77428
77429             function timeout(f, t) {
77430                 timeouts.push(window.setTimeout(f, t));
77431             }
77432
77433
77434             function eventCancel() {
77435                 event.stopPropagation();
77436                 event.preventDefault();
77437             }
77438
77439
77440             function isTownHallSelected() {
77441                 var ids = context.selectedIDs();
77442                 return ids.length === 1 && ids[0] === hallId;
77443             }
77444
77445
77446             function dragMap() {
77447                 context.enter(modeBrowse(context));
77448                 context.history().reset('initial');
77449
77450                 var msec = transitionTime(townHall, context.map().center());
77451                 if (msec) { reveal(null, null, { duration: 0 }); }
77452                 context.map().centerZoomEase(townHall, 19, msec);
77453
77454                 timeout(function() {
77455                     var centerStart = context.map().center();
77456
77457                     var textId = context.lastPointerType() === 'mouse' ? 'drag' : 'drag_touch';
77458                     var dragString = helpString('intro.navigation.map_info') + '{br}' + helpString('intro.navigation.' + textId);
77459                     reveal('.surface', dragString);
77460                     context.map().on('drawn.intro', function() {
77461                         reveal('.surface', dragString, { duration: 0 });
77462                     });
77463
77464                     context.map().on('move.intro', function() {
77465                         var centerNow = context.map().center();
77466                         if (centerStart[0] !== centerNow[0] || centerStart[1] !== centerNow[1]) {
77467                             context.map().on('move.intro', null);
77468                             timeout(function() { continueTo(zoomMap); }, 3000);
77469                         }
77470                     });
77471
77472                 }, msec + 100);
77473
77474                 function continueTo(nextStep) {
77475                     context.map().on('move.intro drawn.intro', null);
77476                     nextStep();
77477                 }
77478             }
77479
77480
77481             function zoomMap() {
77482                 var zoomStart = context.map().zoom();
77483
77484                 var textId = context.lastPointerType() === 'mouse' ? 'zoom' : 'zoom_touch';
77485                 var zoomString = helpString('intro.navigation.' + textId);
77486
77487                 reveal('.surface', zoomString);
77488
77489                 context.map().on('drawn.intro', function() {
77490                     reveal('.surface', zoomString, { duration: 0 });
77491                 });
77492
77493                 context.map().on('move.intro', function() {
77494                     if (context.map().zoom() !== zoomStart) {
77495                         context.map().on('move.intro', null);
77496                         timeout(function() { continueTo(features); }, 3000);
77497                     }
77498                 });
77499
77500                 function continueTo(nextStep) {
77501                     context.map().on('move.intro drawn.intro', null);
77502                     nextStep();
77503                 }
77504             }
77505
77506
77507             function features() {
77508                 var onClick = function() { continueTo(pointsLinesAreas); };
77509
77510                 reveal('.surface', helpString('intro.navigation.features'),
77511                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77512                 );
77513
77514                 context.map().on('drawn.intro', function() {
77515                     reveal('.surface', helpString('intro.navigation.features'),
77516                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77517                     );
77518                 });
77519
77520                 function continueTo(nextStep) {
77521                     context.map().on('drawn.intro', null);
77522                     nextStep();
77523                 }
77524             }
77525
77526             function pointsLinesAreas() {
77527                 var onClick = function() { continueTo(nodesWays); };
77528
77529                 reveal('.surface', helpString('intro.navigation.points_lines_areas'),
77530                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77531                 );
77532
77533                 context.map().on('drawn.intro', function() {
77534                     reveal('.surface', helpString('intro.navigation.points_lines_areas'),
77535                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77536                     );
77537                 });
77538
77539                 function continueTo(nextStep) {
77540                     context.map().on('drawn.intro', null);
77541                     nextStep();
77542                 }
77543             }
77544
77545             function nodesWays() {
77546                 var onClick = function() { continueTo(clickTownHall); };
77547
77548                 reveal('.surface', helpString('intro.navigation.nodes_ways'),
77549                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77550                 );
77551
77552                 context.map().on('drawn.intro', function() {
77553                     reveal('.surface', helpString('intro.navigation.nodes_ways'),
77554                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77555                     );
77556                 });
77557
77558                 function continueTo(nextStep) {
77559                     context.map().on('drawn.intro', null);
77560                     nextStep();
77561                 }
77562             }
77563
77564             function clickTownHall() {
77565                 context.enter(modeBrowse(context));
77566                 context.history().reset('initial');
77567
77568                 var entity = context.hasEntity(hallId);
77569                 if (!entity) { return; }
77570                 reveal(null, null, { duration: 0 });
77571                 context.map().centerZoomEase(entity.loc, 19, 500);
77572
77573                 timeout(function() {
77574                     var entity = context.hasEntity(hallId);
77575                     if (!entity) { return; }
77576                     var box = pointBox(entity.loc, context);
77577                     var textId = context.lastPointerType() === 'mouse' ? 'click_townhall' : 'tap_townhall';
77578                     reveal(box, helpString('intro.navigation.' + textId));
77579
77580                     context.map().on('move.intro drawn.intro', function() {
77581                         var entity = context.hasEntity(hallId);
77582                         if (!entity) { return; }
77583                         var box = pointBox(entity.loc, context);
77584                         reveal(box, helpString('intro.navigation.' + textId), { duration: 0 });
77585                     });
77586
77587                     context.on('enter.intro', function() {
77588                         if (isTownHallSelected()) { continueTo(selectedTownHall); }
77589                     });
77590
77591                 }, 550);  // after centerZoomEase
77592
77593                 context.history().on('change.intro', function() {
77594                     if (!context.hasEntity(hallId)) {
77595                         continueTo(clickTownHall);
77596                     }
77597                 });
77598
77599                 function continueTo(nextStep) {
77600                     context.on('enter.intro', null);
77601                     context.map().on('move.intro drawn.intro', null);
77602                     context.history().on('change.intro', null);
77603                     nextStep();
77604                 }
77605             }
77606
77607
77608             function selectedTownHall() {
77609                 if (!isTownHallSelected()) { return clickTownHall(); }
77610
77611                 var entity = context.hasEntity(hallId);
77612                 if (!entity) { return clickTownHall(); }
77613
77614                 var box = pointBox(entity.loc, context);
77615                 var onClick = function() { continueTo(editorTownHall); };
77616
77617                 reveal(box, helpString('intro.navigation.selected_townhall'),
77618                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77619                 );
77620
77621                 context.map().on('move.intro drawn.intro', function() {
77622                     var entity = context.hasEntity(hallId);
77623                     if (!entity) { return; }
77624                     var box = pointBox(entity.loc, context);
77625                     reveal(box, helpString('intro.navigation.selected_townhall'),
77626                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77627                     );
77628                 });
77629
77630                 context.history().on('change.intro', function() {
77631                     if (!context.hasEntity(hallId)) {
77632                         continueTo(clickTownHall);
77633                     }
77634                 });
77635
77636                 function continueTo(nextStep) {
77637                     context.map().on('move.intro drawn.intro', null);
77638                     context.history().on('change.intro', null);
77639                     nextStep();
77640                 }
77641             }
77642
77643
77644             function editorTownHall() {
77645                 if (!isTownHallSelected()) { return clickTownHall(); }
77646
77647                 // disallow scrolling
77648                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77649
77650                 var onClick = function() { continueTo(presetTownHall); };
77651
77652                 reveal('.entity-editor-pane',
77653                     helpString('intro.navigation.editor_townhall'),
77654                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77655                 );
77656
77657                 context.on('exit.intro', function() {
77658                     continueTo(clickTownHall);
77659                 });
77660
77661                 context.history().on('change.intro', function() {
77662                     if (!context.hasEntity(hallId)) {
77663                         continueTo(clickTownHall);
77664                     }
77665                 });
77666
77667                 function continueTo(nextStep) {
77668                     context.on('exit.intro', null);
77669                     context.history().on('change.intro', null);
77670                     context.container().select('.inspector-wrap').on('wheel.intro', null);
77671                     nextStep();
77672                 }
77673             }
77674
77675
77676             function presetTownHall() {
77677                 if (!isTownHallSelected()) { return clickTownHall(); }
77678
77679                 // reset pane, in case user happened to change it..
77680                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
77681                 // disallow scrolling
77682                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77683
77684                 // preset match, in case the user happened to change it.
77685                 var entity = context.entity(context.selectedIDs()[0]);
77686                 var preset = _mainPresetIndex.match(entity, context.graph());
77687
77688                 var onClick = function() { continueTo(fieldsTownHall); };
77689
77690                 reveal('.entity-editor-pane .section-feature-type',
77691                     helpString('intro.navigation.preset_townhall', { preset: preset.name() }),
77692                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77693                 );
77694
77695                 context.on('exit.intro', function() {
77696                     continueTo(clickTownHall);
77697                 });
77698
77699                 context.history().on('change.intro', function() {
77700                     if (!context.hasEntity(hallId)) {
77701                         continueTo(clickTownHall);
77702                     }
77703                 });
77704
77705                 function continueTo(nextStep) {
77706                     context.on('exit.intro', null);
77707                     context.history().on('change.intro', null);
77708                     context.container().select('.inspector-wrap').on('wheel.intro', null);
77709                     nextStep();
77710                 }
77711             }
77712
77713
77714             function fieldsTownHall() {
77715                 if (!isTownHallSelected()) { return clickTownHall(); }
77716
77717                 // reset pane, in case user happened to change it..
77718                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
77719                 // disallow scrolling
77720                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77721
77722                 var onClick = function() { continueTo(closeTownHall); };
77723
77724                 reveal('.entity-editor-pane .section-preset-fields',
77725                     helpString('intro.navigation.fields_townhall'),
77726                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77727                 );
77728
77729                 context.on('exit.intro', function() {
77730                     continueTo(clickTownHall);
77731                 });
77732
77733                 context.history().on('change.intro', function() {
77734                     if (!context.hasEntity(hallId)) {
77735                         continueTo(clickTownHall);
77736                     }
77737                 });
77738
77739                 function continueTo(nextStep) {
77740                     context.on('exit.intro', null);
77741                     context.history().on('change.intro', null);
77742                     context.container().select('.inspector-wrap').on('wheel.intro', null);
77743                     nextStep();
77744                 }
77745             }
77746
77747
77748             function closeTownHall() {
77749                 if (!isTownHallSelected()) { return clickTownHall(); }
77750
77751                 var selector = '.entity-editor-pane button.close svg use';
77752                 var href = select(selector).attr('href') || '#iD-icon-close';
77753
77754                 reveal('.entity-editor-pane',
77755                     helpString('intro.navigation.close_townhall', { button: icon(href, 'pre-text') })
77756                 );
77757
77758                 context.on('exit.intro', function() {
77759                     continueTo(searchStreet);
77760                 });
77761
77762                 context.history().on('change.intro', function() {
77763                     // update the close icon in the tooltip if the user edits something.
77764                     var selector = '.entity-editor-pane button.close svg use';
77765                     var href = select(selector).attr('href') || '#iD-icon-close';
77766
77767                     reveal('.entity-editor-pane',
77768                         helpString('intro.navigation.close_townhall', { button: icon(href, 'pre-text') }),
77769                         { duration: 0 }
77770                     );
77771                 });
77772
77773                 function continueTo(nextStep) {
77774                     context.on('exit.intro', null);
77775                     context.history().on('change.intro', null);
77776                     nextStep();
77777                 }
77778             }
77779
77780
77781             function searchStreet() {
77782                 context.enter(modeBrowse(context));
77783                 context.history().reset('initial');  // ensure spring street exists
77784
77785                 var msec = transitionTime(springStreet, context.map().center());
77786                 if (msec) { reveal(null, null, { duration: 0 }); }
77787                 context.map().centerZoomEase(springStreet, 19, msec);  // ..and user can see it
77788
77789                 timeout(function() {
77790                     reveal('.search-header input',
77791                         helpString('intro.navigation.search_street', { name: _t('intro.graph.name.spring-street') })
77792                     );
77793
77794                     context.container().select('.search-header input')
77795                         .on('keyup.intro', checkSearchResult);
77796                 }, msec + 100);
77797             }
77798
77799
77800             function checkSearchResult() {
77801                 var first = context.container().select('.feature-list-item:nth-child(0n+2)');  // skip "No Results" item
77802                 var firstName = first.select('.entity-name');
77803                 var name = _t('intro.graph.name.spring-street');
77804
77805                 if (!firstName.empty() && firstName.text() === name) {
77806                     reveal(first.node(),
77807                         helpString('intro.navigation.choose_street', { name: name }),
77808                         { duration: 300 }
77809                     );
77810
77811                     context.on('exit.intro', function() {
77812                         continueTo(selectedStreet);
77813                     });
77814
77815                     context.container().select('.search-header input')
77816                         .on('keydown.intro', eventCancel, true)
77817                         .on('keyup.intro', null);
77818                 }
77819
77820                 function continueTo(nextStep) {
77821                     context.on('exit.intro', null);
77822                     context.container().select('.search-header input')
77823                         .on('keydown.intro', null)
77824                         .on('keyup.intro', null);
77825                     nextStep();
77826                 }
77827             }
77828
77829
77830             function selectedStreet() {
77831                 if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
77832                     return searchStreet();
77833                 }
77834
77835                 var onClick = function() { continueTo(editorStreet); };
77836                 var entity = context.entity(springStreetEndId);
77837                 var box = pointBox(entity.loc, context);
77838                 box.height = 500;
77839
77840                 reveal(box,
77841                     helpString('intro.navigation.selected_street', { name: _t('intro.graph.name.spring-street') }),
77842                     { duration: 600, buttonText: _t('intro.ok'), buttonCallback: onClick }
77843                 );
77844
77845                 timeout(function() {
77846                     context.map().on('move.intro drawn.intro', function() {
77847                         var entity = context.hasEntity(springStreetEndId);
77848                         if (!entity) { return; }
77849                         var box = pointBox(entity.loc, context);
77850                         box.height = 500;
77851                         reveal(box,
77852                             helpString('intro.navigation.selected_street', { name: _t('intro.graph.name.spring-street') }),
77853                             { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77854                         );
77855                     });
77856                 }, 600);  // after reveal.
77857
77858                 context.on('enter.intro', function(mode) {
77859                     if (!context.hasEntity(springStreetId)) {
77860                         return continueTo(searchStreet);
77861                     }
77862                     var ids = context.selectedIDs();
77863                     if (mode.id !== 'select' || !ids.length || ids[0] !== springStreetId) {
77864                         // keep Spring Street selected..
77865                         context.enter(modeSelect(context, [springStreetId]));
77866                     }
77867                 });
77868
77869                 context.history().on('change.intro', function() {
77870                     if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
77871                         timeout(function() {
77872                             continueTo(searchStreet);
77873                         }, 300);  // after any transition (e.g. if user deleted intersection)
77874                     }
77875                 });
77876
77877                 function continueTo(nextStep) {
77878                     context.map().on('move.intro drawn.intro', null);
77879                     context.on('enter.intro', null);
77880                     context.history().on('change.intro', null);
77881                     nextStep();
77882                 }
77883             }
77884
77885
77886             function editorStreet() {
77887                 var selector = '.entity-editor-pane button.close svg use';
77888                 var href = select(selector).attr('href') || '#iD-icon-close';
77889
77890                 reveal('.entity-editor-pane', helpString('intro.navigation.street_different_fields') + '{br}' +
77891                     helpString('intro.navigation.editor_street', {
77892                         button: icon(href, 'pre-text'),
77893                         field1: onewayField.label(),
77894                         field2: maxspeedField.label()
77895                     }));
77896
77897                 context.on('exit.intro', function() {
77898                     continueTo(play);
77899                 });
77900
77901                 context.history().on('change.intro', function() {
77902                     // update the close icon in the tooltip if the user edits something.
77903                     var selector = '.entity-editor-pane button.close svg use';
77904                     var href = select(selector).attr('href') || '#iD-icon-close';
77905
77906                     reveal('.entity-editor-pane', helpString('intro.navigation.street_different_fields') + '{br}' +
77907                         helpString('intro.navigation.editor_street', {
77908                             button: icon(href, 'pre-text'),
77909                             field1: onewayField.label(),
77910                             field2: maxspeedField.label()
77911                         }), { duration: 0 }
77912                     );
77913                 });
77914
77915                 function continueTo(nextStep) {
77916                     context.on('exit.intro', null);
77917                     context.history().on('change.intro', null);
77918                     nextStep();
77919                 }
77920             }
77921
77922
77923             function play() {
77924                 dispatch$1.call('done');
77925                 reveal('.ideditor',
77926                     helpString('intro.navigation.play', { next: _t('intro.points.title') }), {
77927                         tooltipBox: '.intro-nav-wrap .chapter-point',
77928                         buttonText: _t('intro.ok'),
77929                         buttonCallback: function() { reveal('.ideditor'); }
77930                     }
77931                 );
77932             }
77933
77934
77935             chapter.enter = function() {
77936                 dragMap();
77937             };
77938
77939
77940             chapter.exit = function() {
77941                 timeouts.forEach(window.clearTimeout);
77942                 context.on('enter.intro exit.intro', null);
77943                 context.map().on('move.intro drawn.intro', null);
77944                 context.history().on('change.intro', null);
77945                 context.container().select('.inspector-wrap').on('wheel.intro', null);
77946                 context.container().select('.search-header input').on('keydown.intro keyup.intro', null);
77947             };
77948
77949
77950             chapter.restart = function() {
77951                 chapter.exit();
77952                 chapter.enter();
77953             };
77954
77955
77956             return utilRebind(chapter, dispatch$1, 'on');
77957         }
77958
77959         function uiIntroPoint(context, reveal) {
77960             var dispatch$1 = dispatch('done');
77961             var timeouts = [];
77962             var intersection = [-85.63279, 41.94394];
77963             var building = [-85.632422, 41.944045];
77964             var cafePreset = _mainPresetIndex.item('amenity/cafe');
77965             var _pointID = null;
77966
77967
77968             var chapter = {
77969                 title: 'intro.points.title'
77970             };
77971
77972
77973             function timeout(f, t) {
77974                 timeouts.push(window.setTimeout(f, t));
77975             }
77976
77977
77978             function eventCancel() {
77979                 event.stopPropagation();
77980                 event.preventDefault();
77981             }
77982
77983
77984             function addPoint() {
77985                 context.enter(modeBrowse(context));
77986                 context.history().reset('initial');
77987
77988                 var msec = transitionTime(intersection, context.map().center());
77989                 if (msec) { reveal(null, null, { duration: 0 }); }
77990                 context.map().centerZoomEase(intersection, 19, msec);
77991
77992                 timeout(function() {
77993                     var tooltip = reveal('button.add-point',
77994                         helpString('intro.points.points_info') + '{br}' + helpString('intro.points.add_point'));
77995
77996                     _pointID = null;
77997
77998                     tooltip.selectAll('.popover-inner')
77999                         .insert('svg', 'span')
78000                         .attr('class', 'tooltip-illustration')
78001                         .append('use')
78002                         .attr('xlink:href', '#iD-graphic-points');
78003
78004                     context.on('enter.intro', function(mode) {
78005                         if (mode.id !== 'add-point') { return; }
78006                         continueTo(placePoint);
78007                     });
78008                 }, msec + 100);
78009
78010                 function continueTo(nextStep) {
78011                     context.on('enter.intro', null);
78012                     nextStep();
78013                 }
78014             }
78015
78016
78017             function placePoint() {
78018                 if (context.mode().id !== 'add-point') {
78019                     return chapter.restart();
78020                 }
78021
78022                 var pointBox = pad(building, 150, context);
78023                 var textId = context.lastPointerType() === 'mouse' ? 'place_point' : 'place_point_touch';
78024                 reveal(pointBox, helpString('intro.points.' + textId));
78025
78026                 context.map().on('move.intro drawn.intro', function() {
78027                     pointBox = pad(building, 150, context);
78028                     reveal(pointBox, helpString('intro.points.' + textId), { duration: 0 });
78029                 });
78030
78031                 context.on('enter.intro', function(mode) {
78032                     if (mode.id !== 'select') { return chapter.restart(); }
78033                     _pointID = context.mode().selectedIDs()[0];
78034                     continueTo(searchPreset);
78035                 });
78036
78037                 function continueTo(nextStep) {
78038                     context.map().on('move.intro drawn.intro', null);
78039                     context.on('enter.intro', null);
78040                     nextStep();
78041                 }
78042             }
78043
78044
78045             function searchPreset() {
78046                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78047                     return addPoint();
78048                 }
78049
78050                 // disallow scrolling
78051                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78052
78053                 context.container().select('.preset-search-input')
78054                     .on('keydown.intro', null)
78055                     .on('keyup.intro', checkPresetSearch);
78056
78057                 reveal('.preset-search-input',
78058                     helpString('intro.points.search_cafe', { preset: cafePreset.name() })
78059                 );
78060
78061                 context.on('enter.intro', function(mode) {
78062                     if (!_pointID || !context.hasEntity(_pointID)) {
78063                         return continueTo(addPoint);
78064                     }
78065
78066                     var ids = context.selectedIDs();
78067                     if (mode.id !== 'select' || !ids.length || ids[0] !== _pointID) {
78068                         // keep the user's point selected..
78069                         context.enter(modeSelect(context, [_pointID]));
78070
78071                         // disallow scrolling
78072                         context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78073
78074                         context.container().select('.preset-search-input')
78075                             .on('keydown.intro', null)
78076                             .on('keyup.intro', checkPresetSearch);
78077
78078                         reveal('.preset-search-input',
78079                             helpString('intro.points.search_cafe', { preset: cafePreset.name() })
78080                         );
78081
78082                         context.history().on('change.intro', null);
78083                     }
78084                 });
78085
78086
78087                 function checkPresetSearch() {
78088                     var first = context.container().select('.preset-list-item:first-child');
78089
78090                     if (first.classed('preset-amenity-cafe')) {
78091                         context.container().select('.preset-search-input')
78092                             .on('keydown.intro', eventCancel, true)
78093                             .on('keyup.intro', null);
78094
78095                         reveal(first.select('.preset-list-button').node(),
78096                             helpString('intro.points.choose_cafe', { preset: cafePreset.name() }),
78097                             { duration: 300 }
78098                         );
78099
78100                         context.history().on('change.intro', function() {
78101                             continueTo(aboutFeatureEditor);
78102                         });
78103                     }
78104                 }
78105
78106                 function continueTo(nextStep) {
78107                     context.on('enter.intro', null);
78108                     context.history().on('change.intro', null);
78109                     context.container().select('.inspector-wrap').on('wheel.intro', null);
78110                     context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78111                     nextStep();
78112                 }
78113             }
78114
78115
78116             function aboutFeatureEditor() {
78117                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78118                     return addPoint();
78119                 }
78120
78121                 timeout(function() {
78122                     reveal('.entity-editor-pane', helpString('intro.points.feature_editor'), {
78123                         tooltipClass: 'intro-points-describe',
78124                         buttonText: _t('intro.ok'),
78125                         buttonCallback: function() { continueTo(addName); }
78126                     });
78127                 }, 400);
78128
78129                 context.on('exit.intro', function() {
78130                     // if user leaves select mode here, just continue with the tutorial.
78131                     continueTo(reselectPoint);
78132                 });
78133
78134                 function continueTo(nextStep) {
78135                     context.on('exit.intro', null);
78136                     nextStep();
78137                 }
78138             }
78139
78140
78141             function addName() {
78142                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78143                     return addPoint();
78144                 }
78145
78146                 // reset pane, in case user happened to change it..
78147                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78148
78149                 var addNameString = helpString('intro.points.fields_info') + '{br}' + helpString('intro.points.add_name');
78150
78151                 timeout(function() {
78152                     // It's possible for the user to add a name in a previous step..
78153                     // If so, don't tell them to add the name in this step.
78154                     // Give them an OK button instead.
78155                     var entity = context.entity(_pointID);
78156                     if (entity.tags.name) {
78157                         var tooltip = reveal('.entity-editor-pane', addNameString, {
78158                             tooltipClass: 'intro-points-describe',
78159                             buttonText: _t('intro.ok'),
78160                             buttonCallback: function() { continueTo(addCloseEditor); }
78161                         });
78162                         tooltip.select('.instruction').style('display', 'none');
78163
78164                     } else {
78165                         reveal('.entity-editor-pane', addNameString,
78166                             { tooltipClass: 'intro-points-describe' }
78167                         );
78168                     }
78169                 }, 400);
78170
78171                 context.history().on('change.intro', function() {
78172                     continueTo(addCloseEditor);
78173                 });
78174
78175                 context.on('exit.intro', function() {
78176                     // if user leaves select mode here, just continue with the tutorial.
78177                     continueTo(reselectPoint);
78178                 });
78179
78180                 function continueTo(nextStep) {
78181                     context.on('exit.intro', null);
78182                     context.history().on('change.intro', null);
78183                     nextStep();
78184                 }
78185             }
78186
78187
78188             function addCloseEditor() {
78189                 // reset pane, in case user happened to change it..
78190                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78191
78192                 var selector = '.entity-editor-pane button.close svg use';
78193                 var href = select(selector).attr('href') || '#iD-icon-close';
78194
78195                 context.on('exit.intro', function() {
78196                     continueTo(reselectPoint);
78197                 });
78198
78199                 reveal('.entity-editor-pane',
78200                     helpString('intro.points.add_close', { button: icon(href, 'pre-text') })
78201                 );
78202
78203                 function continueTo(nextStep) {
78204                     context.on('exit.intro', null);
78205                     nextStep();
78206                 }
78207             }
78208
78209
78210             function reselectPoint() {
78211                 if (!_pointID) { return chapter.restart(); }
78212                 var entity = context.hasEntity(_pointID);
78213                 if (!entity) { return chapter.restart(); }
78214
78215                 // make sure it's still a cafe, in case user somehow changed it..
78216                 var oldPreset = _mainPresetIndex.match(entity, context.graph());
78217                 context.replace(actionChangePreset(_pointID, oldPreset, cafePreset));
78218
78219                 context.enter(modeBrowse(context));
78220
78221                 var msec = transitionTime(entity.loc, context.map().center());
78222                 if (msec) { reveal(null, null, { duration: 0 }); }
78223                 context.map().centerEase(entity.loc, msec);
78224
78225                 timeout(function() {
78226                     var box = pointBox(entity.loc, context);
78227                     reveal(box, helpString('intro.points.reselect'), { duration: 600 });
78228
78229                     timeout(function() {
78230                         context.map().on('move.intro drawn.intro', function() {
78231                             var entity = context.hasEntity(_pointID);
78232                             if (!entity) { return chapter.restart(); }
78233                             var box = pointBox(entity.loc, context);
78234                             reveal(box, helpString('intro.points.reselect'), { duration: 0 });
78235                         });
78236                     }, 600); // after reveal..
78237
78238                     context.on('enter.intro', function(mode) {
78239                         if (mode.id !== 'select') { return; }
78240                         continueTo(updatePoint);
78241                     });
78242
78243                 }, msec + 100);
78244
78245                 function continueTo(nextStep) {
78246                     context.map().on('move.intro drawn.intro', null);
78247                     context.on('enter.intro', null);
78248                     nextStep();
78249                 }
78250             }
78251
78252
78253             function updatePoint() {
78254                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78255                     return continueTo(reselectPoint);
78256                 }
78257
78258                 // reset pane, in case user happened to untag the point..
78259                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78260
78261                 context.on('exit.intro', function() {
78262                     continueTo(reselectPoint);
78263                 });
78264
78265                 context.history().on('change.intro', function() {
78266                     continueTo(updateCloseEditor);
78267                 });
78268
78269                 timeout(function() {
78270                     reveal('.entity-editor-pane', helpString('intro.points.update'),
78271                         { tooltipClass: 'intro-points-describe' }
78272                     );
78273                 }, 400);
78274
78275                 function continueTo(nextStep) {
78276                     context.on('exit.intro', null);
78277                     context.history().on('change.intro', null);
78278                     nextStep();
78279                 }
78280             }
78281
78282
78283             function updateCloseEditor() {
78284                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78285                     return continueTo(reselectPoint);
78286                 }
78287
78288                 // reset pane, in case user happened to change it..
78289                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78290
78291                 context.on('exit.intro', function() {
78292                     continueTo(rightClickPoint);
78293                 });
78294
78295                 timeout(function() {
78296                     reveal('.entity-editor-pane',
78297                         helpString('intro.points.update_close', { button: icon('#iD-icon-close', 'pre-text') })
78298                     );
78299                 }, 500);
78300
78301                 function continueTo(nextStep) {
78302                     context.on('exit.intro', null);
78303                     nextStep();
78304                 }
78305             }
78306
78307
78308             function rightClickPoint() {
78309                 if (!_pointID) { return chapter.restart(); }
78310                 var entity = context.hasEntity(_pointID);
78311                 if (!entity) { return chapter.restart(); }
78312
78313                 context.enter(modeBrowse(context));
78314
78315                 var box = pointBox(entity.loc, context);
78316                 var textId = context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch';
78317                 reveal(box, helpString('intro.points.' + textId), { duration: 600 });
78318
78319                 timeout(function() {
78320                     context.map().on('move.intro', function() {
78321                         var entity = context.hasEntity(_pointID);
78322                         if (!entity) { return chapter.restart(); }
78323                         var box = pointBox(entity.loc, context);
78324                         reveal(box, helpString('intro.points.' + textId), { duration: 0 });
78325                     });
78326                 }, 600); // after reveal
78327
78328                 context.on('enter.intro', function(mode) {
78329                     if (mode.id !== 'select') { return; }
78330                     var ids = context.selectedIDs();
78331                     if (ids.length !== 1 || ids[0] !== _pointID) { return; }
78332
78333                     timeout(function() {
78334                         var node = selectMenuItem(context, 'delete').node();
78335                         if (!node) { return; }
78336                         continueTo(enterDelete);
78337                     }, 50);  // after menu visible
78338                 });
78339
78340                 function continueTo(nextStep) {
78341                     context.on('enter.intro', null);
78342                     context.map().on('move.intro', null);
78343                     nextStep();
78344                 }
78345             }
78346
78347
78348             function enterDelete() {
78349                 if (!_pointID) { return chapter.restart(); }
78350                 var entity = context.hasEntity(_pointID);
78351                 if (!entity) { return chapter.restart(); }
78352
78353                 var node = selectMenuItem(context, 'delete').node();
78354                 if (!node) { return continueTo(rightClickPoint); }
78355
78356                 reveal('.edit-menu',
78357                     helpString('intro.points.delete'),
78358                     { padding: 50 }
78359                 );
78360
78361                 timeout(function() {
78362                     context.map().on('move.intro', function() {
78363                         reveal('.edit-menu',
78364                             helpString('intro.points.delete'),
78365                             { duration: 0,  padding: 50 }
78366                         );
78367                     });
78368                 }, 300); // after menu visible
78369
78370                 context.on('exit.intro', function() {
78371                     if (!_pointID) { return chapter.restart(); }
78372                     var entity = context.hasEntity(_pointID);
78373                     if (entity) { return continueTo(rightClickPoint); }  // point still exists
78374                 });
78375
78376                 context.history().on('change.intro', function(changed) {
78377                     if (changed.deleted().length) {
78378                         continueTo(undo);
78379                     }
78380                 });
78381
78382                 function continueTo(nextStep) {
78383                     context.map().on('move.intro', null);
78384                     context.history().on('change.intro', null);
78385                     context.on('exit.intro', null);
78386                     nextStep();
78387                 }
78388             }
78389
78390
78391             function undo() {
78392                 context.history().on('change.intro', function() {
78393                     continueTo(play);
78394                 });
78395
78396                 reveal('.top-toolbar button.undo-button',
78397                     helpString('intro.points.undo')
78398                 );
78399
78400                 function continueTo(nextStep) {
78401                     context.history().on('change.intro', null);
78402                     nextStep();
78403                 }
78404             }
78405
78406
78407             function play() {
78408                 dispatch$1.call('done');
78409                 reveal('.ideditor',
78410                     helpString('intro.points.play', { next: _t('intro.areas.title') }), {
78411                         tooltipBox: '.intro-nav-wrap .chapter-area',
78412                         buttonText: _t('intro.ok'),
78413                         buttonCallback: function() { reveal('.ideditor'); }
78414                     }
78415                 );
78416             }
78417
78418
78419             chapter.enter = function() {
78420                 addPoint();
78421             };
78422
78423
78424             chapter.exit = function() {
78425                 timeouts.forEach(window.clearTimeout);
78426                 context.on('enter.intro exit.intro', null);
78427                 context.map().on('move.intro drawn.intro', null);
78428                 context.history().on('change.intro', null);
78429                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78430                 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78431             };
78432
78433
78434             chapter.restart = function() {
78435                 chapter.exit();
78436                 chapter.enter();
78437             };
78438
78439
78440             return utilRebind(chapter, dispatch$1, 'on');
78441         }
78442
78443         function uiIntroArea(context, reveal) {
78444             var dispatch$1 = dispatch('done');
78445             var playground = [-85.63552, 41.94159];
78446             var playgroundPreset = _mainPresetIndex.item('leisure/playground');
78447             var nameField = _mainPresetIndex.field('name');
78448             var descriptionField = _mainPresetIndex.field('description');
78449             var timeouts = [];
78450             var _areaID;
78451
78452
78453             var chapter = {
78454                 title: 'intro.areas.title'
78455             };
78456
78457
78458             function timeout(f, t) {
78459                 timeouts.push(window.setTimeout(f, t));
78460             }
78461
78462
78463             function eventCancel() {
78464                 event.stopPropagation();
78465                 event.preventDefault();
78466             }
78467
78468
78469             function revealPlayground(center, text, options) {
78470                 var padding = 180 * Math.pow(2, context.map().zoom() - 19.5);
78471                 var box = pad(center, padding, context);
78472                 reveal(box, text, options);
78473             }
78474
78475
78476             function addArea() {
78477                 context.enter(modeBrowse(context));
78478                 context.history().reset('initial');
78479                 _areaID = null;
78480
78481                 var msec = transitionTime(playground, context.map().center());
78482                 if (msec) { reveal(null, null, { duration: 0 }); }
78483                 context.map().centerZoomEase(playground, 19, msec);
78484
78485                 timeout(function() {
78486                     var tooltip = reveal('button.add-area',
78487                         helpString('intro.areas.add_playground'));
78488
78489                     tooltip.selectAll('.popover-inner')
78490                         .insert('svg', 'span')
78491                         .attr('class', 'tooltip-illustration')
78492                         .append('use')
78493                         .attr('xlink:href', '#iD-graphic-areas');
78494
78495                     context.on('enter.intro', function(mode) {
78496                         if (mode.id !== 'add-area') { return; }
78497                         continueTo(startPlayground);
78498                     });
78499                 }, msec + 100);
78500
78501                 function continueTo(nextStep) {
78502                     context.on('enter.intro', null);
78503                     nextStep();
78504                 }
78505             }
78506
78507
78508             function startPlayground() {
78509                 if (context.mode().id !== 'add-area') {
78510                     return chapter.restart();
78511                 }
78512
78513                 _areaID = null;
78514                 context.map().zoomEase(19.5, 500);
78515
78516                 timeout(function() {
78517                     var textId = context.lastPointerType() === 'mouse' ? 'starting_node_click' : 'starting_node_tap';
78518                     var startDrawString = helpString('intro.areas.start_playground') + helpString('intro.areas.' + textId);
78519                     revealPlayground(playground,
78520                         startDrawString, { duration: 250 }
78521                     );
78522
78523                     timeout(function() {
78524                         context.map().on('move.intro drawn.intro', function() {
78525                             revealPlayground(playground,
78526                                 startDrawString, { duration: 0 }
78527                             );
78528                         });
78529                         context.on('enter.intro', function(mode) {
78530                             if (mode.id !== 'draw-area') { return chapter.restart(); }
78531                             continueTo(continuePlayground);
78532                         });
78533                     }, 250);  // after reveal
78534
78535                 }, 550);  // after easing
78536
78537                 function continueTo(nextStep) {
78538                     context.map().on('move.intro drawn.intro', null);
78539                     context.on('enter.intro', null);
78540                     nextStep();
78541                 }
78542             }
78543
78544
78545             function continuePlayground() {
78546                 if (context.mode().id !== 'draw-area') {
78547                     return chapter.restart();
78548                 }
78549
78550                 _areaID = null;
78551                 revealPlayground(playground,
78552                     helpString('intro.areas.continue_playground'),
78553                     { duration: 250 }
78554                 );
78555
78556                 timeout(function() {
78557                     context.map().on('move.intro drawn.intro', function() {
78558                         revealPlayground(playground,
78559                             helpString('intro.areas.continue_playground'),
78560                             { duration: 0 }
78561                         );
78562                     });
78563                 }, 250);  // after reveal
78564
78565                 context.on('enter.intro', function(mode) {
78566                     if (mode.id === 'draw-area') {
78567                         var entity = context.hasEntity(context.selectedIDs()[0]);
78568                         if (entity && entity.nodes.length >= 6) {
78569                             return continueTo(finishPlayground);
78570                         } else {
78571                             return;
78572                         }
78573                     } else if (mode.id === 'select') {
78574                         _areaID = context.selectedIDs()[0];
78575                         return continueTo(searchPresets);
78576                     } else {
78577                         return chapter.restart();
78578                     }
78579                 });
78580
78581                 function continueTo(nextStep) {
78582                     context.map().on('move.intro drawn.intro', null);
78583                     context.on('enter.intro', null);
78584                     nextStep();
78585                 }
78586             }
78587
78588
78589             function finishPlayground() {
78590                 if (context.mode().id !== 'draw-area') {
78591                     return chapter.restart();
78592                 }
78593
78594                 _areaID = null;
78595
78596                 var finishString = helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
78597                     helpString('intro.areas.finish_playground');
78598                 revealPlayground(playground,
78599                     finishString, { duration: 250 }
78600                 );
78601
78602                 timeout(function() {
78603                     context.map().on('move.intro drawn.intro', function() {
78604                         revealPlayground(playground,
78605                             finishString, { duration: 0 }
78606                         );
78607                     });
78608                 }, 250);  // after reveal
78609
78610                 context.on('enter.intro', function(mode) {
78611                     if (mode.id === 'draw-area') {
78612                         return;
78613                     } else if (mode.id === 'select') {
78614                         _areaID = context.selectedIDs()[0];
78615                         return continueTo(searchPresets);
78616                     } else {
78617                         return chapter.restart();
78618                     }
78619                 });
78620
78621                 function continueTo(nextStep) {
78622                     context.map().on('move.intro drawn.intro', null);
78623                     context.on('enter.intro', null);
78624                     nextStep();
78625                 }
78626             }
78627
78628
78629             function searchPresets() {
78630                 if (!_areaID || !context.hasEntity(_areaID)) {
78631                     return addArea();
78632                 }
78633                 var ids = context.selectedIDs();
78634                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78635                     context.enter(modeSelect(context, [_areaID]));
78636                 }
78637
78638                 // disallow scrolling
78639                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78640
78641                 timeout(function() {
78642                     // reset pane, in case user somehow happened to change it..
78643                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
78644
78645                     context.container().select('.preset-search-input')
78646                         .on('keydown.intro', null)
78647                         .on('keyup.intro', checkPresetSearch);
78648
78649                     reveal('.preset-search-input',
78650                         helpString('intro.areas.search_playground', { preset: playgroundPreset.name() })
78651                     );
78652                 }, 400);  // after preset list pane visible..
78653
78654                 context.on('enter.intro', function(mode) {
78655                     if (!_areaID || !context.hasEntity(_areaID)) {
78656                         return continueTo(addArea);
78657                     }
78658
78659                     var ids = context.selectedIDs();
78660                     if (mode.id !== 'select' || !ids.length || ids[0] !== _areaID) {
78661                         // keep the user's area selected..
78662                         context.enter(modeSelect(context, [_areaID]));
78663
78664                         // reset pane, in case user somehow happened to change it..
78665                         context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
78666                         // disallow scrolling
78667                         context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78668
78669                         context.container().select('.preset-search-input')
78670                             .on('keydown.intro', null)
78671                             .on('keyup.intro', checkPresetSearch);
78672
78673                         reveal('.preset-search-input',
78674                             helpString('intro.areas.search_playground', { preset: playgroundPreset.name() })
78675                         );
78676
78677                         context.history().on('change.intro', null);
78678                     }
78679                 });
78680
78681                 function checkPresetSearch() {
78682                     var first = context.container().select('.preset-list-item:first-child');
78683
78684                     if (first.classed('preset-leisure-playground')) {
78685                         reveal(first.select('.preset-list-button').node(),
78686                             helpString('intro.areas.choose_playground', { preset: playgroundPreset.name() }),
78687                             { duration: 300 }
78688                         );
78689
78690                         context.container().select('.preset-search-input')
78691                             .on('keydown.intro', eventCancel, true)
78692                             .on('keyup.intro', null);
78693
78694                         context.history().on('change.intro', function() {
78695                             continueTo(clickAddField);
78696                         });
78697                     }
78698                 }
78699
78700                 function continueTo(nextStep) {
78701                     context.container().select('.inspector-wrap').on('wheel.intro', null);
78702                     context.on('enter.intro', null);
78703                     context.history().on('change.intro', null);
78704                     context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78705                     nextStep();
78706                 }
78707             }
78708
78709
78710             function clickAddField() {
78711                 if (!_areaID || !context.hasEntity(_areaID)) {
78712                     return addArea();
78713                 }
78714                 var ids = context.selectedIDs();
78715                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78716                     return searchPresets();
78717                 }
78718
78719                 if (!context.container().select('.form-field-description').empty()) {
78720                     return continueTo(describePlayground);
78721                 }
78722
78723                 // disallow scrolling
78724                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78725
78726                 timeout(function() {
78727                     // reset pane, in case user somehow happened to change it..
78728                     context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78729
78730                     // It's possible for the user to add a description in a previous step..
78731                     // If they did this already, just continue to next step.
78732                     var entity = context.entity(_areaID);
78733                     if (entity.tags.description) {
78734                         return continueTo(play);
78735                     }
78736
78737                     // scroll "Add field" into view
78738                     var box = context.container().select('.more-fields').node().getBoundingClientRect();
78739                     if (box.top > 300) {
78740                         var pane = context.container().select('.entity-editor-pane .inspector-body');
78741                         var start = pane.node().scrollTop;
78742                         var end = start + (box.top - 300);
78743
78744                         pane
78745                             .transition()
78746                             .duration(250)
78747                             .tween('scroll.inspector', function() {
78748                                 var node = this;
78749                                 var i = d3_interpolateNumber(start, end);
78750                                 return function(t) {
78751                                     node.scrollTop = i(t);
78752                                 };
78753                             });
78754                     }
78755
78756                     timeout(function() {
78757                         reveal('.more-fields .combobox-input',
78758                             helpString('intro.areas.add_field', {
78759                                 name: nameField.label(),
78760                                 description: descriptionField.label()
78761                             }),
78762                             { duration: 300 }
78763                         );
78764
78765                         context.container().select('.more-fields .combobox-input')
78766                             .on('click.intro', function() {
78767                                 // Watch for the combobox to appear...
78768                                 var watcher;
78769                                 watcher = window.setInterval(function() {
78770                                     if (!context.container().select('div.combobox').empty()) {
78771                                         window.clearInterval(watcher);
78772                                         continueTo(chooseDescriptionField);
78773                                     }
78774                                 }, 300);
78775                             });
78776                     }, 300);  // after "Add Field" visible
78777
78778                 }, 400);  // after editor pane visible
78779
78780                 context.on('exit.intro', function() {
78781                     return continueTo(searchPresets);
78782                 });
78783
78784                 function continueTo(nextStep) {
78785                     context.container().select('.inspector-wrap').on('wheel.intro', null);
78786                     context.container().select('.more-fields .combobox-input').on('click.intro', null);
78787                     context.on('exit.intro', null);
78788                     nextStep();
78789                 }
78790             }
78791
78792
78793             function chooseDescriptionField() {
78794                 if (!_areaID || !context.hasEntity(_areaID)) {
78795                     return addArea();
78796                 }
78797                 var ids = context.selectedIDs();
78798                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78799                     return searchPresets();
78800                 }
78801
78802                 if (!context.container().select('.form-field-description').empty()) {
78803                     return continueTo(describePlayground);
78804                 }
78805
78806                 // Make sure combobox is ready..
78807                 if (context.container().select('div.combobox').empty()) {
78808                     return continueTo(clickAddField);
78809                 }
78810                 // Watch for the combobox to go away..
78811                 var watcher;
78812                 watcher = window.setInterval(function() {
78813                     if (context.container().select('div.combobox').empty()) {
78814                         window.clearInterval(watcher);
78815                         timeout(function() {
78816                             if (context.container().select('.form-field-description').empty()) {
78817                                 continueTo(retryChooseDescription);
78818                             } else {
78819                                 continueTo(describePlayground);
78820                             }
78821                         }, 300);  // after description field added.
78822                     }
78823                 }, 300);
78824
78825                 reveal('div.combobox',
78826                     helpString('intro.areas.choose_field', { field: descriptionField.label() }),
78827                     { duration: 300 }
78828                 );
78829
78830                 context.on('exit.intro', function() {
78831                     return continueTo(searchPresets);
78832                 });
78833
78834                 function continueTo(nextStep) {
78835                     if (watcher) { window.clearInterval(watcher); }
78836                     context.on('exit.intro', null);
78837                     nextStep();
78838                 }
78839             }
78840
78841
78842             function describePlayground() {
78843                 if (!_areaID || !context.hasEntity(_areaID)) {
78844                     return addArea();
78845                 }
78846                 var ids = context.selectedIDs();
78847                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78848                     return searchPresets();
78849                 }
78850
78851                 // reset pane, in case user happened to change it..
78852                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78853
78854                 if (context.container().select('.form-field-description').empty()) {
78855                     return continueTo(retryChooseDescription);
78856                 }
78857
78858                 context.on('exit.intro', function() {
78859                     continueTo(play);
78860                 });
78861
78862                 reveal('.entity-editor-pane',
78863                     helpString('intro.areas.describe_playground', { button: icon('#iD-icon-close', 'pre-text') }),
78864                     { duration: 300 }
78865                 );
78866
78867                 function continueTo(nextStep) {
78868                     context.on('exit.intro', null);
78869                     nextStep();
78870                 }
78871             }
78872
78873
78874             function retryChooseDescription() {
78875                 if (!_areaID || !context.hasEntity(_areaID)) {
78876                     return addArea();
78877                 }
78878                 var ids = context.selectedIDs();
78879                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78880                     return searchPresets();
78881                 }
78882
78883                 // reset pane, in case user happened to change it..
78884                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78885
78886                 reveal('.entity-editor-pane',
78887                     helpString('intro.areas.retry_add_field', { field: descriptionField.label() }), {
78888                     buttonText: _t('intro.ok'),
78889                     buttonCallback: function() { continueTo(clickAddField); }
78890                 });
78891
78892                 context.on('exit.intro', function() {
78893                     return continueTo(searchPresets);
78894                 });
78895
78896                 function continueTo(nextStep) {
78897                     context.on('exit.intro', null);
78898                     nextStep();
78899                 }
78900             }
78901
78902
78903             function play() {
78904                 dispatch$1.call('done');
78905                 reveal('.ideditor',
78906                     helpString('intro.areas.play', { next: _t('intro.lines.title') }), {
78907                         tooltipBox: '.intro-nav-wrap .chapter-line',
78908                         buttonText: _t('intro.ok'),
78909                         buttonCallback: function() { reveal('.ideditor'); }
78910                     }
78911                 );
78912             }
78913
78914
78915             chapter.enter = function() {
78916                 addArea();
78917             };
78918
78919
78920             chapter.exit = function() {
78921                 timeouts.forEach(window.clearTimeout);
78922                 context.on('enter.intro exit.intro', null);
78923                 context.map().on('move.intro drawn.intro', null);
78924                 context.history().on('change.intro', null);
78925                 context.container().select('.inspector-wrap').on('wheel.intro', null);
78926                 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78927                 context.container().select('.more-fields .combobox-input').on('click.intro', null);
78928             };
78929
78930
78931             chapter.restart = function() {
78932                 chapter.exit();
78933                 chapter.enter();
78934             };
78935
78936
78937             return utilRebind(chapter, dispatch$1, 'on');
78938         }
78939
78940         function uiIntroLine(context, reveal) {
78941             var dispatch$1 = dispatch('done');
78942             var timeouts = [];
78943             var _tulipRoadID = null;
78944             var flowerRoadID = 'w646';
78945             var tulipRoadStart = [-85.6297754121684, 41.95805253325314];
78946             var tulipRoadMidpoint = [-85.62975395449628, 41.95787501510204];
78947             var tulipRoadIntersection = [-85.62974496187628, 41.95742515554585];
78948             var roadCategory = _mainPresetIndex.item('category-road_minor');
78949             var residentialPreset = _mainPresetIndex.item('highway/residential');
78950             var woodRoadID = 'w525';
78951             var woodRoadEndID = 'n2862';
78952             var woodRoadAddNode = [-85.62390110349587, 41.95397111462291];
78953             var woodRoadDragEndpoint = [-85.623867390213, 41.95466987786487];
78954             var woodRoadDragMidpoint = [-85.62386254803509, 41.95430395953872];
78955             var washingtonStreetID = 'w522';
78956             var twelfthAvenueID = 'w1';
78957             var eleventhAvenueEndID = 'n3550';
78958             var twelfthAvenueEndID = 'n5';
78959             var _washingtonSegmentID = null;
78960             var eleventhAvenueEnd = context.entity(eleventhAvenueEndID).loc;
78961             var twelfthAvenueEnd = context.entity(twelfthAvenueEndID).loc;
78962             var deleteLinesLoc = [-85.6219395542764, 41.95228033922477];
78963             var twelfthAvenue = [-85.62219310052491, 41.952505413152956];
78964
78965
78966             var chapter = {
78967                 title: 'intro.lines.title'
78968             };
78969
78970
78971             function timeout(f, t) {
78972                 timeouts.push(window.setTimeout(f, t));
78973             }
78974
78975
78976             function eventCancel() {
78977                 event.stopPropagation();
78978                 event.preventDefault();
78979             }
78980
78981
78982             function addLine() {
78983                 context.enter(modeBrowse(context));
78984                 context.history().reset('initial');
78985
78986                 var msec = transitionTime(tulipRoadStart, context.map().center());
78987                 if (msec) { reveal(null, null, { duration: 0 }); }
78988                 context.map().centerZoomEase(tulipRoadStart, 18.5, msec);
78989
78990                 timeout(function() {
78991                     var tooltip = reveal('button.add-line',
78992                         helpString('intro.lines.add_line'));
78993
78994                     tooltip.selectAll('.popover-inner')
78995                         .insert('svg', 'span')
78996                         .attr('class', 'tooltip-illustration')
78997                         .append('use')
78998                         .attr('xlink:href', '#iD-graphic-lines');
78999
79000                     context.on('enter.intro', function(mode) {
79001                         if (mode.id !== 'add-line') { return; }
79002                         continueTo(startLine);
79003                     });
79004                 }, msec + 100);
79005
79006                 function continueTo(nextStep) {
79007                     context.on('enter.intro', null);
79008                     nextStep();
79009                 }
79010             }
79011
79012
79013             function startLine() {
79014                 if (context.mode().id !== 'add-line') { return chapter.restart(); }
79015
79016                 _tulipRoadID = null;
79017
79018                 var padding = 70 * Math.pow(2, context.map().zoom() - 18);
79019                 var box = pad(tulipRoadStart, padding, context);
79020                 box.height = box.height + 100;
79021
79022                 var textId = context.lastPointerType() === 'mouse' ? 'start_line' : 'start_line_tap';
79023                 var startLineString = helpString('intro.lines.missing_road') + '{br}' +
79024                     helpString('intro.lines.line_draw_info') +
79025                     helpString('intro.lines.' + textId);
79026                 reveal(box, startLineString);
79027
79028                 context.map().on('move.intro drawn.intro', function() {
79029                     padding = 70 * Math.pow(2, context.map().zoom() - 18);
79030                     box = pad(tulipRoadStart, padding, context);
79031                     box.height = box.height + 100;
79032                     reveal(box, startLineString, { duration: 0 });
79033                 });
79034
79035                 context.on('enter.intro', function(mode) {
79036                     if (mode.id !== 'draw-line') { return chapter.restart(); }
79037                     continueTo(drawLine);
79038                 });
79039
79040                 function continueTo(nextStep) {
79041                     context.map().on('move.intro drawn.intro', null);
79042                     context.on('enter.intro', null);
79043                     nextStep();
79044                 }
79045             }
79046
79047
79048             function drawLine() {
79049                 if (context.mode().id !== 'draw-line') { return chapter.restart(); }
79050
79051                 _tulipRoadID = context.mode().selectedIDs()[0];
79052                 context.map().centerEase(tulipRoadMidpoint, 500);
79053
79054                 timeout(function() {
79055                     var padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
79056                     var box = pad(tulipRoadMidpoint, padding, context);
79057                     box.height = box.height * 2;
79058                     reveal(box,
79059                         helpString('intro.lines.intersect', { name: _t('intro.graph.name.flower-street') })
79060                     );
79061
79062                     context.map().on('move.intro drawn.intro', function() {
79063                         padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
79064                         box = pad(tulipRoadMidpoint, padding, context);
79065                         box.height = box.height * 2;
79066                         reveal(box,
79067                             helpString('intro.lines.intersect', { name: _t('intro.graph.name.flower-street') }),
79068                             { duration: 0 }
79069                         );
79070                     });
79071                 }, 550);  // after easing..
79072
79073                 context.history().on('change.intro', function() {
79074                     if (isLineConnected()) {
79075                         continueTo(continueLine);
79076                     }
79077                 });
79078
79079                 context.on('enter.intro', function(mode) {
79080                     if (mode.id === 'draw-line') {
79081                         return;
79082                     } else if (mode.id === 'select') {
79083                         continueTo(retryIntersect);
79084                         return;
79085                     } else {
79086                         return chapter.restart();
79087                     }
79088                 });
79089
79090                 function continueTo(nextStep) {
79091                     context.map().on('move.intro drawn.intro', null);
79092                     context.history().on('change.intro', null);
79093                     context.on('enter.intro', null);
79094                     nextStep();
79095                 }
79096             }
79097
79098
79099             function isLineConnected() {
79100                 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
79101                 if (!entity) { return false; }
79102
79103                 var drawNodes = context.graph().childNodes(entity);
79104                 return drawNodes.some(function(node) {
79105                     return context.graph().parentWays(node).some(function(parent) {
79106                         return parent.id === flowerRoadID;
79107                     });
79108                 });
79109             }
79110
79111
79112             function retryIntersect() {
79113                 select(window).on('pointerdown.intro mousedown.intro', eventCancel, true);
79114
79115                 var box = pad(tulipRoadIntersection, 80, context);
79116                 reveal(box,
79117                     helpString('intro.lines.retry_intersect', { name: _t('intro.graph.name.flower-street') })
79118                 );
79119
79120                 timeout(chapter.restart, 3000);
79121             }
79122
79123
79124             function continueLine() {
79125                 if (context.mode().id !== 'draw-line') { return chapter.restart(); }
79126                 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
79127                 if (!entity) { return chapter.restart(); }
79128
79129                 context.map().centerEase(tulipRoadIntersection, 500);
79130
79131                 var continueLineText = helpString('intro.lines.continue_line') + '{br}' +
79132                     helpString('intro.lines.finish_line_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
79133                     helpString('intro.lines.finish_road');
79134
79135                 reveal('.surface', continueLineText);
79136
79137                 context.on('enter.intro', function(mode) {
79138                     if (mode.id === 'draw-line')
79139                         { return; }
79140                     else if (mode.id === 'select')
79141                         { return continueTo(chooseCategoryRoad); }
79142                     else
79143                         { return chapter.restart(); }
79144                 });
79145
79146                 function continueTo(nextStep) {
79147                     context.on('enter.intro', null);
79148                     nextStep();
79149                 }
79150             }
79151
79152
79153             function chooseCategoryRoad() {
79154                 if (context.mode().id !== 'select') { return chapter.restart(); }
79155
79156                 context.on('exit.intro', function() {
79157                     return chapter.restart();
79158                 });
79159
79160                 var button = context.container().select('.preset-category-road_minor .preset-list-button');
79161                 if (button.empty()) { return chapter.restart(); }
79162
79163                 // disallow scrolling
79164                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79165
79166                 timeout(function() {
79167                     // reset pane, in case user somehow happened to change it..
79168                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
79169
79170                     reveal(button.node(),
79171                         helpString('intro.lines.choose_category_road', { category: roadCategory.name() })
79172                     );
79173
79174                     button.on('click.intro', function() {
79175                         continueTo(choosePresetResidential);
79176                     });
79177
79178                 }, 400);  // after editor pane visible
79179
79180                 function continueTo(nextStep) {
79181                     context.container().select('.inspector-wrap').on('wheel.intro', null);
79182                     context.container().select('.preset-list-button').on('click.intro', null);
79183                     context.on('exit.intro', null);
79184                     nextStep();
79185                 }
79186             }
79187
79188
79189             function choosePresetResidential() {
79190                 if (context.mode().id !== 'select') { return chapter.restart(); }
79191
79192                 context.on('exit.intro', function() {
79193                     return chapter.restart();
79194                 });
79195
79196                 var subgrid = context.container().select('.preset-category-road_minor .subgrid');
79197                 if (subgrid.empty()) { return chapter.restart(); }
79198
79199                 subgrid.selectAll(':not(.preset-highway-residential) .preset-list-button')
79200                     .on('click.intro', function() {
79201                         continueTo(retryPresetResidential);
79202                     });
79203
79204                 subgrid.selectAll('.preset-highway-residential .preset-list-button')
79205                     .on('click.intro', function() {
79206                         continueTo(nameRoad);
79207                     });
79208
79209                 timeout(function() {
79210                     reveal(subgrid.node(),
79211                         helpString('intro.lines.choose_preset_residential', { preset: residentialPreset.name() }),
79212                         { tooltipBox: '.preset-highway-residential .preset-list-button', duration: 300 }
79213                     );
79214                 }, 300);
79215
79216                 function continueTo(nextStep) {
79217                     context.container().select('.preset-list-button').on('click.intro', null);
79218                     context.on('exit.intro', null);
79219                     nextStep();
79220                 }
79221             }
79222
79223
79224             // selected wrong road type
79225             function retryPresetResidential() {
79226                 if (context.mode().id !== 'select') { return chapter.restart(); }
79227
79228                 context.on('exit.intro', function() {
79229                     return chapter.restart();
79230                 });
79231
79232                 // disallow scrolling
79233                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79234
79235                 timeout(function() {
79236                     var button = context.container().select('.entity-editor-pane .preset-list-button');
79237
79238                     reveal(button.node(),
79239                         helpString('intro.lines.retry_preset_residential', { preset: residentialPreset.name() })
79240                     );
79241
79242                     button.on('click.intro', function() {
79243                         continueTo(chooseCategoryRoad);
79244                     });
79245
79246                 }, 500);
79247
79248                 function continueTo(nextStep) {
79249                     context.container().select('.inspector-wrap').on('wheel.intro', null);
79250                     context.container().select('.preset-list-button').on('click.intro', null);
79251                     context.on('exit.intro', null);
79252                     nextStep();
79253                 }
79254             }
79255
79256
79257             function nameRoad() {
79258                 context.on('exit.intro', function() {
79259                     continueTo(didNameRoad);
79260                 });
79261
79262                 timeout(function() {
79263                     reveal('.entity-editor-pane',
79264                         helpString('intro.lines.name_road', { button: icon('#iD-icon-close', 'pre-text') }),
79265                         { tooltipClass: 'intro-lines-name_road' }
79266                     );
79267                 }, 500);
79268
79269                 function continueTo(nextStep) {
79270                     context.on('exit.intro', null);
79271                     nextStep();
79272                 }
79273             }
79274
79275
79276             function didNameRoad() {
79277                 context.history().checkpoint('doneAddLine');
79278
79279                 timeout(function() {
79280                     reveal('.surface', helpString('intro.lines.did_name_road'), {
79281                         buttonText: _t('intro.ok'),
79282                         buttonCallback: function() { continueTo(updateLine); }
79283                     });
79284                 }, 500);
79285
79286                 function continueTo(nextStep) {
79287                     nextStep();
79288                 }
79289             }
79290
79291
79292             function updateLine() {
79293                 context.history().reset('doneAddLine');
79294                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79295                     return chapter.restart();
79296                 }
79297
79298                 var msec = transitionTime(woodRoadDragMidpoint, context.map().center());
79299                 if (msec) { reveal(null, null, { duration: 0 }); }
79300                 context.map().centerZoomEase(woodRoadDragMidpoint, 19, msec);
79301
79302                 timeout(function() {
79303                     var padding = 250 * Math.pow(2, context.map().zoom() - 19);
79304                     var box = pad(woodRoadDragMidpoint, padding, context);
79305                     var advance = function() { continueTo(addNode); };
79306
79307                     reveal(box, helpString('intro.lines.update_line'),
79308                         { buttonText: _t('intro.ok'), buttonCallback: advance }
79309                     );
79310
79311                     context.map().on('move.intro drawn.intro', function() {
79312                         var padding = 250 * Math.pow(2, context.map().zoom() - 19);
79313                         var box = pad(woodRoadDragMidpoint, padding, context);
79314                         reveal(box, helpString('intro.lines.update_line'),
79315                             { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79316                         );
79317                     });
79318                 }, msec + 100);
79319
79320                 function continueTo(nextStep) {
79321                     context.map().on('move.intro drawn.intro', null);
79322                     nextStep();
79323                 }
79324             }
79325
79326
79327             function addNode() {
79328                 context.history().reset('doneAddLine');
79329                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79330                     return chapter.restart();
79331                 }
79332
79333                 var padding = 40 * Math.pow(2, context.map().zoom() - 19);
79334                 var box = pad(woodRoadAddNode, padding, context);
79335                 var addNodeString = helpString('intro.lines.add_node' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
79336                 reveal(box, addNodeString);
79337
79338                 context.map().on('move.intro drawn.intro', function() {
79339                     var padding = 40 * Math.pow(2, context.map().zoom() - 19);
79340                     var box = pad(woodRoadAddNode, padding, context);
79341                     reveal(box, addNodeString, { duration: 0 });
79342                 });
79343
79344                 context.history().on('change.intro', function(changed) {
79345                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79346                         return continueTo(updateLine);
79347                     }
79348                     if (changed.created().length === 1) {
79349                         timeout(function() { continueTo(startDragEndpoint); }, 500);
79350                     }
79351                 });
79352
79353                 context.on('enter.intro', function(mode) {
79354                     if (mode.id !== 'select') {
79355                         continueTo(updateLine);
79356                     }
79357                 });
79358
79359                 function continueTo(nextStep) {
79360                     context.map().on('move.intro drawn.intro', null);
79361                     context.history().on('change.intro', null);
79362                     context.on('enter.intro', null);
79363                     nextStep();
79364                 }
79365             }
79366
79367
79368             function startDragEndpoint() {
79369                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79370                     return continueTo(updateLine);
79371                 }
79372                 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79373                 var box = pad(woodRoadDragEndpoint, padding, context);
79374                 var startDragString = helpString('intro.lines.start_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch')) +
79375                     helpString('intro.lines.drag_to_intersection');
79376                 reveal(box, startDragString);
79377
79378                 context.map().on('move.intro drawn.intro', function() {
79379                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79380                         return continueTo(updateLine);
79381                     }
79382                     var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79383                     var box = pad(woodRoadDragEndpoint, padding, context);
79384                     reveal(box, startDragString, { duration: 0 });
79385
79386                     var entity = context.entity(woodRoadEndID);
79387                     if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) <= 4) {
79388                         continueTo(finishDragEndpoint);
79389                     }
79390                 });
79391
79392                 function continueTo(nextStep) {
79393                     context.map().on('move.intro drawn.intro', null);
79394                     nextStep();
79395                 }
79396             }
79397
79398
79399             function finishDragEndpoint() {
79400                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79401                     return continueTo(updateLine);
79402                 }
79403
79404                 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79405                 var box = pad(woodRoadDragEndpoint, padding, context);
79406                 var finishDragString = helpString('intro.lines.spot_looks_good') +
79407                     helpString('intro.lines.finish_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
79408                 reveal(box, finishDragString);
79409
79410                 context.map().on('move.intro drawn.intro', function() {
79411                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79412                         return continueTo(updateLine);
79413                     }
79414                     var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79415                     var box = pad(woodRoadDragEndpoint, padding, context);
79416                     reveal(box, finishDragString, { duration: 0 });
79417
79418                     var entity = context.entity(woodRoadEndID);
79419                     if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) > 4) {
79420                         continueTo(startDragEndpoint);
79421                     }
79422                 });
79423
79424                 context.on('enter.intro', function() {
79425                     continueTo(startDragMidpoint);
79426                 });
79427
79428                 function continueTo(nextStep) {
79429                     context.map().on('move.intro drawn.intro', null);
79430                     context.on('enter.intro', null);
79431                     nextStep();
79432                 }
79433             }
79434
79435
79436             function startDragMidpoint() {
79437                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79438                     return continueTo(updateLine);
79439                 }
79440                 if (context.selectedIDs().indexOf(woodRoadID) === -1) {
79441                     context.enter(modeSelect(context, [woodRoadID]));
79442                 }
79443
79444                 var padding = 80 * Math.pow(2, context.map().zoom() - 19);
79445                 var box = pad(woodRoadDragMidpoint, padding, context);
79446                 reveal(box, helpString('intro.lines.start_drag_midpoint'));
79447
79448                 context.map().on('move.intro drawn.intro', function() {
79449                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79450                         return continueTo(updateLine);
79451                     }
79452                     var padding = 80 * Math.pow(2, context.map().zoom() - 19);
79453                     var box = pad(woodRoadDragMidpoint, padding, context);
79454                     reveal(box, helpString('intro.lines.start_drag_midpoint'), { duration: 0 });
79455                 });
79456
79457                 context.history().on('change.intro', function(changed) {
79458                     if (changed.created().length === 1) {
79459                         continueTo(continueDragMidpoint);
79460                     }
79461                 });
79462
79463                 context.on('enter.intro', function(mode) {
79464                     if (mode.id !== 'select') {
79465                         // keep Wood Road selected so midpoint triangles are drawn..
79466                         context.enter(modeSelect(context, [woodRoadID]));
79467                     }
79468                 });
79469
79470                 function continueTo(nextStep) {
79471                     context.map().on('move.intro drawn.intro', null);
79472                     context.history().on('change.intro', null);
79473                     context.on('enter.intro', null);
79474                     nextStep();
79475                 }
79476             }
79477
79478
79479             function continueDragMidpoint() {
79480                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79481                     return continueTo(updateLine);
79482                 }
79483
79484                 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79485                 var box = pad(woodRoadDragEndpoint, padding, context);
79486                 box.height += 400;
79487
79488                 var advance = function() {
79489                     context.history().checkpoint('doneUpdateLine');
79490                     continueTo(deleteLines);
79491                 };
79492
79493                 reveal(box, helpString('intro.lines.continue_drag_midpoint'),
79494                     { buttonText: _t('intro.ok'), buttonCallback: advance }
79495                 );
79496
79497                 context.map().on('move.intro drawn.intro', function() {
79498                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79499                         return continueTo(updateLine);
79500                     }
79501                     var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79502                     var box = pad(woodRoadDragEndpoint, padding, context);
79503                     box.height += 400;
79504                     reveal(box, helpString('intro.lines.continue_drag_midpoint'),
79505                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79506                     );
79507                 });
79508
79509                 function continueTo(nextStep) {
79510                     context.map().on('move.intro drawn.intro', null);
79511                     nextStep();
79512                 }
79513             }
79514
79515
79516             function deleteLines() {
79517                 context.history().reset('doneUpdateLine');
79518                 context.enter(modeBrowse(context));
79519
79520                 if (!context.hasEntity(washingtonStreetID) ||
79521                     !context.hasEntity(twelfthAvenueID) ||
79522                     !context.hasEntity(eleventhAvenueEndID)) {
79523                     return chapter.restart();
79524                 }
79525
79526                 var msec = transitionTime(deleteLinesLoc, context.map().center());
79527                 if (msec) { reveal(null, null, { duration: 0 }); }
79528                 context.map().centerZoomEase(deleteLinesLoc, 18, msec);
79529
79530                 timeout(function() {
79531                     var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79532                     var box = pad(deleteLinesLoc, padding, context);
79533                     box.top -= 200;
79534                     box.height += 400;
79535                     var advance = function() { continueTo(rightClickIntersection); };
79536
79537                     reveal(box, helpString('intro.lines.delete_lines', { street: _t('intro.graph.name.12th-avenue') }),
79538                         { buttonText: _t('intro.ok'), buttonCallback: advance }
79539                     );
79540
79541                     context.map().on('move.intro drawn.intro', function() {
79542                         var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79543                         var box = pad(deleteLinesLoc, padding, context);
79544                         box.top -= 200;
79545                         box.height += 400;
79546                         reveal(box, helpString('intro.lines.delete_lines', { street: _t('intro.graph.name.12th-avenue') }),
79547                             { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79548                         );
79549                     });
79550
79551                     context.history().on('change.intro', function() {
79552                         timeout(function() {
79553                             continueTo(deleteLines);
79554                         }, 500);  // after any transition (e.g. if user deleted intersection)
79555                     });
79556
79557                 }, msec + 100);
79558
79559                 function continueTo(nextStep) {
79560                     context.map().on('move.intro drawn.intro', null);
79561                     context.history().on('change.intro', null);
79562                     nextStep();
79563                 }
79564             }
79565
79566
79567             function rightClickIntersection() {
79568                 context.history().reset('doneUpdateLine');
79569                 context.enter(modeBrowse(context));
79570
79571                 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
79572
79573                 var rightClickString = helpString('intro.lines.split_street', {
79574                         street1: _t('intro.graph.name.11th-avenue'),
79575                         street2: _t('intro.graph.name.washington-street')
79576                     }) +
79577                     helpString('intro.lines.' + (context.lastPointerType() === 'mouse' ? 'rightclick_intersection' : 'edit_menu_intersection_touch'));
79578
79579                 timeout(function() {
79580                     var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79581                     var box = pad(eleventhAvenueEnd, padding, context);
79582                     reveal(box, rightClickString);
79583
79584                     context.map().on('move.intro drawn.intro', function() {
79585                         var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79586                         var box = pad(eleventhAvenueEnd, padding, context);
79587                         reveal(box, rightClickString,
79588                             { duration: 0 }
79589                         );
79590                     });
79591
79592                     context.on('enter.intro', function(mode) {
79593                         if (mode.id !== 'select') { return; }
79594                         var ids = context.selectedIDs();
79595                         if (ids.length !== 1 || ids[0] !== eleventhAvenueEndID) { return; }
79596
79597                         timeout(function() {
79598                             var node = selectMenuItem(context, 'split').node();
79599                             if (!node) { return; }
79600                             continueTo(splitIntersection);
79601                         }, 50);  // after menu visible
79602                     });
79603
79604                     context.history().on('change.intro', function() {
79605                         timeout(function() {
79606                             continueTo(deleteLines);
79607                         }, 300);  // after any transition (e.g. if user deleted intersection)
79608                     });
79609
79610                 }, 600);
79611
79612                 function continueTo(nextStep) {
79613                     context.map().on('move.intro drawn.intro', null);
79614                     context.on('enter.intro', null);
79615                     context.history().on('change.intro', null);
79616                     nextStep();
79617                 }
79618             }
79619
79620
79621             function splitIntersection() {
79622                 if (!context.hasEntity(washingtonStreetID) ||
79623                     !context.hasEntity(twelfthAvenueID) ||
79624                     !context.hasEntity(eleventhAvenueEndID)) {
79625                     return continueTo(deleteLines);
79626                 }
79627
79628                 var node = selectMenuItem(context, 'split').node();
79629                 if (!node) { return continueTo(rightClickIntersection); }
79630
79631                 var wasChanged = false;
79632                 _washingtonSegmentID = null;
79633
79634                 reveal('.edit-menu', helpString('intro.lines.split_intersection',
79635                     { street: _t('intro.graph.name.washington-street') }),
79636                     { padding: 50 }
79637                 );
79638
79639                 context.map().on('move.intro drawn.intro', function() {
79640                     var node = selectMenuItem(context, 'split').node();
79641                     if (!wasChanged && !node) { return continueTo(rightClickIntersection); }
79642
79643                     reveal('.edit-menu', helpString('intro.lines.split_intersection',
79644                         { street: _t('intro.graph.name.washington-street') }),
79645                         { duration: 0, padding: 50 }
79646                     );
79647                 });
79648
79649                 context.history().on('change.intro', function(changed) {
79650                     wasChanged = true;
79651                     timeout(function() {
79652                         if (context.history().undoAnnotation() === _t('operations.split.annotation.line')) {
79653                             _washingtonSegmentID = changed.created()[0].id;
79654                             continueTo(didSplit);
79655                         } else {
79656                             _washingtonSegmentID = null;
79657                             continueTo(retrySplit);
79658                         }
79659                     }, 300);  // after any transition (e.g. if user deleted intersection)
79660                 });
79661
79662                 function continueTo(nextStep) {
79663                     context.map().on('move.intro drawn.intro', null);
79664                     context.history().on('change.intro', null);
79665                     nextStep();
79666                 }
79667             }
79668
79669
79670             function retrySplit() {
79671                 context.enter(modeBrowse(context));
79672                 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
79673                 var advance = function() { continueTo(rightClickIntersection); };
79674
79675                 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79676                 var box = pad(eleventhAvenueEnd, padding, context);
79677                 reveal(box, helpString('intro.lines.retry_split'),
79678                     { buttonText: _t('intro.ok'), buttonCallback: advance }
79679                 );
79680
79681                 context.map().on('move.intro drawn.intro', function() {
79682                     var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79683                     var box = pad(eleventhAvenueEnd, padding, context);
79684                     reveal(box, helpString('intro.lines.retry_split'),
79685                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79686                     );
79687                 });
79688
79689                 function continueTo(nextStep) {
79690                     context.map().on('move.intro drawn.intro', null);
79691                     nextStep();
79692                 }
79693             }
79694
79695
79696             function didSplit() {
79697                 if (!_washingtonSegmentID ||
79698                     !context.hasEntity(_washingtonSegmentID) ||
79699                     !context.hasEntity(washingtonStreetID) ||
79700                     !context.hasEntity(twelfthAvenueID) ||
79701                     !context.hasEntity(eleventhAvenueEndID)) {
79702                     return continueTo(rightClickIntersection);
79703                 }
79704
79705                 var ids = context.selectedIDs();
79706                 var string = 'intro.lines.did_split_' + (ids.length > 1 ? 'multi' : 'single');
79707                 var street = _t('intro.graph.name.washington-street');
79708
79709                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79710                 var box = pad(twelfthAvenue, padding, context);
79711                 box.width = box.width / 2;
79712                 reveal(box, helpString(string, { street1: street, street2: street }),
79713                     { duration: 500 }
79714                 );
79715
79716                 timeout(function() {
79717                     context.map().centerZoomEase(twelfthAvenue, 18, 500);
79718
79719                     context.map().on('move.intro drawn.intro', function() {
79720                         var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79721                         var box = pad(twelfthAvenue, padding, context);
79722                         box.width = box.width / 2;
79723                         reveal(box, helpString(string, { street1: street, street2: street }),
79724                             { duration: 0 }
79725                         );
79726                     });
79727                 }, 600);  // after initial reveal and curtain cut
79728
79729                 context.on('enter.intro', function() {
79730                     var ids = context.selectedIDs();
79731                     if (ids.length === 1 && ids[0] === _washingtonSegmentID) {
79732                         continueTo(multiSelect);
79733                     }
79734                 });
79735
79736                 context.history().on('change.intro', function() {
79737                     if (!_washingtonSegmentID ||
79738                         !context.hasEntity(_washingtonSegmentID) ||
79739                         !context.hasEntity(washingtonStreetID) ||
79740                         !context.hasEntity(twelfthAvenueID) ||
79741                         !context.hasEntity(eleventhAvenueEndID)) {
79742                         return continueTo(rightClickIntersection);
79743                     }
79744                 });
79745
79746                 function continueTo(nextStep) {
79747                     context.map().on('move.intro drawn.intro', null);
79748                     context.on('enter.intro', null);
79749                     context.history().on('change.intro', null);
79750                     nextStep();
79751                 }
79752             }
79753
79754
79755             function multiSelect() {
79756                 if (!_washingtonSegmentID ||
79757                     !context.hasEntity(_washingtonSegmentID) ||
79758                     !context.hasEntity(washingtonStreetID) ||
79759                     !context.hasEntity(twelfthAvenueID) ||
79760                     !context.hasEntity(eleventhAvenueEndID)) {
79761                     return continueTo(rightClickIntersection);
79762                 }
79763
79764                 var ids = context.selectedIDs();
79765                 var hasWashington = ids.indexOf(_washingtonSegmentID) !== -1;
79766                 var hasTwelfth = ids.indexOf(twelfthAvenueID) !== -1;
79767
79768                 if (hasWashington && hasTwelfth) {
79769                     return continueTo(multiRightClick);
79770                 } else if (!hasWashington && !hasTwelfth) {
79771                     return continueTo(didSplit);
79772                 }
79773
79774                 context.map().centerZoomEase(twelfthAvenue, 18, 500);
79775
79776                 timeout(function() {
79777                     var selected, other, padding, box;
79778                     if (hasWashington) {
79779                         selected = _t('intro.graph.name.washington-street');
79780                         other = _t('intro.graph.name.12th-avenue');
79781                         padding = 60 * Math.pow(2, context.map().zoom() - 18);
79782                         box = pad(twelfthAvenueEnd, padding, context);
79783                         box.width *= 3;
79784                     } else {
79785                         selected = _t('intro.graph.name.12th-avenue');
79786                         other = _t('intro.graph.name.washington-street');
79787                         padding = 200 * Math.pow(2, context.map().zoom() - 18);
79788                         box = pad(twelfthAvenue, padding, context);
79789                         box.width /= 2;
79790                     }
79791
79792                     reveal(box,
79793                         helpString('intro.lines.multi_select',
79794                             { selected: selected, other1: other }) + ' ' +
79795                         helpString('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'),
79796                             { selected: selected, other2: other })
79797                     );
79798
79799                     context.map().on('move.intro drawn.intro', function() {
79800                         if (hasWashington) {
79801                             selected = _t('intro.graph.name.washington-street');
79802                             other = _t('intro.graph.name.12th-avenue');
79803                             padding = 60 * Math.pow(2, context.map().zoom() - 18);
79804                             box = pad(twelfthAvenueEnd, padding, context);
79805                             box.width *= 3;
79806                         } else {
79807                             selected = _t('intro.graph.name.12th-avenue');
79808                             other = _t('intro.graph.name.washington-street');
79809                             padding = 200 * Math.pow(2, context.map().zoom() - 18);
79810                             box = pad(twelfthAvenue, padding, context);
79811                             box.width /= 2;
79812                         }
79813
79814                         reveal(box,
79815                             helpString('intro.lines.multi_select',
79816                                 { selected: selected, other1: other }) + ' ' +
79817                             helpString('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'),
79818                                 { selected: selected, other2: other }),
79819                             { duration: 0 }
79820                         );
79821                     });
79822
79823                     context.on('enter.intro', function() {
79824                         continueTo(multiSelect);
79825                     });
79826
79827                     context.history().on('change.intro', function() {
79828                         if (!_washingtonSegmentID ||
79829                             !context.hasEntity(_washingtonSegmentID) ||
79830                             !context.hasEntity(washingtonStreetID) ||
79831                             !context.hasEntity(twelfthAvenueID) ||
79832                             !context.hasEntity(eleventhAvenueEndID)) {
79833                             return continueTo(rightClickIntersection);
79834                         }
79835                     });
79836                 }, 600);
79837
79838                 function continueTo(nextStep) {
79839                     context.map().on('move.intro drawn.intro', null);
79840                     context.on('enter.intro', null);
79841                     context.history().on('change.intro', null);
79842                     nextStep();
79843                 }
79844             }
79845
79846
79847             function multiRightClick() {
79848                 if (!_washingtonSegmentID ||
79849                     !context.hasEntity(_washingtonSegmentID) ||
79850                     !context.hasEntity(washingtonStreetID) ||
79851                     !context.hasEntity(twelfthAvenueID) ||
79852                     !context.hasEntity(eleventhAvenueEndID)) {
79853                     return continueTo(rightClickIntersection);
79854                 }
79855
79856                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79857                 var box = pad(twelfthAvenue, padding, context);
79858
79859                 var rightClickString = helpString('intro.lines.multi_select_success') +
79860                     helpString('intro.lines.multi_' + (context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch'));
79861                 reveal(box, rightClickString);
79862
79863                 context.map().on('move.intro drawn.intro', function() {
79864                     var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79865                     var box = pad(twelfthAvenue, padding, context);
79866                     reveal(box, rightClickString, { duration: 0 });
79867                 });
79868
79869                 context.ui().editMenu().on('toggled.intro', function(open) {
79870                     if (!open) { return; }
79871
79872                     timeout(function() {
79873                         var ids = context.selectedIDs();
79874                         if (ids.length === 2 &&
79875                             ids.indexOf(twelfthAvenueID) !== -1 &&
79876                             ids.indexOf(_washingtonSegmentID) !== -1) {
79877                                 var node = selectMenuItem(context, 'delete').node();
79878                                 if (!node) { return; }
79879                                 continueTo(multiDelete);
79880                         } else if (ids.length === 1 &&
79881                             ids.indexOf(_washingtonSegmentID) !== -1) {
79882                             return continueTo(multiSelect);
79883                         } else {
79884                             return continueTo(didSplit);
79885                         }
79886                     }, 300);  // after edit menu visible
79887                 });
79888
79889                 context.history().on('change.intro', function() {
79890                     if (!_washingtonSegmentID ||
79891                         !context.hasEntity(_washingtonSegmentID) ||
79892                         !context.hasEntity(washingtonStreetID) ||
79893                         !context.hasEntity(twelfthAvenueID) ||
79894                         !context.hasEntity(eleventhAvenueEndID)) {
79895                         return continueTo(rightClickIntersection);
79896                     }
79897                 });
79898
79899                 function continueTo(nextStep) {
79900                     context.map().on('move.intro drawn.intro', null);
79901                     context.ui().editMenu().on('toggled.intro', null);
79902                     context.history().on('change.intro', null);
79903                     nextStep();
79904                 }
79905             }
79906
79907
79908             function multiDelete() {
79909                 if (!_washingtonSegmentID ||
79910                     !context.hasEntity(_washingtonSegmentID) ||
79911                     !context.hasEntity(washingtonStreetID) ||
79912                     !context.hasEntity(twelfthAvenueID) ||
79913                     !context.hasEntity(eleventhAvenueEndID)) {
79914                     return continueTo(rightClickIntersection);
79915                 }
79916
79917                 var node = selectMenuItem(context, 'delete').node();
79918                 if (!node) { return continueTo(multiRightClick); }
79919
79920                 reveal('.edit-menu',
79921                     helpString('intro.lines.multi_delete'),
79922                     { padding: 50 }
79923                 );
79924
79925                 context.map().on('move.intro drawn.intro', function() {
79926                     reveal('.edit-menu',
79927                         helpString('intro.lines.multi_delete'),
79928                         { duration: 0, padding: 50 }
79929                     );
79930                 });
79931
79932                 context.on('exit.intro', function() {
79933                     if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
79934                         return continueTo(multiSelect);  // left select mode but roads still exist
79935                     }
79936                 });
79937
79938                 context.history().on('change.intro', function() {
79939                     if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
79940                         continueTo(retryDelete);         // changed something but roads still exist
79941                     } else {
79942                         continueTo(play);
79943                     }
79944                 });
79945
79946                 function continueTo(nextStep) {
79947                     context.map().on('move.intro drawn.intro', null);
79948                     context.on('exit.intro', null);
79949                     context.history().on('change.intro', null);
79950                     nextStep();
79951                 }
79952             }
79953
79954
79955             function retryDelete() {
79956                 context.enter(modeBrowse(context));
79957
79958                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79959                 var box = pad(twelfthAvenue, padding, context);
79960                 reveal(box, helpString('intro.lines.retry_delete'), {
79961                     buttonText: _t('intro.ok'),
79962                     buttonCallback: function() { continueTo(multiSelect); }
79963                 });
79964
79965                 function continueTo(nextStep) {
79966                     nextStep();
79967                 }
79968             }
79969
79970
79971             function play() {
79972                 dispatch$1.call('done');
79973                 reveal('.ideditor',
79974                     helpString('intro.lines.play', { next: _t('intro.buildings.title') }), {
79975                         tooltipBox: '.intro-nav-wrap .chapter-building',
79976                         buttonText: _t('intro.ok'),
79977                         buttonCallback: function() { reveal('.ideditor'); }
79978                     }
79979                 );
79980            }
79981
79982
79983             chapter.enter = function() {
79984                 addLine();
79985             };
79986
79987
79988             chapter.exit = function() {
79989                 timeouts.forEach(window.clearTimeout);
79990                 select(window).on('pointerdown.intro mousedown.intro', null, true);
79991                 context.on('enter.intro exit.intro', null);
79992                 context.map().on('move.intro drawn.intro', null);
79993                 context.history().on('change.intro', null);
79994                 context.container().select('.inspector-wrap').on('wheel.intro', null);
79995                 context.container().select('.preset-list-button').on('click.intro', null);
79996             };
79997
79998
79999             chapter.restart = function() {
80000                 chapter.exit();
80001                 chapter.enter();
80002             };
80003
80004
80005             return utilRebind(chapter, dispatch$1, 'on');
80006         }
80007
80008         function uiIntroBuilding(context, reveal) {
80009             var dispatch$1 = dispatch('done');
80010             var house = [-85.62815, 41.95638];
80011             var tank = [-85.62732, 41.95347];
80012             var buildingCatetory = _mainPresetIndex.item('category-building');
80013             var housePreset = _mainPresetIndex.item('building/house');
80014             var tankPreset = _mainPresetIndex.item('man_made/storage_tank');
80015             var timeouts = [];
80016             var _houseID = null;
80017             var _tankID = null;
80018
80019
80020             var chapter = {
80021                 title: 'intro.buildings.title'
80022             };
80023
80024
80025             function timeout(f, t) {
80026                 timeouts.push(window.setTimeout(f, t));
80027             }
80028
80029
80030             function eventCancel() {
80031                 event.stopPropagation();
80032                 event.preventDefault();
80033             }
80034
80035
80036             function revealHouse(center, text, options) {
80037                 var padding = 160 * Math.pow(2, context.map().zoom() - 20);
80038                 var box = pad(center, padding, context);
80039                 reveal(box, text, options);
80040             }
80041
80042
80043             function revealTank(center, text, options) {
80044                 var padding = 190 * Math.pow(2, context.map().zoom() - 19.5);
80045                 var box = pad(center, padding, context);
80046                 reveal(box, text, options);
80047             }
80048
80049
80050             function addHouse() {
80051                 context.enter(modeBrowse(context));
80052                 context.history().reset('initial');
80053                 _houseID = null;
80054
80055                 var msec = transitionTime(house, context.map().center());
80056                 if (msec) { reveal(null, null, { duration: 0 }); }
80057                 context.map().centerZoomEase(house, 19, msec);
80058
80059                 timeout(function() {
80060                     var tooltip = reveal('button.add-area',
80061                         helpString('intro.buildings.add_building'));
80062
80063                     tooltip.selectAll('.popover-inner')
80064                         .insert('svg', 'span')
80065                         .attr('class', 'tooltip-illustration')
80066                         .append('use')
80067                         .attr('xlink:href', '#iD-graphic-buildings');
80068
80069                     context.on('enter.intro', function(mode) {
80070                         if (mode.id !== 'add-area') { return; }
80071                         continueTo(startHouse);
80072                     });
80073                 }, msec + 100);
80074
80075                 function continueTo(nextStep) {
80076                     context.on('enter.intro', null);
80077                     nextStep();
80078                 }
80079             }
80080
80081
80082             function startHouse() {
80083                 if (context.mode().id !== 'add-area') {
80084                     return continueTo(addHouse);
80085                 }
80086
80087                 _houseID = null;
80088                 context.map().zoomEase(20, 500);
80089
80090                 timeout(function() {
80091                     var startString = helpString('intro.buildings.start_building') +
80092                         helpString('intro.buildings.building_corner_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
80093                     revealHouse(house, startString);
80094
80095                     context.map().on('move.intro drawn.intro', function() {
80096                         revealHouse(house, startString, { duration: 0 });
80097                     });
80098
80099                     context.on('enter.intro', function(mode) {
80100                         if (mode.id !== 'draw-area') { return chapter.restart(); }
80101                         continueTo(continueHouse);
80102                     });
80103
80104                 }, 550);  // after easing
80105
80106                 function continueTo(nextStep) {
80107                     context.map().on('move.intro drawn.intro', null);
80108                     context.on('enter.intro', null);
80109                     nextStep();
80110                 }
80111             }
80112
80113
80114             function continueHouse() {
80115                 if (context.mode().id !== 'draw-area') {
80116                     return continueTo(addHouse);
80117                 }
80118
80119                 _houseID = null;
80120
80121                 var continueString = helpString('intro.buildings.continue_building') + '{br}' +
80122                     helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
80123                     helpString('intro.buildings.finish_building');
80124
80125                 revealHouse(house, continueString);
80126
80127                 context.map().on('move.intro drawn.intro', function() {
80128                     revealHouse(house, continueString, { duration: 0 });
80129                 });
80130
80131                 context.on('enter.intro', function(mode) {
80132                     if (mode.id === 'draw-area') {
80133                         return;
80134                     } else if (mode.id === 'select') {
80135                         var graph = context.graph();
80136                         var way = context.entity(context.selectedIDs()[0]);
80137                         var nodes = graph.childNodes(way);
80138                         var points = utilArrayUniq(nodes)
80139                             .map(function(n) { return context.projection(n.loc); });
80140
80141                         if (isMostlySquare(points)) {
80142                             _houseID = way.id;
80143                             return continueTo(chooseCategoryBuilding);
80144                         } else {
80145                             return continueTo(retryHouse);
80146                         }
80147
80148                     } else {
80149                         return chapter.restart();
80150                     }
80151                 });
80152
80153                 function continueTo(nextStep) {
80154                     context.map().on('move.intro drawn.intro', null);
80155                     context.on('enter.intro', null);
80156                     nextStep();
80157                 }
80158             }
80159
80160
80161             function retryHouse() {
80162                 var onClick = function() { continueTo(addHouse); };
80163
80164                 revealHouse(house, helpString('intro.buildings.retry_building'),
80165                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
80166                 );
80167
80168                 context.map().on('move.intro drawn.intro', function() {
80169                     revealHouse(house, helpString('intro.buildings.retry_building'),
80170                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
80171                     );
80172                 });
80173
80174                 function continueTo(nextStep) {
80175                     context.map().on('move.intro drawn.intro', null);
80176                     nextStep();
80177                 }
80178             }
80179
80180
80181             function chooseCategoryBuilding() {
80182                 if (!_houseID || !context.hasEntity(_houseID)) {
80183                     return addHouse();
80184                 }
80185                 var ids = context.selectedIDs();
80186                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80187                     context.enter(modeSelect(context, [_houseID]));
80188                 }
80189
80190                 // disallow scrolling
80191                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80192
80193                 timeout(function() {
80194                     // reset pane, in case user somehow happened to change it..
80195                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80196
80197                     var button = context.container().select('.preset-category-building .preset-list-button');
80198
80199                     reveal(button.node(),
80200                         helpString('intro.buildings.choose_category_building', { category: buildingCatetory.name() })
80201                     );
80202
80203                     button.on('click.intro', function() {
80204                         button.on('click.intro', null);
80205                         continueTo(choosePresetHouse);
80206                     });
80207
80208                 }, 400);  // after preset list pane visible..
80209
80210
80211                 context.on('enter.intro', function(mode) {
80212                     if (!_houseID || !context.hasEntity(_houseID)) {
80213                         return continueTo(addHouse);
80214                     }
80215                     var ids = context.selectedIDs();
80216                     if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
80217                         return continueTo(chooseCategoryBuilding);
80218                     }
80219                 });
80220
80221                 function continueTo(nextStep) {
80222                     context.container().select('.inspector-wrap').on('wheel.intro', null);
80223                     context.container().select('.preset-list-button').on('click.intro', null);
80224                     context.on('enter.intro', null);
80225                     nextStep();
80226                 }
80227             }
80228
80229
80230             function choosePresetHouse() {
80231                 if (!_houseID || !context.hasEntity(_houseID)) {
80232                     return addHouse();
80233                 }
80234                 var ids = context.selectedIDs();
80235                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80236                     context.enter(modeSelect(context, [_houseID]));
80237                 }
80238
80239                 // disallow scrolling
80240                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80241
80242                 timeout(function() {
80243                     // reset pane, in case user somehow happened to change it..
80244                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80245
80246                     var button = context.container().select('.preset-building-house .preset-list-button');
80247
80248                     reveal(button.node(),
80249                         helpString('intro.buildings.choose_preset_house', { preset: housePreset.name() }),
80250                         { duration: 300 }
80251                     );
80252
80253                     button.on('click.intro', function() {
80254                         button.on('click.intro', null);
80255                         continueTo(closeEditorHouse);
80256                     });
80257
80258                 }, 400);  // after preset list pane visible..
80259
80260                 context.on('enter.intro', function(mode) {
80261                     if (!_houseID || !context.hasEntity(_houseID)) {
80262                         return continueTo(addHouse);
80263                     }
80264                     var ids = context.selectedIDs();
80265                     if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
80266                         return continueTo(chooseCategoryBuilding);
80267                     }
80268                 });
80269
80270                 function continueTo(nextStep) {
80271                     context.container().select('.inspector-wrap').on('wheel.intro', null);
80272                     context.container().select('.preset-list-button').on('click.intro', null);
80273                     context.on('enter.intro', null);
80274                     nextStep();
80275                 }
80276             }
80277
80278
80279             function closeEditorHouse() {
80280                 if (!_houseID || !context.hasEntity(_houseID)) {
80281                     return addHouse();
80282                 }
80283                 var ids = context.selectedIDs();
80284                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80285                     context.enter(modeSelect(context, [_houseID]));
80286                 }
80287
80288                 context.history().checkpoint('hasHouse');
80289
80290                 context.on('exit.intro', function() {
80291                     continueTo(rightClickHouse);
80292                 });
80293
80294                 timeout(function() {
80295                     reveal('.entity-editor-pane',
80296                         helpString('intro.buildings.close', { button: icon('#iD-icon-close', 'pre-text') })
80297                     );
80298                 }, 500);
80299
80300                 function continueTo(nextStep) {
80301                     context.on('exit.intro', null);
80302                     nextStep();
80303                 }
80304             }
80305
80306
80307             function rightClickHouse() {
80308                 if (!_houseID) { return chapter.restart(); }
80309
80310                 context.enter(modeBrowse(context));
80311                 context.history().reset('hasHouse');
80312                 var zoom = context.map().zoom();
80313                 if (zoom < 20) {
80314                     zoom = 20;
80315                 }
80316                 context.map().centerZoomEase(house, zoom, 500);
80317
80318                 context.on('enter.intro', function(mode) {
80319                     if (mode.id !== 'select') { return; }
80320                     var ids = context.selectedIDs();
80321                     if (ids.length !== 1 || ids[0] !== _houseID) { return; }
80322
80323                     timeout(function() {
80324                         var node = selectMenuItem(context, 'orthogonalize').node();
80325                         if (!node) { return; }
80326                         continueTo(clickSquare);
80327                     }, 50);  // after menu visible
80328                 });
80329
80330                 context.map().on('move.intro drawn.intro', function() {
80331                     var rightclickString = helpString('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_building' : 'edit_menu_building_touch'));
80332                     revealHouse(house, rightclickString, { duration: 0 });
80333                 });
80334
80335                 context.history().on('change.intro', function() {
80336                     continueTo(rightClickHouse);
80337                 });
80338
80339                 function continueTo(nextStep) {
80340                     context.on('enter.intro', null);
80341                     context.map().on('move.intro drawn.intro', null);
80342                     context.history().on('change.intro', null);
80343                     nextStep();
80344                 }
80345             }
80346
80347
80348             function clickSquare() {
80349                 if (!_houseID) { return chapter.restart(); }
80350                 var entity = context.hasEntity(_houseID);
80351                 if (!entity) { return continueTo(rightClickHouse); }
80352
80353                 var node = selectMenuItem(context, 'orthogonalize').node();
80354                 if (!node) { return continueTo(rightClickHouse); }
80355
80356                 var wasChanged = false;
80357
80358                 reveal('.edit-menu',
80359                     helpString('intro.buildings.square_building'),
80360                     { padding: 50 }
80361                 );
80362
80363                 context.on('enter.intro', function(mode) {
80364                     if (mode.id === 'browse') {
80365                         continueTo(rightClickHouse);
80366                     } else if (mode.id === 'move' || mode.id === 'rotate') {
80367                         continueTo(retryClickSquare);
80368                     }
80369                 });
80370
80371                 context.map().on('move.intro', function() {
80372                     var node = selectMenuItem(context, 'orthogonalize').node();
80373                     if (!wasChanged && !node) { return continueTo(rightClickHouse); }
80374
80375                     reveal('.edit-menu',
80376                         helpString('intro.buildings.square_building'),
80377                         { duration: 0, padding: 50 }
80378                     );
80379                 });
80380
80381                 context.history().on('change.intro', function() {
80382                     wasChanged = true;
80383                     context.history().on('change.intro', null);
80384
80385                     // Something changed.  Wait for transition to complete and check undo annotation.
80386                     timeout(function() {
80387                         if (context.history().undoAnnotation() === _t('operations.orthogonalize.annotation.feature.single')) {
80388                             continueTo(doneSquare);
80389                         } else {
80390                             continueTo(retryClickSquare);
80391                         }
80392                     }, 500);  // after transitioned actions
80393                 });
80394
80395                 function continueTo(nextStep) {
80396                     context.on('enter.intro', null);
80397                     context.map().on('move.intro', null);
80398                     context.history().on('change.intro', null);
80399                     nextStep();
80400                 }
80401             }
80402
80403
80404             function retryClickSquare() {
80405                 context.enter(modeBrowse(context));
80406
80407                 revealHouse(house, helpString('intro.buildings.retry_square'), {
80408                     buttonText: _t('intro.ok'),
80409                     buttonCallback: function() { continueTo(rightClickHouse); }
80410                 });
80411
80412                 function continueTo(nextStep) {
80413                     nextStep();
80414                 }
80415             }
80416
80417
80418             function doneSquare() {
80419                 context.history().checkpoint('doneSquare');
80420
80421                 revealHouse(house, helpString('intro.buildings.done_square'), {
80422                     buttonText: _t('intro.ok'),
80423                     buttonCallback: function() { continueTo(addTank); }
80424                 });
80425
80426                 function continueTo(nextStep) {
80427                     nextStep();
80428                 }
80429             }
80430
80431
80432             function addTank() {
80433                 context.enter(modeBrowse(context));
80434                 context.history().reset('doneSquare');
80435                 _tankID = null;
80436
80437                 var msec = transitionTime(tank, context.map().center());
80438                 if (msec) { reveal(null, null, { duration: 0 }); }
80439                 context.map().centerZoomEase(tank, 19.5, msec);
80440
80441                 timeout(function() {
80442                     reveal('button.add-area',
80443                         helpString('intro.buildings.add_tank')
80444                     );
80445
80446                     context.on('enter.intro', function(mode) {
80447                         if (mode.id !== 'add-area') { return; }
80448                         continueTo(startTank);
80449                     });
80450                 }, msec + 100);
80451
80452                 function continueTo(nextStep) {
80453                     context.on('enter.intro', null);
80454                     nextStep();
80455                 }
80456             }
80457
80458
80459             function startTank() {
80460                 if (context.mode().id !== 'add-area') {
80461                     return continueTo(addTank);
80462                 }
80463
80464                 _tankID = null;
80465
80466                 timeout(function() {
80467                     var startString = helpString('intro.buildings.start_tank') +
80468                         helpString('intro.buildings.tank_edge_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
80469                     revealTank(tank, startString);
80470
80471                     context.map().on('move.intro drawn.intro', function() {
80472                         revealTank(tank, startString, { duration: 0 });
80473                     });
80474
80475                     context.on('enter.intro', function(mode) {
80476                         if (mode.id !== 'draw-area') { return chapter.restart(); }
80477                         continueTo(continueTank);
80478                     });
80479
80480                 }, 550);  // after easing
80481
80482                 function continueTo(nextStep) {
80483                     context.map().on('move.intro drawn.intro', null);
80484                     context.on('enter.intro', null);
80485                     nextStep();
80486                 }
80487             }
80488
80489
80490             function continueTank() {
80491                 if (context.mode().id !== 'draw-area') {
80492                     return continueTo(addTank);
80493                 }
80494
80495                 _tankID = null;
80496
80497                 var continueString = helpString('intro.buildings.continue_tank') + '{br}' +
80498                     helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
80499                     helpString('intro.buildings.finish_tank');
80500
80501                 revealTank(tank, continueString);
80502
80503                 context.map().on('move.intro drawn.intro', function() {
80504                     revealTank(tank, continueString, { duration: 0 });
80505                 });
80506
80507                 context.on('enter.intro', function(mode) {
80508                     if (mode.id === 'draw-area') {
80509                         return;
80510                     } else if (mode.id === 'select') {
80511                         _tankID = context.selectedIDs()[0];
80512                         return continueTo(searchPresetTank);
80513                     } else {
80514                         return continueTo(addTank);
80515                     }
80516                 });
80517
80518                 function continueTo(nextStep) {
80519                     context.map().on('move.intro drawn.intro', null);
80520                     context.on('enter.intro', null);
80521                     nextStep();
80522                 }
80523             }
80524
80525
80526             function searchPresetTank() {
80527                 if (!_tankID || !context.hasEntity(_tankID)) {
80528                     return addTank();
80529                 }
80530                 var ids = context.selectedIDs();
80531                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
80532                     context.enter(modeSelect(context, [_tankID]));
80533                 }
80534
80535                 // disallow scrolling
80536                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80537
80538                 timeout(function() {
80539                     // reset pane, in case user somehow happened to change it..
80540                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80541
80542                     context.container().select('.preset-search-input')
80543                         .on('keydown.intro', null)
80544                         .on('keyup.intro', checkPresetSearch);
80545
80546                     reveal('.preset-search-input',
80547                         helpString('intro.buildings.search_tank', { preset: tankPreset.name() })
80548                     );
80549                 }, 400);  // after preset list pane visible..
80550
80551                 context.on('enter.intro', function(mode) {
80552                     if (!_tankID || !context.hasEntity(_tankID)) {
80553                         return continueTo(addTank);
80554                     }
80555
80556                     var ids = context.selectedIDs();
80557                     if (mode.id !== 'select' || !ids.length || ids[0] !== _tankID) {
80558                         // keep the user's area selected..
80559                         context.enter(modeSelect(context, [_tankID]));
80560
80561                         // reset pane, in case user somehow happened to change it..
80562                         context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80563                         // disallow scrolling
80564                         context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80565
80566                         context.container().select('.preset-search-input')
80567                             .on('keydown.intro', null)
80568                             .on('keyup.intro', checkPresetSearch);
80569
80570                         reveal('.preset-search-input',
80571                             helpString('intro.buildings.search_tank', { preset: tankPreset.name() })
80572                         );
80573
80574                         context.history().on('change.intro', null);
80575                     }
80576                 });
80577
80578                 function checkPresetSearch() {
80579                     var first = context.container().select('.preset-list-item:first-child');
80580
80581                     if (first.classed('preset-man_made-storage_tank')) {
80582                         reveal(first.select('.preset-list-button').node(),
80583                             helpString('intro.buildings.choose_tank', { preset: tankPreset.name() }),
80584                             { duration: 300 }
80585                         );
80586
80587                         context.container().select('.preset-search-input')
80588                             .on('keydown.intro', eventCancel, true)
80589                             .on('keyup.intro', null);
80590
80591                         context.history().on('change.intro', function() {
80592                             continueTo(closeEditorTank);
80593                         });
80594                     }
80595                 }
80596
80597                 function continueTo(nextStep) {
80598                     context.container().select('.inspector-wrap').on('wheel.intro', null);
80599                     context.on('enter.intro', null);
80600                     context.history().on('change.intro', null);
80601                     context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80602                     nextStep();
80603                 }
80604             }
80605
80606
80607             function closeEditorTank() {
80608                 if (!_tankID || !context.hasEntity(_tankID)) {
80609                     return addTank();
80610                 }
80611                 var ids = context.selectedIDs();
80612                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
80613                     context.enter(modeSelect(context, [_tankID]));
80614                 }
80615
80616                 context.history().checkpoint('hasTank');
80617
80618                 context.on('exit.intro', function() {
80619                     continueTo(rightClickTank);
80620                 });
80621
80622                 timeout(function() {
80623                     reveal('.entity-editor-pane',
80624                         helpString('intro.buildings.close', { button: icon('#iD-icon-close', 'pre-text') })
80625                     );
80626                 }, 500);
80627
80628                 function continueTo(nextStep) {
80629                     context.on('exit.intro', null);
80630                     nextStep();
80631                 }
80632             }
80633
80634
80635             function rightClickTank() {
80636                 if (!_tankID) { return continueTo(addTank); }
80637
80638                 context.enter(modeBrowse(context));
80639                 context.history().reset('hasTank');
80640                 context.map().centerEase(tank, 500);
80641
80642                 timeout(function() {
80643                     context.on('enter.intro', function(mode) {
80644                         if (mode.id !== 'select') { return; }
80645                         var ids = context.selectedIDs();
80646                         if (ids.length !== 1 || ids[0] !== _tankID) { return; }
80647
80648                         timeout(function() {
80649                             var node = selectMenuItem(context, 'circularize').node();
80650                             if (!node) { return; }
80651                             continueTo(clickCircle);
80652                         }, 50);  // after menu visible
80653                     });
80654
80655                     var rightclickString = helpString('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_tank' : 'edit_menu_tank_touch'));
80656
80657                     revealTank(tank, rightclickString);
80658
80659                     context.map().on('move.intro drawn.intro', function() {
80660                         revealTank(tank, rightclickString, { duration: 0 });
80661                     });
80662
80663                     context.history().on('change.intro', function() {
80664                         continueTo(rightClickTank);
80665                     });
80666
80667                 }, 600);
80668
80669                 function continueTo(nextStep) {
80670                     context.on('enter.intro', null);
80671                     context.map().on('move.intro drawn.intro', null);
80672                     context.history().on('change.intro', null);
80673                     nextStep();
80674                 }
80675             }
80676
80677
80678             function clickCircle() {
80679                 if (!_tankID) { return chapter.restart(); }
80680                 var entity = context.hasEntity(_tankID);
80681                 if (!entity) { return continueTo(rightClickTank); }
80682
80683                 var node = selectMenuItem(context, 'circularize').node();
80684                 if (!node) { return continueTo(rightClickTank); }
80685
80686                 var wasChanged = false;
80687
80688                 reveal('.edit-menu',
80689                     helpString('intro.buildings.circle_tank'),
80690                     { padding: 50 }
80691                 );
80692
80693                 context.on('enter.intro', function(mode) {
80694                     if (mode.id === 'browse') {
80695                         continueTo(rightClickTank);
80696                     } else if (mode.id === 'move' || mode.id === 'rotate') {
80697                         continueTo(retryClickCircle);
80698                     }
80699                 });
80700
80701                 context.map().on('move.intro', function() {
80702                     var node = selectMenuItem(context, 'circularize').node();
80703                     if (!wasChanged && !node) { return continueTo(rightClickTank); }
80704
80705                     reveal('.edit-menu',
80706                         helpString('intro.buildings.circle_tank'),
80707                         { duration: 0, padding: 50 }
80708                     );
80709                 });
80710
80711                 context.history().on('change.intro', function() {
80712                     wasChanged = true;
80713                     context.history().on('change.intro', null);
80714
80715                     // Something changed.  Wait for transition to complete and check undo annotation.
80716                     timeout(function() {
80717                         if (context.history().undoAnnotation() === _t('operations.circularize.annotation.single')) {
80718                             continueTo(play);
80719                         } else {
80720                             continueTo(retryClickCircle);
80721                         }
80722                     }, 500);  // after transitioned actions
80723                 });
80724
80725                 function continueTo(nextStep) {
80726                     context.on('enter.intro', null);
80727                     context.map().on('move.intro', null);
80728                     context.history().on('change.intro', null);
80729                     nextStep();
80730                 }
80731             }
80732
80733
80734             function retryClickCircle() {
80735                 context.enter(modeBrowse(context));
80736
80737                 revealTank(tank, helpString('intro.buildings.retry_circle'), {
80738                     buttonText: _t('intro.ok'),
80739                     buttonCallback: function() { continueTo(rightClickTank); }
80740                 });
80741
80742                 function continueTo(nextStep) {
80743                     nextStep();
80744                 }
80745             }
80746
80747
80748             function play() {
80749                 dispatch$1.call('done');
80750                 reveal('.ideditor',
80751                     helpString('intro.buildings.play', { next: _t('intro.startediting.title') }), {
80752                         tooltipBox: '.intro-nav-wrap .chapter-startEditing',
80753                         buttonText: _t('intro.ok'),
80754                         buttonCallback: function() { reveal('.ideditor'); }
80755                     }
80756                 );
80757             }
80758
80759
80760             chapter.enter = function() {
80761                 addHouse();
80762             };
80763
80764
80765             chapter.exit = function() {
80766                 timeouts.forEach(window.clearTimeout);
80767                 context.on('enter.intro exit.intro', null);
80768                 context.map().on('move.intro drawn.intro', null);
80769                 context.history().on('change.intro', null);
80770                 context.container().select('.inspector-wrap').on('wheel.intro', null);
80771                 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80772                 context.container().select('.more-fields .combobox-input').on('click.intro', null);
80773             };
80774
80775
80776             chapter.restart = function() {
80777                 chapter.exit();
80778                 chapter.enter();
80779             };
80780
80781
80782             return utilRebind(chapter, dispatch$1, 'on');
80783         }
80784
80785         function uiIntroStartEditing(context, reveal) {
80786             var dispatch$1 = dispatch('done', 'startEditing');
80787             var modalSelection = select(null);
80788
80789
80790             var chapter = {
80791                 title: 'intro.startediting.title'
80792             };
80793
80794             function showHelp() {
80795                 reveal('.map-control.help-control',
80796                     helpString('intro.startediting.help'), {
80797                         buttonText: _t('intro.ok'),
80798                         buttonCallback: function() { shortcuts(); }
80799                     }
80800                 );
80801             }
80802
80803             function shortcuts() {
80804                 reveal('.map-control.help-control',
80805                     helpString('intro.startediting.shortcuts'), {
80806                         buttonText: _t('intro.ok'),
80807                         buttonCallback: function() { showSave(); }
80808                     }
80809                 );
80810             }
80811
80812             function showSave() {
80813                 context.container().selectAll('.shaded').remove();  // in case user opened keyboard shortcuts
80814                 reveal('.top-toolbar button.save',
80815                     helpString('intro.startediting.save'), {
80816                         buttonText: _t('intro.ok'),
80817                         buttonCallback: function() { showStart(); }
80818                     }
80819                 );
80820             }
80821
80822             function showStart() {
80823                 context.container().selectAll('.shaded').remove();  // in case user opened keyboard shortcuts
80824
80825                 modalSelection = uiModal(context.container());
80826
80827                 modalSelection.select('.modal')
80828                     .attr('class', 'modal-splash modal');
80829
80830                 modalSelection.selectAll('.close').remove();
80831
80832                 var startbutton = modalSelection.select('.content')
80833                     .attr('class', 'fillL')
80834                     .append('button')
80835                         .attr('class', 'modal-section huge-modal-button')
80836                         .on('click', function() {
80837                             modalSelection.remove();
80838                         });
80839
80840                     startbutton
80841                         .append('svg')
80842                         .attr('class', 'illustration')
80843                         .append('use')
80844                         .attr('xlink:href', '#iD-logo-walkthrough');
80845
80846                     startbutton
80847                         .append('h2')
80848                         .text(_t('intro.startediting.start'));
80849
80850                 dispatch$1.call('startEditing');
80851             }
80852
80853
80854             chapter.enter = function() {
80855                 showHelp();
80856             };
80857
80858
80859             chapter.exit = function() {
80860                 modalSelection.remove();
80861                 context.container().selectAll('.shaded').remove();  // in case user opened keyboard shortcuts
80862             };
80863
80864
80865             return utilRebind(chapter, dispatch$1, 'on');
80866         }
80867
80868         var chapterUi = {
80869           welcome: uiIntroWelcome,
80870           navigation: uiIntroNavigation,
80871           point: uiIntroPoint,
80872           area: uiIntroArea,
80873           line: uiIntroLine,
80874           building: uiIntroBuilding,
80875           startEditing: uiIntroStartEditing
80876         };
80877
80878         var chapterFlow = [
80879           'welcome',
80880           'navigation',
80881           'point',
80882           'area',
80883           'line',
80884           'building',
80885           'startEditing'
80886         ];
80887
80888
80889         function uiIntro(context) {
80890           var INTRO_IMAGERY = 'EsriWorldImageryClarity';
80891           var _introGraph = {};
80892           var _currChapter;
80893
80894
80895           function intro(selection) {
80896             _mainFileFetcher.get('intro_graph')
80897               .then(function (dataIntroGraph) {
80898                 // create entities for intro graph and localize names
80899                 for (var id in dataIntroGraph) {
80900                   if (!_introGraph[id]) {
80901                     _introGraph[id] = osmEntity(localize(dataIntroGraph[id]));
80902                   }
80903                 }
80904                 selection.call(startIntro);
80905               })
80906               .catch(function() { /* ignore */ });
80907           }
80908
80909
80910           function startIntro(selection) {
80911             context.enter(modeBrowse(context));
80912
80913             // Save current map state
80914             var osm = context.connection();
80915             var history = context.history().toJSON();
80916             var hash = window.location.hash;
80917             var center = context.map().center();
80918             var zoom = context.map().zoom();
80919             var background = context.background().baseLayerSource();
80920             var overlays = context.background().overlayLayerSources();
80921             var opacity = context.container().selectAll('.main-map .layer-background').style('opacity');
80922             var caches = osm && osm.caches();
80923             var baseEntities = context.history().graph().base().entities;
80924
80925             // Show sidebar and disable the sidebar resizing button
80926             // (this needs to be before `context.inIntro(true)`)
80927             context.ui().sidebar.expand();
80928             context.container().selectAll('button.sidebar-toggle').classed('disabled', true);
80929
80930             // Block saving
80931             context.inIntro(true);
80932
80933             // Load semi-real data used in intro
80934             if (osm) { osm.toggle(false).reset(); }
80935             context.history().reset();
80936             context.history().merge(Object.values(coreGraph().load(_introGraph).entities));
80937             context.history().checkpoint('initial');
80938
80939             // Setup imagery
80940             var imagery = context.background().findSource(INTRO_IMAGERY);
80941             if (imagery) {
80942               context.background().baseLayerSource(imagery);
80943             } else {
80944               context.background().bing();
80945             }
80946             overlays.forEach(function (d) { return context.background().toggleOverlayLayer(d); });
80947
80948             // Setup data layers (only OSM)
80949             var layers = context.layers();
80950             layers.all().forEach(function (item) {
80951               // if the layer has the function `enabled`
80952               if (typeof item.layer.enabled === 'function') {
80953                 item.layer.enabled(item.id === 'osm');
80954               }
80955             });
80956
80957
80958             context.container().selectAll('.main-map .layer-background').style('opacity', 1);
80959
80960             var curtain = uiCurtain(context.container().node());
80961             selection.call(curtain);
80962
80963             // Store that the user started the walkthrough..
80964             corePreferences('walkthrough_started', 'yes');
80965
80966             // Restore previous walkthrough progress..
80967             var storedProgress = corePreferences('walkthrough_progress') || '';
80968             var progress = storedProgress.split(';').filter(Boolean);
80969
80970             var chapters = chapterFlow.map(function (chapter, i) {
80971               var s = chapterUi[chapter](context, curtain.reveal)
80972                 .on('done', function () {
80973
80974                   buttons
80975                     .filter(function (d) { return d.title === s.title; })
80976                     .classed('finished', true);
80977
80978                   if (i < chapterFlow.length - 1) {
80979                     var next = chapterFlow[i + 1];
80980                     context.container().select(("button.chapter-" + next))
80981                       .classed('next', true);
80982                   }
80983
80984                   // Store walkthrough progress..
80985                   progress.push(chapter);
80986                   corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';'));
80987                 });
80988               return s;
80989             });
80990
80991             chapters[chapters.length - 1].on('startEditing', function () {
80992               // Store walkthrough progress..
80993               progress.push('startEditing');
80994               corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';'));
80995
80996               // Store if walkthrough is completed..
80997               var incomplete = utilArrayDifference(chapterFlow, progress);
80998               if (!incomplete.length) {
80999                 corePreferences('walkthrough_completed', 'yes');
81000               }
81001
81002               curtain.remove();
81003               navwrap.remove();
81004               context.container().selectAll('.main-map .layer-background').style('opacity', opacity);
81005               context.container().selectAll('button.sidebar-toggle').classed('disabled', false);
81006               if (osm) { osm.toggle(true).reset().caches(caches); }
81007               context.history().reset().merge(Object.values(baseEntities));
81008               context.background().baseLayerSource(background);
81009               overlays.forEach(function (d) { return context.background().toggleOverlayLayer(d); });
81010               if (history) { context.history().fromJSON(history, false); }
81011               context.map().centerZoom(center, zoom);
81012               window.location.replace(hash);
81013               context.inIntro(false);
81014             });
81015
81016             var navwrap = selection
81017               .append('div')
81018               .attr('class', 'intro-nav-wrap fillD');
81019
81020             navwrap
81021               .append('svg')
81022               .attr('class', 'intro-nav-wrap-logo')
81023               .append('use')
81024               .attr('xlink:href', '#iD-logo-walkthrough');
81025
81026             var buttonwrap = navwrap
81027               .append('div')
81028               .attr('class', 'joined')
81029               .selectAll('button.chapter');
81030
81031             var buttons = buttonwrap
81032               .data(chapters)
81033               .enter()
81034               .append('button')
81035               .attr('class', function (d, i) { return ("chapter chapter-" + (chapterFlow[i])); })
81036               .on('click', enterChapter);
81037
81038             buttons
81039               .append('span')
81040               .text(function (d) { return _t(d.title); });
81041
81042             buttons
81043               .append('span')
81044               .attr('class', 'status')
81045               .call(svgIcon((_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'));
81046
81047             enterChapter(chapters[0]);
81048
81049
81050             function enterChapter(newChapter) {
81051               if (_currChapter) { _currChapter.exit(); }
81052               context.enter(modeBrowse(context));
81053
81054               _currChapter = newChapter;
81055               _currChapter.enter();
81056
81057               buttons
81058                 .classed('next', false)
81059                 .classed('active', function (d) { return d.title === _currChapter.title; });
81060             }
81061           }
81062
81063
81064           return intro;
81065         }
81066
81067         function uiIssuesInfo(context) {
81068
81069             var warningsItem = {
81070                 id: 'warnings',
81071                 count: 0,
81072                 iconID: 'iD-icon-alert',
81073                 descriptionID: 'issues.warnings_and_errors'
81074             };
81075
81076             var resolvedItem = {
81077                 id: 'resolved',
81078                 count: 0,
81079                 iconID: 'iD-icon-apply',
81080                 descriptionID: 'issues.user_resolved_issues'
81081             };
81082
81083             function update(selection) {
81084
81085                 var shownItems = [];
81086
81087                 var liveIssues = context.validator().getIssues({
81088                     what: corePreferences('validate-what') || 'edited',
81089                     where: corePreferences('validate-where') || 'all'
81090                 });
81091                 if (liveIssues.length) {
81092                     warningsItem.count = liveIssues.length;
81093                     shownItems.push(warningsItem);
81094                 }
81095
81096                 if (corePreferences('validate-what') === 'all') {
81097                     var resolvedIssues = context.validator().getResolvedIssues();
81098                     if (resolvedIssues.length) {
81099                         resolvedItem.count = resolvedIssues.length;
81100                         shownItems.push(resolvedItem);
81101                     }
81102                 }
81103
81104                 var chips = selection.selectAll('.chip')
81105                     .data(shownItems, function(d) {
81106                         return d.id;
81107                     });
81108
81109                 chips.exit().remove();
81110
81111                 var enter = chips.enter()
81112                     .append('a')
81113                     .attr('class', function(d) {
81114                         return 'chip ' + d.id + '-count';
81115                     })
81116                     .attr('href', '#')
81117                     .attr('tabindex', -1)
81118                     .each(function(d) {
81119
81120                         var chipSelection = select(this);
81121
81122                         var tooltipBehavior = uiTooltip()
81123                             .placement('top')
81124                             .title(_t(d.descriptionID));
81125
81126                         chipSelection
81127                             .call(tooltipBehavior)
81128                             .on('click', function() {
81129                                 event.preventDefault();
81130
81131                                 tooltipBehavior.hide(select(this));
81132                                 // open the Issues pane
81133                                 context.ui().togglePanes(context.container().select('.map-panes .issues-pane'));
81134                             });
81135
81136                         chipSelection.call(svgIcon('#' + d.iconID));
81137
81138                     });
81139
81140                 enter.append('span')
81141                     .attr('class', 'count');
81142
81143                 enter.merge(chips)
81144                     .selectAll('span.count')
81145                     .text(function(d) {
81146                         return d.count.toString();
81147                     });
81148             }
81149
81150
81151             return function(selection) {
81152                 update(selection);
81153
81154                 context.validator().on('validated.infobox', function() {
81155                     update(selection);
81156                 });
81157             };
81158         }
81159
81160         // import { utilGetDimensions } from '../util/dimensions';
81161
81162
81163         function uiMapInMap(context) {
81164
81165             function mapInMap(selection) {
81166                 var backgroundLayer = rendererTileLayer(context);
81167                 var overlayLayers = {};
81168                 var projection = geoRawMercator();
81169                 var dataLayer = svgData(projection, context).showLabels(false);
81170                 var debugLayer = svgDebug(projection, context);
81171                 var zoom = d3_zoom()
81172                     .scaleExtent([geoZoomToScale(0.5), geoZoomToScale(24)])
81173                     .on('start', zoomStarted)
81174                     .on('zoom', zoomed)
81175                     .on('end', zoomEnded);
81176
81177                 var wrap = select(null);
81178                 var tiles = select(null);
81179                 var viewport = select(null);
81180
81181                 var _isTransformed = false;
81182                 var _isHidden = true;
81183                 var _skipEvents = false;
81184                 var _gesture = null;
81185                 var _zDiff = 6;    // by default, minimap renders at (main zoom - 6)
81186                 var _dMini;        // dimensions of minimap
81187                 var _cMini;        // center pixel of minimap
81188                 var _tStart;       // transform at start of gesture
81189                 var _tCurr;        // transform at most recent event
81190                 var _timeoutID;
81191
81192
81193                 function zoomStarted() {
81194                     if (_skipEvents) { return; }
81195                     _tStart = _tCurr = projection.transform();
81196                     _gesture = null;
81197                 }
81198
81199
81200                 function zoomed() {
81201                     if (_skipEvents) { return; }
81202
81203                     var x = event.transform.x;
81204                     var y = event.transform.y;
81205                     var k = event.transform.k;
81206                     var isZooming = (k !== _tStart.k);
81207                     var isPanning = (x !== _tStart.x || y !== _tStart.y);
81208
81209                     if (!isZooming && !isPanning) {
81210                         return;  // no change
81211                     }
81212
81213                     // lock in either zooming or panning, don't allow both in minimap.
81214                     if (!_gesture) {
81215                         _gesture = isZooming ? 'zoom' : 'pan';
81216                     }
81217
81218                     var tMini = projection.transform();
81219                     var tX, tY, scale;
81220
81221                     if (_gesture === 'zoom') {
81222                         scale = k / tMini.k;
81223                         tX = (_cMini[0] / scale - _cMini[0]) * scale;
81224                         tY = (_cMini[1] / scale - _cMini[1]) * scale;
81225                     } else {
81226                         k = tMini.k;
81227                         scale = 1;
81228                         tX = x - tMini.x;
81229                         tY = y - tMini.y;
81230                     }
81231
81232                     utilSetTransform(tiles, tX, tY, scale);
81233                     utilSetTransform(viewport, 0, 0, scale);
81234                     _isTransformed = true;
81235                     _tCurr = identity$2.translate(x, y).scale(k);
81236
81237                     var zMain = geoScaleToZoom(context.projection.scale());
81238                     var zMini = geoScaleToZoom(k);
81239
81240                     _zDiff = zMain - zMini;
81241
81242                     queueRedraw();
81243                 }
81244
81245
81246                 function zoomEnded() {
81247                     if (_skipEvents) { return; }
81248                     if (_gesture !== 'pan') { return; }
81249
81250                     updateProjection();
81251                     _gesture = null;
81252                     context.map().center(projection.invert(_cMini));   // recenter main map..
81253                 }
81254
81255
81256                 function updateProjection() {
81257                     var loc = context.map().center();
81258                     var tMain = context.projection.transform();
81259                     var zMain = geoScaleToZoom(tMain.k);
81260                     var zMini = Math.max(zMain - _zDiff, 0.5);
81261                     var kMini = geoZoomToScale(zMini);
81262
81263                     projection
81264                         .translate([tMain.x, tMain.y])
81265                         .scale(kMini);
81266
81267                     var point = projection(loc);
81268                     var mouse = (_gesture === 'pan') ? geoVecSubtract([_tCurr.x, _tCurr.y], [_tStart.x, _tStart.y]) : [0, 0];
81269                     var xMini = _cMini[0] - point[0] + tMain.x + mouse[0];
81270                     var yMini = _cMini[1] - point[1] + tMain.y + mouse[1];
81271
81272                     projection
81273                         .translate([xMini, yMini])
81274                         .clipExtent([[0, 0], _dMini]);
81275
81276                     _tCurr = projection.transform();
81277
81278                     if (_isTransformed) {
81279                         utilSetTransform(tiles, 0, 0);
81280                         utilSetTransform(viewport, 0, 0);
81281                         _isTransformed = false;
81282                     }
81283
81284                     zoom
81285                         .scaleExtent([geoZoomToScale(0.5), geoZoomToScale(zMain - 3)]);
81286
81287                     _skipEvents = true;
81288                     wrap.call(zoom.transform, _tCurr);
81289                     _skipEvents = false;
81290                 }
81291
81292
81293                 function redraw() {
81294                     clearTimeout(_timeoutID);
81295                     if (_isHidden) { return; }
81296
81297                     updateProjection();
81298                     var zMini = geoScaleToZoom(projection.scale());
81299
81300                     // setup tile container
81301                     tiles = wrap
81302                         .selectAll('.map-in-map-tiles')
81303                         .data([0]);
81304
81305                     tiles = tiles.enter()
81306                         .append('div')
81307                         .attr('class', 'map-in-map-tiles')
81308                         .merge(tiles);
81309
81310                     // redraw background
81311                     backgroundLayer
81312                         .source(context.background().baseLayerSource())
81313                         .projection(projection)
81314                         .dimensions(_dMini);
81315
81316                     var background = tiles
81317                         .selectAll('.map-in-map-background')
81318                         .data([0]);
81319
81320                     background.enter()
81321                         .append('div')
81322                         .attr('class', 'map-in-map-background')
81323                         .merge(background)
81324                         .call(backgroundLayer);
81325
81326
81327                     // redraw overlay
81328                     var overlaySources = context.background().overlayLayerSources();
81329                     var activeOverlayLayers = [];
81330                     for (var i = 0; i < overlaySources.length; i++) {
81331                         if (overlaySources[i].validZoom(zMini)) {
81332                             if (!overlayLayers[i]) { overlayLayers[i] = rendererTileLayer(context); }
81333                             activeOverlayLayers.push(overlayLayers[i]
81334                                 .source(overlaySources[i])
81335                                 .projection(projection)
81336                                 .dimensions(_dMini));
81337                         }
81338                     }
81339
81340                     var overlay = tiles
81341                         .selectAll('.map-in-map-overlay')
81342                         .data([0]);
81343
81344                     overlay = overlay.enter()
81345                         .append('div')
81346                         .attr('class', 'map-in-map-overlay')
81347                         .merge(overlay);
81348
81349
81350                     var overlays = overlay
81351                         .selectAll('div')
81352                         .data(activeOverlayLayers, function(d) { return d.source().name(); });
81353
81354                     overlays.exit()
81355                         .remove();
81356
81357                     overlays = overlays.enter()
81358                         .append('div')
81359                         .merge(overlays)
81360                         .each(function(layer) { select(this).call(layer); });
81361
81362
81363                     var dataLayers = tiles
81364                         .selectAll('.map-in-map-data')
81365                         .data([0]);
81366
81367                     dataLayers.exit()
81368                         .remove();
81369
81370                     dataLayers = dataLayers.enter()
81371                         .append('svg')
81372                         .attr('class', 'map-in-map-data')
81373                         .merge(dataLayers)
81374                         .call(dataLayer)
81375                         .call(debugLayer);
81376
81377
81378                     // redraw viewport bounding box
81379                     if (_gesture !== 'pan') {
81380                         var getPath = d3_geoPath(projection);
81381                         var bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] };
81382
81383                         viewport = wrap.selectAll('.map-in-map-viewport')
81384                             .data([0]);
81385
81386                         viewport = viewport.enter()
81387                             .append('svg')
81388                             .attr('class', 'map-in-map-viewport')
81389                             .merge(viewport);
81390
81391
81392                         var path = viewport.selectAll('.map-in-map-bbox')
81393                             .data([bbox]);
81394
81395                         path.enter()
81396                             .append('path')
81397                             .attr('class', 'map-in-map-bbox')
81398                             .merge(path)
81399                             .attr('d', getPath)
81400                             .classed('thick', function(d) { return getPath.area(d) < 30; });
81401                     }
81402                 }
81403
81404
81405                 function queueRedraw() {
81406                     clearTimeout(_timeoutID);
81407                     _timeoutID = setTimeout(function() { redraw(); }, 750);
81408                 }
81409
81410
81411                 function toggle() {
81412                     if (event) { event.preventDefault(); }
81413
81414                     _isHidden = !_isHidden;
81415
81416                     context.container().select('.minimap-toggle-item')
81417                         .classed('active', !_isHidden)
81418                         .select('input')
81419                         .property('checked', !_isHidden);
81420
81421                     if (_isHidden) {
81422                         wrap
81423                             .style('display', 'block')
81424                             .style('opacity', '1')
81425                             .transition()
81426                             .duration(200)
81427                             .style('opacity', '0')
81428                             .on('end', function() {
81429                                 selection.selectAll('.map-in-map')
81430                                     .style('display', 'none');
81431                             });
81432                     } else {
81433                         wrap
81434                             .style('display', 'block')
81435                             .style('opacity', '0')
81436                             .transition()
81437                             .duration(200)
81438                             .style('opacity', '1')
81439                             .on('end', function() {
81440                                 redraw();
81441                             });
81442                     }
81443                 }
81444
81445
81446                 uiMapInMap.toggle = toggle;
81447
81448                 wrap = selection.selectAll('.map-in-map')
81449                     .data([0]);
81450
81451                 wrap = wrap.enter()
81452                     .append('div')
81453                     .attr('class', 'map-in-map')
81454                     .style('display', (_isHidden ? 'none' : 'block'))
81455                     .call(zoom)
81456                     .on('dblclick.zoom', null)
81457                     .merge(wrap);
81458
81459                 // reflow warning: Hardcode dimensions - currently can't resize it anyway..
81460                 _dMini = [200,150]; //utilGetDimensions(wrap);
81461                 _cMini = geoVecScale(_dMini, 0.5);
81462
81463                 context.map()
81464                     .on('drawn.map-in-map', function(drawn) {
81465                         if (drawn.full === true) {
81466                             redraw();
81467                         }
81468                     });
81469
81470                 redraw();
81471
81472                 context.keybinding()
81473                     .on(_t('background.minimap.key'), toggle);
81474             }
81475
81476             return mapInMap;
81477         }
81478
81479         function uiNotice(context) {
81480
81481             return function(selection) {
81482                 var div = selection
81483                     .append('div')
81484                     .attr('class', 'notice');
81485
81486                 var button = div
81487                     .append('button')
81488                     .attr('class', 'zoom-to notice fillD')
81489                     .on('click', function() {
81490                         context.map().zoomEase(context.minEditableZoom());
81491                     })
81492                     .on('wheel', function() {   // let wheel events pass through #4482
81493                         var e2 = new WheelEvent(event.type, event);
81494                         context.surface().node().dispatchEvent(e2);
81495                     });
81496
81497                 button
81498                     .call(svgIcon('#iD-icon-plus', 'pre-text'))
81499                     .append('span')
81500                     .attr('class', 'label')
81501                     .text(_t('zoom_in_edit'));
81502
81503
81504                 function disableTooHigh() {
81505                     var canEdit = context.map().zoom() >= context.minEditableZoom();
81506                     div.style('display', canEdit ? 'none' : 'block');
81507                 }
81508
81509                 context.map()
81510                     .on('move.notice', debounce(disableTooHigh, 500));
81511
81512                 disableTooHigh();
81513             };
81514         }
81515
81516         function uiPhotoviewer(context) {
81517
81518             var dispatch$1 = dispatch('resize');
81519
81520             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
81521
81522             function photoviewer(selection) {
81523                 selection
81524                     .append('button')
81525                     .attr('class', 'thumb-hide')
81526                     .on('click', function () {
81527                         if (services.streetside) { services.streetside.hideViewer(context); }
81528                         if (services.mapillary) { services.mapillary.hideViewer(context); }
81529                         if (services.openstreetcam) { services.openstreetcam.hideViewer(context); }
81530                     })
81531                     .append('div')
81532                     .call(svgIcon('#iD-icon-close'));
81533
81534                 function preventDefault() {
81535                     event.preventDefault();
81536                 }
81537
81538                 selection
81539                     .append('button')
81540                     .attr('class', 'resize-handle-xy')
81541                     .on('touchstart touchdown touchend', preventDefault)
81542                     .on(
81543                         _pointerPrefix + 'down',
81544                         buildResizeListener(selection, 'resize', dispatch$1, { resizeOnX: true, resizeOnY: true })
81545                     );
81546
81547                 selection
81548                     .append('button')
81549                     .attr('class', 'resize-handle-x')
81550                     .on('touchstart touchdown touchend', preventDefault)
81551                     .on(
81552                         _pointerPrefix + 'down',
81553                         buildResizeListener(selection, 'resize', dispatch$1, { resizeOnX: true })
81554                     );
81555
81556                 selection
81557                     .append('button')
81558                     .attr('class', 'resize-handle-y')
81559                     .on('touchstart touchdown touchend', preventDefault)
81560                     .on(
81561                         _pointerPrefix + 'down',
81562                         buildResizeListener(selection, 'resize', dispatch$1, { resizeOnY: true })
81563                     );
81564
81565                 services.streetside.loadViewer(context);
81566                 services.mapillary.loadViewer(context);
81567                 services.openstreetcam.loadViewer(context);
81568
81569                 function buildResizeListener(target, eventName, dispatch, options) {
81570
81571                     var resizeOnX = !!options.resizeOnX;
81572                     var resizeOnY = !!options.resizeOnY;
81573                     var minHeight = options.minHeight || 240;
81574                     var minWidth = options.minWidth || 320;
81575                     var pointerId;
81576                     var startX;
81577                     var startY;
81578                     var startWidth;
81579                     var startHeight;
81580
81581                     function startResize() {
81582                         if (pointerId !== (event.pointerId || 'mouse')) { return; }
81583
81584                         event.preventDefault();
81585                         event.stopPropagation();
81586
81587                         var mapSize = context.map().dimensions();
81588
81589                         if (resizeOnX) {
81590                             var maxWidth = mapSize[0];
81591                             var newWidth = clamp((startWidth + event.clientX - startX), minWidth, maxWidth);
81592                             target.style('width', newWidth + 'px');
81593                         }
81594
81595                         if (resizeOnY) {
81596                             var maxHeight = mapSize[1] - 90;  // preserve space at top/bottom of map
81597                             var newHeight = clamp((startHeight + startY - event.clientY), minHeight, maxHeight);
81598                             target.style('height', newHeight + 'px');
81599                         }
81600
81601                         dispatch.call(eventName, target, utilGetDimensions(target, true));
81602                     }
81603
81604                     function clamp(num, min, max) {
81605                         return Math.max(min, Math.min(num, max));
81606                     }
81607
81608                     function stopResize() {
81609                         if (pointerId !== (event.pointerId || 'mouse')) { return; }
81610
81611                         event.preventDefault();
81612                         event.stopPropagation();
81613
81614                         // remove all the listeners we added
81615                         select(window)
81616                             .on('.' + eventName, null);
81617                     }
81618
81619                     return function initResize() {
81620                         event.preventDefault();
81621                         event.stopPropagation();
81622
81623                         pointerId = event.pointerId || 'mouse';
81624
81625                         startX = event.clientX;
81626                         startY = event.clientY;
81627                         var targetRect = target.node().getBoundingClientRect();
81628                         startWidth = targetRect.width;
81629                         startHeight = targetRect.height;
81630
81631                         select(window)
81632                             .on(_pointerPrefix + 'move.' + eventName, startResize, false)
81633                             .on(_pointerPrefix + 'up.' + eventName, stopResize, false);
81634
81635                         if (_pointerPrefix === 'pointer') {
81636                             select(window)
81637                                 .on('pointercancel.' + eventName, stopResize, false);
81638                         }
81639                     };
81640                 }
81641             }
81642
81643             photoviewer.onMapResize = function() {
81644                 var photoviewer = context.container().select('.photoviewer');
81645                 var content = context.container().select('.main-content');
81646                 var mapDimensions = utilGetDimensions(content, true);
81647                 // shrink photo viewer if it is too big
81648                 // (-90 preserves space at top and bottom of map used by menus)
81649                 var photoDimensions = utilGetDimensions(photoviewer, true);
81650                 if (photoDimensions[0] > mapDimensions[0] || photoDimensions[1] > (mapDimensions[1] - 90)) {
81651                     var setPhotoDimensions = [
81652                         Math.min(photoDimensions[0], mapDimensions[0]),
81653                         Math.min(photoDimensions[1], mapDimensions[1] - 90) ];
81654
81655                     photoviewer
81656                         .style('width', setPhotoDimensions[0] + 'px')
81657                         .style('height', setPhotoDimensions[1] + 'px');
81658
81659                     dispatch$1.call('resize', photoviewer, setPhotoDimensions);
81660                 }
81661             };
81662
81663             return utilRebind(photoviewer, dispatch$1, 'on');
81664         }
81665
81666         function uiRestore(context) {
81667           return function(selection) {
81668             if (!context.history().hasRestorableChanges()) { return; }
81669
81670             var modalSelection = uiModal(selection, true);
81671
81672             modalSelection.select('.modal')
81673               .attr('class', 'modal fillL');
81674
81675             var introModal = modalSelection.select('.content');
81676
81677             introModal
81678               .append('div')
81679               .attr('class', 'modal-section')
81680               .append('h3')
81681               .text(_t('restore.heading'));
81682
81683             introModal
81684               .append('div')
81685               .attr('class','modal-section')
81686               .append('p')
81687               .text(_t('restore.description'));
81688
81689             var buttonWrap = introModal
81690               .append('div')
81691               .attr('class', 'modal-actions');
81692
81693             var restore = buttonWrap
81694               .append('button')
81695               .attr('class', 'restore')
81696               .on('click', function () {
81697                 context.history().restore();
81698                 modalSelection.remove();
81699               });
81700
81701             restore
81702               .append('svg')
81703               .attr('class', 'logo logo-restore')
81704               .append('use')
81705               .attr('xlink:href', '#iD-logo-restore');
81706
81707             restore
81708               .append('div')
81709               .text(_t('restore.restore'));
81710
81711             var reset = buttonWrap
81712               .append('button')
81713               .attr('class', 'reset')
81714               .on('click', function () {
81715                 context.history().clearSaved();
81716                 modalSelection.remove();
81717               });
81718
81719             reset
81720               .append('svg')
81721               .attr('class', 'logo logo-reset')
81722               .append('use')
81723               .attr('xlink:href', '#iD-logo-reset');
81724
81725             reset
81726               .append('div')
81727               .text(_t('restore.reset'));
81728
81729             restore.node().focus();
81730           };
81731         }
81732
81733         function uiScale(context) {
81734             var projection = context.projection,
81735                 isImperial = !_mainLocalizer.usesMetric(),
81736                 maxLength = 180,
81737                 tickHeight = 8;
81738
81739
81740             function scaleDefs(loc1, loc2) {
81741                 var lat = (loc2[1] + loc1[1]) / 2,
81742                     conversion = (isImperial ? 3.28084 : 1),
81743                     dist = geoLonToMeters(loc2[0] - loc1[0], lat) * conversion,
81744                     scale = { dist: 0, px: 0, text: '' },
81745                     buckets, i, val, dLon;
81746
81747                 if (isImperial) {
81748                     buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
81749                 } else {
81750                     buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
81751                 }
81752
81753                 // determine a user-friendly endpoint for the scale
81754                 for (i = 0; i < buckets.length; i++) {
81755                     val = buckets[i];
81756                     if (dist >= val) {
81757                         scale.dist = Math.floor(dist / val) * val;
81758                         break;
81759                     } else {
81760                         scale.dist = +dist.toFixed(2);
81761                     }
81762                 }
81763
81764                 dLon = geoMetersToLon(scale.dist / conversion, lat);
81765                 scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
81766
81767                 scale.text = displayLength(scale.dist / conversion, isImperial);
81768
81769                 return scale;
81770             }
81771
81772
81773             function update(selection) {
81774                 // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
81775                 var dims = context.map().dimensions(),
81776                     loc1 = projection.invert([0, dims[1]]),
81777                     loc2 = projection.invert([maxLength, dims[1]]),
81778                     scale = scaleDefs(loc1, loc2);
81779
81780                 selection.select('.scale-path')
81781                     .attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
81782
81783                 selection.select('.scale-textgroup')
81784                     .attr('transform', 'translate(' + (scale.px + 8) + ',' + tickHeight + ')');
81785
81786                 selection.select('.scale-text')
81787                     .text(scale.text);
81788             }
81789
81790
81791             return function(selection) {
81792                 function switchUnits() {
81793                     isImperial = !isImperial;
81794                     selection.call(update);
81795                 }
81796
81797                 var scalegroup = selection.append('svg')
81798                     .attr('class', 'scale')
81799                     .on('click', switchUnits)
81800                     .append('g')
81801                     .attr('transform', 'translate(10,11)');
81802
81803                 scalegroup
81804                     .append('path')
81805                     .attr('class', 'scale-path');
81806
81807                 scalegroup
81808                     .append('g')
81809                     .attr('class', 'scale-textgroup')
81810                     .append('text')
81811                     .attr('class', 'scale-text');
81812
81813                 selection.call(update);
81814
81815                 context.map().on('move.scale', function() {
81816                     update(selection);
81817                 });
81818             };
81819         }
81820
81821         function uiShortcuts(context) {
81822             var detected = utilDetect();
81823             var _activeTab = 0;
81824             var _modalSelection;
81825             var _selection = select(null);
81826
81827
81828             context.keybinding()
81829                 .on([_t('shortcuts.toggle.key'), '?'], function () {
81830                     if (context.container().selectAll('.modal-shortcuts').size()) {  // already showing
81831                         if (_modalSelection) {
81832                             _modalSelection.close();
81833                             _modalSelection = null;
81834                         }
81835                     } else {
81836                         _modalSelection = uiModal(_selection);
81837                         _modalSelection.call(shortcutsModal);
81838                     }
81839                 });
81840
81841
81842             function shortcutsModal(_modalSelection) {
81843                 _modalSelection.select('.modal')
81844                     .classed('modal-shortcuts', true);
81845
81846                 var content = _modalSelection.select('.content');
81847
81848                 content
81849                     .append('div')
81850                     .attr('class', 'modal-section')
81851                     .append('h3')
81852                     .text(_t('shortcuts.title'));
81853
81854                 _mainFileFetcher.get('shortcuts')
81855                     .then(function(data) { content.call(render, data); })
81856                     .catch(function() { /* ignore */ });
81857             }
81858
81859
81860             function render(selection, dataShortcuts) {
81861                 var wrapper = selection
81862                     .selectAll('.wrapper')
81863                     .data([0]);
81864
81865                 var wrapperEnter = wrapper
81866                     .enter()
81867                     .append('div')
81868                     .attr('class', 'wrapper modal-section');
81869
81870                 var tabsBar = wrapperEnter
81871                     .append('div')
81872                     .attr('class', 'tabs-bar');
81873
81874                 var shortcutsList = wrapperEnter
81875                     .append('div')
81876                     .attr('class', 'shortcuts-list');
81877
81878                 wrapper = wrapper.merge(wrapperEnter);
81879
81880                 var tabs = tabsBar
81881                     .selectAll('.tab')
81882                     .data(dataShortcuts);
81883
81884                 var tabsEnter = tabs
81885                     .enter()
81886                     .append('div')
81887                     .attr('class', 'tab')
81888                     .on('click', function (d, i) {
81889                         _activeTab = i;
81890                         render(selection, dataShortcuts);
81891                     });
81892
81893                 tabsEnter
81894                     .append('span')
81895                     .text(function (d) { return _t(d.text); });
81896
81897                 tabs = tabs
81898                     .merge(tabsEnter);
81899
81900                 // Update
81901                 wrapper.selectAll('.tab')
81902                     .classed('active', function (d, i) {
81903                         return i === _activeTab;
81904                     });
81905
81906
81907                 var shortcuts = shortcutsList
81908                     .selectAll('.shortcut-tab')
81909                     .data(dataShortcuts);
81910
81911                 var shortcutsEnter = shortcuts
81912                     .enter()
81913                     .append('div')
81914                     .attr('class', function(d) { return 'shortcut-tab shortcut-tab-' + d.tab; });
81915
81916                 var columnsEnter = shortcutsEnter
81917                     .selectAll('.shortcut-column')
81918                     .data(function (d) { return d.columns; })
81919                     .enter()
81920                     .append('table')
81921                     .attr('class', 'shortcut-column');
81922
81923                 var rowsEnter = columnsEnter
81924                     .selectAll('.shortcut-row')
81925                     .data(function (d) { return d.rows; })
81926                     .enter()
81927                     .append('tr')
81928                     .attr('class', 'shortcut-row');
81929
81930
81931                 var sectionRows = rowsEnter
81932                     .filter(function (d) { return !d.shortcuts; });
81933
81934                 sectionRows
81935                     .append('td');
81936
81937                 sectionRows
81938                     .append('td')
81939                     .attr('class', 'shortcut-section')
81940                     .append('h3')
81941                     .text(function (d) { return _t(d.text); });
81942
81943
81944                 var shortcutRows = rowsEnter
81945                     .filter(function (d) { return d.shortcuts; });
81946
81947                 var shortcutKeys = shortcutRows
81948                     .append('td')
81949                     .attr('class', 'shortcut-keys');
81950
81951                 var modifierKeys = shortcutKeys
81952                     .filter(function (d) { return d.modifiers; });
81953
81954                 modifierKeys
81955                     .selectAll('kbd.modifier')
81956                     .data(function (d) {
81957                         if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
81958                             return ['⌘'];
81959                         } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
81960                             return [];
81961                         } else {
81962                             return d.modifiers;
81963                         }
81964                     })
81965                     .enter()
81966                     .each(function () {
81967                         var selection = select(this);
81968
81969                         selection
81970                             .append('kbd')
81971                             .attr('class', 'modifier')
81972                             .text(function (d) { return uiCmd.display(d); });
81973
81974                         selection
81975                             .append('span')
81976                             .text('+');
81977                     });
81978
81979
81980                 shortcutKeys
81981                     .selectAll('kbd.shortcut')
81982                     .data(function (d) {
81983                         var arr = d.shortcuts;
81984                         if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
81985                             arr = ['Y'];
81986                         } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
81987                             arr = ['F11'];
81988                         }
81989
81990                         // replace translations
81991                         arr = arr.map(function(s) {
81992                             return uiCmd.display(s.indexOf('.') !== -1 ? _t(s) : s);
81993                         });
81994
81995                         return utilArrayUniq(arr).map(function(s) {
81996                             return {
81997                                 shortcut: s,
81998                                 separator: d.separator,
81999                                 suffix: d.suffix
82000                             };
82001                         });
82002                     })
82003                     .enter()
82004                     .each(function (d, i, nodes) {
82005                         var selection = select(this);
82006                         var click = d.shortcut.toLowerCase().match(/(.*).click/);
82007
82008                         if (click && click[1]) {   // replace "left_click", "right_click" with mouse icon
82009                             selection
82010                                 .call(svgIcon('#iD-walkthrough-mouse-' + click[1], 'operation'));
82011                         } else if (d.shortcut.toLowerCase() === 'long-press') {
82012                             selection
82013                                 .call(svgIcon('#iD-walkthrough-longpress', 'longpress operation'));
82014                         } else if (d.shortcut.toLowerCase() === 'tap') {
82015                             selection
82016                                 .call(svgIcon('#iD-walkthrough-tap', 'tap operation'));
82017                         } else {
82018                             selection
82019                                 .append('kbd')
82020                                 .attr('class', 'shortcut')
82021                                 .text(function (d) { return d.shortcut; });
82022                         }
82023
82024                         if (i < nodes.length - 1) {
82025                             selection
82026                                 .append('span')
82027                                 .text(d.separator || '\u00a0' + _t('shortcuts.or') + '\u00a0');
82028                         } else if (i === nodes.length - 1 && d.suffix) {
82029                             selection
82030                                 .append('span')
82031                                 .text(d.suffix);
82032                         }
82033                     });
82034
82035
82036                 shortcutKeys
82037                     .filter(function(d) { return d.gesture; })
82038                     .each(function () {
82039                         var selection = select(this);
82040
82041                         selection
82042                             .append('span')
82043                             .text('+');
82044
82045                         selection
82046                             .append('span')
82047                             .attr('class', 'gesture')
82048                             .text(function (d) { return _t(d.gesture); });
82049                     });
82050
82051
82052                 shortcutRows
82053                     .append('td')
82054                     .attr('class', 'shortcut-desc')
82055                     .text(function (d) { return d.text ? _t(d.text) : '\u00a0'; });
82056
82057
82058                 shortcuts = shortcuts
82059                     .merge(shortcutsEnter);
82060
82061                 // Update
82062                 wrapper.selectAll('.shortcut-tab')
82063                     .style('display', function (d, i) {
82064                         return i === _activeTab ? 'flex' : 'none';
82065                     });
82066             }
82067
82068
82069             return function(selection, show) {
82070                 _selection = selection;
82071                 if (show) {
82072                     _modalSelection = uiModal(selection);
82073                     _modalSelection.call(shortcutsModal);
82074                 }
82075             };
82076         }
82077
82078         var pair_1 = pair;
82079
82080
82081         function search(input, dims) {
82082           if (!dims) { dims = 'NSEW'; }
82083           if (typeof input !== 'string') { return null; }
82084
82085           input = input.toUpperCase();
82086           var regex = /^[\s\,]*([NSEW])?\s*([\-|\—|\―]?[0-9.]+)[°º˚]?\s*(?:([0-9.]+)['’′‘]\s*)?(?:([0-9.]+)(?:''|"|”|″)\s*)?([NSEW])?/;
82087
82088           var m = input.match(regex);
82089           if (!m) { return null; }  // no match
82090
82091           var matched = m[0];
82092
82093           // extract dimension.. m[1] = leading, m[5] = trailing
82094           var dim;
82095           if (m[1] && m[5]) {                 // if matched both..
82096             dim = m[1];                       // keep leading
82097             matched = matched.slice(0, -1);   // remove trailing dimension from match
82098           } else {
82099             dim = m[1] || m[5];
82100           }
82101
82102           // if unrecognized dimension
82103           if (dim && dims.indexOf(dim) === -1) { return null; }
82104
82105           // extract DMS
82106           var deg = m[2] ? parseFloat(m[2]) : 0;
82107           var min = m[3] ? parseFloat(m[3]) / 60 : 0;
82108           var sec = m[4] ? parseFloat(m[4]) / 3600 : 0;
82109           var sign = (deg < 0) ? -1 : 1;
82110           if (dim === 'S' || dim === 'W') { sign *= -1; }
82111
82112           return {
82113             val: (Math.abs(deg) + min + sec) * sign,
82114             dim: dim,
82115             matched: matched,
82116             remain: input.slice(matched.length)
82117           };
82118         }
82119
82120
82121         function pair(input, dims) {
82122           input = input.trim();
82123           var one = search(input, dims);
82124           if (!one) { return null; }
82125
82126           input = one.remain.trim();
82127           var two = search(input, dims);
82128           if (!two || two.remain) { return null; }
82129
82130           if (one.dim) {
82131             return swapdim(one.val, two.val, one.dim);
82132           } else {
82133             return [one.val, two.val];
82134           }
82135         }
82136
82137
82138         function swapdim(a, b, dim) {
82139           if (dim === 'N' || dim === 'S') { return [a, b]; }
82140           if (dim === 'W' || dim === 'E') { return [b, a]; }
82141         }
82142
82143         function uiFeatureList(context) {
82144             var _geocodeResults;
82145
82146
82147             function featureList(selection) {
82148                 var header = selection
82149                     .append('div')
82150                     .attr('class', 'header fillL cf');
82151
82152                 header
82153                     .append('h3')
82154                     .text(_t('inspector.feature_list'));
82155
82156                 var searchWrap = selection
82157                     .append('div')
82158                     .attr('class', 'search-header');
82159
82160                 var search = searchWrap
82161                     .append('input')
82162                     .attr('placeholder', _t('inspector.search'))
82163                     .attr('type', 'search')
82164                     .call(utilNoAuto)
82165                     .on('keypress', keypress)
82166                     .on('keydown', keydown)
82167                     .on('input', inputevent);
82168
82169                 searchWrap
82170                     .call(svgIcon('#iD-icon-search', 'pre-text'));
82171
82172                 var listWrap = selection
82173                     .append('div')
82174                     .attr('class', 'inspector-body');
82175
82176                 var list = listWrap
82177                     .append('div')
82178                     .attr('class', 'feature-list cf');
82179
82180                 context
82181                     .on('exit.feature-list', clearSearch);
82182                 context.map()
82183                     .on('drawn.feature-list', mapDrawn);
82184
82185                 context.keybinding()
82186                     .on(uiCmd('⌘F'), focusSearch);
82187
82188
82189                 function focusSearch() {
82190                     var mode = context.mode() && context.mode().id;
82191                     if (mode !== 'browse') { return; }
82192
82193                     event.preventDefault();
82194                     search.node().focus();
82195                 }
82196
82197
82198                 function keydown() {
82199                     if (event.keyCode === 27) {  // escape
82200                         search.node().blur();
82201                     }
82202                 }
82203
82204
82205                 function keypress() {
82206                     var q = search.property('value'),
82207                         items = list.selectAll('.feature-list-item');
82208                     if (event.keyCode === 13 && q.length && items.size()) {  // return
82209                         click(items.datum());
82210                     }
82211                 }
82212
82213
82214                 function inputevent() {
82215                     _geocodeResults = undefined;
82216                     drawList();
82217                 }
82218
82219
82220                 function clearSearch() {
82221                     search.property('value', '');
82222                     drawList();
82223                 }
82224
82225
82226                 function mapDrawn(e) {
82227                     if (e.full) {
82228                         drawList();
82229                     }
82230                 }
82231
82232
82233                 function features() {
82234                     var result = [];
82235                     var graph = context.graph();
82236                     var visibleCenter = context.map().extent().center();
82237                     var q = search.property('value').toLowerCase();
82238
82239                     if (!q) { return result; }
82240
82241                     var idMatch = q.match(/(?:^|\W)(node|way|relation|[nwr])\W?0*([1-9]\d*)(?:\W|$)/i);
82242
82243                     if (idMatch) {
82244                         var elemType = idMatch[1].charAt(0);
82245                         var elemId = idMatch[2];
82246                         result.push({
82247                             id: elemType + elemId,
82248                             geometry: elemType === 'n' ? 'point' : elemType === 'w' ? 'line' : 'relation',
82249                             type: elemType === 'n' ? _t('inspector.node') : elemType === 'w' ? _t('inspector.way') : _t('inspector.relation'),
82250                             name: elemId
82251                         });
82252                     }
82253
82254                     var locationMatch = pair_1(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
82255
82256                     if (locationMatch) {
82257                         var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
82258                         result.push({
82259                             id: -1,
82260                             geometry: 'point',
82261                             type: _t('inspector.location'),
82262                             name: dmsCoordinatePair([loc[1], loc[0]]),
82263                             location: loc
82264                         });
82265                     }
82266
82267                     var allEntities = graph.entities;
82268                     var localResults = [];
82269                     for (var id in allEntities) {
82270                         var entity = allEntities[id];
82271                         if (!entity) { continue; }
82272
82273                         var name = utilDisplayName(entity) || '';
82274                         if (name.toLowerCase().indexOf(q) < 0) { continue; }
82275
82276                         var matched = _mainPresetIndex.match(entity, graph);
82277                         var type = (matched && matched.name()) || utilDisplayType(entity.id);
82278                         var extent = entity.extent(graph);
82279                         var distance = extent ? geoSphericalDistance(visibleCenter, extent.center()) : 0;
82280
82281                         localResults.push({
82282                             id: entity.id,
82283                             entity: entity,
82284                             geometry: entity.geometry(graph),
82285                             type: type,
82286                             name: name,
82287                             distance: distance
82288                         });
82289
82290                         if (localResults.length > 100) { break; }
82291                     }
82292                     localResults = localResults.sort(function byDistance(a, b) {
82293                         return a.distance - b.distance;
82294                     });
82295                     result = result.concat(localResults);
82296
82297                     (_geocodeResults || []).forEach(function(d) {
82298                         if (d.osm_type && d.osm_id) {    // some results may be missing these - #1890
82299
82300                             // Make a temporary osmEntity so we can preset match
82301                             // and better localize the search result - #4725
82302                             var id = osmEntity.id.fromOSM(d.osm_type, d.osm_id);
82303                             var tags = {};
82304                             tags[d.class] = d.type;
82305
82306                             var attrs = { id: id, type: d.osm_type, tags: tags };
82307                             if (d.osm_type === 'way') {   // for ways, add some fake closed nodes
82308                                 attrs.nodes = ['a','a'];  // so that geometry area is possible
82309                             }
82310
82311                             var tempEntity = osmEntity(attrs);
82312                             var tempGraph = coreGraph([tempEntity]);
82313                             var matched = _mainPresetIndex.match(tempEntity, tempGraph);
82314                             var type = (matched && matched.name()) || utilDisplayType(id);
82315
82316                             result.push({
82317                                 id: tempEntity.id,
82318                                 geometry: tempEntity.geometry(tempGraph),
82319                                 type: type,
82320                                 name: d.display_name,
82321                                 extent: new geoExtent(
82322                                     [parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])],
82323                                     [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
82324                             });
82325                         }
82326                     });
82327
82328                     if (q.match(/^[0-9]+$/)) {
82329                         // if query is just a number, possibly an OSM ID without a prefix
82330                         result.push({
82331                             id: 'n' + q,
82332                             geometry: 'point',
82333                             type: _t('inspector.node'),
82334                             name: q
82335                         });
82336                         result.push({
82337                             id: 'w' + q,
82338                             geometry: 'line',
82339                             type: _t('inspector.way'),
82340                             name: q
82341                         });
82342                         result.push({
82343                             id: 'r' + q,
82344                             geometry: 'relation',
82345                             type: _t('inspector.relation'),
82346                             name: q
82347                         });
82348                     }
82349
82350                     return result;
82351                 }
82352
82353
82354                 function drawList() {
82355                     var value = search.property('value');
82356                     var results = features();
82357
82358                     list.classed('filtered', value.length);
82359
82360                     var resultsIndicator = list.selectAll('.no-results-item')
82361                         .data([0])
82362                         .enter()
82363                         .append('button')
82364                         .property('disabled', true)
82365                         .attr('class', 'no-results-item')
82366                         .call(svgIcon('#iD-icon-alert', 'pre-text'));
82367
82368                     resultsIndicator.append('span')
82369                         .attr('class', 'entity-name');
82370
82371                     list.selectAll('.no-results-item .entity-name')
82372                         .text(_t('geocoder.no_results_worldwide'));
82373
82374                     if (services.geocoder) {
82375                       list.selectAll('.geocode-item')
82376                           .data([0])
82377                           .enter()
82378                           .append('button')
82379                           .attr('class', 'geocode-item')
82380                           .on('click', geocoderSearch)
82381                           .append('div')
82382                           .attr('class', 'label')
82383                           .append('span')
82384                           .attr('class', 'entity-name')
82385                           .text(_t('geocoder.search'));
82386                     }
82387
82388                     list.selectAll('.no-results-item')
82389                         .style('display', (value.length && !results.length) ? 'block' : 'none');
82390
82391                     list.selectAll('.geocode-item')
82392                         .style('display', (value && _geocodeResults === undefined) ? 'block' : 'none');
82393
82394                     list.selectAll('.feature-list-item')
82395                         .data([-1])
82396                         .remove();
82397
82398                     var items = list.selectAll('.feature-list-item')
82399                         .data(results, function(d) { return d.id; });
82400
82401                     var enter = items.enter()
82402                         .insert('button', '.geocode-item')
82403                         .attr('class', 'feature-list-item')
82404                         .on('mouseover', mouseover)
82405                         .on('mouseout', mouseout)
82406                         .on('click', click);
82407
82408                     var label = enter
82409                         .append('div')
82410                         .attr('class', 'label');
82411
82412                     label
82413                         .each(function(d) {
82414                             select(this)
82415                                 .call(svgIcon('#iD-icon-' + d.geometry, 'pre-text'));
82416                         });
82417
82418                     label
82419                         .append('span')
82420                         .attr('class', 'entity-type')
82421                         .text(function(d) { return d.type; });
82422
82423                     label
82424                         .append('span')
82425                         .attr('class', 'entity-name')
82426                         .text(function(d) { return d.name; });
82427
82428                     enter
82429                         .style('opacity', 0)
82430                         .transition()
82431                         .style('opacity', 1);
82432
82433                     items.order();
82434
82435                     items.exit()
82436                         .remove();
82437                 }
82438
82439
82440                 function mouseover(d) {
82441                     if (d.id === -1) { return; }
82442
82443                     utilHighlightEntities([d.id], true, context);
82444                 }
82445
82446
82447                 function mouseout(d) {
82448                     if (d.id === -1) { return; }
82449
82450                     utilHighlightEntities([d.id], false, context);
82451                 }
82452
82453
82454                 function click(d) {
82455                     event.preventDefault();
82456
82457                     if (d.location) {
82458                         context.map().centerZoomEase([d.location[1], d.location[0]], 19);
82459
82460                     } else if (d.entity) {
82461                         utilHighlightEntities([d.id], false, context);
82462
82463                         context.enter(modeSelect(context, [d.entity.id]));
82464                         context.map().zoomToEase(d.entity);
82465
82466                     } else {
82467                         // download, zoom to, and select the entity with the given ID
82468                         context.zoomToEntity(d.id);
82469                     }
82470                 }
82471
82472
82473                 function geocoderSearch() {
82474                     services.geocoder.search(search.property('value'), function (err, resp) {
82475                         _geocodeResults = resp || [];
82476                         drawList();
82477                     });
82478                 }
82479             }
82480
82481
82482             return featureList;
82483         }
82484
82485         function uiSectionEntityIssues(context) {
82486
82487             var _entityIDs = [];
82488             var _issues = [];
82489             var _activeIssueID;
82490
82491             var section = uiSection('entity-issues', context)
82492                 .shouldDisplay(function() {
82493                     return _issues.length > 0;
82494                 })
82495                 .title(function() {
82496                     return _t('issues.list_title', { count: _issues.length });
82497                 })
82498                 .disclosureContent(renderDisclosureContent);
82499
82500             context.validator()
82501                 .on('validated.entity_issues', function() {
82502                     // Refresh on validated events
82503                     reloadIssues();
82504                     section.reRender();
82505                 })
82506                 .on('focusedIssue.entity_issues', function(issue) {
82507                      makeActiveIssue(issue.id);
82508                 });
82509
82510             function reloadIssues() {
82511                 _issues = context.validator().getSharedEntityIssues(_entityIDs, { includeDisabledRules: true });
82512             }
82513
82514             function makeActiveIssue(issueID) {
82515                 _activeIssueID = issueID;
82516                 section.selection().selectAll('.issue-container')
82517                     .classed('active', function(d) { return d.id === _activeIssueID; });
82518             }
82519
82520             function renderDisclosureContent(selection) {
82521
82522                 selection.classed('grouped-items-area', true);
82523
82524                 _activeIssueID = _issues.length > 0 ? _issues[0].id : null;
82525
82526                 var containers = selection.selectAll('.issue-container')
82527                     .data(_issues, function(d) { return d.id; });
82528
82529                 // Exit
82530                 containers.exit()
82531                     .remove();
82532
82533                 // Enter
82534                 var containersEnter = containers.enter()
82535                     .append('div')
82536                     .attr('class', 'issue-container');
82537
82538
82539                 var itemsEnter = containersEnter
82540                     .append('div')
82541                     .attr('class', function(d) { return 'issue severity-' + d.severity; })
82542                     .on('mouseover.highlight', function(d) {
82543                         // don't hover-highlight the selected entity
82544                         var ids = d.entityIds
82545                             .filter(function(e) { return _entityIDs.indexOf(e) === -1; });
82546
82547                         utilHighlightEntities(ids, true, context);
82548                     })
82549                     .on('mouseout.highlight', function(d) {
82550                         var ids = d.entityIds
82551                             .filter(function(e) { return _entityIDs.indexOf(e) === -1; });
82552
82553                         utilHighlightEntities(ids, false, context);
82554                     });
82555
82556                 var labelsEnter = itemsEnter
82557                     .append('div')
82558                     .attr('class', 'issue-label')
82559                     .on('click', function(d) {
82560
82561                         makeActiveIssue(d.id); // expand only the clicked item
82562
82563                         var extent = d.extent(context.graph());
82564                         if (extent) {
82565                             var setZoom = Math.max(context.map().zoom(), 19);
82566                             context.map().unobscuredCenterZoomEase(extent.center(), setZoom);
82567                         }
82568                     });
82569
82570                 var textEnter = labelsEnter
82571                     .append('span')
82572                     .attr('class', 'issue-text');
82573
82574                 textEnter
82575                     .append('span')
82576                     .attr('class', 'issue-icon')
82577                     .each(function(d) {
82578                         var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
82579                         select(this)
82580                             .call(svgIcon(iconName));
82581                     });
82582
82583                 textEnter
82584                     .append('span')
82585                     .attr('class', 'issue-message');
82586
82587
82588                 var infoButton = labelsEnter
82589                     .append('button')
82590                     .attr('class', 'issue-info-button')
82591                     .attr('title', _t('icons.information'))
82592                     .attr('tabindex', -1)
82593                     .call(svgIcon('#iD-icon-inspect'));
82594
82595                 infoButton
82596                     .on('click', function () {
82597                         event.stopPropagation();
82598                         event.preventDefault();
82599                         this.blur();    // avoid keeping focus on the button - #4641
82600
82601                         var container = select(this.parentNode.parentNode.parentNode);
82602                         var info = container.selectAll('.issue-info');
82603                         var isExpanded = info.classed('expanded');
82604
82605                         if (isExpanded) {
82606                             info
82607                                 .transition()
82608                                 .duration(200)
82609                                 .style('max-height', '0px')
82610                                 .style('opacity', '0')
82611                                 .on('end', function () {
82612                                     info.classed('expanded', false);
82613                                 });
82614                         } else {
82615                             info
82616                                 .classed('expanded', true)
82617                                 .transition()
82618                                 .duration(200)
82619                                 .style('max-height', '200px')
82620                                 .style('opacity', '1')
82621                                 .on('end', function () {
82622                                     info.style('max-height', null);
82623                                 });
82624                         }
82625                     });
82626
82627                 itemsEnter
82628                     .append('ul')
82629                     .attr('class', 'issue-fix-list');
82630
82631                 containersEnter
82632                     .append('div')
82633                     .attr('class', 'issue-info')
82634                     .style('max-height', '0')
82635                     .style('opacity', '0')
82636                     .each(function(d) {
82637                         if (typeof d.reference === 'function') {
82638                             select(this)
82639                                 .call(d.reference);
82640                         } else {
82641                             select(this)
82642                                 .text(_t('inspector.no_documentation_key'));
82643                         }
82644                     });
82645
82646
82647                 // Update
82648                 containers = containers
82649                     .merge(containersEnter)
82650                     .classed('active', function(d) { return d.id === _activeIssueID; });
82651
82652                 containers.selectAll('.issue-message')
82653                     .text(function(d) {
82654                         return d.message(context);
82655                     });
82656
82657                 // fixes
82658                 var fixLists = containers.selectAll('.issue-fix-list');
82659
82660                 var fixes = fixLists.selectAll('.issue-fix-item')
82661                     .data(function(d) { return d.fixes ? d.fixes(context) : []; }, function(fix) { return fix.id; });
82662
82663                 fixes.exit()
82664                     .remove();
82665
82666                 var fixesEnter = fixes.enter()
82667                     .append('li')
82668                     .attr('class', 'issue-fix-item')
82669                     .on('click', function(d) {
82670                         // not all fixes are actionable
82671                         if (!select(this).classed('actionable') || !d.onClick) { return; }
82672
82673                         // Don't run another fix for this issue within a second of running one
82674                         // (Necessary for "Select a feature type" fix. Most fixes should only ever run once)
82675                         if (d.issue.dateLastRanFix && new Date() - d.issue.dateLastRanFix < 1000) { return; }
82676                         d.issue.dateLastRanFix = new Date();
82677
82678                         // remove hover-highlighting
82679                         utilHighlightEntities(d.issue.entityIds.concat(d.entityIds), false, context);
82680
82681                         new Promise(function(resolve, reject) {
82682                             d.onClick(context, resolve, reject);
82683                             if (d.onClick.length <= 1) {
82684                                 // if the fix doesn't take any completion parameters then consider it resolved
82685                                 resolve();
82686                             }
82687                         })
82688                         .then(function() {
82689                             // revalidate whenever the fix has finished running successfully
82690                             context.validator().validate();
82691                         });
82692                     })
82693                     .on('mouseover.highlight', function(d) {
82694                         utilHighlightEntities(d.entityIds, true, context);
82695                     })
82696                     .on('mouseout.highlight', function(d) {
82697                         utilHighlightEntities(d.entityIds, false, context);
82698                     });
82699
82700                 fixesEnter
82701                     .append('span')
82702                     .attr('class', 'fix-icon')
82703                     .each(function(d) {
82704                         var iconName = d.icon || 'iD-icon-wrench';
82705                         if (iconName.startsWith('maki')) {
82706                             iconName += '-15';
82707                         }
82708                         select(this).call(svgIcon('#' + iconName));
82709                     });
82710
82711                 fixesEnter
82712                     .append('span')
82713                     .attr('class', 'fix-message')
82714                     .text(function(d) { return d.title; });
82715
82716                 fixesEnter.merge(fixes)
82717                     .classed('actionable', function(d) {
82718                         return d.onClick;
82719                     })
82720                     .attr('title', function(d) {
82721                         if (d.disabledReason) {
82722                             return d.disabledReason;
82723                         }
82724                         return null;
82725                     });
82726             }
82727
82728             section.entityIDs = function(val) {
82729                 if (!arguments.length) { return _entityIDs; }
82730                 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
82731                     _entityIDs = val;
82732                     _activeIssueID = null;
82733                     reloadIssues();
82734                 }
82735                 return section;
82736             };
82737
82738             return section;
82739         }
82740
82741         function uiPresetIcon() {
82742           var _preset;
82743           var _geometry;
82744           var _sizeClass = 'medium';
82745
82746
82747           function isSmall() {
82748             return _sizeClass === 'small';
82749           }
82750
82751
82752           function presetIcon(selection) {
82753             selection.each(render);
82754           }
82755
82756
82757           function getIcon(p, geom) {
82758             if (isSmall() && p.isFallback && p.isFallback())
82759               { return 'iD-icon-' + p.id; }
82760             else if (p.icon)
82761               { return p.icon; }
82762             else if (geom === 'line')
82763               { return 'iD-other-line'; }
82764             else if (geom === 'vertex')
82765               { return p.isFallback() ? '' : 'temaki-vertex'; }
82766             else if (isSmall() && geom === 'point')
82767               { return ''; }
82768             else
82769               { return 'maki-marker-stroked'; }
82770           }
82771
82772
82773           function renderPointBorder(enter) {
82774             var w = 40;
82775             var h = 40;
82776
82777             enter
82778               .append('svg')
82779               .attr('class', 'preset-icon-fill preset-icon-point-border')
82780               .attr('width', w)
82781               .attr('height', h)
82782               .attr('viewBox', ("0 0 " + w + " " + h))
82783               .append('path')
82784               .attr('transform', 'translate(11.5, 8)')
82785               .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');
82786           }
82787
82788
82789           function renderCircleFill(fillEnter) {
82790             var w = 60;
82791             var h = 60;
82792             var d = 40;
82793
82794             fillEnter
82795               .append('svg')
82796               .attr('class', 'preset-icon-fill preset-icon-fill-vertex')
82797               .attr('width', w)
82798               .attr('height', h)
82799               .attr('viewBox', ("0 0 " + w + " " + h))
82800               .append('circle')
82801               .attr('cx', w / 2)
82802               .attr('cy', h / 2)
82803               .attr('r', d / 2);
82804           }
82805
82806
82807           function renderSquareFill(fillEnter) {
82808             var d = isSmall() ? 40 : 60;
82809             var w = d;
82810             var h = d;
82811             var l = d * 2/3;
82812             var c1 = (w-l) / 2;
82813             var c2 = c1 + l;
82814
82815             fillEnter = fillEnter
82816               .append('svg')
82817               .attr('class', 'preset-icon-fill preset-icon-fill-area')
82818               .attr('width', w)
82819               .attr('height', h)
82820               .attr('viewBox', ("0 0 " + w + " " + h));
82821
82822             ['fill', 'stroke'].forEach(function (klass) {
82823               fillEnter
82824                 .append('path')
82825                 .attr('d', ("M" + c1 + " " + c1 + " L" + c1 + " " + c2 + " L" + c2 + " " + c2 + " L" + c2 + " " + c1 + " Z"))
82826                 .attr('class', ("line area " + klass));
82827             });
82828
82829             var rVertex = 2.5;
82830             [[c1, c1], [c1, c2], [c2, c2], [c2, c1]].forEach(function (point) {
82831               fillEnter
82832                 .append('circle')
82833                 .attr('class', 'vertex')
82834                 .attr('cx', point[0])
82835                 .attr('cy', point[1])
82836                 .attr('r', rVertex);
82837             });
82838
82839             if (!isSmall()) {
82840               var rMidpoint = 1.25;
82841               [[c1, w/2], [c2, w/2], [h/2, c1], [h/2, c2]].forEach(function (point) {
82842                 fillEnter
82843                   .append('circle')
82844                   .attr('class', 'midpoint')
82845                   .attr('cx', point[0])
82846                   .attr('cy', point[1])
82847                   .attr('r', rMidpoint);
82848               });
82849             }
82850           }
82851
82852
82853           function renderLine(lineEnter) {
82854             var d = isSmall() ? 40 : 60;
82855             // draw the line parametrically
82856             var w = d;
82857             var h = d;
82858             var y = Math.round(d * 0.72);
82859             var l = Math.round(d * 0.6);
82860             var r = 2.5;
82861             var x1 = (w - l) / 2;
82862             var x2 = x1 + l;
82863
82864             lineEnter = lineEnter
82865               .append('svg')
82866               .attr('class', 'preset-icon-line')
82867               .attr('width', w)
82868               .attr('height', h)
82869               .attr('viewBox', ("0 0 " + w + " " + h));
82870
82871             ['casing', 'stroke'].forEach(function (klass) {
82872               lineEnter
82873                 .append('path')
82874                 .attr('d', ("M" + x1 + " " + y + " L" + x2 + " " + y))
82875                 .attr('class', ("line " + klass));
82876             });
82877
82878             [[x1-1, y], [x2+1, y]].forEach(function (point) {
82879               lineEnter
82880                 .append('circle')
82881                 .attr('class', 'vertex')
82882                 .attr('cx', point[0])
82883                 .attr('cy', point[1])
82884                 .attr('r', r);
82885             });
82886           }
82887
82888
82889           function renderRoute(routeEnter) {
82890             var d = isSmall() ? 40 : 60;
82891             // draw the route parametrically
82892             var w = d;
82893             var h = d;
82894             var y1 = Math.round(d * 0.80);
82895             var y2 = Math.round(d * 0.68);
82896             var l = Math.round(d * 0.6);
82897             var r = 2;
82898             var x1 = (w - l) / 2;
82899             var x2 = x1 + l / 3;
82900             var x3 = x2 + l / 3;
82901             var x4 = x3 + l / 3;
82902
82903             routeEnter = routeEnter
82904               .append('svg')
82905               .attr('class', 'preset-icon-route')
82906               .attr('width', w)
82907               .attr('height', h)
82908               .attr('viewBox', ("0 0 " + w + " " + h));
82909
82910             ['casing', 'stroke'].forEach(function (klass) {
82911               routeEnter
82912                 .append('path')
82913                 .attr('d', ("M" + x1 + " " + y1 + " L" + x2 + " " + y2))
82914                 .attr('class', ("segment0 line " + klass));
82915               routeEnter
82916                 .append('path')
82917                 .attr('d', ("M" + x2 + " " + y2 + " L" + x3 + " " + y1))
82918                 .attr('class', ("segment1 line " + klass));
82919               routeEnter
82920                 .append('path')
82921                 .attr('d', ("M" + x3 + " " + y1 + " L" + x4 + " " + y2))
82922                 .attr('class', ("segment2 line " + klass));
82923             });
82924
82925             [[x1, y1], [x2, y2], [x3, y1], [x4, y2]].forEach(function (point) {
82926               routeEnter
82927                 .append('circle')
82928                 .attr('class', 'vertex')
82929                 .attr('cx', point[0])
82930                 .attr('cy', point[1])
82931                 .attr('r', r);
82932             });
82933           }
82934
82935
82936           // Route icons are drawn with a zigzag annotation underneath:
82937           //     o   o
82938           //    / \ /
82939           //   o   o
82940           // This dataset defines the styles that are used to draw the zigzag segments.
82941           var routeSegments = {
82942             bicycle: ['highway/cycleway', 'highway/cycleway', 'highway/cycleway'],
82943             bus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
82944             trolleybus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
82945             detour: ['highway/tertiary', 'highway/residential', 'highway/unclassified'],
82946             ferry: ['route/ferry', 'route/ferry', 'route/ferry'],
82947             foot: ['highway/footway', 'highway/footway', 'highway/footway'],
82948             hiking: ['highway/path', 'highway/path', 'highway/path'],
82949             horse: ['highway/bridleway', 'highway/bridleway', 'highway/bridleway'],
82950             light_rail: ['railway/light_rail', 'railway/light_rail', 'railway/light_rail'],
82951             monorail: ['railway/monorail', 'railway/monorail', 'railway/monorail'],
82952             pipeline: ['man_made/pipeline', 'man_made/pipeline', 'man_made/pipeline'],
82953             piste: ['piste/downhill', 'piste/hike', 'piste/nordic'],
82954             power: ['power/line', 'power/line', 'power/line'],
82955             road: ['highway/secondary', 'highway/primary', 'highway/trunk'],
82956             subway: ['railway/subway', 'railway/subway', 'railway/subway'],
82957             train: ['railway/rail', 'railway/rail', 'railway/rail'],
82958             tram: ['railway/tram', 'railway/tram', 'railway/tram'],
82959             waterway: ['waterway/stream', 'waterway/stream', 'waterway/stream']
82960           };
82961
82962
82963           function render() {
82964             var p = _preset.apply(this, arguments);
82965             var geom = _geometry ? _geometry.apply(this, arguments) : null;
82966             if (geom === 'relation' && p.tags && ((p.tags.type === 'route' && p.tags.route && routeSegments[p.tags.route]) || p.tags.type === 'waterway')) {
82967               geom = 'route';
82968             }
82969
82970             var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
82971             var isFallback = isSmall() && p.isFallback && p.isFallback();
82972             var imageURL = (showThirdPartyIcons === 'true') && p.imageURL;
82973             var picon = getIcon(p, geom);
82974             var isMaki = picon && /^maki-/.test(picon);
82975             var isTemaki = picon && /^temaki-/.test(picon);
82976             var isFa = picon && /^fa[srb]-/.test(picon);
82977             var isTnp = picon && /^tnp-/.test(picon);
82978             var isiDIcon = picon && !(isMaki || isTemaki || isFa || isTnp);
82979             var isCategory = !p.setTags;
82980             var drawPoint = picon && geom === 'point' && isSmall() && !isFallback;
82981             var drawVertex = picon !== null && geom === 'vertex' && (!isSmall() || !isFallback);
82982             var drawLine = picon && geom === 'line' && !isFallback && !isCategory;
82983             var drawArea = picon && geom === 'area' && !isFallback;
82984             var drawRoute = picon && geom === 'route';
82985             var isFramed = (drawVertex || drawArea || drawLine || drawRoute);
82986
82987             var tags = !isCategory ? p.setTags({}, geom) : {};
82988             for (var k in tags) {
82989               if (tags[k] === '*') {
82990                 tags[k] = 'yes';
82991               }
82992             }
82993
82994             var tagClasses = svgTagClasses().getClassesString(tags, '');
82995             var selection = select(this);
82996
82997             var container = selection.selectAll('.preset-icon-container')
82998               .data([0]);
82999
83000             container = container.enter()
83001               .append('div')
83002               .attr('class', ("preset-icon-container " + _sizeClass))
83003               .merge(container);
83004
83005             container
83006               .classed('showing-img', !!imageURL)
83007               .classed('fallback', isFallback);
83008
83009
83010             var pointBorder = container.selectAll('.preset-icon-point-border')
83011               .data(drawPoint ? [0] : []);
83012
83013             pointBorder.exit()
83014               .remove();
83015
83016             var pointBorderEnter = pointBorder.enter();
83017             renderPointBorder(pointBorderEnter);
83018             pointBorder = pointBorderEnter.merge(pointBorder);
83019
83020
83021             var vertexFill = container.selectAll('.preset-icon-fill-vertex')
83022               .data(drawVertex ? [0] : []);
83023
83024             vertexFill.exit()
83025               .remove();
83026
83027             var vertexFillEnter = vertexFill.enter();
83028             renderCircleFill(vertexFillEnter);
83029             vertexFill = vertexFillEnter.merge(vertexFill);
83030
83031
83032             var fill = container.selectAll('.preset-icon-fill-area')
83033               .data(drawArea ? [0] : []);
83034
83035             fill.exit()
83036               .remove();
83037
83038             var fillEnter = fill.enter();
83039             renderSquareFill(fillEnter);
83040             fill = fillEnter.merge(fill);
83041
83042             fill.selectAll('path.stroke')
83043               .attr('class', ("area stroke " + tagClasses));
83044             fill.selectAll('path.fill')
83045               .attr('class', ("area fill " + tagClasses));
83046
83047
83048             var line = container.selectAll('.preset-icon-line')
83049               .data(drawLine ? [0] : []);
83050
83051             line.exit()
83052               .remove();
83053
83054             var lineEnter = line.enter();
83055             renderLine(lineEnter);
83056             line = lineEnter.merge(line);
83057
83058             line.selectAll('path.stroke')
83059               .attr('class', ("line stroke " + tagClasses));
83060             line.selectAll('path.casing')
83061               .attr('class', ("line casing " + tagClasses));
83062
83063
83064             var route = container.selectAll('.preset-icon-route')
83065               .data(drawRoute ? [0] : []);
83066
83067             route.exit()
83068               .remove();
83069
83070             var routeEnter = route.enter();
83071             renderRoute(routeEnter);
83072             route = routeEnter.merge(route);
83073
83074             if (drawRoute) {
83075               var routeType = p.tags.type === 'waterway' ? 'waterway' : p.tags.route;
83076               var segmentPresetIDs = routeSegments[routeType];
83077               for (var i in segmentPresetIDs) {
83078                 var segmentPreset = _mainPresetIndex.item(segmentPresetIDs[i]);
83079                 var segmentTagClasses = svgTagClasses().getClassesString(segmentPreset.tags, '');
83080                 route.selectAll(("path.stroke.segment" + i))
83081                   .attr('class', ("segment" + i + " line stroke " + segmentTagClasses));
83082                 route.selectAll(("path.casing.segment" + i))
83083                   .attr('class', ("segment" + i + " line casing " + segmentTagClasses));
83084               }
83085             }
83086
83087
83088             var icon = container.selectAll('.preset-icon')
83089               .data(picon ? [0] : []);
83090
83091             icon.exit()
83092               .remove();
83093
83094             icon = icon.enter()
83095               .append('div')
83096               .attr('class', 'preset-icon')
83097               .call(svgIcon(''))
83098               .merge(icon);
83099
83100             icon
83101               .attr('class', 'preset-icon ' + (geom ? geom + '-geom' : ''))
83102               .classed('framed', isFramed)
83103               .classed('preset-icon-iD', isiDIcon);
83104
83105             icon.selectAll('svg')
83106               .attr('class', 'icon ' + picon + ' ' + (!isiDIcon && geom !== 'line'  ? '' : tagClasses));
83107
83108             icon.selectAll('use')
83109               .attr('href', '#' + picon + (isMaki ? (isSmall() && geom === 'point' ? '-11' : '-15') : ''));
83110
83111             var imageIcon = container.selectAll('img.image-icon')
83112               .data(imageURL ? [0] : []);
83113
83114             imageIcon.exit()
83115               .remove();
83116
83117             imageIcon = imageIcon.enter()
83118               .append('img')
83119               .attr('class', 'image-icon')
83120               .on('load', function () { return container.classed('showing-img', true); } )
83121               .on('error', function () { return container.classed('showing-img', false); } )
83122               .merge(imageIcon);
83123
83124             imageIcon
83125               .attr('src', imageURL);
83126           }
83127
83128
83129           presetIcon.preset = function(val) {
83130             if (!arguments.length) { return _preset; }
83131             _preset = utilFunctor(val);
83132             return presetIcon;
83133           };
83134
83135
83136           presetIcon.geometry = function(val) {
83137             if (!arguments.length) { return _geometry; }
83138             _geometry = utilFunctor(val);
83139             return presetIcon;
83140           };
83141
83142
83143           presetIcon.sizeClass = function(val) {
83144             if (!arguments.length) { return _sizeClass; }
83145             _sizeClass = val;
83146             return presetIcon;
83147           };
83148
83149           return presetIcon;
83150         }
83151
83152         function uiSectionFeatureType(context) {
83153
83154             var dispatch$1 = dispatch('choose');
83155
83156             var _entityIDs = [];
83157             var _presets = [];
83158
83159             var _tagReference;
83160
83161             var section = uiSection('feature-type', context)
83162                 .title(_t('inspector.feature_type'))
83163                 .disclosureContent(renderDisclosureContent);
83164
83165             function renderDisclosureContent(selection) {
83166
83167                 selection.classed('preset-list-item', true);
83168                 selection.classed('mixed-types', _presets.length > 1);
83169
83170                 var presetButtonWrap = selection
83171                     .selectAll('.preset-list-button-wrap')
83172                     .data([0])
83173                     .enter()
83174                     .append('div')
83175                     .attr('class', 'preset-list-button-wrap');
83176
83177                 var presetButton = presetButtonWrap
83178                     .append('button')
83179                     .attr('class', 'preset-list-button preset-reset')
83180                     .call(uiTooltip()
83181                         .title(_t('inspector.back_tooltip'))
83182                         .placement('bottom')
83183                     );
83184
83185                 presetButton.append('div')
83186                     .attr('class', 'preset-icon-container');
83187
83188                 presetButton
83189                     .append('div')
83190                     .attr('class', 'label')
83191                     .append('div')
83192                     .attr('class', 'label-inner');
83193
83194                 presetButtonWrap.append('div')
83195                     .attr('class', 'accessory-buttons');
83196
83197                 var tagReferenceBodyWrap = selection
83198                     .selectAll('.tag-reference-body-wrap')
83199                     .data([0]);
83200
83201                 tagReferenceBodyWrap = tagReferenceBodyWrap
83202                     .enter()
83203                     .append('div')
83204                     .attr('class', 'tag-reference-body-wrap')
83205                     .merge(tagReferenceBodyWrap);
83206
83207                 // update header
83208                 if (_tagReference) {
83209                     selection.selectAll('.preset-list-button-wrap .accessory-buttons')
83210                         .style('display', _presets.length === 1 ? null : 'none')
83211                         .call(_tagReference.button);
83212
83213                     tagReferenceBodyWrap
83214                         .style('display', _presets.length === 1 ? null : 'none')
83215                         .call(_tagReference.body);
83216                 }
83217
83218                 selection.selectAll('.preset-reset')
83219                     .on('click', function() {
83220                          dispatch$1.call('choose', this, _presets);
83221                     })
83222                     .on('pointerdown pointerup mousedown mouseup', function() {
83223                         event.preventDefault();
83224                         event.stopPropagation();
83225                     });
83226
83227                 var geometries = entityGeometries();
83228                 selection.select('.preset-list-item button')
83229                     .call(uiPresetIcon()
83230                         .geometry(_presets.length === 1 ? (geometries.length === 1 && geometries[0]) : null)
83231                         .preset(_presets.length === 1 ? _presets[0] : _mainPresetIndex.item('point'))
83232                     );
83233
83234                 // NOTE: split on en-dash, not a hypen (to avoid conflict with hyphenated names)
83235                 var names = _presets.length === 1 ? _presets[0].name().split(' – ') : [_t('inspector.multiple_types')];
83236
83237                 var label = selection.select('.label-inner');
83238                 var nameparts = label.selectAll('.namepart')
83239                     .data(names, function(d) { return d; });
83240
83241                 nameparts.exit()
83242                     .remove();
83243
83244                 nameparts
83245                     .enter()
83246                     .append('div')
83247                     .attr('class', 'namepart')
83248                     .text(function(d) { return d; });
83249             }
83250
83251             section.entityIDs = function(val) {
83252                 if (!arguments.length) { return _entityIDs; }
83253                 _entityIDs = val;
83254                 return section;
83255             };
83256
83257             section.presets = function(val) {
83258                 if (!arguments.length) { return _presets; }
83259
83260                 // don't reload the same preset
83261                 if (!utilArrayIdentical(val, _presets)) {
83262                     _presets = val;
83263
83264                     var geometries = entityGeometries();
83265                     if (_presets.length === 1 && geometries.length) {
83266                         _tagReference = uiTagReference(_presets[0].reference(geometries[0]))
83267                             .showing(false);
83268                     }
83269                 }
83270
83271                 return section;
83272             };
83273
83274             function entityGeometries() {
83275
83276                 var counts = {};
83277
83278                 for (var i in _entityIDs) {
83279                     var geometry = context.graph().geometry(_entityIDs[i]);
83280                     if (!counts[geometry]) { counts[geometry] = 0; }
83281                     counts[geometry] += 1;
83282                 }
83283
83284                 return Object.keys(counts).sort(function(geom1, geom2) {
83285                     return counts[geom2] - counts[geom1];
83286                 });
83287             }
83288
83289             return utilRebind(section, dispatch$1, 'on');
83290         }
83291
83292         // This currently only works with the 'restrictions' field
83293         // It borrows some code from uiHelp
83294
83295         function uiFieldHelp(context, fieldName) {
83296             var fieldHelp = {};
83297             var _inspector = select(null);
83298             var _wrap = select(null);
83299             var _body = select(null);
83300
83301             var fieldHelpKeys = {
83302                 restrictions: [
83303                     ['about',[
83304                         'about',
83305                         'from_via_to',
83306                         'maxdist',
83307                         'maxvia'
83308                     ]],
83309                     ['inspecting',[
83310                         'about',
83311                         'from_shadow',
83312                         'allow_shadow',
83313                         'restrict_shadow',
83314                         'only_shadow',
83315                         'restricted',
83316                         'only'
83317                     ]],
83318                     ['modifying',[
83319                         'about',
83320                         'indicators',
83321                         'allow_turn',
83322                         'restrict_turn',
83323                         'only_turn'
83324                     ]],
83325                     ['tips',[
83326                         'simple',
83327                         'simple_example',
83328                         'indirect',
83329                         'indirect_example',
83330                         'indirect_noedit'
83331                     ]]
83332                 ]
83333             };
83334
83335             var fieldHelpHeadings = {};
83336
83337             var replacements = {
83338                 distField: _t('restriction.controls.distance'),
83339                 viaField: _t('restriction.controls.via'),
83340                 fromShadow: icon('#iD-turn-shadow', 'pre-text shadow from'),
83341                 allowShadow: icon('#iD-turn-shadow', 'pre-text shadow allow'),
83342                 restrictShadow: icon('#iD-turn-shadow', 'pre-text shadow restrict'),
83343                 onlyShadow: icon('#iD-turn-shadow', 'pre-text shadow only'),
83344                 allowTurn: icon('#iD-turn-yes', 'pre-text turn'),
83345                 restrictTurn: icon('#iD-turn-no', 'pre-text turn'),
83346                 onlyTurn: icon('#iD-turn-only', 'pre-text turn')
83347             };
83348
83349
83350             // For each section, squash all the texts into a single markdown document
83351             var docs = fieldHelpKeys[fieldName].map(function(key) {
83352                 var helpkey = 'help.field.' + fieldName + '.' + key[0];
83353                 var text = key[1].reduce(function(all, part) {
83354                     var subkey = helpkey + '.' + part;
83355                     var depth = fieldHelpHeadings[subkey];                     // is this subkey a heading?
83356                     var hhh = depth ? Array(depth + 1).join('#') + ' ' : '';   // if so, prepend with some ##'s
83357                     return all + hhh + _t(subkey, replacements) + '\n\n';
83358                 }, '');
83359
83360                 return {
83361                     key: helpkey,
83362                     title: _t(helpkey + '.title'),
83363                     html: marked_1(text.trim())
83364                 };
83365             });
83366
83367
83368             function show() {
83369                 updatePosition();
83370
83371                 _body
83372                     .classed('hide', false)
83373                     .style('opacity', '0')
83374                     .transition()
83375                     .duration(200)
83376                     .style('opacity', '1');
83377             }
83378
83379
83380             function hide() {
83381                 _body
83382                     .classed('hide', true)
83383                     .transition()
83384                     .duration(200)
83385                     .style('opacity', '0')
83386                     .on('end', function () {
83387                         _body.classed('hide', true);
83388                     });
83389             }
83390
83391
83392             function clickHelp(index) {
83393                 var d = docs[index];
83394                 var tkeys = fieldHelpKeys[fieldName][index][1];
83395
83396                 _body.selectAll('.field-help-nav-item')
83397                     .classed('active', function(d, i) { return i === index; });
83398
83399                 var content = _body.selectAll('.field-help-content')
83400                     .html(d.html);
83401
83402                 // class the paragraphs so we can find and style them
83403                 content.selectAll('p')
83404                     .attr('class', function(d, i) { return tkeys[i]; });
83405
83406                 // insert special content for certain help sections
83407                 if (d.key === 'help.field.restrictions.inspecting') {
83408                     content
83409                         .insert('img', 'p.from_shadow')
83410                         .attr('class', 'field-help-image cf')
83411                         .attr('src', context.imagePath('tr_inspect.gif'));
83412
83413                 } else if (d.key === 'help.field.restrictions.modifying') {
83414                     content
83415                         .insert('img', 'p.allow_turn')
83416                         .attr('class', 'field-help-image cf')
83417                         .attr('src', context.imagePath('tr_modify.gif'));
83418                 }
83419             }
83420
83421
83422             fieldHelp.button = function(selection) {
83423                 if (_body.empty()) { return; }
83424
83425                 var button = selection.selectAll('.field-help-button')
83426                     .data([0]);
83427
83428                 // enter/update
83429                 button.enter()
83430                     .append('button')
83431                     .attr('class', 'field-help-button')
83432                     .attr('tabindex', -1)
83433                     .call(svgIcon('#iD-icon-help'))
83434                     .merge(button)
83435                     .on('click', function () {
83436                         event.stopPropagation();
83437                         event.preventDefault();
83438                         if (_body.classed('hide')) {
83439                             show();
83440                         } else {
83441                             hide();
83442                         }
83443                     });
83444             };
83445
83446
83447             function updatePosition() {
83448                 var wrap = _wrap.node();
83449                 var inspector = _inspector.node();
83450                 var wRect = wrap.getBoundingClientRect();
83451                 var iRect = inspector.getBoundingClientRect();
83452
83453                 _body
83454                     .style('top', wRect.top + inspector.scrollTop - iRect.top + 'px');
83455             }
83456
83457
83458             fieldHelp.body = function(selection) {
83459                 // This control expects the field to have a form-field-input-wrap div
83460                 _wrap = selection.selectAll('.form-field-input-wrap');
83461                 if (_wrap.empty()) { return; }
83462
83463                 // absolute position relative to the inspector, so it "floats" above the fields
83464                 _inspector = context.container().select('.sidebar .entity-editor-pane .inspector-body');
83465                 if (_inspector.empty()) { return; }
83466
83467                 _body = _inspector.selectAll('.field-help-body')
83468                     .data([0]);
83469
83470                 var enter = _body.enter()
83471                     .append('div')
83472                     .attr('class', 'field-help-body hide');   // initially hidden
83473
83474                 var titleEnter = enter
83475                     .append('div')
83476                     .attr('class', 'field-help-title cf');
83477
83478                 titleEnter
83479                     .append('h2')
83480                     .attr('class', ((_mainLocalizer.textDirection() === 'rtl') ? 'fr' : 'fl'))
83481                     .text(_t('help.field.' + fieldName + '.title'));
83482
83483                 titleEnter
83484                     .append('button')
83485                     .attr('class', 'fr close')
83486                     .on('click', function() {
83487                         event.stopPropagation();
83488                         event.preventDefault();
83489                         hide();
83490                     })
83491                     .call(svgIcon('#iD-icon-close'));
83492
83493                 var navEnter = enter
83494                     .append('div')
83495                     .attr('class', 'field-help-nav cf');
83496
83497                 var titles = docs.map(function(d) { return d.title; });
83498                 navEnter.selectAll('.field-help-nav-item')
83499                     .data(titles)
83500                     .enter()
83501                     .append('div')
83502                     .attr('class', 'field-help-nav-item')
83503                     .text(function(d) { return d; })
83504                     .on('click', function(d, i) {
83505                         event.stopPropagation();
83506                         event.preventDefault();
83507                         clickHelp(i);
83508                     });
83509
83510                 enter
83511                     .append('div')
83512                     .attr('class', 'field-help-content');
83513
83514                 _body = _body
83515                     .merge(enter);
83516
83517                 clickHelp(0);
83518             };
83519
83520
83521             return fieldHelp;
83522         }
83523
83524         function uiFieldCheck(field, context) {
83525             var dispatch$1 = dispatch('change');
83526             var options = field.strings && field.strings.options;
83527             var values = [];
83528             var texts = [];
83529
83530             var _tags;
83531
83532             var input = select(null);
83533             var text = select(null);
83534             var label = select(null);
83535             var reverser = select(null);
83536
83537             var _impliedYes;
83538             var _entityIDs = [];
83539             var _value;
83540
83541
83542             if (options) {
83543                 for (var k in options) {
83544                     values.push(k === 'undefined' ? undefined : k);
83545                     texts.push(field.t('options.' + k, { 'default': options[k] }));
83546                 }
83547             } else {
83548                 values = [undefined, 'yes'];
83549                 texts = [_t('inspector.unknown'), _t('inspector.check.yes')];
83550                 if (field.type !== 'defaultCheck') {
83551                     values.push('no');
83552                     texts.push(_t('inspector.check.no'));
83553                 }
83554             }
83555
83556
83557             // Checks tags to see whether an undefined value is "Assumed to be Yes"
83558             function checkImpliedYes() {
83559                 _impliedYes = (field.id === 'oneway_yes');
83560
83561                 // hack: pretend `oneway` field is a `oneway_yes` field
83562                 // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
83563                 if (field.id === 'oneway') {
83564                     var entity = context.entity(_entityIDs[0]);
83565                     for (var key in entity.tags) {
83566                         if (key in osmOneWayTags && (entity.tags[key] in osmOneWayTags[key])) {
83567                             _impliedYes = true;
83568                             texts[0] = _t('presets.fields.oneway_yes.options.undefined');
83569                             break;
83570                         }
83571                     }
83572                 }
83573             }
83574
83575
83576             function reverserHidden() {
83577                 if (!context.container().select('div.inspector-hover').empty()) { return true; }
83578                 return !(_value === 'yes' || (_impliedYes && !_value));
83579             }
83580
83581
83582             function reverserSetText(selection) {
83583                 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
83584                 if (reverserHidden() || !entity) { return selection; }
83585
83586                 var first = entity.first();
83587                 var last = entity.isClosed() ? entity.nodes[entity.nodes.length - 2] : entity.last();
83588                 var pseudoDirection = first < last;
83589                 var icon = pseudoDirection ? '#iD-icon-forward' : '#iD-icon-backward';
83590
83591                 selection.selectAll('.reverser-span')
83592                     .text(_t('inspector.check.reverser'))
83593                     .call(svgIcon(icon, 'inline'));
83594
83595                 return selection;
83596             }
83597
83598
83599             var check = function(selection) {
83600                 checkImpliedYes();
83601
83602                 label = selection.selectAll('.form-field-input-wrap')
83603                     .data([0]);
83604
83605                 var enter = label.enter()
83606                     .append('label')
83607                     .attr('class', 'form-field-input-wrap form-field-input-check');
83608
83609                 enter
83610                     .append('input')
83611                     .property('indeterminate', field.type !== 'defaultCheck')
83612                     .attr('type', 'checkbox')
83613                     .attr('id', field.domId);
83614
83615                 enter
83616                     .append('span')
83617                     .text(texts[0])
83618                     .attr('class', 'value');
83619
83620                 if (field.type === 'onewayCheck') {
83621                     enter
83622                         .append('a')
83623                         .attr('class', 'reverser button' + (reverserHidden() ? ' hide' : ''))
83624                         .attr('href', '#')
83625                         .append('span')
83626                         .attr('class', 'reverser-span');
83627                 }
83628
83629                 label = label.merge(enter);
83630                 input = label.selectAll('input');
83631                 text = label.selectAll('span.value');
83632
83633                 input
83634                     .on('click', function() {
83635                         event.stopPropagation();
83636                         var t = {};
83637
83638                         if (Array.isArray(_tags[field.key])) {
83639                             if (values.indexOf('yes') !== -1) {
83640                                 t[field.key] = 'yes';
83641                             } else {
83642                                 t[field.key] = values[0];
83643                             }
83644                         } else {
83645                             t[field.key] = values[(values.indexOf(_value) + 1) % values.length];
83646                         }
83647
83648                         // Don't cycle through `alternating` or `reversible` states - #4970
83649                         // (They are supported as translated strings, but should not toggle with clicks)
83650                         if (t[field.key] === 'reversible' || t[field.key] === 'alternating') {
83651                             t[field.key] = values[0];
83652                         }
83653
83654                         dispatch$1.call('change', this, t);
83655                     });
83656
83657                 if (field.type === 'onewayCheck') {
83658                     reverser = label.selectAll('.reverser');
83659
83660                     reverser
83661                         .call(reverserSetText)
83662                         .on('click', function() {
83663                             event.preventDefault();
83664                             event.stopPropagation();
83665                             context.perform(
83666                                 function(graph) {
83667                                     for (var i in _entityIDs) {
83668                                         graph = actionReverse(_entityIDs[i])(graph);
83669                                     }
83670                                     return graph;
83671                                 },
83672                                 _t('operations.reverse.annotation')
83673                             );
83674
83675                             // must manually revalidate since no 'change' event was called
83676                             context.validator().validate();
83677
83678                             select(this)
83679                                 .call(reverserSetText);
83680                         });
83681                 }
83682             };
83683
83684
83685             check.entityIDs = function(val) {
83686                 if (!arguments.length) { return _entityIDs; }
83687                 _entityIDs = val;
83688                 return check;
83689             };
83690
83691
83692             check.tags = function(tags) {
83693
83694                 _tags = tags;
83695
83696                 function isChecked(val) {
83697                     return val !== 'no' && val !== '' && val !== undefined && val !== null;
83698                 }
83699
83700                 function textFor(val) {
83701                     if (val === '') { val = undefined; }
83702                     var index = values.indexOf(val);
83703                     return (index !== -1 ? texts[index] : ('"' + val + '"'));
83704                 }
83705
83706                 checkImpliedYes();
83707
83708                 var isMixed = Array.isArray(tags[field.key]);
83709
83710                 _value = !isMixed && tags[field.key] && tags[field.key].toLowerCase();
83711
83712                 if (field.type === 'onewayCheck' && (_value === '1' || _value === '-1')) {
83713                     _value = 'yes';
83714                 }
83715
83716                 input
83717                     .property('indeterminate', isMixed || (field.type !== 'defaultCheck' && !_value))
83718                     .property('checked', isChecked(_value));
83719
83720                 text
83721                     .text(isMixed ? _t('inspector.multiple_values') : textFor(_value))
83722                     .classed('mixed', isMixed);
83723
83724                 label
83725                     .classed('set', !!_value);
83726
83727                 if (field.type === 'onewayCheck') {
83728                     reverser
83729                         .classed('hide', reverserHidden())
83730                         .call(reverserSetText);
83731                 }
83732             };
83733
83734
83735             check.focus = function() {
83736                 input.node().focus();
83737             };
83738
83739             return utilRebind(check, dispatch$1, 'on');
83740         }
83741
83742         function uiFieldCombo(field, context) {
83743             var dispatch$1 = dispatch('change');
83744             var taginfo = services.taginfo;
83745             var isMulti = (field.type === 'multiCombo');
83746             var isNetwork = (field.type === 'networkCombo');
83747             var isSemi = (field.type === 'semiCombo');
83748             var optstrings = field.strings && field.strings.options;
83749             var optarray = field.options;
83750             var snake_case = (field.snake_case || (field.snake_case === undefined));
83751             var caseSensitive = field.caseSensitive;
83752             var combobox = uiCombobox(context, 'combo-' + field.safeid)
83753                 .caseSensitive(caseSensitive)
83754                 .minItems(isMulti || isSemi ? 1 : 2);
83755             var container = select(null);
83756             var inputWrap = select(null);
83757             var input = select(null);
83758             var _comboData = [];
83759             var _multiData = [];
83760             var _entityIDs = [];
83761             var _tags;
83762             var _countryCode;
83763             var _staticPlaceholder;
83764
83765             // initialize deprecated tags array
83766             var _dataDeprecated = [];
83767             _mainFileFetcher.get('deprecated')
83768                 .then(function(d) { _dataDeprecated = d; })
83769                 .catch(function() { /* ignore */ });
83770
83771
83772             // ensure multiCombo field.key ends with a ':'
83773             if (isMulti && /[^:]$/.test(field.key)) {
83774                 field.key += ':';
83775             }
83776
83777
83778             function snake(s) {
83779                 return s.replace(/\s+/g, '_');
83780             }
83781
83782             function unsnake(s) {
83783                 return s.replace(/_+/g, ' ');
83784             }
83785
83786             function clean(s) {
83787                 return s.split(';')
83788                     .map(function(s) { return s.trim(); })
83789                     .join(';');
83790             }
83791
83792
83793             // returns the tag value for a display value
83794             // (for multiCombo, dval should be the key suffix, not the entire key)
83795             function tagValue(dval) {
83796                 dval = clean(dval || '');
83797
83798                 if (optstrings) {
83799                     var found = _comboData.find(function(o) {
83800                         return o.key && clean(o.value) === dval;
83801                     });
83802                     if (found) {
83803                         return found.key;
83804                     }
83805                 }
83806
83807                 if (field.type === 'typeCombo' && !dval) {
83808                     return 'yes';
83809                 }
83810
83811                 return (snake_case ? snake(dval) : dval) || undefined;
83812             }
83813
83814
83815             // returns the display value for a tag value
83816             // (for multiCombo, tval should be the key suffix, not the entire key)
83817             function displayValue(tval) {
83818                 tval = tval || '';
83819
83820                 if (optstrings) {
83821                     var found = _comboData.find(function(o) {
83822                         return o.key === tval && o.value;
83823                     });
83824                     if (found) {
83825                         return found.value;
83826                     }
83827                 }
83828
83829                 if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
83830                     return '';
83831                 }
83832
83833                 return snake_case ? unsnake(tval) : tval;
83834             }
83835
83836
83837             // Compute the difference between arrays of objects by `value` property
83838             //
83839             // objectDifference([{value:1}, {value:2}, {value:3}], [{value:2}])
83840             // > [{value:1}, {value:3}]
83841             //
83842             function objectDifference(a, b) {
83843                 return a.filter(function(d1) {
83844                     return !b.some(function(d2) {
83845                         return !d2.isMixed && d1.value === d2.value;
83846                     });
83847                 });
83848             }
83849
83850
83851             function initCombo(selection, attachTo) {
83852                 if (optstrings) {
83853                     selection.attr('readonly', 'readonly');
83854                     selection.call(combobox, attachTo);
83855                     setStaticValues(setPlaceholder);
83856
83857                 } else if (optarray) {
83858                     selection.call(combobox, attachTo);
83859                     setStaticValues(setPlaceholder);
83860
83861                 } else if (taginfo) {
83862                     selection.call(combobox.fetcher(setTaginfoValues), attachTo);
83863                     setTaginfoValues('', setPlaceholder);
83864                 }
83865             }
83866
83867
83868             function setStaticValues(callback) {
83869                 if (!(optstrings || optarray)) { return; }
83870
83871                 if (optstrings) {
83872                     _comboData = Object.keys(optstrings).map(function(k) {
83873                         var v = field.t('options.' + k, { 'default': optstrings[k] });
83874                         return {
83875                             key: k,
83876                             value: v,
83877                             title: v
83878                         };
83879                     });
83880
83881                 } else if (optarray) {
83882                     _comboData = optarray.map(function(k) {
83883                         var v = snake_case ? unsnake(k) : k;
83884                         return {
83885                             key: k,
83886                             value: v,
83887                             title: v
83888                         };
83889                     });
83890                 }
83891
83892                 combobox.data(objectDifference(_comboData, _multiData));
83893                 if (callback) { callback(_comboData); }
83894             }
83895
83896
83897             function setTaginfoValues(q, callback) {
83898                 var fn = isMulti ? 'multikeys' : 'values';
83899                 var query = (isMulti ? field.key : '') + q;
83900                 var hasCountryPrefix = isNetwork && _countryCode && _countryCode.indexOf(q.toLowerCase()) === 0;
83901                 if (hasCountryPrefix) {
83902                     query = _countryCode + ':';
83903                 }
83904
83905                 var params = {
83906                     debounce: (q !== ''),
83907                     key: field.key,
83908                     query: query
83909                 };
83910
83911                 if (_entityIDs.length) {
83912                     params.geometry = context.graph().geometry(_entityIDs[0]);
83913                 }
83914
83915                 taginfo[fn](params, function(err, data) {
83916                     if (err) { return; }
83917
83918                     data = data.filter(function(d) {
83919
83920                         if (field.type === 'typeCombo' && d.value === 'yes') {
83921                             // don't show the fallback value
83922                             return false;
83923                         }
83924
83925                         // don't show values with very low usage
83926                         return !d.count || d.count > 10;
83927                     });
83928
83929                     var deprecatedValues = osmEntity.deprecatedTagValuesByKey(_dataDeprecated)[field.key];
83930                     if (deprecatedValues) {
83931                         // don't suggest deprecated tag values
83932                         data = data.filter(function(d) {
83933                             return deprecatedValues.indexOf(d.value) === -1;
83934                         });
83935                     }
83936
83937                     if (hasCountryPrefix) {
83938                         data = data.filter(function(d) {
83939                             return d.value.toLowerCase().indexOf(_countryCode + ':') === 0;
83940                         });
83941                     }
83942
83943                     // hide the caret if there are no suggestions
83944                     container.classed('empty-combobox', data.length === 0);
83945
83946                     _comboData = data.map(function(d) {
83947                         var k = d.value;
83948                         if (isMulti) { k = k.replace(field.key, ''); }
83949                         var v = snake_case ? unsnake(k) : k;
83950                         return {
83951                             key: k,
83952                             value: v,
83953                             title: isMulti ? v : d.title
83954                         };
83955                     });
83956
83957                     _comboData = objectDifference(_comboData, _multiData);
83958                     if (callback) { callback(_comboData); }
83959                 });
83960             }
83961
83962
83963             function setPlaceholder(values) {
83964
83965                 if (isMulti || isSemi) {
83966                     _staticPlaceholder = field.placeholder() || _t('inspector.add');
83967                 } else {
83968                     var vals = values
83969                         .map(function(d) { return d.value; })
83970                         .filter(function(s) { return s.length < 20; });
83971
83972                     var placeholders = vals.length > 1 ? vals : values.map(function(d) { return d.key; });
83973                     _staticPlaceholder = field.placeholder() || placeholders.slice(0, 3).join(', ');
83974                 }
83975
83976                 if (!/(…|\.\.\.)$/.test(_staticPlaceholder)) {
83977                     _staticPlaceholder += '…';
83978                 }
83979
83980                 var ph;
83981                 if (!isMulti && !isSemi && _tags && Array.isArray(_tags[field.key])) {
83982                     ph = _t('inspector.multiple_values');
83983                 } else {
83984                     ph =  _staticPlaceholder;
83985                 }
83986
83987                 container.selectAll('input')
83988                     .attr('placeholder', ph);
83989             }
83990
83991
83992             function change() {
83993                 var t = {};
83994                 var val;
83995
83996                 if (isMulti || isSemi) {
83997                     val = tagValue(utilGetSetValue(input).replace(/,/g, ';')) || '';
83998                     container.classed('active', false);
83999                     utilGetSetValue(input, '');
84000
84001                     var vals = val.split(';').filter(Boolean);
84002                     if (!vals.length) { return; }
84003
84004                     if (isMulti) {
84005                         utilArrayUniq(vals).forEach(function(v) {
84006                             var key = field.key + v;
84007                             if (_tags) {
84008                                 // don't set a multicombo value to 'yes' if it already has a non-'no' value
84009                                 // e.g. `language:de=main`
84010                                 var old = _tags[key];
84011                                 if (typeof old === 'string' && old.toLowerCase() !== 'no') { return; }
84012                             }
84013                             key = context.cleanTagKey(key);
84014                             field.keys.push(key);
84015                             t[key] = 'yes';
84016                         });
84017
84018                     } else if (isSemi) {
84019                         var arr = _multiData.map(function(d) { return d.key; });
84020                         arr = arr.concat(vals);
84021                         t[field.key] = context.cleanTagValue(utilArrayUniq(arr).filter(Boolean).join(';'));
84022                     }
84023
84024                     window.setTimeout(function() { input.node().focus(); }, 10);
84025
84026                 } else {
84027                     var rawValue = utilGetSetValue(input);
84028
84029                     // don't override multiple values with blank string
84030                     if (!rawValue && Array.isArray(_tags[field.key])) { return; }
84031
84032                     val = context.cleanTagValue(tagValue(rawValue));
84033                     t[field.key] = val || undefined;
84034                 }
84035
84036                 dispatch$1.call('change', this, t);
84037             }
84038
84039
84040             function removeMultikey(d) {
84041                 event.stopPropagation();
84042                 var t = {};
84043                 if (isMulti) {
84044                     t[d.key] = undefined;
84045                 } else if (isSemi) {
84046                     var arr = _multiData.map(function(md) {
84047                         return md.key === d.key ? null : md.key;
84048                     }).filter(Boolean);
84049
84050                     arr = utilArrayUniq(arr);
84051                     t[field.key] = arr.length ? arr.join(';') : undefined;
84052                 }
84053                 dispatch$1.call('change', this, t);
84054             }
84055
84056
84057             function combo(selection) {
84058                 container = selection.selectAll('.form-field-input-wrap')
84059                     .data([0]);
84060
84061                 var type = (isMulti || isSemi) ? 'multicombo': 'combo';
84062                 container = container.enter()
84063                     .append('div')
84064                     .attr('class', 'form-field-input-wrap form-field-input-' + type)
84065                     .merge(container);
84066
84067                 if (isMulti || isSemi) {
84068                     container = container.selectAll('.chiplist')
84069                         .data([0]);
84070
84071                     var listClass = 'chiplist';
84072
84073                     // Use a separate line for each value in the Destinations field
84074                     // to mimic highway exit signs
84075                     if (field.key === 'destination') {
84076                         listClass += ' full-line-chips';
84077                     }
84078
84079                     container = container.enter()
84080                         .append('ul')
84081                         .attr('class', listClass)
84082                         .on('click', function() {
84083                             window.setTimeout(function() { input.node().focus(); }, 10);
84084                         })
84085                         .merge(container);
84086
84087
84088                     inputWrap = container.selectAll('.input-wrap')
84089                         .data([0]);
84090
84091                     inputWrap = inputWrap.enter()
84092                         .append('li')
84093                         .attr('class', 'input-wrap')
84094                         .merge(inputWrap);
84095
84096                     input = inputWrap.selectAll('input')
84097                         .data([0]);
84098                 } else {
84099                     input = container.selectAll('input')
84100                         .data([0]);
84101                 }
84102
84103                 input = input.enter()
84104                     .append('input')
84105                     .attr('type', 'text')
84106                     .attr('id', field.domId)
84107                     .call(utilNoAuto)
84108                     .call(initCombo, selection)
84109                     .merge(input);
84110
84111                 if (isNetwork) {
84112                     var extent = combinedEntityExtent();
84113                     var countryCode = extent && iso1A2Code(extent.center());
84114                     _countryCode = countryCode && countryCode.toLowerCase();
84115                 }
84116
84117                 input
84118                     .on('change', change)
84119                     .on('blur', change);
84120
84121                 input
84122                     .on('keydown.field', function() {
84123                         switch (event.keyCode) {
84124                             case 13: // ↩ Return
84125                                 input.node().blur(); // blurring also enters the value
84126                                 event.stopPropagation();
84127                                 break;
84128                         }
84129                     });
84130
84131                 if (isMulti || isSemi) {
84132                     combobox
84133                         .on('accept', function() {
84134                             input.node().blur();
84135                             input.node().focus();
84136                         });
84137
84138                     input
84139                         .on('focus', function() { container.classed('active', true); });
84140                 }
84141             }
84142
84143
84144             combo.tags = function(tags) {
84145                 _tags = tags;
84146
84147                 if (isMulti || isSemi) {
84148                     _multiData = [];
84149
84150                     var maxLength;
84151
84152                     if (isMulti) {
84153                         // Build _multiData array containing keys already set..
84154                         for (var k in tags) {
84155                             if (k.indexOf(field.key) !== 0) { continue; }
84156                             var v = tags[k];
84157                             if (!v || (typeof v === 'string' && v.toLowerCase() === 'no')) { continue; }
84158
84159                             var suffix = k.substring(field.key.length);
84160                             _multiData.push({
84161                                 key: k,
84162                                 value: displayValue(suffix),
84163                                 isMixed: Array.isArray(v)
84164                             });
84165                         }
84166
84167                         // Set keys for form-field modified (needed for undo and reset buttons)..
84168                         field.keys = _multiData.map(function(d) { return d.key; });
84169
84170                         // limit the input length so it fits after prepending the key prefix
84171                         maxLength = context.maxCharsForTagKey() - utilUnicodeCharsCount(field.key);
84172
84173                     } else if (isSemi) {
84174
84175                         var allValues = [];
84176                         var commonValues;
84177                         if (Array.isArray(tags[field.key])) {
84178
84179                             tags[field.key].forEach(function(tagVal) {
84180                                 var thisVals = utilArrayUniq((tagVal || '').split(';')).filter(Boolean);
84181                                 allValues = allValues.concat(thisVals);
84182                                 if (!commonValues) {
84183                                     commonValues = thisVals;
84184                                 } else {
84185                                     commonValues = commonValues.filter(function (value) { return thisVals.includes(value); });
84186                                 }
84187                             });
84188                             allValues = utilArrayUniq(allValues).filter(Boolean);
84189
84190                         } else {
84191                             allValues =  utilArrayUniq((tags[field.key] || '').split(';')).filter(Boolean);
84192                             commonValues = allValues;
84193                         }
84194
84195                         _multiData = allValues.map(function(v) {
84196                             return {
84197                                 key: v,
84198                                 value: displayValue(v),
84199                                 isMixed: !commonValues.includes(v)
84200                             };
84201                         });
84202
84203                         var currLength = utilUnicodeCharsCount(commonValues.join(';'));
84204
84205                         // limit the input length to the remaining available characters
84206                         maxLength = context.maxCharsForTagValue() - currLength;
84207
84208                         if (currLength > 0) {
84209                             // account for the separator if a new value will be appended to existing
84210                             maxLength -= 1;
84211                         }
84212                     }
84213                     // a negative maxlength doesn't make sense
84214                     maxLength = Math.max(0, maxLength);
84215
84216                     var allowDragAndDrop = isSemi // only semiCombo values are ordered
84217                         && !Array.isArray(tags[field.key]);
84218
84219                     // Exclude existing multikeys from combo options..
84220                     var available = objectDifference(_comboData, _multiData);
84221                     combobox.data(available);
84222
84223                     // Hide 'Add' button if this field uses fixed set of
84224                     // translateable optstrings and they're all currently used,
84225                     // or if the field is already at its character limit
84226                     var hideAdd = (optstrings && !available.length) || maxLength <= 0;
84227                     container.selectAll('.chiplist .input-wrap')
84228                         .style('display', hideAdd ? 'none' : null);
84229
84230
84231                     // Render chips
84232                     var chips = container.selectAll('.chip')
84233                         .data(_multiData);
84234
84235                     chips.exit()
84236                         .remove();
84237
84238                     var enter = chips.enter()
84239                         .insert('li', '.input-wrap')
84240                         .attr('class', 'chip');
84241
84242                     enter.append('span');
84243                     enter.append('a');
84244
84245                     chips = chips.merge(enter)
84246                         .order()
84247                         .classed('draggable', allowDragAndDrop)
84248                         .classed('mixed', function(d) {
84249                             return d.isMixed;
84250                         })
84251                         .attr('title', function(d) {
84252                             return d.isMixed ? _t('inspector.unshared_value_tooltip') : null;
84253                         });
84254
84255                     if (allowDragAndDrop) {
84256                         registerDragAndDrop(chips);
84257                     }
84258
84259                     chips.select('span')
84260                         .text(function(d) { return d.value; });
84261
84262                     chips.select('a')
84263                         .on('click', removeMultikey)
84264                         .attr('class', 'remove')
84265                         .text('×');
84266
84267                 } else {
84268                     var isMixed = Array.isArray(tags[field.key]);
84269
84270                     var mixedValues = isMixed && tags[field.key].map(function(val) {
84271                         return displayValue(val);
84272                     }).filter(Boolean);
84273
84274                     utilGetSetValue(input, !isMixed ? displayValue(tags[field.key]) : '')
84275                         .attr('title', isMixed ? mixedValues.join('\n') : undefined)
84276                         .attr('placeholder', isMixed ? _t('inspector.multiple_values') : _staticPlaceholder || '')
84277                         .classed('mixed', isMixed);
84278                 }
84279             };
84280
84281             function registerDragAndDrop(selection) {
84282
84283                 // allow drag and drop re-ordering of chips
84284                 var dragOrigin, targetIndex;
84285                 selection.call(d3_drag()
84286                     .on('start', function() {
84287                         dragOrigin = {
84288                             x: event.x,
84289                             y: event.y
84290                         };
84291                         targetIndex = null;
84292                     })
84293                     .on('drag', function(d, index) {
84294                         var x = event.x - dragOrigin.x,
84295                             y = event.y - dragOrigin.y;
84296
84297                         if (!select(this).classed('dragging') &&
84298                             // don't display drag until dragging beyond a distance threshold
84299                             Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) { return; }
84300
84301                         select(this)
84302                             .classed('dragging', true);
84303
84304                         targetIndex = null;
84305                         var targetIndexOffsetTop = null;
84306                         var draggedTagWidth = select(this).node().offsetWidth;
84307
84308                         if (field.key === 'destination') { // meaning tags are full width
84309                             container.selectAll('.chip')
84310                                 .style('transform', function(d2, index2) {
84311                                     var node = select(this).node();
84312
84313                                     if (index === index2) {
84314                                         return 'translate(' + x + 'px, ' + y + 'px)';
84315                                     // move the dragged tag up the order
84316                                     } else if (index2 > index && event.y > node.offsetTop) {
84317                                         if (targetIndex === null || index2 > targetIndex) {
84318                                             targetIndex = index2;
84319                                         }
84320                                         return 'translateY(-100%)';
84321                                     // move the dragged tag down the order
84322                                     } else if (index2 < index && event.y < node.offsetTop + node.offsetHeight) {
84323                                         if (targetIndex === null || index2 < targetIndex) {
84324                                             targetIndex = index2;
84325                                         }
84326                                         return 'translateY(100%)';
84327                                     }
84328                                     return null;
84329                                 });
84330                         } else {
84331                             container.selectAll('.chip')
84332                                 .each(function(d2, index2) {
84333                                     var node = select(this).node();
84334
84335                                     // check the cursor is in the bounding box
84336                                     if (
84337                                         index !== index2 &&
84338                                         event.x < node.offsetLeft + node.offsetWidth + 5 &&
84339                                         event.x > node.offsetLeft &&
84340                                         event.y < node.offsetTop + node.offsetHeight &&
84341                                         event.y > node.offsetTop
84342                                     ) {
84343                                         targetIndex = index2;
84344                                         targetIndexOffsetTop = node.offsetTop;
84345                                     }
84346                                 })
84347                                 .style('transform', function(d2, index2) {
84348                                     var node = select(this).node();
84349
84350                                     if (index === index2) {
84351                                         return 'translate(' + x + 'px, ' + y + 'px)';
84352                                     }
84353
84354                                     // only translate tags in the same row
84355                                     if (node.offsetTop === targetIndexOffsetTop) {
84356                                         if (index2 < index && index2 >= targetIndex) {
84357                                             return 'translateX(' + draggedTagWidth + 'px)';
84358                                         } else if (index2 > index && index2 <= targetIndex) {
84359                                             return 'translateX(-' + draggedTagWidth + 'px)';
84360                                         }
84361                                     }
84362                                     return null;
84363                                 });
84364                             }
84365                     })
84366                     .on('end', function(d, index) {
84367                         if (!select(this).classed('dragging')) {
84368                             return;
84369                         }
84370
84371                         select(this)
84372                             .classed('dragging', false);
84373
84374                         container.selectAll('.chip')
84375                             .style('transform', null);
84376
84377                         if (typeof targetIndex === 'number') {
84378                             var element = _multiData[index];
84379                             _multiData.splice(index, 1);
84380                             _multiData.splice(targetIndex, 0, element);
84381
84382                             var t = {};
84383
84384                             if (_multiData.length) {
84385                                 t[field.key] = _multiData.map(function(element) {
84386                                     return element.key;
84387                                 }).join(';');
84388                             } else {
84389                                 t[field.key] = undefined;
84390                             }
84391
84392                             dispatch$1.call('change', this, t);
84393                         }
84394                         dragOrigin = undefined;
84395                         targetIndex = undefined;
84396                     })
84397                 );
84398             }
84399
84400
84401             combo.focus = function() {
84402                 input.node().focus();
84403             };
84404
84405
84406             combo.entityIDs = function(val) {
84407                 if (!arguments.length) { return _entityIDs; }
84408                 _entityIDs = val;
84409                 return combo;
84410             };
84411
84412
84413             function combinedEntityExtent() {
84414                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
84415             }
84416
84417
84418             return utilRebind(combo, dispatch$1, 'on');
84419         }
84420
84421         function uiFieldText(field, context) {
84422             var dispatch$1 = dispatch('change');
84423             var input = select(null);
84424             var outlinkButton = select(null);
84425             var _entityIDs = [];
84426             var _tags;
84427             var _phoneFormats = {};
84428
84429             if (field.type === 'tel') {
84430                 _mainFileFetcher.get('phone_formats')
84431                     .then(function(d) {
84432                         _phoneFormats = d;
84433                         updatePhonePlaceholder();
84434                     })
84435                     .catch(function() { /* ignore */ });
84436             }
84437
84438             function i(selection) {
84439                 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
84440                 var preset = entity && _mainPresetIndex.match(entity, context.graph());
84441                 var isLocked = preset && preset.suggestion && field.id === 'brand';
84442                 field.locked(isLocked);
84443
84444                 var wrap = selection.selectAll('.form-field-input-wrap')
84445                     .data([0]);
84446
84447                 wrap = wrap.enter()
84448                     .append('div')
84449                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
84450                     .merge(wrap);
84451
84452                 input = wrap.selectAll('input')
84453                     .data([0]);
84454
84455                 input = input.enter()
84456                     .append('input')
84457                     .attr('type', field.type === 'identifier' ? 'text' : field.type)
84458                     .attr('id', field.domId)
84459                     .classed(field.type, true)
84460                     .call(utilNoAuto)
84461                     .merge(input);
84462
84463                 input
84464                     .classed('disabled', !!isLocked)
84465                     .attr('readonly', isLocked || null)
84466                     .on('input', change(true))
84467                     .on('blur', change())
84468                     .on('change', change());
84469
84470
84471                 if (field.type === 'tel') {
84472                     updatePhonePlaceholder();
84473
84474                 } else if (field.type === 'number') {
84475                     var rtl = (_mainLocalizer.textDirection() === 'rtl');
84476
84477                     input.attr('type', 'text');
84478
84479                     var buttons = wrap.selectAll('.increment, .decrement')
84480                         .data(rtl ? [1, -1] : [-1, 1]);
84481
84482                     buttons.enter()
84483                         .append('button')
84484                         .attr('tabindex', -1)
84485                         .attr('class', function(d) {
84486                             var which = (d === 1 ? 'increment' : 'decrement');
84487                             return 'form-field-button ' + which;
84488                         })
84489                         .merge(buttons)
84490                         .on('click', function(d) {
84491                             event.preventDefault();
84492                             var raw_vals = input.node().value || '0';
84493                             var vals = raw_vals.split(';');
84494                             vals = vals.map(function(v) {
84495                                 var num = parseFloat(v.trim(), 10);
84496                                 return isFinite(num) ? clamped(num + d) : v.trim();
84497                             });
84498                             input.node().value = vals.join(';');
84499                             change()();
84500                         });
84501                 } else if (field.type === 'identifier' && field.urlFormat && field.pattern) {
84502
84503                     input.attr('type', 'text');
84504
84505                     outlinkButton = wrap.selectAll('.foreign-id-permalink')
84506                         .data([0]);
84507
84508                     outlinkButton.enter()
84509                         .append('button')
84510                         .attr('tabindex', -1)
84511                         .call(svgIcon('#iD-icon-out-link'))
84512                         .attr('class', 'form-field-button foreign-id-permalink')
84513                         .attr('title', function() {
84514                             var domainResults = /^https?:\/\/(.{1,}?)\//.exec(field.urlFormat);
84515                             if (domainResults.length >= 2 && domainResults[1]) {
84516                                 var domain = domainResults[1];
84517                                 return _t('icons.view_on', { domain: domain });
84518                             }
84519                             return '';
84520                         })
84521                         .on('click', function() {
84522                             event.preventDefault();
84523
84524                             var value = validIdentifierValueForLink();
84525                             if (value) {
84526                                 var url = field.urlFormat.replace(/{value}/, encodeURIComponent(value));
84527                                 window.open(url, '_blank');
84528                             }
84529                         })
84530                         .merge(outlinkButton);
84531                 }
84532             }
84533
84534
84535             function updatePhonePlaceholder() {
84536                 if (input.empty() || !Object.keys(_phoneFormats).length) { return; }
84537
84538                 var extent = combinedEntityExtent();
84539                 var countryCode = extent && iso1A2Code(extent.center());
84540                 var format = countryCode && _phoneFormats[countryCode.toLowerCase()];
84541                 if (format) { input.attr('placeholder', format); }
84542             }
84543
84544
84545             function validIdentifierValueForLink() {
84546                 if (field.type === 'identifier' && field.pattern) {
84547                     var value = utilGetSetValue(input).trim().split(';')[0];
84548                     return value && value.match(new RegExp(field.pattern));
84549                 }
84550                 return null;
84551             }
84552
84553
84554             // clamp number to min/max
84555             function clamped(num) {
84556                 if (field.minValue !== undefined) {
84557                     num = Math.max(num, field.minValue);
84558                 }
84559                 if (field.maxValue !== undefined) {
84560                     num = Math.min(num, field.maxValue);
84561                 }
84562                 return num;
84563             }
84564
84565
84566             function change(onInput) {
84567                 return function() {
84568                     var t = {};
84569                     var val = utilGetSetValue(input);
84570                     if (!onInput) { val = context.cleanTagValue(val); }
84571
84572                     // don't override multiple values with blank string
84573                     if (!val && Array.isArray(_tags[field.key])) { return; }
84574
84575                     if (!onInput) {
84576                         if (field.type === 'number' && val) {
84577                             var vals = val.split(';');
84578                             vals = vals.map(function(v) {
84579                                 var num = parseFloat(v.trim(), 10);
84580                                 return isFinite(num) ? clamped(num) : v.trim();
84581                             });
84582                             val = vals.join(';');
84583                         }
84584                         utilGetSetValue(input, val);
84585                     }
84586                     t[field.key] = val || undefined;
84587                     dispatch$1.call('change', this, t, onInput);
84588                 };
84589             }
84590
84591
84592             i.entityIDs = function(val) {
84593                 if (!arguments.length) { return _entityIDs; }
84594                 _entityIDs = val;
84595                 return i;
84596             };
84597
84598
84599             i.tags = function(tags) {
84600                 _tags = tags;
84601
84602                 var isMixed = Array.isArray(tags[field.key]);
84603
84604                 utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '')
84605                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined)
84606                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : (field.placeholder() || _t('inspector.unknown')))
84607                     .classed('mixed', isMixed);
84608
84609                 if (outlinkButton && !outlinkButton.empty()) {
84610                     var disabled = !validIdentifierValueForLink();
84611                     outlinkButton.classed('disabled', disabled);
84612                 }
84613             };
84614
84615
84616             i.focus = function() {
84617                 var node = input.node();
84618                 if (node) { node.focus(); }
84619             };
84620
84621             function combinedEntityExtent() {
84622                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
84623             }
84624
84625             return utilRebind(i, dispatch$1, 'on');
84626         }
84627
84628         function uiFieldAccess(field, context) {
84629             var dispatch$1 = dispatch('change');
84630             var items = select(null);
84631             var _tags;
84632
84633             function access(selection) {
84634                 var wrap = selection.selectAll('.form-field-input-wrap')
84635                     .data([0]);
84636
84637                 wrap = wrap.enter()
84638                     .append('div')
84639                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
84640                     .merge(wrap);
84641
84642                 var list = wrap.selectAll('ul')
84643                     .data([0]);
84644
84645                 list = list.enter()
84646                     .append('ul')
84647                     .attr('class', 'rows')
84648                     .merge(list);
84649
84650
84651                 items = list.selectAll('li')
84652                     .data(field.keys);
84653
84654                 // Enter
84655                 var enter = items.enter()
84656                     .append('li')
84657                     .attr('class', function(d) { return 'labeled-input preset-access-' + d; });
84658
84659                 enter
84660                     .append('span')
84661                     .attr('class', 'label preset-label-access')
84662                     .attr('for', function(d) { return 'preset-input-access-' + d; })
84663                     .text(function(d) { return field.t('types.' + d); });
84664
84665                 enter
84666                     .append('div')
84667                     .attr('class', 'preset-input-access-wrap')
84668                     .append('input')
84669                     .attr('type', 'text')
84670                     .attr('class', function(d) { return 'preset-input-access preset-input-access-' + d; })
84671                     .call(utilNoAuto)
84672                     .each(function(d) {
84673                         select(this)
84674                             .call(uiCombobox(context, 'access-' + d)
84675                                 .data(access.options(d))
84676                             );
84677                     });
84678
84679
84680                 // Update
84681                 items = items.merge(enter);
84682
84683                 wrap.selectAll('.preset-input-access')
84684                     .on('change', change)
84685                     .on('blur', change);
84686             }
84687
84688
84689             function change(d) {
84690                 var tag = {};
84691                 var value = context.cleanTagValue(utilGetSetValue(select(this)));
84692
84693                 // don't override multiple values with blank string
84694                 if (!value && typeof _tags[d] !== 'string') { return; }
84695
84696                 tag[d] = value || undefined;
84697                 dispatch$1.call('change', this, tag);
84698             }
84699
84700
84701             access.options = function(type) {
84702                 var options = ['no', 'permissive', 'private', 'permit', 'destination'];
84703
84704                 if (type !== 'access') {
84705                     options.unshift('yes');
84706                     options.push('designated');
84707
84708                     if (type === 'bicycle') {
84709                         options.push('dismount');
84710                     }
84711                 }
84712
84713                 return options.map(function(option) {
84714                     return {
84715                         title: field.t('options.' + option + '.description'),
84716                         value: option
84717                     };
84718                 });
84719             };
84720
84721
84722             var placeholdersByHighway = {
84723                 footway: {
84724                     foot: 'designated',
84725                     motor_vehicle: 'no'
84726                 },
84727                 steps: {
84728                     foot: 'yes',
84729                     motor_vehicle: 'no',
84730                     bicycle: 'no',
84731                     horse: 'no'
84732                 },
84733                 pedestrian: {
84734                     foot: 'yes',
84735                     motor_vehicle: 'no'
84736                 },
84737                 cycleway: {
84738                     motor_vehicle: 'no',
84739                     bicycle: 'designated'
84740                 },
84741                 bridleway: {
84742                     motor_vehicle: 'no',
84743                     horse: 'designated'
84744                 },
84745                 path: {
84746                     foot: 'yes',
84747                     motor_vehicle: 'no',
84748                     bicycle: 'yes',
84749                     horse: 'yes'
84750                 },
84751                 motorway: {
84752                     foot: 'no',
84753                     motor_vehicle: 'yes',
84754                     bicycle: 'no',
84755                     horse: 'no'
84756                 },
84757                 trunk: {
84758                     motor_vehicle: 'yes'
84759                 },
84760                 primary: {
84761                     foot: 'yes',
84762                     motor_vehicle: 'yes',
84763                     bicycle: 'yes',
84764                     horse: 'yes'
84765                 },
84766                 secondary: {
84767                     foot: 'yes',
84768                     motor_vehicle: 'yes',
84769                     bicycle: 'yes',
84770                     horse: 'yes'
84771                 },
84772                 tertiary: {
84773                     foot: 'yes',
84774                     motor_vehicle: 'yes',
84775                     bicycle: 'yes',
84776                     horse: 'yes'
84777                 },
84778                 residential: {
84779                     foot: 'yes',
84780                     motor_vehicle: 'yes',
84781                     bicycle: 'yes',
84782                     horse: 'yes'
84783                 },
84784                 unclassified: {
84785                     foot: 'yes',
84786                     motor_vehicle: 'yes',
84787                     bicycle: 'yes',
84788                     horse: 'yes'
84789                 },
84790                 service: {
84791                     foot: 'yes',
84792                     motor_vehicle: 'yes',
84793                     bicycle: 'yes',
84794                     horse: 'yes'
84795                 },
84796                 motorway_link: {
84797                     foot: 'no',
84798                     motor_vehicle: 'yes',
84799                     bicycle: 'no',
84800                     horse: 'no'
84801                 },
84802                 trunk_link: {
84803                     motor_vehicle: 'yes'
84804                 },
84805                 primary_link: {
84806                     foot: 'yes',
84807                     motor_vehicle: 'yes',
84808                     bicycle: 'yes',
84809                     horse: 'yes'
84810                 },
84811                 secondary_link: {
84812                     foot: 'yes',
84813                     motor_vehicle: 'yes',
84814                     bicycle: 'yes',
84815                     horse: 'yes'
84816                 },
84817                 tertiary_link: {
84818                     foot: 'yes',
84819                     motor_vehicle: 'yes',
84820                     bicycle: 'yes',
84821                     horse: 'yes'
84822                 }
84823             };
84824
84825
84826             access.tags = function(tags) {
84827                 _tags = tags;
84828
84829                 utilGetSetValue(items.selectAll('.preset-input-access'), function(d) {
84830                         return typeof tags[d] === 'string' ? tags[d] : '';
84831                     })
84832                     .classed('mixed', function(d) {
84833                         return tags[d] && Array.isArray(tags[d]);
84834                     })
84835                     .attr('title', function(d) {
84836                         return tags[d] && Array.isArray(tags[d]) && tags[d].filter(Boolean).join('\n');
84837                     })
84838                     .attr('placeholder', function(d) {
84839                         if (tags[d] && Array.isArray(tags[d])) {
84840                             return _t('inspector.multiple_values');
84841                         }
84842                         if (d === 'access') {
84843                             return 'yes';
84844                         }
84845                         if (tags.access && typeof tags.access === 'string') {
84846                             return tags.access;
84847                         }
84848                         if (tags.highway) {
84849                             if (typeof tags.highway === 'string') {
84850                                 if (placeholdersByHighway[tags.highway] &&
84851                                     placeholdersByHighway[tags.highway][d]) {
84852
84853                                     return placeholdersByHighway[tags.highway][d];
84854                                 }
84855                             } else {
84856                                 var impliedAccesses = tags.highway.filter(Boolean).map(function(highwayVal) {
84857                                     return placeholdersByHighway[highwayVal] && placeholdersByHighway[highwayVal][d];
84858                                 }).filter(Boolean);
84859
84860                                 if (impliedAccesses.length === tags.highway.length &&
84861                                     new Set(impliedAccesses).size === 1) {
84862                                     // if all the highway values have the same implied access for this type then use that
84863                                     return impliedAccesses[0];
84864                                 }
84865                             }
84866                         }
84867                         return field.placeholder();
84868                     });
84869             };
84870
84871
84872             access.focus = function() {
84873                 items.selectAll('.preset-input-access')
84874                     .node().focus();
84875             };
84876
84877
84878             return utilRebind(access, dispatch$1, 'on');
84879         }
84880
84881         function uiFieldAddress(field, context) {
84882             var dispatch$1 = dispatch('change');
84883             var _selection = select(null);
84884             var _wrap = select(null);
84885             var addrField = _mainPresetIndex.field('address');   // needed for placeholder strings
84886
84887             var _entityIDs = [];
84888             var _tags;
84889             var _countryCode;
84890             var _addressFormats = [{
84891                 format: [
84892                     ['housenumber', 'street'],
84893                     ['city', 'postcode']
84894                 ]
84895               }];
84896
84897             _mainFileFetcher.get('address_formats')
84898                 .then(function(d) {
84899                     _addressFormats = d;
84900                     if (!_selection.empty()) {
84901                         _selection.call(address);
84902                     }
84903                 })
84904                 .catch(function() { /* ignore */ });
84905
84906
84907             function getNearStreets() {
84908                 var extent = combinedEntityExtent();
84909                 var l = extent.center();
84910                 var box = geoExtent(l).padByMeters(200);
84911
84912                 var streets = context.history().intersects(box)
84913                     .filter(isAddressable)
84914                     .map(function(d) {
84915                         var loc = context.projection([
84916                             (extent[0][0] + extent[1][0]) / 2,
84917                             (extent[0][1] + extent[1][1]) / 2
84918                         ]);
84919                         var choice = geoChooseEdge(context.graph().childNodes(d), loc, context.projection);
84920
84921                         return {
84922                             title: d.tags.name,
84923                             value: d.tags.name,
84924                             dist: choice.distance
84925                         };
84926                     })
84927                     .sort(function(a, b) {
84928                         return a.dist - b.dist;
84929                     });
84930
84931                 return utilArrayUniqBy(streets, 'value');
84932
84933                 function isAddressable(d) {
84934                     return d.tags.highway && d.tags.name && d.type === 'way';
84935                 }
84936             }
84937
84938
84939             function getNearCities() {
84940                 var extent = combinedEntityExtent();
84941                 var l = extent.center();
84942                 var box = geoExtent(l).padByMeters(200);
84943
84944                 var cities = context.history().intersects(box)
84945                     .filter(isAddressable)
84946                     .map(function(d) {
84947                         return {
84948                             title: d.tags['addr:city'] || d.tags.name,
84949                             value: d.tags['addr:city'] || d.tags.name,
84950                             dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
84951                         };
84952                     })
84953                     .sort(function(a, b) {
84954                         return a.dist - b.dist;
84955                     });
84956
84957                 return utilArrayUniqBy(cities, 'value');
84958
84959
84960                 function isAddressable(d) {
84961                     if (d.tags.name) {
84962                         if (d.tags.admin_level === '8' && d.tags.boundary === 'administrative')
84963                             { return true; }
84964                         if (d.tags.border_type === 'city')
84965                             { return true; }
84966                         if (d.tags.place === 'city' || d.tags.place === 'town' || d.tags.place === 'village')
84967                             { return true; }
84968                     }
84969
84970                     if (d.tags['addr:city'])
84971                         { return true; }
84972
84973                     return false;
84974                 }
84975             }
84976
84977             function getNearValues(key) {
84978                 var extent = combinedEntityExtent();
84979                 var l = extent.center();
84980                 var box = geoExtent(l).padByMeters(200);
84981
84982                 var results = context.history().intersects(box)
84983                     .filter(function hasTag(d) { return _entityIDs.indexOf(d.id) === -1 && d.tags[key]; })
84984                     .map(function(d) {
84985                         return {
84986                             title: d.tags[key],
84987                             value: d.tags[key],
84988                             dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
84989                         };
84990                     })
84991                     .sort(function(a, b) {
84992                         return a.dist - b.dist;
84993                     });
84994
84995                 return utilArrayUniqBy(results, 'value');
84996             }
84997
84998
84999             function updateForCountryCode() {
85000
85001                 if (!_countryCode) { return; }
85002
85003                 var addressFormat;
85004                 for (var i = 0; i < _addressFormats.length; i++) {
85005                     var format = _addressFormats[i];
85006                     if (!format.countryCodes) {
85007                         addressFormat = format;   // choose the default format, keep going
85008                     } else if (format.countryCodes.indexOf(_countryCode) !== -1) {
85009                         addressFormat = format;   // choose the country format, stop here
85010                         break;
85011                     }
85012                 }
85013
85014                 var dropdowns = addressFormat.dropdowns || [
85015                     'city', 'county', 'country', 'district', 'hamlet',
85016                     'neighbourhood', 'place', 'postcode', 'province',
85017                     'quarter', 'state', 'street', 'subdistrict', 'suburb'
85018                 ];
85019
85020                 var widths = addressFormat.widths || {
85021                     housenumber: 1/3, street: 2/3,
85022                     city: 2/3, state: 1/4, postcode: 1/3
85023                 };
85024
85025                 function row(r) {
85026                     // Normalize widths.
85027                     var total = r.reduce(function(sum, key) {
85028                         return sum + (widths[key] || 0.5);
85029                     }, 0);
85030
85031                     return r.map(function(key) {
85032                         return {
85033                             id: key,
85034                             width: (widths[key] || 0.5) / total
85035                         };
85036                     });
85037                 }
85038
85039                 var rows = _wrap.selectAll('.addr-row')
85040                     .data(addressFormat.format, function(d) {
85041                         return d.toString();
85042                     });
85043
85044                 rows.exit()
85045                     .remove();
85046
85047                 rows
85048                     .enter()
85049                     .append('div')
85050                     .attr('class', 'addr-row')
85051                     .selectAll('input')
85052                     .data(row)
85053                     .enter()
85054                     .append('input')
85055                     .property('type', 'text')
85056                     .call(updatePlaceholder)
85057                     .attr('class', function (d) { return 'addr-' + d.id; })
85058                     .call(utilNoAuto)
85059                     .each(addDropdown)
85060                     .style('width', function (d) { return d.width * 100 + '%'; });
85061
85062
85063                 function addDropdown(d) {
85064                     if (dropdowns.indexOf(d.id) === -1) { return; }  // not a dropdown
85065
85066                     var nearValues = (d.id === 'street') ? getNearStreets
85067                         : (d.id === 'city') ? getNearCities
85068                         : getNearValues;
85069
85070                     select(this)
85071                         .call(uiCombobox(context, 'address-' + d.id)
85072                             .minItems(1)
85073                             .caseSensitive(true)
85074                             .fetcher(function(value, callback) {
85075                                 callback(nearValues('addr:' + d.id));
85076                             })
85077                         );
85078                 }
85079
85080                 _wrap.selectAll('input')
85081                     .on('blur', change())
85082                     .on('change', change());
85083
85084                 _wrap.selectAll('input:not(.combobox-input)')
85085                     .on('input', change(true));
85086
85087                 if (_tags) { updateTags(_tags); }
85088             }
85089
85090
85091             function address(selection) {
85092                 _selection = selection;
85093
85094                 _wrap = selection.selectAll('.form-field-input-wrap')
85095                     .data([0]);
85096
85097                 _wrap = _wrap.enter()
85098                     .append('div')
85099                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85100                     .merge(_wrap);
85101
85102                 var extent = combinedEntityExtent();
85103
85104                 if (extent) {
85105                     var countryCode;
85106                     if (context.inIntro()) {
85107                         // localize the address format for the walkthrough
85108                         countryCode = _t('intro.graph.countrycode');
85109                     } else {
85110                         var center = extent.center();
85111                         countryCode = iso1A2Code(center);
85112                     }
85113                     if (countryCode) {
85114                         _countryCode = countryCode.toLowerCase();
85115                         updateForCountryCode();
85116                     }
85117                 }
85118             }
85119
85120
85121             function change(onInput) {
85122                 return function() {
85123                     var tags = {};
85124
85125                     _wrap.selectAll('input')
85126                         .each(function (subfield) {
85127                             var key = field.key + ':' + subfield.id;
85128
85129                             var value = this.value;
85130                             if (!onInput) { value = context.cleanTagValue(value); }
85131
85132                             // don't override multiple values with blank string
85133                             if (Array.isArray(_tags[key]) && !value) { return; }
85134
85135                             tags[key] = value || undefined;
85136                         });
85137
85138                     dispatch$1.call('change', this, tags, onInput);
85139                 };
85140             }
85141
85142             function updatePlaceholder(inputSelection) {
85143                 return inputSelection.attr('placeholder', function(subfield) {
85144                     if (_tags && Array.isArray(_tags[field.key + ':' + subfield.id])) {
85145                         return _t('inspector.multiple_values');
85146                     }
85147                     if (_countryCode) {
85148                         var localkey = subfield.id + '!' + _countryCode;
85149                         var tkey = addrField.strings.placeholders[localkey] ? localkey : subfield.id;
85150                         return addrField.t('placeholders.' + tkey);
85151                     }
85152                 });
85153             }
85154
85155
85156             function updateTags(tags) {
85157                 utilGetSetValue(_wrap.selectAll('input'), function (subfield) {
85158                         var val = tags[field.key + ':' + subfield.id];
85159                         return typeof val === 'string' ? val : '';
85160                     })
85161                     .attr('title', function(subfield) {
85162                         var val = tags[field.key + ':' + subfield.id];
85163                         return val && Array.isArray(val) && val.filter(Boolean).join('\n');
85164                     })
85165                     .classed('mixed', function(subfield) {
85166                         return Array.isArray(tags[field.key + ':' + subfield.id]);
85167                     })
85168                     .call(updatePlaceholder);
85169             }
85170
85171
85172             function combinedEntityExtent() {
85173                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
85174             }
85175
85176
85177             address.entityIDs = function(val) {
85178                 if (!arguments.length) { return _entityIDs; }
85179                 _entityIDs = val;
85180                 return address;
85181             };
85182
85183
85184             address.tags = function(tags) {
85185                 _tags = tags;
85186                 updateTags(tags);
85187             };
85188
85189
85190             address.focus = function() {
85191                 var node = _wrap.selectAll('input').node();
85192                 if (node) { node.focus(); }
85193             };
85194
85195
85196             return utilRebind(address, dispatch$1, 'on');
85197         }
85198
85199         function uiFieldCycleway(field, context) {
85200             var dispatch$1 = dispatch('change');
85201             var items = select(null);
85202             var wrap = select(null);
85203             var _tags;
85204
85205             function cycleway(selection) {
85206
85207                 function stripcolon(s) {
85208                     return s.replace(':', '');
85209                 }
85210
85211
85212                 wrap = selection.selectAll('.form-field-input-wrap')
85213                     .data([0]);
85214
85215                 wrap = wrap.enter()
85216                     .append('div')
85217                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85218                     .merge(wrap);
85219
85220
85221                 var div = wrap.selectAll('ul')
85222                     .data([0]);
85223
85224                 div = div.enter()
85225                     .append('ul')
85226                     .attr('class', 'rows')
85227                     .merge(div);
85228
85229                 var keys = ['cycleway:left', 'cycleway:right'];
85230
85231                 items = div.selectAll('li')
85232                     .data(keys);
85233
85234                 var enter = items.enter()
85235                     .append('li')
85236                     .attr('class', function(d) { return 'labeled-input preset-cycleway-' + stripcolon(d); });
85237
85238                 enter
85239                     .append('span')
85240                     .attr('class', 'label preset-label-cycleway')
85241                     .attr('for', function(d) { return 'preset-input-cycleway-' + stripcolon(d); })
85242                     .text(function(d) { return field.t('types.' + d); });
85243
85244                 enter
85245                     .append('div')
85246                     .attr('class', 'preset-input-cycleway-wrap')
85247                     .append('input')
85248                     .attr('type', 'text')
85249                     .attr('class', function(d) { return 'preset-input-cycleway preset-input-' + stripcolon(d); })
85250                     .call(utilNoAuto)
85251                     .each(function(d) {
85252                         select(this)
85253                             .call(uiCombobox(context, 'cycleway-' + stripcolon(d))
85254                                 .data(cycleway.options(d))
85255                             );
85256                     });
85257
85258                 items = items.merge(enter);
85259
85260                 // Update
85261                 wrap.selectAll('.preset-input-cycleway')
85262                     .on('change', change)
85263                     .on('blur', change);
85264             }
85265
85266
85267             function change(key) {
85268
85269                 var newValue = context.cleanTagValue(utilGetSetValue(select(this)));
85270
85271                 // don't override multiple values with blank string
85272                 if (!newValue && (Array.isArray(_tags.cycleway) || Array.isArray(_tags[key]))) { return; }
85273
85274                 if (newValue === 'none' || newValue === '') { newValue = undefined; }
85275
85276                 var otherKey = key === 'cycleway:left' ? 'cycleway:right' : 'cycleway:left';
85277                 var otherValue = typeof _tags.cycleway === 'string' ? _tags.cycleway : _tags[otherKey];
85278                 if (otherValue && Array.isArray(otherValue)) {
85279                     // we must always have an explicit value for comparison
85280                     otherValue = otherValue[0];
85281                 }
85282                 if (otherValue === 'none' || otherValue === '') { otherValue = undefined; }
85283
85284                 var tag = {};
85285
85286                 // If the left and right tags match, use the cycleway tag to tag both
85287                 // sides the same way
85288                 if (newValue === otherValue) {
85289                     tag = {
85290                         cycleway: newValue,
85291                         'cycleway:left': undefined,
85292                         'cycleway:right': undefined
85293                     };
85294                 } else {
85295                     // Always set both left and right as changing one can affect the other
85296                     tag = {
85297                         cycleway: undefined
85298                     };
85299                     tag[key] = newValue;
85300                     tag[otherKey] = otherValue;
85301                 }
85302
85303                 dispatch$1.call('change', this, tag);
85304             }
85305
85306
85307             cycleway.options = function() {
85308                 return Object.keys(field.strings.options).map(function(option) {
85309                     return {
85310                         title: field.t('options.' + option + '.description'),
85311                         value: option
85312                     };
85313                 });
85314             };
85315
85316
85317             cycleway.tags = function(tags) {
85318                 _tags = tags;
85319
85320                 // If cycleway is set, use that instead of individual values
85321                 var commonValue = typeof tags.cycleway === 'string' && tags.cycleway;
85322
85323                 utilGetSetValue(items.selectAll('.preset-input-cycleway'), function(d) {
85324                         if (commonValue) { return commonValue; }
85325                         return !tags.cycleway && typeof tags[d] === 'string' ? tags[d] : '';
85326                     })
85327                     .attr('title', function(d) {
85328                         if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
85329                             var vals = [];
85330                             if (Array.isArray(tags.cycleway)) {
85331                                 vals = vals.concat(tags.cycleway);
85332                             }
85333                             if (Array.isArray(tags[d])) {
85334                                 vals = vals.concat(tags[d]);
85335                             }
85336                             return vals.filter(Boolean).join('\n');
85337                         }
85338                         return null;
85339                     })
85340                     .attr('placeholder', function(d) {
85341                         if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
85342                             return _t('inspector.multiple_values');
85343                         }
85344                         return field.placeholder();
85345                     })
85346                     .classed('mixed', function(d) {
85347                         return Array.isArray(tags.cycleway) || Array.isArray(tags[d]);
85348                     });
85349             };
85350
85351
85352             cycleway.focus = function() {
85353                 var node = wrap.selectAll('input').node();
85354                 if (node) { node.focus(); }
85355             };
85356
85357
85358             return utilRebind(cycleway, dispatch$1, 'on');
85359         }
85360
85361         function uiFieldLanes(field, context) {
85362             var dispatch$1 = dispatch('change');
85363             var LANE_WIDTH = 40;
85364             var LANE_HEIGHT = 200;
85365             var _entityIDs = [];
85366
85367             function lanes(selection) {
85368                 var lanesData = context.entity(_entityIDs[0]).lanes();
85369
85370                 if (!context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
85371                     selection.call(lanes.off);
85372                     return;
85373                 }
85374
85375                 var wrap = selection.selectAll('.form-field-input-wrap')
85376                     .data([0]);
85377
85378                 wrap = wrap.enter()
85379                     .append('div')
85380                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85381                     .merge(wrap);
85382
85383                 var surface =  wrap.selectAll('.surface')
85384                     .data([0]);
85385
85386                 var d = utilGetDimensions(wrap);
85387                 var freeSpace = d[0] - lanesData.lanes.length * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5;
85388
85389                 surface = surface.enter()
85390                     .append('svg')
85391                     .attr('width', d[0])
85392                     .attr('height', 300)
85393                     .attr('class', 'surface')
85394                     .merge(surface);
85395
85396
85397                 var lanesSelection = surface.selectAll('.lanes')
85398                     .data([0]);
85399
85400                 lanesSelection = lanesSelection.enter()
85401                     .append('g')
85402                     .attr('class', 'lanes')
85403                     .merge(lanesSelection);
85404
85405                 lanesSelection
85406                     .attr('transform', function () {
85407                         return 'translate(' + (freeSpace / 2) + ', 0)';
85408                     });
85409
85410
85411                 var lane = lanesSelection.selectAll('.lane')
85412                    .data(lanesData.lanes);
85413
85414                 lane.exit()
85415                     .remove();
85416
85417                 var enter = lane.enter()
85418                     .append('g')
85419                     .attr('class', 'lane');
85420
85421                 enter
85422                     .append('g')
85423                     .append('rect')
85424                     .attr('y', 50)
85425                     .attr('width', LANE_WIDTH)
85426                     .attr('height', LANE_HEIGHT);
85427
85428                 enter
85429                     .append('g')
85430                     .attr('class', 'forward')
85431                     .append('text')
85432                     .attr('y', 40)
85433                     .attr('x', 14)
85434                     .text('▲');
85435
85436                 enter
85437                     .append('g')
85438                     .attr('class', 'bothways')
85439                     .append('text')
85440                     .attr('y', 40)
85441                     .attr('x', 14)
85442                     .text('▲▼');
85443
85444                 enter
85445                     .append('g')
85446                     .attr('class', 'backward')
85447                     .append('text')
85448                     .attr('y', 40)
85449                     .attr('x', 14)
85450                     .text('▼');
85451
85452
85453                 lane = lane
85454                     .merge(enter);
85455
85456                 lane
85457                     .attr('transform', function(d) {
85458                         return 'translate(' + (LANE_WIDTH * d.index * 1.5) + ', 0)';
85459                     });
85460
85461                 lane.select('.forward')
85462                     .style('visibility', function(d) {
85463                         return d.direction === 'forward' ? 'visible' : 'hidden';
85464                     });
85465
85466                 lane.select('.bothways')
85467                     .style('visibility', function(d) {
85468                         return d.direction === 'bothways' ? 'visible' : 'hidden';
85469                     });
85470
85471                 lane.select('.backward')
85472                     .style('visibility', function(d) {
85473                         return d.direction === 'backward' ? 'visible' : 'hidden';
85474                     });
85475             }
85476
85477
85478             lanes.entityIDs = function(val) {
85479                 _entityIDs = val;
85480             };
85481
85482             lanes.tags = function() {};
85483             lanes.focus = function() {};
85484             lanes.off = function() {};
85485
85486             return utilRebind(lanes, dispatch$1, 'on');
85487         }
85488
85489         uiFieldLanes.supportsMultiselection = false;
85490
85491         var _languagesArray = [];
85492
85493
85494         function uiFieldLocalized(field, context) {
85495             var dispatch$1 = dispatch('change', 'input');
85496             var wikipedia = services.wikipedia;
85497             var input = select(null);
85498             var localizedInputs = select(null);
85499             var _countryCode;
85500             var _tags;
85501
85502
85503             // A concern here in switching to async data means that _languagesArray will not
85504             // be available the first time through, so things like the fetchers and
85505             // the language() function will not work immediately.
85506             _mainFileFetcher.get('languages')
85507                 .then(loadLanguagesArray)
85508                 .catch(function() { /* ignore */ });
85509
85510             var _territoryLanguages = {};
85511             _mainFileFetcher.get('territory_languages')
85512                 .then(function(d) { _territoryLanguages = d; })
85513                 .catch(function() { /* ignore */ });
85514
85515
85516             var allSuggestions = _mainPresetIndex.collection.filter(function(p) {
85517                 return p.suggestion === true;
85518             });
85519
85520             // reuse these combos
85521             var langCombo = uiCombobox(context, 'localized-lang')
85522                 .fetcher(fetchLanguages)
85523                 .minItems(0);
85524
85525             var brandCombo = uiCombobox(context, 'localized-brand')
85526                 .canAutocomplete(false)
85527                 .minItems(1);
85528
85529             var _selection = select(null);
85530             var _multilingual = [];
85531             var _buttonTip = uiTooltip()
85532                 .title(_t('translate.translate'))
85533                 .placement('left');
85534             var _wikiTitles;
85535             var _entityIDs = [];
85536
85537
85538             function loadLanguagesArray(dataLanguages) {
85539                 if (_languagesArray.length !== 0) { return; }
85540
85541                 // some conversion is needed to ensure correct OSM tags are used
85542                 var replacements = {
85543                     sr: 'sr-Cyrl',      // in OSM, `sr` implies Cyrillic
85544                     'sr-Cyrl': false    // `sr-Cyrl` isn't used in OSM
85545                 };
85546
85547                 for (var code in dataLanguages) {
85548                     if (replacements[code] === false) { continue; }
85549                     var metaCode = code;
85550                     if (replacements[code]) { metaCode = replacements[code]; }
85551
85552                     _languagesArray.push({
85553                         localName: _mainLocalizer.languageName(metaCode, { localOnly: true }),
85554                         nativeName: dataLanguages[metaCode].nativeName,
85555                         code: code,
85556                         label: _mainLocalizer.languageName(metaCode)
85557                     });
85558                 }
85559             }
85560
85561
85562             function calcLocked() {
85563
85564                 // only lock the Name field
85565                 var isLocked = field.id === 'name' &&
85566                     _entityIDs.length &&
85567                     // lock the field if any feature needs it
85568                     _entityIDs.some(function(entityID) {
85569
85570                         var entity = context.graph().hasEntity(entityID);
85571                         if (!entity) { return false; }
85572
85573                         var original = context.graph().base().entities[_entityIDs[0]];
85574                         var hasOriginalName = original && entity.tags.name && entity.tags.name === original.tags.name;
85575                         // if the name was already edited manually then allow further editing
85576                         if (!hasOriginalName) { return false; }
85577
85578                         // features linked to Wikidata are likely important and should be protected
85579                         if (entity.tags.wikidata) { return true; }
85580
85581                         // assume the name has already been confirmed if its source has been researched
85582                         if (entity.tags['name:etymology:wikidata']) { return true; }
85583
85584                         var preset = _mainPresetIndex.match(entity, context.graph());
85585                         var isSuggestion = preset && preset.suggestion;
85586                         var showsBrand = preset && preset.originalFields.filter(function(d) {
85587                             return d.id === 'brand';
85588                         }).length;
85589                         // protect standardized brand names
85590                         return isSuggestion && !showsBrand;
85591                     });
85592
85593                 field.locked(isLocked);
85594             }
85595
85596
85597             // update _multilingual, maintaining the existing order
85598             function calcMultilingual(tags) {
85599                 var existingLangsOrdered = _multilingual.map(function(item) {
85600                     return item.lang;
85601                 });
85602                 var existingLangs = new Set(existingLangsOrdered.filter(Boolean));
85603
85604                 for (var k in tags) {
85605                     var m = k.match(/^(.*):([a-zA-Z_-]+)$/);
85606                     if (m && m[1] === field.key && m[2]) {
85607                         var item = { lang: m[2], value: tags[k] };
85608                         if (existingLangs.has(item.lang)) {
85609                             // update the value
85610                             _multilingual[existingLangsOrdered.indexOf(item.lang)].value = item.value;
85611                             existingLangs.delete(item.lang);
85612                         } else {
85613                             _multilingual.push(item);
85614                         }
85615                     }
85616                 }
85617
85618                 _multilingual = _multilingual.filter(function(item) {
85619                     return !item.lang || !existingLangs.has(item.lang);
85620                 });
85621             }
85622
85623
85624             function localized(selection) {
85625                 _selection = selection;
85626                 calcLocked();
85627                 var isLocked = field.locked();
85628                 var singularEntity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85629                 var preset = singularEntity && _mainPresetIndex.match(singularEntity, context.graph());
85630
85631                 var wrap = selection.selectAll('.form-field-input-wrap')
85632                     .data([0]);
85633
85634                 // enter/update
85635                 wrap = wrap.enter()
85636                     .append('div')
85637                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85638                     .merge(wrap);
85639
85640                 input = wrap.selectAll('.localized-main')
85641                     .data([0]);
85642
85643                 // enter/update
85644                 input = input.enter()
85645                     .append('input')
85646                     .attr('type', 'text')
85647                     .attr('id', field.domId)
85648                     .attr('class', 'localized-main')
85649                     .call(utilNoAuto)
85650                     .merge(input);
85651
85652                 if (preset && field.id === 'name') {
85653                     var pTag = preset.id.split('/', 2);
85654                     var pKey = pTag[0];
85655                     var pValue = pTag[1];
85656
85657                     if (!preset.suggestion) {
85658                         // Not a suggestion preset - Add a suggestions dropdown if it makes sense to.
85659                         // This code attempts to determine if the matched preset is the
85660                         // kind of preset that even can benefit from name suggestions..
85661                         // - true = shops, cafes, hotels, etc. (also generic and fallback presets)
85662                         // - false = churches, parks, hospitals, etc. (things not in the index)
85663                         var isFallback = preset.isFallback();
85664                         var goodSuggestions = allSuggestions.filter(function(s) {
85665                             if (isFallback) { return true; }
85666                             var sTag = s.id.split('/', 2);
85667                             var sKey = sTag[0];
85668                             var sValue = sTag[1];
85669                             return pKey === sKey && (!pValue || pValue === sValue);
85670                         });
85671
85672                         // Show the suggestions.. If the user picks one, change the tags..
85673                         if (allSuggestions.length && goodSuggestions.length) {
85674                             input
85675                                 .on('blur.localized', checkBrandOnBlur)
85676                                 .call(brandCombo
85677                                     .fetcher(fetchBrandNames(preset, allSuggestions))
85678                                     .on('accept', acceptBrand)
85679                                     .on('cancel', cancelBrand)
85680                                 );
85681                         }
85682                     }
85683                 }
85684
85685                 input
85686                     .classed('disabled', !!isLocked)
85687                     .attr('readonly', isLocked || null)
85688                     .on('input', change(true))
85689                     .on('blur', change())
85690                     .on('change', change());
85691
85692
85693                 var translateButton = wrap.selectAll('.localized-add')
85694                     .data([0]);
85695
85696                 translateButton = translateButton.enter()
85697                     .append('button')
85698                     .attr('class', 'localized-add form-field-button')
85699                     .attr('tabindex', -1)
85700                     .call(svgIcon('#iD-icon-plus'))
85701                     .merge(translateButton);
85702
85703                 translateButton
85704                     .classed('disabled', !!isLocked)
85705                     .call(isLocked ? _buttonTip.destroy : _buttonTip)
85706                     .on('click', addNew);
85707
85708
85709                 if (_tags && !_multilingual.length) {
85710                     calcMultilingual(_tags);
85711                 }
85712
85713                 localizedInputs = selection.selectAll('.localized-multilingual')
85714                     .data([0]);
85715
85716                 localizedInputs = localizedInputs.enter()
85717                     .append('div')
85718                     .attr('class', 'localized-multilingual')
85719                     .merge(localizedInputs);
85720
85721                 localizedInputs
85722                     .call(renderMultilingual);
85723
85724                 localizedInputs.selectAll('button, input')
85725                     .classed('disabled', !!isLocked)
85726                     .attr('readonly', isLocked || null);
85727
85728
85729
85730                 // We are not guaranteed to get an `accept` or `cancel` when blurring the field.
85731                 // (This can happen if the user actives the combo, arrows down, and then clicks off to blur)
85732                 // So compare the current field value against the suggestions one last time.
85733                 function checkBrandOnBlur() {
85734                     var latest = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85735                     if (!latest) { return; }   // deleting the entity blurred the field?
85736
85737                     var preset = _mainPresetIndex.match(latest, context.graph());
85738                     if (preset && preset.suggestion) { return; }   // already accepted
85739
85740                     // note: here we are testing against "decorated" names, i.e. 'Starbucks – Cafe'
85741                     var name = utilGetSetValue(input).trim();
85742                     var matched = allSuggestions.filter(function(s) { return name === s.name(); });
85743
85744                     if (matched.length === 1) {
85745                         acceptBrand({ suggestion: matched[0] });
85746                     } else {
85747                         cancelBrand();
85748                     }
85749                 }
85750
85751
85752                 function acceptBrand(d) {
85753
85754                     var entity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85755
85756                     if (!d || !entity) {
85757                         cancelBrand();
85758                         return;
85759                     }
85760
85761                     var tags = entity.tags;
85762                     var geometry = entity.geometry(context.graph());
85763                     var removed = preset.unsetTags(tags, geometry);
85764                     for (var k in tags) {
85765                         tags[k] = removed[k];  // set removed tags to `undefined`
85766                     }
85767                     tags = d.suggestion.setTags(tags, geometry);
85768                     utilGetSetValue(input, tags.name);
85769                     dispatch$1.call('change', this, tags);
85770                 }
85771
85772
85773                 // user hit escape, clean whatever preset name appears after the last ' – '
85774                 function cancelBrand() {
85775                     var name = utilGetSetValue(input);
85776                     var clean = cleanName(name);
85777                     if (clean !== name) {
85778                         utilGetSetValue(input, clean);
85779                         dispatch$1.call('change', this, { name: clean });
85780                     }
85781                 }
85782
85783                 // Remove whatever is after the last ' – '
85784                 // NOTE: split/join on en-dash, not a hypen (to avoid conflict with fr - nl names in Brussels etc)
85785                 function cleanName(name) {
85786                     var parts = name.split(' – ');
85787                     if (parts.length > 1) {
85788                         parts.pop();
85789                         name = parts.join(' – ');
85790                     }
85791                     return name;
85792                 }
85793
85794
85795                 function fetchBrandNames(preset, suggestions) {
85796                     var pTag = preset.id.split('/', 2);
85797                     var pKey = pTag[0];
85798                     var pValue = pTag[1];
85799
85800                     return function(value, callback) {
85801                         var results = [];
85802                         if (value && value.length > 2) {
85803                             for (var i = 0; i < suggestions.length; i++) {
85804                                 var s = suggestions[i];
85805
85806                                 // don't suggest brands from incompatible countries
85807                                 if (_countryCode && s.countryCodes &&
85808                                     s.countryCodes.indexOf(_countryCode) === -1) { continue; }
85809
85810                                 var sTag = s.id.split('/', 2);
85811                                 var sKey = sTag[0];
85812                                 var sValue = sTag[1];
85813                                 var name = s.name();
85814                                 var dist = utilEditDistance(value, name.substring(0, value.length));
85815                                 var matchesPreset = (pKey === sKey && (!pValue || pValue === sValue));
85816
85817                                 if (dist < 1 || (matchesPreset && dist < 3)) {
85818                                     var obj = {
85819                                         title: name,
85820                                         value: name,
85821                                         suggestion: s,
85822                                         dist: dist + (matchesPreset ? 0 : 1)  // penalize if not matched preset
85823                                     };
85824                                     results.push(obj);
85825                                 }
85826                             }
85827                             results.sort(function(a, b) { return a.dist - b.dist; });
85828                         }
85829                         results = results.slice(0, 10);
85830                         callback(results);
85831                     };
85832                 }
85833
85834
85835                 function addNew() {
85836                     event.preventDefault();
85837                     if (field.locked()) { return; }
85838
85839                     var defaultLang = _mainLocalizer.languageCode().toLowerCase();
85840                     var langExists = _multilingual.find(function(datum) { return datum.lang === defaultLang; });
85841                     var isLangEn = defaultLang.indexOf('en') > -1;
85842                     if (isLangEn || langExists) {
85843                         defaultLang = '';
85844                         langExists = _multilingual.find(function(datum) { return datum.lang === defaultLang; });
85845                     }
85846
85847                     if (!langExists) {
85848                         // prepend the value so it appears at the top
85849                         _multilingual.unshift({ lang: defaultLang, value: '' });
85850
85851                         localizedInputs
85852                             .call(renderMultilingual);
85853                     }
85854                 }
85855
85856
85857                 function change(onInput) {
85858                     return function() {
85859                         if (field.locked()) {
85860                             event.preventDefault();
85861                             return;
85862                         }
85863
85864                         var val = utilGetSetValue(select(this));
85865                         if (!onInput) { val = context.cleanTagValue(val); }
85866
85867                         // don't override multiple values with blank string
85868                         if (!val && Array.isArray(_tags[field.key])) { return; }
85869
85870                         var t = {};
85871
85872                         t[field.key] = val || undefined;
85873                         dispatch$1.call('change', this, t, onInput);
85874                     };
85875                 }
85876             }
85877
85878
85879             function key(lang) {
85880                 return field.key + ':' + lang;
85881             }
85882
85883
85884             function changeLang(d) {
85885                 var tags = {};
85886
85887                 // make sure unrecognized suffixes are lowercase - #7156
85888                 var lang = utilGetSetValue(select(this)).toLowerCase();
85889
85890                 var language = _languagesArray.find(function(d) {
85891                     return d.label.toLowerCase() === lang ||
85892                         (d.localName && d.localName.toLowerCase() === lang) ||
85893                         (d.nativeName && d.nativeName.toLowerCase() === lang);
85894                 });
85895                 if (language) { lang = language.code; }
85896
85897                 if (d.lang && d.lang !== lang) {
85898                     tags[key(d.lang)] = undefined;
85899                 }
85900
85901                 var newKey = lang && context.cleanTagKey(key(lang));
85902
85903                 var value = utilGetSetValue(select(this.parentNode).selectAll('.localized-value'));
85904
85905                 if (newKey && value) {
85906                     tags[newKey] = value;
85907                 } else if (newKey && _wikiTitles && _wikiTitles[d.lang]) {
85908                     tags[newKey] = _wikiTitles[d.lang];
85909                 }
85910
85911                 d.lang = lang;
85912                 dispatch$1.call('change', this, tags);
85913             }
85914
85915
85916             function changeValue(d) {
85917                 if (!d.lang) { return; }
85918                 var value = context.cleanTagValue(utilGetSetValue(select(this))) || undefined;
85919
85920                 // don't override multiple values with blank string
85921                 if (!value && Array.isArray(d.value)) { return; }
85922
85923                 var t = {};
85924                 t[key(d.lang)] = value;
85925                 d.value = value;
85926                 dispatch$1.call('change', this, t);
85927             }
85928
85929
85930             function fetchLanguages(value, cb) {
85931                 var v = value.toLowerCase();
85932
85933                 // show the user's language first
85934                 var langCodes = [_mainLocalizer.localeCode(), _mainLocalizer.languageCode()];
85935
85936                 if (_countryCode && _territoryLanguages[_countryCode]) {
85937                     langCodes = langCodes.concat(_territoryLanguages[_countryCode]);
85938                 }
85939
85940                 var langItems = [];
85941                 langCodes.forEach(function(code) {
85942                     var langItem = _languagesArray.find(function(item) {
85943                         return item.code === code;
85944                     });
85945                     if (langItem) { langItems.push(langItem); }
85946                 });
85947                 langItems = utilArrayUniq(langItems.concat(_languagesArray));
85948
85949                 cb(langItems.filter(function(d) {
85950                     return d.label.toLowerCase().indexOf(v) >= 0 ||
85951                         (d.localName && d.localName.toLowerCase().indexOf(v) >= 0) ||
85952                         (d.nativeName && d.nativeName.toLowerCase().indexOf(v) >= 0) ||
85953                         d.code.toLowerCase().indexOf(v) >= 0;
85954                 }).map(function(d) {
85955                     return { value: d.label };
85956                 }));
85957             }
85958
85959
85960             function renderMultilingual(selection) {
85961                 var entries = selection.selectAll('div.entry')
85962                     .data(_multilingual, function(d) { return d.lang; });
85963
85964                 entries.exit()
85965                     .style('top', '0')
85966                     .style('max-height', '240px')
85967                     .transition()
85968                     .duration(200)
85969                     .style('opacity', '0')
85970                     .style('max-height', '0px')
85971                     .remove();
85972
85973                 var entriesEnter = entries.enter()
85974                     .append('div')
85975                     .attr('class', 'entry')
85976                     .each(function(_, index) {
85977                         var wrap = select(this);
85978
85979                         var domId = utilUniqueDomId(index);
85980
85981                         var label = wrap
85982                             .append('label')
85983                             .attr('class', 'field-label')
85984                             .attr('for', domId);
85985
85986                         var text = label
85987                             .append('span')
85988                             .attr('class', 'label-text');
85989
85990                         text
85991                             .append('span')
85992                             .attr('class', 'label-textvalue')
85993                             .text(_t('translate.localized_translation_label'));
85994
85995                         text
85996                             .append('span')
85997                             .attr('class', 'label-textannotation');
85998
85999                         label
86000                             .append('button')
86001                             .attr('class', 'remove-icon-multilingual')
86002                             .on('click', function(d, index) {
86003                                 if (field.locked()) { return; }
86004                                 event.preventDefault();
86005
86006                                 if (!d.lang || !d.value) {
86007                                     _multilingual.splice(index, 1);
86008                                     renderMultilingual(selection);
86009                                 } else {
86010                                     // remove from entity tags
86011                                     var t = {};
86012                                     t[key(d.lang)] = undefined;
86013                                     dispatch$1.call('change', this, t);
86014                                 }
86015
86016                             })
86017                             .call(svgIcon('#iD-operation-delete'));
86018
86019                         wrap
86020                             .append('input')
86021                             .attr('class', 'localized-lang')
86022                             .attr('id', domId)
86023                             .attr('type', 'text')
86024                             .attr('placeholder', _t('translate.localized_translation_language'))
86025                             .on('blur', changeLang)
86026                             .on('change', changeLang)
86027                             .call(langCombo);
86028
86029                         wrap
86030                             .append('input')
86031                             .attr('type', 'text')
86032                             .attr('class', 'localized-value')
86033                             .on('blur', changeValue)
86034                             .on('change', changeValue);
86035                     });
86036
86037                 entriesEnter
86038                     .style('margin-top', '0px')
86039                     .style('max-height', '0px')
86040                     .style('opacity', '0')
86041                     .transition()
86042                     .duration(200)
86043                     .style('margin-top', '10px')
86044                     .style('max-height', '240px')
86045                     .style('opacity', '1')
86046                     .on('end', function() {
86047                         select(this)
86048                             .style('max-height', '')
86049                             .style('overflow', 'visible');
86050                     });
86051
86052                 entries = entries.merge(entriesEnter);
86053
86054                 entries.order();
86055
86056                 entries.classed('present', function(d) {
86057                     return d.lang && d.value;
86058                 });
86059
86060                 utilGetSetValue(entries.select('.localized-lang'), function(d) {
86061                     return _mainLocalizer.languageName(d.lang);
86062                 });
86063
86064                 utilGetSetValue(entries.select('.localized-value'), function(d) {
86065                         return typeof d.value === 'string' ? d.value : '';
86066                     })
86067                     .attr('title', function(d) {
86068                         return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : null;
86069                     })
86070                     .attr('placeholder', function(d) {
86071                         return Array.isArray(d.value) ? _t('inspector.multiple_values') : _t('translate.localized_translation_name');
86072                     })
86073                     .classed('mixed', function(d) {
86074                         return Array.isArray(d.value);
86075                     });
86076             }
86077
86078
86079             localized.tags = function(tags) {
86080                 _tags = tags;
86081
86082                 // Fetch translations from wikipedia
86083                 if (typeof tags.wikipedia === 'string' && !_wikiTitles) {
86084                     _wikiTitles = {};
86085                     var wm = tags.wikipedia.match(/([^:]+):(.+)/);
86086                     if (wm && wm[0] && wm[1]) {
86087                         wikipedia.translations(wm[1], wm[2], function(err, d) {
86088                             if (err || !d) { return; }
86089                             _wikiTitles = d;
86090                         });
86091                     }
86092                 }
86093
86094                 var isMixed = Array.isArray(tags[field.key]);
86095
86096                 utilGetSetValue(input, typeof tags[field.key] === 'string' ? tags[field.key] : '')
86097                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined)
86098                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder())
86099                     .classed('mixed', isMixed);
86100
86101                 calcMultilingual(tags);
86102
86103                 _selection
86104                     .call(localized);
86105             };
86106
86107
86108             localized.focus = function() {
86109                 input.node().focus();
86110             };
86111
86112
86113             localized.entityIDs = function(val) {
86114                 if (!arguments.length) { return _entityIDs; }
86115                 _entityIDs = val;
86116                 _multilingual = [];
86117                 loadCountryCode();
86118                 return localized;
86119             };
86120
86121             function loadCountryCode() {
86122                 var extent = combinedEntityExtent();
86123                 var countryCode = extent && iso1A2Code(extent.center());
86124                 _countryCode = countryCode && countryCode.toLowerCase();
86125             }
86126
86127             function combinedEntityExtent() {
86128                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
86129             }
86130
86131             return utilRebind(localized, dispatch$1, 'on');
86132         }
86133
86134         function uiFieldMaxspeed(field, context) {
86135             var dispatch$1 = dispatch('change');
86136             var unitInput = select(null);
86137             var input = select(null);
86138             var _entityIDs = [];
86139             var _tags;
86140             var _isImperial;
86141
86142             var speedCombo = uiCombobox(context, 'maxspeed');
86143             var unitCombo = uiCombobox(context, 'maxspeed-unit')
86144                     .data(['km/h', 'mph'].map(comboValues));
86145
86146             var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120];
86147             var imperialValues = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80];
86148
86149
86150             function maxspeed(selection) {
86151
86152                 var wrap = selection.selectAll('.form-field-input-wrap')
86153                     .data([0]);
86154
86155                 wrap = wrap.enter()
86156                     .append('div')
86157                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
86158                     .merge(wrap);
86159
86160
86161                 input = wrap.selectAll('input.maxspeed-number')
86162                     .data([0]);
86163
86164                 input = input.enter()
86165                     .append('input')
86166                     .attr('type', 'text')
86167                     .attr('class', 'maxspeed-number')
86168                     .attr('id', field.domId)
86169                     .call(utilNoAuto)
86170                     .call(speedCombo)
86171                     .merge(input);
86172
86173                 input
86174                     .on('change', change)
86175                     .on('blur', change);
86176
86177                 var loc = combinedEntityExtent().center();
86178                 _isImperial = roadSpeedUnit(loc) === 'mph';
86179
86180                 unitInput = wrap.selectAll('input.maxspeed-unit')
86181                     .data([0]);
86182
86183                 unitInput = unitInput.enter()
86184                     .append('input')
86185                     .attr('type', 'text')
86186                     .attr('class', 'maxspeed-unit')
86187                     .call(unitCombo)
86188                     .merge(unitInput);
86189
86190                 unitInput
86191                     .on('blur', changeUnits)
86192                     .on('change', changeUnits);
86193
86194
86195                 function changeUnits() {
86196                     _isImperial = utilGetSetValue(unitInput) === 'mph';
86197                     utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
86198                     setUnitSuggestions();
86199                     change();
86200                 }
86201             }
86202
86203
86204             function setUnitSuggestions() {
86205                 speedCombo.data((_isImperial ? imperialValues : metricValues).map(comboValues));
86206                 utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
86207             }
86208
86209
86210             function comboValues(d) {
86211                 return {
86212                     value: d.toString(),
86213                     title: d.toString()
86214                 };
86215             }
86216
86217
86218             function change() {
86219                 var tag = {};
86220                 var value = utilGetSetValue(input).trim();
86221
86222                 // don't override multiple values with blank string
86223                 if (!value && Array.isArray(_tags[field.key])) { return; }
86224
86225                 if (!value) {
86226                     tag[field.key] = undefined;
86227                 } else if (isNaN(value) || !_isImperial) {
86228                     tag[field.key] = context.cleanTagValue(value);
86229                 } else {
86230                     tag[field.key] = context.cleanTagValue(value + ' mph');
86231                 }
86232
86233                 dispatch$1.call('change', this, tag);
86234             }
86235
86236
86237             maxspeed.tags = function(tags) {
86238                 _tags = tags;
86239
86240                 var value = tags[field.key];
86241                 var isMixed = Array.isArray(value);
86242
86243                 if (!isMixed) {
86244                     if (value && value.indexOf('mph') >= 0) {
86245                         value = parseInt(value, 10).toString();
86246                         _isImperial = true;
86247                     } else if (value) {
86248                         _isImperial = false;
86249                     }
86250                 }
86251
86252                 setUnitSuggestions();
86253
86254                 utilGetSetValue(input, typeof value === 'string' ? value : '')
86255                     .attr('title', isMixed ? value.filter(Boolean).join('\n') : null)
86256                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder())
86257                     .classed('mixed', isMixed);
86258             };
86259
86260
86261             maxspeed.focus = function() {
86262                 input.node().focus();
86263             };
86264
86265
86266             maxspeed.entityIDs = function(val) {
86267                 _entityIDs = val;
86268             };
86269
86270
86271             function combinedEntityExtent() {
86272                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
86273             }
86274
86275
86276             return utilRebind(maxspeed, dispatch$1, 'on');
86277         }
86278
86279         function uiFieldRadio(field, context) {
86280             var dispatch$1 = dispatch('change');
86281             var placeholder = select(null);
86282             var wrap = select(null);
86283             var labels = select(null);
86284             var radios = select(null);
86285             var radioData = (field.options || (field.strings && field.strings.options && Object.keys(field.strings.options)) || field.keys).slice();  // shallow copy
86286             var typeField;
86287             var layerField;
86288             var _oldType = {};
86289             var _entityIDs = [];
86290
86291
86292             function selectedKey() {
86293                 var node = wrap.selectAll('.form-field-input-radio label.active input');
86294                 return !node.empty() && node.datum();
86295             }
86296
86297
86298             function radio(selection) {
86299                 selection.classed('preset-radio', true);
86300
86301                 wrap = selection.selectAll('.form-field-input-wrap')
86302                     .data([0]);
86303
86304                 var enter = wrap.enter()
86305                     .append('div')
86306                     .attr('class', 'form-field-input-wrap form-field-input-radio');
86307
86308                 enter
86309                     .append('span')
86310                     .attr('class', 'placeholder');
86311
86312                 wrap = wrap
86313                     .merge(enter);
86314
86315
86316                 placeholder = wrap.selectAll('.placeholder');
86317
86318                 labels = wrap.selectAll('label')
86319                     .data(radioData);
86320
86321                 enter = labels.enter()
86322                     .append('label');
86323
86324                 enter
86325                     .append('input')
86326                     .attr('type', 'radio')
86327                     .attr('name', field.id)
86328                     .attr('value', function(d) { return field.t('options.' + d, { 'default': d }); })
86329                     .attr('checked', false);
86330
86331                 enter
86332                     .append('span')
86333                     .text(function(d) { return field.t('options.' + d, { 'default': d }); });
86334
86335                 labels = labels
86336                     .merge(enter);
86337
86338                 radios = labels.selectAll('input')
86339                     .on('change', changeRadio);
86340
86341             }
86342
86343
86344             function structureExtras(selection, tags) {
86345                 var selected = selectedKey() || tags.layer !== undefined;
86346                 var type = _mainPresetIndex.field(selected);
86347                 var layer = _mainPresetIndex.field('layer');
86348                 var showLayer = (selected === 'bridge' || selected === 'tunnel' || tags.layer !== undefined);
86349
86350
86351                 var extrasWrap = selection.selectAll('.structure-extras-wrap')
86352                     .data(selected ? [0] : []);
86353
86354                 extrasWrap.exit()
86355                     .remove();
86356
86357                 extrasWrap = extrasWrap.enter()
86358                     .append('div')
86359                     .attr('class', 'structure-extras-wrap')
86360                     .merge(extrasWrap);
86361
86362                 var list = extrasWrap.selectAll('ul')
86363                     .data([0]);
86364
86365                 list = list.enter()
86366                     .append('ul')
86367                     .attr('class', 'rows')
86368                     .merge(list);
86369
86370
86371                 // Type
86372                 if (type) {
86373                     if (!typeField || typeField.id !== selected) {
86374                         typeField = uiField(context, type, _entityIDs, { wrap: false })
86375                             .on('change', changeType);
86376                     }
86377                     typeField.tags(tags);
86378                 } else {
86379                     typeField = null;
86380                 }
86381
86382                 var typeItem = list.selectAll('.structure-type-item')
86383                     .data(typeField ? [typeField] : [], function(d) { return d.id; });
86384
86385                 // Exit
86386                 typeItem.exit()
86387                     .remove();
86388
86389                 // Enter
86390                 var typeEnter = typeItem.enter()
86391                     .insert('li', ':first-child')
86392                     .attr('class', 'labeled-input structure-type-item');
86393
86394                 typeEnter
86395                     .append('span')
86396                     .attr('class', 'label structure-label-type')
86397                     .attr('for', 'preset-input-' + selected)
86398                     .text(_t('inspector.radio.structure.type'));
86399
86400                 typeEnter
86401                     .append('div')
86402                     .attr('class', 'structure-input-type-wrap');
86403
86404                 // Update
86405                 typeItem = typeItem
86406                     .merge(typeEnter);
86407
86408                 if (typeField) {
86409                     typeItem.selectAll('.structure-input-type-wrap')
86410                         .call(typeField.render);
86411                 }
86412
86413
86414                 // Layer
86415                 if (layer && showLayer) {
86416                     if (!layerField) {
86417                         layerField = uiField(context, layer, _entityIDs, { wrap: false })
86418                             .on('change', changeLayer);
86419                     }
86420                     layerField.tags(tags);
86421                     field.keys = utilArrayUnion(field.keys, ['layer']);
86422                 } else {
86423                     layerField = null;
86424                     field.keys = field.keys.filter(function(k) { return k !== 'layer'; });
86425                 }
86426
86427                 var layerItem = list.selectAll('.structure-layer-item')
86428                     .data(layerField ? [layerField] : []);
86429
86430                 // Exit
86431                 layerItem.exit()
86432                     .remove();
86433
86434                 // Enter
86435                 var layerEnter = layerItem.enter()
86436                     .append('li')
86437                     .attr('class', 'labeled-input structure-layer-item');
86438
86439                 layerEnter
86440                     .append('span')
86441                     .attr('class', 'label structure-label-layer')
86442                     .attr('for', 'preset-input-layer')
86443                     .text(_t('inspector.radio.structure.layer'));
86444
86445                 layerEnter
86446                     .append('div')
86447                     .attr('class', 'structure-input-layer-wrap');
86448
86449                 // Update
86450                 layerItem = layerItem
86451                     .merge(layerEnter);
86452
86453                 if (layerField) {
86454                     layerItem.selectAll('.structure-input-layer-wrap')
86455                         .call(layerField.render);
86456                 }
86457             }
86458
86459
86460             function changeType(t, onInput) {
86461                 var key = selectedKey();
86462                 if (!key) { return; }
86463
86464                 var val = t[key];
86465                 if (val !== 'no') {
86466                     _oldType[key] = val;
86467                 }
86468
86469                 if (field.type === 'structureRadio') {
86470                     // remove layer if it should not be set
86471                     if (val === 'no' ||
86472                         (key !== 'bridge' && key !== 'tunnel') ||
86473                         (key === 'tunnel' && val === 'building_passage')) {
86474                         t.layer = undefined;
86475                     }
86476                     // add layer if it should be set
86477                     if (t.layer === undefined) {
86478                         if (key === 'bridge' && val !== 'no') {
86479                             t.layer = '1';
86480                         }
86481                         if (key === 'tunnel' && val !== 'no' && val !== 'building_passage') {
86482                             t.layer = '-1';
86483                         }
86484                     }
86485                  }
86486
86487                 dispatch$1.call('change', this, t, onInput);
86488             }
86489
86490
86491             function changeLayer(t, onInput) {
86492                 if (t.layer === '0') {
86493                     t.layer = undefined;
86494                 }
86495                 dispatch$1.call('change', this, t, onInput);
86496             }
86497
86498
86499             function changeRadio() {
86500                 var t = {};
86501                 var activeKey;
86502
86503                 if (field.key) {
86504                     t[field.key] = undefined;
86505                 }
86506
86507                 radios.each(function(d) {
86508                     var active = select(this).property('checked');
86509                     if (active) { activeKey = d; }
86510
86511                     if (field.key) {
86512                         if (active) { t[field.key] = d; }
86513                     } else {
86514                         var val = _oldType[activeKey] || 'yes';
86515                         t[d] = active ? val : undefined;
86516                     }
86517                 });
86518
86519                 if (field.type === 'structureRadio') {
86520                     if (activeKey === 'bridge') {
86521                         t.layer = '1';
86522                     } else if (activeKey === 'tunnel' && t.tunnel !== 'building_passage') {
86523                         t.layer = '-1';
86524                     } else {
86525                         t.layer = undefined;
86526                     }
86527                 }
86528
86529                 dispatch$1.call('change', this, t);
86530             }
86531
86532
86533             radio.tags = function(tags) {
86534
86535                 radios.property('checked', function(d) {
86536                     if (field.key) {
86537                         return tags[field.key] === d;
86538                     }
86539                     return !!(typeof tags[d] === 'string' && tags[d].toLowerCase() !== 'no');
86540                 });
86541
86542                 function isMixed(d) {
86543                     if (field.key) {
86544                         return Array.isArray(tags[field.key]) && tags[field.key].includes(d);
86545                     }
86546                     return Array.isArray(tags[d]);
86547                 }
86548
86549                 labels
86550                     .classed('active', function(d) {
86551                         if (field.key) {
86552                             return (Array.isArray(tags[field.key]) && tags[field.key].includes(d))
86553                                 || tags[field.key] === d;
86554                         }
86555                         return Array.isArray(tags[d]) || !!(tags[d] && tags[d].toLowerCase() !== 'no');
86556                     })
86557                     .classed('mixed', isMixed)
86558                     .attr('title', function(d) {
86559                         return isMixed(d) ? _t('inspector.unshared_value_tooltip') : null;
86560                     });
86561
86562
86563                 var selection = radios.filter(function() { return this.checked; });
86564
86565                 if (selection.empty()) {
86566                     placeholder.text(_t('inspector.none'));
86567                 } else {
86568                     placeholder.text(selection.attr('value'));
86569                     _oldType[selection.datum()] = tags[selection.datum()];
86570                 }
86571
86572                 if (field.type === 'structureRadio') {
86573                     // For waterways without a tunnel tag, set 'culvert' as
86574                     // the _oldType to default to if the user picks 'tunnel'
86575                     if (!!tags.waterway && !_oldType.tunnel) {
86576                         _oldType.tunnel = 'culvert';
86577                     }
86578
86579                     wrap.call(structureExtras, tags);
86580                 }
86581             };
86582
86583
86584             radio.focus = function() {
86585                 radios.node().focus();
86586             };
86587
86588
86589             radio.entityIDs = function(val) {
86590                 if (!arguments.length) { return _entityIDs; }
86591                 _entityIDs = val;
86592                 _oldType = {};
86593                 return radio;
86594             };
86595
86596
86597             radio.isAllowed = function() {
86598                 return _entityIDs.length === 1;
86599             };
86600
86601
86602             return utilRebind(radio, dispatch$1, 'on');
86603         }
86604
86605         function uiFieldRestrictions(field, context) {
86606             var dispatch$1 = dispatch('change');
86607             var breathe = behaviorBreathe();
86608
86609             corePreferences('turn-restriction-via-way', null);                 // remove old key
86610             var storedViaWay = corePreferences('turn-restriction-via-way0');   // use new key #6922
86611             var storedDistance = corePreferences('turn-restriction-distance');
86612
86613             var _maxViaWay = storedViaWay !== null ? (+storedViaWay) : 0;
86614             var _maxDistance = storedDistance ? (+storedDistance) : 30;
86615             var _initialized = false;
86616             var _parent = select(null);       // the entire field
86617             var _container = select(null);    // just the map
86618             var _oldTurns;
86619             var _graph;
86620             var _vertexID;
86621             var _intersection;
86622             var _fromWayID;
86623
86624             var _lastXPos;
86625
86626
86627             function restrictions(selection) {
86628                 _parent = selection;
86629
86630                 // try to reuse the intersection, but always rebuild it if the graph has changed
86631                 if (_vertexID && (context.graph() !== _graph || !_intersection)) {
86632                     _graph = context.graph();
86633                     _intersection = osmIntersection(_graph, _vertexID, _maxDistance);
86634                 }
86635
86636                 // It's possible for there to be no actual intersection here.
86637                 // for example, a vertex of two `highway=path`
86638                 // In this case, hide the field.
86639                 var isOK = (
86640                     _intersection &&
86641                     _intersection.vertices.length &&           // has vertices
86642                     _intersection.vertices                     // has the vertex that the user selected
86643                         .filter(function(vertex) { return vertex.id === _vertexID; }).length &&
86644                     _intersection.ways.length > 2 &&           // has more than 2 ways
86645                     _intersection.ways                         // has more than 1 TO way
86646                         .filter(function(way) { return way.__to; }).length > 1
86647                 );
86648
86649                 // Also hide in the case where
86650                 select(selection.node().parentNode).classed('hide', !isOK);
86651
86652                 // if form field is hidden or has detached from dom, clean up.
86653                 if (!isOK ||
86654                     !context.container().select('.inspector-wrap.inspector-hidden').empty() ||
86655                     !selection.node().parentNode ||
86656                     !selection.node().parentNode.parentNode) {
86657                     selection.call(restrictions.off);
86658                     return;
86659                 }
86660
86661
86662                 var wrap = selection.selectAll('.form-field-input-wrap')
86663                     .data([0]);
86664
86665                 wrap = wrap.enter()
86666                     .append('div')
86667                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
86668                     .merge(wrap);
86669
86670                 var container = wrap.selectAll('.restriction-container')
86671                     .data([0]);
86672
86673                 // enter
86674                 var containerEnter = container.enter()
86675                     .append('div')
86676                     .attr('class', 'restriction-container');
86677
86678                 containerEnter
86679                     .append('div')
86680                     .attr('class', 'restriction-help');
86681
86682                 // update
86683                 _container = containerEnter
86684                     .merge(container)
86685                     .call(renderViewer);
86686
86687                 var controls = wrap.selectAll('.restriction-controls')
86688                     .data([0]);
86689
86690                 // enter/update
86691                 controls.enter()
86692                     .append('div')
86693                     .attr('class', 'restriction-controls-container')
86694                     .append('div')
86695                     .attr('class', 'restriction-controls')
86696                     .merge(controls)
86697                     .call(renderControls);
86698             }
86699
86700
86701             function renderControls(selection) {
86702                 var distControl = selection.selectAll('.restriction-distance')
86703                     .data([0]);
86704
86705                 distControl.exit()
86706                     .remove();
86707
86708                 var distControlEnter = distControl.enter()
86709                     .append('div')
86710                     .attr('class', 'restriction-control restriction-distance');
86711
86712                 distControlEnter
86713                     .append('span')
86714                     .attr('class', 'restriction-control-label restriction-distance-label')
86715                     .text(_t('restriction.controls.distance') + ':');
86716
86717                 distControlEnter
86718                     .append('input')
86719                     .attr('class', 'restriction-distance-input')
86720                     .attr('type', 'range')
86721                     .attr('min', '20')
86722                     .attr('max', '50')
86723                     .attr('step', '5');
86724
86725                 distControlEnter
86726                     .append('span')
86727                     .attr('class', 'restriction-distance-text');
86728
86729                 // update
86730                 selection.selectAll('.restriction-distance-input')
86731                     .property('value', _maxDistance)
86732                     .on('input', function() {
86733                         var val = select(this).property('value');
86734                         _maxDistance = +val;
86735                         _intersection = null;
86736                         _container.selectAll('.layer-osm .layer-turns *').remove();
86737                         corePreferences('turn-restriction-distance', _maxDistance);
86738                         _parent.call(restrictions);
86739                     });
86740
86741                 selection.selectAll('.restriction-distance-text')
86742                     .text(displayMaxDistance(_maxDistance));
86743
86744
86745                 var viaControl = selection.selectAll('.restriction-via-way')
86746                     .data([0]);
86747
86748                 viaControl.exit()
86749                     .remove();
86750
86751                 var viaControlEnter = viaControl.enter()
86752                     .append('div')
86753                     .attr('class', 'restriction-control restriction-via-way');
86754
86755                 viaControlEnter
86756                     .append('span')
86757                     .attr('class', 'restriction-control-label restriction-via-way-label')
86758                     .text(_t('restriction.controls.via') + ':');
86759
86760                 viaControlEnter
86761                     .append('input')
86762                     .attr('class', 'restriction-via-way-input')
86763                     .attr('type', 'range')
86764                     .attr('min', '0')
86765                     .attr('max', '2')
86766                     .attr('step', '1');
86767
86768                 viaControlEnter
86769                     .append('span')
86770                     .attr('class', 'restriction-via-way-text');
86771
86772                 // update
86773                 selection.selectAll('.restriction-via-way-input')
86774                     .property('value', _maxViaWay)
86775                     .on('input', function() {
86776                         var val = select(this).property('value');
86777                         _maxViaWay = +val;
86778                         _container.selectAll('.layer-osm .layer-turns *').remove();
86779                         corePreferences('turn-restriction-via-way0', _maxViaWay);
86780                         _parent.call(restrictions);
86781                     });
86782
86783                 selection.selectAll('.restriction-via-way-text')
86784                     .text(displayMaxVia(_maxViaWay));
86785             }
86786
86787
86788             function renderViewer(selection) {
86789                 if (!_intersection) { return; }
86790
86791                 var vgraph = _intersection.graph;
86792                 var filter = utilFunctor(true);
86793                 var projection = geoRawMercator();
86794
86795                 // Reflow warning: `utilGetDimensions` calls `getBoundingClientRect`
86796                 // Instead of asking the restriction-container for its dimensions,
86797                 //  we can ask the .sidebar, which can have its dimensions cached.
86798                 // width: calc as sidebar - padding
86799                 // height: hardcoded (from `80_app.css`)
86800                 // var d = utilGetDimensions(selection);
86801                 var sdims = utilGetDimensions(context.container().select('.sidebar'));
86802                 var d = [ sdims[0] - 50, 370 ];
86803                 var c = geoVecScale(d, 0.5);
86804                 var z = 22;
86805
86806                 projection.scale(geoZoomToScale(z));
86807
86808                 // Calculate extent of all key vertices
86809                 var extent = geoExtent();
86810                 for (var i = 0; i < _intersection.vertices.length; i++) {
86811                     extent._extend(_intersection.vertices[i].extent());
86812                 }
86813
86814                 // If this is a large intersection, adjust zoom to fit extent
86815                 if (_intersection.vertices.length > 1) {
86816                     var padding = 180;   // in z22 pixels
86817                     var tl = projection([extent[0][0], extent[1][1]]);
86818                     var br = projection([extent[1][0], extent[0][1]]);
86819                     var hFactor = (br[0] - tl[0]) / (d[0] - padding);
86820                     var vFactor = (br[1] - tl[1]) / (d[1] - padding);
86821                     var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
86822                     var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
86823                     z = z - Math.max(hZoomDiff, vZoomDiff);
86824                     projection.scale(geoZoomToScale(z));
86825                 }
86826
86827                 var padTop = 35;   // reserve top space for hint text
86828                 var extentCenter = projection(extent.center());
86829                 extentCenter[1] = extentCenter[1] - padTop;
86830
86831                 projection
86832                     .translate(geoVecSubtract(c, extentCenter))
86833                     .clipExtent([[0, 0], d]);
86834
86835                 var drawLayers = svgLayers(projection, context).only(['osm','touch']).dimensions(d);
86836                 var drawVertices = svgVertices(projection, context);
86837                 var drawLines = svgLines(projection, context);
86838                 var drawTurns = svgTurns(projection, context);
86839
86840                 var firstTime = selection.selectAll('.surface').empty();
86841
86842                 selection
86843                     .call(drawLayers);
86844
86845                 var surface = selection.selectAll('.surface')
86846                     .classed('tr', true);
86847
86848                 if (firstTime) {
86849                     _initialized = true;
86850
86851                     surface
86852                         .call(breathe);
86853                 }
86854
86855                 // This can happen if we've lowered the detail while a FROM way
86856                 // is selected, and that way is no longer part of the intersection.
86857                 if (_fromWayID && !vgraph.hasEntity(_fromWayID)) {
86858                     _fromWayID = null;
86859                     _oldTurns = null;
86860                 }
86861
86862                 surface
86863                     .call(utilSetDimensions, d)
86864                     .call(drawVertices, vgraph, _intersection.vertices, filter, extent, z)
86865                     .call(drawLines, vgraph, _intersection.ways, filter)
86866                     .call(drawTurns, vgraph, _intersection.turns(_fromWayID, _maxViaWay));
86867
86868                 surface
86869                     .on('click.restrictions', click)
86870                     .on('mouseover.restrictions', mouseover);
86871
86872                 surface
86873                     .selectAll('.selected')
86874                     .classed('selected', false);
86875
86876                 surface
86877                     .selectAll('.related')
86878                     .classed('related', false);
86879
86880                 if (_fromWayID) {
86881                     var way = vgraph.entity(_fromWayID);
86882                     surface
86883                         .selectAll('.' + _fromWayID)
86884                         .classed('selected', true)
86885                         .classed('related', true);
86886                 }
86887
86888                 document.addEventListener('resizeWindow', function () {
86889                     utilSetDimensions(_container, null);
86890                     redraw(1);
86891                 }, false);
86892
86893                 updateHints(null);
86894
86895
86896                 function click() {
86897                     surface
86898                         .call(breathe.off)
86899                         .call(breathe);
86900
86901                     var datum = event.target.__data__;
86902                     var entity = datum && datum.properties && datum.properties.entity;
86903                     if (entity) {
86904                         datum = entity;
86905                     }
86906
86907                     if (datum instanceof osmWay && (datum.__from || datum.__via)) {
86908                         _fromWayID = datum.id;
86909                         _oldTurns = null;
86910                         redraw();
86911
86912                     } else if (datum instanceof osmTurn) {
86913                         var actions, extraActions, turns, i;
86914                         var restrictionType = osmInferRestriction(vgraph, datum, projection);
86915
86916                         if (datum.restrictionID && !datum.direct) {
86917                             return;
86918
86919                         } else if (datum.restrictionID && !datum.only) {    // NO -> ONLY
86920                             var seen = {};
86921                             var datumOnly = JSON.parse(JSON.stringify(datum));   // deep clone the datum
86922                             datumOnly.only = true;                               // but change this property
86923                             restrictionType = restrictionType.replace(/^no/, 'only');
86924
86925                             // Adding an ONLY restriction should destroy all other direct restrictions from the FROM towards the VIA.
86926                             // We will remember them in _oldTurns, and restore them if the user clicks again.
86927                             turns = _intersection.turns(_fromWayID, 2);
86928                             extraActions = [];
86929                             _oldTurns = [];
86930                             for (i = 0; i < turns.length; i++) {
86931                                 var turn = turns[i];
86932                                 if (seen[turn.restrictionID]) { continue; }  // avoid deleting the turn twice (#4968, #4928)
86933
86934                                 if (turn.direct && turn.path[1] === datum.path[1]) {
86935                                     seen[turns[i].restrictionID] = true;
86936                                     turn.restrictionType = osmInferRestriction(vgraph, turn, projection);
86937                                     _oldTurns.push(turn);
86938                                     extraActions.push(actionUnrestrictTurn(turn));
86939                                 }
86940                             }
86941
86942                             actions = _intersection.actions.concat(extraActions, [
86943                                 actionRestrictTurn(datumOnly, restrictionType),
86944                                 _t('operations.restriction.annotation.create')
86945                             ]);
86946
86947                         } else if (datum.restrictionID) {   // ONLY -> Allowed
86948                             // Restore whatever restrictions we might have destroyed by cycling thru the ONLY state.
86949                             // This relies on the assumption that the intersection was already split up when we
86950                             // performed the previous action (NO -> ONLY), so the IDs in _oldTurns shouldn't have changed.
86951                             turns = _oldTurns || [];
86952                             extraActions = [];
86953                             for (i = 0; i < turns.length; i++) {
86954                                 if (turns[i].key !== datum.key) {
86955                                     extraActions.push(actionRestrictTurn(turns[i], turns[i].restrictionType));
86956                                 }
86957                             }
86958                             _oldTurns = null;
86959
86960                             actions = _intersection.actions.concat(extraActions, [
86961                                 actionUnrestrictTurn(datum),
86962                                 _t('operations.restriction.annotation.delete')
86963                             ]);
86964
86965                         } else {    // Allowed -> NO
86966                             actions = _intersection.actions.concat([
86967                                 actionRestrictTurn(datum, restrictionType),
86968                                 _t('operations.restriction.annotation.create')
86969                             ]);
86970                         }
86971
86972                         context.perform.apply(context, actions);
86973
86974                         // At this point the datum will be changed, but will have same key..
86975                         // Refresh it and update the help..
86976                         var s = surface.selectAll('.' + datum.key);
86977                         datum = s.empty() ? null : s.datum();
86978                         updateHints(datum);
86979
86980                     } else {
86981                         _fromWayID = null;
86982                         _oldTurns = null;
86983                         redraw();
86984                     }
86985                 }
86986
86987
86988                 function mouseover() {
86989                     var datum = event.target.__data__;
86990                     updateHints(datum);
86991                 }
86992
86993                 _lastXPos = _lastXPos || sdims[0];
86994
86995                 function redraw(minChange) {
86996                     var xPos = -1;
86997
86998                     if (minChange) {
86999                         xPos = utilGetDimensions(context.container().select('.sidebar'))[0];
87000                     }
87001
87002                     if (!minChange || (minChange && Math.abs(xPos - _lastXPos) >= minChange)) {
87003                         if (context.hasEntity(_vertexID)) {
87004                             _lastXPos = xPos;
87005                             _container.call(renderViewer);
87006                         }
87007                     }
87008                 }
87009
87010
87011                 function highlightPathsFrom(wayID) {
87012                     surface.selectAll('.related')
87013                         .classed('related', false)
87014                         .classed('allow', false)
87015                         .classed('restrict', false)
87016                         .classed('only', false);
87017
87018                     surface.selectAll('.' + wayID)
87019                         .classed('related', true);
87020
87021                     if (wayID) {
87022                         var turns = _intersection.turns(wayID, _maxViaWay);
87023                         for (var i = 0; i < turns.length; i++) {
87024                             var turn = turns[i];
87025                             var ids = [turn.to.way];
87026                             var klass = (turn.no ? 'restrict' : (turn.only ? 'only' : 'allow'));
87027
87028                             if (turn.only || turns.length === 1) {
87029                                 if (turn.via.ways) {
87030                                     ids = ids.concat(turn.via.ways);
87031                                 }
87032                             } else if (turn.to.way === wayID) {
87033                                 continue;
87034                             }
87035
87036                             surface.selectAll(utilEntitySelector(ids))
87037                                 .classed('related', true)
87038                                 .classed('allow', (klass === 'allow'))
87039                                 .classed('restrict', (klass === 'restrict'))
87040                                 .classed('only', (klass === 'only'));
87041                         }
87042                     }
87043                 }
87044
87045
87046                 function updateHints(datum) {
87047                     var help = _container.selectAll('.restriction-help').html('');
87048
87049                     var placeholders = {};
87050                     ['from', 'via', 'to'].forEach(function(k) {
87051                         placeholders[k] = '<span class="qualifier">' + _t('restriction.help.' + k) + '</span>';
87052                     });
87053
87054                     var entity = datum && datum.properties && datum.properties.entity;
87055                     if (entity) {
87056                         datum = entity;
87057                     }
87058
87059                     if (_fromWayID) {
87060                         way = vgraph.entity(_fromWayID);
87061                         surface
87062                             .selectAll('.' + _fromWayID)
87063                             .classed('selected', true)
87064                             .classed('related', true);
87065                     }
87066
87067                     // Hovering a way
87068                     if (datum instanceof osmWay && datum.__from) {
87069                         way = datum;
87070
87071                         highlightPathsFrom(_fromWayID ? null : way.id);
87072                         surface.selectAll('.' + way.id)
87073                             .classed('related', true);
87074
87075                         var clickSelect = (!_fromWayID || _fromWayID !== way.id);
87076                         help
87077                             .append('div')      // "Click to select FROM {fromName}." / "FROM {fromName}"
87078                             .html(_t('restriction.help.' + (clickSelect ? 'select_from_name' : 'from_name'), {
87079                                 from: placeholders.from,
87080                                 fromName: displayName(way.id, vgraph)
87081                             }));
87082
87083
87084                     // Hovering a turn arrow
87085                     } else if (datum instanceof osmTurn) {
87086                         var restrictionType = osmInferRestriction(vgraph, datum, projection);
87087                         var turnType = restrictionType.replace(/^(only|no)\_/, '');
87088                         var indirect = (datum.direct === false ? _t('restriction.help.indirect') : '');
87089                         var klass, turnText, nextText;
87090
87091                         if (datum.no) {
87092                             klass = 'restrict';
87093                             turnText = _t('restriction.help.turn.no_' + turnType, { indirect: indirect });
87094                             nextText = _t('restriction.help.turn.only_' + turnType, { indirect: '' });
87095                         } else if (datum.only) {
87096                             klass = 'only';
87097                             turnText = _t('restriction.help.turn.only_' + turnType, { indirect: indirect });
87098                             nextText = _t('restriction.help.turn.allowed_' + turnType, { indirect: '' });
87099                         } else {
87100                             klass = 'allow';
87101                             turnText = _t('restriction.help.turn.allowed_' + turnType, { indirect: indirect });
87102                             nextText = _t('restriction.help.turn.no_' + turnType, { indirect: '' });
87103                         }
87104
87105                         help
87106                             .append('div')      // "NO Right Turn (indirect)"
87107                             .attr('class', 'qualifier ' + klass)
87108                             .text(turnText);
87109
87110                         help
87111                             .append('div')      // "FROM {fromName} TO {toName}"
87112                             .html(_t('restriction.help.from_name_to_name', {
87113                                 from: placeholders.from,
87114                                 fromName: displayName(datum.from.way, vgraph),
87115                                 to: placeholders.to,
87116                                 toName: displayName(datum.to.way, vgraph)
87117                             }));
87118
87119                         if (datum.via.ways && datum.via.ways.length) {
87120                             var names = [];
87121                             for (var i = 0; i < datum.via.ways.length; i++) {
87122                                 var prev = names[names.length - 1];
87123                                 var curr = displayName(datum.via.ways[i], vgraph);
87124                                 if (!prev || curr !== prev)   // collapse identical names
87125                                     { names.push(curr); }
87126                             }
87127
87128                             help
87129                                 .append('div')      // "VIA {viaNames}"
87130                                 .html(_t('restriction.help.via_names', {
87131                                     via: placeholders.via,
87132                                     viaNames: names.join(', ')
87133                                 }));
87134                         }
87135
87136                         if (!indirect) {
87137                             help
87138                                 .append('div')      // Click for "No Right Turn"
87139                                 .text(_t('restriction.help.toggle', { turn: nextText.trim() }));
87140                         }
87141
87142                         highlightPathsFrom(null);
87143                         var alongIDs = datum.path.slice();
87144                         surface.selectAll(utilEntitySelector(alongIDs))
87145                             .classed('related', true)
87146                             .classed('allow', (klass === 'allow'))
87147                             .classed('restrict', (klass === 'restrict'))
87148                             .classed('only', (klass === 'only'));
87149
87150
87151                     // Hovering empty surface
87152                     } else {
87153                         highlightPathsFrom(null);
87154                         if (_fromWayID) {
87155                             help
87156                                 .append('div')      // "FROM {fromName}"
87157                                 .html(_t('restriction.help.from_name', {
87158                                     from: placeholders.from,
87159                                     fromName: displayName(_fromWayID, vgraph)
87160                                 }));
87161
87162                         } else {
87163                             help
87164                                 .append('div')      // "Click to select a FROM segment."
87165                                 .html(_t('restriction.help.select_from', {
87166                                     from: placeholders.from
87167                                 }));
87168                         }
87169                     }
87170                 }
87171             }
87172
87173
87174             function displayMaxDistance(maxDist) {
87175                 var isImperial = !_mainLocalizer.usesMetric();
87176                 var opts;
87177
87178                 if (isImperial) {
87179                     var distToFeet = {   // imprecise conversion for prettier display
87180                         20: 70, 25: 85, 30: 100, 35: 115, 40: 130, 45: 145, 50: 160
87181                     }[maxDist];
87182                     opts = { distance: _t('units.feet', { quantity: distToFeet }) };
87183                 } else {
87184                     opts = { distance: _t('units.meters', { quantity: maxDist }) };
87185                 }
87186
87187                 return _t('restriction.controls.distance_up_to', opts);
87188             }
87189
87190
87191             function displayMaxVia(maxVia) {
87192                 return maxVia === 0 ? _t('restriction.controls.via_node_only')
87193                     : maxVia === 1 ? _t('restriction.controls.via_up_to_one')
87194                     : _t('restriction.controls.via_up_to_two');
87195             }
87196
87197
87198             function displayName(entityID, graph) {
87199                 var entity = graph.entity(entityID);
87200                 var name = utilDisplayName(entity) || '';
87201                 var matched = _mainPresetIndex.match(entity, graph);
87202                 var type = (matched && matched.name()) || utilDisplayType(entity.id);
87203                 return name || type;
87204             }
87205
87206
87207             restrictions.entityIDs = function(val) {
87208                 _intersection = null;
87209                 _fromWayID = null;
87210                 _oldTurns = null;
87211                 _vertexID = val[0];
87212             };
87213
87214
87215             restrictions.tags = function() {};
87216             restrictions.focus = function() {};
87217
87218
87219             restrictions.off = function(selection) {
87220                 if (!_initialized) { return; }
87221
87222                 selection.selectAll('.surface')
87223                     .call(breathe.off)
87224                     .on('click.restrictions', null)
87225                     .on('mouseover.restrictions', null);
87226
87227                 select(window)
87228                     .on('resize.restrictions', null);
87229             };
87230
87231
87232             return utilRebind(restrictions, dispatch$1, 'on');
87233         }
87234
87235         uiFieldRestrictions.supportsMultiselection = false;
87236
87237         function uiFieldTextarea(field, context) {
87238             var dispatch$1 = dispatch('change');
87239             var input = select(null);
87240             var _tags;
87241
87242
87243             function textarea(selection) {
87244                 var wrap = selection.selectAll('.form-field-input-wrap')
87245                     .data([0]);
87246
87247                 wrap = wrap.enter()
87248                     .append('div')
87249                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
87250                     .merge(wrap);
87251
87252                 input = wrap.selectAll('textarea')
87253                     .data([0]);
87254
87255                 input = input.enter()
87256                     .append('textarea')
87257                     .attr('id', field.domId)
87258                     .call(utilNoAuto)
87259                     .on('input', change(true))
87260                     .on('blur', change())
87261                     .on('change', change())
87262                     .merge(input);
87263             }
87264
87265
87266             function change(onInput) {
87267                 return function() {
87268
87269                     var val = utilGetSetValue(input);
87270                     if (!onInput) { val = context.cleanTagValue(val); }
87271
87272                     // don't override multiple values with blank string
87273                     if (!val && Array.isArray(_tags[field.key])) { return; }
87274
87275                     var t = {};
87276                     t[field.key] = val || undefined;
87277                     dispatch$1.call('change', this, t, onInput);
87278                 };
87279             }
87280
87281
87282             textarea.tags = function(tags) {
87283                 _tags = tags;
87284
87285                 var isMixed = Array.isArray(tags[field.key]);
87286
87287                 utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '')
87288                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined)
87289                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : (field.placeholder() || _t('inspector.unknown')))
87290                     .classed('mixed', isMixed);
87291             };
87292
87293
87294             textarea.focus = function() {
87295                 input.node().focus();
87296             };
87297
87298
87299             return utilRebind(textarea, dispatch$1, 'on');
87300         }
87301
87302         function uiFieldWikidata(field, context) {
87303             var wikidata = services.wikidata;
87304             var dispatch$1 = dispatch('change');
87305
87306             var _selection = select(null);
87307             var _searchInput = select(null);
87308             var _qid = null;
87309             var _wikidataEntity = null;
87310             var _wikiURL = '';
87311             var _entityIDs = [];
87312
87313             var _wikipediaKey = field.keys && field.keys.find(function(key) {
87314                     return key.includes('wikipedia');
87315                 }),
87316                 _hintKey = field.key === 'wikidata' ? 'name' : field.key.split(':')[0];
87317
87318             var combobox = uiCombobox(context, 'combo-' + field.safeid)
87319                 .caseSensitive(true)
87320                 .minItems(1);
87321
87322             function wiki(selection) {
87323
87324                 _selection = selection;
87325
87326                 var wrap = selection.selectAll('.form-field-input-wrap')
87327                     .data([0]);
87328
87329                 wrap = wrap.enter()
87330                     .append('div')
87331                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
87332                     .merge(wrap);
87333
87334
87335                 var list = wrap.selectAll('ul')
87336                     .data([0]);
87337
87338                 list = list.enter()
87339                     .append('ul')
87340                     .attr('class', 'rows')
87341                     .merge(list);
87342
87343                 var searchRow = list.selectAll('li.wikidata-search')
87344                     .data([0]);
87345
87346                 var searchRowEnter = searchRow.enter()
87347                     .append('li')
87348                     .attr('class', 'wikidata-search');
87349
87350                 searchRowEnter
87351                     .append('input')
87352                     .attr('type', 'text')
87353                     .attr('id', field.domId)
87354                     .style('flex', '1')
87355                     .call(utilNoAuto)
87356                     .on('focus', function() {
87357                         var node = select(this).node();
87358                         node.setSelectionRange(0, node.value.length);
87359                     })
87360                     .on('blur', function() {
87361                         setLabelForEntity();
87362                     })
87363                     .call(combobox.fetcher(fetchWikidataItems));
87364
87365                 combobox.on('accept', function(d) {
87366                     _qid = d.id;
87367                     change();
87368                 }).on('cancel', function() {
87369                     setLabelForEntity();
87370                 });
87371
87372                 searchRowEnter
87373                     .append('button')
87374                     .attr('class', 'form-field-button wiki-link')
87375                     .attr('title', _t('icons.view_on', { domain: 'wikidata.org' }))
87376                     .attr('tabindex', -1)
87377                     .call(svgIcon('#iD-icon-out-link'))
87378                     .on('click', function() {
87379                         event.preventDefault();
87380                         if (_wikiURL) { window.open(_wikiURL, '_blank'); }
87381                     });
87382
87383                 searchRow = searchRow.merge(searchRowEnter);
87384
87385                 _searchInput = searchRow.select('input');
87386
87387                 var wikidataProperties = ['description', 'identifier'];
87388
87389                 var items = list.selectAll('li.labeled-input')
87390                     .data(wikidataProperties);
87391
87392                 // Enter
87393                 var enter = items.enter()
87394                     .append('li')
87395                     .attr('class', function(d) { return 'labeled-input preset-wikidata-' + d; });
87396
87397                 enter
87398                     .append('span')
87399                     .attr('class', 'label')
87400                     .text(function(d) { return _t('wikidata.' + d); });
87401
87402                 enter
87403                     .append('input')
87404                     .attr('type', 'text')
87405                     .call(utilNoAuto)
87406                     .classed('disabled', 'true')
87407                     .attr('readonly', 'true');
87408
87409                 enter
87410                     .append('button')
87411                     .attr('class', 'form-field-button')
87412                     .attr('title', _t('icons.copy'))
87413                     .attr('tabindex', -1)
87414                     .call(svgIcon('#iD-operation-copy'))
87415                     .on('click', function() {
87416                         event.preventDefault();
87417                         select(this.parentNode)
87418                             .select('input')
87419                             .node()
87420                             .select();
87421                         document.execCommand('copy');
87422                     });
87423
87424             }
87425
87426             function fetchWikidataItems(q, callback) {
87427
87428                 if (!q && _hintKey) {
87429                     // other tags may be good search terms
87430                     for (var i in _entityIDs) {
87431                         var entity = context.hasEntity(_entityIDs[i]);
87432                         if (entity.tags[_hintKey]) {
87433                             q = entity.tags[_hintKey];
87434                             break;
87435                         }
87436                     }
87437                 }
87438
87439                 wikidata.itemsForSearchQuery(q, function(err, data) {
87440                     if (err) { return; }
87441
87442                     for (var i in data) {
87443                         data[i].value = data[i].label + ' (' +  data[i].id + ')';
87444                         data[i].title = data[i].description;
87445                     }
87446
87447                     if (callback) { callback(data); }
87448                 });
87449             }
87450
87451
87452             function change() {
87453                 var syncTags = {};
87454                 syncTags[field.key] = _qid;
87455                 dispatch$1.call('change', this, syncTags);
87456
87457                 // attempt asynchronous update of wikidata tag..
87458                 var initGraph = context.graph();
87459                 var initEntityIDs = _entityIDs;
87460
87461                 wikidata.entityByQID(_qid, function(err, entity) {
87462                     if (err) { return; }
87463
87464                     // If graph has changed, we can't apply this update.
87465                     if (context.graph() !== initGraph) { return; }
87466
87467                     if (!entity.sitelinks) { return; }
87468
87469                     var langs = wikidata.languagesToQuery();
87470                     // use the label and description languages as fallbacks
87471                     ['labels', 'descriptions'].forEach(function(key) {
87472                         if (!entity[key]) { return; }
87473
87474                         var valueLangs = Object.keys(entity[key]);
87475                         if (valueLangs.length === 0) { return; }
87476                         var valueLang = valueLangs[0];
87477
87478                         if (langs.indexOf(valueLang) === -1) {
87479                             langs.push(valueLang);
87480                         }
87481                     });
87482
87483                     var newWikipediaValue;
87484
87485                     if (_wikipediaKey) {
87486                         var foundPreferred;
87487                         for (var i in langs) {
87488                             var lang = langs[i];
87489                             var siteID = lang.replace('-', '_') + 'wiki';
87490                             if (entity.sitelinks[siteID]) {
87491                                 foundPreferred = true;
87492                                 newWikipediaValue = lang + ':' + entity.sitelinks[siteID].title;
87493                                 // use the first match
87494                                 break;
87495                             }
87496                         }
87497
87498                         if (!foundPreferred) {
87499                             // No wikipedia sites available in the user's language or the fallback languages,
87500                             // default to any wikipedia sitelink
87501
87502                             var wikiSiteKeys = Object.keys(entity.sitelinks).filter(function(site) {
87503                                 return site.endsWith('wiki');
87504                             });
87505
87506                             if (wikiSiteKeys.length === 0) {
87507                                 // if no wikipedia pages are linked to this wikidata entity, delete that tag
87508                                 newWikipediaValue = null;
87509                             } else {
87510                                 var wikiLang = wikiSiteKeys[0].slice(0, -4).replace('_', '-');
87511                                 var wikiTitle = entity.sitelinks[wikiSiteKeys[0]].title;
87512                                 newWikipediaValue = wikiLang + ':' + wikiTitle;
87513                             }
87514                         }
87515                     }
87516
87517                     if (newWikipediaValue) {
87518                         newWikipediaValue = context.cleanTagValue(newWikipediaValue);
87519                     }
87520
87521                     if (typeof newWikipediaValue === 'undefined') { return; }
87522
87523                     var actions = initEntityIDs.map(function(entityID) {
87524                         var entity = context.hasEntity(entityID);
87525                         if (!entity) { return; }
87526
87527                         var currTags = Object.assign({}, entity.tags);  // shallow copy
87528                         if (newWikipediaValue === null) {
87529                             if (!currTags[_wikipediaKey]) { return; }
87530
87531                             delete currTags[_wikipediaKey];
87532                         } else {
87533                             currTags[_wikipediaKey] = newWikipediaValue;
87534                         }
87535
87536                         return actionChangeTags(entityID, currTags);
87537                     }).filter(Boolean);
87538
87539                     if (!actions.length) { return; }
87540
87541                     // Coalesce the update of wikidata tag into the previous tag change
87542                     context.overwrite(
87543                         function actionUpdateWikipediaTags(graph) {
87544                             actions.forEach(function(action) {
87545                                 graph = action(graph);
87546                             });
87547                             return graph;
87548                         },
87549                         context.history().undoAnnotation()
87550                     );
87551
87552                     // do not dispatch.call('change') here, because entity_editor
87553                     // changeTags() is not intended to be called asynchronously
87554                 });
87555             }
87556
87557             function setLabelForEntity() {
87558                 var label = '';
87559                 if (_wikidataEntity) {
87560                     label = entityPropertyForDisplay(_wikidataEntity, 'labels');
87561                     if (label.length === 0) {
87562                         label = _wikidataEntity.id.toString();
87563                     }
87564                 }
87565                 utilGetSetValue(_searchInput, label);
87566             }
87567
87568
87569             wiki.tags = function(tags) {
87570
87571                 var isMixed = Array.isArray(tags[field.key]);
87572                 _searchInput
87573                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : null)
87574                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : '')
87575                     .classed('mixed', isMixed);
87576
87577                 _qid = typeof tags[field.key] === 'string' && tags[field.key] || '';
87578
87579                 if (!/^Q[0-9]*$/.test(_qid)) {   // not a proper QID
87580                     unrecognized();
87581                     return;
87582                 }
87583
87584                 // QID value in correct format
87585                 _wikiURL = 'https://wikidata.org/wiki/' + _qid;
87586                 wikidata.entityByQID(_qid, function(err, entity) {
87587                     if (err) {
87588                         unrecognized();
87589                         return;
87590                     }
87591                     _wikidataEntity = entity;
87592
87593                     setLabelForEntity();
87594
87595                     var description = entityPropertyForDisplay(entity, 'descriptions');
87596
87597                     _selection.select('button.wiki-link')
87598                         .classed('disabled', false);
87599
87600                     _selection.select('.preset-wikidata-description')
87601                         .style('display', function(){
87602                             return description.length > 0 ? 'flex' : 'none';
87603                         })
87604                         .select('input')
87605                         .attr('value', description);
87606
87607                     _selection.select('.preset-wikidata-identifier')
87608                         .style('display', function(){
87609                             return entity.id ? 'flex' : 'none';
87610                         })
87611                         .select('input')
87612                         .attr('value', entity.id);
87613                 });
87614
87615
87616                 // not a proper QID
87617                 function unrecognized() {
87618                     _wikidataEntity = null;
87619                     setLabelForEntity();
87620
87621                     _selection.select('.preset-wikidata-description')
87622                         .style('display', 'none');
87623                     _selection.select('.preset-wikidata-identifier')
87624                         .style('display', 'none');
87625
87626                     _selection.select('button.wiki-link')
87627                         .classed('disabled', true);
87628
87629                     if (_qid && _qid !== '') {
87630                         _wikiURL = 'https://wikidata.org/wiki/Special:Search?search=' + _qid;
87631                     } else {
87632                         _wikiURL = '';
87633                     }
87634                 }
87635             };
87636
87637             function entityPropertyForDisplay(wikidataEntity, propKey) {
87638                 if (!wikidataEntity[propKey]) { return ''; }
87639                 var propObj = wikidataEntity[propKey];
87640                 var langKeys = Object.keys(propObj);
87641                 if (langKeys.length === 0) { return ''; }
87642                 // sorted by priority, since we want to show the user's language first if possible
87643                 var langs = wikidata.languagesToQuery();
87644                 for (var i in langs) {
87645                     var lang = langs[i];
87646                     var valueObj = propObj[lang];
87647                     if (valueObj && valueObj.value && valueObj.value.length > 0) { return valueObj.value; }
87648                 }
87649                 // default to any available value
87650                 return propObj[langKeys[0]].value;
87651             }
87652
87653
87654             wiki.entityIDs = function(val) {
87655                 if (!arguments.length) { return _entityIDs; }
87656                 _entityIDs = val;
87657                 return wiki;
87658             };
87659
87660
87661             wiki.focus = function() {
87662                 _searchInput.node().focus();
87663             };
87664
87665
87666             return utilRebind(wiki, dispatch$1, 'on');
87667         }
87668
87669         function uiFieldWikipedia(field, context) {
87670           var arguments$1 = arguments;
87671
87672           var dispatch$1 = dispatch('change');
87673           var wikipedia = services.wikipedia;
87674           var wikidata = services.wikidata;
87675           var _langInput = select(null);
87676           var _titleInput = select(null);
87677           var _wikiURL = '';
87678           var _entityIDs;
87679           var _tags;
87680
87681           var _dataWikipedia = [];
87682           _mainFileFetcher.get('wmf_sitematrix')
87683             .then(function (d) {
87684               _dataWikipedia = d;
87685               if (_tags) { updateForTags(_tags); }
87686             })
87687             .catch(function () { /* ignore */ });
87688
87689
87690           var langCombo = uiCombobox(context, 'wikipedia-lang')
87691             .fetcher(function (value, callback) {
87692               var v = value.toLowerCase();
87693               callback(_dataWikipedia
87694                 .filter(function (d) {
87695                   return d[0].toLowerCase().indexOf(v) >= 0 ||
87696                     d[1].toLowerCase().indexOf(v) >= 0 ||
87697                     d[2].toLowerCase().indexOf(v) >= 0;
87698                 })
87699                 .map(function (d) { return ({ value: d[1] }); })
87700               );
87701             });
87702
87703           var titleCombo = uiCombobox(context, 'wikipedia-title')
87704             .fetcher(function (value, callback) {
87705               if (!value) {
87706                 value = '';
87707                 for (var i in _entityIDs) {
87708                   var entity = context.hasEntity(_entityIDs[i]);
87709                   if (entity.tags.name) {
87710                     value = entity.tags.name;
87711                     break;
87712                   }
87713                 }
87714               }
87715               var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
87716               searchfn(language()[2], value, function (query, data) {
87717                 callback( data.map(function (d) { return ({ value: d }); }) );
87718               });
87719             });
87720
87721
87722           function wiki(selection) {
87723             var wrap = selection.selectAll('.form-field-input-wrap')
87724               .data([0]);
87725
87726             wrap = wrap.enter()
87727               .append('div')
87728               .attr('class', ("form-field-input-wrap form-field-input-" + (field.type)))
87729               .merge(wrap);
87730
87731
87732             var langContainer = wrap.selectAll('.wiki-lang-container')
87733               .data([0]);
87734
87735             langContainer = langContainer.enter()
87736               .append('div')
87737               .attr('class', 'wiki-lang-container')
87738               .merge(langContainer);
87739
87740
87741             _langInput = langContainer.selectAll('input.wiki-lang')
87742               .data([0]);
87743
87744             _langInput = _langInput.enter()
87745               .append('input')
87746               .attr('type', 'text')
87747               .attr('class', 'wiki-lang')
87748               .attr('placeholder', _t('translate.localized_translation_language'))
87749               .call(utilNoAuto)
87750               .call(langCombo)
87751               .merge(_langInput);
87752
87753             utilGetSetValue(_langInput, language()[1]);
87754
87755             _langInput
87756               .on('blur', changeLang)
87757               .on('change', changeLang);
87758
87759
87760             var titleContainer = wrap.selectAll('.wiki-title-container')
87761               .data([0]);
87762
87763             titleContainer = titleContainer.enter()
87764               .append('div')
87765               .attr('class', 'wiki-title-container')
87766               .merge(titleContainer);
87767
87768             _titleInput = titleContainer.selectAll('input.wiki-title')
87769               .data([0]);
87770
87771             _titleInput = _titleInput.enter()
87772               .append('input')
87773               .attr('type', 'text')
87774               .attr('class', 'wiki-title')
87775               .attr('id', field.domId)
87776               .call(utilNoAuto)
87777               .call(titleCombo)
87778               .merge(_titleInput);
87779
87780             _titleInput
87781               .on('blur', blur)
87782               .on('change', change);
87783
87784
87785             var link = titleContainer.selectAll('.wiki-link')
87786               .data([0]);
87787
87788             link = link.enter()
87789               .append('button')
87790               .attr('class', 'form-field-button wiki-link')
87791               .attr('tabindex', -1)
87792               .attr('title', _t('icons.view_on', { domain: 'wikipedia.org' }))
87793               .call(svgIcon('#iD-icon-out-link'))
87794               .merge(link);
87795
87796             link
87797               .on('click', function () {
87798                 event.preventDefault();
87799                 if (_wikiURL) { window.open(_wikiURL, '_blank'); }
87800               });
87801           }
87802
87803
87804           function language() {
87805             var value = utilGetSetValue(_langInput).toLowerCase();
87806             var locale = _mainLocalizer.localeCode().toLowerCase();
87807             var localeLanguage;
87808             return _dataWikipedia.find(function (d) {
87809               if (d[2] === locale) { localeLanguage = d; }
87810               return d[0].toLowerCase() === value || d[1].toLowerCase() === value || d[2] === value;
87811             }) || localeLanguage || ['English', 'English', 'en'];
87812           }
87813
87814
87815           function changeLang() {
87816             utilGetSetValue(_langInput, language()[1]);
87817             change(true);
87818           }
87819
87820
87821           function blur() {
87822             change(true);
87823           }
87824
87825
87826           function change(skipWikidata) {
87827             var value = utilGetSetValue(_titleInput);
87828             var m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/);
87829             var l = m && _dataWikipedia.find(function (d) { return m[1] === d[2]; });
87830             var syncTags = {};
87831
87832             if (l) {
87833               // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
87834               value = decodeURIComponent(m[2]).replace(/_/g, ' ');
87835               if (m[3]) {
87836                 var anchor;
87837                 // try {
87838                 // leave this out for now - #6232
87839                   // Best-effort `anchordecode:` implementation
87840                   // anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
87841                 // } catch (e) {
87842                 anchor = decodeURIComponent(m[3]);
87843                 // }
87844                 value += '#' + anchor.replace(/_/g, ' ');
87845               }
87846               value = value.slice(0, 1).toUpperCase() + value.slice(1);
87847               utilGetSetValue(_langInput, l[1]);
87848               utilGetSetValue(_titleInput, value);
87849             }
87850
87851             if (value) {
87852               syncTags.wikipedia = context.cleanTagValue(language()[2] + ':' + value);
87853             } else {
87854               syncTags.wikipedia = undefined;
87855             }
87856
87857             dispatch$1.call('change', this, syncTags);
87858
87859
87860             if (skipWikidata || !value || !language()[2]) { return; }
87861
87862             // attempt asynchronous update of wikidata tag..
87863             var initGraph = context.graph();
87864             var initEntityIDs = _entityIDs;
87865
87866             wikidata.itemsByTitle(language()[2], value, function (err, data) {
87867               if (err || !data || !Object.keys(data).length) { return; }
87868
87869               // If graph has changed, we can't apply this update.
87870               if (context.graph() !== initGraph) { return; }
87871
87872               var qids = Object.keys(data);
87873               var value = qids && qids.find(function (id) { return id.match(/^Q\d+$/); });
87874
87875               var actions = initEntityIDs.map(function (entityID) {
87876                 var entity = context.entity(entityID).tags;
87877                 var currTags = Object.assign({}, entity);  // shallow copy
87878                 if (currTags.wikidata !== value) {
87879                     currTags.wikidata = value;
87880                     return actionChangeTags(entityID, currTags);
87881                 }
87882               }).filter(Boolean);
87883
87884               if (!actions.length) { return; }
87885
87886               // Coalesce the update of wikidata tag into the previous tag change
87887               context.overwrite(
87888                 function actionUpdateWikidataTags(graph) {
87889                   actions.forEach(function(action) {
87890                     graph = action(graph);
87891                   });
87892                   return graph;
87893                 },
87894                 context.history().undoAnnotation()
87895               );
87896
87897               // do not dispatch.call('change') here, because entity_editor
87898               // changeTags() is not intended to be called asynchronously
87899             });
87900           }
87901
87902
87903           wiki.tags = function (tags) {
87904             _tags = tags;
87905             updateForTags(tags);
87906           };
87907
87908           function updateForTags(tags) {
87909
87910             var value = typeof tags[field.key] === 'string' ? tags[field.key] : '';
87911             var m = value.match(/([^:]+):([^#]+)(?:#(.+))?/);
87912             var l = m && _dataWikipedia.find(function (d) { return m[1] === d[2]; });
87913             var anchor = m && m[3];
87914
87915             // value in correct format
87916             if (l) {
87917               utilGetSetValue(_langInput, l[1]);
87918               utilGetSetValue(_titleInput, m[2] + (anchor ? ('#' + anchor) : ''));
87919               if (anchor) {
87920                 try {
87921                   // Best-effort `anchorencode:` implementation
87922                   anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
87923                 } catch (e) {
87924                   anchor = anchor.replace(/ /g, '_');
87925                 }
87926               }
87927               _wikiURL = 'https://' + m[1] + '.wikipedia.org/wiki/' +
87928                 m[2].replace(/ /g, '_') + (anchor ? ('#' + anchor) : '');
87929
87930             // unrecognized value format
87931             } else {
87932               utilGetSetValue(_titleInput, value);
87933               if (value && value !== '') {
87934                 utilGetSetValue(_langInput, '');
87935                 _wikiURL = "https://en.wikipedia.org/wiki/Special:Search?search=" + value;
87936               } else {
87937                 _wikiURL = '';
87938               }
87939             }
87940           }
87941
87942
87943           wiki.entityIDs = function (val) {
87944             if (!arguments$1.length) { return _entityIDs; }
87945             _entityIDs = val;
87946             return wiki;
87947           };
87948
87949
87950           wiki.focus = function () {
87951             _titleInput.node().focus();
87952           };
87953
87954
87955           return utilRebind(wiki, dispatch$1, 'on');
87956         }
87957
87958         uiFieldWikipedia.supportsMultiselection = false;
87959
87960         var uiFields = {
87961             access: uiFieldAccess,
87962             address: uiFieldAddress,
87963             check: uiFieldCheck,
87964             combo: uiFieldCombo,
87965             cycleway: uiFieldCycleway,
87966             defaultCheck: uiFieldCheck,
87967             email: uiFieldText,
87968             identifier: uiFieldText,
87969             lanes: uiFieldLanes,
87970             localized: uiFieldLocalized,
87971             maxspeed: uiFieldMaxspeed,
87972             multiCombo: uiFieldCombo,
87973             networkCombo: uiFieldCombo,
87974             number: uiFieldText,
87975             onewayCheck: uiFieldCheck,
87976             radio: uiFieldRadio,
87977             restrictions: uiFieldRestrictions,
87978             semiCombo: uiFieldCombo,
87979             structureRadio: uiFieldRadio,
87980             tel: uiFieldText,
87981             text: uiFieldText,
87982             textarea: uiFieldTextarea,
87983             typeCombo: uiFieldCombo,
87984             url: uiFieldText,
87985             wikidata: uiFieldWikidata,
87986             wikipedia: uiFieldWikipedia
87987         };
87988
87989         function uiField(context, presetField, entityIDs, options) {
87990             options = Object.assign({
87991                 show: true,
87992                 wrap: true,
87993                 remove: true,
87994                 revert: true,
87995                 info: true
87996             }, options);
87997
87998             var dispatch$1 = dispatch('change', 'revert');
87999             var field = Object.assign({}, presetField);   // shallow copy
88000             field.domId = utilUniqueDomId('form-field-' + field.safeid);
88001             var _show = options.show;
88002             var _state = '';
88003             var _tags = {};
88004
88005             var _locked = false;
88006             var _lockedTip = uiTooltip()
88007                 .title(_t('inspector.lock.suggestion', { label: field.label }))
88008                 .placement('bottom');
88009
88010
88011             field.keys = field.keys || [field.key];
88012
88013             // only create the fields that are actually being shown
88014             if (_show && !field.impl) {
88015                 createField();
88016             }
88017
88018             // Creates the field.. This is done lazily,
88019             // once we know that the field will be shown.
88020             function createField() {
88021                 field.impl = uiFields[field.type](field, context)
88022                     .on('change', function(t, onInput) {
88023                         dispatch$1.call('change', field, t, onInput);
88024                     });
88025
88026                 if (entityIDs) {
88027                     field.entityIDs = entityIDs;
88028                     // if this field cares about the entities, pass them along
88029                     if (field.impl.entityIDs) {
88030                         field.impl.entityIDs(entityIDs);
88031                     }
88032                 }
88033             }
88034
88035
88036             function isModified() {
88037                 if (!entityIDs || !entityIDs.length) { return false; }
88038                 return entityIDs.some(function(entityID) {
88039                     var original = context.graph().base().entities[entityID];
88040                     var latest = context.graph().entity(entityID);
88041                     return field.keys.some(function(key) {
88042                         return original ? latest.tags[key] !== original.tags[key] : latest.tags[key];
88043                     });
88044                 });
88045             }
88046
88047
88048             function tagsContainFieldKey() {
88049                 return field.keys.some(function(key) {
88050                     if (field.type === 'multiCombo') {
88051                         for (var tagKey in _tags) {
88052                             if (tagKey.indexOf(key) === 0) {
88053                                 return true;
88054                             }
88055                         }
88056                         return false;
88057                     }
88058                     return _tags[key] !== undefined;
88059                 });
88060             }
88061
88062
88063             function revert(d) {
88064                 event.stopPropagation();
88065                 event.preventDefault();
88066                 if (!entityIDs || _locked) { return; }
88067
88068                 dispatch$1.call('revert', d, d.keys);
88069             }
88070
88071
88072             function remove(d) {
88073                 event.stopPropagation();
88074                 event.preventDefault();
88075                 if (_locked) { return; }
88076
88077                 var t = {};
88078                 d.keys.forEach(function(key) {
88079                     t[key] = undefined;
88080                 });
88081
88082                 dispatch$1.call('change', d, t);
88083             }
88084
88085
88086             field.render = function(selection) {
88087                 var container = selection.selectAll('.form-field')
88088                     .data([field]);
88089
88090                 // Enter
88091                 var enter = container.enter()
88092                     .append('div')
88093                     .attr('class', function(d) { return 'form-field form-field-' + d.safeid; })
88094                     .classed('nowrap', !options.wrap);
88095
88096                 if (options.wrap) {
88097                     var labelEnter = enter
88098                         .append('label')
88099                         .attr('class', 'field-label')
88100                         .attr('for', function(d) { return d.domId; });
88101
88102                     var textEnter = labelEnter
88103                         .append('span')
88104                         .attr('class', 'label-text');
88105
88106                     textEnter
88107                         .append('span')
88108                         .attr('class', 'label-textvalue')
88109                         .text(function(d) { return d.label(); });
88110
88111                     textEnter
88112                         .append('span')
88113                         .attr('class', 'label-textannotation');
88114
88115                     if (options.remove) {
88116                         labelEnter
88117                             .append('button')
88118                             .attr('class', 'remove-icon')
88119                             .attr('title', _t('icons.remove'))
88120                             .attr('tabindex', -1)
88121                             .call(svgIcon('#iD-operation-delete'));
88122                     }
88123
88124                     if (options.revert) {
88125                         labelEnter
88126                             .append('button')
88127                             .attr('class', 'modified-icon')
88128                             .attr('title', _t('icons.undo'))
88129                             .attr('tabindex', -1)
88130                             .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-redo' : '#iD-icon-undo'));
88131                     }
88132                 }
88133
88134
88135                 // Update
88136                 container = container
88137                     .merge(enter);
88138
88139                 container.select('.field-label > .remove-icon')  // propagate bound data
88140                     .on('click', remove);
88141
88142                 container.select('.field-label > .modified-icon')  // propagate bound data
88143                     .on('click', revert);
88144
88145                 container
88146                     .each(function(d) {
88147                         var selection = select(this);
88148
88149                         if (!d.impl) {
88150                             createField();
88151                         }
88152
88153                         var reference, help;
88154
88155                         // instantiate field help
88156                         if (options.wrap && field.type === 'restrictions') {
88157                             help = uiFieldHelp(context, 'restrictions');
88158                         }
88159
88160                         // instantiate tag reference
88161                         if (options.wrap && options.info) {
88162                             var referenceKey = d.key;
88163                             if (d.type === 'multiCombo') {   // lookup key without the trailing ':'
88164                                 referenceKey = referenceKey.replace(/:$/, '');
88165                             }
88166
88167                             reference = uiTagReference(d.reference || { key: referenceKey });
88168                             if (_state === 'hover') {
88169                                 reference.showing(false);
88170                             }
88171                         }
88172
88173                         selection
88174                             .call(d.impl);
88175
88176                         // add field help components
88177                         if (help) {
88178                             selection
88179                                 .call(help.body)
88180                                 .select('.field-label')
88181                                 .call(help.button);
88182                         }
88183
88184                         // add tag reference components
88185                         if (reference) {
88186                             selection
88187                                 .call(reference.body)
88188                                 .select('.field-label')
88189                                 .call(reference.button);
88190                         }
88191
88192                         d.impl.tags(_tags);
88193                     });
88194
88195
88196                     container
88197                         .classed('locked', _locked)
88198                         .classed('modified', isModified())
88199                         .classed('present', tagsContainFieldKey());
88200
88201
88202                     // show a tip and lock icon if the field is locked
88203                     var annotation = container.selectAll('.field-label .label-textannotation');
88204                     var icon = annotation.selectAll('.icon')
88205                         .data(_locked ? [0]: []);
88206
88207                     icon.exit()
88208                         .remove();
88209
88210                     icon.enter()
88211                         .append('svg')
88212                         .attr('class', 'icon')
88213                         .append('use')
88214                         .attr('xlink:href', '#fas-lock');
88215
88216                     container.call(_locked ? _lockedTip : _lockedTip.destroy);
88217             };
88218
88219
88220             field.state = function(val) {
88221                 if (!arguments.length) { return _state; }
88222                 _state = val;
88223                 return field;
88224             };
88225
88226
88227             field.tags = function(val) {
88228                 if (!arguments.length) { return _tags; }
88229                 _tags = val;
88230
88231                 if (tagsContainFieldKey() && !_show) {
88232                     // always show a field if it has a value to display
88233                     _show = true;
88234                     if (!field.impl) {
88235                         createField();
88236                     }
88237                 }
88238
88239                 return field;
88240             };
88241
88242
88243             field.locked = function(val) {
88244                 if (!arguments.length) { return _locked; }
88245                 _locked = val;
88246                 return field;
88247             };
88248
88249
88250             field.show = function() {
88251                 _show = true;
88252                 if (!field.impl) {
88253                     createField();
88254                 }
88255                 if (field.default && field.key && _tags[field.key] !== field.default) {
88256                     var t = {};
88257                     t[field.key] = field.default;
88258                     dispatch$1.call('change', this, t);
88259                 }
88260             };
88261
88262             // A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown
88263             field.isShown = function() {
88264                 return _show;
88265             };
88266
88267
88268             // An allowed field can appear in the UI or in the 'Add field' dropdown.
88269             // A non-allowed field is hidden from the user altogether
88270             field.isAllowed = function() {
88271
88272                 if (entityIDs &&
88273                     entityIDs.length > 1 &&
88274                     uiFields[field.type].supportsMultiselection === false) { return false; }
88275
88276                 if (field.geometry && !entityIDs.every(function(entityID) {
88277                     return field.matchGeometry(context.graph().geometry(entityID));
88278                 })) { return false; }
88279
88280                 if (field.countryCodes || field.notCountryCodes) {
88281                     var extent = combinedEntityExtent();
88282                     if (!extent) { return true; }
88283
88284                     var center = extent.center();
88285                     var countryCode = iso1A2Code(center);
88286
88287                     if (!countryCode) { return false; }
88288
88289                     countryCode = countryCode.toLowerCase();
88290
88291                     if (field.countryCodes && field.countryCodes.indexOf(countryCode) === -1) {
88292                         return false;
88293                     }
88294                     if (field.notCountryCodes && field.notCountryCodes.indexOf(countryCode) !== -1) {
88295                         return false;
88296                     }
88297                 }
88298
88299                 var prerequisiteTag = field.prerequisiteTag;
88300
88301                 if (entityIDs &&
88302                     !tagsContainFieldKey() && // ignore tagging prerequisites if a value is already present
88303                     prerequisiteTag) {
88304
88305                     if (!entityIDs.every(function(entityID) {
88306                         var entity = context.graph().entity(entityID);
88307                         if (prerequisiteTag.key) {
88308                             var value = entity.tags[prerequisiteTag.key];
88309                             if (!value) { return false; }
88310
88311                             if (prerequisiteTag.valueNot) {
88312                                 return prerequisiteTag.valueNot !== value;
88313                             }
88314                             if (prerequisiteTag.value) {
88315                                 return prerequisiteTag.value === value;
88316                             }
88317                         } else if (prerequisiteTag.keyNot) {
88318                             if (entity.tags[prerequisiteTag.keyNot]) { return false; }
88319                         }
88320                         return true;
88321                     })) { return false; }
88322                 }
88323
88324                 return true;
88325             };
88326
88327
88328             field.focus = function() {
88329                 if (field.impl) {
88330                     field.impl.focus();
88331                 }
88332             };
88333
88334
88335             function combinedEntityExtent() {
88336                 return entityIDs && entityIDs.length && entityIDs.reduce(function(extent, entityID) {
88337                     var entity = context.graph().entity(entityID);
88338                     return extent.extend(entity.extent(context.graph()));
88339                 }, geoExtent());
88340             }
88341
88342
88343             return utilRebind(field, dispatch$1, 'on');
88344         }
88345
88346         function uiFormFields(context) {
88347             var moreCombo = uiCombobox(context, 'more-fields').minItems(1);
88348             var _fieldsArr = [];
88349             var _lastPlaceholder = '';
88350             var _state = '';
88351             var _klass = '';
88352
88353
88354             function formFields(selection) {
88355                 var allowedFields = _fieldsArr.filter(function(field) { return field.isAllowed(); });
88356                 var shown = allowedFields.filter(function(field) { return field.isShown(); });
88357                 var notShown = allowedFields.filter(function(field) { return !field.isShown(); });
88358
88359                 var container = selection.selectAll('.form-fields-container')
88360                     .data([0]);
88361
88362                 container = container.enter()
88363                     .append('div')
88364                     .attr('class', 'form-fields-container ' + (_klass || ''))
88365                     .merge(container);
88366
88367
88368                 var fields = container.selectAll('.wrap-form-field')
88369                     .data(shown, function(d) { return d.id + (d.entityIDs ? d.entityIDs.join() : ''); });
88370
88371                 fields.exit()
88372                     .remove();
88373
88374                 // Enter
88375                 var enter = fields.enter()
88376                     .append('div')
88377                     .attr('class', function(d) { return 'wrap-form-field wrap-form-field-' + d.safeid; });
88378
88379                 // Update
88380                 fields = fields
88381                     .merge(enter);
88382
88383                 fields
88384                     .order()
88385                     .each(function(d) {
88386                         select(this)
88387                             .call(d.render);
88388                     });
88389
88390
88391                 var titles = [];
88392                 var moreFields = notShown.map(function(field) {
88393                     var label = field.label();
88394                     titles.push(label);
88395
88396                     var terms = field.terms();
88397                     if (field.key) { terms.push(field.key); }
88398                     if (field.keys) { terms = terms.concat(field.keys); }
88399
88400                     return {
88401                         title: label,
88402                         value: label,
88403                         field: field,
88404                         terms: terms
88405                     };
88406                 });
88407
88408                 var placeholder = titles.slice(0,3).join(', ') + ((titles.length > 3) ? '…' : '');
88409
88410
88411                 var more = selection.selectAll('.more-fields')
88412                     .data((_state === 'hover' || moreFields.length === 0) ? [] : [0]);
88413
88414                 more.exit()
88415                     .remove();
88416
88417                 var moreEnter = more.enter()
88418                     .append('div')
88419                     .attr('class', 'more-fields')
88420                     .append('label');
88421
88422                 moreEnter
88423                     .append('span')
88424                     .text(_t('inspector.add_fields'));
88425
88426                 more = moreEnter
88427                     .merge(more);
88428
88429
88430                 var input = more.selectAll('.value')
88431                     .data([0]);
88432
88433                 input.exit()
88434                     .remove();
88435
88436                 input = input.enter()
88437                     .append('input')
88438                     .attr('class', 'value')
88439                     .attr('type', 'text')
88440                     .attr('placeholder', placeholder)
88441                     .call(utilNoAuto)
88442                     .merge(input);
88443
88444                 input
88445                     .call(utilGetSetValue, '')
88446                     .call(moreCombo
88447                         .data(moreFields)
88448                         .on('accept', function (d) {
88449                             if (!d) { return; }  // user entered something that was not matched
88450                             var field = d.field;
88451                             field.show();
88452                             selection.call(formFields);  // rerender
88453                             if (field.type !== 'semiCombo' && field.type !== 'multiCombo') {
88454                                 field.focus();
88455                             }
88456                         })
88457                     );
88458
88459                 // avoid updating placeholder excessively (triggers style recalc)
88460                 if (_lastPlaceholder !== placeholder) {
88461                     input.attr('placeholder', placeholder);
88462                     _lastPlaceholder = placeholder;
88463                 }
88464             }
88465
88466
88467             formFields.fieldsArr = function(val) {
88468                 if (!arguments.length) { return _fieldsArr; }
88469                 _fieldsArr = val || [];
88470                 return formFields;
88471             };
88472
88473             formFields.state = function(val) {
88474                 if (!arguments.length) { return _state; }
88475                 _state = val;
88476                 return formFields;
88477             };
88478
88479             formFields.klass = function(val) {
88480                 if (!arguments.length) { return _klass; }
88481                 _klass = val;
88482                 return formFields;
88483             };
88484
88485
88486             return formFields;
88487         }
88488
88489         function uiSectionPresetFields(context) {
88490
88491             var section = uiSection('preset-fields', context)
88492                 .title(function() {
88493                     return _t('inspector.fields');
88494                 })
88495                 .disclosureContent(renderDisclosureContent);
88496
88497             var dispatch$1 = dispatch('change', 'revert');
88498             var formFields = uiFormFields(context);
88499             var _state;
88500             var _fieldsArr;
88501             var _presets = [];
88502             var _tags;
88503             var _entityIDs;
88504
88505             function renderDisclosureContent(selection) {
88506                 if (!_fieldsArr) {
88507
88508                     var graph = context.graph();
88509
88510                     var geometries = Object.keys(_entityIDs.reduce(function(geoms, entityID) {
88511                         return geoms[graph.entity(entityID).geometry(graph)] = true;
88512                     }, {}));
88513
88514                     var presetsManager = _mainPresetIndex;
88515
88516                     var allFields = [];
88517                     var allMoreFields = [];
88518                     var sharedTotalFields;
88519
88520                     _presets.forEach(function(preset) {
88521                         var fields = preset.fields();
88522                         var moreFields = preset.moreFields();
88523
88524                         allFields = utilArrayUnion(allFields, fields);
88525                         allMoreFields = utilArrayUnion(allMoreFields, moreFields);
88526
88527                         if (!sharedTotalFields) {
88528                             sharedTotalFields = utilArrayUnion(fields, moreFields);
88529                         } else {
88530                             sharedTotalFields = sharedTotalFields.filter(function(field) {
88531                                 return fields.indexOf(field) !== -1 || moreFields.indexOf(field) !== -1;
88532                             });
88533                         }
88534                     });
88535
88536                     var sharedFields = allFields.filter(function(field) {
88537                         return sharedTotalFields.indexOf(field) !== -1;
88538                     });
88539                     var sharedMoreFields = allMoreFields.filter(function(field) {
88540                         return sharedTotalFields.indexOf(field) !== -1;
88541                     });
88542
88543                     _fieldsArr = [];
88544
88545                     sharedFields.forEach(function(field) {
88546                         if (field.matchAllGeometry(geometries)) {
88547                             _fieldsArr.push(
88548                                 uiField(context, field, _entityIDs)
88549                             );
88550                         }
88551                     });
88552
88553                     var singularEntity = _entityIDs.length === 1 && graph.hasEntity(_entityIDs[0]);
88554                     if (singularEntity && singularEntity.isHighwayIntersection(graph) && presetsManager.field('restrictions')) {
88555                         _fieldsArr.push(
88556                             uiField(context, presetsManager.field('restrictions'), _entityIDs)
88557                         );
88558                     }
88559
88560                     var additionalFields = utilArrayUnion(sharedMoreFields, presetsManager.universal());
88561                     additionalFields.sort(function(field1, field2) {
88562                         return field1.label().localeCompare(field2.label(), _mainLocalizer.localeCode());
88563                     });
88564
88565                     additionalFields.forEach(function(field) {
88566                         if (sharedFields.indexOf(field) === -1 &&
88567                             field.matchAllGeometry(geometries)) {
88568                             _fieldsArr.push(
88569                                 uiField(context, field, _entityIDs, { show: false })
88570                             );
88571                         }
88572                     });
88573
88574                     _fieldsArr.forEach(function(field) {
88575                         field
88576                             .on('change', function(t, onInput) {
88577                                 dispatch$1.call('change', field, _entityIDs, t, onInput);
88578                             })
88579                             .on('revert', function(keys) {
88580                                 dispatch$1.call('revert', field, keys);
88581                             });
88582                     });
88583                 }
88584
88585                 _fieldsArr.forEach(function(field) {
88586                     field
88587                         .state(_state)
88588                         .tags(_tags);
88589                 });
88590
88591
88592                 selection
88593                     .call(formFields
88594                         .fieldsArr(_fieldsArr)
88595                         .state(_state)
88596                         .klass('grouped-items-area')
88597                     );
88598
88599
88600                 selection.selectAll('.wrap-form-field input')
88601                     .on('keydown', function() {
88602                         // if user presses enter, and combobox is not active, accept edits..
88603                         if (event.keyCode === 13 && context.container().select('.combobox').empty()) {
88604                             context.enter(modeBrowse(context));
88605                         }
88606                     });
88607             }
88608
88609             section.presets = function(val) {
88610                 if (!arguments.length) { return _presets; }
88611                 if (!_presets || !val || !utilArrayIdentical(_presets, val)) {
88612                     _presets = val;
88613                     _fieldsArr = null;
88614                 }
88615                 return section;
88616             };
88617
88618             section.state = function(val) {
88619                 if (!arguments.length) { return _state; }
88620                 _state = val;
88621                 return section;
88622             };
88623
88624             section.tags = function(val) {
88625                 if (!arguments.length) { return _tags; }
88626                 _tags = val;
88627                 // Don't reset _fieldsArr here.
88628                 return section;
88629             };
88630
88631             section.entityIDs = function(val) {
88632                 if (!arguments.length) { return _entityIDs; }
88633                 if (!val || !_entityIDs || !utilArrayIdentical(_entityIDs, val)) {
88634                     _entityIDs = val;
88635                     _fieldsArr = null;
88636                 }
88637                 return section;
88638             };
88639
88640             return utilRebind(section, dispatch$1, 'on');
88641         }
88642
88643         function uiSectionRawMemberEditor(context) {
88644
88645             var section = uiSection('raw-member-editor', context)
88646                 .shouldDisplay(function() {
88647                     if (!_entityIDs || _entityIDs.length !== 1) { return false; }
88648
88649                     var entity = context.hasEntity(_entityIDs[0]);
88650                     return entity && entity.type === 'relation';
88651                 })
88652                 .title(function() {
88653                     var entity = context.hasEntity(_entityIDs[0]);
88654                     if (!entity) { return ''; }
88655
88656                     var gt = entity.members.length > _maxMembers ? '>' : '';
88657                     var count = gt + entity.members.slice(0, _maxMembers).length;
88658                     return _t('inspector.title_count', { title: _t('inspector.members'), count: count });
88659                 })
88660                 .disclosureContent(renderDisclosureContent);
88661
88662             var taginfo = services.taginfo;
88663             var _entityIDs;
88664             var _maxMembers = 1000;
88665
88666             function downloadMember(d) {
88667                 event.preventDefault();
88668
88669                 // display the loading indicator
88670                 select(this.parentNode).classed('tag-reference-loading', true);
88671                 context.loadEntity(d.id, function() {
88672                     section.reRender();
88673                 });
88674             }
88675
88676             function zoomToMember(d) {
88677                 event.preventDefault();
88678
88679                 var entity = context.entity(d.id);
88680                 context.map().zoomToEase(entity);
88681
88682                 // highlight the feature in case it wasn't previously on-screen
88683                 utilHighlightEntities([d.id], true, context);
88684             }
88685
88686
88687             function selectMember(d) {
88688                 event.preventDefault();
88689
88690                 // remove the hover-highlight styling
88691                 utilHighlightEntities([d.id], false, context);
88692
88693                 var entity = context.entity(d.id);
88694                 var mapExtent = context.map().extent();
88695                 if (!entity.intersects(mapExtent, context.graph())) {
88696                     // zoom to the entity if its extent is not visible now
88697                     context.map().zoomToEase(entity);
88698                 }
88699
88700                 context.enter(modeSelect(context, [d.id]));
88701             }
88702
88703
88704             function changeRole(d) {
88705                 var oldRole = d.role;
88706                 var newRole = context.cleanRelationRole(select(this).property('value'));
88707
88708                 if (oldRole !== newRole) {
88709                     var member = { id: d.id, type: d.type, role: newRole };
88710                     context.perform(
88711                         actionChangeMember(d.relation.id, member, d.index),
88712                         _t('operations.change_role.annotation')
88713                     );
88714                 }
88715             }
88716
88717
88718             function deleteMember(d) {
88719
88720                 // remove the hover-highlight styling
88721                 utilHighlightEntities([d.id], false, context);
88722
88723                 context.perform(
88724                     actionDeleteMember(d.relation.id, d.index),
88725                     _t('operations.delete_member.annotation')
88726                 );
88727
88728                 if (!context.hasEntity(d.relation.id)) {
88729                     context.enter(modeBrowse(context));
88730                 }
88731             }
88732
88733             function renderDisclosureContent(selection) {
88734
88735                 var entityID = _entityIDs[0];
88736
88737                 var memberships = [];
88738                 var entity = context.entity(entityID);
88739                 entity.members.slice(0, _maxMembers).forEach(function(member, index) {
88740                     memberships.push({
88741                         index: index,
88742                         id: member.id,
88743                         type: member.type,
88744                         role: member.role,
88745                         relation: entity,
88746                         member: context.hasEntity(member.id),
88747                         domId: utilUniqueDomId(entityID + '-member-' + index)
88748                     });
88749                 });
88750
88751                 var list = selection.selectAll('.member-list')
88752                     .data([0]);
88753
88754                 list = list.enter()
88755                     .append('ul')
88756                     .attr('class', 'member-list')
88757                     .merge(list);
88758
88759
88760                 var items = list.selectAll('li')
88761                     .data(memberships, function(d) {
88762                         return osmEntity.key(d.relation) + ',' + d.index + ',' +
88763                             (d.member ? osmEntity.key(d.member) : 'incomplete');
88764                     });
88765
88766                 items.exit()
88767                     .each(unbind)
88768                     .remove();
88769
88770                 var itemsEnter = items.enter()
88771                     .append('li')
88772                     .attr('class', 'member-row form-field')
88773                     .classed('member-incomplete', function(d) { return !d.member; });
88774
88775                 itemsEnter
88776                     .each(function(d) {
88777                         var item = select(this);
88778
88779                         var label = item
88780                             .append('label')
88781                             .attr('class', 'field-label')
88782                             .attr('for', d.domId);
88783
88784                         if (d.member) {
88785                             // highlight the member feature in the map while hovering on the list item
88786                             item
88787                                 .on('mouseover', function() {
88788                                     utilHighlightEntities([d.id], true, context);
88789                                 })
88790                                 .on('mouseout', function() {
88791                                     utilHighlightEntities([d.id], false, context);
88792                                 });
88793
88794                             var labelLink = label
88795                                 .append('span')
88796                                 .attr('class', 'label-text')
88797                                 .append('a')
88798                                 .attr('href', '#')
88799                                 .on('click', selectMember);
88800
88801                             labelLink
88802                                 .append('span')
88803                                 .attr('class', 'member-entity-type')
88804                                 .text(function(d) {
88805                                     var matched = _mainPresetIndex.match(d.member, context.graph());
88806                                     return (matched && matched.name()) || utilDisplayType(d.member.id);
88807                                 });
88808
88809                             labelLink
88810                                 .append('span')
88811                                 .attr('class', 'member-entity-name')
88812                                 .text(function(d) { return utilDisplayName(d.member); });
88813
88814                             label
88815                                 .append('button')
88816                                 .attr('tabindex', -1)
88817                                 .attr('title', _t('icons.remove'))
88818                                 .attr('class', 'remove member-delete')
88819                                 .call(svgIcon('#iD-operation-delete'));
88820
88821                             label
88822                                 .append('button')
88823                                 .attr('class', 'member-zoom')
88824                                 .attr('title', _t('icons.zoom_to'))
88825                                 .call(svgIcon('#iD-icon-framed-dot', 'monochrome'))
88826                                 .on('click', zoomToMember);
88827
88828                         } else {
88829                             var labelText = label
88830                                 .append('span')
88831                                 .attr('class', 'label-text');
88832
88833                             labelText
88834                                 .append('span')
88835                                 .attr('class', 'member-entity-type')
88836                                 .text(_t('inspector.' + d.type, { id: d.id }));
88837
88838                             labelText
88839                                 .append('span')
88840                                 .attr('class', 'member-entity-name')
88841                                 .text(_t('inspector.incomplete', { id: d.id }));
88842
88843                             label
88844                                 .append('button')
88845                                 .attr('class', 'member-download')
88846                                 .attr('title', _t('icons.download'))
88847                                 .attr('tabindex', -1)
88848                                 .call(svgIcon('#iD-icon-load'))
88849                                 .on('click', downloadMember);
88850                         }
88851                     });
88852
88853                 var wrapEnter = itemsEnter
88854                     .append('div')
88855                     .attr('class', 'form-field-input-wrap form-field-input-member');
88856
88857                 wrapEnter
88858                     .append('input')
88859                     .attr('class', 'member-role')
88860                     .attr('id', function(d) {
88861                         return d.domId;
88862                     })
88863                     .property('type', 'text')
88864                     .attr('placeholder', _t('inspector.role'))
88865                     .call(utilNoAuto);
88866
88867                 if (taginfo) {
88868                     wrapEnter.each(bindTypeahead);
88869                 }
88870
88871                 // update
88872                 items = items
88873                     .merge(itemsEnter)
88874                     .order();
88875
88876                 items.select('input.member-role')
88877                     .property('value', function(d) { return d.role; })
88878                     .on('blur', changeRole)
88879                     .on('change', changeRole);
88880
88881                 items.select('button.member-delete')
88882                     .on('click', deleteMember);
88883
88884                 var dragOrigin, targetIndex;
88885
88886                 items.call(d3_drag()
88887                     .on('start', function() {
88888                         dragOrigin = {
88889                             x: event.x,
88890                             y: event.y
88891                         };
88892                         targetIndex = null;
88893                     })
88894                     .on('drag', function(d, index) {
88895                         var x = event.x - dragOrigin.x,
88896                             y = event.y - dragOrigin.y;
88897
88898                         if (!select(this).classed('dragging') &&
88899                             // don't display drag until dragging beyond a distance threshold
88900                             Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) { return; }
88901
88902                         select(this)
88903                             .classed('dragging', true);
88904
88905                         targetIndex = null;
88906
88907                         selection.selectAll('li.member-row')
88908                             .style('transform', function(d2, index2) {
88909                                 var node = select(this).node();
88910                                 if (index === index2) {
88911                                     return 'translate(' + x + 'px, ' + y + 'px)';
88912                                 } else if (index2 > index && event.y > node.offsetTop) {
88913                                     if (targetIndex === null || index2 > targetIndex) {
88914                                         targetIndex = index2;
88915                                     }
88916                                     return 'translateY(-100%)';
88917                                 } else if (index2 < index && event.y < node.offsetTop + node.offsetHeight) {
88918                                     if (targetIndex === null || index2 < targetIndex) {
88919                                         targetIndex = index2;
88920                                     }
88921                                     return 'translateY(100%)';
88922                                 }
88923                                 return null;
88924                             });
88925                     })
88926                     .on('end', function(d, index) {
88927
88928                         if (!select(this).classed('dragging')) {
88929                             return;
88930                         }
88931
88932                         select(this)
88933                             .classed('dragging', false);
88934
88935                         selection.selectAll('li.member-row')
88936                             .style('transform', null);
88937
88938                         if (targetIndex !== null) {
88939                             // dragged to a new position, reorder
88940                             context.perform(
88941                                 actionMoveMember(d.relation.id, index, targetIndex),
88942                                 _t('operations.reorder_members.annotation')
88943                             );
88944                         }
88945                     })
88946                 );
88947
88948
88949
88950                 function bindTypeahead(d) {
88951                     var row = select(this);
88952                     var role = row.selectAll('input.member-role');
88953                     var origValue = role.property('value');
88954
88955                     function sort(value, data) {
88956                         var sameletter = [];
88957                         var other = [];
88958                         for (var i = 0; i < data.length; i++) {
88959                             if (data[i].value.substring(0, value.length) === value) {
88960                                 sameletter.push(data[i]);
88961                             } else {
88962                                 other.push(data[i]);
88963                             }
88964                         }
88965                         return sameletter.concat(other);
88966                     }
88967
88968                     role.call(uiCombobox(context, 'member-role')
88969                         .fetcher(function(role, callback) {
88970                             // The `geometry` param is used in the `taginfo.js` interface for
88971                             // filtering results, as a key into the `tag_members_fractions`
88972                             // object.  If we don't know the geometry because the member is
88973                             // not yet downloaded, it's ok to guess based on type.
88974                             var geometry;
88975                             if (d.member) {
88976                                 geometry = context.graph().geometry(d.member.id);
88977                             } else if (d.type === 'relation') {
88978                                 geometry = 'relation';
88979                             } else if (d.type === 'way') {
88980                                 geometry = 'line';
88981                             } else {
88982                                 geometry = 'point';
88983                             }
88984
88985                             var rtype = entity.tags.type;
88986                             taginfo.roles({
88987                                 debounce: true,
88988                                 rtype: rtype || '',
88989                                 geometry: geometry,
88990                                 query: role
88991                             }, function(err, data) {
88992                                 if (!err) { callback(sort(role, data)); }
88993                             });
88994                         })
88995                         .on('cancel', function() {
88996                             role.property('value', origValue);
88997                         })
88998                     );
88999                 }
89000
89001
89002                 function unbind() {
89003                     var row = select(this);
89004
89005                     row.selectAll('input.member-role')
89006                         .call(uiCombobox.off, context);
89007                 }
89008             }
89009
89010             section.entityIDs = function(val) {
89011                 if (!arguments.length) { return _entityIDs; }
89012                 _entityIDs = val;
89013                 return section;
89014             };
89015
89016
89017             return section;
89018         }
89019
89020         function uiSectionRawMembershipEditor(context) {
89021
89022             var section = uiSection('raw-membership-editor', context)
89023                 .shouldDisplay(function() {
89024                     return _entityIDs && _entityIDs.length === 1;
89025                 })
89026                 .title(function() {
89027                     var entity = context.hasEntity(_entityIDs[0]);
89028                     if (!entity) { return ''; }
89029
89030                     var parents = context.graph().parentRelations(entity);
89031                     var gt = parents.length > _maxMemberships ? '>' : '';
89032                     var count = gt + parents.slice(0, _maxMemberships).length;
89033                     return _t('inspector.title_count', { title: _t('inspector.relations'), count: count });
89034                 })
89035                 .disclosureContent(renderDisclosureContent);
89036
89037             var taginfo = services.taginfo;
89038             var nearbyCombo = uiCombobox(context, 'parent-relation')
89039                 .minItems(1)
89040                 .fetcher(fetchNearbyRelations)
89041                 .itemsMouseEnter(function(d) {
89042                     if (d.relation) { utilHighlightEntities([d.relation.id], true, context); }
89043                 })
89044                 .itemsMouseLeave(function(d) {
89045                     if (d.relation) { utilHighlightEntities([d.relation.id], false, context); }
89046                 });
89047             var _inChange = false;
89048             var _entityIDs = [];
89049             var _showBlank;
89050             var _maxMemberships = 1000;
89051
89052             function selectRelation(d) {
89053                 event.preventDefault();
89054
89055                 // remove the hover-highlight styling
89056                 utilHighlightEntities([d.relation.id], false, context);
89057
89058                 context.enter(modeSelect(context, [d.relation.id]));
89059             }
89060
89061             function zoomToRelation(d) {
89062                 event.preventDefault();
89063
89064                 var entity = context.entity(d.relation.id);
89065                 context.map().zoomToEase(entity);
89066
89067                 // highlight the relation in case it wasn't previously on-screen
89068                 utilHighlightEntities([d.relation.id], true, context);
89069             }
89070
89071
89072             function changeRole(d) {
89073                 if (d === 0) { return; }    // called on newrow (shoudn't happen)
89074                 if (_inChange) { return; }  // avoid accidental recursive call #5731
89075
89076                 var oldRole = d.member.role;
89077                 var newRole = context.cleanRelationRole(select(this).property('value'));
89078
89079                 if (oldRole !== newRole) {
89080                     _inChange = true;
89081                     context.perform(
89082                         actionChangeMember(d.relation.id, Object.assign({}, d.member, { role: newRole }), d.index),
89083                         _t('operations.change_role.annotation')
89084                     );
89085                 }
89086                 _inChange = false;
89087             }
89088
89089
89090             function addMembership(d, role) {
89091                 this.blur();           // avoid keeping focus on the button
89092                 _showBlank = false;
89093
89094                 var member = { id: _entityIDs[0], type: context.entity(_entityIDs[0]).type, role: role };
89095
89096                 if (d.relation) {
89097                     context.perform(
89098                         actionAddMember(d.relation.id, member),
89099                         _t('operations.add_member.annotation')
89100                     );
89101
89102                 } else {
89103                     var relation = osmRelation();
89104                     context.perform(
89105                         actionAddEntity(relation),
89106                         actionAddMember(relation.id, member),
89107                         _t('operations.add.annotation.relation')
89108                     );
89109
89110                     context.enter(modeSelect(context, [relation.id]).newFeature(true));
89111                 }
89112             }
89113
89114
89115             function deleteMembership(d) {
89116                 this.blur();           // avoid keeping focus on the button
89117                 if (d === 0) { return; }   // called on newrow (shoudn't happen)
89118
89119                 // remove the hover-highlight styling
89120                 utilHighlightEntities([d.relation.id], false, context);
89121
89122                 context.perform(
89123                     actionDeleteMember(d.relation.id, d.index),
89124                     _t('operations.delete_member.annotation')
89125                 );
89126             }
89127
89128
89129             function fetchNearbyRelations(q, callback) {
89130                 var newRelation = { relation: null, value: _t('inspector.new_relation') };
89131
89132                 var entityID = _entityIDs[0];
89133
89134                 var result = [];
89135
89136                 var graph = context.graph();
89137
89138                 function baseDisplayLabel(entity) {
89139                     var matched = _mainPresetIndex.match(entity, graph);
89140                     var presetName = (matched && matched.name()) || _t('inspector.relation');
89141                     var entityName = utilDisplayName(entity) || '';
89142
89143                     return presetName + ' ' + entityName;
89144                 }
89145
89146                 var explicitRelation = q && context.hasEntity(q.toLowerCase());
89147                 if (explicitRelation && explicitRelation.type === 'relation' && explicitRelation.id !== entityID) {
89148                     // loaded relation is specified explicitly, only show that
89149
89150                     result.push({
89151                         relation: explicitRelation,
89152                         value: baseDisplayLabel(explicitRelation) + ' ' + explicitRelation.id
89153                     });
89154                 } else {
89155
89156                     context.history().intersects(context.map().extent()).forEach(function(entity) {
89157                         if (entity.type !== 'relation' || entity.id === entityID) { return; }
89158
89159                         var value = baseDisplayLabel(entity);
89160                         if (q && (value + ' ' + entity.id).toLowerCase().indexOf(q.toLowerCase()) === -1) { return; }
89161
89162                         result.push({ relation: entity, value: value });
89163                     });
89164
89165                     result.sort(function(a, b) {
89166                         return osmRelation.creationOrder(a.relation, b.relation);
89167                     });
89168
89169                     // Dedupe identical names by appending relation id - see #2891
89170                     var dupeGroups = Object.values(utilArrayGroupBy(result, 'value'))
89171                         .filter(function(v) { return v.length > 1; });
89172
89173                     dupeGroups.forEach(function(group) {
89174                         group.forEach(function(obj) {
89175                             obj.value += ' ' + obj.relation.id;
89176                         });
89177                     });
89178                 }
89179
89180                 result.forEach(function(obj) {
89181                     obj.title = obj.value;
89182                 });
89183
89184                 result.unshift(newRelation);
89185                 callback(result);
89186             }
89187
89188             function renderDisclosureContent(selection) {
89189
89190                 var entityID = _entityIDs[0];
89191
89192                 var entity = context.entity(entityID);
89193                 var parents = context.graph().parentRelations(entity);
89194
89195                 var memberships = [];
89196
89197                 parents.slice(0, _maxMemberships).forEach(function(relation) {
89198                     relation.members.forEach(function(member, index) {
89199                         if (member.id === entity.id) {
89200                             memberships.push({
89201                                 relation: relation,
89202                                 member: member,
89203                                 index: index,
89204                                 domId: utilUniqueDomId(entityID + '-membership-' + relation.id + '-' + index)
89205                             });
89206                         }
89207                     });
89208                 });
89209
89210                 var list = selection.selectAll('.member-list')
89211                     .data([0]);
89212
89213                 list = list.enter()
89214                     .append('ul')
89215                     .attr('class', 'member-list')
89216                     .merge(list);
89217
89218
89219                 var items = list.selectAll('li.member-row-normal')
89220                     .data(memberships, function(d) {
89221                         return osmEntity.key(d.relation) + ',' + d.index;
89222                     });
89223
89224                 items.exit()
89225                     .each(unbind)
89226                     .remove();
89227
89228                 // Enter
89229                 var itemsEnter = items.enter()
89230                     .append('li')
89231                     .attr('class', 'member-row member-row-normal form-field');
89232
89233                 // highlight the relation in the map while hovering on the list item
89234                 itemsEnter.on('mouseover', function(d) {
89235                         utilHighlightEntities([d.relation.id], true, context);
89236                     })
89237                     .on('mouseout', function(d) {
89238                         utilHighlightEntities([d.relation.id], false, context);
89239                     });
89240
89241                 var labelEnter = itemsEnter
89242                     .append('label')
89243                     .attr('class', 'field-label')
89244                     .attr('for', function(d) {
89245                         return d.domId;
89246                     });
89247
89248                 var labelLink = labelEnter
89249                     .append('span')
89250                     .attr('class', 'label-text')
89251                     .append('a')
89252                     .attr('href', '#')
89253                     .on('click', selectRelation);
89254
89255                 labelLink
89256                     .append('span')
89257                     .attr('class', 'member-entity-type')
89258                     .text(function(d) {
89259                         var matched = _mainPresetIndex.match(d.relation, context.graph());
89260                         return (matched && matched.name()) || _t('inspector.relation');
89261                     });
89262
89263                 labelLink
89264                     .append('span')
89265                     .attr('class', 'member-entity-name')
89266                     .text(function(d) { return utilDisplayName(d.relation); });
89267
89268                 labelEnter
89269                     .append('button')
89270                     .attr('tabindex', -1)
89271                     .attr('class', 'remove member-delete')
89272                     .call(svgIcon('#iD-operation-delete'))
89273                     .on('click', deleteMembership);
89274
89275                 labelEnter
89276                     .append('button')
89277                     .attr('class', 'member-zoom')
89278                     .attr('title', _t('icons.zoom_to'))
89279                     .call(svgIcon('#iD-icon-framed-dot', 'monochrome'))
89280                     .on('click', zoomToRelation);
89281
89282                 var wrapEnter = itemsEnter
89283                     .append('div')
89284                     .attr('class', 'form-field-input-wrap form-field-input-member');
89285
89286                 wrapEnter
89287                     .append('input')
89288                     .attr('class', 'member-role')
89289                     .attr('id', function(d) {
89290                         return d.domId;
89291                     })
89292                     .property('type', 'text')
89293                     .attr('placeholder', _t('inspector.role'))
89294                     .call(utilNoAuto)
89295                     .property('value', function(d) { return d.member.role; })
89296                     .on('blur', changeRole)
89297                     .on('change', changeRole);
89298
89299                 if (taginfo) {
89300                     wrapEnter.each(bindTypeahead);
89301                 }
89302
89303
89304                 var newMembership = list.selectAll('.member-row-new')
89305                     .data(_showBlank ? [0] : []);
89306
89307                 // Exit
89308                 newMembership.exit()
89309                     .remove();
89310
89311                 // Enter
89312                 var newMembershipEnter = newMembership.enter()
89313                     .append('li')
89314                     .attr('class', 'member-row member-row-new form-field');
89315
89316                 var newLabelEnter = newMembershipEnter
89317                     .append('label')
89318                     .attr('class', 'field-label');
89319
89320                 newLabelEnter
89321                     .append('input')
89322                     .attr('placeholder', _t('inspector.choose_relation'))
89323                     .attr('type', 'text')
89324                     .attr('class', 'member-entity-input')
89325                     .call(utilNoAuto);
89326
89327                 newLabelEnter
89328                     .append('button')
89329                     .attr('tabindex', -1)
89330                     .attr('class', 'remove member-delete')
89331                     .call(svgIcon('#iD-operation-delete'))
89332                     .on('click', function() {
89333                         list.selectAll('.member-row-new')
89334                             .remove();
89335                     });
89336
89337                 var newWrapEnter = newMembershipEnter
89338                     .append('div')
89339                     .attr('class', 'form-field-input-wrap form-field-input-member');
89340
89341                 newWrapEnter
89342                     .append('input')
89343                     .attr('class', 'member-role')
89344                     .property('type', 'text')
89345                     .attr('placeholder', _t('inspector.role'))
89346                     .call(utilNoAuto);
89347
89348                 // Update
89349                 newMembership = newMembership
89350                     .merge(newMembershipEnter);
89351
89352                 newMembership.selectAll('.member-entity-input')
89353                     .on('blur', cancelEntity)   // if it wasn't accepted normally, cancel it
89354                     .call(nearbyCombo
89355                         .on('accept', acceptEntity)
89356                         .on('cancel', cancelEntity)
89357                     );
89358
89359
89360                 // Container for the Add button
89361                 var addRow = selection.selectAll('.add-row')
89362                     .data([0]);
89363
89364                 // enter
89365                 var addRowEnter = addRow.enter()
89366                     .append('div')
89367                     .attr('class', 'add-row');
89368
89369                 var addRelationButton = addRowEnter
89370                     .append('button')
89371                     .attr('class', 'add-relation');
89372
89373                 addRelationButton
89374                     .call(svgIcon('#iD-icon-plus', 'light'));
89375                 addRelationButton
89376                     .call(uiTooltip().title(_t('inspector.add_to_relation')).placement(_mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left'));
89377
89378                 addRowEnter
89379                     .append('div')
89380                     .attr('class', 'space-value');   // preserve space
89381
89382                 addRowEnter
89383                     .append('div')
89384                     .attr('class', 'space-buttons');  // preserve space
89385
89386                 // update
89387                 addRow = addRow
89388                     .merge(addRowEnter);
89389
89390                 addRow.select('.add-relation')
89391                     .on('click', function() {
89392                         _showBlank = true;
89393                         section.reRender();
89394                         list.selectAll('.member-entity-input').node().focus();
89395                     });
89396
89397
89398                 function acceptEntity(d) {
89399                     if (!d) {
89400                         cancelEntity();
89401                         return;
89402                     }
89403                     // remove hover-higlighting
89404                     if (d.relation) { utilHighlightEntities([d.relation.id], false, context); }
89405
89406                     var role = context.cleanRelationRole(list.selectAll('.member-row-new .member-role').property('value'));
89407                     addMembership(d, role);
89408                 }
89409
89410
89411                 function cancelEntity() {
89412                     var input = newMembership.selectAll('.member-entity-input');
89413                     input.property('value', '');
89414
89415                     // remove hover-higlighting
89416                     context.surface().selectAll('.highlighted')
89417                         .classed('highlighted', false);
89418                 }
89419
89420
89421                 function bindTypeahead(d) {
89422                     var row = select(this);
89423                     var role = row.selectAll('input.member-role');
89424                     var origValue = role.property('value');
89425
89426                     function sort(value, data) {
89427                         var sameletter = [];
89428                         var other = [];
89429                         for (var i = 0; i < data.length; i++) {
89430                             if (data[i].value.substring(0, value.length) === value) {
89431                                 sameletter.push(data[i]);
89432                             } else {
89433                                 other.push(data[i]);
89434                             }
89435                         }
89436                         return sameletter.concat(other);
89437                     }
89438
89439                     role.call(uiCombobox(context, 'member-role')
89440                         .fetcher(function(role, callback) {
89441                             var rtype = d.relation.tags.type;
89442                             taginfo.roles({
89443                                 debounce: true,
89444                                 rtype: rtype || '',
89445                                 geometry: context.graph().geometry(entityID),
89446                                 query: role
89447                             }, function(err, data) {
89448                                 if (!err) { callback(sort(role, data)); }
89449                             });
89450                         })
89451                         .on('cancel', function() {
89452                             role.property('value', origValue);
89453                         })
89454                     );
89455                 }
89456
89457
89458                 function unbind() {
89459                     var row = select(this);
89460
89461                     row.selectAll('input.member-role')
89462                         .call(uiCombobox.off, context);
89463                 }
89464             }
89465
89466
89467             section.entityIDs = function(val) {
89468                 if (!arguments.length) { return _entityIDs; }
89469                 _entityIDs = val;
89470                 _showBlank = false;
89471                 return section;
89472             };
89473
89474
89475             return section;
89476         }
89477
89478         function uiSectionSelectionList(context) {
89479
89480             var _selectedIDs = [];
89481
89482             var section = uiSection('selected-features', context)
89483                 .shouldDisplay(function() {
89484                     return _selectedIDs.length > 1;
89485                 })
89486                 .title(function() {
89487                     return _t('inspector.title_count', { title: _t('inspector.features'), count: _selectedIDs.length });
89488                 })
89489                 .disclosureContent(renderDisclosureContent);
89490
89491             context.history()
89492                 .on('change.selectionList', function(difference) {
89493                     if (difference) {
89494                         section.reRender();
89495                     }
89496                 });
89497
89498             section.entityIDs = function(val) {
89499                 if (!arguments.length) { return _selectedIDs; }
89500                 _selectedIDs = val;
89501                 return section;
89502             };
89503
89504             function selectEntity(entity) {
89505                 context.enter(modeSelect(context, [entity.id]));
89506             }
89507
89508             function deselectEntity(entity) {
89509                 event.stopPropagation();
89510
89511                 var selectedIDs = _selectedIDs.slice();
89512                 var index = selectedIDs.indexOf(entity.id);
89513                 if (index > -1) {
89514                     selectedIDs.splice(index, 1);
89515                     context.enter(modeSelect(context, selectedIDs));
89516                 }
89517             }
89518
89519             function renderDisclosureContent(selection) {
89520
89521                 var list = selection.selectAll('.feature-list')
89522                     .data([0]);
89523
89524                 list = list.enter()
89525                     .append('div')
89526                     .attr('class', 'feature-list')
89527                     .merge(list);
89528
89529                 var entities = _selectedIDs
89530                     .map(function(id) { return context.hasEntity(id); })
89531                     .filter(Boolean);
89532
89533                 var items = list.selectAll('.feature-list-item')
89534                     .data(entities, osmEntity.key);
89535
89536                 items.exit()
89537                     .remove();
89538
89539                 // Enter
89540                 var enter = items.enter()
89541                     .append('div')
89542                     .attr('class', 'feature-list-item')
89543                     .on('click', selectEntity);
89544
89545                 enter
89546                     .each(function(d) {
89547                         select(this).on('mouseover', function() {
89548                             utilHighlightEntities([d.id], true, context);
89549                         });
89550                         select(this).on('mouseout', function() {
89551                             utilHighlightEntities([d.id], false, context);
89552                         });
89553                     });
89554
89555                 var label = enter
89556                     .append('button')
89557                     .attr('class', 'label');
89558
89559                 enter
89560                     .append('button')
89561                     .attr('class', 'close')
89562                     .attr('title', _t('icons.deselect'))
89563                     .on('click', deselectEntity)
89564                     .call(svgIcon('#iD-icon-close'));
89565
89566                 label
89567                     .append('span')
89568                     .attr('class', 'entity-geom-icon')
89569                     .call(svgIcon('', 'pre-text'));
89570
89571                 label
89572                     .append('span')
89573                     .attr('class', 'entity-type');
89574
89575                 label
89576                     .append('span')
89577                     .attr('class', 'entity-name');
89578
89579                 // Update
89580                 items = items.merge(enter);
89581
89582                 items.selectAll('.entity-geom-icon use')
89583                     .attr('href', function() {
89584                         var entity = this.parentNode.parentNode.__data__;
89585                         return '#iD-icon-' + entity.geometry(context.graph());
89586                     });
89587
89588                 items.selectAll('.entity-type')
89589                     .text(function(entity) { return _mainPresetIndex.match(entity, context.graph()).name(); });
89590
89591                 items.selectAll('.entity-name')
89592                     .text(function(d) {
89593                         // fetch latest entity
89594                         var entity = context.entity(d.id);
89595                         return utilDisplayName(entity);
89596                     });
89597             }
89598
89599             return section;
89600         }
89601
89602         function uiEntityEditor(context) {
89603             var dispatch$1 = dispatch('choose');
89604             var _state = 'select';
89605             var _coalesceChanges = false;
89606             var _modified = false;
89607             var _base;
89608             var _entityIDs;
89609             var _activePresets = [];
89610             var _newFeature;
89611
89612             var _sections;
89613
89614             function entityEditor(selection) {
89615
89616                 var combinedTags = utilCombinedTags(_entityIDs, context.graph());
89617
89618                 // Header
89619                 var header = selection.selectAll('.header')
89620                     .data([0]);
89621
89622                 // Enter
89623                 var headerEnter = header.enter()
89624                     .append('div')
89625                     .attr('class', 'header fillL cf');
89626
89627                 headerEnter
89628                     .append('button')
89629                     .attr('class', 'preset-reset preset-choose')
89630                     .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-forward' : '#iD-icon-backward'));
89631
89632                 headerEnter
89633                     .append('button')
89634                     .attr('class', 'close')
89635                     .on('click', function() { context.enter(modeBrowse(context)); })
89636                     .call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close'));
89637
89638                 headerEnter
89639                     .append('h3');
89640
89641                 // Update
89642                 header = header
89643                     .merge(headerEnter);
89644
89645                 header.selectAll('h3')
89646                     .text(_entityIDs.length === 1 ? _t('inspector.edit') : _t('inspector.edit_features'));
89647
89648                 header.selectAll('.preset-reset')
89649                     .on('click', function() {
89650                         dispatch$1.call('choose', this, _activePresets);
89651                     });
89652
89653                 // Body
89654                 var body = selection.selectAll('.inspector-body')
89655                     .data([0]);
89656
89657                 // Enter
89658                 var bodyEnter = body.enter()
89659                     .append('div')
89660                     .attr('class', 'entity-editor inspector-body sep-top');
89661
89662                 // Update
89663                 body = body
89664                     .merge(bodyEnter);
89665
89666                 if (!_sections) {
89667                     _sections = [
89668                         uiSectionSelectionList(context),
89669                         uiSectionFeatureType(context).on('choose', function(presets) {
89670                             dispatch$1.call('choose', this, presets);
89671                         }),
89672                         uiSectionEntityIssues(context),
89673                         uiSectionPresetFields(context).on('change', changeTags).on('revert', revertTags),
89674                         uiSectionRawTagEditor('raw-tag-editor', context).on('change', changeTags),
89675                         uiSectionRawMemberEditor(context),
89676                         uiSectionRawMembershipEditor(context)
89677                     ];
89678                 }
89679
89680                 _sections.forEach(function(section) {
89681                     if (section.entityIDs) {
89682                         section.entityIDs(_entityIDs);
89683                     }
89684                     if (section.presets) {
89685                         section.presets(_activePresets);
89686                     }
89687                     if (section.tags) {
89688                         section.tags(combinedTags);
89689                     }
89690                     if (section.state) {
89691                         section.state(_state);
89692                     }
89693                     body.call(section.render);
89694                 });
89695
89696                 body
89697                     .selectAll('.key-trap-wrap')
89698                     .data([0])
89699                     .enter()
89700                     .append('div')
89701                     .attr('class', 'key-trap-wrap')
89702                     .append('input')
89703                     .attr('type', 'text')
89704                     .attr('class', 'key-trap')
89705                     .on('keydown.key-trap', function() {
89706                         // On tabbing, send focus back to the first field on the inspector-body
89707                         // (probably the `name` field) #4159
89708                         if (event.keyCode === 9 && !event.shiftKey) {
89709                             event.preventDefault();
89710                             body.select('input').node().focus();
89711                         }
89712                     });
89713
89714                 context.history()
89715                     .on('change.entity-editor', historyChanged);
89716
89717                 function historyChanged(difference) {
89718                     if (selection.selectAll('.entity-editor').empty()) { return; }
89719                     if (_state === 'hide') { return; }
89720                     var significant = !difference ||
89721                             difference.didChange.properties ||
89722                             difference.didChange.addition ||
89723                             difference.didChange.deletion;
89724                     if (!significant) { return; }
89725
89726                     _entityIDs = _entityIDs.filter(context.hasEntity);
89727                     if (!_entityIDs.length) { return; }
89728
89729                     var priorActivePreset = _activePresets.length === 1 && _activePresets[0];
89730
89731                     loadActivePresets();
89732
89733                     var graph = context.graph();
89734                     entityEditor.modified(_base !== graph);
89735                     entityEditor(selection);
89736
89737                     if (priorActivePreset && _activePresets.length === 1 && priorActivePreset !== _activePresets[0]) {
89738                         // flash the button to indicate the preset changed
89739                         context.container().selectAll('.entity-editor button.preset-reset .label')
89740                             .style('background-color', '#fff')
89741                             .transition()
89742                             .duration(750)
89743                             .style('background-color', null);
89744                     }
89745                 }
89746             }
89747
89748
89749             // Tag changes that fire on input can all get coalesced into a single
89750             // history operation when the user leaves the field.  #2342
89751             // Use explicit entityIDs in case the selection changes before the event is fired.
89752             function changeTags(entityIDs, changed, onInput) {
89753
89754                 var actions = [];
89755                 for (var i in entityIDs) {
89756                     var entityID = entityIDs[i];
89757                     var entity = context.entity(entityID);
89758
89759                     var tags = Object.assign({}, entity.tags);   // shallow copy
89760
89761                     for (var k in changed) {
89762                         if (!k) { continue; }
89763                         var v = changed[k];
89764                         if (v !== undefined || tags.hasOwnProperty(k)) {
89765                             tags[k] = v;
89766                         }
89767                     }
89768
89769                     if (!onInput) {
89770                         tags = utilCleanTags(tags);
89771                     }
89772
89773                     if (!fastDeepEqual(entity.tags, tags)) {
89774                         actions.push(actionChangeTags(entityID, tags));
89775                     }
89776                 }
89777
89778                 if (actions.length) {
89779                     var combinedAction = function(graph) {
89780                         actions.forEach(function(action) {
89781                             graph = action(graph);
89782                         });
89783                         return graph;
89784                     };
89785
89786                     var annotation = _t('operations.change_tags.annotation');
89787
89788                     if (_coalesceChanges) {
89789                         context.overwrite(combinedAction, annotation);
89790                     } else {
89791                         context.perform(combinedAction, annotation);
89792                         _coalesceChanges = !!onInput;
89793                     }
89794                 }
89795
89796                 // if leaving field (blur event), rerun validation
89797                 if (!onInput) {
89798                     context.validator().validate();
89799                 }
89800             }
89801
89802             function revertTags(keys) {
89803
89804                 var actions = [];
89805                 for (var i in _entityIDs) {
89806                     var entityID = _entityIDs[i];
89807
89808                     var original = context.graph().base().entities[entityID];
89809                     var changed = {};
89810                     for (var j in keys) {
89811                         var key = keys[j];
89812                         changed[key] = original ? original.tags[key] : undefined;
89813                     }
89814
89815                     var entity = context.entity(entityID);
89816                     var tags = Object.assign({}, entity.tags);   // shallow copy
89817
89818                     for (var k in changed) {
89819                         if (!k) { continue; }
89820                         var v = changed[k];
89821                         if (v !== undefined || tags.hasOwnProperty(k)) {
89822                             tags[k] = v;
89823                         }
89824                     }
89825
89826
89827                     tags = utilCleanTags(tags);
89828
89829                     if (!fastDeepEqual(entity.tags, tags)) {
89830                         actions.push(actionChangeTags(entityID, tags));
89831                     }
89832
89833                 }
89834
89835                 if (actions.length) {
89836                     var combinedAction = function(graph) {
89837                         actions.forEach(function(action) {
89838                             graph = action(graph);
89839                         });
89840                         return graph;
89841                     };
89842
89843                     var annotation = _t('operations.change_tags.annotation');
89844
89845                     if (_coalesceChanges) {
89846                         context.overwrite(combinedAction, annotation);
89847                     } else {
89848                         context.perform(combinedAction, annotation);
89849                         _coalesceChanges = false;
89850                     }
89851                 }
89852
89853                 context.validator().validate();
89854             }
89855
89856
89857             entityEditor.modified = function(val) {
89858                 if (!arguments.length) { return _modified; }
89859                 _modified = val;
89860                 return entityEditor;
89861             };
89862
89863
89864             entityEditor.state = function(val) {
89865                 if (!arguments.length) { return _state; }
89866                 _state = val;
89867                 return entityEditor;
89868             };
89869
89870
89871             entityEditor.entityIDs = function(val) {
89872                 if (!arguments.length) { return _entityIDs; }
89873                 if (val && _entityIDs && utilArrayIdentical(_entityIDs, val)) { return entityEditor; }  // exit early if no change
89874
89875                 _entityIDs = val;
89876                 _base = context.graph();
89877                 _coalesceChanges = false;
89878
89879                 loadActivePresets(true);
89880
89881                 return entityEditor
89882                     .modified(false);
89883             };
89884
89885
89886             entityEditor.newFeature = function(val) {
89887                 if (!arguments.length) { return _newFeature; }
89888                 _newFeature = val;
89889                 return entityEditor;
89890             };
89891
89892
89893             function loadActivePresets(isForNewSelection) {
89894
89895                 var graph = context.graph();
89896
89897                 var counts = {};
89898
89899                 for (var i in _entityIDs) {
89900                     var entity = graph.hasEntity(_entityIDs[i]);
89901                     if (!entity) { return; }
89902
89903                     var match = _mainPresetIndex.match(entity, graph);
89904
89905                     if (!counts[match.id]) { counts[match.id] = 0; }
89906                     counts[match.id] += 1;
89907                 }
89908
89909                 var matches = Object.keys(counts).sort(function(p1, p2) {
89910                     return counts[p2] - counts[p1];
89911                 }).map(function(pID) {
89912                     return _mainPresetIndex.item(pID);
89913                 });
89914
89915                 if (!isForNewSelection) {
89916                     // A "weak" preset doesn't set any tags. (e.g. "Address")
89917                     var weakPreset = _activePresets.length === 1 &&
89918                         !_activePresets[0].isFallback() &&
89919                         Object.keys(_activePresets[0].addTags || {}).length === 0;
89920                     // Don't replace a weak preset with a fallback preset (e.g. "Point")
89921                     if (weakPreset && matches.length === 1 && matches[0].isFallback()) { return; }
89922                 }
89923
89924                 entityEditor.presets(matches);
89925             }
89926
89927             entityEditor.presets = function(val) {
89928                 if (!arguments.length) { return _activePresets; }
89929
89930                 // don't reload the same preset
89931                 if (!utilArrayIdentical(val, _activePresets)) {
89932                     _activePresets = val;
89933                 }
89934                 return entityEditor;
89935             };
89936
89937             return utilRebind(entityEditor, dispatch$1, 'on');
89938         }
89939
89940         function uiPresetList(context) {
89941             var dispatch$1 = dispatch('cancel', 'choose');
89942             var _entityIDs;
89943             var _currentPresets;
89944             var _autofocus = false;
89945
89946
89947             function presetList(selection) {
89948                 if (!_entityIDs) { return; }
89949
89950                 var presets = _mainPresetIndex.matchAllGeometry(entityGeometries());
89951
89952                 selection.html('');
89953
89954                 var messagewrap = selection
89955                     .append('div')
89956                     .attr('class', 'header fillL');
89957
89958                 var message = messagewrap
89959                     .append('h3')
89960                     .text(_t('inspector.choose'));
89961
89962                 messagewrap
89963                     .append('button')
89964                     .attr('class', 'preset-choose')
89965                     .on('click', function() { dispatch$1.call('cancel', this); })
89966                     .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward'));
89967
89968                 function initialKeydown() {
89969                     // hack to let delete shortcut work when search is autofocused
89970                     if (search.property('value').length === 0 &&
89971                         (event.keyCode === utilKeybinding.keyCodes['⌫'] ||
89972                          event.keyCode === utilKeybinding.keyCodes['⌦'])) {
89973                         event.preventDefault();
89974                         event.stopPropagation();
89975                         operationDelete(context, _entityIDs)();
89976
89977                     // hack to let undo work when search is autofocused
89978                     } else if (search.property('value').length === 0 &&
89979                         (event.ctrlKey || event.metaKey) &&
89980                         event.keyCode === utilKeybinding.keyCodes.z) {
89981                         event.preventDefault();
89982                         event.stopPropagation();
89983                         context.undo();
89984                     } else if (!event.ctrlKey && !event.metaKey) {
89985                         // don't check for delete/undo hack on future keydown events
89986                         select(this).on('keydown', keydown);
89987                         keydown.call(this);
89988                     }
89989                 }
89990
89991                 function keydown() {
89992                     // down arrow
89993                     if (event.keyCode === utilKeybinding.keyCodes['↓'] &&
89994                         // if insertion point is at the end of the string
89995                         search.node().selectionStart === search.property('value').length) {
89996                         event.preventDefault();
89997                         event.stopPropagation();
89998                         // move focus to the first item in the preset list
89999                         var buttons = list.selectAll('.preset-list-button');
90000                         if (!buttons.empty()) { buttons.nodes()[0].focus(); }
90001                     }
90002                 }
90003
90004                 function keypress() {
90005                     // enter
90006                     var value = search.property('value');
90007                     if (event.keyCode === 13 && value.length) {
90008                         list.selectAll('.preset-list-item:first-child')
90009                             .each(function(d) { d.choose.call(this); });
90010                     }
90011                 }
90012
90013                 function inputevent() {
90014                     var value = search.property('value');
90015                     list.classed('filtered', value.length);
90016                     var extent = combinedEntityExtent();
90017                     var results, messageText;
90018                     if (value.length && extent) {
90019                         var center = extent.center();
90020                         var countryCode = iso1A2Code(center);
90021
90022                         results = presets.search(value, entityGeometries()[0], countryCode && countryCode.toLowerCase());
90023                         messageText = _t('inspector.results', {
90024                             n: results.collection.length,
90025                             search: value
90026                         });
90027                     } else {
90028                         results = _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro());
90029                         messageText = _t('inspector.choose');
90030                     }
90031                     list.call(drawList, results);
90032                     message.text(messageText);
90033                 }
90034
90035                 var searchWrap = selection
90036                     .append('div')
90037                     .attr('class', 'search-header');
90038
90039                 var search = searchWrap
90040                     .append('input')
90041                     .attr('class', 'preset-search-input')
90042                     .attr('placeholder', _t('inspector.search'))
90043                     .attr('type', 'search')
90044                     .call(utilNoAuto)
90045                     .on('keydown', initialKeydown)
90046                     .on('keypress', keypress)
90047                     .on('input', inputevent);
90048
90049                 searchWrap
90050                     .call(svgIcon('#iD-icon-search', 'pre-text'));
90051
90052                 if (_autofocus) {
90053                     search.node().focus();
90054                 }
90055
90056                 var listWrap = selection
90057                     .append('div')
90058                     .attr('class', 'inspector-body');
90059
90060                 var list = listWrap
90061                     .append('div')
90062                     .attr('class', 'preset-list')
90063                     .call(drawList, _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro()));
90064
90065                 context.features().on('change.preset-list', updateForFeatureHiddenState);
90066             }
90067
90068
90069             function drawList(list, presets) {
90070                 presets = presets.matchAllGeometry(entityGeometries());
90071                 var collection = presets.collection.reduce(function(collection, preset) {
90072                     if (!preset) { return collection; }
90073
90074                     if (preset.members) {
90075                         if (preset.members.collection.filter(function(preset) {
90076                             return preset.addable();
90077                         }).length > 1) {
90078                             collection.push(CategoryItem(preset));
90079                         }
90080                     } else if (preset.addable()) {
90081                         collection.push(PresetItem(preset));
90082                     }
90083                     return collection;
90084                 }, []);
90085
90086                 var items = list.selectAll('.preset-list-item')
90087                     .data(collection, function(d) { return d.preset.id; });
90088
90089                 items.order();
90090
90091                 items.exit()
90092                     .remove();
90093
90094                 items.enter()
90095                     .append('div')
90096                     .attr('class', function(item) { return 'preset-list-item preset-' + item.preset.id.replace('/', '-'); })
90097                     .classed('current', function(item) { return _currentPresets.indexOf(item.preset) !== -1; })
90098                     .each(function(item) { select(this).call(item); })
90099                     .style('opacity', 0)
90100                     .transition()
90101                     .style('opacity', 1);
90102
90103                 updateForFeatureHiddenState();
90104             }
90105
90106             function itemKeydown(){
90107                 // the actively focused item
90108                 var item = select(this.closest('.preset-list-item'));
90109                 var parentItem = select(item.node().parentNode.closest('.preset-list-item'));
90110
90111                 // arrow down, move focus to the next, lower item
90112                 if (event.keyCode === utilKeybinding.keyCodes['↓']) {
90113                     event.preventDefault();
90114                     event.stopPropagation();
90115                     // the next item in the list at the same level
90116                     var nextItem = select(item.node().nextElementSibling);
90117                     // if there is no next item in this list
90118                     if (nextItem.empty()) {
90119                         // if there is a parent item
90120                         if (!parentItem.empty()) {
90121                             // the item is the last item of a sublist,
90122                             // select the next item at the parent level
90123                             nextItem = select(parentItem.node().nextElementSibling);
90124                         }
90125                     // if the focused item is expanded
90126                     } else if (select(this).classed('expanded')) {
90127                         // select the first subitem instead
90128                         nextItem = item.select('.subgrid .preset-list-item:first-child');
90129                     }
90130                     if (!nextItem.empty()) {
90131                         // focus on the next item
90132                         nextItem.select('.preset-list-button').node().focus();
90133                     }
90134
90135                 // arrow up, move focus to the previous, higher item
90136                 } else if (event.keyCode === utilKeybinding.keyCodes['↑']) {
90137                     event.preventDefault();
90138                     event.stopPropagation();
90139                     // the previous item in the list at the same level
90140                     var previousItem = select(item.node().previousElementSibling);
90141
90142                     // if there is no previous item in this list
90143                     if (previousItem.empty()) {
90144                         // if there is a parent item
90145                         if (!parentItem.empty()) {
90146                             // the item is the first subitem of a sublist select the parent item
90147                             previousItem = parentItem;
90148                         }
90149                     // if the previous item is expanded
90150                     } else if (previousItem.select('.preset-list-button').classed('expanded')) {
90151                         // select the last subitem of the sublist of the previous item
90152                         previousItem = previousItem.select('.subgrid .preset-list-item:last-child');
90153                     }
90154
90155                     if (!previousItem.empty()) {
90156                         // focus on the previous item
90157                         previousItem.select('.preset-list-button').node().focus();
90158                     } else {
90159                         // the focus is at the top of the list, move focus back to the search field
90160                         var search = select(this.closest('.preset-list-pane')).select('.preset-search-input');
90161                         search.node().focus();
90162                     }
90163
90164                 // arrow left, move focus to the parent item if there is one
90165                 } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '→' : '←']) {
90166                     event.preventDefault();
90167                     event.stopPropagation();
90168                     // if there is a parent item, focus on the parent item
90169                     if (!parentItem.empty()) {
90170                         parentItem.select('.preset-list-button').node().focus();
90171                     }
90172
90173                 // arrow right, choose this item
90174                 } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '←' : '→']) {
90175                     event.preventDefault();
90176                     event.stopPropagation();
90177                     item.datum().choose.call(select(this).node());
90178                 }
90179             }
90180
90181
90182             function CategoryItem(preset) {
90183                 var box, sublist, shown = false;
90184
90185                 function item(selection) {
90186                     var wrap = selection.append('div')
90187                         .attr('class', 'preset-list-button-wrap category');
90188
90189                     function click() {
90190                         var isExpanded = select(this).classed('expanded');
90191                         var iconName = isExpanded ?
90192                             (_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward') : '#iD-icon-down';
90193                         select(this)
90194                             .classed('expanded', !isExpanded);
90195                         select(this).selectAll('div.label-inner svg.icon use')
90196                             .attr('href', iconName);
90197                         item.choose();
90198                     }
90199
90200                     var geometries = entityGeometries();
90201
90202                     var button = wrap
90203                         .append('button')
90204                         .attr('class', 'preset-list-button')
90205                         .classed('expanded', false)
90206                         .call(uiPresetIcon()
90207                             .geometry(geometries.length === 1 && geometries[0])
90208                             .preset(preset))
90209                         .on('click', click)
90210                         .on('keydown', function() {
90211                             // right arrow, expand the focused item
90212                             if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '←' : '→']) {
90213                                 event.preventDefault();
90214                                 event.stopPropagation();
90215                                 // if the item isn't expanded
90216                                 if (!select(this).classed('expanded')) {
90217                                     // toggle expansion (expand the item)
90218                                     click.call(this);
90219                                 }
90220                             // left arrow, collapse the focused item
90221                             } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '→' : '←']) {
90222                                 event.preventDefault();
90223                                 event.stopPropagation();
90224                                 // if the item is expanded
90225                                 if (select(this).classed('expanded')) {
90226                                     // toggle expansion (collapse the item)
90227                                     click.call(this);
90228                                 }
90229                             } else {
90230                                 itemKeydown.call(this);
90231                             }
90232                         });
90233
90234                     var label = button
90235                         .append('div')
90236                         .attr('class', 'label')
90237                         .append('div')
90238                         .attr('class', 'label-inner');
90239
90240                     label
90241                         .append('div')
90242                         .attr('class', 'namepart')
90243                         .call(svgIcon((_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'))
90244                         .append('span')
90245                         .html(function() { return preset.name() + '&hellip;'; });
90246
90247                     box = selection.append('div')
90248                         .attr('class', 'subgrid')
90249                         .style('max-height', '0px')
90250                         .style('opacity', 0);
90251
90252                     box.append('div')
90253                         .attr('class', 'arrow');
90254
90255                     sublist = box.append('div')
90256                         .attr('class', 'preset-list fillL3');
90257                 }
90258
90259
90260                 item.choose = function() {
90261                     if (!box || !sublist) { return; }
90262
90263                     if (shown) {
90264                         shown = false;
90265                         box.transition()
90266                             .duration(200)
90267                             .style('opacity', '0')
90268                             .style('max-height', '0px')
90269                             .style('padding-bottom', '0px');
90270                     } else {
90271                         shown = true;
90272                         var members = preset.members.matchAllGeometry(entityGeometries());
90273                         sublist.call(drawList, members);
90274                         box.transition()
90275                             .duration(200)
90276                             .style('opacity', '1')
90277                             .style('max-height', 200 + members.collection.length * 190 + 'px')
90278                             .style('padding-bottom', '10px');
90279                     }
90280                 };
90281
90282                 item.preset = preset;
90283                 return item;
90284             }
90285
90286
90287             function PresetItem(preset) {
90288                 function item(selection) {
90289                     var wrap = selection.append('div')
90290                         .attr('class', 'preset-list-button-wrap');
90291
90292                     var geometries = entityGeometries();
90293
90294                     var button = wrap.append('button')
90295                         .attr('class', 'preset-list-button')
90296                         .call(uiPresetIcon()
90297                             .geometry(geometries.length === 1 && geometries[0])
90298                             .preset(preset))
90299                         .on('click', item.choose)
90300                         .on('keydown', itemKeydown);
90301
90302                     var label = button
90303                         .append('div')
90304                         .attr('class', 'label')
90305                         .append('div')
90306                         .attr('class', 'label-inner');
90307
90308                     // NOTE: split/join on en-dash, not a hypen (to avoid conflict with fr - nl names in Brussels etc)
90309                     label.selectAll('.namepart')
90310                         .data(preset.name().split(' – '))
90311                         .enter()
90312                         .append('div')
90313                         .attr('class', 'namepart')
90314                         .text(function(d) { return d; });
90315
90316                     wrap.call(item.reference.button);
90317                     selection.call(item.reference.body);
90318                 }
90319
90320                 item.choose = function() {
90321                     if (select(this).classed('disabled')) { return; }
90322                     if (!context.inIntro()) {
90323                         _mainPresetIndex.setMostRecent(preset, entityGeometries()[0]);
90324                     }
90325                     context.perform(
90326                         function(graph) {
90327                             for (var i in _entityIDs) {
90328                                 var entityID = _entityIDs[i];
90329                                 var oldPreset = _mainPresetIndex.match(graph.entity(entityID), graph);
90330                                 graph = actionChangePreset(entityID, oldPreset, preset)(graph);
90331                             }
90332                             return graph;
90333                         },
90334                         _t('operations.change_tags.annotation')
90335                     );
90336
90337                     context.validator().validate();  // rerun validation
90338                     dispatch$1.call('choose', this, preset);
90339                 };
90340
90341                 item.help = function() {
90342                     event.stopPropagation();
90343                     item.reference.toggle();
90344                 };
90345
90346                 item.preset = preset;
90347                 item.reference = uiTagReference(preset.reference(entityGeometries()[0]));
90348
90349                 return item;
90350             }
90351
90352
90353             function updateForFeatureHiddenState() {
90354                 if (!_entityIDs.every(context.hasEntity)) { return; }
90355
90356                 var geometries = entityGeometries();
90357                 var button = context.container().selectAll('.preset-list .preset-list-button');
90358
90359                 // remove existing tooltips
90360                 button.call(uiTooltip().destroyAny);
90361
90362                 button.each(function(item, index) {
90363                     var hiddenPresetFeaturesId;
90364                     for (var i in geometries) {
90365                         hiddenPresetFeaturesId = context.features().isHiddenPreset(item.preset, geometries[i]);
90366                         if (hiddenPresetFeaturesId) { break; }
90367                     }
90368                     var isHiddenPreset = !context.inIntro() &&
90369                         !!hiddenPresetFeaturesId &&
90370                         (_currentPresets.length !== 1 || item.preset !== _currentPresets[0]);
90371
90372                     select(this)
90373                         .classed('disabled', isHiddenPreset);
90374
90375                     if (isHiddenPreset) {
90376                         var isAutoHidden = context.features().autoHidden(hiddenPresetFeaturesId);
90377                         var tooltipIdSuffix = isAutoHidden ? 'zoom' : 'manual';
90378                         var tooltipObj = { features: _t('feature.' + hiddenPresetFeaturesId + '.description') };
90379                         select(this).call(uiTooltip()
90380                             .title(_t('inspector.hidden_preset.' + tooltipIdSuffix, tooltipObj))
90381                             .placement(index < 2 ? 'bottom' : 'top')
90382                         );
90383                     }
90384                 });
90385             }
90386
90387             presetList.autofocus = function(val) {
90388                 if (!arguments.length) { return _autofocus; }
90389                 _autofocus = val;
90390                 return presetList;
90391             };
90392
90393             presetList.entityIDs = function(val) {
90394                 if (!arguments.length) { return _entityIDs; }
90395                 _entityIDs = val;
90396                 if (_entityIDs && _entityIDs.length) {
90397                     var presets = _entityIDs.map(function(entityID) {
90398                         return _mainPresetIndex.match(context.entity(entityID), context.graph());
90399                     });
90400                     presetList.presets(presets);
90401                 }
90402                 return presetList;
90403             };
90404
90405             presetList.presets = function(val) {
90406                 if (!arguments.length) { return _currentPresets; }
90407                 _currentPresets = val;
90408                 return presetList;
90409             };
90410
90411             function entityGeometries() {
90412
90413                 var counts = {};
90414
90415                 for (var i in _entityIDs) {
90416                     var entityID = _entityIDs[i];
90417                     var entity = context.entity(entityID);
90418                     var geometry = entity.geometry(context.graph());
90419
90420                     // Treat entities on addr:interpolation lines as points, not vertices (#3241)
90421                     if (geometry === 'vertex' && entity.isOnAddressLine(context.graph())) {
90422                         geometry = 'point';
90423                     }
90424
90425                     if (!counts[geometry]) { counts[geometry] = 0; }
90426                     counts[geometry] += 1;
90427                 }
90428
90429                 return Object.keys(counts).sort(function(geom1, geom2) {
90430                     return counts[geom2] - counts[geom1];
90431                 });
90432             }
90433
90434             function combinedEntityExtent() {
90435                 return _entityIDs.reduce(function(extent, entityID) {
90436                     var entity = context.graph().entity(entityID);
90437                     return extent.extend(entity.extent(context.graph()));
90438                 }, geoExtent());
90439             }
90440
90441             return utilRebind(presetList, dispatch$1, 'on');
90442         }
90443
90444         function uiInspector(context) {
90445             var presetList = uiPresetList(context);
90446             var entityEditor = uiEntityEditor(context);
90447             var wrap = select(null),
90448                 presetPane = select(null),
90449                 editorPane = select(null);
90450             var _state = 'select';
90451             var _entityIDs;
90452             var _newFeature = false;
90453
90454
90455             function inspector(selection) {
90456                 presetList
90457                     .entityIDs(_entityIDs)
90458                     .autofocus(_newFeature)
90459                     .on('choose', inspector.setPreset)
90460                     .on('cancel', function() {
90461                         wrap.transition()
90462                             .styleTween('right', function() { return interpolate('-100%', '0%'); });
90463                         editorPane.call(entityEditor);
90464                     });
90465
90466                 entityEditor
90467                     .state(_state)
90468                     .entityIDs(_entityIDs)
90469                     .on('choose', inspector.showList);
90470
90471                 wrap = selection.selectAll('.panewrap')
90472                     .data([0]);
90473
90474                 var enter = wrap.enter()
90475                     .append('div')
90476                     .attr('class', 'panewrap');
90477
90478                 enter
90479                     .append('div')
90480                     .attr('class', 'preset-list-pane pane');
90481
90482                 enter
90483                     .append('div')
90484                     .attr('class', 'entity-editor-pane pane');
90485
90486                 wrap = wrap.merge(enter);
90487                 presetPane = wrap.selectAll('.preset-list-pane');
90488                 editorPane = wrap.selectAll('.entity-editor-pane');
90489
90490                 function shouldDefaultToPresetList() {
90491                     // always show the inspector on hover
90492                     if (_state !== 'select') { return false; }
90493
90494                     // can only change preset on single selection
90495                     if (_entityIDs.length !== 1) { return false; }
90496
90497                     var entityID = _entityIDs[0];
90498                     var entity = context.hasEntity(entityID);
90499                     if (!entity) { return false; }
90500
90501                     // default to inspector if there are already tags
90502                     if (entity.hasNonGeometryTags()) { return false; }
90503
90504                     // prompt to select preset if feature is new and untagged
90505                     if (_newFeature) { return true; }
90506
90507                     // all existing features except vertices should default to inspector
90508                     if (entity.geometry(context.graph()) !== 'vertex') { return false; }
90509
90510                     // show vertex relations if any
90511                     if (context.graph().parentRelations(entity).length) { return false; }
90512
90513                     // show vertex issues if there are any
90514                     if (context.validator().getEntityIssues(entityID).length) { return false; }
90515
90516                     // show turn retriction editor for junction vertices
90517                     if (entity.isHighwayIntersection(context.graph())) { return false; }
90518
90519                     // otherwise show preset list for uninteresting vertices
90520                     return true;
90521                 }
90522
90523                 if (shouldDefaultToPresetList()) {
90524                     wrap.style('right', '-100%');
90525                     presetPane.call(presetList);
90526                 } else {
90527                     wrap.style('right', '0%');
90528                     editorPane.call(entityEditor);
90529                 }
90530
90531                 var footer = selection.selectAll('.footer')
90532                     .data([0]);
90533
90534                 footer = footer.enter()
90535                     .append('div')
90536                     .attr('class', 'footer')
90537                     .merge(footer);
90538
90539                 footer
90540                     .call(uiViewOnOSM(context)
90541                         .what(context.hasEntity(_entityIDs.length === 1 && _entityIDs[0]))
90542                     );
90543             }
90544
90545             inspector.showList = function(presets) {
90546
90547                 wrap.transition()
90548                     .styleTween('right', function() { return interpolate('0%', '-100%'); });
90549
90550                 if (presets) {
90551                     presetList.presets(presets);
90552                 }
90553
90554                 presetPane
90555                     .call(presetList.autofocus(true));
90556             };
90557
90558             inspector.setPreset = function(preset) {
90559
90560                 // upon setting multipolygon, go to the area preset list instead of the editor
90561                 if (preset.id === 'type/multipolygon') {
90562                     presetPane
90563                         .call(presetList.autofocus(true));
90564
90565                 } else {
90566                     wrap.transition()
90567                         .styleTween('right', function() { return interpolate('-100%', '0%'); });
90568
90569                     editorPane
90570                         .call(entityEditor.presets([preset]));
90571                 }
90572
90573             };
90574
90575             inspector.state = function(val) {
90576                 if (!arguments.length) { return _state; }
90577                 _state = val;
90578                 entityEditor.state(_state);
90579
90580                 // remove any old field help overlay that might have gotten attached to the inspector
90581                 context.container().selectAll('.field-help-body').remove();
90582
90583                 return inspector;
90584             };
90585
90586
90587             inspector.entityIDs = function(val) {
90588                 if (!arguments.length) { return _entityIDs; }
90589                 _entityIDs = val;
90590                 return inspector;
90591             };
90592
90593
90594             inspector.newFeature = function(val) {
90595                 if (!arguments.length) { return _newFeature; }
90596                 _newFeature = val;
90597                 return inspector;
90598             };
90599
90600
90601             return inspector;
90602         }
90603
90604         function uiSidebar(context) {
90605             var inspector = uiInspector(context);
90606             var dataEditor = uiDataEditor(context);
90607             var noteEditor = uiNoteEditor(context);
90608             var improveOsmEditor = uiImproveOsmEditor(context);
90609             var keepRightEditor = uiKeepRightEditor(context);
90610             var osmoseEditor = uiOsmoseEditor(context);
90611             var _current;
90612             var _wasData = false;
90613             var _wasNote = false;
90614             var _wasQaItem = false;
90615
90616             // use pointer events on supported platforms; fallback to mouse events
90617             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
90618
90619
90620             function sidebar(selection) {
90621                 var container = context.container();
90622                 var minWidth = 240;
90623                 var sidebarWidth;
90624                 var containerWidth;
90625                 var dragOffset;
90626
90627                 // Set the initial width constraints
90628                 selection
90629                     .style('min-width', minWidth + 'px')
90630                     .style('max-width', '400px')
90631                     .style('width', '33.3333%');
90632
90633                 var resizer = selection
90634                     .append('div')
90635                     .attr('class', 'sidebar-resizer')
90636                     .on(_pointerPrefix + 'down.sidebar-resizer', pointerdown);
90637
90638                 var downPointerId, lastClientX, containerLocGetter;
90639
90640                 function pointerdown() {
90641                     if (downPointerId) { return; }
90642
90643                     if ('button' in event && event.button !== 0) { return; }
90644
90645                     downPointerId = event.pointerId || 'mouse';
90646
90647                     lastClientX = event.clientX;
90648
90649                     containerLocGetter = utilFastMouse(container.node());
90650
90651                     // offset from edge of sidebar-resizer
90652                     dragOffset = utilFastMouse(resizer.node())(event)[0] - 1;
90653
90654                     sidebarWidth = selection.node().getBoundingClientRect().width;
90655                     containerWidth = container.node().getBoundingClientRect().width;
90656                     var widthPct = (sidebarWidth / containerWidth) * 100;
90657                     selection
90658                         .style('width', widthPct + '%')    // lock in current width
90659                         .style('max-width', '85%');        // but allow larger widths
90660
90661                     resizer.classed('dragging', true);
90662
90663                     select(window)
90664                         .on('touchmove.sidebar-resizer', function() {
90665                             // disable page scrolling while resizing on touch input
90666                             event.preventDefault();
90667                         }, { passive: false })
90668                         .on(_pointerPrefix + 'move.sidebar-resizer', pointermove)
90669                         .on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', pointerup);
90670                 }
90671
90672                 function pointermove() {
90673
90674                     if (downPointerId !== (event.pointerId || 'mouse')) { return; }
90675
90676                     event.preventDefault();
90677
90678                     var dx = event.clientX - lastClientX;
90679
90680                     lastClientX = event.clientX;
90681
90682                     var isRTL = (_mainLocalizer.textDirection() === 'rtl');
90683                     var scaleX = isRTL ? 0 : 1;
90684                     var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
90685
90686                     var x = containerLocGetter(event)[0] - dragOffset;
90687                     sidebarWidth = isRTL ? containerWidth - x : x;
90688
90689                     var isCollapsed = selection.classed('collapsed');
90690                     var shouldCollapse = sidebarWidth < minWidth;
90691
90692                     selection.classed('collapsed', shouldCollapse);
90693
90694                     if (shouldCollapse) {
90695                         if (!isCollapsed) {
90696                             selection
90697                                 .style(xMarginProperty, '-400px')
90698                                 .style('width', '400px');
90699
90700                             context.ui().onResize([(sidebarWidth - dx) * scaleX, 0]);
90701                         }
90702
90703                     } else {
90704                         var widthPct = (sidebarWidth / containerWidth) * 100;
90705                         selection
90706                             .style(xMarginProperty, null)
90707                             .style('width', widthPct + '%');
90708
90709                         if (isCollapsed) {
90710                             context.ui().onResize([-sidebarWidth * scaleX, 0]);
90711                         } else {
90712                             context.ui().onResize([-dx * scaleX, 0]);
90713                         }
90714                     }
90715                 }
90716
90717                 function pointerup() {
90718                     if (downPointerId !== (event.pointerId || 'mouse')) { return; }
90719
90720                     downPointerId = null;
90721
90722                     resizer.classed('dragging', false);
90723
90724                     select(window)
90725                         .on('touchmove.sidebar-resizer', null)
90726                         .on(_pointerPrefix + 'move.sidebar-resizer', null)
90727                         .on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', null);
90728                 }
90729
90730                 var featureListWrap = selection
90731                     .append('div')
90732                     .attr('class', 'feature-list-pane')
90733                     .call(uiFeatureList(context));
90734
90735                 var inspectorWrap = selection
90736                     .append('div')
90737                     .attr('class', 'inspector-hidden inspector-wrap');
90738
90739                 var hoverModeSelect = function(targets) {
90740                     context.container().selectAll('.feature-list-item').classed('hover', false);
90741
90742                     if (context.selectedIDs().length > 1 &&
90743                         targets && targets.length) {
90744
90745                         var elements = context.container().selectAll('.feature-list-item')
90746                             .filter(function (node) {
90747                                 return targets.indexOf(node) !== -1;
90748                             });
90749
90750                         if (!elements.empty()) {
90751                             elements.classed('hover', true);
90752                         }
90753                     }
90754                 };
90755
90756                 sidebar.hoverModeSelect = throttle(hoverModeSelect, 200);
90757
90758                 function hover(targets) {
90759                     var datum = targets && targets.length && targets[0];
90760                     if (datum && datum.__featurehash__) {   // hovering on data
90761                         _wasData = true;
90762                         sidebar
90763                             .show(dataEditor.datum(datum));
90764
90765                         selection.selectAll('.sidebar-component')
90766                             .classed('inspector-hover', true);
90767
90768                     } else if (datum instanceof osmNote) {
90769                         if (context.mode().id === 'drag-note') { return; }
90770                         _wasNote = true;
90771
90772                         var osm = services.osm;
90773                         if (osm) {
90774                             datum = osm.getNote(datum.id);   // marker may contain stale data - get latest
90775                         }
90776
90777                         sidebar
90778                             .show(noteEditor.note(datum));
90779
90780                         selection.selectAll('.sidebar-component')
90781                             .classed('inspector-hover', true);
90782
90783                     } else if (datum instanceof QAItem) {
90784                         _wasQaItem = true;
90785
90786                         var errService = services[datum.service];
90787                         if (errService) {
90788                             // marker may contain stale data - get latest
90789                             datum = errService.getError(datum.id);
90790                         }
90791
90792                         // Currently only three possible services
90793                         var errEditor;
90794                         if (datum.service === 'keepRight') {
90795                             errEditor = keepRightEditor;
90796                         } else if (datum.service === 'osmose') {
90797                             errEditor = osmoseEditor;
90798                         } else {
90799                             errEditor = improveOsmEditor;
90800                         }
90801
90802                         context.container().selectAll('.qaItem.' + datum.service)
90803                             .classed('hover', function(d) { return d.id === datum.id; });
90804
90805                         sidebar
90806                             .show(errEditor.error(datum));
90807
90808                         selection.selectAll('.sidebar-component')
90809                             .classed('inspector-hover', true);
90810
90811                     } else if (!_current && (datum instanceof osmEntity)) {
90812                         featureListWrap
90813                             .classed('inspector-hidden', true);
90814
90815                         inspectorWrap
90816                             .classed('inspector-hidden', false)
90817                             .classed('inspector-hover', true);
90818
90819                         if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), [datum.id]) || inspector.state() !== 'hover') {
90820                             inspector
90821                                 .state('hover')
90822                                 .entityIDs([datum.id])
90823                                 .newFeature(false);
90824
90825                             inspectorWrap
90826                                 .call(inspector);
90827                         }
90828
90829                     } else if (!_current) {
90830                         featureListWrap
90831                             .classed('inspector-hidden', false);
90832                         inspectorWrap
90833                             .classed('inspector-hidden', true);
90834                         inspector
90835                             .state('hide');
90836
90837                     } else if (_wasData || _wasNote || _wasQaItem) {
90838                         _wasNote = false;
90839                         _wasData = false;
90840                         _wasQaItem = false;
90841                         context.container().selectAll('.note').classed('hover', false);
90842                         context.container().selectAll('.qaItem').classed('hover', false);
90843                         sidebar.hide();
90844                     }
90845                 }
90846
90847                 sidebar.hover = throttle(hover, 200);
90848
90849
90850                 sidebar.intersects = function(extent) {
90851                     var rect = selection.node().getBoundingClientRect();
90852                     return extent.intersects([
90853                         context.projection.invert([0, rect.height]),
90854                         context.projection.invert([rect.width, 0])
90855                     ]);
90856                 };
90857
90858
90859                 sidebar.select = function(ids, newFeature) {
90860                     sidebar.hide();
90861
90862                     if (ids && ids.length) {
90863
90864                         var entity = ids.length === 1 && context.entity(ids[0]);
90865                         if (entity && newFeature && selection.classed('collapsed')) {
90866                             // uncollapse the sidebar
90867                             var extent = entity.extent(context.graph());
90868                             sidebar.expand(sidebar.intersects(extent));
90869                         }
90870
90871                         featureListWrap
90872                             .classed('inspector-hidden', true);
90873
90874                         inspectorWrap
90875                             .classed('inspector-hidden', false)
90876                             .classed('inspector-hover', false);
90877
90878                         if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), ids) || inspector.state() !== 'select') {
90879                             inspector
90880                                 .state('select')
90881                                 .entityIDs(ids)
90882                                 .newFeature(newFeature);
90883
90884                             inspectorWrap
90885                                 .call(inspector);
90886                         }
90887
90888                     } else {
90889                         inspector
90890                             .state('hide');
90891                     }
90892                 };
90893
90894
90895                 sidebar.showPresetList = function() {
90896                     inspector.showList();
90897                 };
90898
90899
90900                 sidebar.show = function(component, element) {
90901                     featureListWrap
90902                         .classed('inspector-hidden', true);
90903                     inspectorWrap
90904                         .classed('inspector-hidden', true);
90905
90906                     if (_current) { _current.remove(); }
90907                     _current = selection
90908                         .append('div')
90909                         .attr('class', 'sidebar-component')
90910                         .call(component, element);
90911                 };
90912
90913
90914                 sidebar.hide = function() {
90915                     featureListWrap
90916                         .classed('inspector-hidden', false);
90917                     inspectorWrap
90918                         .classed('inspector-hidden', true);
90919
90920                     if (_current) { _current.remove(); }
90921                     _current = null;
90922                 };
90923
90924
90925                 sidebar.expand = function(moveMap) {
90926                     if (selection.classed('collapsed')) {
90927                         sidebar.toggle(moveMap);
90928                     }
90929                 };
90930
90931
90932                 sidebar.collapse = function(moveMap) {
90933                     if (!selection.classed('collapsed')) {
90934                         sidebar.toggle(moveMap);
90935                     }
90936                 };
90937
90938
90939                 sidebar.toggle = function(moveMap) {
90940                     var e = event;
90941                     if (e && e.sourceEvent) {
90942                         e.sourceEvent.preventDefault();
90943                     } else if (e) {
90944                         e.preventDefault();
90945                     }
90946
90947                     // Don't allow sidebar to toggle when the user is in the walkthrough.
90948                     if (context.inIntro()) { return; }
90949
90950                     var isCollapsed = selection.classed('collapsed');
90951                     var isCollapsing = !isCollapsed;
90952                     var isRTL = (_mainLocalizer.textDirection() === 'rtl');
90953                     var scaleX = isRTL ? 0 : 1;
90954                     var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
90955
90956                     sidebarWidth = selection.node().getBoundingClientRect().width;
90957
90958                     // switch from % to px
90959                     selection.style('width', sidebarWidth + 'px');
90960
90961                     var startMargin, endMargin, lastMargin;
90962                     if (isCollapsing) {
90963                         startMargin = lastMargin = 0;
90964                         endMargin = -sidebarWidth;
90965                     } else {
90966                         startMargin = lastMargin = -sidebarWidth;
90967                         endMargin = 0;
90968                     }
90969
90970                     selection.transition()
90971                         .style(xMarginProperty, endMargin + 'px')
90972                         .tween('panner', function() {
90973                             var i = d3_interpolateNumber(startMargin, endMargin);
90974                             return function(t) {
90975                                 var dx = lastMargin - Math.round(i(t));
90976                                 lastMargin = lastMargin - dx;
90977                                 context.ui().onResize(moveMap ? undefined : [dx * scaleX, 0]);
90978                             };
90979                         })
90980                         .on('end', function() {
90981                             selection.classed('collapsed', isCollapsing);
90982
90983                             // switch back from px to %
90984                             if (!isCollapsing) {
90985                                 var containerWidth = container.node().getBoundingClientRect().width;
90986                                 var widthPct = (sidebarWidth / containerWidth) * 100;
90987                                 selection
90988                                     .style(xMarginProperty, null)
90989                                     .style('width', widthPct + '%');
90990                             }
90991                         });
90992                 };
90993
90994                 // toggle the sidebar collapse when double-clicking the resizer
90995                 resizer.on('dblclick', sidebar.toggle);
90996
90997                 // ensure hover sidebar is closed when zooming out beyond editable zoom
90998                 context.map().on('crossEditableZoom.sidebar', function(within) {
90999                     if (!within && !selection.select('.inspector-hover').empty()) {
91000                         hover([]);
91001                     }
91002                 });
91003             }
91004
91005             sidebar.showPresetList = function() {};
91006             sidebar.hover = function() {};
91007             sidebar.hover.cancel = function() {};
91008             sidebar.intersects = function() {};
91009             sidebar.select = function() {};
91010             sidebar.show = function() {};
91011             sidebar.hide = function() {};
91012             sidebar.expand = function() {};
91013             sidebar.collapse = function() {};
91014             sidebar.toggle = function() {};
91015
91016             return sidebar;
91017         }
91018
91019         function uiSourceSwitch(context) {
91020             var keys;
91021
91022
91023             function click() {
91024                 event.preventDefault();
91025
91026                 var osm = context.connection();
91027                 if (!osm) { return; }
91028
91029                 if (context.inIntro()) { return; }
91030
91031                 if (context.history().hasChanges() &&
91032                     !window.confirm(_t('source_switch.lose_changes'))) { return; }
91033
91034                 var isLive = select(this)
91035                     .classed('live');
91036
91037                 isLive = !isLive;
91038                 context.enter(modeBrowse(context));
91039                 context.history().clearSaved();          // remove saved history
91040                 context.flush();                         // remove stored data
91041
91042                 select(this)
91043                     .text(isLive ? _t('source_switch.live') : _t('source_switch.dev'))
91044                     .classed('live', isLive)
91045                     .classed('chip', isLive);
91046
91047                 osm.switch(isLive ? keys[0] : keys[1]);  // switch connection (warning: dispatches 'change' event)
91048             }
91049
91050             var sourceSwitch = function(selection) {
91051                 selection
91052                     .append('a')
91053                     .attr('href', '#')
91054                     .text(_t('source_switch.live'))
91055                     .attr('class', 'live chip')
91056                     .on('click', click);
91057             };
91058
91059
91060             sourceSwitch.keys = function(_) {
91061                 if (!arguments.length) { return keys; }
91062                 keys = _;
91063                 return sourceSwitch;
91064             };
91065
91066
91067             return sourceSwitch;
91068         }
91069
91070         function uiSpinner(context) {
91071             var osm = context.connection();
91072
91073
91074             return function(selection) {
91075                 var img = selection
91076                     .append('img')
91077                     .attr('src', context.imagePath('loader-black.gif'))
91078                     .style('opacity', 0);
91079
91080                 if (osm) {
91081                     osm
91082                         .on('loading.spinner', function() {
91083                             img.transition()
91084                                 .style('opacity', 1);
91085                         })
91086                         .on('loaded.spinner', function() {
91087                             img.transition()
91088                                 .style('opacity', 0);
91089                         });
91090                 }
91091             };
91092         }
91093
91094         function uiSplash(context) {
91095           return function (selection) {
91096             // Exception - if there are restorable changes, skip this splash screen.
91097             // This is because we currently only support one `uiModal` at a time
91098             //  and we need to show them `uiRestore`` instead of this one.
91099             if (context.history().hasRestorableChanges()) { return; }
91100
91101             // If user has not seen this version of the privacy policy, show the splash again.
91102             var updateMessage = '';
91103             var sawPrivacyVersion = corePreferences('sawPrivacyVersion');
91104             var showSplash = !corePreferences('sawSplash');
91105             if (sawPrivacyVersion !== context.privacyVersion) {
91106               updateMessage = _t('splash.privacy_update');
91107               showSplash = true;
91108             }
91109
91110             if (!showSplash) { return; }
91111
91112             corePreferences('sawSplash', true);
91113             corePreferences('sawPrivacyVersion', context.privacyVersion);
91114
91115             // fetch intro graph data now, while user is looking at the splash screen
91116             _mainFileFetcher.get('intro_graph');
91117
91118             var modalSelection = uiModal(selection);
91119
91120             modalSelection.select('.modal')
91121               .attr('class', 'modal-splash modal');
91122
91123             var introModal = modalSelection.select('.content')
91124               .append('div')
91125               .attr('class', 'fillL');
91126
91127             introModal
91128               .append('div')
91129               .attr('class','modal-section')
91130               .append('h3')
91131               .text(_t('splash.welcome'));
91132
91133             var modalSection = introModal
91134               .append('div')
91135               .attr('class','modal-section');
91136
91137             modalSection
91138               .append('p')
91139               .html(_t('splash.text', {
91140                 version: context.version,
91141                 website: '<a target="_blank" href="http://ideditor.blog/">ideditor.blog</a>',
91142                 github: '<a target="_blank" href="https://github.com/openstreetmap/iD">github.com</a>'
91143               }));
91144
91145             modalSection
91146               .append('p')
91147               .html(_t('splash.privacy', {
91148                 updateMessage: updateMessage,
91149                 privacyLink: '<a target="_blank" href="https://github.com/openstreetmap/iD/blob/release/PRIVACY.md">' +
91150                   _t('splash.privacy_policy') + '</a>'
91151               }));
91152
91153             var buttonWrap = introModal
91154               .append('div')
91155               .attr('class', 'modal-actions');
91156
91157             var walkthrough = buttonWrap
91158               .append('button')
91159               .attr('class', 'walkthrough')
91160               .on('click', function () {
91161                 context.container().call(uiIntro(context));
91162                 modalSelection.close();
91163               });
91164
91165             walkthrough
91166               .append('svg')
91167               .attr('class', 'logo logo-walkthrough')
91168               .append('use')
91169               .attr('xlink:href', '#iD-logo-walkthrough');
91170
91171             walkthrough
91172               .append('div')
91173               .text(_t('splash.walkthrough'));
91174
91175             var startEditing = buttonWrap
91176               .append('button')
91177               .attr('class', 'start-editing')
91178               .on('click', modalSelection.close);
91179
91180             startEditing
91181               .append('svg')
91182               .attr('class', 'logo logo-features')
91183               .append('use')
91184               .attr('xlink:href', '#iD-logo-features');
91185
91186             startEditing
91187               .append('div')
91188               .text(_t('splash.start'));
91189
91190             modalSelection.select('button.close')
91191               .attr('class','hide');
91192           };
91193         }
91194
91195         function uiStatus(context) {
91196             var osm = context.connection();
91197
91198
91199             return function(selection) {
91200                 if (!osm) { return; }
91201
91202                 function update(err, apiStatus) {
91203                     selection.html('');
91204
91205                     if (err) {
91206                         if (apiStatus === 'connectionSwitched') {
91207                             // if the connection was just switched, we can't rely on
91208                             // the status (we're getting the status of the previous api)
91209                             return;
91210
91211                         } else if (apiStatus === 'rateLimited') {
91212                             selection
91213                                 .text(_t('osm_api_status.message.rateLimit'))
91214                                 .append('a')
91215                                 .attr('class', 'api-status-login')
91216                                 .attr('target', '_blank')
91217                                 .call(svgIcon('#iD-icon-out-link', 'inline'))
91218                                 .append('span')
91219                                 .text(_t('login'))
91220                                 .on('click.login', function() {
91221                                     event.preventDefault();
91222                                     osm.authenticate();
91223                                 });
91224                         } else {
91225
91226                             // don't allow retrying too rapidly
91227                             var throttledRetry = throttle(function() {
91228                                 // try loading the visible tiles
91229                                 context.loadTiles(context.projection);
91230                                 // manually reload the status too in case all visible tiles were already loaded
91231                                 osm.reloadApiStatus();
91232                             }, 2000);
91233
91234                             // eslint-disable-next-line no-warning-comments
91235                             // TODO: nice messages for different error types
91236                             selection
91237                                 .text(_t('osm_api_status.message.error') + ' ')
91238                                 .append('a')
91239                                 // let the user manually retry their connection directly
91240                                 .text(_t('osm_api_status.retry'))
91241                                 .on('click.retry', function() {
91242                                     event.preventDefault();
91243                                     throttledRetry();
91244                                 });
91245                         }
91246
91247                     } else if (apiStatus === 'readonly') {
91248                         selection.text(_t('osm_api_status.message.readonly'));
91249                     } else if (apiStatus === 'offline') {
91250                         selection.text(_t('osm_api_status.message.offline'));
91251                     }
91252
91253                     selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
91254                 }
91255
91256                 osm.on('apiStatusChange.uiStatus', update);
91257
91258                 // reload the status periodically regardless of other factors
91259                 window.setInterval(function() {
91260                     osm.reloadApiStatus();
91261                 }, 90000);
91262
91263                 // load the initial status in case no OSM data was loaded yet
91264                 osm.reloadApiStatus();
91265             };
91266         }
91267
91268         function modeDrawArea(context, wayID, startGraph, button) {
91269             var mode = {
91270                 button: button,
91271                 id: 'draw-area'
91272             };
91273
91274             var behavior = behaviorDrawWay(context, wayID, mode, startGraph)
91275                 .on('rejectedSelfIntersection.modeDrawArea', function() {
91276                     context.ui().flash
91277                         .text(_t('self_intersection.error.areas'))();
91278                 });
91279
91280             mode.wayID = wayID;
91281
91282             mode.enter = function() {
91283                 context.install(behavior);
91284             };
91285
91286             mode.exit = function() {
91287                 context.uninstall(behavior);
91288             };
91289
91290             mode.selectedIDs = function() {
91291                 return [wayID];
91292             };
91293
91294             mode.activeID = function() {
91295                 return (behavior && behavior.activeID()) || [];
91296             };
91297
91298             return mode;
91299         }
91300
91301         function modeAddArea(context, mode) {
91302             mode.id = 'add-area';
91303
91304             var behavior = behaviorAddWay(context)
91305                 .on('start', start)
91306                 .on('startFromWay', startFromWay)
91307                 .on('startFromNode', startFromNode);
91308
91309             var defaultTags = { area: 'yes' };
91310             if (mode.preset) { defaultTags = mode.preset.setTags(defaultTags, 'area'); }
91311
91312
91313             function actionClose(wayId) {
91314                 return function (graph) {
91315                     return graph.replace(graph.entity(wayId).close());
91316                 };
91317             }
91318
91319
91320             function start(loc) {
91321                 var startGraph = context.graph();
91322                 var node = osmNode({ loc: loc });
91323                 var way = osmWay({ tags: defaultTags });
91324
91325                 context.perform(
91326                     actionAddEntity(node),
91327                     actionAddEntity(way),
91328                     actionAddVertex(way.id, node.id),
91329                     actionClose(way.id)
91330                 );
91331
91332                 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
91333             }
91334
91335
91336             function startFromWay(loc, edge) {
91337                 var startGraph = context.graph();
91338                 var node = osmNode({ loc: loc });
91339                 var way = osmWay({ tags: defaultTags });
91340
91341                 context.perform(
91342                     actionAddEntity(node),
91343                     actionAddEntity(way),
91344                     actionAddVertex(way.id, node.id),
91345                     actionClose(way.id),
91346                     actionAddMidpoint({ loc: loc, edge: edge }, node)
91347                 );
91348
91349                 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
91350             }
91351
91352
91353             function startFromNode(node) {
91354                 var startGraph = context.graph();
91355                 var way = osmWay({ tags: defaultTags });
91356
91357                 context.perform(
91358                     actionAddEntity(way),
91359                     actionAddVertex(way.id, node.id),
91360                     actionClose(way.id)
91361                 );
91362
91363                 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
91364             }
91365
91366
91367             mode.enter = function() {
91368                 context.install(behavior);
91369             };
91370
91371
91372             mode.exit = function() {
91373                 context.uninstall(behavior);
91374             };
91375
91376
91377             return mode;
91378         }
91379
91380         function modeAddLine(context, mode) {
91381             mode.id = 'add-line';
91382
91383             var behavior = behaviorAddWay(context)
91384                 .on('start', start)
91385                 .on('startFromWay', startFromWay)
91386                 .on('startFromNode', startFromNode);
91387
91388             var defaultTags = {};
91389             if (mode.preset) { defaultTags = mode.preset.setTags(defaultTags, 'line'); }
91390
91391
91392             function start(loc) {
91393                 var startGraph = context.graph();
91394                 var node = osmNode({ loc: loc });
91395                 var way = osmWay({ tags: defaultTags });
91396
91397                 context.perform(
91398                     actionAddEntity(node),
91399                     actionAddEntity(way),
91400                     actionAddVertex(way.id, node.id)
91401                 );
91402
91403                 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
91404             }
91405
91406
91407             function startFromWay(loc, edge) {
91408                 var startGraph = context.graph();
91409                 var node = osmNode({ loc: loc });
91410                 var way = osmWay({ tags: defaultTags });
91411
91412                 context.perform(
91413                     actionAddEntity(node),
91414                     actionAddEntity(way),
91415                     actionAddVertex(way.id, node.id),
91416                     actionAddMidpoint({ loc: loc, edge: edge }, node)
91417                 );
91418
91419                 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
91420             }
91421
91422
91423             function startFromNode(node) {
91424                 var startGraph = context.graph();
91425                 var way = osmWay({ tags: defaultTags });
91426
91427                 context.perform(
91428                     actionAddEntity(way),
91429                     actionAddVertex(way.id, node.id)
91430                 );
91431
91432                 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
91433             }
91434
91435
91436             mode.enter = function() {
91437                 context.install(behavior);
91438             };
91439
91440
91441             mode.exit = function() {
91442                 context.uninstall(behavior);
91443             };
91444
91445             return mode;
91446         }
91447
91448         function modeAddPoint(context, mode) {
91449
91450             mode.id = 'add-point';
91451
91452             var behavior = behaviorDraw(context)
91453                 .on('click', add)
91454                 .on('clickWay', addWay)
91455                 .on('clickNode', addNode)
91456                 .on('cancel', cancel)
91457                 .on('finish', cancel);
91458
91459             var defaultTags = {};
91460             if (mode.preset) { defaultTags = mode.preset.setTags(defaultTags, 'point'); }
91461
91462
91463             function add(loc) {
91464                 var node = osmNode({ loc: loc, tags: defaultTags });
91465
91466                 context.perform(
91467                     actionAddEntity(node),
91468                     _t('operations.add.annotation.point')
91469                 );
91470
91471                 enterSelectMode(node);
91472             }
91473
91474
91475             function addWay(loc, edge) {
91476                 var node = osmNode({ tags: defaultTags });
91477
91478                 context.perform(
91479                     actionAddMidpoint({loc: loc, edge: edge}, node),
91480                     _t('operations.add.annotation.vertex')
91481                 );
91482
91483                 enterSelectMode(node);
91484             }
91485
91486             function enterSelectMode(node) {
91487                 context.enter(
91488                     modeSelect(context, [node.id]).newFeature(true)
91489                 );
91490             }
91491
91492
91493             function addNode(node) {
91494                 if (Object.keys(defaultTags).length === 0) {
91495                     enterSelectMode(node);
91496                     return;
91497                 }
91498
91499                 var tags = Object.assign({}, node.tags);  // shallow copy
91500                 for (var key in defaultTags) {
91501                     tags[key] = defaultTags[key];
91502                 }
91503
91504                 context.perform(
91505                     actionChangeTags(node.id, tags),
91506                     _t('operations.add.annotation.point')
91507                 );
91508
91509                 enterSelectMode(node);
91510             }
91511
91512
91513             function cancel() {
91514                 context.enter(modeBrowse(context));
91515             }
91516
91517
91518             mode.enter = function() {
91519                 context.install(behavior);
91520             };
91521
91522
91523             mode.exit = function() {
91524                 context.uninstall(behavior);
91525             };
91526
91527
91528             return mode;
91529         }
91530
91531         function modeAddNote(context) {
91532             var mode = {
91533                 id: 'add-note',
91534                 button: 'note',
91535                 title: _t('modes.add_note.title'),
91536                 description: _t('modes.add_note.description'),
91537                 key: _t('modes.add_note.key')
91538             };
91539
91540             var behavior = behaviorDraw(context)
91541                 .on('click', add)
91542                 .on('cancel', cancel)
91543                 .on('finish', cancel);
91544
91545
91546             function add(loc) {
91547                 var osm = services.osm;
91548                 if (!osm) { return; }
91549
91550                 var note = osmNote({ loc: loc, status: 'open', comments: [] });
91551                 osm.replaceNote(note);
91552
91553                 // force a reraw (there is no history change that would otherwise do this)
91554                 context.map().pan([0,0]);
91555
91556                 context
91557                     .selectedNoteID(note.id)
91558                     .enter(modeSelectNote(context, note.id).newFeature(true));
91559             }
91560
91561
91562             function cancel() {
91563                 context.enter(modeBrowse(context));
91564             }
91565
91566
91567             mode.enter = function() {
91568                 context.install(behavior);
91569             };
91570
91571
91572             mode.exit = function() {
91573                 context.uninstall(behavior);
91574             };
91575
91576
91577             return mode;
91578         }
91579
91580         function uiConflicts(context) {
91581             var dispatch$1 = dispatch('cancel', 'save');
91582             var keybinding = utilKeybinding('conflicts');
91583             var _origChanges;
91584             var _conflictList;
91585             var _shownConflictIndex;
91586
91587
91588             function keybindingOn() {
91589                 select(document)
91590                     .call(keybinding.on('⎋', cancel, true));
91591             }
91592
91593             function keybindingOff() {
91594                 select(document)
91595                     .call(keybinding.unbind);
91596             }
91597
91598             function tryAgain() {
91599                 keybindingOff();
91600                 dispatch$1.call('save');
91601             }
91602
91603             function cancel() {
91604                 keybindingOff();
91605                 dispatch$1.call('cancel');
91606             }
91607
91608
91609             function conflicts(selection) {
91610                 keybindingOn();
91611
91612                 var headerEnter = selection.selectAll('.header')
91613                     .data([0])
91614                     .enter()
91615                     .append('div')
91616                     .attr('class', 'header fillL');
91617
91618                 headerEnter
91619                     .append('button')
91620                     .attr('class', 'fr')
91621                     .on('click', cancel)
91622                     .call(svgIcon('#iD-icon-close'));
91623
91624                 headerEnter
91625                     .append('h3')
91626                     .text(_t('save.conflict.header'));
91627
91628                 var bodyEnter = selection.selectAll('.body')
91629                     .data([0])
91630                     .enter()
91631                     .append('div')
91632                     .attr('class', 'body fillL');
91633
91634                 var conflictsHelpEnter = bodyEnter
91635                     .append('div')
91636                     .attr('class', 'conflicts-help')
91637                     .text(_t('save.conflict.help'));
91638
91639
91640                 // Download changes link
91641                 var detected = utilDetect();
91642                 var changeset = new osmChangeset();
91643
91644                 delete changeset.id;  // Export without changeset_id
91645
91646                 var data = JXON.stringify(changeset.osmChangeJXON(_origChanges));
91647                 var blob = new Blob([data], { type: 'text/xml;charset=utf-8;' });
91648                 var fileName = 'changes.osc';
91649
91650                 var linkEnter = conflictsHelpEnter.selectAll('.download-changes')
91651                     .append('a')
91652                     .attr('class', 'download-changes');
91653
91654                 if (detected.download) {      // All except IE11 and Edge
91655                     linkEnter                 // download the data as a file
91656                         .attr('href', window.URL.createObjectURL(blob))
91657                         .attr('download', fileName);
91658
91659                 } else {                      // IE11 and Edge
91660                     linkEnter                 // open data uri in a new tab
91661                         .attr('target', '_blank')
91662                         .on('click.download', function() {
91663                             navigator.msSaveBlob(blob, fileName);
91664                         });
91665                 }
91666
91667                 linkEnter
91668                     .call(svgIcon('#iD-icon-load', 'inline'))
91669                     .append('span')
91670                     .text(_t('save.conflict.download_changes'));
91671
91672
91673                 bodyEnter
91674                     .append('div')
91675                     .attr('class', 'conflict-container fillL3')
91676                     .call(showConflict, 0);
91677
91678                 bodyEnter
91679                     .append('div')
91680                     .attr('class', 'conflicts-done')
91681                     .attr('opacity', 0)
91682                     .style('display', 'none')
91683                     .text(_t('save.conflict.done'));
91684
91685                 var buttonsEnter = bodyEnter
91686                     .append('div')
91687                     .attr('class','buttons col12 joined conflicts-buttons');
91688
91689                 buttonsEnter
91690                     .append('button')
91691                     .attr('disabled', _conflictList.length > 1)
91692                     .attr('class', 'action conflicts-button col6')
91693                     .text(_t('save.title'))
91694                     .on('click.try_again', tryAgain);
91695
91696                 buttonsEnter
91697                     .append('button')
91698                     .attr('class', 'secondary-action conflicts-button col6')
91699                     .text(_t('confirm.cancel'))
91700                     .on('click.cancel', cancel);
91701             }
91702
91703
91704             function showConflict(selection, index) {
91705                 index = utilWrap(index, _conflictList.length);
91706                 _shownConflictIndex = index;
91707
91708                 var parent = select(selection.node().parentNode);
91709
91710                 // enable save button if this is the last conflict being reviewed..
91711                 if (index === _conflictList.length - 1) {
91712                     window.setTimeout(function() {
91713                         parent.select('.conflicts-button')
91714                             .attr('disabled', null);
91715
91716                         parent.select('.conflicts-done')
91717                             .transition()
91718                             .attr('opacity', 1)
91719                             .style('display', 'block');
91720                     }, 250);
91721                 }
91722
91723                 var conflict = selection
91724                     .selectAll('.conflict')
91725                     .data([_conflictList[index]]);
91726
91727                 conflict.exit()
91728                     .remove();
91729
91730                 var conflictEnter = conflict.enter()
91731                     .append('div')
91732                     .attr('class', 'conflict');
91733
91734                 conflictEnter
91735                     .append('h4')
91736                     .attr('class', 'conflict-count')
91737                     .text(_t('save.conflict.count', { num: index + 1, total: _conflictList.length }));
91738
91739                 conflictEnter
91740                     .append('a')
91741                     .attr('class', 'conflict-description')
91742                     .attr('href', '#')
91743                     .text(function(d) { return d.name; })
91744                     .on('click', function(d) {
91745                         event.preventDefault();
91746                         zoomToEntity(d.id);
91747                     });
91748
91749                 var details = conflictEnter
91750                     .append('div')
91751                     .attr('class', 'conflict-detail-container');
91752
91753                 details
91754                     .append('ul')
91755                     .attr('class', 'conflict-detail-list')
91756                     .selectAll('li')
91757                     .data(function(d) { return d.details || []; })
91758                     .enter()
91759                     .append('li')
91760                     .attr('class', 'conflict-detail-item')
91761                     .html(function(d) { return d; });
91762
91763                 details
91764                     .append('div')
91765                     .attr('class', 'conflict-choices')
91766                     .call(addChoices);
91767
91768                 details
91769                     .append('div')
91770                     .attr('class', 'conflict-nav-buttons joined cf')
91771                     .selectAll('button')
91772                     .data(['previous', 'next'])
91773                     .enter()
91774                     .append('button')
91775                     .text(function(d) { return _t('save.conflict.' + d); })
91776                     .attr('class', 'conflict-nav-button action col6')
91777                     .attr('disabled', function(d, i) {
91778                         return (i === 0 && index === 0) ||
91779                             (i === 1 && index === _conflictList.length - 1) || null;
91780                     })
91781                     .on('click', function(d, i) {
91782                         event.preventDefault();
91783
91784                         var container = parent.selectAll('.conflict-container');
91785                         var sign = (i === 0 ? -1 : 1);
91786
91787                         container
91788                             .selectAll('.conflict')
91789                             .remove();
91790
91791                         container
91792                             .call(showConflict, index + sign);
91793                     });
91794
91795             }
91796
91797
91798             function addChoices(selection) {
91799                 var choices = selection
91800                     .append('ul')
91801                     .attr('class', 'layer-list')
91802                     .selectAll('li')
91803                     .data(function(d) { return d.choices || []; });
91804
91805                 // enter
91806                 var choicesEnter = choices.enter()
91807                     .append('li')
91808                     .attr('class', 'layer');
91809
91810                 var labelEnter = choicesEnter
91811                     .append('label');
91812
91813                 labelEnter
91814                     .append('input')
91815                     .attr('type', 'radio')
91816                     .attr('name', function(d) { return d.id; })
91817                     .on('change', function(d, i) {
91818                         var ul = this.parentNode.parentNode.parentNode;
91819                         ul.__data__.chosen = i;
91820                         choose(ul, d);
91821                     });
91822
91823                 labelEnter
91824                     .append('span')
91825                     .text(function(d) { return d.text; });
91826
91827                 // update
91828                 choicesEnter
91829                     .merge(choices)
91830                     .each(function(d, i) {
91831                         var ul = this.parentNode;
91832                         if (ul.__data__.chosen === i) {
91833                             choose(ul, d);
91834                         }
91835                     });
91836             }
91837
91838
91839             function choose(ul, datum) {
91840                 if (event) { event.preventDefault(); }
91841
91842                 select(ul)
91843                     .selectAll('li')
91844                     .classed('active', function(d) { return d === datum; })
91845                     .selectAll('input')
91846                     .property('checked', function(d) { return d === datum; });
91847
91848                 var extent = geoExtent();
91849                 var entity;
91850
91851                 entity = context.graph().hasEntity(datum.id);
91852                 if (entity) { extent._extend(entity.extent(context.graph())); }
91853
91854                 datum.action();
91855
91856                 entity = context.graph().hasEntity(datum.id);
91857                 if (entity) { extent._extend(entity.extent(context.graph())); }
91858
91859                 zoomToEntity(datum.id, extent);
91860             }
91861
91862
91863             function zoomToEntity(id, extent) {
91864                 context.surface().selectAll('.hover')
91865                     .classed('hover', false);
91866
91867                 var entity = context.graph().hasEntity(id);
91868                 if (entity) {
91869                     if (extent) {
91870                         context.map().trimmedExtent(extent);
91871                     } else {
91872                         context.map().zoomToEase(entity);
91873                     }
91874                     context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph()))
91875                         .classed('hover', true);
91876                 }
91877             }
91878
91879
91880             // The conflict list should be an array of objects like:
91881             // {
91882             //     id: id,
91883             //     name: entityName(local),
91884             //     details: merge.conflicts(),
91885             //     chosen: 1,
91886             //     choices: [
91887             //         choice(id, keepMine, forceLocal),
91888             //         choice(id, keepTheirs, forceRemote)
91889             //     ]
91890             // }
91891             conflicts.conflictList = function(_) {
91892                 if (!arguments.length) { return _conflictList; }
91893                 _conflictList = _;
91894                 return conflicts;
91895             };
91896
91897
91898             conflicts.origChanges = function(_) {
91899                 if (!arguments.length) { return _origChanges; }
91900                 _origChanges = _;
91901                 return conflicts;
91902             };
91903
91904
91905             conflicts.shownEntityIds = function() {
91906                 if (_conflictList && typeof _shownConflictIndex === 'number') {
91907                     return [_conflictList[_shownConflictIndex].id];
91908                 }
91909                 return [];
91910             };
91911
91912
91913             return utilRebind(conflicts, dispatch$1, 'on');
91914         }
91915
91916         function uiConfirm(selection) {
91917             var modalSelection = uiModal(selection);
91918
91919             modalSelection.select('.modal')
91920                 .classed('modal-alert', true);
91921
91922             var section = modalSelection.select('.content');
91923
91924             section.append('div')
91925                 .attr('class', 'modal-section header');
91926
91927             section.append('div')
91928                 .attr('class', 'modal-section message-text');
91929
91930             var buttons = section.append('div')
91931                 .attr('class', 'modal-section buttons cf');
91932
91933
91934             modalSelection.okButton = function() {
91935                 buttons
91936                     .append('button')
91937                     .attr('class', 'button ok-button action')
91938                     .on('click.confirm', function() {
91939                         modalSelection.remove();
91940                     })
91941                     .text(_t('confirm.okay'))
91942                     .node()
91943                     .focus();
91944
91945                 return modalSelection;
91946             };
91947
91948
91949             return modalSelection;
91950         }
91951
91952         function uiChangesetEditor(context) {
91953             var dispatch$1 = dispatch('change');
91954             var formFields = uiFormFields(context);
91955             var commentCombo = uiCombobox(context, 'comment').caseSensitive(true);
91956             var _fieldsArr;
91957             var _tags;
91958             var _changesetID;
91959
91960
91961             function changesetEditor(selection) {
91962                 render(selection);
91963             }
91964
91965
91966             function render(selection) {
91967                 var initial = false;
91968
91969                 if (!_fieldsArr) {
91970                     initial = true;
91971                     var presets = _mainPresetIndex;
91972
91973                     _fieldsArr = [
91974                         uiField(context, presets.field('comment'), null, { show: true, revert: false }),
91975                         uiField(context, presets.field('source'), null, { show: false, revert: false }),
91976                         uiField(context, presets.field('hashtags'), null, { show: false, revert: false }) ];
91977
91978                     _fieldsArr.forEach(function(field) {
91979                         field
91980                             .on('change', function(t, onInput) {
91981                                 dispatch$1.call('change', field, undefined, t, onInput);
91982                             });
91983                     });
91984                 }
91985
91986                 _fieldsArr.forEach(function(field) {
91987                     field
91988                         .tags(_tags);
91989                 });
91990
91991
91992                 selection
91993                     .call(formFields.fieldsArr(_fieldsArr));
91994
91995
91996                 if (initial) {
91997                     var commentField = selection.select('.form-field-comment textarea');
91998                     var commentNode = commentField.node();
91999
92000                     if (commentNode) {
92001                         commentNode.focus();
92002                         commentNode.select();
92003                     }
92004
92005                     // trigger a 'blur' event so that comment field can be cleaned
92006                     // and checked for hashtags, even if retrieved from localstorage
92007                     utilTriggerEvent(commentField, 'blur');
92008
92009                     var osm = context.connection();
92010                     if (osm) {
92011                         osm.userChangesets(function (err, changesets) {
92012                             if (err) { return; }
92013
92014                             var comments = changesets.map(function(changeset) {
92015                                 var comment = changeset.tags.comment;
92016                                 return comment ? { title: comment, value: comment } : null;
92017                             }).filter(Boolean);
92018
92019                             commentField
92020                                 .call(commentCombo
92021                                     .data(utilArrayUniqBy(comments, 'title'))
92022                                 );
92023                         });
92024                     }
92025                 }
92026
92027                 // Add warning if comment mentions Google
92028                 var hasGoogle = _tags.comment.match(/google/i);
92029                 var commentWarning = selection.select('.form-field-comment').selectAll('.comment-warning')
92030                     .data(hasGoogle ? [0] : []);
92031
92032                 commentWarning.exit()
92033                     .transition()
92034                     .duration(200)
92035                     .style('opacity', 0)
92036                     .remove();
92037
92038                 var commentEnter = commentWarning.enter()
92039                     .insert('div', '.tag-reference-body')
92040                     .attr('class', 'field-warning comment-warning')
92041                     .style('opacity', 0);
92042
92043                 commentEnter
92044                     .append('a')
92045                     .attr('target', '_blank')
92046                     .attr('tabindex', -1)
92047                     .call(svgIcon('#iD-icon-alert', 'inline'))
92048                     .attr('href', _t('commit.google_warning_link'))
92049                     .append('span')
92050                     .text(_t('commit.google_warning'));
92051
92052                 commentEnter
92053                     .transition()
92054                     .duration(200)
92055                     .style('opacity', 1);
92056             }
92057
92058
92059             changesetEditor.tags = function(_) {
92060                 if (!arguments.length) { return _tags; }
92061                 _tags = _;
92062                 // Don't reset _fieldsArr here.
92063                 return changesetEditor;
92064             };
92065
92066
92067             changesetEditor.changesetID = function(_) {
92068                 if (!arguments.length) { return _changesetID; }
92069                 if (_changesetID === _) { return changesetEditor; }
92070                 _changesetID = _;
92071                 _fieldsArr = null;
92072                 return changesetEditor;
92073             };
92074
92075
92076             return utilRebind(changesetEditor, dispatch$1, 'on');
92077         }
92078
92079         function uiSectionChanges(context) {
92080             var detected = utilDetect();
92081
92082             var _discardTags = {};
92083             _mainFileFetcher.get('discarded')
92084                 .then(function(d) { _discardTags = d; })
92085                 .catch(function() { /* ignore */ });
92086
92087             var section = uiSection('changes-list', context)
92088                 .title(function() {
92089                     var history = context.history();
92090                     var summary = history.difference().summary();
92091                     return _t('commit.changes', { count: summary.length });
92092                 })
92093                 .disclosureContent(renderDisclosureContent);
92094
92095             function renderDisclosureContent(selection) {
92096                 var history = context.history();
92097                 var summary = history.difference().summary();
92098
92099                 var container = selection.selectAll('.commit-section')
92100                     .data([0]);
92101
92102                 var containerEnter = container.enter()
92103                     .append('div')
92104                     .attr('class', 'commit-section');
92105
92106                 containerEnter
92107                     .append('ul')
92108                     .attr('class', 'changeset-list');
92109
92110                 container = containerEnter
92111                     .merge(container);
92112
92113
92114                 var items = container.select('ul').selectAll('li')
92115                     .data(summary);
92116
92117                 var itemsEnter = items.enter()
92118                     .append('li')
92119                     .attr('class', 'change-item');
92120
92121                 itemsEnter
92122                     .each(function(d) {
92123                         select(this)
92124                             .call(svgIcon('#iD-icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
92125                     });
92126
92127                 itemsEnter
92128                     .append('span')
92129                     .attr('class', 'change-type')
92130                     .text(function(d) { return _t('commit.' + d.changeType) + ' '; });
92131
92132                 itemsEnter
92133                     .append('strong')
92134                     .attr('class', 'entity-type')
92135                     .text(function(d) {
92136                         var matched = _mainPresetIndex.match(d.entity, d.graph);
92137                         return (matched && matched.name()) || utilDisplayType(d.entity.id);
92138                     });
92139
92140                 itemsEnter
92141                     .append('span')
92142                     .attr('class', 'entity-name')
92143                     .text(function(d) {
92144                         var name = utilDisplayName(d.entity) || '',
92145                             string = '';
92146                         if (name !== '') {
92147                             string += ':';
92148                         }
92149                         return string += ' ' + name;
92150                     });
92151
92152                 itemsEnter
92153                     .style('opacity', 0)
92154                     .transition()
92155                     .style('opacity', 1);
92156
92157                 items = itemsEnter
92158                     .merge(items);
92159
92160                 items
92161                     .on('mouseover', mouseover)
92162                     .on('mouseout', mouseout)
92163                     .on('click', click);
92164
92165
92166                 // Download changeset link
92167                 var changeset = new osmChangeset().update({ id: undefined });
92168                 var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
92169
92170                 delete changeset.id;  // Export without chnageset_id
92171
92172                 var data = JXON.stringify(changeset.osmChangeJXON(changes));
92173                 var blob = new Blob([data], {type: 'text/xml;charset=utf-8;'});
92174                 var fileName = 'changes.osc';
92175
92176                 var linkEnter = container.selectAll('.download-changes')
92177                     .data([0])
92178                     .enter()
92179                     .append('a')
92180                     .attr('class', 'download-changes');
92181
92182                 if (detected.download) {      // All except IE11 and Edge
92183                     linkEnter                 // download the data as a file
92184                         .attr('href', window.URL.createObjectURL(blob))
92185                         .attr('download', fileName);
92186
92187                 } else {                      // IE11 and Edge
92188                     linkEnter                 // open data uri in a new tab
92189                         .attr('target', '_blank')
92190                         .on('click.download', function() {
92191                             navigator.msSaveBlob(blob, fileName);
92192                         });
92193                 }
92194
92195                 linkEnter
92196                     .call(svgIcon('#iD-icon-load', 'inline'))
92197                     .append('span')
92198                     .text(_t('commit.download_changes'));
92199
92200
92201                 function mouseover(d) {
92202                     if (d.entity) {
92203                         context.surface().selectAll(
92204                             utilEntityOrMemberSelector([d.entity.id], context.graph())
92205                         ).classed('hover', true);
92206                     }
92207                 }
92208
92209
92210                 function mouseout() {
92211                     context.surface().selectAll('.hover')
92212                         .classed('hover', false);
92213                 }
92214
92215
92216                 function click(change) {
92217                     if (change.changeType !== 'deleted') {
92218                         var entity = change.entity;
92219                         context.map().zoomToEase(entity);
92220                         context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph()))
92221                             .classed('hover', true);
92222                     }
92223                 }
92224             }
92225
92226             return section;
92227         }
92228
92229         function uiCommitWarnings(context) {
92230
92231             function commitWarnings(selection) {
92232                 var issuesBySeverity = context.validator()
92233                     .getIssuesBySeverity({ what: 'edited', where: 'all', includeDisabledRules: true });
92234
92235                 for (var severity in issuesBySeverity) {
92236                     var issues = issuesBySeverity[severity];
92237                     var section = severity + '-section';
92238                     var issueItem = severity + '-item';
92239
92240                     var container = selection.selectAll('.' + section)
92241                         .data(issues.length ? [0] : []);
92242
92243                     container.exit()
92244                         .remove();
92245
92246                     var containerEnter = container.enter()
92247                         .append('div')
92248                         .attr('class', 'modal-section ' + section + ' fillL2');
92249
92250                     containerEnter
92251                         .append('h3')
92252                         .text(severity === 'warning' ? _t('commit.warnings') : _t('commit.errors'));
92253
92254                     containerEnter
92255                         .append('ul')
92256                         .attr('class', 'changeset-list');
92257
92258                     container = containerEnter
92259                         .merge(container);
92260
92261
92262                     var items = container.select('ul').selectAll('li')
92263                         .data(issues, function(d) { return d.id; });
92264
92265                     items.exit()
92266                         .remove();
92267
92268                     var itemsEnter = items.enter()
92269                         .append('li')
92270                         .attr('class', issueItem);
92271
92272                     itemsEnter
92273                         .call(svgIcon('#iD-icon-alert', 'pre-text'));
92274
92275                     itemsEnter
92276                         .append('strong')
92277                         .attr('class', 'issue-message');
92278
92279                     itemsEnter.filter(function(d) { return d.tooltip; })
92280                         .call(uiTooltip()
92281                             .title(function(d) { return d.tooltip; })
92282                             .placement('top')
92283                         );
92284
92285                     items = itemsEnter
92286                         .merge(items);
92287
92288                     items.selectAll('.issue-message')
92289                         .text(function(d) {
92290                             return d.message(context);
92291                         });
92292
92293                     items
92294                         .on('mouseover', function(d) {
92295                             if (d.entityIds) {
92296                                 context.surface().selectAll(
92297                                     utilEntityOrMemberSelector(
92298                                         d.entityIds,
92299                                         context.graph()
92300                                     )
92301                                 ).classed('hover', true);
92302                             }
92303                         })
92304                         .on('mouseout', function() {
92305                             context.surface().selectAll('.hover')
92306                                 .classed('hover', false);
92307                         })
92308                         .on('click', function(d) {
92309                             context.validator().focusIssue(d);
92310                         });
92311                 }
92312             }
92313
92314
92315             return commitWarnings;
92316         }
92317
92318         var readOnlyTags = [
92319             /^changesets_count$/,
92320             /^created_by$/,
92321             /^ideditor:/,
92322             /^imagery_used$/,
92323             /^host$/,
92324             /^locale$/,
92325             /^warnings:/,
92326             /^resolved:/,
92327             /^closed:note$/,
92328             /^closed:keepright$/,
92329             /^closed:improveosm:/,
92330             /^closed:osmose:/
92331         ];
92332
92333         // treat most punctuation (except -, _, +, &) as hashtag delimiters - #4398
92334         // from https://stackoverflow.com/a/25575009
92335         var hashtagRegex = /(#[^\u2000-\u206F\u2E00-\u2E7F\s\\'!"#$%()*,.\/:;<=>?@\[\]^`{|}~]+)/g;
92336
92337
92338         function uiCommit(context) {
92339             var dispatch$1 = dispatch('cancel');
92340             var _userDetails;
92341             var _selection;
92342
92343             var changesetEditor = uiChangesetEditor(context)
92344                 .on('change', changeTags);
92345             var rawTagEditor = uiSectionRawTagEditor('changeset-tag-editor', context)
92346                 .on('change', changeTags)
92347                 .readOnlyTags(readOnlyTags);
92348             var commitChanges = uiSectionChanges(context);
92349             var commitWarnings = uiCommitWarnings(context);
92350
92351
92352             function commit(selection) {
92353                 _selection = selection;
92354
92355                 // Initialize changeset if one does not exist yet.
92356                 if (!context.changeset) { initChangeset(); }
92357
92358                 loadDerivedChangesetTags();
92359
92360                 selection.call(render);
92361             }
92362
92363             function initChangeset() {
92364
92365                 // expire stored comment, hashtags, source after cutoff datetime - #3947 #4899
92366                 var commentDate = +corePreferences('commentDate') || 0;
92367                 var currDate = Date.now();
92368                 var cutoff = 2 * 86400 * 1000;   // 2 days
92369                 if (commentDate > currDate || currDate - commentDate > cutoff) {
92370                     corePreferences('comment', null);
92371                     corePreferences('hashtags', null);
92372                     corePreferences('source', null);
92373                 }
92374
92375                 // load in explicitly-set values, if any
92376                 if (context.defaultChangesetComment()) {
92377                     corePreferences('comment', context.defaultChangesetComment());
92378                     corePreferences('commentDate', Date.now());
92379                 }
92380                 if (context.defaultChangesetSource()) {
92381                     corePreferences('source', context.defaultChangesetSource());
92382                     corePreferences('commentDate', Date.now());
92383                 }
92384                 if (context.defaultChangesetHashtags()) {
92385                     corePreferences('hashtags', context.defaultChangesetHashtags());
92386                     corePreferences('commentDate', Date.now());
92387                 }
92388
92389                 var detected = utilDetect();
92390                 var tags = {
92391                     comment: corePreferences('comment') || '',
92392                     created_by: context.cleanTagValue('iD ' + context.version),
92393                     host: context.cleanTagValue(detected.host),
92394                     locale: context.cleanTagValue(_mainLocalizer.localeCode())
92395                 };
92396
92397                 // call findHashtags initially - this will remove stored
92398                 // hashtags if any hashtags are found in the comment - #4304
92399                 findHashtags(tags, true);
92400
92401                 var hashtags = corePreferences('hashtags');
92402                 if (hashtags) {
92403                     tags.hashtags = hashtags;
92404                 }
92405
92406                 var source = corePreferences('source');
92407                 if (source) {
92408                     tags.source = source;
92409                 }
92410                 var photoOverlaysUsed = context.history().photoOverlaysUsed();
92411                 if (photoOverlaysUsed.length) {
92412                     var sources = (tags.source || '').split(';');
92413
92414                     // include this tag for any photo layer
92415                     if (sources.indexOf('streetlevel imagery') === -1) {
92416                         sources.push('streetlevel imagery');
92417                     }
92418
92419                     // add the photo overlays used during editing as sources
92420                     photoOverlaysUsed.forEach(function(photoOverlay) {
92421                         if (sources.indexOf(photoOverlay) === -1) {
92422                             sources.push(photoOverlay);
92423                         }
92424                     });
92425
92426                     tags.source = context.cleanTagValue(sources.join(';'));
92427                 }
92428
92429                 context.changeset = new osmChangeset({ tags: tags });
92430             }
92431
92432             // Calculates read-only metadata tags based on the user's editing session and applies
92433             // them to the changeset.
92434             function loadDerivedChangesetTags() {
92435
92436                 var osm = context.connection();
92437                 if (!osm) { return; }
92438
92439                 var tags = Object.assign({}, context.changeset.tags);   // shallow copy
92440
92441                 // assign tags for imagery used
92442                 var imageryUsed = context.cleanTagValue(context.history().imageryUsed().join(';'));
92443                 tags.imagery_used = imageryUsed || 'None';
92444
92445                 // assign tags for closed issues and notes
92446                 var osmClosed = osm.getClosedIDs();
92447                 var itemType;
92448                 if (osmClosed.length) {
92449                     tags['closed:note'] = context.cleanTagValue(osmClosed.join(';'));
92450                 }
92451                 if (services.keepRight) {
92452                     var krClosed = services.keepRight.getClosedIDs();
92453                     if (krClosed.length) {
92454                         tags['closed:keepright'] = context.cleanTagValue(krClosed.join(';'));
92455                     }
92456                 }
92457                 if (services.improveOSM) {
92458                     var iOsmClosed = services.improveOSM.getClosedCounts();
92459                     for (itemType in iOsmClosed) {
92460                         tags['closed:improveosm:' + itemType] = context.cleanTagValue(iOsmClosed[itemType].toString());
92461                     }
92462                 }
92463                 if (services.osmose) {
92464                     var osmoseClosed = services.osmose.getClosedCounts();
92465                     for (itemType in osmoseClosed) {
92466                         tags['closed:osmose:' + itemType] = context.cleanTagValue(osmoseClosed[itemType].toString());
92467                     }
92468                 }
92469
92470                 // remove existing issue counts
92471                 for (var key in tags) {
92472                     if (key.match(/(^warnings:)|(^resolved:)/)) {
92473                         delete tags[key];
92474                     }
92475                 }
92476
92477                 function addIssueCounts(issues, prefix) {
92478                     var issuesByType = utilArrayGroupBy(issues, 'type');
92479                     for (var issueType in issuesByType) {
92480                         var issuesOfType = issuesByType[issueType];
92481                         if (issuesOfType[0].subtype) {
92482                             var issuesBySubtype = utilArrayGroupBy(issuesOfType, 'subtype');
92483                             for (var issueSubtype in issuesBySubtype) {
92484                                 var issuesOfSubtype = issuesBySubtype[issueSubtype];
92485                                 tags[prefix + ':' + issueType + ':' + issueSubtype] = context.cleanTagValue(issuesOfSubtype.length.toString());
92486                             }
92487                         } else {
92488                             tags[prefix + ':' + issueType] = context.cleanTagValue(issuesOfType.length.toString());
92489                         }
92490                     }
92491                 }
92492
92493                 // add counts of warnings generated by the user's edits
92494                 var warnings = context.validator()
92495                     .getIssuesBySeverity({ what: 'edited', where: 'all', includeIgnored: true, includeDisabledRules: true }).warning;
92496                 addIssueCounts(warnings, 'warnings');
92497
92498                 // add counts of issues resolved by the user's edits
92499                 var resolvedIssues = context.validator().getResolvedIssues();
92500                 addIssueCounts(resolvedIssues, 'resolved');
92501
92502                 context.changeset = context.changeset.update({ tags: tags });
92503             }
92504
92505             function render(selection) {
92506
92507                 var osm = context.connection();
92508                 if (!osm) { return; }
92509
92510                 var header = selection.selectAll('.header')
92511                     .data([0]);
92512
92513                 var headerTitle = header.enter()
92514                     .append('div')
92515                     .attr('class', 'header fillL header-container');
92516
92517                 headerTitle
92518                     .append('div')
92519                     .attr('class', 'header-block header-block-outer');
92520
92521                 headerTitle
92522                     .append('div')
92523                     .attr('class', 'header-block')
92524                     .append('h3')
92525                     .text(_t('commit.title'));
92526
92527                 headerTitle
92528                     .append('div')
92529                     .attr('class', 'header-block header-block-outer header-block-close')
92530                     .append('button')
92531                     .attr('class', 'close')
92532                     .on('click', function() {
92533                         dispatch$1.call('cancel', this);
92534                     })
92535                     .call(svgIcon('#iD-icon-close'));
92536
92537                 var body = selection.selectAll('.body')
92538                     .data([0]);
92539
92540                 body = body.enter()
92541                     .append('div')
92542                     .attr('class', 'body')
92543                     .merge(body);
92544
92545
92546                 // Changeset Section
92547                 var changesetSection = body.selectAll('.changeset-editor')
92548                     .data([0]);
92549
92550                 changesetSection = changesetSection.enter()
92551                     .append('div')
92552                     .attr('class', 'modal-section changeset-editor')
92553                     .merge(changesetSection);
92554
92555                 changesetSection
92556                     .call(changesetEditor
92557                         .changesetID(context.changeset.id)
92558                         .tags(context.changeset.tags)
92559                     );
92560
92561
92562                 // Warnings
92563                 body.call(commitWarnings);
92564
92565
92566                 // Upload Explanation
92567                 var saveSection = body.selectAll('.save-section')
92568                     .data([0]);
92569
92570                 saveSection = saveSection.enter()
92571                     .append('div')
92572                     .attr('class','modal-section save-section fillL')
92573                     .merge(saveSection);
92574
92575                 var prose = saveSection.selectAll('.commit-info')
92576                     .data([0]);
92577
92578                 if (prose.enter().size()) {   // first time, make sure to update user details in prose
92579                     _userDetails = null;
92580                 }
92581
92582                 prose = prose.enter()
92583                     .append('p')
92584                     .attr('class', 'commit-info')
92585                     .text(_t('commit.upload_explanation'))
92586                     .merge(prose);
92587
92588                 // always check if this has changed, but only update prose.html()
92589                 // if needed, because it can trigger a style recalculation
92590                 osm.userDetails(function(err, user) {
92591                     if (err) { return; }
92592
92593                     if (_userDetails === user) { return; }  // no change
92594                     _userDetails = user;
92595
92596                     var userLink = select(document.createElement('div'));
92597
92598                     if (user.image_url) {
92599                         userLink
92600                             .append('img')
92601                             .attr('src', user.image_url)
92602                             .attr('class', 'icon pre-text user-icon');
92603                     }
92604
92605                     userLink
92606                         .append('a')
92607                         .attr('class', 'user-info')
92608                         .text(user.display_name)
92609                         .attr('href', osm.userURL(user.display_name))
92610                         .attr('target', '_blank');
92611
92612                     prose
92613                         .html(_t('commit.upload_explanation_with_user', { user: userLink.html() }));
92614                 });
92615
92616
92617                 // Request Review
92618                 var requestReview = saveSection.selectAll('.request-review')
92619                     .data([0]);
92620
92621                 // Enter
92622                 var requestReviewEnter = requestReview.enter()
92623                     .append('div')
92624                     .attr('class', 'request-review');
92625
92626                 var requestReviewDomId = utilUniqueDomId('commit-input-request-review');
92627
92628                 var labelEnter = requestReviewEnter
92629                     .append('label')
92630                     .attr('for', requestReviewDomId);
92631
92632                 labelEnter
92633                     .append('input')
92634                     .attr('type', 'checkbox')
92635                     .attr('id', requestReviewDomId);
92636
92637                 labelEnter
92638                     .append('span')
92639                     .text(_t('commit.request_review'));
92640
92641                 // Update
92642                 requestReview = requestReview
92643                     .merge(requestReviewEnter);
92644
92645                 var requestReviewInput = requestReview.selectAll('input')
92646                     .property('checked', isReviewRequested(context.changeset.tags))
92647                     .on('change', toggleRequestReview);
92648
92649
92650                 // Buttons
92651                 var buttonSection = saveSection.selectAll('.buttons')
92652                     .data([0]);
92653
92654                 // enter
92655                 var buttonEnter = buttonSection.enter()
92656                     .append('div')
92657                     .attr('class', 'buttons fillL');
92658
92659                 buttonEnter
92660                     .append('button')
92661                     .attr('class', 'secondary-action button cancel-button')
92662                     .append('span')
92663                     .attr('class', 'label')
92664                     .text(_t('commit.cancel'));
92665
92666                 var uploadButton = buttonEnter
92667                     .append('button')
92668                     .attr('class', 'action button save-button');
92669
92670                 uploadButton.append('span')
92671                     .attr('class', 'label')
92672                     .text(_t('commit.save'));
92673
92674                 var uploadBlockerTooltipText = getUploadBlockerMessage();
92675
92676                 // update
92677                 buttonSection = buttonSection
92678                     .merge(buttonEnter);
92679
92680                 buttonSection.selectAll('.cancel-button')
92681                     .on('click.cancel', function() {
92682                         dispatch$1.call('cancel', this);
92683                     });
92684
92685                 buttonSection.selectAll('.save-button')
92686                     .classed('disabled', uploadBlockerTooltipText !== null)
92687                     .on('click.save', function() {
92688                         if (!select(this).classed('disabled')) {
92689                             this.blur();    // avoid keeping focus on the button - #4641
92690                             context.uploader().save(context.changeset);
92691                         }
92692                     });
92693
92694                 // remove any existing tooltip
92695                 uiTooltip().destroyAny(buttonSection.selectAll('.save-button'));
92696
92697                 if (uploadBlockerTooltipText) {
92698                     buttonSection.selectAll('.save-button')
92699                         .call(uiTooltip().title(uploadBlockerTooltipText).placement('top'));
92700                 }
92701
92702                 // Raw Tag Editor
92703                 var tagSection = body.selectAll('.tag-section.raw-tag-editor')
92704                     .data([0]);
92705
92706                 tagSection = tagSection.enter()
92707                     .append('div')
92708                     .attr('class', 'modal-section tag-section raw-tag-editor')
92709                     .merge(tagSection);
92710
92711                 tagSection
92712                     .call(rawTagEditor
92713                         .tags(Object.assign({}, context.changeset.tags))   // shallow copy
92714                         .render
92715                     );
92716
92717                 var changesSection = body.selectAll('.commit-changes-section')
92718                     .data([0]);
92719
92720                 changesSection = changesSection.enter()
92721                     .append('div')
92722                     .attr('class', 'modal-section commit-changes-section')
92723                     .merge(changesSection);
92724
92725                 // Change summary
92726                 changesSection.call(commitChanges.render);
92727
92728
92729                 function toggleRequestReview() {
92730                     var rr = requestReviewInput.property('checked');
92731                     updateChangeset({ review_requested: (rr ? 'yes' : undefined) });
92732
92733                     tagSection
92734                         .call(rawTagEditor
92735                             .tags(Object.assign({}, context.changeset.tags))   // shallow copy
92736                             .render
92737                         );
92738                 }
92739             }
92740
92741
92742             function getUploadBlockerMessage() {
92743                 var errors = context.validator()
92744                     .getIssuesBySeverity({ what: 'edited', where: 'all' }).error;
92745
92746                 if (errors.length) {
92747                     return _t('commit.outstanding_errors_message', { count: errors.length });
92748
92749                 } else {
92750                     var hasChangesetComment = context.changeset && context.changeset.tags.comment && context.changeset.tags.comment.trim().length;
92751                     if (!hasChangesetComment) {
92752                         return _t('commit.comment_needed_message');
92753                     }
92754                 }
92755                 return null;
92756             }
92757
92758
92759             function changeTags(_, changed, onInput) {
92760                 if (changed.hasOwnProperty('comment')) {
92761                     if (changed.comment === undefined) {
92762                         changed.comment = '';
92763                     }
92764                     if (!onInput) {
92765                         corePreferences('comment', changed.comment);
92766                         corePreferences('commentDate', Date.now());
92767                     }
92768                 }
92769                 if (changed.hasOwnProperty('source')) {
92770                     if (changed.source === undefined) {
92771                         corePreferences('source', null);
92772                     } else if (!onInput) {
92773                         corePreferences('source', changed.source);
92774                         corePreferences('commentDate', Date.now());
92775                     }
92776                 }
92777                 // no need to update `prefs` for `hashtags` here since it's done in `updateChangeset`
92778
92779                 updateChangeset(changed, onInput);
92780
92781                 if (_selection) {
92782                     _selection.call(render);
92783                 }
92784             }
92785
92786
92787             function findHashtags(tags, commentOnly) {
92788                 var detectedHashtags = commentHashtags();
92789
92790                 if (detectedHashtags.length) {
92791                     // always remove stored hashtags if there are hashtags in the comment - #4304
92792                     corePreferences('hashtags', null);
92793                 }
92794                 if (!detectedHashtags.length || !commentOnly) {
92795                     detectedHashtags = detectedHashtags.concat(hashtagHashtags());
92796                 }
92797
92798                 var allLowerCase = new Set();
92799                 return detectedHashtags.filter(function(hashtag) {
92800                     // Compare tags as lowercase strings, but keep original case tags
92801                     var lowerCase = hashtag.toLowerCase();
92802                     if (!allLowerCase.has(lowerCase)) {
92803                         allLowerCase.add(lowerCase);
92804                         return true;
92805                     }
92806                     return false;
92807                 });
92808
92809                 // Extract hashtags from `comment`
92810                 function commentHashtags() {
92811                     var matches = (tags.comment || '')
92812                         .replace(/http\S*/g, '')  // drop anything that looks like a URL - #4289
92813                         .match(hashtagRegex);
92814
92815                     return matches || [];
92816                 }
92817
92818                 // Extract and clean hashtags from `hashtags`
92819                 function hashtagHashtags() {
92820                     var matches = (tags.hashtags || '')
92821                         .split(/[,;\s]+/)
92822                         .map(function (s) {
92823                             if (s[0] !== '#') { s = '#' + s; }    // prepend '#'
92824                             var matched = s.match(hashtagRegex);
92825                             return matched && matched[0];
92826                         }).filter(Boolean);                       // exclude falsy
92827
92828                     return matches || [];
92829                 }
92830             }
92831
92832
92833             function isReviewRequested(tags) {
92834                 var rr = tags.review_requested;
92835                 if (rr === undefined) { return false; }
92836                 rr = rr.trim().toLowerCase();
92837                 return !(rr === '' || rr === 'no');
92838             }
92839
92840
92841             function updateChangeset(changed, onInput) {
92842                 var tags = Object.assign({}, context.changeset.tags);   // shallow copy
92843
92844                 Object.keys(changed).forEach(function(k) {
92845                     var v = changed[k];
92846                     k = context.cleanTagKey(k);
92847                     if (readOnlyTags.indexOf(k) !== -1) { return; }
92848
92849                     if (k !== '' && v !== undefined) {
92850                         if (onInput) {
92851                             tags[k] = v;
92852                         } else {
92853                             tags[k] = context.cleanTagValue(v);
92854                         }
92855                     } else {
92856                         delete tags[k];
92857                     }
92858                 });
92859
92860                 if (!onInput) {
92861                     // when changing the comment, override hashtags with any found in comment.
92862                     var commentOnly = changed.hasOwnProperty('comment') && (changed.comment !== '');
92863                     var arr = findHashtags(tags, commentOnly);
92864                     if (arr.length) {
92865                         tags.hashtags = context.cleanTagValue(arr.join(';'));
92866                         corePreferences('hashtags', tags.hashtags);
92867                     } else {
92868                         delete tags.hashtags;
92869                         corePreferences('hashtags', null);
92870                     }
92871                 }
92872
92873                 // always update userdetails, just in case user reauthenticates as someone else
92874                 if (_userDetails && _userDetails.changesets_count !== undefined) {
92875                     var changesetsCount = parseInt(_userDetails.changesets_count, 10) + 1;  // #4283
92876                     tags.changesets_count = String(changesetsCount);
92877
92878                     // first 100 edits - new user
92879                     if (changesetsCount <= 100) {
92880                         var s;
92881                         s = corePreferences('walkthrough_completed');
92882                         if (s) {
92883                             tags['ideditor:walkthrough_completed'] = s;
92884                         }
92885
92886                         s = corePreferences('walkthrough_progress');
92887                         if (s) {
92888                             tags['ideditor:walkthrough_progress'] = s;
92889                         }
92890
92891                         s = corePreferences('walkthrough_started');
92892                         if (s) {
92893                             tags['ideditor:walkthrough_started'] = s;
92894                         }
92895                     }
92896                 } else {
92897                     delete tags.changesets_count;
92898                 }
92899
92900                 if (!fastDeepEqual(context.changeset.tags, tags)) {
92901                     context.changeset = context.changeset.update({ tags: tags });
92902                 }
92903             }
92904
92905
92906             commit.reset = function() {
92907                 context.changeset = null;
92908             };
92909
92910
92911             return utilRebind(commit, dispatch$1, 'on');
92912         }
92913
92914         var RADIUS = 6378137;
92915         var FLATTENING = 1/298.257223563;
92916         var POLAR_RADIUS$1 = 6356752.3142;
92917
92918         var wgs84 = {
92919                 RADIUS: RADIUS,
92920                 FLATTENING: FLATTENING,
92921                 POLAR_RADIUS: POLAR_RADIUS$1
92922         };
92923
92924         var geometry_1 = geometry;
92925         var ring = ringArea;
92926
92927         function geometry(_) {
92928             var area = 0, i;
92929             switch (_.type) {
92930                 case 'Polygon':
92931                     return polygonArea(_.coordinates);
92932                 case 'MultiPolygon':
92933                     for (i = 0; i < _.coordinates.length; i++) {
92934                         area += polygonArea(_.coordinates[i]);
92935                     }
92936                     return area;
92937                 case 'Point':
92938                 case 'MultiPoint':
92939                 case 'LineString':
92940                 case 'MultiLineString':
92941                     return 0;
92942                 case 'GeometryCollection':
92943                     for (i = 0; i < _.geometries.length; i++) {
92944                         area += geometry(_.geometries[i]);
92945                     }
92946                     return area;
92947             }
92948         }
92949
92950         function polygonArea(coords) {
92951             var area = 0;
92952             if (coords && coords.length > 0) {
92953                 area += Math.abs(ringArea(coords[0]));
92954                 for (var i = 1; i < coords.length; i++) {
92955                     area -= Math.abs(ringArea(coords[i]));
92956                 }
92957             }
92958             return area;
92959         }
92960
92961         /**
92962          * Calculate the approximate area of the polygon were it projected onto
92963          *     the earth.  Note that this area will be positive if ring is oriented
92964          *     clockwise, otherwise it will be negative.
92965          *
92966          * Reference:
92967          * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
92968          *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
92969          *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
92970          *
92971          * Returns:
92972          * {float} The approximate signed geodesic area of the polygon in square
92973          *     meters.
92974          */
92975
92976         function ringArea(coords) {
92977             var p1, p2, p3, lowerIndex, middleIndex, upperIndex, i,
92978             area = 0,
92979             coordsLength = coords.length;
92980
92981             if (coordsLength > 2) {
92982                 for (i = 0; i < coordsLength; i++) {
92983                     if (i === coordsLength - 2) {// i = N-2
92984                         lowerIndex = coordsLength - 2;
92985                         middleIndex = coordsLength -1;
92986                         upperIndex = 0;
92987                     } else if (i === coordsLength - 1) {// i = N-1
92988                         lowerIndex = coordsLength - 1;
92989                         middleIndex = 0;
92990                         upperIndex = 1;
92991                     } else { // i = 0 to N-3
92992                         lowerIndex = i;
92993                         middleIndex = i+1;
92994                         upperIndex = i+2;
92995                     }
92996                     p1 = coords[lowerIndex];
92997                     p2 = coords[middleIndex];
92998                     p3 = coords[upperIndex];
92999                     area += ( rad(p3[0]) - rad(p1[0]) ) * Math.sin( rad(p2[1]));
93000                 }
93001
93002                 area = area * wgs84.RADIUS * wgs84.RADIUS / 2;
93003             }
93004
93005             return area;
93006         }
93007
93008         function rad(_) {
93009             return _ * Math.PI / 180;
93010         }
93011
93012         var geojsonArea = {
93013                 geometry: geometry_1,
93014                 ring: ring
93015         };
93016
93017         function toRadians(angleInDegrees) {
93018           return (angleInDegrees * Math.PI) / 180;
93019         }
93020
93021         function toDegrees(angleInRadians) {
93022           return (angleInRadians * 180) / Math.PI;
93023         }
93024
93025         function offset(c1, distance, bearing) {
93026           var lat1 = toRadians(c1[1]);
93027           var lon1 = toRadians(c1[0]);
93028           var dByR = distance / 6378137; // distance divided by 6378137 (radius of the earth) wgs84
93029           var lat = Math.asin(
93030             Math.sin(lat1) * Math.cos(dByR) +
93031               Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing)
93032           );
93033           var lon =
93034             lon1 +
93035             Math.atan2(
93036               Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1),
93037               Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat)
93038             );
93039           return [toDegrees(lon), toDegrees(lat)];
93040         }
93041
93042         function validateCenter(center) {
93043           var validCenterLengths = [2, 3];
93044           if (!Array.isArray(center) || !validCenterLengths.includes(center.length)) {
93045             throw new Error("ERROR! Center has to be an array of length two or three");
93046           }
93047           var lng = center[0];
93048           var lat = center[1];
93049           if (typeof lng !== "number" || typeof lat !== "number") {
93050             throw new Error(
93051               ("ERROR! Longitude and Latitude has to be numbers but where " + (typeof lng) + " and " + (typeof lat))
93052             );
93053           }
93054           if (lng > 180 || lng < -180) {
93055             throw new Error(
93056               ("ERROR! Longitude has to be between -180 and 180 but was " + lng)
93057             );
93058           }
93059
93060           if (lat > 90 || lat < -90) {
93061             throw new Error(
93062               ("ERROR! Latitude has to be between -90 and 90 but was " + lat)
93063             );
93064           }
93065         }
93066
93067         function validateRadius(radius) {
93068           if (typeof radius !== "number") {
93069             throw new Error(
93070               ("ERROR! Radius has to be a positive number but was: " + (typeof radius))
93071             );
93072           }
93073
93074           if (radius <= 0) {
93075             throw new Error(
93076               ("ERROR! Radius has to be a positive number but was: " + radius)
93077             );
93078           }
93079         }
93080
93081         function validateNumberOfSegments(numberOfSegments) {
93082           if (typeof numberOfSegments !== "number" && numberOfSegments !== undefined) {
93083             throw new Error(
93084               ("ERROR! Number of segments has to be a number but was: " + (typeof numberOfSegments))
93085             );
93086           }
93087
93088           if (numberOfSegments < 3) {
93089             throw new Error(
93090               ("ERROR! Number of segments has to be at least 3 but was: " + numberOfSegments)
93091             );
93092           }
93093         }
93094
93095         function validateInput(ref) {
93096           var center = ref.center;
93097           var radius = ref.radius;
93098           var numberOfSegments = ref.numberOfSegments;
93099
93100           validateCenter(center);
93101           validateRadius(radius);
93102           validateNumberOfSegments(numberOfSegments);
93103         }
93104
93105         var circleToPolygon = function circleToPolygon(center, radius, numberOfSegments) {
93106           var n = numberOfSegments ? numberOfSegments : 32;
93107
93108           // validateInput() throws error on invalid input and do nothing on valid input
93109           validateInput({ center: center, radius: radius, numberOfSegments: numberOfSegments });
93110
93111           var coordinates = [];
93112           for (var i = 0; i < n; ++i) {
93113             coordinates.push(offset(center, radius, (2 * Math.PI * -i) / n));
93114           }
93115           coordinates.push(coordinates[0]);
93116
93117           return {
93118             type: "Polygon",
93119             coordinates: [coordinates]
93120           };
93121         };
93122
93123         var geojsonPrecision = createCommonjsModule(function (module) {
93124         (function() {
93125
93126           function parse(t, coordinatePrecision, extrasPrecision) {
93127
93128             function point(p) {
93129               return p.map(function(e, index) {
93130                 if (index < 2) {
93131                     return 1 * e.toFixed(coordinatePrecision);
93132                 } else {
93133                     return 1 * e.toFixed(extrasPrecision);
93134                 }
93135               });
93136             }
93137
93138             function multi(l) {
93139               return l.map(point);
93140             }
93141
93142             function poly(p) {
93143               return p.map(multi);
93144             }
93145
93146             function multiPoly(m) {
93147               return m.map(poly);
93148             }
93149
93150             function geometry(obj) {
93151               if (!obj) {
93152                 return {};
93153               }
93154               
93155               switch (obj.type) {
93156                 case "Point":
93157                   obj.coordinates = point(obj.coordinates);
93158                   return obj;
93159                 case "LineString":
93160                 case "MultiPoint":
93161                   obj.coordinates = multi(obj.coordinates);
93162                   return obj;
93163                 case "Polygon":
93164                 case "MultiLineString":
93165                   obj.coordinates = poly(obj.coordinates);
93166                   return obj;
93167                 case "MultiPolygon":
93168                   obj.coordinates = multiPoly(obj.coordinates);
93169                   return obj;
93170                 case "GeometryCollection":
93171                   obj.geometries = obj.geometries.map(geometry);
93172                   return obj;
93173                 default :
93174                   return {};
93175               }
93176             }
93177
93178             function feature(obj) {
93179               obj.geometry = geometry(obj.geometry);
93180               return obj
93181             }
93182
93183             function featureCollection(f) {
93184               f.features = f.features.map(feature);
93185               return f;
93186             }
93187
93188             function geometryCollection(g) {
93189               g.geometries = g.geometries.map(geometry);
93190               return g;
93191             }
93192
93193             if (!t) {
93194               return t;
93195             }
93196
93197             switch (t.type) {
93198               case "Feature":
93199                 return feature(t);
93200               case "GeometryCollection" :
93201                 return geometryCollection(t);
93202               case "FeatureCollection" :
93203                 return featureCollection(t);
93204               case "Point":
93205               case "LineString":
93206               case "Polygon":
93207               case "MultiPoint":
93208               case "MultiPolygon":
93209               case "MultiLineString":
93210                 return geometry(t);
93211               default :
93212                 return t;
93213             }
93214               
93215           }
93216
93217           module.exports = parse;
93218           module.exports.parse = parse;
93219
93220         }());
93221         });
93222
93223         /* Polyfill service v3.13.0
93224          * For detailed credits and licence information see http://github.com/financial-times/polyfill-service
93225          *
93226          * - Array.prototype.fill, License: CC0 */
93227
93228         if (!('fill' in Array.prototype)) {
93229           Object.defineProperty(Array.prototype, 'fill', {
93230             configurable: true,
93231             value: function fill (value) {
93232               if (this === undefined || this === null) {
93233                 throw new TypeError(this + ' is not an object')
93234               }
93235
93236               var arrayLike = Object(this);
93237
93238               var length = Math.max(Math.min(arrayLike.length, 9007199254740991), 0) || 0;
93239
93240               var relativeStart = 1 in arguments ? parseInt(Number(arguments[1]), 10) || 0 : 0;
93241
93242               relativeStart = relativeStart < 0 ? Math.max(length + relativeStart, 0) : Math.min(relativeStart, length);
93243
93244               var relativeEnd = 2 in arguments && arguments[2] !== undefined ? parseInt(Number(arguments[2]), 10) || 0 : length;
93245
93246               relativeEnd = relativeEnd < 0 ? Math.max(length + arguments[2], 0) : Math.min(relativeEnd, length);
93247
93248               while (relativeStart < relativeEnd) {
93249                 arrayLike[relativeStart] = value;
93250
93251                 ++relativeStart;
93252               }
93253
93254               return arrayLike
93255             },
93256             writable: true
93257           });
93258         }
93259
93260         /**
93261          * Polyfill for IE support
93262          */
93263         Number.isFinite = Number.isFinite || function (value) {
93264           return typeof value === 'number' && isFinite(value)
93265         };
93266
93267         Number.isInteger = Number.isInteger || function (val) {
93268           return typeof val === 'number' &&
93269           isFinite(val) &&
93270           Math.floor(val) === val
93271         };
93272
93273         Number.parseFloat = Number.parseFloat || parseFloat;
93274
93275         Number.isNaN = Number.isNaN || function (value) {
93276           return value !== value // eslint-disable-line
93277         };
93278
93279         /**
93280          * Polyfill for IE support
93281          */
93282         Math.trunc = Math.trunc || function (x) {
93283           return x < 0 ? Math.ceil(x) : Math.floor(x)
93284         };
93285
93286         var NumberUtil = function NumberUtil () {};
93287
93288         NumberUtil.prototype.interfaces_ = function interfaces_ () {
93289           return []
93290         };
93291         NumberUtil.prototype.getClass = function getClass () {
93292           return NumberUtil
93293         };
93294         NumberUtil.prototype.equalsWithTolerance = function equalsWithTolerance (x1, x2, tolerance) {
93295           return Math.abs(x1 - x2) <= tolerance
93296         };
93297
93298         var IllegalArgumentException = (function (Error) {
93299                 function IllegalArgumentException (message) {
93300                         Error.call(this, message);
93301                         this.name = 'IllegalArgumentException';
93302                         this.message = message;
93303                         this.stack = (new Error()).stack;
93304                 }
93305
93306                 if ( Error ) { IllegalArgumentException.__proto__ = Error; }
93307                 IllegalArgumentException.prototype = Object.create( Error && Error.prototype );
93308                 IllegalArgumentException.prototype.constructor = IllegalArgumentException;
93309
93310                 return IllegalArgumentException;
93311         }(Error));
93312
93313         var Double = function Double () {};
93314
93315         var staticAccessors$1 = { MAX_VALUE: { configurable: true } };
93316
93317         Double.isNaN = function isNaN (n) { return Number.isNaN(n) };
93318         Double.doubleToLongBits = function doubleToLongBits (n) { return n };
93319         Double.longBitsToDouble = function longBitsToDouble (n) { return n };
93320         Double.isInfinite = function isInfinite (n) { return !Number.isFinite(n) };
93321         staticAccessors$1.MAX_VALUE.get = function () { return Number.MAX_VALUE };
93322
93323         Object.defineProperties( Double, staticAccessors$1 );
93324
93325         var Comparable = function Comparable () {};
93326
93327         var Clonable = function Clonable () {};
93328
93329         var Comparator = function Comparator () {};
93330
93331         function Serializable () {}
93332
93333         // import Assert from '../util/Assert'
93334
93335         var Coordinate = function Coordinate () {
93336           this.x = null;
93337           this.y = null;
93338           this.z = null;
93339           if (arguments.length === 0) {
93340             this.x = 0.0;
93341             this.y = 0.0;
93342             this.z = Coordinate.NULL_ORDINATE;
93343           } else if (arguments.length === 1) {
93344             var c = arguments[0];
93345             this.x = c.x;
93346             this.y = c.y;
93347             this.z = c.z;
93348           } else if (arguments.length === 2) {
93349             this.x = arguments[0];
93350             this.y = arguments[1];
93351             this.z = Coordinate.NULL_ORDINATE;
93352           } else if (arguments.length === 3) {
93353             this.x = arguments[0];
93354             this.y = arguments[1];
93355             this.z = arguments[2];
93356           }
93357         };
93358
93359         var staticAccessors = { DimensionalComparator: { configurable: true },serialVersionUID: { configurable: true },NULL_ORDINATE: { configurable: true },X: { configurable: true },Y: { configurable: true },Z: { configurable: true } };
93360         Coordinate.prototype.setOrdinate = function setOrdinate (ordinateIndex, value) {
93361           switch (ordinateIndex) {
93362             case Coordinate.X:
93363               this.x = value;
93364               break
93365             case Coordinate.Y:
93366               this.y = value;
93367               break
93368             case Coordinate.Z:
93369               this.z = value;
93370               break
93371             default:
93372               throw new IllegalArgumentException('Invalid ordinate index: ' + ordinateIndex)
93373           }
93374         };
93375         Coordinate.prototype.equals2D = function equals2D () {
93376           if (arguments.length === 1) {
93377             var other = arguments[0];
93378             if (this.x !== other.x) {
93379               return false
93380             }
93381             if (this.y !== other.y) {
93382               return false
93383             }
93384             return true
93385           } else if (arguments.length === 2) {
93386             var c = arguments[0];
93387             var tolerance = arguments[1];
93388             if (!NumberUtil.equalsWithTolerance(this.x, c.x, tolerance)) {
93389               return false
93390             }
93391             if (!NumberUtil.equalsWithTolerance(this.y, c.y, tolerance)) {
93392               return false
93393             }
93394             return true
93395           }
93396         };
93397         Coordinate.prototype.getOrdinate = function getOrdinate (ordinateIndex) {
93398           switch (ordinateIndex) {
93399             case Coordinate.X:
93400               return this.x
93401             case Coordinate.Y:
93402               return this.y
93403             case Coordinate.Z:
93404               return this.z
93405           }
93406           throw new IllegalArgumentException('Invalid ordinate index: ' + ordinateIndex)
93407         };
93408         Coordinate.prototype.equals3D = function equals3D (other) {
93409           return this.x === other.x &&
93410                  this.y === other.y &&
93411                  ((this.z === other.z || Double.isNaN(this.z)) &&
93412                  Double.isNaN(other.z))
93413         };
93414         Coordinate.prototype.equals = function equals (other) {
93415           if (!(other instanceof Coordinate)) {
93416             return false
93417           }
93418           return this.equals2D(other)
93419         };
93420         Coordinate.prototype.equalInZ = function equalInZ (c, tolerance) {
93421           return NumberUtil.equalsWithTolerance(this.z, c.z, tolerance)
93422         };
93423         Coordinate.prototype.compareTo = function compareTo (o) {
93424           var other = o;
93425           if (this.x < other.x) { return -1 }
93426           if (this.x > other.x) { return 1 }
93427           if (this.y < other.y) { return -1 }
93428           if (this.y > other.y) { return 1 }
93429           return 0
93430         };
93431         Coordinate.prototype.clone = function clone () {
93432           // try {
93433           // var coord = null
93434           // return coord
93435           // } catch (e) {
93436           // if (e instanceof CloneNotSupportedException) {
93437           //   Assert.shouldNeverReachHere("this shouldn't happen because this class is Cloneable")
93438           //   return null
93439           // } else throw e
93440           // } finally {}
93441         };
93442         Coordinate.prototype.copy = function copy () {
93443           return new Coordinate(this)
93444         };
93445         Coordinate.prototype.toString = function toString () {
93446           return '(' + this.x + ', ' + this.y + ', ' + this.z + ')'
93447         };
93448         Coordinate.prototype.distance3D = function distance3D (c) {
93449           var dx = this.x - c.x;
93450           var dy = this.y - c.y;
93451           var dz = this.z - c.z;
93452           return Math.sqrt(dx * dx + dy * dy + dz * dz)
93453         };
93454         Coordinate.prototype.distance = function distance (c) {
93455           var dx = this.x - c.x;
93456           var dy = this.y - c.y;
93457           return Math.sqrt(dx * dx + dy * dy)
93458         };
93459         Coordinate.prototype.hashCode = function hashCode () {
93460           var result = 17;
93461           result = 37 * result + Coordinate.hashCode(this.x);
93462           result = 37 * result + Coordinate.hashCode(this.y);
93463           return result
93464         };
93465         Coordinate.prototype.setCoordinate = function setCoordinate (other) {
93466           this.x = other.x;
93467           this.y = other.y;
93468           this.z = other.z;
93469         };
93470         Coordinate.prototype.interfaces_ = function interfaces_ () {
93471           return [Comparable, Clonable, Serializable]
93472         };
93473         Coordinate.prototype.getClass = function getClass () {
93474           return Coordinate
93475         };
93476         Coordinate.hashCode = function hashCode () {
93477           if (arguments.length === 1) {
93478             var x = arguments[0];
93479             var f = Double.doubleToLongBits(x);
93480             return Math.trunc((f ^ f) >>> 32)
93481           }
93482         };
93483         staticAccessors.DimensionalComparator.get = function () { return DimensionalComparator };
93484         staticAccessors.serialVersionUID.get = function () { return 6683108902428366910 };
93485         staticAccessors.NULL_ORDINATE.get = function () { return Double.NaN };
93486         staticAccessors.X.get = function () { return 0 };
93487         staticAccessors.Y.get = function () { return 1 };
93488         staticAccessors.Z.get = function () { return 2 };
93489
93490         Object.defineProperties( Coordinate, staticAccessors );
93491
93492         var DimensionalComparator = function DimensionalComparator (dimensionsToTest) {
93493           this._dimensionsToTest = 2;
93494           if (arguments.length === 0) ; else if (arguments.length === 1) {
93495             var dimensionsToTest$1 = arguments[0];
93496             if (dimensionsToTest$1 !== 2 && dimensionsToTest$1 !== 3) { throw new IllegalArgumentException('only 2 or 3 dimensions may be specified') }
93497             this._dimensionsToTest = dimensionsToTest$1;
93498           }
93499         };
93500         DimensionalComparator.prototype.compare = function compare (o1, o2) {
93501           var c1 = o1;
93502           var c2 = o2;
93503           var compX = DimensionalComparator.compare(c1.x, c2.x);
93504           if (compX !== 0) { return compX }
93505           var compY = DimensionalComparator.compare(c1.y, c2.y);
93506           if (compY !== 0) { return compY }
93507           if (this._dimensionsToTest <= 2) { return 0 }
93508           var compZ = DimensionalComparator.compare(c1.z, c2.z);
93509           return compZ
93510         };
93511         DimensionalComparator.prototype.interfaces_ = function interfaces_ () {
93512           return [Comparator]
93513         };
93514         DimensionalComparator.prototype.getClass = function getClass () {
93515           return DimensionalComparator
93516         };
93517         DimensionalComparator.compare = function compare (a, b) {
93518           if (a < b) { return -1 }
93519           if (a > b) { return 1 }
93520           if (Double.isNaN(a)) {
93521             if (Double.isNaN(b)) { return 0 }
93522             return -1
93523           }
93524           if (Double.isNaN(b)) { return 1 }
93525           return 0
93526         };
93527
93528         // import hasInterface from '../../../../hasInterface'
93529         // import CoordinateSequence from './CoordinateSequence'
93530
93531         var CoordinateSequenceFactory = function CoordinateSequenceFactory () {};
93532
93533         CoordinateSequenceFactory.prototype.create = function create () {
93534           // if (arguments.length === 1) {
93535           // if (arguments[0] instanceof Array) {
93536           //   let coordinates = arguments[0]
93537           // } else if (hasInterface(arguments[0], CoordinateSequence)) {
93538           //   let coordSeq = arguments[0]
93539           // }
93540           // } else if (arguments.length === 2) {
93541           // let size = arguments[0]
93542           // let dimension = arguments[1]
93543           // }
93544         };
93545         CoordinateSequenceFactory.prototype.interfaces_ = function interfaces_ () {
93546           return []
93547         };
93548         CoordinateSequenceFactory.prototype.getClass = function getClass () {
93549           return CoordinateSequenceFactory
93550         };
93551
93552         var Location = function Location () {};
93553
93554         var staticAccessors$4 = { INTERIOR: { configurable: true },BOUNDARY: { configurable: true },EXTERIOR: { configurable: true },NONE: { configurable: true } };
93555
93556         Location.prototype.interfaces_ = function interfaces_ () {
93557           return []
93558         };
93559         Location.prototype.getClass = function getClass () {
93560           return Location
93561         };
93562         Location.toLocationSymbol = function toLocationSymbol (locationValue) {
93563           switch (locationValue) {
93564             case Location.EXTERIOR:
93565               return 'e'
93566             case Location.BOUNDARY:
93567               return 'b'
93568             case Location.INTERIOR:
93569               return 'i'
93570             case Location.NONE:
93571               return '-'
93572           }
93573           throw new IllegalArgumentException('Unknown location value: ' + locationValue)
93574         };
93575         staticAccessors$4.INTERIOR.get = function () { return 0 };
93576         staticAccessors$4.BOUNDARY.get = function () { return 1 };
93577         staticAccessors$4.EXTERIOR.get = function () { return 2 };
93578         staticAccessors$4.NONE.get = function () { return -1 };
93579
93580         Object.defineProperties( Location, staticAccessors$4 );
93581
93582         var hasInterface = function (o, i) {
93583           return o.interfaces_ && o.interfaces_().indexOf(i) > -1
93584         };
93585
93586         var MathUtil = function MathUtil () {};
93587
93588         var staticAccessors$5 = { LOG_10: { configurable: true } };
93589
93590         MathUtil.prototype.interfaces_ = function interfaces_ () {
93591           return []
93592         };
93593         MathUtil.prototype.getClass = function getClass () {
93594           return MathUtil
93595         };
93596         MathUtil.log10 = function log10 (x) {
93597           var ln = Math.log(x);
93598           if (Double.isInfinite(ln)) { return ln }
93599           if (Double.isNaN(ln)) { return ln }
93600           return ln / MathUtil.LOG_10
93601         };
93602         MathUtil.min = function min (v1, v2, v3, v4) {
93603           var min = v1;
93604           if (v2 < min) { min = v2; }
93605           if (v3 < min) { min = v3; }
93606           if (v4 < min) { min = v4; }
93607           return min
93608         };
93609         MathUtil.clamp = function clamp () {
93610           if (typeof arguments[2] === 'number' && (typeof arguments[0] === 'number' && typeof arguments[1] === 'number')) {
93611             var x = arguments[0];
93612             var min = arguments[1];
93613             var max = arguments[2];
93614             if (x < min) { return min }
93615             if (x > max) { return max }
93616             return x
93617           } else if (Number.isInteger(arguments[2]) && (Number.isInteger(arguments[0]) && Number.isInteger(arguments[1]))) {
93618             var x$1 = arguments[0];
93619             var min$1 = arguments[1];
93620             var max$1 = arguments[2];
93621             if (x$1 < min$1) { return min$1 }
93622             if (x$1 > max$1) { return max$1 }
93623             return x$1
93624           }
93625         };
93626         MathUtil.wrap = function wrap (index, max) {
93627           if (index < 0) {
93628             return max - -index % max
93629           }
93630           return index % max
93631         };
93632         MathUtil.max = function max () {
93633           if (arguments.length === 3) {
93634             var v1 = arguments[0];
93635             var v2 = arguments[1];
93636             var v3 = arguments[2];
93637             var max = v1;
93638             if (v2 > max) { max = v2; }
93639             if (v3 > max) { max = v3; }
93640             return max
93641           } else if (arguments.length === 4) {
93642             var v1$1 = arguments[0];
93643             var v2$1 = arguments[1];
93644             var v3$1 = arguments[2];
93645             var v4 = arguments[3];
93646             var max$1 = v1$1;
93647             if (v2$1 > max$1) { max$1 = v2$1; }
93648             if (v3$1 > max$1) { max$1 = v3$1; }
93649             if (v4 > max$1) { max$1 = v4; }
93650             return max$1
93651           }
93652         };
93653         MathUtil.average = function average (x1, x2) {
93654           return (x1 + x2) / 2.0
93655         };
93656         staticAccessors$5.LOG_10.get = function () { return Math.log(10) };
93657
93658         Object.defineProperties( MathUtil, staticAccessors$5 );
93659
93660         var StringBuffer = function StringBuffer (str) {
93661           this.str = str;
93662         };
93663         StringBuffer.prototype.append = function append (e) {
93664           this.str += e;
93665         };
93666
93667         StringBuffer.prototype.setCharAt = function setCharAt (i, c) {
93668           this.str = this.str.substr(0, i) + c + this.str.substr(i + 1);
93669         };
93670
93671         StringBuffer.prototype.toString = function toString (e) {
93672           return this.str
93673         };
93674
93675         var Integer = function Integer (value) {
93676           this.value = value;
93677         };
93678         Integer.prototype.intValue = function intValue () {
93679           return this.value
93680         };
93681         Integer.prototype.compareTo = function compareTo (o) {
93682           if (this.value < o) { return -1 }
93683           if (this.value > o) { return 1 }
93684           return 0
93685         };
93686         Integer.isNaN = function isNaN (n) { return Number.isNaN(n) };
93687
93688         var Character = function Character () {};
93689
93690         Character.isWhitespace = function isWhitespace (c) { return ((c <= 32 && c >= 0) || c === 127) };
93691         Character.toUpperCase = function toUpperCase (c) { return c.toUpperCase() };
93692
93693         var DD = function DD () {
93694           this._hi = 0.0;
93695           this._lo = 0.0;
93696           if (arguments.length === 0) {
93697             this.init(0.0);
93698           } else if (arguments.length === 1) {
93699             if (typeof arguments[0] === 'number') {
93700               var x = arguments[0];
93701               this.init(x);
93702             } else if (arguments[0] instanceof DD) {
93703               var dd = arguments[0];
93704               this.init(dd);
93705             } else if (typeof arguments[0] === 'string') {
93706               var str = arguments[0];
93707               DD.call(this, DD.parse(str));
93708             }
93709           } else if (arguments.length === 2) {
93710             var hi = arguments[0];
93711             var lo = arguments[1];
93712             this.init(hi, lo);
93713           }
93714         };
93715
93716         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 } };
93717         DD.prototype.le = function le (y) {
93718           return (this._hi < y._hi || this._hi === y._hi) && this._lo <= y._lo
93719         };
93720         DD.prototype.extractSignificantDigits = function extractSignificantDigits (insertDecimalPoint, magnitude) {
93721           var y = this.abs();
93722           var mag = DD.magnitude(y._hi);
93723           var scale = DD.TEN.pow(mag);
93724           y = y.divide(scale);
93725           if (y.gt(DD.TEN)) {
93726             y = y.divide(DD.TEN);
93727             mag += 1;
93728           } else if (y.lt(DD.ONE)) {
93729             y = y.multiply(DD.TEN);
93730             mag -= 1;
93731           }
93732           var decimalPointPos = mag + 1;
93733           var buf = new StringBuffer();
93734           var numDigits = DD.MAX_PRINT_DIGITS - 1;
93735           for (var i = 0; i <= numDigits; i++) {
93736             if (insertDecimalPoint && i === decimalPointPos) {
93737               buf.append('.');
93738             }
93739             var digit = Math.trunc(y._hi);
93740             if (digit < 0) {
93741               break
93742             }
93743             var rebiasBy10 = false;
93744             var digitChar = 0;
93745             if (digit > 9) {
93746               rebiasBy10 = true;
93747               digitChar = '9';
93748             } else {
93749               digitChar = '0' + digit;
93750             }
93751             buf.append(digitChar);
93752             y = y.subtract(DD.valueOf(digit)).multiply(DD.TEN);
93753             if (rebiasBy10) { y.selfAdd(DD.TEN); }
93754             var continueExtractingDigits = true;
93755             var remMag = DD.magnitude(y._hi);
93756             if (remMag < 0 && Math.abs(remMag) >= numDigits - i) { continueExtractingDigits = false; }
93757             if (!continueExtractingDigits) { break }
93758           }
93759           magnitude[0] = mag;
93760           return buf.toString()
93761         };
93762         DD.prototype.sqr = function sqr () {
93763           return this.multiply(this)
93764         };
93765         DD.prototype.doubleValue = function doubleValue () {
93766           return this._hi + this._lo
93767         };
93768         DD.prototype.subtract = function subtract () {
93769           if (arguments[0] instanceof DD) {
93770             var y = arguments[0];
93771             return this.add(y.negate())
93772           } else if (typeof arguments[0] === 'number') {
93773             var y$1 = arguments[0];
93774             return this.add(-y$1)
93775           }
93776         };
93777         DD.prototype.equals = function equals () {
93778           if (arguments.length === 1) {
93779             var y = arguments[0];
93780             return this._hi === y._hi && this._lo === y._lo
93781           }
93782         };
93783         DD.prototype.isZero = function isZero () {
93784           return this._hi === 0.0 && this._lo === 0.0
93785         };
93786         DD.prototype.selfSubtract = function selfSubtract () {
93787           if (arguments[0] instanceof DD) {
93788             var y = arguments[0];
93789             if (this.isNaN()) { return this }
93790             return this.selfAdd(-y._hi, -y._lo)
93791           } else if (typeof arguments[0] === 'number') {
93792             var y$1 = arguments[0];
93793             if (this.isNaN()) { return this }
93794             return this.selfAdd(-y$1, 0.0)
93795           }
93796         };
93797         DD.prototype.getSpecialNumberString = function getSpecialNumberString () {
93798           if (this.isZero()) { return '0.0' }
93799           if (this.isNaN()) { return 'NaN ' }
93800           return null
93801         };
93802         DD.prototype.min = function min (x) {
93803           if (this.le(x)) {
93804             return this
93805           } else {
93806             return x
93807           }
93808         };
93809         DD.prototype.selfDivide = function selfDivide () {
93810           if (arguments.length === 1) {
93811             if (arguments[0] instanceof DD) {
93812               var y = arguments[0];
93813               return this.selfDivide(y._hi, y._lo)
93814             } else if (typeof arguments[0] === 'number') {
93815               var y$1 = arguments[0];
93816               return this.selfDivide(y$1, 0.0)
93817             }
93818           } else if (arguments.length === 2) {
93819             var yhi = arguments[0];
93820             var ylo = arguments[1];
93821             var hc = null;
93822             var tc = null;
93823             var hy = null;
93824             var ty = null;
93825             var C = null;
93826             var c = null;
93827             var U = null;
93828             var u = null;
93829             C = this._hi / yhi;
93830             c = DD.SPLIT * C;
93831             hc = c - C;
93832             u = DD.SPLIT * yhi;
93833             hc = c - hc;
93834             tc = C - hc;
93835             hy = u - yhi;
93836             U = C * yhi;
93837             hy = u - hy;
93838             ty = yhi - hy;
93839             u = hc * hy - U + hc * ty + tc * hy + tc * ty;
93840             c = (this._hi - U - u + this._lo - C * ylo) / yhi;
93841             u = C + c;
93842             this._hi = u;
93843             this._lo = C - u + c;
93844             return this
93845           }
93846         };
93847         DD.prototype.dump = function dump () {
93848           return 'DD<' + this._hi + ', ' + this._lo + '>'
93849         };
93850         DD.prototype.divide = function divide () {
93851           if (arguments[0] instanceof DD) {
93852             var y = arguments[0];
93853             var hc = null;
93854             var tc = null;
93855             var hy = null;
93856             var ty = null;
93857             var C = null;
93858             var c = null;
93859             var U = null;
93860             var u = null;
93861             C = this._hi / y._hi;
93862             c = DD.SPLIT * C;
93863             hc = c - C;
93864             u = DD.SPLIT * y._hi;
93865             hc = c - hc;
93866             tc = C - hc;
93867             hy = u - y._hi;
93868             U = C * y._hi;
93869             hy = u - hy;
93870             ty = y._hi - hy;
93871             u = hc * hy - U + hc * ty + tc * hy + tc * ty;
93872             c = (this._hi - U - u + this._lo - C * y._lo) / y._hi;
93873             u = C + c;
93874             var zhi = u;
93875             var zlo = C - u + c;
93876             return new DD(zhi, zlo)
93877           } else if (typeof arguments[0] === 'number') {
93878             var y$1 = arguments[0];
93879             if (Double.isNaN(y$1)) { return DD.createNaN() }
93880             return DD.copy(this).selfDivide(y$1, 0.0)
93881           }
93882         };
93883         DD.prototype.ge = function ge (y) {
93884           return (this._hi > y._hi || this._hi === y._hi) && this._lo >= y._lo
93885         };
93886         DD.prototype.pow = function pow (exp) {
93887           if (exp === 0.0) { return DD.valueOf(1.0) }
93888           var r = new DD(this);
93889           var s = DD.valueOf(1.0);
93890           var n = Math.abs(exp);
93891           if (n > 1) {
93892             while (n > 0) {
93893               if (n % 2 === 1) {
93894                 s.selfMultiply(r);
93895               }
93896               n /= 2;
93897               if (n > 0) { r = r.sqr(); }
93898             }
93899           } else {
93900             s = r;
93901           }
93902           if (exp < 0) { return s.reciprocal() }
93903           return s
93904         };
93905         DD.prototype.ceil = function ceil () {
93906           if (this.isNaN()) { return DD.NaN }
93907           var fhi = Math.ceil(this._hi);
93908           var flo = 0.0;
93909           if (fhi === this._hi) {
93910             flo = Math.ceil(this._lo);
93911           }
93912           return new DD(fhi, flo)
93913         };
93914         DD.prototype.compareTo = function compareTo (o) {
93915           var other = o;
93916           if (this._hi < other._hi) { return -1 }
93917           if (this._hi > other._hi) { return 1 }
93918           if (this._lo < other._lo) { return -1 }
93919           if (this._lo > other._lo) { return 1 }
93920           return 0
93921         };
93922         DD.prototype.rint = function rint () {
93923           if (this.isNaN()) { return this }
93924           var plus5 = this.add(0.5);
93925           return plus5.floor()
93926         };
93927         DD.prototype.setValue = function setValue () {
93928           if (arguments[0] instanceof DD) {
93929             var value = arguments[0];
93930             this.init(value);
93931             return this
93932           } else if (typeof arguments[0] === 'number') {
93933             var value$1 = arguments[0];
93934             this.init(value$1);
93935             return this
93936           }
93937         };
93938         DD.prototype.max = function max (x) {
93939           if (this.ge(x)) {
93940             return this
93941           } else {
93942             return x
93943           }
93944         };
93945         DD.prototype.sqrt = function sqrt () {
93946           if (this.isZero()) { return DD.valueOf(0.0) }
93947           if (this.isNegative()) {
93948             return DD.NaN
93949           }
93950           var x = 1.0 / Math.sqrt(this._hi);
93951           var ax = this._hi * x;
93952           var axdd = DD.valueOf(ax);
93953           var diffSq = this.subtract(axdd.sqr());
93954           var d2 = diffSq._hi * (x * 0.5);
93955           return axdd.add(d2)
93956         };
93957         DD.prototype.selfAdd = function selfAdd () {
93958           if (arguments.length === 1) {
93959             if (arguments[0] instanceof DD) {
93960               var y = arguments[0];
93961               return this.selfAdd(y._hi, y._lo)
93962             } else if (typeof arguments[0] === 'number') {
93963               var y$1 = arguments[0];
93964               var H = null;
93965               var h = null;
93966               var S = null;
93967               var s = null;
93968               var e = null;
93969               var f = null;
93970               S = this._hi + y$1;
93971               e = S - this._hi;
93972               s = S - e;
93973               s = y$1 - e + (this._hi - s);
93974               f = s + this._lo;
93975               H = S + f;
93976               h = f + (S - H);
93977               this._hi = H + h;
93978               this._lo = h + (H - this._hi);
93979               return this
93980             }
93981           } else if (arguments.length === 2) {
93982             var yhi = arguments[0];
93983             var ylo = arguments[1];
93984             var H$1 = null;
93985             var h$1 = null;
93986             var T = null;
93987             var t = null;
93988             var S$1 = null;
93989             var s$1 = null;
93990             var e$1 = null;
93991             var f$1 = null;
93992             S$1 = this._hi + yhi;
93993             T = this._lo + ylo;
93994             e$1 = S$1 - this._hi;
93995             f$1 = T - this._lo;
93996             s$1 = S$1 - e$1;
93997             t = T - f$1;
93998             s$1 = yhi - e$1 + (this._hi - s$1);
93999             t = ylo - f$1 + (this._lo - t);
94000             e$1 = s$1 + T;
94001             H$1 = S$1 + e$1;
94002             h$1 = e$1 + (S$1 - H$1);
94003             e$1 = t + h$1;
94004             var zhi = H$1 + e$1;
94005             var zlo = e$1 + (H$1 - zhi);
94006             this._hi = zhi;
94007             this._lo = zlo;
94008             return this
94009           }
94010         };
94011         DD.prototype.selfMultiply = function selfMultiply () {
94012           if (arguments.length === 1) {
94013             if (arguments[0] instanceof DD) {
94014               var y = arguments[0];
94015               return this.selfMultiply(y._hi, y._lo)
94016             } else if (typeof arguments[0] === 'number') {
94017               var y$1 = arguments[0];
94018               return this.selfMultiply(y$1, 0.0)
94019             }
94020           } else if (arguments.length === 2) {
94021             var yhi = arguments[0];
94022             var ylo = arguments[1];
94023             var hx = null;
94024             var tx = null;
94025             var hy = null;
94026             var ty = null;
94027             var C = null;
94028             var c = null;
94029             C = DD.SPLIT * this._hi;
94030             hx = C - this._hi;
94031             c = DD.SPLIT * yhi;
94032             hx = C - hx;
94033             tx = this._hi - hx;
94034             hy = c - yhi;
94035             C = this._hi * yhi;
94036             hy = c - hy;
94037             ty = yhi - hy;
94038             c = hx * hy - C + hx * ty + tx * hy + tx * ty + (this._hi * ylo + this._lo * yhi);
94039             var zhi = C + c;
94040             hx = C - zhi;
94041             var zlo = c + hx;
94042             this._hi = zhi;
94043             this._lo = zlo;
94044             return this
94045           }
94046         };
94047         DD.prototype.selfSqr = function selfSqr () {
94048           return this.selfMultiply(this)
94049         };
94050         DD.prototype.floor = function floor () {
94051           if (this.isNaN()) { return DD.NaN }
94052           var fhi = Math.floor(this._hi);
94053           var flo = 0.0;
94054           if (fhi === this._hi) {
94055             flo = Math.floor(this._lo);
94056           }
94057           return new DD(fhi, flo)
94058         };
94059         DD.prototype.negate = function negate () {
94060           if (this.isNaN()) { return this }
94061           return new DD(-this._hi, -this._lo)
94062         };
94063         DD.prototype.clone = function clone () {
94064           // try {
94065           // return null
94066           // } catch (ex) {
94067           // if (ex instanceof CloneNotSupportedException) {
94068           //   return null
94069           // } else throw ex
94070           // } finally {}
94071         };
94072         DD.prototype.multiply = function multiply () {
94073           if (arguments[0] instanceof DD) {
94074             var y = arguments[0];
94075             if (y.isNaN()) { return DD.createNaN() }
94076             return DD.copy(this).selfMultiply(y)
94077           } else if (typeof arguments[0] === 'number') {
94078             var y$1 = arguments[0];
94079             if (Double.isNaN(y$1)) { return DD.createNaN() }
94080             return DD.copy(this).selfMultiply(y$1, 0.0)
94081           }
94082         };
94083         DD.prototype.isNaN = function isNaN () {
94084           return Double.isNaN(this._hi)
94085         };
94086         DD.prototype.intValue = function intValue () {
94087           return Math.trunc(this._hi)
94088         };
94089         DD.prototype.toString = function toString () {
94090           var mag = DD.magnitude(this._hi);
94091           if (mag >= -3 && mag <= 20) { return this.toStandardNotation() }
94092           return this.toSciNotation()
94093         };
94094         DD.prototype.toStandardNotation = function toStandardNotation () {
94095           var specialStr = this.getSpecialNumberString();
94096           if (specialStr !== null) { return specialStr }
94097           var magnitude = new Array(1).fill(null);
94098           var sigDigits = this.extractSignificantDigits(true, magnitude);
94099           var decimalPointPos = magnitude[0] + 1;
94100           var num = sigDigits;
94101           if (sigDigits.charAt(0) === '.') {
94102             num = '0' + sigDigits;
94103           } else if (decimalPointPos < 0) {
94104             num = '0.' + DD.stringOfChar('0', -decimalPointPos) + sigDigits;
94105           } else if (sigDigits.indexOf('.') === -1) {
94106             var numZeroes = decimalPointPos - sigDigits.length;
94107             var zeroes = DD.stringOfChar('0', numZeroes);
94108             num = sigDigits + zeroes + '.0';
94109           }
94110           if (this.isNegative()) { return '-' + num }
94111           return num
94112         };
94113         DD.prototype.reciprocal = function reciprocal () {
94114           var hc = null;
94115           var tc = null;
94116           var hy = null;
94117           var ty = null;
94118           var C = null;
94119           var c = null;
94120           var U = null;
94121           var u = null;
94122           C = 1.0 / this._hi;
94123           c = DD.SPLIT * C;
94124           hc = c - C;
94125           u = DD.SPLIT * this._hi;
94126           hc = c - hc;
94127           tc = C - hc;
94128           hy = u - this._hi;
94129           U = C * this._hi;
94130           hy = u - hy;
94131           ty = this._hi - hy;
94132           u = hc * hy - U + hc * ty + tc * hy + tc * ty;
94133           c = (1.0 - U - u - C * this._lo) / this._hi;
94134           var zhi = C + c;
94135           var zlo = C - zhi + c;
94136           return new DD(zhi, zlo)
94137         };
94138         DD.prototype.toSciNotation = function toSciNotation () {
94139           if (this.isZero()) { return DD.SCI_NOT_ZERO }
94140           var specialStr = this.getSpecialNumberString();
94141           if (specialStr !== null) { return specialStr }
94142           var magnitude = new Array(1).fill(null);
94143           var digits = this.extractSignificantDigits(false, magnitude);
94144           var expStr = DD.SCI_NOT_EXPONENT_CHAR + magnitude[0];
94145           if (digits.charAt(0) === '0') {
94146             throw new Error('Found leading zero: ' + digits)
94147           }
94148           var trailingDigits = '';
94149           if (digits.length > 1) { trailingDigits = digits.substring(1); }
94150           var digitsWithDecimal = digits.charAt(0) + '.' + trailingDigits;
94151           if (this.isNegative()) { return '-' + digitsWithDecimal + expStr }
94152           return digitsWithDecimal + expStr
94153         };
94154         DD.prototype.abs = function abs () {
94155           if (this.isNaN()) { return DD.NaN }
94156           if (this.isNegative()) { return this.negate() }
94157           return new DD(this)
94158         };
94159         DD.prototype.isPositive = function isPositive () {
94160           return (this._hi > 0.0 || this._hi === 0.0) && this._lo > 0.0
94161         };
94162         DD.prototype.lt = function lt (y) {
94163           return (this._hi < y._hi || this._hi === y._hi) && this._lo < y._lo
94164         };
94165         DD.prototype.add = function add () {
94166           if (arguments[0] instanceof DD) {
94167             var y = arguments[0];
94168             return DD.copy(this).selfAdd(y)
94169           } else if (typeof arguments[0] === 'number') {
94170             var y$1 = arguments[0];
94171             return DD.copy(this).selfAdd(y$1)
94172           }
94173         };
94174         DD.prototype.init = function init () {
94175           if (arguments.length === 1) {
94176             if (typeof arguments[0] === 'number') {
94177               var x = arguments[0];
94178               this._hi = x;
94179               this._lo = 0.0;
94180             } else if (arguments[0] instanceof DD) {
94181               var dd = arguments[0];
94182               this._hi = dd._hi;
94183               this._lo = dd._lo;
94184             }
94185           } else if (arguments.length === 2) {
94186             var hi = arguments[0];
94187             var lo = arguments[1];
94188             this._hi = hi;
94189             this._lo = lo;
94190           }
94191         };
94192         DD.prototype.gt = function gt (y) {
94193           return (this._hi > y._hi || this._hi === y._hi) && this._lo > y._lo
94194         };
94195         DD.prototype.isNegative = function isNegative () {
94196           return (this._hi < 0.0 || this._hi === 0.0) && this._lo < 0.0
94197         };
94198         DD.prototype.trunc = function trunc () {
94199           if (this.isNaN()) { return DD.NaN }
94200           if (this.isPositive()) { return this.floor(); } else { return this.ceil() }
94201         };
94202         DD.prototype.signum = function signum () {
94203           if (this._hi > 0) { return 1 }
94204           if (this._hi < 0) { return -1 }
94205           if (this._lo > 0) { return 1 }
94206           if (this._lo < 0) { return -1 }
94207           return 0
94208         };
94209         DD.prototype.interfaces_ = function interfaces_ () {
94210           return [Serializable, Comparable, Clonable]
94211         };
94212         DD.prototype.getClass = function getClass () {
94213           return DD
94214         };
94215         DD.sqr = function sqr (x) {
94216           return DD.valueOf(x).selfMultiply(x)
94217         };
94218         DD.valueOf = function valueOf () {
94219           if (typeof arguments[0] === 'string') {
94220             var str = arguments[0];
94221             return DD.parse(str)
94222           } else if (typeof arguments[0] === 'number') {
94223             var x = arguments[0];
94224             return new DD(x)
94225           }
94226         };
94227         DD.sqrt = function sqrt (x) {
94228           return DD.valueOf(x).sqrt()
94229         };
94230         DD.parse = function parse (str) {
94231           var i = 0;
94232           var strlen = str.length;
94233           while (Character.isWhitespace(str.charAt(i))) { i++; }
94234           var isNegative = false;
94235           if (i < strlen) {
94236             var signCh = str.charAt(i);
94237             if (signCh === '-' || signCh === '+') {
94238               i++;
94239               if (signCh === '-') { isNegative = true; }
94240             }
94241           }
94242           var val = new DD();
94243           var numDigits = 0;
94244           var numBeforeDec = 0;
94245           var exp = 0;
94246           while (true) {
94247             if (i >= strlen) { break }
94248             var ch = str.charAt(i);
94249             i++;
94250             if (Character.isDigit(ch)) {
94251               var d = ch - '0';
94252               val.selfMultiply(DD.TEN);
94253               val.selfAdd(d);
94254               numDigits++;
94255               continue
94256             }
94257             if (ch === '.') {
94258               numBeforeDec = numDigits;
94259               continue
94260             }
94261             if (ch === 'e' || ch === 'E') {
94262               var expStr = str.substring(i);
94263               try {
94264                 exp = Integer.parseInt(expStr);
94265               } catch (ex) {
94266                 if (ex instanceof Error) {
94267                   throw new Error('Invalid exponent ' + expStr + ' in string ' + str)
94268                 } else { throw ex }
94269               } finally {}
94270               break
94271             }
94272             throw new Error("Unexpected character '" + ch + "' at position " + i + ' in string ' + str)
94273           }
94274           var val2 = val;
94275           var numDecPlaces = numDigits - numBeforeDec - exp;
94276           if (numDecPlaces === 0) {
94277             val2 = val;
94278           } else if (numDecPlaces > 0) {
94279             var scale = DD.TEN.pow(numDecPlaces);
94280             val2 = val.divide(scale);
94281           } else if (numDecPlaces < 0) {
94282             var scale$1 = DD.TEN.pow(-numDecPlaces);
94283             val2 = val.multiply(scale$1);
94284           }
94285           if (isNegative) {
94286             return val2.negate()
94287           }
94288           return val2
94289         };
94290         DD.createNaN = function createNaN () {
94291           return new DD(Double.NaN, Double.NaN)
94292         };
94293         DD.copy = function copy (dd) {
94294           return new DD(dd)
94295         };
94296         DD.magnitude = function magnitude (x) {
94297           var xAbs = Math.abs(x);
94298           var xLog10 = Math.log(xAbs) / Math.log(10);
94299           var xMag = Math.trunc(Math.floor(xLog10));
94300           var xApprox = Math.pow(10, xMag);
94301           if (xApprox * 10 <= xAbs) { xMag += 1; }
94302           return xMag
94303         };
94304         DD.stringOfChar = function stringOfChar (ch, len) {
94305           var buf = new StringBuffer();
94306           for (var i = 0; i < len; i++) {
94307             buf.append(ch);
94308           }
94309           return buf.toString()
94310         };
94311         staticAccessors$7.PI.get = function () { return new DD(3.141592653589793116e+00, 1.224646799147353207e-16) };
94312         staticAccessors$7.TWO_PI.get = function () { return new DD(6.283185307179586232e+00, 2.449293598294706414e-16) };
94313         staticAccessors$7.PI_2.get = function () { return new DD(1.570796326794896558e+00, 6.123233995736766036e-17) };
94314         staticAccessors$7.E.get = function () { return new DD(2.718281828459045091e+00, 1.445646891729250158e-16) };
94315         staticAccessors$7.NaN.get = function () { return new DD(Double.NaN, Double.NaN) };
94316         staticAccessors$7.EPS.get = function () { return 1.23259516440783e-32 };
94317         staticAccessors$7.SPLIT.get = function () { return 134217729.0 };
94318         staticAccessors$7.MAX_PRINT_DIGITS.get = function () { return 32 };
94319         staticAccessors$7.TEN.get = function () { return DD.valueOf(10.0) };
94320         staticAccessors$7.ONE.get = function () { return DD.valueOf(1.0) };
94321         staticAccessors$7.SCI_NOT_EXPONENT_CHAR.get = function () { return 'E' };
94322         staticAccessors$7.SCI_NOT_ZERO.get = function () { return '0.0E0' };
94323
94324         Object.defineProperties( DD, staticAccessors$7 );
94325
94326         var CGAlgorithmsDD = function CGAlgorithmsDD () {};
94327
94328         var staticAccessors$6 = { DP_SAFE_EPSILON: { configurable: true } };
94329
94330         CGAlgorithmsDD.prototype.interfaces_ = function interfaces_ () {
94331           return []
94332         };
94333         CGAlgorithmsDD.prototype.getClass = function getClass () {
94334           return CGAlgorithmsDD
94335         };
94336         CGAlgorithmsDD.orientationIndex = function orientationIndex (p1, p2, q) {
94337           var index = CGAlgorithmsDD.orientationIndexFilter(p1, p2, q);
94338           if (index <= 1) { return index }
94339           var dx1 = DD.valueOf(p2.x).selfAdd(-p1.x);
94340           var dy1 = DD.valueOf(p2.y).selfAdd(-p1.y);
94341           var dx2 = DD.valueOf(q.x).selfAdd(-p2.x);
94342           var dy2 = DD.valueOf(q.y).selfAdd(-p2.y);
94343           return dx1.selfMultiply(dy2).selfSubtract(dy1.selfMultiply(dx2)).signum()
94344         };
94345         CGAlgorithmsDD.signOfDet2x2 = function signOfDet2x2 (x1, y1, x2, y2) {
94346           var det = x1.multiply(y2).selfSubtract(y1.multiply(x2));
94347           return det.signum()
94348         };
94349         CGAlgorithmsDD.intersection = function intersection (p1, p2, q1, q2) {
94350           var denom1 = DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(DD.valueOf(p2.x).selfSubtract(p1.x));
94351           var denom2 = DD.valueOf(q2.x).selfSubtract(q1.x).selfMultiply(DD.valueOf(p2.y).selfSubtract(p1.y));
94352           var denom = denom1.subtract(denom2);
94353           var numx1 = DD.valueOf(q2.x).selfSubtract(q1.x).selfMultiply(DD.valueOf(p1.y).selfSubtract(q1.y));
94354           var numx2 = DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(DD.valueOf(p1.x).selfSubtract(q1.x));
94355           var numx = numx1.subtract(numx2);
94356           var fracP = numx.selfDivide(denom).doubleValue();
94357           var x = DD.valueOf(p1.x).selfAdd(DD.valueOf(p2.x).selfSubtract(p1.x).selfMultiply(fracP)).doubleValue();
94358           var numy1 = DD.valueOf(p2.x).selfSubtract(p1.x).selfMultiply(DD.valueOf(p1.y).selfSubtract(q1.y));
94359           var numy2 = DD.valueOf(p2.y).selfSubtract(p1.y).selfMultiply(DD.valueOf(p1.x).selfSubtract(q1.x));
94360           var numy = numy1.subtract(numy2);
94361           var fracQ = numy.selfDivide(denom).doubleValue();
94362           var y = DD.valueOf(q1.y).selfAdd(DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(fracQ)).doubleValue();
94363           return new Coordinate(x, y)
94364         };
94365         CGAlgorithmsDD.orientationIndexFilter = function orientationIndexFilter (pa, pb, pc) {
94366           var detsum = null;
94367           var detleft = (pa.x - pc.x) * (pb.y - pc.y);
94368           var detright = (pa.y - pc.y) * (pb.x - pc.x);
94369           var det = detleft - detright;
94370           if (detleft > 0.0) {
94371             if (detright <= 0.0) {
94372               return CGAlgorithmsDD.signum(det)
94373             } else {
94374               detsum = detleft + detright;
94375             }
94376           } else if (detleft < 0.0) {
94377             if (detright >= 0.0) {
94378               return CGAlgorithmsDD.signum(det)
94379             } else {
94380               detsum = -detleft - detright;
94381             }
94382           } else {
94383             return CGAlgorithmsDD.signum(det)
94384           }
94385           var errbound = CGAlgorithmsDD.DP_SAFE_EPSILON * detsum;
94386           if (det >= errbound || -det >= errbound) {
94387             return CGAlgorithmsDD.signum(det)
94388           }
94389           return 2
94390         };
94391         CGAlgorithmsDD.signum = function signum (x) {
94392           if (x > 0) { return 1 }
94393           if (x < 0) { return -1 }
94394           return 0
94395         };
94396         staticAccessors$6.DP_SAFE_EPSILON.get = function () { return 1e-15 };
94397
94398         Object.defineProperties( CGAlgorithmsDD, staticAccessors$6 );
94399
94400         var CoordinateSequence = function CoordinateSequence () {};
94401
94402         var staticAccessors$8 = { X: { configurable: true },Y: { configurable: true },Z: { configurable: true },M: { configurable: true } };
94403
94404         staticAccessors$8.X.get = function () { return 0 };
94405         staticAccessors$8.Y.get = function () { return 1 };
94406         staticAccessors$8.Z.get = function () { return 2 };
94407         staticAccessors$8.M.get = function () { return 3 };
94408         CoordinateSequence.prototype.setOrdinate = function setOrdinate (index, ordinateIndex, value) {};
94409         CoordinateSequence.prototype.size = function size () {};
94410         CoordinateSequence.prototype.getOrdinate = function getOrdinate (index, ordinateIndex) {};
94411         CoordinateSequence.prototype.getCoordinate = function getCoordinate () {};
94412         CoordinateSequence.prototype.getCoordinateCopy = function getCoordinateCopy (i) {};
94413         CoordinateSequence.prototype.getDimension = function getDimension () {};
94414         CoordinateSequence.prototype.getX = function getX (index) {};
94415         CoordinateSequence.prototype.clone = function clone () {};
94416         CoordinateSequence.prototype.expandEnvelope = function expandEnvelope (env) {};
94417         CoordinateSequence.prototype.copy = function copy () {};
94418         CoordinateSequence.prototype.getY = function getY (index) {};
94419         CoordinateSequence.prototype.toCoordinateArray = function toCoordinateArray () {};
94420         CoordinateSequence.prototype.interfaces_ = function interfaces_ () {
94421           return [Clonable]
94422         };
94423         CoordinateSequence.prototype.getClass = function getClass () {
94424           return CoordinateSequence
94425         };
94426
94427         Object.defineProperties( CoordinateSequence, staticAccessors$8 );
94428
94429         var Exception = function Exception () {};
94430
94431         var NotRepresentableException = (function (Exception$$1) {
94432           function NotRepresentableException () {
94433             Exception$$1.call(this, 'Projective point not representable on the Cartesian plane.');
94434           }
94435
94436           if ( Exception$$1 ) { NotRepresentableException.__proto__ = Exception$$1; }
94437           NotRepresentableException.prototype = Object.create( Exception$$1 && Exception$$1.prototype );
94438           NotRepresentableException.prototype.constructor = NotRepresentableException;
94439           NotRepresentableException.prototype.interfaces_ = function interfaces_ () {
94440             return []
94441           };
94442           NotRepresentableException.prototype.getClass = function getClass () {
94443             return NotRepresentableException
94444           };
94445
94446           return NotRepresentableException;
94447         }(Exception));
94448
94449         var System = function System () {};
94450
94451         System.arraycopy = function arraycopy (src, srcPos, dest, destPos, len) {
94452           var c = 0;
94453           for (var i = srcPos; i < srcPos + len; i++) {
94454             dest[destPos + c] = src[i];
94455             c++;
94456           }
94457         };
94458
94459         System.getProperty = function getProperty (name) {
94460           return {
94461             'line.separator': '\n'
94462           }[name]
94463         };
94464
94465         var HCoordinate = function HCoordinate () {
94466           this.x = null;
94467           this.y = null;
94468           this.w = null;
94469           if (arguments.length === 0) {
94470             this.x = 0.0;
94471             this.y = 0.0;
94472             this.w = 1.0;
94473           } else if (arguments.length === 1) {
94474             var p = arguments[0];
94475             this.x = p.x;
94476             this.y = p.y;
94477             this.w = 1.0;
94478           } else if (arguments.length === 2) {
94479             if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
94480               var _x = arguments[0];
94481               var _y = arguments[1];
94482               this.x = _x;
94483               this.y = _y;
94484               this.w = 1.0;
94485             } else if (arguments[0] instanceof HCoordinate && arguments[1] instanceof HCoordinate) {
94486               var p1 = arguments[0];
94487               var p2 = arguments[1];
94488               this.x = p1.y * p2.w - p2.y * p1.w;
94489               this.y = p2.x * p1.w - p1.x * p2.w;
94490               this.w = p1.x * p2.y - p2.x * p1.y;
94491             } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
94492               var p1$1 = arguments[0];
94493               var p2$1 = arguments[1];
94494               this.x = p1$1.y - p2$1.y;
94495               this.y = p2$1.x - p1$1.x;
94496               this.w = p1$1.x * p2$1.y - p2$1.x * p1$1.y;
94497             }
94498           } else if (arguments.length === 3) {
94499             var _x$1 = arguments[0];
94500             var _y$1 = arguments[1];
94501             var _w = arguments[2];
94502             this.x = _x$1;
94503             this.y = _y$1;
94504             this.w = _w;
94505           } else if (arguments.length === 4) {
94506             var p1$2 = arguments[0];
94507             var p2$2 = arguments[1];
94508             var q1 = arguments[2];
94509             var q2 = arguments[3];
94510             var px = p1$2.y - p2$2.y;
94511             var py = p2$2.x - p1$2.x;
94512             var pw = p1$2.x * p2$2.y - p2$2.x * p1$2.y;
94513             var qx = q1.y - q2.y;
94514             var qy = q2.x - q1.x;
94515             var qw = q1.x * q2.y - q2.x * q1.y;
94516             this.x = py * qw - qy * pw;
94517             this.y = qx * pw - px * qw;
94518             this.w = px * qy - qx * py;
94519           }
94520         };
94521         HCoordinate.prototype.getY = function getY () {
94522           var a = this.y / this.w;
94523           if (Double.isNaN(a) || Double.isInfinite(a)) {
94524             throw new NotRepresentableException()
94525           }
94526           return a
94527         };
94528         HCoordinate.prototype.getX = function getX () {
94529           var a = this.x / this.w;
94530           if (Double.isNaN(a) || Double.isInfinite(a)) {
94531             throw new NotRepresentableException()
94532           }
94533           return a
94534         };
94535         HCoordinate.prototype.getCoordinate = function getCoordinate () {
94536           var p = new Coordinate();
94537           p.x = this.getX();
94538           p.y = this.getY();
94539           return p
94540         };
94541         HCoordinate.prototype.interfaces_ = function interfaces_ () {
94542           return []
94543         };
94544         HCoordinate.prototype.getClass = function getClass () {
94545           return HCoordinate
94546         };
94547         HCoordinate.intersection = function intersection (p1, p2, q1, q2) {
94548           var px = p1.y - p2.y;
94549           var py = p2.x - p1.x;
94550           var pw = p1.x * p2.y - p2.x * p1.y;
94551           var qx = q1.y - q2.y;
94552           var qy = q2.x - q1.x;
94553           var qw = q1.x * q2.y - q2.x * q1.y;
94554           var x = py * qw - qy * pw;
94555           var y = qx * pw - px * qw;
94556           var w = px * qy - qx * py;
94557           var xInt = x / w;
94558           var yInt = y / w;
94559           if (Double.isNaN(xInt) || (Double.isInfinite(xInt) || Double.isNaN(yInt)) || Double.isInfinite(yInt)) {
94560             throw new NotRepresentableException()
94561           }
94562           return new Coordinate(xInt, yInt)
94563         };
94564
94565         var Envelope = function Envelope () {
94566           this._minx = null;
94567           this._maxx = null;
94568           this._miny = null;
94569           this._maxy = null;
94570           if (arguments.length === 0) {
94571             this.init();
94572           } else if (arguments.length === 1) {
94573             if (arguments[0] instanceof Coordinate) {
94574               var p = arguments[0];
94575               this.init(p.x, p.x, p.y, p.y);
94576             } else if (arguments[0] instanceof Envelope) {
94577               var env = arguments[0];
94578               this.init(env);
94579             }
94580           } else if (arguments.length === 2) {
94581             var p1 = arguments[0];
94582             var p2 = arguments[1];
94583             this.init(p1.x, p2.x, p1.y, p2.y);
94584           } else if (arguments.length === 4) {
94585             var x1 = arguments[0];
94586             var x2 = arguments[1];
94587             var y1 = arguments[2];
94588             var y2 = arguments[3];
94589             this.init(x1, x2, y1, y2);
94590           }
94591         };
94592
94593         var staticAccessors$9 = { serialVersionUID: { configurable: true } };
94594         Envelope.prototype.getArea = function getArea () {
94595           return this.getWidth() * this.getHeight()
94596         };
94597         Envelope.prototype.equals = function equals (other) {
94598           if (!(other instanceof Envelope)) {
94599             return false
94600           }
94601           var otherEnvelope = other;
94602           if (this.isNull()) {
94603             return otherEnvelope.isNull()
94604           }
94605           return this._maxx === otherEnvelope.getMaxX() && this._maxy === otherEnvelope.getMaxY() && this._minx === otherEnvelope.getMinX() && this._miny === otherEnvelope.getMinY()
94606         };
94607         Envelope.prototype.intersection = function intersection (env) {
94608           if (this.isNull() || env.isNull() || !this.intersects(env)) { return new Envelope() }
94609           var intMinX = this._minx > env._minx ? this._minx : env._minx;
94610           var intMinY = this._miny > env._miny ? this._miny : env._miny;
94611           var intMaxX = this._maxx < env._maxx ? this._maxx : env._maxx;
94612           var intMaxY = this._maxy < env._maxy ? this._maxy : env._maxy;
94613           return new Envelope(intMinX, intMaxX, intMinY, intMaxY)
94614         };
94615         Envelope.prototype.isNull = function isNull () {
94616           return this._maxx < this._minx
94617         };
94618         Envelope.prototype.getMaxX = function getMaxX () {
94619           return this._maxx
94620         };
94621         Envelope.prototype.covers = function covers () {
94622           if (arguments.length === 1) {
94623             if (arguments[0] instanceof Coordinate) {
94624               var p = arguments[0];
94625               return this.covers(p.x, p.y)
94626             } else if (arguments[0] instanceof Envelope) {
94627               var other = arguments[0];
94628               if (this.isNull() || other.isNull()) {
94629                 return false
94630               }
94631               return other.getMinX() >= this._minx && other.getMaxX() <= this._maxx && other.getMinY() >= this._miny && other.getMaxY() <= this._maxy
94632             }
94633           } else if (arguments.length === 2) {
94634             var x = arguments[0];
94635             var y = arguments[1];
94636             if (this.isNull()) { return false }
94637             return x >= this._minx && x <= this._maxx && y >= this._miny && y <= this._maxy
94638           }
94639         };
94640         Envelope.prototype.intersects = function intersects () {
94641           if (arguments.length === 1) {
94642             if (arguments[0] instanceof Envelope) {
94643               var other = arguments[0];
94644               if (this.isNull() || other.isNull()) {
94645                 return false
94646               }
94647               return !(other._minx > this._maxx || other._maxx < this._minx || other._miny > this._maxy || other._maxy < this._miny)
94648             } else if (arguments[0] instanceof Coordinate) {
94649               var p = arguments[0];
94650               return this.intersects(p.x, p.y)
94651             }
94652           } else if (arguments.length === 2) {
94653             var x = arguments[0];
94654             var y = arguments[1];
94655             if (this.isNull()) { return false }
94656             return !(x > this._maxx || x < this._minx || y > this._maxy || y < this._miny)
94657           }
94658         };
94659         Envelope.prototype.getMinY = function getMinY () {
94660           return this._miny
94661         };
94662         Envelope.prototype.getMinX = function getMinX () {
94663           return this._minx
94664         };
94665         Envelope.prototype.expandToInclude = function expandToInclude () {
94666           if (arguments.length === 1) {
94667             if (arguments[0] instanceof Coordinate) {
94668               var p = arguments[0];
94669               this.expandToInclude(p.x, p.y);
94670             } else if (arguments[0] instanceof Envelope) {
94671               var other = arguments[0];
94672               if (other.isNull()) {
94673                 return null
94674               }
94675               if (this.isNull()) {
94676                 this._minx = other.getMinX();
94677                 this._maxx = other.getMaxX();
94678                 this._miny = other.getMinY();
94679                 this._maxy = other.getMaxY();
94680               } else {
94681                 if (other._minx < this._minx) {
94682                   this._minx = other._minx;
94683                 }
94684                 if (other._maxx > this._maxx) {
94685                   this._maxx = other._maxx;
94686                 }
94687                 if (other._miny < this._miny) {
94688                   this._miny = other._miny;
94689                 }
94690                 if (other._maxy > this._maxy) {
94691                   this._maxy = other._maxy;
94692                 }
94693               }
94694             }
94695           } else if (arguments.length === 2) {
94696             var x = arguments[0];
94697             var y = arguments[1];
94698             if (this.isNull()) {
94699               this._minx = x;
94700               this._maxx = x;
94701               this._miny = y;
94702               this._maxy = y;
94703             } else {
94704               if (x < this._minx) {
94705                 this._minx = x;
94706               }
94707               if (x > this._maxx) {
94708                 this._maxx = x;
94709               }
94710               if (y < this._miny) {
94711                 this._miny = y;
94712               }
94713               if (y > this._maxy) {
94714                 this._maxy = y;
94715               }
94716             }
94717           }
94718         };
94719         Envelope.prototype.minExtent = function minExtent () {
94720           if (this.isNull()) { return 0.0 }
94721           var w = this.getWidth();
94722           var h = this.getHeight();
94723           if (w < h) { return w }
94724           return h
94725         };
94726         Envelope.prototype.getWidth = function getWidth () {
94727           if (this.isNull()) {
94728             return 0
94729           }
94730           return this._maxx - this._minx
94731         };
94732         Envelope.prototype.compareTo = function compareTo (o) {
94733           var env = o;
94734           if (this.isNull()) {
94735             if (env.isNull()) { return 0 }
94736             return -1
94737           } else {
94738             if (env.isNull()) { return 1 }
94739           }
94740           if (this._minx < env._minx) { return -1 }
94741           if (this._minx > env._minx) { return 1 }
94742           if (this._miny < env._miny) { return -1 }
94743           if (this._miny > env._miny) { return 1 }
94744           if (this._maxx < env._maxx) { return -1 }
94745           if (this._maxx > env._maxx) { return 1 }
94746           if (this._maxy < env._maxy) { return -1 }
94747           if (this._maxy > env._maxy) { return 1 }
94748           return 0
94749         };
94750         Envelope.prototype.translate = function translate (transX, transY) {
94751           if (this.isNull()) {
94752             return null
94753           }
94754           this.init(this.getMinX() + transX, this.getMaxX() + transX, this.getMinY() + transY, this.getMaxY() + transY);
94755         };
94756         Envelope.prototype.toString = function toString () {
94757           return 'Env[' + this._minx + ' : ' + this._maxx + ', ' + this._miny + ' : ' + this._maxy + ']'
94758         };
94759         Envelope.prototype.setToNull = function setToNull () {
94760           this._minx = 0;
94761           this._maxx = -1;
94762           this._miny = 0;
94763           this._maxy = -1;
94764         };
94765         Envelope.prototype.getHeight = function getHeight () {
94766           if (this.isNull()) {
94767             return 0
94768           }
94769           return this._maxy - this._miny
94770         };
94771         Envelope.prototype.maxExtent = function maxExtent () {
94772           if (this.isNull()) { return 0.0 }
94773           var w = this.getWidth();
94774           var h = this.getHeight();
94775           if (w > h) { return w }
94776           return h
94777         };
94778         Envelope.prototype.expandBy = function expandBy () {
94779           if (arguments.length === 1) {
94780             var distance = arguments[0];
94781             this.expandBy(distance, distance);
94782           } else if (arguments.length === 2) {
94783             var deltaX = arguments[0];
94784             var deltaY = arguments[1];
94785             if (this.isNull()) { return null }
94786             this._minx -= deltaX;
94787             this._maxx += deltaX;
94788             this._miny -= deltaY;
94789             this._maxy += deltaY;
94790             if (this._minx > this._maxx || this._miny > this._maxy) { this.setToNull(); }
94791           }
94792         };
94793         Envelope.prototype.contains = function contains () {
94794           if (arguments.length === 1) {
94795             if (arguments[0] instanceof Envelope) {
94796               var other = arguments[0];
94797               return this.covers(other)
94798             } else if (arguments[0] instanceof Coordinate) {
94799               var p = arguments[0];
94800               return this.covers(p)
94801             }
94802           } else if (arguments.length === 2) {
94803             var x = arguments[0];
94804             var y = arguments[1];
94805             return this.covers(x, y)
94806           }
94807         };
94808         Envelope.prototype.centre = function centre () {
94809           if (this.isNull()) { return null }
94810           return new Coordinate((this.getMinX() + this.getMaxX()) / 2.0, (this.getMinY() + this.getMaxY()) / 2.0)
94811         };
94812         Envelope.prototype.init = function init () {
94813           if (arguments.length === 0) {
94814             this.setToNull();
94815           } else if (arguments.length === 1) {
94816             if (arguments[0] instanceof Coordinate) {
94817               var p = arguments[0];
94818               this.init(p.x, p.x, p.y, p.y);
94819             } else if (arguments[0] instanceof Envelope) {
94820               var env = arguments[0];
94821               this._minx = env._minx;
94822               this._maxx = env._maxx;
94823               this._miny = env._miny;
94824               this._maxy = env._maxy;
94825             }
94826           } else if (arguments.length === 2) {
94827             var p1 = arguments[0];
94828             var p2 = arguments[1];
94829             this.init(p1.x, p2.x, p1.y, p2.y);
94830           } else if (arguments.length === 4) {
94831             var x1 = arguments[0];
94832             var x2 = arguments[1];
94833             var y1 = arguments[2];
94834             var y2 = arguments[3];
94835             if (x1 < x2) {
94836               this._minx = x1;
94837               this._maxx = x2;
94838             } else {
94839               this._minx = x2;
94840               this._maxx = x1;
94841             }
94842             if (y1 < y2) {
94843               this._miny = y1;
94844               this._maxy = y2;
94845             } else {
94846               this._miny = y2;
94847               this._maxy = y1;
94848             }
94849           }
94850         };
94851         Envelope.prototype.getMaxY = function getMaxY () {
94852           return this._maxy
94853         };
94854         Envelope.prototype.distance = function distance (env) {
94855           if (this.intersects(env)) { return 0 }
94856           var dx = 0.0;
94857           if (this._maxx < env._minx) { dx = env._minx - this._maxx; } else if (this._minx > env._maxx) { dx = this._minx - env._maxx; }
94858           var dy = 0.0;
94859           if (this._maxy < env._miny) { dy = env._miny - this._maxy; } else if (this._miny > env._maxy) { dy = this._miny - env._maxy; }
94860           if (dx === 0.0) { return dy }
94861           if (dy === 0.0) { return dx }
94862           return Math.sqrt(dx * dx + dy * dy)
94863         };
94864         Envelope.prototype.hashCode = function hashCode () {
94865           var result = 17;
94866           result = 37 * result + Coordinate.hashCode(this._minx);
94867           result = 37 * result + Coordinate.hashCode(this._maxx);
94868           result = 37 * result + Coordinate.hashCode(this._miny);
94869           result = 37 * result + Coordinate.hashCode(this._maxy);
94870           return result
94871         };
94872         Envelope.prototype.interfaces_ = function interfaces_ () {
94873           return [Comparable, Serializable]
94874         };
94875         Envelope.prototype.getClass = function getClass () {
94876           return Envelope
94877         };
94878         Envelope.intersects = function intersects () {
94879           if (arguments.length === 3) {
94880             var p1 = arguments[0];
94881             var p2 = arguments[1];
94882             var q = arguments[2];
94883             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))) {
94884               return true
94885             }
94886             return false
94887           } else if (arguments.length === 4) {
94888             var p1$1 = arguments[0];
94889             var p2$1 = arguments[1];
94890             var q1 = arguments[2];
94891             var q2 = arguments[3];
94892             var minq = Math.min(q1.x, q2.x);
94893             var maxq = Math.max(q1.x, q2.x);
94894             var minp = Math.min(p1$1.x, p2$1.x);
94895             var maxp = Math.max(p1$1.x, p2$1.x);
94896             if (minp > maxq) { return false }
94897             if (maxp < minq) { return false }
94898             minq = Math.min(q1.y, q2.y);
94899             maxq = Math.max(q1.y, q2.y);
94900             minp = Math.min(p1$1.y, p2$1.y);
94901             maxp = Math.max(p1$1.y, p2$1.y);
94902             if (minp > maxq) { return false }
94903             if (maxp < minq) { return false }
94904             return true
94905           }
94906         };
94907         staticAccessors$9.serialVersionUID.get = function () { return 5873921885273102420 };
94908
94909         Object.defineProperties( Envelope, staticAccessors$9 );
94910
94911         var regExes = {
94912           'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
94913           'emptyTypeStr': /^\s*(\w+)\s*EMPTY\s*$/,
94914           'spaces': /\s+/,
94915           'parenComma': /\)\s*,\s*\(/,
94916           'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, // can't use {2} here
94917           'trimParens': /^\s*\(?(.*?)\)?\s*$/
94918         };
94919
94920         /**
94921          * Class for reading and writing Well-Known Text.
94922          *
94923          * NOTE: Adapted from OpenLayers 2.11 implementation.
94924          */
94925
94926         /** Create a new parser for WKT
94927          *
94928          * @param {GeometryFactory} geometryFactory
94929          * @return An instance of WKTParser.
94930          * @constructor
94931          * @private
94932          */
94933         var WKTParser = function WKTParser (geometryFactory) {
94934           this.geometryFactory = geometryFactory || new GeometryFactory();
94935         };
94936         /**
94937          * Deserialize a WKT string and return a geometry. Supports WKT for POINT,
94938          * MULTIPOINT, LINESTRING, LINEARRING, MULTILINESTRING, POLYGON, MULTIPOLYGON,
94939          * and GEOMETRYCOLLECTION.
94940          *
94941          * @param {String} wkt A WKT string.
94942          * @return {Geometry} A geometry instance.
94943          * @private
94944          */
94945         WKTParser.prototype.read = function read (wkt) {
94946           var geometry, type, str;
94947           wkt = wkt.replace(/[\n\r]/g, ' ');
94948           var matches = regExes.typeStr.exec(wkt);
94949           if (wkt.search('EMPTY') !== -1) {
94950             matches = regExes.emptyTypeStr.exec(wkt);
94951             matches[2] = undefined;
94952           }
94953           if (matches) {
94954             type = matches[1].toLowerCase();
94955             str = matches[2];
94956             if (parse$1[type]) {
94957               geometry = parse$1[type].apply(this, [str]);
94958             }
94959           }
94960
94961           if (geometry === undefined) { throw new Error('Could not parse WKT ' + wkt) }
94962
94963           return geometry
94964         };
94965
94966         /**
94967          * Serialize a geometry into a WKT string.
94968          *
94969          * @param {Geometry} geometry A feature or array of features.
94970          * @return {String} The WKT string representation of the input geometries.
94971          * @private
94972          */
94973         WKTParser.prototype.write = function write (geometry) {
94974           return this.extractGeometry(geometry)
94975         };
94976
94977         /**
94978          * Entry point to construct the WKT for a single Geometry object.
94979          *
94980          * @param {Geometry} geometry
94981          * @return {String} A WKT string of representing the geometry.
94982          * @private
94983          */
94984         WKTParser.prototype.extractGeometry = function extractGeometry (geometry) {
94985           var type = geometry.getGeometryType().toLowerCase();
94986           if (!extract$1[type]) {
94987             return null
94988           }
94989           var wktType = type.toUpperCase();
94990           var data;
94991           if (geometry.isEmpty()) {
94992             data = wktType + ' EMPTY';
94993           } else {
94994             data = wktType + '(' + extract$1[type].apply(this, [geometry]) + ')';
94995           }
94996           return data
94997         };
94998
94999         /**
95000          * Object with properties corresponding to the geometry types. Property values
95001          * are functions that do the actual data extraction.
95002          * @private
95003          */
95004         var extract$1 = {
95005           coordinate: function coordinate (coordinate$1) {
95006             return coordinate$1.x + ' ' + coordinate$1.y
95007           },
95008
95009           /**
95010            * Return a space delimited string of point coordinates.
95011            *
95012            * @param {Point}
95013            *          point
95014            * @return {String} A string of coordinates representing the point.
95015            */
95016           point: function point (point$1) {
95017             return extract$1.coordinate.call(this, point$1._coordinates._coordinates[0])
95018           },
95019
95020           /**
95021            * Return a comma delimited string of point coordinates from a multipoint.
95022            *
95023            * @param {MultiPoint}
95024            *          multipoint
95025            * @return {String} A string of point coordinate strings representing the
95026            *         multipoint.
95027            */
95028           multipoint: function multipoint (multipoint$1) {
95029             var this$1 = this;
95030
95031             var array = [];
95032             for (var i = 0, len = multipoint$1._geometries.length; i < len; ++i) {
95033               array.push('(' + extract$1.point.apply(this$1, [multipoint$1._geometries[i]]) + ')');
95034             }
95035             return array.join(',')
95036           },
95037
95038           /**
95039            * Return a comma delimited string of point coordinates from a line.
95040            *
95041            * @param {LineString} linestring
95042            * @return {String} A string of point coordinate strings representing the linestring.
95043            */
95044           linestring: function linestring (linestring$1) {
95045             var this$1 = this;
95046
95047             var array = [];
95048             for (var i = 0, len = linestring$1._points._coordinates.length; i < len; ++i) {
95049               array.push(extract$1.coordinate.apply(this$1, [linestring$1._points._coordinates[i]]));
95050             }
95051             return array.join(',')
95052           },
95053
95054           linearring: function linearring (linearring$1) {
95055             var this$1 = this;
95056
95057             var array = [];
95058             for (var i = 0, len = linearring$1._points._coordinates.length; i < len; ++i) {
95059               array.push(extract$1.coordinate.apply(this$1, [linearring$1._points._coordinates[i]]));
95060             }
95061             return array.join(',')
95062           },
95063
95064           /**
95065            * Return a comma delimited string of linestring strings from a
95066            * multilinestring.
95067            *
95068            * @param {MultiLineString} multilinestring
95069            * @return {String} A string of of linestring strings representing the multilinestring.
95070            */
95071           multilinestring: function multilinestring (multilinestring$1) {
95072             var this$1 = this;
95073
95074             var array = [];
95075             for (var i = 0, len = multilinestring$1._geometries.length; i < len; ++i) {
95076               array.push('(' +
95077                 extract$1.linestring.apply(this$1, [multilinestring$1._geometries[i]]) +
95078                 ')');
95079             }
95080             return array.join(',')
95081           },
95082
95083           /**
95084            * Return a comma delimited string of linear ring arrays from a polygon.
95085            *
95086            * @param {Polygon} polygon
95087            * @return {String} An array of linear ring arrays representing the polygon.
95088            */
95089           polygon: function polygon (polygon$1) {
95090             var this$1 = this;
95091
95092             var array = [];
95093             array.push('(' + extract$1.linestring.apply(this, [polygon$1._shell]) + ')');
95094             for (var i = 0, len = polygon$1._holes.length; i < len; ++i) {
95095               array.push('(' + extract$1.linestring.apply(this$1, [polygon$1._holes[i]]) + ')');
95096             }
95097             return array.join(',')
95098           },
95099
95100           /**
95101            * Return an array of polygon arrays from a multipolygon.
95102            *
95103            * @param {MultiPolygon} multipolygon
95104            * @return {String} An array of polygon arrays representing the multipolygon.
95105            */
95106           multipolygon: function multipolygon (multipolygon$1) {
95107             var this$1 = this;
95108
95109             var array = [];
95110             for (var i = 0, len = multipolygon$1._geometries.length; i < len; ++i) {
95111               array.push('(' + extract$1.polygon.apply(this$1, [multipolygon$1._geometries[i]]) + ')');
95112             }
95113             return array.join(',')
95114           },
95115
95116           /**
95117            * Return the WKT portion between 'GEOMETRYCOLLECTION(' and ')' for an
95118            * geometrycollection.
95119            *
95120            * @param {GeometryCollection} collection
95121            * @return {String} internal WKT representation of the collection.
95122            */
95123           geometrycollection: function geometrycollection (collection) {
95124             var this$1 = this;
95125
95126             var array = [];
95127             for (var i = 0, len = collection._geometries.length; i < len; ++i) {
95128               array.push(this$1.extractGeometry(collection._geometries[i]));
95129             }
95130             return array.join(',')
95131           }
95132         };
95133
95134         /**
95135          * Object with properties corresponding to the geometry types. Property values
95136          * are functions that do the actual parsing.
95137          * @private
95138          */
95139         var parse$1 = {
95140           /**
95141            * Return point geometry given a point WKT fragment.
95142            *
95143            * @param {String} str A WKT fragment representing the point.
95144            * @return {Point} A point geometry.
95145            * @private
95146            */
95147           point: function point (str) {
95148             if (str === undefined) {
95149               return this.geometryFactory.createPoint()
95150             }
95151
95152             var coords = str.trim().split(regExes.spaces);
95153             return this.geometryFactory.createPoint(new Coordinate(Number.parseFloat(coords[0]),
95154               Number.parseFloat(coords[1])))
95155           },
95156
95157           /**
95158            * Return a multipoint geometry given a multipoint WKT fragment.
95159            *
95160            * @param {String} str A WKT fragment representing the multipoint.
95161            * @return {Point} A multipoint feature.
95162            * @private
95163            */
95164           multipoint: function multipoint (str) {
95165             var this$1 = this;
95166
95167             if (str === undefined) {
95168               return this.geometryFactory.createMultiPoint()
95169             }
95170
95171             var point;
95172             var points = str.trim().split(',');
95173             var components = [];
95174             for (var i = 0, len = points.length; i < len; ++i) {
95175               point = points[i].replace(regExes.trimParens, '$1');
95176               components.push(parse$1.point.apply(this$1, [point]));
95177             }
95178             return this.geometryFactory.createMultiPoint(components)
95179           },
95180
95181           /**
95182            * Return a linestring geometry given a linestring WKT fragment.
95183            *
95184            * @param {String} str A WKT fragment representing the linestring.
95185            * @return {LineString} A linestring geometry.
95186            * @private
95187            */
95188           linestring: function linestring (str) {
95189             if (str === undefined) {
95190               return this.geometryFactory.createLineString()
95191             }
95192
95193             var points = str.trim().split(',');
95194             var components = [];
95195             var coords;
95196             for (var i = 0, len = points.length; i < len; ++i) {
95197               coords = points[i].trim().split(regExes.spaces);
95198               components.push(new Coordinate(Number.parseFloat(coords[0]), Number.parseFloat(coords[1])));
95199             }
95200             return this.geometryFactory.createLineString(components)
95201           },
95202
95203           /**
95204            * Return a linearring geometry given a linearring WKT fragment.
95205            *
95206            * @param {String} str A WKT fragment representing the linearring.
95207            * @return {LinearRing} A linearring geometry.
95208            * @private
95209            */
95210           linearring: function linearring (str) {
95211             if (str === undefined) {
95212               return this.geometryFactory.createLinearRing()
95213             }
95214
95215             var points = str.trim().split(',');
95216             var components = [];
95217             var coords;
95218             for (var i = 0, len = points.length; i < len; ++i) {
95219               coords = points[i].trim().split(regExes.spaces);
95220               components.push(new Coordinate(Number.parseFloat(coords[0]), Number.parseFloat(coords[1])));
95221             }
95222             return this.geometryFactory.createLinearRing(components)
95223           },
95224
95225           /**
95226            * Return a multilinestring geometry given a multilinestring WKT fragment.
95227            *
95228            * @param {String} str A WKT fragment representing the multilinestring.
95229            * @return {MultiLineString} A multilinestring geometry.
95230            * @private
95231            */
95232           multilinestring: function multilinestring (str) {
95233             var this$1 = this;
95234
95235             if (str === undefined) {
95236               return this.geometryFactory.createMultiLineString()
95237             }
95238
95239             var line;
95240             var lines = str.trim().split(regExes.parenComma);
95241             var components = [];
95242             for (var i = 0, len = lines.length; i < len; ++i) {
95243               line = lines[i].replace(regExes.trimParens, '$1');
95244               components.push(parse$1.linestring.apply(this$1, [line]));
95245             }
95246             return this.geometryFactory.createMultiLineString(components)
95247           },
95248
95249           /**
95250            * Return a polygon geometry given a polygon WKT fragment.
95251            *
95252            * @param {String} str A WKT fragment representing the polygon.
95253            * @return {Polygon} A polygon geometry.
95254            * @private
95255            */
95256           polygon: function polygon (str) {
95257             var this$1 = this;
95258
95259             if (str === undefined) {
95260               return this.geometryFactory.createPolygon()
95261             }
95262
95263             var ring, linestring, linearring;
95264             var rings = str.trim().split(regExes.parenComma);
95265             var shell;
95266             var holes = [];
95267             for (var i = 0, len = rings.length; i < len; ++i) {
95268               ring = rings[i].replace(regExes.trimParens, '$1');
95269               linestring = parse$1.linestring.apply(this$1, [ring]);
95270               linearring = this$1.geometryFactory.createLinearRing(linestring._points);
95271               if (i === 0) {
95272                 shell = linearring;
95273               } else {
95274                 holes.push(linearring);
95275               }
95276             }
95277             return this.geometryFactory.createPolygon(shell, holes)
95278           },
95279
95280           /**
95281            * Return a multipolygon geometry given a multipolygon WKT fragment.
95282            *
95283            * @param {String} str A WKT fragment representing the multipolygon.
95284            * @return {MultiPolygon} A multipolygon geometry.
95285            * @private
95286            */
95287           multipolygon: function multipolygon (str) {
95288             var this$1 = this;
95289
95290             if (str === undefined) {
95291               return this.geometryFactory.createMultiPolygon()
95292             }
95293
95294             var polygon;
95295             var polygons = str.trim().split(regExes.doubleParenComma);
95296             var components = [];
95297             for (var i = 0, len = polygons.length; i < len; ++i) {
95298               polygon = polygons[i].replace(regExes.trimParens, '$1');
95299               components.push(parse$1.polygon.apply(this$1, [polygon]));
95300             }
95301             return this.geometryFactory.createMultiPolygon(components)
95302           },
95303
95304           /**
95305            * Return a geometrycollection given a geometrycollection WKT fragment.
95306            *
95307            * @param {String} str A WKT fragment representing the geometrycollection.
95308            * @return {GeometryCollection}
95309            * @private
95310            */
95311           geometrycollection: function geometrycollection (str) {
95312             var this$1 = this;
95313
95314             if (str === undefined) {
95315               return this.geometryFactory.createGeometryCollection()
95316             }
95317
95318             // separate components of the collection with |
95319             str = str.replace(/,\s*([A-Za-z])/g, '|$1');
95320             var wktArray = str.trim().split('|');
95321             var components = [];
95322             for (var i = 0, len = wktArray.length; i < len; ++i) {
95323               components.push(this$1.read(wktArray[i]));
95324             }
95325             return this.geometryFactory.createGeometryCollection(components)
95326           }
95327         };
95328
95329         /**
95330          * Writes the Well-Known Text representation of a {@link Geometry}. The
95331          * Well-Known Text format is defined in the <A
95332          * HREF="http://www.opengis.org/techno/specs.htm"> OGC Simple Features
95333          * Specification for SQL</A>.
95334          * <p>
95335          * The <code>WKTWriter</code> outputs coordinates rounded to the precision
95336          * model. Only the maximum number of decimal places necessary to represent the
95337          * ordinates to the required precision will be output.
95338          * <p>
95339          * The SFS WKT spec does not define a special tag for {@link LinearRing}s.
95340          * Under the spec, rings are output as <code>LINESTRING</code>s.
95341          */
95342
95343         /**
95344          * @param {GeometryFactory} geometryFactory
95345          * @constructor
95346          */
95347         var WKTWriter = function WKTWriter (geometryFactory) {
95348           this.parser = new WKTParser(geometryFactory);
95349         };
95350
95351         /**
95352          * Converts a <code>Geometry</code> to its Well-known Text representation.
95353          *
95354          * @param {Geometry} geometry a <code>Geometry</code> to process.
95355          * @return {string} a <Geometry Tagged Text> string (see the OpenGIS Simple
95356          *       Features Specification).
95357          * @memberof WKTWriter
95358          */
95359         WKTWriter.prototype.write = function write (geometry) {
95360           return this.parser.write(geometry)
95361         };
95362         /**
95363          * Generates the WKT for a <tt>LINESTRING</tt> specified by two
95364          * {@link Coordinate}s.
95365          *
95366          * @param p0 the first coordinate.
95367          * @param p1 the second coordinate.
95368          *
95369          * @return the WKT.
95370          * @private
95371          */
95372         WKTWriter.toLineString = function toLineString (p0, p1) {
95373           if (arguments.length !== 2) {
95374             throw new Error('Not implemented')
95375           }
95376           return 'LINESTRING ( ' + p0.x + ' ' + p0.y + ', ' + p1.x + ' ' + p1.y + ' )'
95377         };
95378
95379         var RuntimeException = (function (Error) {
95380           function RuntimeException (message) {
95381             Error.call(this, message);
95382             this.name = 'RuntimeException';
95383             this.message = message;
95384             this.stack = (new Error()).stack;
95385           }
95386
95387           if ( Error ) { RuntimeException.__proto__ = Error; }
95388           RuntimeException.prototype = Object.create( Error && Error.prototype );
95389           RuntimeException.prototype.constructor = RuntimeException;
95390
95391           return RuntimeException;
95392         }(Error));
95393
95394         var AssertionFailedException = (function (RuntimeException$$1) {
95395           function AssertionFailedException () {
95396             RuntimeException$$1.call(this);
95397             if (arguments.length === 0) {
95398               RuntimeException$$1.call(this);
95399             } else if (arguments.length === 1) {
95400               var message = arguments[0];
95401               RuntimeException$$1.call(this, message);
95402             }
95403           }
95404
95405           if ( RuntimeException$$1 ) { AssertionFailedException.__proto__ = RuntimeException$$1; }
95406           AssertionFailedException.prototype = Object.create( RuntimeException$$1 && RuntimeException$$1.prototype );
95407           AssertionFailedException.prototype.constructor = AssertionFailedException;
95408           AssertionFailedException.prototype.interfaces_ = function interfaces_ () {
95409             return []
95410           };
95411           AssertionFailedException.prototype.getClass = function getClass () {
95412             return AssertionFailedException
95413           };
95414
95415           return AssertionFailedException;
95416         }(RuntimeException));
95417
95418         var Assert = function Assert () {};
95419
95420         Assert.prototype.interfaces_ = function interfaces_ () {
95421           return []
95422         };
95423         Assert.prototype.getClass = function getClass () {
95424           return Assert
95425         };
95426         Assert.shouldNeverReachHere = function shouldNeverReachHere () {
95427           if (arguments.length === 0) {
95428             Assert.shouldNeverReachHere(null);
95429           } else if (arguments.length === 1) {
95430             var message = arguments[0];
95431             throw new AssertionFailedException('Should never reach here' + (message !== null ? ': ' + message : ''))
95432           }
95433         };
95434         Assert.isTrue = function isTrue () {
95435           var assertion;
95436           var message;
95437           if (arguments.length === 1) {
95438             assertion = arguments[0];
95439             Assert.isTrue(assertion, null);
95440           } else if (arguments.length === 2) {
95441             assertion = arguments[0];
95442             message = arguments[1];
95443             if (!assertion) {
95444               if (message === null) {
95445                 throw new AssertionFailedException()
95446               } else {
95447                 throw new AssertionFailedException(message)
95448               }
95449             }
95450           }
95451         };
95452         Assert.equals = function equals () {
95453           var expectedValue;
95454           var actualValue;
95455           var message;
95456           if (arguments.length === 2) {
95457             expectedValue = arguments[0];
95458             actualValue = arguments[1];
95459             Assert.equals(expectedValue, actualValue, null);
95460           } else if (arguments.length === 3) {
95461             expectedValue = arguments[0];
95462             actualValue = arguments[1];
95463             message = arguments[2];
95464             if (!actualValue.equals(expectedValue)) {
95465               throw new AssertionFailedException('Expected ' + expectedValue + ' but encountered ' + actualValue + (message !== null ? ': ' + message : ''))
95466             }
95467           }
95468         };
95469
95470         var LineIntersector = function LineIntersector () {
95471           this._result = null;
95472           this._inputLines = Array(2).fill().map(function () { return Array(2); });
95473           this._intPt = new Array(2).fill(null);
95474           this._intLineIndex = null;
95475           this._isProper = null;
95476           this._pa = null;
95477           this._pb = null;
95478           this._precisionModel = null;
95479           this._intPt[0] = new Coordinate();
95480           this._intPt[1] = new Coordinate();
95481           this._pa = this._intPt[0];
95482           this._pb = this._intPt[1];
95483           this._result = 0;
95484         };
95485
95486         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 } };
95487         LineIntersector.prototype.getIndexAlongSegment = function getIndexAlongSegment (segmentIndex, intIndex) {
95488           this.computeIntLineIndex();
95489           return this._intLineIndex[segmentIndex][intIndex]
95490         };
95491         LineIntersector.prototype.getTopologySummary = function getTopologySummary () {
95492           var catBuf = new StringBuffer();
95493           if (this.isEndPoint()) { catBuf.append(' endpoint'); }
95494           if (this._isProper) { catBuf.append(' proper'); }
95495           if (this.isCollinear()) { catBuf.append(' collinear'); }
95496           return catBuf.toString()
95497         };
95498         LineIntersector.prototype.computeIntersection = function computeIntersection (p1, p2, p3, p4) {
95499           this._inputLines[0][0] = p1;
95500           this._inputLines[0][1] = p2;
95501           this._inputLines[1][0] = p3;
95502           this._inputLines[1][1] = p4;
95503           this._result = this.computeIntersect(p1, p2, p3, p4);
95504         };
95505         LineIntersector.prototype.getIntersectionNum = function getIntersectionNum () {
95506           return this._result
95507         };
95508         LineIntersector.prototype.computeIntLineIndex = function computeIntLineIndex () {
95509           if (arguments.length === 0) {
95510             if (this._intLineIndex === null) {
95511               this._intLineIndex = Array(2).fill().map(function () { return Array(2); });
95512               this.computeIntLineIndex(0);
95513               this.computeIntLineIndex(1);
95514             }
95515           } else if (arguments.length === 1) {
95516             var segmentIndex = arguments[0];
95517             var dist0 = this.getEdgeDistance(segmentIndex, 0);
95518             var dist1 = this.getEdgeDistance(segmentIndex, 1);
95519             if (dist0 > dist1) {
95520               this._intLineIndex[segmentIndex][0] = 0;
95521               this._intLineIndex[segmentIndex][1] = 1;
95522             } else {
95523               this._intLineIndex[segmentIndex][0] = 1;
95524               this._intLineIndex[segmentIndex][1] = 0;
95525             }
95526           }
95527         };
95528         LineIntersector.prototype.isProper = function isProper () {
95529           return this.hasIntersection() && this._isProper
95530         };
95531         LineIntersector.prototype.setPrecisionModel = function setPrecisionModel (precisionModel) {
95532           this._precisionModel = precisionModel;
95533         };
95534         LineIntersector.prototype.isInteriorIntersection = function isInteriorIntersection () {
95535             var this$1 = this;
95536
95537           if (arguments.length === 0) {
95538             if (this.isInteriorIntersection(0)) { return true }
95539             if (this.isInteriorIntersection(1)) { return true }
95540             return false
95541           } else if (arguments.length === 1) {
95542             var inputLineIndex = arguments[0];
95543             for (var i = 0; i < this._result; i++) {
95544               if (!(this$1._intPt[i].equals2D(this$1._inputLines[inputLineIndex][0]) || this$1._intPt[i].equals2D(this$1._inputLines[inputLineIndex][1]))) {
95545                 return true
95546               }
95547             }
95548             return false
95549           }
95550         };
95551         LineIntersector.prototype.getIntersection = function getIntersection (intIndex) {
95552           return this._intPt[intIndex]
95553         };
95554         LineIntersector.prototype.isEndPoint = function isEndPoint () {
95555           return this.hasIntersection() && !this._isProper
95556         };
95557         LineIntersector.prototype.hasIntersection = function hasIntersection () {
95558           return this._result !== LineIntersector.NO_INTERSECTION
95559         };
95560         LineIntersector.prototype.getEdgeDistance = function getEdgeDistance (segmentIndex, intIndex) {
95561           var dist = LineIntersector.computeEdgeDistance(this._intPt[intIndex], this._inputLines[segmentIndex][0], this._inputLines[segmentIndex][1]);
95562           return dist
95563         };
95564         LineIntersector.prototype.isCollinear = function isCollinear () {
95565           return this._result === LineIntersector.COLLINEAR_INTERSECTION
95566         };
95567         LineIntersector.prototype.toString = function toString () {
95568           return WKTWriter.toLineString(this._inputLines[0][0], this._inputLines[0][1]) + ' - ' + WKTWriter.toLineString(this._inputLines[1][0], this._inputLines[1][1]) + this.getTopologySummary()
95569         };
95570         LineIntersector.prototype.getEndpoint = function getEndpoint (segmentIndex, ptIndex) {
95571           return this._inputLines[segmentIndex][ptIndex]
95572         };
95573         LineIntersector.prototype.isIntersection = function isIntersection (pt) {
95574             var this$1 = this;
95575
95576           for (var i = 0; i < this._result; i++) {
95577             if (this$1._intPt[i].equals2D(pt)) {
95578               return true
95579             }
95580           }
95581           return false
95582         };
95583         LineIntersector.prototype.getIntersectionAlongSegment = function getIntersectionAlongSegment (segmentIndex, intIndex) {
95584           this.computeIntLineIndex();
95585           return this._intPt[this._intLineIndex[segmentIndex][intIndex]]
95586         };
95587         LineIntersector.prototype.interfaces_ = function interfaces_ () {
95588           return []
95589         };
95590         LineIntersector.prototype.getClass = function getClass () {
95591           return LineIntersector
95592         };
95593         LineIntersector.computeEdgeDistance = function computeEdgeDistance (p, p0, p1) {
95594           var dx = Math.abs(p1.x - p0.x);
95595           var dy = Math.abs(p1.y - p0.y);
95596           var dist = -1.0;
95597           if (p.equals(p0)) {
95598             dist = 0.0;
95599           } else if (p.equals(p1)) {
95600             if (dx > dy) { dist = dx; } else { dist = dy; }
95601           } else {
95602             var pdx = Math.abs(p.x - p0.x);
95603             var pdy = Math.abs(p.y - p0.y);
95604             if (dx > dy) { dist = pdx; } else { dist = pdy; }
95605             if (dist === 0.0 && !p.equals(p0)) {
95606               dist = Math.max(pdx, pdy);
95607             }
95608           }
95609           Assert.isTrue(!(dist === 0.0 && !p.equals(p0)), 'Bad distance calculation');
95610           return dist
95611         };
95612         LineIntersector.nonRobustComputeEdgeDistance = function nonRobustComputeEdgeDistance (p, p1, p2) {
95613           var dx = p.x - p1.x;
95614           var dy = p.y - p1.y;
95615           var dist = Math.sqrt(dx * dx + dy * dy);
95616           Assert.isTrue(!(dist === 0.0 && !p.equals(p1)), 'Invalid distance calculation');
95617           return dist
95618         };
95619         staticAccessors$10.DONT_INTERSECT.get = function () { return 0 };
95620         staticAccessors$10.DO_INTERSECT.get = function () { return 1 };
95621         staticAccessors$10.COLLINEAR.get = function () { return 2 };
95622         staticAccessors$10.NO_INTERSECTION.get = function () { return 0 };
95623         staticAccessors$10.POINT_INTERSECTION.get = function () { return 1 };
95624         staticAccessors$10.COLLINEAR_INTERSECTION.get = function () { return 2 };
95625
95626         Object.defineProperties( LineIntersector, staticAccessors$10 );
95627
95628         var RobustLineIntersector = (function (LineIntersector$$1) {
95629           function RobustLineIntersector () {
95630             LineIntersector$$1.apply(this, arguments);
95631           }
95632
95633           if ( LineIntersector$$1 ) { RobustLineIntersector.__proto__ = LineIntersector$$1; }
95634           RobustLineIntersector.prototype = Object.create( LineIntersector$$1 && LineIntersector$$1.prototype );
95635           RobustLineIntersector.prototype.constructor = RobustLineIntersector;
95636
95637           RobustLineIntersector.prototype.isInSegmentEnvelopes = function isInSegmentEnvelopes (intPt) {
95638             var env0 = new Envelope(this._inputLines[0][0], this._inputLines[0][1]);
95639             var env1 = new Envelope(this._inputLines[1][0], this._inputLines[1][1]);
95640             return env0.contains(intPt) && env1.contains(intPt)
95641           };
95642           RobustLineIntersector.prototype.computeIntersection = function computeIntersection () {
95643             if (arguments.length === 3) {
95644               var p = arguments[0];
95645               var p1 = arguments[1];
95646               var p2 = arguments[2];
95647               this._isProper = false;
95648               if (Envelope.intersects(p1, p2, p)) {
95649                 if (CGAlgorithms.orientationIndex(p1, p2, p) === 0 && CGAlgorithms.orientationIndex(p2, p1, p) === 0) {
95650                   this._isProper = true;
95651                   if (p.equals(p1) || p.equals(p2)) {
95652                     this._isProper = false;
95653                   }
95654                   this._result = LineIntersector$$1.POINT_INTERSECTION;
95655                   return null
95656                 }
95657               }
95658               this._result = LineIntersector$$1.NO_INTERSECTION;
95659             } else { return LineIntersector$$1.prototype.computeIntersection.apply(this, arguments) }
95660           };
95661           RobustLineIntersector.prototype.normalizeToMinimum = function normalizeToMinimum (n1, n2, n3, n4, normPt) {
95662             normPt.x = this.smallestInAbsValue(n1.x, n2.x, n3.x, n4.x);
95663             normPt.y = this.smallestInAbsValue(n1.y, n2.y, n3.y, n4.y);
95664             n1.x -= normPt.x;
95665             n1.y -= normPt.y;
95666             n2.x -= normPt.x;
95667             n2.y -= normPt.y;
95668             n3.x -= normPt.x;
95669             n3.y -= normPt.y;
95670             n4.x -= normPt.x;
95671             n4.y -= normPt.y;
95672           };
95673           RobustLineIntersector.prototype.safeHCoordinateIntersection = function safeHCoordinateIntersection (p1, p2, q1, q2) {
95674             var intPt = null;
95675             try {
95676               intPt = HCoordinate.intersection(p1, p2, q1, q2);
95677             } catch (e) {
95678               if (e instanceof NotRepresentableException) {
95679                 intPt = RobustLineIntersector.nearestEndpoint(p1, p2, q1, q2);
95680               } else { throw e }
95681             } finally {}
95682             return intPt
95683           };
95684           RobustLineIntersector.prototype.intersection = function intersection (p1, p2, q1, q2) {
95685             var intPt = this.intersectionWithNormalization(p1, p2, q1, q2);
95686             if (!this.isInSegmentEnvelopes(intPt)) {
95687               intPt = new Coordinate(RobustLineIntersector.nearestEndpoint(p1, p2, q1, q2));
95688             }
95689             if (this._precisionModel !== null) {
95690               this._precisionModel.makePrecise(intPt);
95691             }
95692             return intPt
95693           };
95694           RobustLineIntersector.prototype.smallestInAbsValue = function smallestInAbsValue (x1, x2, x3, x4) {
95695             var x = x1;
95696             var xabs = Math.abs(x);
95697             if (Math.abs(x2) < xabs) {
95698               x = x2;
95699               xabs = Math.abs(x2);
95700             }
95701             if (Math.abs(x3) < xabs) {
95702               x = x3;
95703               xabs = Math.abs(x3);
95704             }
95705             if (Math.abs(x4) < xabs) {
95706               x = x4;
95707             }
95708             return x
95709           };
95710           RobustLineIntersector.prototype.checkDD = function checkDD (p1, p2, q1, q2, intPt) {
95711             var intPtDD = CGAlgorithmsDD.intersection(p1, p2, q1, q2);
95712             var isIn = this.isInSegmentEnvelopes(intPtDD);
95713             System.out.println('DD in env = ' + isIn + '  --------------------- ' + intPtDD);
95714             if (intPt.distance(intPtDD) > 0.0001) {
95715               System.out.println('Distance = ' + intPt.distance(intPtDD));
95716             }
95717           };
95718           RobustLineIntersector.prototype.intersectionWithNormalization = function intersectionWithNormalization (p1, p2, q1, q2) {
95719             var n1 = new Coordinate(p1);
95720             var n2 = new Coordinate(p2);
95721             var n3 = new Coordinate(q1);
95722             var n4 = new Coordinate(q2);
95723             var normPt = new Coordinate();
95724             this.normalizeToEnvCentre(n1, n2, n3, n4, normPt);
95725             var intPt = this.safeHCoordinateIntersection(n1, n2, n3, n4);
95726             intPt.x += normPt.x;
95727             intPt.y += normPt.y;
95728             return intPt
95729           };
95730           RobustLineIntersector.prototype.computeCollinearIntersection = function computeCollinearIntersection (p1, p2, q1, q2) {
95731             var p1q1p2 = Envelope.intersects(p1, p2, q1);
95732             var p1q2p2 = Envelope.intersects(p1, p2, q2);
95733             var q1p1q2 = Envelope.intersects(q1, q2, p1);
95734             var q1p2q2 = Envelope.intersects(q1, q2, p2);
95735             if (p1q1p2 && p1q2p2) {
95736               this._intPt[0] = q1;
95737               this._intPt[1] = q2;
95738               return LineIntersector$$1.COLLINEAR_INTERSECTION
95739             }
95740             if (q1p1q2 && q1p2q2) {
95741               this._intPt[0] = p1;
95742               this._intPt[1] = p2;
95743               return LineIntersector$$1.COLLINEAR_INTERSECTION
95744             }
95745             if (p1q1p2 && q1p1q2) {
95746               this._intPt[0] = q1;
95747               this._intPt[1] = p1;
95748               return q1.equals(p1) && !p1q2p2 && !q1p2q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95749             }
95750             if (p1q1p2 && q1p2q2) {
95751               this._intPt[0] = q1;
95752               this._intPt[1] = p2;
95753               return q1.equals(p2) && !p1q2p2 && !q1p1q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95754             }
95755             if (p1q2p2 && q1p1q2) {
95756               this._intPt[0] = q2;
95757               this._intPt[1] = p1;
95758               return q2.equals(p1) && !p1q1p2 && !q1p2q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95759             }
95760             if (p1q2p2 && q1p2q2) {
95761               this._intPt[0] = q2;
95762               this._intPt[1] = p2;
95763               return q2.equals(p2) && !p1q1p2 && !q1p1q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95764             }
95765             return LineIntersector$$1.NO_INTERSECTION
95766           };
95767           RobustLineIntersector.prototype.normalizeToEnvCentre = function normalizeToEnvCentre (n00, n01, n10, n11, normPt) {
95768             var minX0 = n00.x < n01.x ? n00.x : n01.x;
95769             var minY0 = n00.y < n01.y ? n00.y : n01.y;
95770             var maxX0 = n00.x > n01.x ? n00.x : n01.x;
95771             var maxY0 = n00.y > n01.y ? n00.y : n01.y;
95772             var minX1 = n10.x < n11.x ? n10.x : n11.x;
95773             var minY1 = n10.y < n11.y ? n10.y : n11.y;
95774             var maxX1 = n10.x > n11.x ? n10.x : n11.x;
95775             var maxY1 = n10.y > n11.y ? n10.y : n11.y;
95776             var intMinX = minX0 > minX1 ? minX0 : minX1;
95777             var intMaxX = maxX0 < maxX1 ? maxX0 : maxX1;
95778             var intMinY = minY0 > minY1 ? minY0 : minY1;
95779             var intMaxY = maxY0 < maxY1 ? maxY0 : maxY1;
95780             var intMidX = (intMinX + intMaxX) / 2.0;
95781             var intMidY = (intMinY + intMaxY) / 2.0;
95782             normPt.x = intMidX;
95783             normPt.y = intMidY;
95784             n00.x -= normPt.x;
95785             n00.y -= normPt.y;
95786             n01.x -= normPt.x;
95787             n01.y -= normPt.y;
95788             n10.x -= normPt.x;
95789             n10.y -= normPt.y;
95790             n11.x -= normPt.x;
95791             n11.y -= normPt.y;
95792           };
95793           RobustLineIntersector.prototype.computeIntersect = function computeIntersect (p1, p2, q1, q2) {
95794             this._isProper = false;
95795             if (!Envelope.intersects(p1, p2, q1, q2)) { return LineIntersector$$1.NO_INTERSECTION }
95796             var Pq1 = CGAlgorithms.orientationIndex(p1, p2, q1);
95797             var Pq2 = CGAlgorithms.orientationIndex(p1, p2, q2);
95798             if ((Pq1 > 0 && Pq2 > 0) || (Pq1 < 0 && Pq2 < 0)) {
95799               return LineIntersector$$1.NO_INTERSECTION
95800             }
95801             var Qp1 = CGAlgorithms.orientationIndex(q1, q2, p1);
95802             var Qp2 = CGAlgorithms.orientationIndex(q1, q2, p2);
95803             if ((Qp1 > 0 && Qp2 > 0) || (Qp1 < 0 && Qp2 < 0)) {
95804               return LineIntersector$$1.NO_INTERSECTION
95805             }
95806             var collinear = Pq1 === 0 && Pq2 === 0 && Qp1 === 0 && Qp2 === 0;
95807             if (collinear) {
95808               return this.computeCollinearIntersection(p1, p2, q1, q2)
95809             }
95810             if (Pq1 === 0 || Pq2 === 0 || Qp1 === 0 || Qp2 === 0) {
95811               this._isProper = false;
95812               if (p1.equals2D(q1) || p1.equals2D(q2)) {
95813                 this._intPt[0] = p1;
95814               } else if (p2.equals2D(q1) || p2.equals2D(q2)) {
95815                 this._intPt[0] = p2;
95816               } else if (Pq1 === 0) {
95817                 this._intPt[0] = new Coordinate(q1);
95818               } else if (Pq2 === 0) {
95819                 this._intPt[0] = new Coordinate(q2);
95820               } else if (Qp1 === 0) {
95821                 this._intPt[0] = new Coordinate(p1);
95822               } else if (Qp2 === 0) {
95823                 this._intPt[0] = new Coordinate(p2);
95824               }
95825             } else {
95826               this._isProper = true;
95827               this._intPt[0] = this.intersection(p1, p2, q1, q2);
95828             }
95829             return LineIntersector$$1.POINT_INTERSECTION
95830           };
95831           RobustLineIntersector.prototype.interfaces_ = function interfaces_ () {
95832             return []
95833           };
95834           RobustLineIntersector.prototype.getClass = function getClass () {
95835             return RobustLineIntersector
95836           };
95837           RobustLineIntersector.nearestEndpoint = function nearestEndpoint (p1, p2, q1, q2) {
95838             var nearestPt = p1;
95839             var minDist = CGAlgorithms.distancePointLine(p1, q1, q2);
95840             var dist = CGAlgorithms.distancePointLine(p2, q1, q2);
95841             if (dist < minDist) {
95842               minDist = dist;
95843               nearestPt = p2;
95844             }
95845             dist = CGAlgorithms.distancePointLine(q1, p1, p2);
95846             if (dist < minDist) {
95847               minDist = dist;
95848               nearestPt = q1;
95849             }
95850             dist = CGAlgorithms.distancePointLine(q2, p1, p2);
95851             if (dist < minDist) {
95852               minDist = dist;
95853               nearestPt = q2;
95854             }
95855             return nearestPt
95856           };
95857
95858           return RobustLineIntersector;
95859         }(LineIntersector));
95860
95861         var RobustDeterminant = function RobustDeterminant () {};
95862
95863         RobustDeterminant.prototype.interfaces_ = function interfaces_ () {
95864           return []
95865         };
95866         RobustDeterminant.prototype.getClass = function getClass () {
95867           return RobustDeterminant
95868         };
95869         RobustDeterminant.orientationIndex = function orientationIndex (p1, p2, q) {
95870           var dx1 = p2.x - p1.x;
95871           var dy1 = p2.y - p1.y;
95872           var dx2 = q.x - p2.x;
95873           var dy2 = q.y - p2.y;
95874           return RobustDeterminant.signOfDet2x2(dx1, dy1, dx2, dy2)
95875         };
95876         RobustDeterminant.signOfDet2x2 = function signOfDet2x2 (x1, y1, x2, y2) {
95877           var sign = null;
95878           var swap = null;
95879           var k = null;
95880           sign = 1;
95881           if (x1 === 0.0 || y2 === 0.0) {
95882             if (y1 === 0.0 || x2 === 0.0) {
95883               return 0
95884             } else if (y1 > 0) {
95885               if (x2 > 0) {
95886                 return -sign
95887               } else {
95888                 return sign
95889               }
95890             } else {
95891               if (x2 > 0) {
95892                 return sign
95893               } else {
95894                 return -sign
95895               }
95896             }
95897           }
95898           if (y1 === 0.0 || x2 === 0.0) {
95899             if (y2 > 0) {
95900               if (x1 > 0) {
95901                 return sign
95902               } else {
95903                 return -sign
95904               }
95905             } else {
95906               if (x1 > 0) {
95907                 return -sign
95908               } else {
95909                 return sign
95910               }
95911             }
95912           }
95913           if (y1 > 0.0) {
95914             if (y2 > 0.0) {
95915               if (y1 <= y2) ; else {
95916                 sign = -sign;
95917                 swap = x1;
95918                 x1 = x2;
95919                 x2 = swap;
95920                 swap = y1;
95921                 y1 = y2;
95922                 y2 = swap;
95923               }
95924             } else {
95925               if (y1 <= -y2) {
95926                 sign = -sign;
95927                 x2 = -x2;
95928                 y2 = -y2;
95929               } else {
95930                 swap = x1;
95931                 x1 = -x2;
95932                 x2 = swap;
95933                 swap = y1;
95934                 y1 = -y2;
95935                 y2 = swap;
95936               }
95937             }
95938           } else {
95939             if (y2 > 0.0) {
95940               if (-y1 <= y2) {
95941                 sign = -sign;
95942                 x1 = -x1;
95943                 y1 = -y1;
95944               } else {
95945                 swap = -x1;
95946                 x1 = x2;
95947                 x2 = swap;
95948                 swap = -y1;
95949                 y1 = y2;
95950                 y2 = swap;
95951               }
95952             } else {
95953               if (y1 >= y2) {
95954                 x1 = -x1;
95955                 y1 = -y1;
95956                 x2 = -x2;
95957                 y2 = -y2;
95958               } else {
95959                 sign = -sign;
95960                 swap = -x1;
95961                 x1 = -x2;
95962                 x2 = swap;
95963                 swap = -y1;
95964                 y1 = -y2;
95965                 y2 = swap;
95966               }
95967             }
95968           }
95969           if (x1 > 0.0) {
95970             if (x2 > 0.0) {
95971               if (x1 <= x2) ; else {
95972                 return sign
95973               }
95974             } else {
95975               return sign
95976             }
95977           } else {
95978             if (x2 > 0.0) {
95979               return -sign
95980             } else {
95981               if (x1 >= x2) {
95982                 sign = -sign;
95983                 x1 = -x1;
95984                 x2 = -x2;
95985               } else {
95986                 return -sign
95987               }
95988             }
95989           }
95990           while (true) {
95991             k = Math.floor(x2 / x1);
95992             x2 = x2 - k * x1;
95993             y2 = y2 - k * y1;
95994             if (y2 < 0.0) {
95995               return -sign
95996             }
95997             if (y2 > y1) {
95998               return sign
95999             }
96000             if (x1 > x2 + x2) {
96001               if (y1 < y2 + y2) {
96002                 return sign
96003               }
96004             } else {
96005               if (y1 > y2 + y2) {
96006                 return -sign
96007               } else {
96008                 x2 = x1 - x2;
96009                 y2 = y1 - y2;
96010                 sign = -sign;
96011               }
96012             }
96013             if (y2 === 0.0) {
96014               if (x2 === 0.0) {
96015                 return 0
96016               } else {
96017                 return -sign
96018               }
96019             }
96020             if (x2 === 0.0) {
96021               return sign
96022             }
96023             k = Math.floor(x1 / x2);
96024             x1 = x1 - k * x2;
96025             y1 = y1 - k * y2;
96026             if (y1 < 0.0) {
96027               return sign
96028             }
96029             if (y1 > y2) {
96030               return -sign
96031             }
96032             if (x2 > x1 + x1) {
96033               if (y2 < y1 + y1) {
96034                 return -sign
96035               }
96036             } else {
96037               if (y2 > y1 + y1) {
96038                 return sign
96039               } else {
96040                 x1 = x2 - x1;
96041                 y1 = y2 - y1;
96042                 sign = -sign;
96043               }
96044             }
96045             if (y1 === 0.0) {
96046               if (x1 === 0.0) {
96047                 return 0
96048               } else {
96049                 return sign
96050               }
96051             }
96052             if (x1 === 0.0) {
96053               return -sign
96054             }
96055           }
96056         };
96057
96058         var RayCrossingCounter = function RayCrossingCounter () {
96059           this._p = null;
96060           this._crossingCount = 0;
96061           this._isPointOnSegment = false;
96062           var p = arguments[0];
96063           this._p = p;
96064         };
96065         RayCrossingCounter.prototype.countSegment = function countSegment (p1, p2) {
96066           if (p1.x < this._p.x && p2.x < this._p.x) { return null }
96067           if (this._p.x === p2.x && this._p.y === p2.y) {
96068             this._isPointOnSegment = true;
96069             return null
96070           }
96071           if (p1.y === this._p.y && p2.y === this._p.y) {
96072             var minx = p1.x;
96073             var maxx = p2.x;
96074             if (minx > maxx) {
96075               minx = p2.x;
96076               maxx = p1.x;
96077             }
96078             if (this._p.x >= minx && this._p.x <= maxx) {
96079               this._isPointOnSegment = true;
96080             }
96081             return null
96082           }
96083           if ((p1.y > this._p.y && p2.y <= this._p.y) || (p2.y > this._p.y && p1.y <= this._p.y)) {
96084             var x1 = p1.x - this._p.x;
96085             var y1 = p1.y - this._p.y;
96086             var x2 = p2.x - this._p.x;
96087             var y2 = p2.y - this._p.y;
96088             var xIntSign = RobustDeterminant.signOfDet2x2(x1, y1, x2, y2);
96089             if (xIntSign === 0.0) {
96090               this._isPointOnSegment = true;
96091               return null
96092             }
96093             if (y2 < y1) { xIntSign = -xIntSign; }
96094             if (xIntSign > 0.0) {
96095               this._crossingCount++;
96096             }
96097           }
96098         };
96099         RayCrossingCounter.prototype.isPointInPolygon = function isPointInPolygon () {
96100           return this.getLocation() !== Location.EXTERIOR
96101         };
96102         RayCrossingCounter.prototype.getLocation = function getLocation () {
96103           if (this._isPointOnSegment) { return Location.BOUNDARY }
96104           if (this._crossingCount % 2 === 1) {
96105             return Location.INTERIOR
96106           }
96107           return Location.EXTERIOR
96108         };
96109         RayCrossingCounter.prototype.isOnSegment = function isOnSegment () {
96110           return this._isPointOnSegment
96111         };
96112         RayCrossingCounter.prototype.interfaces_ = function interfaces_ () {
96113           return []
96114         };
96115         RayCrossingCounter.prototype.getClass = function getClass () {
96116           return RayCrossingCounter
96117         };
96118         RayCrossingCounter.locatePointInRing = function locatePointInRing () {
96119           if (arguments[0] instanceof Coordinate && hasInterface(arguments[1], CoordinateSequence)) {
96120             var p = arguments[0];
96121             var ring = arguments[1];
96122             var counter = new RayCrossingCounter(p);
96123             var p1 = new Coordinate();
96124             var p2 = new Coordinate();
96125             for (var i = 1; i < ring.size(); i++) {
96126               ring.getCoordinate(i, p1);
96127               ring.getCoordinate(i - 1, p2);
96128               counter.countSegment(p1, p2);
96129               if (counter.isOnSegment()) { return counter.getLocation() }
96130             }
96131             return counter.getLocation()
96132           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Array) {
96133             var p$1 = arguments[0];
96134             var ring$1 = arguments[1];
96135             var counter$1 = new RayCrossingCounter(p$1);
96136             for (var i$1 = 1; i$1 < ring$1.length; i$1++) {
96137               var p1$1 = ring$1[i$1];
96138               var p2$1 = ring$1[i$1 - 1];
96139               counter$1.countSegment(p1$1, p2$1);
96140               if (counter$1.isOnSegment()) { return counter$1.getLocation() }
96141             }
96142             return counter$1.getLocation()
96143           }
96144         };
96145
96146         var CGAlgorithms = function CGAlgorithms () {};
96147
96148         var staticAccessors$3 = { CLOCKWISE: { configurable: true },RIGHT: { configurable: true },COUNTERCLOCKWISE: { configurable: true },LEFT: { configurable: true },COLLINEAR: { configurable: true },STRAIGHT: { configurable: true } };
96149
96150         CGAlgorithms.prototype.interfaces_ = function interfaces_ () {
96151           return []
96152         };
96153         CGAlgorithms.prototype.getClass = function getClass () {
96154           return CGAlgorithms
96155         };
96156         CGAlgorithms.orientationIndex = function orientationIndex (p1, p2, q) {
96157           return CGAlgorithmsDD.orientationIndex(p1, p2, q)
96158         };
96159         CGAlgorithms.signedArea = function signedArea () {
96160           if (arguments[0] instanceof Array) {
96161             var ring = arguments[0];
96162             if (ring.length < 3) { return 0.0 }
96163             var sum = 0.0;
96164             var x0 = ring[0].x;
96165             for (var i = 1; i < ring.length - 1; i++) {
96166               var x = ring[i].x - x0;
96167               var y1 = ring[i + 1].y;
96168               var y2 = ring[i - 1].y;
96169               sum += x * (y2 - y1);
96170             }
96171             return sum / 2.0
96172           } else if (hasInterface(arguments[0], CoordinateSequence)) {
96173             var ring$1 = arguments[0];
96174             var n = ring$1.size();
96175             if (n < 3) { return 0.0 }
96176             var p0 = new Coordinate();
96177             var p1 = new Coordinate();
96178             var p2 = new Coordinate();
96179             ring$1.getCoordinate(0, p1);
96180             ring$1.getCoordinate(1, p2);
96181             var x0$1 = p1.x;
96182             p2.x -= x0$1;
96183             var sum$1 = 0.0;
96184             for (var i$1 = 1; i$1 < n - 1; i$1++) {
96185               p0.y = p1.y;
96186               p1.x = p2.x;
96187               p1.y = p2.y;
96188               ring$1.getCoordinate(i$1 + 1, p2);
96189               p2.x -= x0$1;
96190               sum$1 += p1.x * (p0.y - p2.y);
96191             }
96192             return sum$1 / 2.0
96193           }
96194         };
96195         CGAlgorithms.distanceLineLine = function distanceLineLine (A, B, C, D) {
96196           if (A.equals(B)) { return CGAlgorithms.distancePointLine(A, C, D) }
96197           if (C.equals(D)) { return CGAlgorithms.distancePointLine(D, A, B) }
96198           var noIntersection = false;
96199           if (!Envelope.intersects(A, B, C, D)) {
96200             noIntersection = true;
96201           } else {
96202             var denom = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x);
96203             if (denom === 0) {
96204               noIntersection = true;
96205             } else {
96206               var rNumb = (A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y);
96207               var sNum = (A.y - C.y) * (B.x - A.x) - (A.x - C.x) * (B.y - A.y);
96208               var s = sNum / denom;
96209               var r = rNumb / denom;
96210               if (r < 0 || r > 1 || s < 0 || s > 1) {
96211                 noIntersection = true;
96212               }
96213             }
96214           }
96215           if (noIntersection) {
96216             return MathUtil.min(CGAlgorithms.distancePointLine(A, C, D), CGAlgorithms.distancePointLine(B, C, D), CGAlgorithms.distancePointLine(C, A, B), CGAlgorithms.distancePointLine(D, A, B))
96217           }
96218           return 0.0
96219         };
96220         CGAlgorithms.isPointInRing = function isPointInRing (p, ring) {
96221           return CGAlgorithms.locatePointInRing(p, ring) !== Location.EXTERIOR
96222         };
96223         CGAlgorithms.computeLength = function computeLength (pts) {
96224           var n = pts.size();
96225           if (n <= 1) { return 0.0 }
96226           var len = 0.0;
96227           var p = new Coordinate();
96228           pts.getCoordinate(0, p);
96229           var x0 = p.x;
96230           var y0 = p.y;
96231           for (var i = 1; i < n; i++) {
96232             pts.getCoordinate(i, p);
96233             var x1 = p.x;
96234             var y1 = p.y;
96235             var dx = x1 - x0;
96236             var dy = y1 - y0;
96237             len += Math.sqrt(dx * dx + dy * dy);
96238             x0 = x1;
96239             y0 = y1;
96240           }
96241           return len
96242         };
96243         CGAlgorithms.isCCW = function isCCW (ring) {
96244           var nPts = ring.length - 1;
96245           if (nPts < 3) { throw new IllegalArgumentException('Ring has fewer than 4 points, so orientation cannot be determined') }
96246           var hiPt = ring[0];
96247           var hiIndex = 0;
96248           for (var i = 1; i <= nPts; i++) {
96249             var p = ring[i];
96250             if (p.y > hiPt.y) {
96251               hiPt = p;
96252               hiIndex = i;
96253             }
96254           }
96255           var iPrev = hiIndex;
96256           do {
96257             iPrev = iPrev - 1;
96258             if (iPrev < 0) { iPrev = nPts; }
96259           } while (ring[iPrev].equals2D(hiPt) && iPrev !== hiIndex)
96260           var iNext = hiIndex;
96261           do {
96262             iNext = (iNext + 1) % nPts;
96263           } while (ring[iNext].equals2D(hiPt) && iNext !== hiIndex)
96264           var prev = ring[iPrev];
96265           var next = ring[iNext];
96266           if (prev.equals2D(hiPt) || next.equals2D(hiPt) || prev.equals2D(next)) { return false }
96267           var disc = CGAlgorithms.computeOrientation(prev, hiPt, next);
96268           var isCCW = false;
96269           if (disc === 0) {
96270             isCCW = prev.x > next.x;
96271           } else {
96272             isCCW = disc > 0;
96273           }
96274           return isCCW
96275         };
96276         CGAlgorithms.locatePointInRing = function locatePointInRing (p, ring) {
96277           return RayCrossingCounter.locatePointInRing(p, ring)
96278         };
96279         CGAlgorithms.distancePointLinePerpendicular = function distancePointLinePerpendicular (p, A, B) {
96280           var len2 = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y);
96281           var s = ((A.y - p.y) * (B.x - A.x) - (A.x - p.x) * (B.y - A.y)) / len2;
96282           return Math.abs(s) * Math.sqrt(len2)
96283         };
96284         CGAlgorithms.computeOrientation = function computeOrientation (p1, p2, q) {
96285           return CGAlgorithms.orientationIndex(p1, p2, q)
96286         };
96287         CGAlgorithms.distancePointLine = function distancePointLine () {
96288           if (arguments.length === 2) {
96289             var p = arguments[0];
96290             var line = arguments[1];
96291             if (line.length === 0) { throw new IllegalArgumentException('Line array must contain at least one vertex') }
96292             var minDistance = p.distance(line[0]);
96293             for (var i = 0; i < line.length - 1; i++) {
96294               var dist = CGAlgorithms.distancePointLine(p, line[i], line[i + 1]);
96295               if (dist < minDistance) {
96296                 minDistance = dist;
96297               }
96298             }
96299             return minDistance
96300           } else if (arguments.length === 3) {
96301             var p$1 = arguments[0];
96302             var A = arguments[1];
96303             var B = arguments[2];
96304             if (A.x === B.x && A.y === B.y) { return p$1.distance(A) }
96305             var len2 = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y);
96306             var r = ((p$1.x - A.x) * (B.x - A.x) + (p$1.y - A.y) * (B.y - A.y)) / len2;
96307             if (r <= 0.0) { return p$1.distance(A) }
96308             if (r >= 1.0) { return p$1.distance(B) }
96309             var s = ((A.y - p$1.y) * (B.x - A.x) - (A.x - p$1.x) * (B.y - A.y)) / len2;
96310             return Math.abs(s) * Math.sqrt(len2)
96311           }
96312         };
96313         CGAlgorithms.isOnLine = function isOnLine (p, pt) {
96314           var lineIntersector = new RobustLineIntersector();
96315           for (var i = 1; i < pt.length; i++) {
96316             var p0 = pt[i - 1];
96317             var p1 = pt[i];
96318             lineIntersector.computeIntersection(p, p0, p1);
96319             if (lineIntersector.hasIntersection()) {
96320               return true
96321             }
96322           }
96323           return false
96324         };
96325         staticAccessors$3.CLOCKWISE.get = function () { return -1 };
96326         staticAccessors$3.RIGHT.get = function () { return CGAlgorithms.CLOCKWISE };
96327         staticAccessors$3.COUNTERCLOCKWISE.get = function () { return 1 };
96328         staticAccessors$3.LEFT.get = function () { return CGAlgorithms.COUNTERCLOCKWISE };
96329         staticAccessors$3.COLLINEAR.get = function () { return 0 };
96330         staticAccessors$3.STRAIGHT.get = function () { return CGAlgorithms.COLLINEAR };
96331
96332         Object.defineProperties( CGAlgorithms, staticAccessors$3 );
96333
96334         var GeometryComponentFilter = function GeometryComponentFilter () {};
96335
96336         GeometryComponentFilter.prototype.filter = function filter (geom) {};
96337         GeometryComponentFilter.prototype.interfaces_ = function interfaces_ () {
96338           return []
96339         };
96340         GeometryComponentFilter.prototype.getClass = function getClass () {
96341           return GeometryComponentFilter
96342         };
96343
96344         var Geometry = function Geometry () {
96345           var factory = arguments[0];
96346
96347           this._envelope = null;
96348           this._factory = null;
96349           this._SRID = null;
96350           this._userData = null;
96351           this._factory = factory;
96352           this._SRID = factory.getSRID();
96353         };
96354
96355         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 } };
96356         Geometry.prototype.isGeometryCollection = function isGeometryCollection () {
96357           return this.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION
96358         };
96359         Geometry.prototype.getFactory = function getFactory () {
96360           return this._factory
96361         };
96362         Geometry.prototype.getGeometryN = function getGeometryN (n) {
96363           return this
96364         };
96365         Geometry.prototype.getArea = function getArea () {
96366           return 0.0
96367         };
96368         Geometry.prototype.isRectangle = function isRectangle () {
96369           return false
96370         };
96371         Geometry.prototype.equals = function equals () {
96372           if (arguments[0] instanceof Geometry) {
96373             var g$1 = arguments[0];
96374             if (g$1 === null) { return false }
96375             return this.equalsTopo(g$1)
96376           } else if (arguments[0] instanceof Object) {
96377             var o = arguments[0];
96378             if (!(o instanceof Geometry)) { return false }
96379             var g = o;
96380             return this.equalsExact(g)
96381           }
96382         };
96383         Geometry.prototype.equalsExact = function equalsExact (other) {
96384           return this === other || this.equalsExact(other, 0)
96385         };
96386         Geometry.prototype.geometryChanged = function geometryChanged () {
96387           this.apply(Geometry.geometryChangedFilter);
96388         };
96389         Geometry.prototype.geometryChangedAction = function geometryChangedAction () {
96390           this._envelope = null;
96391         };
96392         Geometry.prototype.equalsNorm = function equalsNorm (g) {
96393           if (g === null) { return false }
96394           return this.norm().equalsExact(g.norm())
96395         };
96396         Geometry.prototype.getLength = function getLength () {
96397           return 0.0
96398         };
96399         Geometry.prototype.getNumGeometries = function getNumGeometries () {
96400           return 1
96401         };
96402         Geometry.prototype.compareTo = function compareTo () {
96403           if (arguments.length === 1) {
96404             var o = arguments[0];
96405             var other = o;
96406             if (this.getSortIndex() !== other.getSortIndex()) {
96407               return this.getSortIndex() - other.getSortIndex()
96408             }
96409             if (this.isEmpty() && other.isEmpty()) {
96410               return 0
96411             }
96412             if (this.isEmpty()) {
96413               return -1
96414             }
96415             if (other.isEmpty()) {
96416               return 1
96417             }
96418             return this.compareToSameClass(o)
96419           } else if (arguments.length === 2) {
96420             var other$1 = arguments[0];
96421             var comp = arguments[1];
96422             if (this.getSortIndex() !== other$1.getSortIndex()) {
96423               return this.getSortIndex() - other$1.getSortIndex()
96424             }
96425             if (this.isEmpty() && other$1.isEmpty()) {
96426               return 0
96427             }
96428             if (this.isEmpty()) {
96429               return -1
96430             }
96431             if (other$1.isEmpty()) {
96432               return 1
96433             }
96434             return this.compareToSameClass(other$1, comp)
96435           }
96436         };
96437         Geometry.prototype.getUserData = function getUserData () {
96438           return this._userData
96439         };
96440         Geometry.prototype.getSRID = function getSRID () {
96441           return this._SRID
96442         };
96443         Geometry.prototype.getEnvelope = function getEnvelope () {
96444           return this.getFactory().toGeometry(this.getEnvelopeInternal())
96445         };
96446         Geometry.prototype.checkNotGeometryCollection = function checkNotGeometryCollection (g) {
96447           if (g.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION) {
96448             throw new IllegalArgumentException('This method does not support GeometryCollection arguments')
96449           }
96450         };
96451         Geometry.prototype.equal = function equal (a, b, tolerance) {
96452           if (tolerance === 0) {
96453             return a.equals(b)
96454           }
96455           return a.distance(b) <= tolerance
96456         };
96457         Geometry.prototype.norm = function norm () {
96458           var copy = this.copy();
96459           copy.normalize();
96460           return copy
96461         };
96462         Geometry.prototype.getPrecisionModel = function getPrecisionModel () {
96463           return this._factory.getPrecisionModel()
96464         };
96465         Geometry.prototype.getEnvelopeInternal = function getEnvelopeInternal () {
96466           if (this._envelope === null) {
96467             this._envelope = this.computeEnvelopeInternal();
96468           }
96469           return new Envelope(this._envelope)
96470         };
96471         Geometry.prototype.setSRID = function setSRID (SRID) {
96472           this._SRID = SRID;
96473         };
96474         Geometry.prototype.setUserData = function setUserData (userData) {
96475           this._userData = userData;
96476         };
96477         Geometry.prototype.compare = function compare (a, b) {
96478           var i = a.iterator();
96479           var j = b.iterator();
96480           while (i.hasNext() && j.hasNext()) {
96481             var aElement = i.next();
96482             var bElement = j.next();
96483             var comparison = aElement.compareTo(bElement);
96484             if (comparison !== 0) {
96485               return comparison
96486             }
96487           }
96488           if (i.hasNext()) {
96489             return 1
96490           }
96491           if (j.hasNext()) {
96492             return -1
96493           }
96494           return 0
96495         };
96496         Geometry.prototype.hashCode = function hashCode () {
96497           return this.getEnvelopeInternal().hashCode()
96498         };
96499         Geometry.prototype.isGeometryCollectionOrDerived = function isGeometryCollectionOrDerived () {
96500           if (this.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION || this.getSortIndex() === Geometry.SORTINDEX_MULTIPOINT || this.getSortIndex() === Geometry.SORTINDEX_MULTILINESTRING || this.getSortIndex() === Geometry.SORTINDEX_MULTIPOLYGON) {
96501             return true
96502           }
96503           return false
96504         };
96505         Geometry.prototype.interfaces_ = function interfaces_ () {
96506           return [Clonable, Comparable, Serializable]
96507         };
96508         Geometry.prototype.getClass = function getClass () {
96509           return Geometry
96510         };
96511         Geometry.hasNonEmptyElements = function hasNonEmptyElements (geometries) {
96512           for (var i = 0; i < geometries.length; i++) {
96513             if (!geometries[i].isEmpty()) {
96514               return true
96515             }
96516           }
96517           return false
96518         };
96519         Geometry.hasNullElements = function hasNullElements (array) {
96520           for (var i = 0; i < array.length; i++) {
96521             if (array[i] === null) {
96522               return true
96523             }
96524           }
96525           return false
96526         };
96527         staticAccessors$11.serialVersionUID.get = function () { return 8763622679187376702 };
96528         staticAccessors$11.SORTINDEX_POINT.get = function () { return 0 };
96529         staticAccessors$11.SORTINDEX_MULTIPOINT.get = function () { return 1 };
96530         staticAccessors$11.SORTINDEX_LINESTRING.get = function () { return 2 };
96531         staticAccessors$11.SORTINDEX_LINEARRING.get = function () { return 3 };
96532         staticAccessors$11.SORTINDEX_MULTILINESTRING.get = function () { return 4 };
96533         staticAccessors$11.SORTINDEX_POLYGON.get = function () { return 5 };
96534         staticAccessors$11.SORTINDEX_MULTIPOLYGON.get = function () { return 6 };
96535         staticAccessors$11.SORTINDEX_GEOMETRYCOLLECTION.get = function () { return 7 };
96536         staticAccessors$11.geometryChangedFilter.get = function () { return geometryChangedFilter };
96537
96538         Object.defineProperties( Geometry, staticAccessors$11 );
96539
96540         var geometryChangedFilter = function geometryChangedFilter () {};
96541
96542         geometryChangedFilter.interfaces_ = function interfaces_ () {
96543           return [GeometryComponentFilter]
96544         };
96545         geometryChangedFilter.filter = function filter (geom) {
96546           geom.geometryChangedAction();
96547         };
96548
96549         var CoordinateFilter = function CoordinateFilter () {};
96550
96551         CoordinateFilter.prototype.filter = function filter (coord) {};
96552         CoordinateFilter.prototype.interfaces_ = function interfaces_ () {
96553           return []
96554         };
96555         CoordinateFilter.prototype.getClass = function getClass () {
96556           return CoordinateFilter
96557         };
96558
96559         var BoundaryNodeRule = function BoundaryNodeRule () {};
96560
96561         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 } };
96562
96563         BoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {};
96564         BoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96565           return []
96566         };
96567         BoundaryNodeRule.prototype.getClass = function getClass () {
96568           return BoundaryNodeRule
96569         };
96570         staticAccessors$12.Mod2BoundaryNodeRule.get = function () { return Mod2BoundaryNodeRule };
96571         staticAccessors$12.EndPointBoundaryNodeRule.get = function () { return EndPointBoundaryNodeRule };
96572         staticAccessors$12.MultiValentEndPointBoundaryNodeRule.get = function () { return MultiValentEndPointBoundaryNodeRule };
96573         staticAccessors$12.MonoValentEndPointBoundaryNodeRule.get = function () { return MonoValentEndPointBoundaryNodeRule };
96574         staticAccessors$12.MOD2_BOUNDARY_RULE.get = function () { return new Mod2BoundaryNodeRule() };
96575         staticAccessors$12.ENDPOINT_BOUNDARY_RULE.get = function () { return new EndPointBoundaryNodeRule() };
96576         staticAccessors$12.MULTIVALENT_ENDPOINT_BOUNDARY_RULE.get = function () { return new MultiValentEndPointBoundaryNodeRule() };
96577         staticAccessors$12.MONOVALENT_ENDPOINT_BOUNDARY_RULE.get = function () { return new MonoValentEndPointBoundaryNodeRule() };
96578         staticAccessors$12.OGC_SFS_BOUNDARY_RULE.get = function () { return BoundaryNodeRule.MOD2_BOUNDARY_RULE };
96579
96580         Object.defineProperties( BoundaryNodeRule, staticAccessors$12 );
96581
96582         var Mod2BoundaryNodeRule = function Mod2BoundaryNodeRule () {};
96583
96584         Mod2BoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96585           return boundaryCount % 2 === 1
96586         };
96587         Mod2BoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96588           return [BoundaryNodeRule]
96589         };
96590         Mod2BoundaryNodeRule.prototype.getClass = function getClass () {
96591           return Mod2BoundaryNodeRule
96592         };
96593
96594         var EndPointBoundaryNodeRule = function EndPointBoundaryNodeRule () {};
96595
96596         EndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96597           return boundaryCount > 0
96598         };
96599         EndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96600           return [BoundaryNodeRule]
96601         };
96602         EndPointBoundaryNodeRule.prototype.getClass = function getClass () {
96603           return EndPointBoundaryNodeRule
96604         };
96605
96606         var MultiValentEndPointBoundaryNodeRule = function MultiValentEndPointBoundaryNodeRule () {};
96607
96608         MultiValentEndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96609           return boundaryCount > 1
96610         };
96611         MultiValentEndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96612           return [BoundaryNodeRule]
96613         };
96614         MultiValentEndPointBoundaryNodeRule.prototype.getClass = function getClass () {
96615           return MultiValentEndPointBoundaryNodeRule
96616         };
96617
96618         var MonoValentEndPointBoundaryNodeRule = function MonoValentEndPointBoundaryNodeRule () {};
96619
96620         MonoValentEndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96621           return boundaryCount === 1
96622         };
96623         MonoValentEndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96624           return [BoundaryNodeRule]
96625         };
96626         MonoValentEndPointBoundaryNodeRule.prototype.getClass = function getClass () {
96627           return MonoValentEndPointBoundaryNodeRule
96628         };
96629
96630         // import Iterator from './Iterator'
96631
96632         /**
96633          * @see http://download.oracle.com/javase/6/docs/api/java/util/Collection.html
96634          *
96635          * @constructor
96636          * @private
96637          */
96638         var Collection = function Collection () {};
96639
96640         Collection.prototype.add = function add () {};
96641
96642         /**
96643          * Appends all of the elements in the specified collection to the end of this
96644          * list, in the order that they are returned by the specified collection's
96645          * iterator (optional operation).
96646          * @param {javascript.util.Collection} c
96647          * @return {boolean}
96648          */
96649         Collection.prototype.addAll = function addAll () {};
96650
96651         /**
96652          * Returns true if this collection contains no elements.
96653          * @return {boolean}
96654          */
96655         Collection.prototype.isEmpty = function isEmpty () {};
96656
96657         /**
96658          * Returns an iterator over the elements in this collection.
96659          * @return {javascript.util.Iterator}
96660          */
96661         Collection.prototype.iterator = function iterator () {};
96662
96663         /**
96664          * Returns an iterator over the elements in this collection.
96665          * @return {number}
96666          */
96667         Collection.prototype.size = function size () {};
96668
96669         /**
96670          * Returns an array containing all of the elements in this collection.
96671          * @return {Array}
96672          */
96673         Collection.prototype.toArray = function toArray () {};
96674
96675         /**
96676          * Removes a single instance of the specified element from this collection if it
96677          * is present. (optional)
96678          * @param {Object} e
96679          * @return {boolean}
96680          */
96681         Collection.prototype.remove = function remove () {};
96682
96683         /**
96684          * @param {string=} message Optional message
96685          * @extends {Error}
96686          * @constructor
96687          * @private
96688          */
96689         function IndexOutOfBoundsException (message) {
96690           this.message = message || '';
96691         }
96692         IndexOutOfBoundsException.prototype = new Error();
96693
96694         /**
96695          * @type {string}
96696          */
96697         IndexOutOfBoundsException.prototype.name = 'IndexOutOfBoundsException';
96698
96699         /**
96700          * @see http://download.oracle.com/javase/6/docs/api/java/util/Iterator.html
96701          * @constructor
96702          * @private
96703          */
96704         var Iterator$1 = function Iterator () {};
96705
96706         Iterator$1.prototype.hasNext = function hasNext () {};
96707
96708         /**
96709          * Returns the next element in the iteration.
96710          * @return {Object}
96711          */
96712         Iterator$1.prototype.next = function next () {};
96713
96714         /**
96715          * Removes from the underlying collection the last element returned by the
96716          * iterator (optional operation).
96717          */
96718         Iterator$1.prototype.remove = function remove () {};
96719
96720         /**
96721          * @see http://download.oracle.com/javase/6/docs/api/java/util/List.html
96722          *
96723          * @extends {javascript.util.Collection}
96724          * @constructor
96725          * @private
96726          */
96727         var List = (function (Collection$$1) {
96728           function List () {
96729             Collection$$1.apply(this, arguments);
96730           }
96731
96732           if ( Collection$$1 ) { List.__proto__ = Collection$$1; }
96733           List.prototype = Object.create( Collection$$1 && Collection$$1.prototype );
96734           List.prototype.constructor = List;
96735
96736           List.prototype.get = function get () { };
96737
96738           /**
96739            * Replaces the element at the specified position in this list with the
96740            * specified element (optional operation).
96741            * @param {number} index
96742            * @param {Object} e
96743            * @return {Object}
96744            */
96745           List.prototype.set = function set () { };
96746
96747           /**
96748            * Returns true if this collection contains no elements.
96749            * @return {boolean}
96750            */
96751           List.prototype.isEmpty = function isEmpty () { };
96752
96753           return List;
96754         }(Collection));
96755
96756         /**
96757          * @param {string=} message Optional message
96758          * @extends {Error}
96759          * @constructor
96760          * @private
96761          */
96762         function NoSuchElementException (message) {
96763           this.message = message || '';
96764         }
96765         NoSuchElementException.prototype = new Error();
96766
96767         /**
96768          * @type {string}
96769          */
96770         NoSuchElementException.prototype.name = 'NoSuchElementException';
96771
96772         // import OperationNotSupported from './OperationNotSupported'
96773
96774         /**
96775          * @see http://download.oracle.com/javase/6/docs/api/java/util/ArrayList.html
96776          *
96777          * @extends List
96778          * @private
96779          */
96780         var ArrayList = (function (List$$1) {
96781           function ArrayList () {
96782             List$$1.call(this);
96783             this.array_ = [];
96784
96785             if (arguments[0] instanceof Collection) {
96786               this.addAll(arguments[0]);
96787             }
96788           }
96789
96790           if ( List$$1 ) { ArrayList.__proto__ = List$$1; }
96791           ArrayList.prototype = Object.create( List$$1 && List$$1.prototype );
96792           ArrayList.prototype.constructor = ArrayList;
96793
96794           ArrayList.prototype.ensureCapacity = function ensureCapacity () {};
96795           ArrayList.prototype.interfaces_ = function interfaces_ () { return [List$$1, Collection] };
96796
96797           /**
96798            * @override
96799            */
96800           ArrayList.prototype.add = function add (e) {
96801             if (arguments.length === 1) {
96802               this.array_.push(e);
96803             } else {
96804               this.array_.splice(arguments[0], arguments[1]);
96805             }
96806             return true
96807           };
96808
96809           ArrayList.prototype.clear = function clear () {
96810             this.array_ = [];
96811           };
96812
96813           /**
96814            * @override
96815            */
96816           ArrayList.prototype.addAll = function addAll (c) {
96817             var this$1 = this;
96818
96819             for (var i = c.iterator(); i.hasNext();) {
96820               this$1.add(i.next());
96821             }
96822             return true
96823           };
96824
96825           /**
96826            * @override
96827            */
96828           ArrayList.prototype.set = function set (index, element) {
96829             var oldElement = this.array_[index];
96830             this.array_[index] = element;
96831             return oldElement
96832           };
96833
96834           /**
96835            * @override
96836            */
96837           ArrayList.prototype.iterator = function iterator () {
96838             return new Iterator_(this)
96839           };
96840
96841           /**
96842            * @override
96843            */
96844           ArrayList.prototype.get = function get (index) {
96845             if (index < 0 || index >= this.size()) {
96846               throw new IndexOutOfBoundsException()
96847             }
96848
96849             return this.array_[index]
96850           };
96851
96852           /**
96853            * @override
96854            */
96855           ArrayList.prototype.isEmpty = function isEmpty () {
96856             return this.array_.length === 0
96857           };
96858
96859           /**
96860            * @override
96861            */
96862           ArrayList.prototype.size = function size () {
96863             return this.array_.length
96864           };
96865
96866           /**
96867            * @override
96868            */
96869           ArrayList.prototype.toArray = function toArray () {
96870             var this$1 = this;
96871
96872             var array = [];
96873
96874             for (var i = 0, len = this.array_.length; i < len; i++) {
96875               array.push(this$1.array_[i]);
96876             }
96877
96878             return array
96879           };
96880
96881           /**
96882            * @override
96883            */
96884           ArrayList.prototype.remove = function remove (o) {
96885             var this$1 = this;
96886
96887             var found = false;
96888
96889             for (var i = 0, len = this.array_.length; i < len; i++) {
96890               if (this$1.array_[i] === o) {
96891                 this$1.array_.splice(i, 1);
96892                 found = true;
96893                 break
96894               }
96895             }
96896
96897             return found
96898           };
96899
96900           return ArrayList;
96901         }(List));
96902
96903         /**
96904          * @extends {Iterator}
96905          * @param {ArrayList} arrayList
96906          * @constructor
96907          * @private
96908          */
96909         var Iterator_ = (function (Iterator$$1) {
96910           function Iterator_ (arrayList) {
96911             Iterator$$1.call(this);
96912             /**
96913              * @type {ArrayList}
96914              * @private
96915             */
96916             this.arrayList_ = arrayList;
96917             /**
96918              * @type {number}
96919              * @private
96920             */
96921             this.position_ = 0;
96922           }
96923
96924           if ( Iterator$$1 ) { Iterator_.__proto__ = Iterator$$1; }
96925           Iterator_.prototype = Object.create( Iterator$$1 && Iterator$$1.prototype );
96926           Iterator_.prototype.constructor = Iterator_;
96927
96928           /**
96929            * @override
96930            */
96931           Iterator_.prototype.next = function next () {
96932             if (this.position_ === this.arrayList_.size()) {
96933               throw new NoSuchElementException()
96934             }
96935             return this.arrayList_.get(this.position_++)
96936           };
96937
96938           /**
96939            * @override
96940            */
96941           Iterator_.prototype.hasNext = function hasNext () {
96942             if (this.position_ < this.arrayList_.size()) {
96943               return true
96944             } else {
96945               return false
96946             }
96947           };
96948
96949           /**
96950            * TODO: should be in ListIterator
96951            * @override
96952            */
96953           Iterator_.prototype.set = function set (element) {
96954             return this.arrayList_.set(this.position_ - 1, element)
96955           };
96956
96957           /**
96958            * @override
96959            */
96960           Iterator_.prototype.remove = function remove () {
96961             this.arrayList_.remove(this.arrayList_.get(this.position_));
96962           };
96963
96964           return Iterator_;
96965         }(Iterator$1));
96966
96967         var CoordinateList = (function (ArrayList$$1) {
96968           function CoordinateList () {
96969             ArrayList$$1.call(this);
96970             if (arguments.length === 0) ; else if (arguments.length === 1) {
96971               var coord = arguments[0];
96972               this.ensureCapacity(coord.length);
96973               this.add(coord, true);
96974             } else if (arguments.length === 2) {
96975               var coord$1 = arguments[0];
96976               var allowRepeated = arguments[1];
96977               this.ensureCapacity(coord$1.length);
96978               this.add(coord$1, allowRepeated);
96979             }
96980           }
96981
96982           if ( ArrayList$$1 ) { CoordinateList.__proto__ = ArrayList$$1; }
96983           CoordinateList.prototype = Object.create( ArrayList$$1 && ArrayList$$1.prototype );
96984           CoordinateList.prototype.constructor = CoordinateList;
96985
96986           var staticAccessors = { coordArrayType: { configurable: true } };
96987           staticAccessors.coordArrayType.get = function () { return new Array(0).fill(null) };
96988           CoordinateList.prototype.getCoordinate = function getCoordinate (i) {
96989             return this.get(i)
96990           };
96991           CoordinateList.prototype.addAll = function addAll () {
96992             var this$1 = this;
96993
96994             if (arguments.length === 2) {
96995               var coll = arguments[0];
96996               var allowRepeated = arguments[1];
96997               var isChanged = false;
96998               for (var i = coll.iterator(); i.hasNext();) {
96999                 this$1.add(i.next(), allowRepeated);
97000                 isChanged = true;
97001               }
97002               return isChanged
97003             } else { return ArrayList$$1.prototype.addAll.apply(this, arguments) }
97004           };
97005           CoordinateList.prototype.clone = function clone () {
97006             var this$1 = this;
97007
97008             var clone = ArrayList$$1.prototype.clone.call(this);
97009             for (var i = 0; i < this.size(); i++) {
97010               clone.add(i, this$1.get(i).copy());
97011             }
97012             return clone
97013           };
97014           CoordinateList.prototype.toCoordinateArray = function toCoordinateArray () {
97015             return this.toArray(CoordinateList.coordArrayType)
97016           };
97017           CoordinateList.prototype.add = function add () {
97018             var this$1 = this;
97019
97020             if (arguments.length === 1) {
97021               var coord = arguments[0];
97022               ArrayList$$1.prototype.add.call(this, coord);
97023             } else if (arguments.length === 2) {
97024               if (arguments[0] instanceof Array && typeof arguments[1] === 'boolean') {
97025                 var coord$1 = arguments[0];
97026                 var allowRepeated = arguments[1];
97027                 this.add(coord$1, allowRepeated, true);
97028                 return true
97029               } else if (arguments[0] instanceof Coordinate && typeof arguments[1] === 'boolean') {
97030                 var coord$2 = arguments[0];
97031                 var allowRepeated$1 = arguments[1];
97032                 if (!allowRepeated$1) {
97033                   if (this.size() >= 1) {
97034                     var last = this.get(this.size() - 1);
97035                     if (last.equals2D(coord$2)) { return null }
97036                   }
97037                 }
97038                 ArrayList$$1.prototype.add.call(this, coord$2);
97039               } else if (arguments[0] instanceof Object && typeof arguments[1] === 'boolean') {
97040                 var obj = arguments[0];
97041                 var allowRepeated$2 = arguments[1];
97042                 this.add(obj, allowRepeated$2);
97043                 return true
97044               }
97045             } else if (arguments.length === 3) {
97046               if (typeof arguments[2] === 'boolean' && (arguments[0] instanceof Array && typeof arguments[1] === 'boolean')) {
97047                 var coord$3 = arguments[0];
97048                 var allowRepeated$3 = arguments[1];
97049                 var direction = arguments[2];
97050                 if (direction) {
97051                   for (var i$1 = 0; i$1 < coord$3.length; i$1++) {
97052                     this$1.add(coord$3[i$1], allowRepeated$3);
97053                   }
97054                 } else {
97055                   for (var i$2 = coord$3.length - 1; i$2 >= 0; i$2--) {
97056                     this$1.add(coord$3[i$2], allowRepeated$3);
97057                   }
97058                 }
97059                 return true
97060               } else if (typeof arguments[2] === 'boolean' && (Number.isInteger(arguments[0]) && arguments[1] instanceof Coordinate)) {
97061                 var i$3 = arguments[0];
97062                 var coord$4 = arguments[1];
97063                 var allowRepeated$4 = arguments[2];
97064                 if (!allowRepeated$4) {
97065                   var size = this.size();
97066                   if (size > 0) {
97067                     if (i$3 > 0) {
97068                       var prev = this.get(i$3 - 1);
97069                       if (prev.equals2D(coord$4)) { return null }
97070                     }
97071                     if (i$3 < size) {
97072                       var next = this.get(i$3);
97073                       if (next.equals2D(coord$4)) { return null }
97074                     }
97075                   }
97076                 }
97077                 ArrayList$$1.prototype.add.call(this, i$3, coord$4);
97078               }
97079             } else if (arguments.length === 4) {
97080               var coord$5 = arguments[0];
97081               var allowRepeated$5 = arguments[1];
97082               var start = arguments[2];
97083               var end = arguments[3];
97084               var inc = 1;
97085               if (start > end) { inc = -1; }
97086               for (var i = start; i !== end; i += inc) {
97087                 this$1.add(coord$5[i], allowRepeated$5);
97088               }
97089               return true
97090             }
97091           };
97092           CoordinateList.prototype.closeRing = function closeRing () {
97093             if (this.size() > 0) { this.add(new Coordinate(this.get(0)), false); }
97094           };
97095           CoordinateList.prototype.interfaces_ = function interfaces_ () {
97096             return []
97097           };
97098           CoordinateList.prototype.getClass = function getClass () {
97099             return CoordinateList
97100           };
97101
97102           Object.defineProperties( CoordinateList, staticAccessors );
97103
97104           return CoordinateList;
97105         }(ArrayList));
97106
97107         var CoordinateArrays = function CoordinateArrays () {};
97108
97109         var staticAccessors$13 = { ForwardComparator: { configurable: true },BidirectionalComparator: { configurable: true },coordArrayType: { configurable: true } };
97110
97111         staticAccessors$13.ForwardComparator.get = function () { return ForwardComparator };
97112         staticAccessors$13.BidirectionalComparator.get = function () { return BidirectionalComparator };
97113         staticAccessors$13.coordArrayType.get = function () { return new Array(0).fill(null) };
97114
97115         CoordinateArrays.prototype.interfaces_ = function interfaces_ () {
97116           return []
97117         };
97118         CoordinateArrays.prototype.getClass = function getClass () {
97119           return CoordinateArrays
97120         };
97121         CoordinateArrays.isRing = function isRing (pts) {
97122           if (pts.length < 4) { return false }
97123           if (!pts[0].equals2D(pts[pts.length - 1])) { return false }
97124           return true
97125         };
97126         CoordinateArrays.ptNotInList = function ptNotInList (testPts, pts) {
97127           for (var i = 0; i < testPts.length; i++) {
97128             var testPt = testPts[i];
97129             if (CoordinateArrays.indexOf(testPt, pts) < 0) { return testPt }
97130           }
97131           return null
97132         };
97133         CoordinateArrays.scroll = function scroll (coordinates, firstCoordinate) {
97134           var i = CoordinateArrays.indexOf(firstCoordinate, coordinates);
97135           if (i < 0) { return null }
97136           var newCoordinates = new Array(coordinates.length).fill(null);
97137           System.arraycopy(coordinates, i, newCoordinates, 0, coordinates.length - i);
97138           System.arraycopy(coordinates, 0, newCoordinates, coordinates.length - i, i);
97139           System.arraycopy(newCoordinates, 0, coordinates, 0, coordinates.length);
97140         };
97141         CoordinateArrays.equals = function equals () {
97142           if (arguments.length === 2) {
97143             var coord1 = arguments[0];
97144             var coord2 = arguments[1];
97145             if (coord1 === coord2) { return true }
97146             if (coord1 === null || coord2 === null) { return false }
97147             if (coord1.length !== coord2.length) { return false }
97148             for (var i = 0; i < coord1.length; i++) {
97149               if (!coord1[i].equals(coord2[i])) { return false }
97150             }
97151             return true
97152           } else if (arguments.length === 3) {
97153             var coord1$1 = arguments[0];
97154             var coord2$1 = arguments[1];
97155             var coordinateComparator = arguments[2];
97156             if (coord1$1 === coord2$1) { return true }
97157             if (coord1$1 === null || coord2$1 === null) { return false }
97158             if (coord1$1.length !== coord2$1.length) { return false }
97159             for (var i$1 = 0; i$1 < coord1$1.length; i$1++) {
97160               if (coordinateComparator.compare(coord1$1[i$1], coord2$1[i$1]) !== 0) { return false }
97161             }
97162             return true
97163           }
97164         };
97165         CoordinateArrays.intersection = function intersection (coordinates, env) {
97166           var coordList = new CoordinateList();
97167           for (var i = 0; i < coordinates.length; i++) {
97168             if (env.intersects(coordinates[i])) { coordList.add(coordinates[i], true); }
97169           }
97170           return coordList.toCoordinateArray()
97171         };
97172         CoordinateArrays.hasRepeatedPoints = function hasRepeatedPoints (coord) {
97173           for (var i = 1; i < coord.length; i++) {
97174             if (coord[i - 1].equals(coord[i])) {
97175               return true
97176             }
97177           }
97178           return false
97179         };
97180         CoordinateArrays.removeRepeatedPoints = function removeRepeatedPoints (coord) {
97181           if (!CoordinateArrays.hasRepeatedPoints(coord)) { return coord }
97182           var coordList = new CoordinateList(coord, false);
97183           return coordList.toCoordinateArray()
97184         };
97185         CoordinateArrays.reverse = function reverse (coord) {
97186           var last = coord.length - 1;
97187           var mid = Math.trunc(last / 2);
97188           for (var i = 0; i <= mid; i++) {
97189             var tmp = coord[i];
97190             coord[i] = coord[last - i];
97191             coord[last - i] = tmp;
97192           }
97193         };
97194         CoordinateArrays.removeNull = function removeNull (coord) {
97195           var nonNull = 0;
97196           for (var i = 0; i < coord.length; i++) {
97197             if (coord[i] !== null) { nonNull++; }
97198           }
97199           var newCoord = new Array(nonNull).fill(null);
97200           if (nonNull === 0) { return newCoord }
97201           var j = 0;
97202           for (var i$1 = 0; i$1 < coord.length; i$1++) {
97203             if (coord[i$1] !== null) { newCoord[j++] = coord[i$1]; }
97204           }
97205           return newCoord
97206         };
97207         CoordinateArrays.copyDeep = function copyDeep () {
97208           if (arguments.length === 1) {
97209             var coordinates = arguments[0];
97210             var copy = new Array(coordinates.length).fill(null);
97211             for (var i = 0; i < coordinates.length; i++) {
97212               copy[i] = new Coordinate(coordinates[i]);
97213             }
97214             return copy
97215           } else if (arguments.length === 5) {
97216             var src = arguments[0];
97217             var srcStart = arguments[1];
97218             var dest = arguments[2];
97219             var destStart = arguments[3];
97220             var length = arguments[4];
97221             for (var i$1 = 0; i$1 < length; i$1++) {
97222               dest[destStart + i$1] = new Coordinate(src[srcStart + i$1]);
97223             }
97224           }
97225         };
97226         CoordinateArrays.isEqualReversed = function isEqualReversed (pts1, pts2) {
97227           for (var i = 0; i < pts1.length; i++) {
97228             var p1 = pts1[i];
97229             var p2 = pts2[pts1.length - i - 1];
97230             if (p1.compareTo(p2) !== 0) { return false }
97231           }
97232           return true
97233         };
97234         CoordinateArrays.envelope = function envelope (coordinates) {
97235           var env = new Envelope();
97236           for (var i = 0; i < coordinates.length; i++) {
97237             env.expandToInclude(coordinates[i]);
97238           }
97239           return env
97240         };
97241         CoordinateArrays.toCoordinateArray = function toCoordinateArray (coordList) {
97242           return coordList.toArray(CoordinateArrays.coordArrayType)
97243         };
97244         CoordinateArrays.atLeastNCoordinatesOrNothing = function atLeastNCoordinatesOrNothing (n, c) {
97245           return c.length >= n ? c : []
97246         };
97247         CoordinateArrays.indexOf = function indexOf (coordinate, coordinates) {
97248           for (var i = 0; i < coordinates.length; i++) {
97249             if (coordinate.equals(coordinates[i])) {
97250               return i
97251             }
97252           }
97253           return -1
97254         };
97255         CoordinateArrays.increasingDirection = function increasingDirection (pts) {
97256           for (var i = 0; i < Math.trunc(pts.length / 2); i++) {
97257             var j = pts.length - 1 - i;
97258             var comp = pts[i].compareTo(pts[j]);
97259             if (comp !== 0) { return comp }
97260           }
97261           return 1
97262         };
97263         CoordinateArrays.compare = function compare (pts1, pts2) {
97264           var i = 0;
97265           while (i < pts1.length && i < pts2.length) {
97266             var compare = pts1[i].compareTo(pts2[i]);
97267             if (compare !== 0) { return compare }
97268             i++;
97269           }
97270           if (i < pts2.length) { return -1 }
97271           if (i < pts1.length) { return 1 }
97272           return 0
97273         };
97274         CoordinateArrays.minCoordinate = function minCoordinate (coordinates) {
97275           var minCoord = null;
97276           for (var i = 0; i < coordinates.length; i++) {
97277             if (minCoord === null || minCoord.compareTo(coordinates[i]) > 0) {
97278               minCoord = coordinates[i];
97279             }
97280           }
97281           return minCoord
97282         };
97283         CoordinateArrays.extract = function extract (pts, start, end) {
97284           start = MathUtil.clamp(start, 0, pts.length);
97285           end = MathUtil.clamp(end, -1, pts.length);
97286           var npts = end - start + 1;
97287           if (end < 0) { npts = 0; }
97288           if (start >= pts.length) { npts = 0; }
97289           if (end < start) { npts = 0; }
97290           var extractPts = new Array(npts).fill(null);
97291           if (npts === 0) { return extractPts }
97292           var iPts = 0;
97293           for (var i = start; i <= end; i++) {
97294             extractPts[iPts++] = pts[i];
97295           }
97296           return extractPts
97297         };
97298
97299         Object.defineProperties( CoordinateArrays, staticAccessors$13 );
97300
97301         var ForwardComparator = function ForwardComparator () {};
97302
97303         ForwardComparator.prototype.compare = function compare (o1, o2) {
97304           var pts1 = o1;
97305           var pts2 = o2;
97306           return CoordinateArrays.compare(pts1, pts2)
97307         };
97308         ForwardComparator.prototype.interfaces_ = function interfaces_ () {
97309           return [Comparator]
97310         };
97311         ForwardComparator.prototype.getClass = function getClass () {
97312           return ForwardComparator
97313         };
97314
97315         var BidirectionalComparator = function BidirectionalComparator () {};
97316
97317         BidirectionalComparator.prototype.compare = function compare (o1, o2) {
97318           var pts1 = o1;
97319           var pts2 = o2;
97320           if (pts1.length < pts2.length) { return -1 }
97321           if (pts1.length > pts2.length) { return 1 }
97322           if (pts1.length === 0) { return 0 }
97323           var forwardComp = CoordinateArrays.compare(pts1, pts2);
97324           var isEqualRev = CoordinateArrays.isEqualReversed(pts1, pts2);
97325           if (isEqualRev) { return 0 }
97326           return forwardComp
97327         };
97328         BidirectionalComparator.prototype.OLDcompare = function OLDcompare (o1, o2) {
97329           var pts1 = o1;
97330           var pts2 = o2;
97331           if (pts1.length < pts2.length) { return -1 }
97332           if (pts1.length > pts2.length) { return 1 }
97333           if (pts1.length === 0) { return 0 }
97334           var dir1 = CoordinateArrays.increasingDirection(pts1);
97335           var dir2 = CoordinateArrays.increasingDirection(pts2);
97336           var i1 = dir1 > 0 ? 0 : pts1.length - 1;
97337           var i2 = dir2 > 0 ? 0 : pts1.length - 1;
97338           for (var i = 0; i < pts1.length; i++) {
97339             var comparePt = pts1[i1].compareTo(pts2[i2]);
97340             if (comparePt !== 0) { return comparePt }
97341             i1 += dir1;
97342             i2 += dir2;
97343           }
97344           return 0
97345         };
97346         BidirectionalComparator.prototype.interfaces_ = function interfaces_ () {
97347           return [Comparator]
97348         };
97349         BidirectionalComparator.prototype.getClass = function getClass () {
97350           return BidirectionalComparator
97351         };
97352
97353         /**
97354          * @see http://download.oracle.com/javase/6/docs/api/java/util/Map.html
97355          *
97356          * @constructor
97357          * @private
97358          */
97359         var Map$1$1 = function Map () {};
97360
97361         Map$1$1.prototype.get = function get () {};
97362         /**
97363          * Associates the specified value with the specified key in this map (optional
97364          * operation).
97365          * @param {Object} key
97366          * @param {Object} value
97367          * @return {Object}
97368          */
97369         Map$1$1.prototype.put = function put () {};
97370
97371         /**
97372          * Returns the number of key-value mappings in this map.
97373          * @return {number}
97374          */
97375         Map$1$1.prototype.size = function size () {};
97376
97377         /**
97378          * Returns a Collection view of the values contained in this map.
97379          * @return {javascript.util.Collection}
97380          */
97381         Map$1$1.prototype.values = function values () {};
97382
97383         /**
97384          * Returns a {@link Set} view of the mappings contained in this map.
97385          * The set is backed by the map, so changes to the map are
97386          * reflected in the set, and vice-versa.If the map is modified
97387          * while an iteration over the set is in progress (except through
97388          * the iterator's own <tt>remove</tt> operation, or through the
97389          * <tt>setValue</tt> operation on a map entry returned by the
97390          * iterator) the results of the iteration are undefined.The set
97391          * supports element removal, which removes the corresponding
97392          * mapping from the map, via the <tt>Iterator.remove</tt>,
97393          * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
97394          * <tt>clear</tt> operations.It does not support the
97395          * <tt>add</tt> or <tt>addAll</tt> operations.
97396          *
97397          * @return {Set} a set view of the mappings contained in this map
97398          */
97399         Map$1$1.prototype.entrySet = function entrySet () {};
97400
97401         /**
97402          * @see http://download.oracle.com/javase/6/docs/api/java/util/SortedMap.html
97403          *
97404          * @extends {Map}
97405          * @constructor
97406          * @private
97407          */
97408         var SortedMap = (function (Map) {
97409                 function SortedMap () {
97410                         Map.apply(this, arguments);
97411                 }if ( Map ) { SortedMap.__proto__ = Map; }
97412                 SortedMap.prototype = Object.create( Map && Map.prototype );
97413                 SortedMap.prototype.constructor = SortedMap;
97414
97415                 
97416
97417                 return SortedMap;
97418         }(Map$1$1));
97419
97420         /**
97421          * @param {string=} message Optional message
97422          * @extends {Error}
97423          * @constructor
97424          * @private
97425          */
97426         function OperationNotSupported (message) {
97427           this.message = message || '';
97428         }
97429         OperationNotSupported.prototype = new Error();
97430
97431         /**
97432          * @type {string}
97433          */
97434         OperationNotSupported.prototype.name = 'OperationNotSupported';
97435
97436         /**
97437          * @see http://download.oracle.com/javase/6/docs/api/java/util/Set.html
97438          *
97439          * @extends {Collection}
97440          * @constructor
97441          * @private
97442          */
97443         function Set$2() {}
97444         Set$2.prototype = new Collection();
97445
97446
97447         /**
97448          * Returns true if this set contains the specified element. More formally,
97449          * returns true if and only if this set contains an element e such that (o==null ?
97450          * e==null : o.equals(e)).
97451          * @param {Object} e
97452          * @return {boolean}
97453          */
97454         Set$2.prototype.contains = function() {};
97455
97456         /**
97457          * @see http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html
97458          *
97459          * @extends {javascript.util.Set}
97460          * @constructor
97461          * @private
97462          */
97463         var HashSet = (function (Set$$1) {
97464           function HashSet () {
97465             Set$$1.call(this);
97466             this.array_ = [];
97467
97468             if (arguments[0] instanceof Collection) {
97469               this.addAll(arguments[0]);
97470             }
97471           }
97472
97473           if ( Set$$1 ) { HashSet.__proto__ = Set$$1; }
97474           HashSet.prototype = Object.create( Set$$1 && Set$$1.prototype );
97475           HashSet.prototype.constructor = HashSet;
97476
97477           /**
97478            * @override
97479            */
97480           HashSet.prototype.contains = function contains (o) {
97481             var this$1 = this;
97482
97483             for (var i = 0, len = this.array_.length; i < len; i++) {
97484               var e = this$1.array_[i];
97485               if (e === o) {
97486                 return true
97487               }
97488             }
97489             return false
97490           };
97491
97492           /**
97493            * @override
97494            */
97495           HashSet.prototype.add = function add (o) {
97496             if (this.contains(o)) {
97497               return false
97498             }
97499
97500             this.array_.push(o);
97501
97502             return true
97503           };
97504
97505           /**
97506            * @override
97507            */
97508           HashSet.prototype.addAll = function addAll (c) {
97509             var this$1 = this;
97510
97511             for (var i = c.iterator(); i.hasNext();) {
97512               this$1.add(i.next());
97513             }
97514             return true
97515           };
97516
97517           /**
97518            * @override
97519            */
97520           HashSet.prototype.remove = function remove (o) {
97521             // throw new javascript.util.OperationNotSupported()
97522             throw new Error()
97523           };
97524
97525           /**
97526            * @override
97527            */
97528           HashSet.prototype.size = function size () {
97529             return this.array_.length
97530           };
97531
97532           /**
97533            * @override
97534            */
97535           HashSet.prototype.isEmpty = function isEmpty () {
97536             return this.array_.length === 0
97537           };
97538
97539           /**
97540            * @override
97541            */
97542           HashSet.prototype.toArray = function toArray () {
97543             var this$1 = this;
97544
97545             var array = [];
97546
97547             for (var i = 0, len = this.array_.length; i < len; i++) {
97548               array.push(this$1.array_[i]);
97549             }
97550
97551             return array
97552           };
97553
97554           /**
97555            * @override
97556            */
97557           HashSet.prototype.iterator = function iterator () {
97558             return new Iterator_$1(this)
97559           };
97560
97561           return HashSet;
97562         }(Set$2));
97563
97564         /**
97565            * @extends {Iterator}
97566            * @param {HashSet} hashSet
97567            * @constructor
97568            * @private
97569            */
97570         var Iterator_$1 = (function (Iterator$$1) {
97571           function Iterator_ (hashSet) {
97572             Iterator$$1.call(this);
97573             /**
97574              * @type {HashSet}
97575              * @private
97576              */
97577             this.hashSet_ = hashSet;
97578             /**
97579              * @type {number}
97580              * @private
97581              */
97582             this.position_ = 0;
97583           }
97584
97585           if ( Iterator$$1 ) { Iterator_.__proto__ = Iterator$$1; }
97586           Iterator_.prototype = Object.create( Iterator$$1 && Iterator$$1.prototype );
97587           Iterator_.prototype.constructor = Iterator_;
97588
97589           /**
97590            * @override
97591            */
97592           Iterator_.prototype.next = function next () {
97593             if (this.position_ === this.hashSet_.size()) {
97594               throw new NoSuchElementException()
97595             }
97596             return this.hashSet_.array_[this.position_++]
97597           };
97598
97599           /**
97600            * @override
97601            */
97602           Iterator_.prototype.hasNext = function hasNext () {
97603             if (this.position_ < this.hashSet_.size()) {
97604               return true
97605             } else {
97606               return false
97607             }
97608           };
97609
97610           /**
97611            * @override
97612            */
97613           Iterator_.prototype.remove = function remove () {
97614             throw new OperationNotSupported()
97615           };
97616
97617           return Iterator_;
97618         }(Iterator$1));
97619
97620         var BLACK = 0;
97621         var RED = 1;
97622         function colorOf (p) { return (p === null ? BLACK : p.color) }
97623         function parentOf (p) { return (p === null ? null : p.parent) }
97624         function setColor (p, c) { if (p !== null) { p.color = c; } }
97625         function leftOf (p) { return (p === null ? null : p.left) }
97626         function rightOf (p) { return (p === null ? null : p.right) }
97627
97628         /**
97629          * @see http://download.oracle.com/javase/6/docs/api/java/util/TreeMap.html
97630          *
97631          * @extends {SortedMap}
97632          * @constructor
97633          * @private
97634          */
97635         function TreeMap () {
97636           /**
97637            * @type {Object}
97638            * @private
97639            */
97640           this.root_ = null;
97641           /**
97642            * @type {number}
97643            * @private
97644           */
97645           this.size_ = 0;
97646         }
97647         TreeMap.prototype = new SortedMap();
97648
97649         /**
97650          * @override
97651          */
97652         TreeMap.prototype.get = function (key) {
97653           var p = this.root_;
97654           while (p !== null) {
97655             var cmp = key['compareTo'](p.key);
97656             if (cmp < 0) { p = p.left; }
97657             else if (cmp > 0) { p = p.right; }
97658             else { return p.value }
97659           }
97660           return null
97661         };
97662
97663         /**
97664          * @override
97665          */
97666         TreeMap.prototype.put = function (key, value) {
97667           if (this.root_ === null) {
97668             this.root_ = {
97669               key: key,
97670               value: value,
97671               left: null,
97672               right: null,
97673               parent: null,
97674               color: BLACK,
97675               getValue: function getValue () { return this.value },
97676               getKey: function getKey () { return this.key }
97677             };
97678             this.size_ = 1;
97679             return null
97680           }
97681           var t = this.root_;
97682           var parent;
97683           var cmp;
97684           do {
97685             parent = t;
97686             cmp = key['compareTo'](t.key);
97687             if (cmp < 0) {
97688               t = t.left;
97689             } else if (cmp > 0) {
97690               t = t.right;
97691             } else {
97692               var oldValue = t.value;
97693               t.value = value;
97694               return oldValue
97695             }
97696           } while (t !== null)
97697           var e = {
97698             key: key,
97699             left: null,
97700             right: null,
97701             value: value,
97702             parent: parent,
97703             color: BLACK,
97704             getValue: function getValue () { return this.value },
97705             getKey: function getKey () { return this.key }
97706           };
97707           if (cmp < 0) {
97708             parent.left = e;
97709           } else {
97710             parent.right = e;
97711           }
97712           this.fixAfterInsertion(e);
97713           this.size_++;
97714           return null
97715         };
97716
97717         /**
97718          * @param {Object} x
97719          */
97720         TreeMap.prototype.fixAfterInsertion = function (x) {
97721           var this$1 = this;
97722
97723           x.color = RED;
97724           while (x != null && x !== this.root_ && x.parent.color === RED) {
97725             if (parentOf(x) === leftOf(parentOf(parentOf(x)))) {
97726               var y = rightOf(parentOf(parentOf(x)));
97727               if (colorOf(y) === RED) {
97728                 setColor(parentOf(x), BLACK);
97729                 setColor(y, BLACK);
97730                 setColor(parentOf(parentOf(x)), RED);
97731                 x = parentOf(parentOf(x));
97732               } else {
97733                 if (x === rightOf(parentOf(x))) {
97734                   x = parentOf(x);
97735                   this$1.rotateLeft(x);
97736                 }
97737                 setColor(parentOf(x), BLACK);
97738                 setColor(parentOf(parentOf(x)), RED);
97739                 this$1.rotateRight(parentOf(parentOf(x)));
97740               }
97741             } else {
97742               var y$1 = leftOf(parentOf(parentOf(x)));
97743               if (colorOf(y$1) === RED) {
97744                 setColor(parentOf(x), BLACK);
97745                 setColor(y$1, BLACK);
97746                 setColor(parentOf(parentOf(x)), RED);
97747                 x = parentOf(parentOf(x));
97748               } else {
97749                 if (x === leftOf(parentOf(x))) {
97750                   x = parentOf(x);
97751                   this$1.rotateRight(x);
97752                 }
97753                 setColor(parentOf(x), BLACK);
97754                 setColor(parentOf(parentOf(x)), RED);
97755                 this$1.rotateLeft(parentOf(parentOf(x)));
97756               }
97757             }
97758           }
97759           this.root_.color = BLACK;
97760         };
97761
97762         /**
97763          * @override
97764          */
97765         TreeMap.prototype.values = function () {
97766           var arrayList = new ArrayList();
97767           var p = this.getFirstEntry();
97768           if (p !== null) {
97769             arrayList.add(p.value);
97770             while ((p = TreeMap.successor(p)) !== null) {
97771               arrayList.add(p.value);
97772             }
97773           }
97774           return arrayList
97775         };
97776
97777         /**
97778          * @override
97779          */
97780         TreeMap.prototype.entrySet = function () {
97781           var hashSet = new HashSet();
97782           var p = this.getFirstEntry();
97783           if (p !== null) {
97784             hashSet.add(p);
97785             while ((p = TreeMap.successor(p)) !== null) {
97786               hashSet.add(p);
97787             }
97788           }
97789           return hashSet
97790         };
97791
97792         /**
97793          * @param {Object} p
97794          */
97795         TreeMap.prototype.rotateLeft = function (p) {
97796           if (p != null) {
97797             var r = p.right;
97798             p.right = r.left;
97799             if (r.left != null) { r.left.parent = p; }
97800             r.parent = p.parent;
97801             if (p.parent === null) { this.root_ = r; } else if (p.parent.left === p) { p.parent.left = r; } else { p.parent.right = r; }
97802             r.left = p;
97803             p.parent = r;
97804           }
97805         };
97806
97807         /**
97808          * @param {Object} p
97809          */
97810         TreeMap.prototype.rotateRight = function (p) {
97811           if (p != null) {
97812             var l = p.left;
97813             p.left = l.right;
97814             if (l.right != null) { l.right.parent = p; }
97815             l.parent = p.parent;
97816             if (p.parent === null) { this.root_ = l; } else if (p.parent.right === p) { p.parent.right = l; } else { p.parent.left = l; }
97817             l.right = p;
97818             p.parent = l;
97819           }
97820         };
97821
97822         /**
97823          * @return {Object}
97824          */
97825         TreeMap.prototype.getFirstEntry = function () {
97826           var p = this.root_;
97827           if (p != null) {
97828             while (p.left != null) {
97829               p = p.left;
97830             }
97831           }
97832           return p
97833         };
97834
97835         /**
97836          * @param {Object} t
97837          * @return {Object}
97838          * @private
97839          */
97840         TreeMap.successor = function (t) {
97841           if (t === null) { return null } else if (t.right !== null) {
97842             var p = t.right;
97843             while (p.left !== null) {
97844               p = p.left;
97845             }
97846             return p
97847           } else {
97848             var p$1 = t.parent;
97849             var ch = t;
97850             while (p$1 !== null && ch === p$1.right) {
97851               ch = p$1;
97852               p$1 = p$1.parent;
97853             }
97854             return p$1
97855           }
97856         };
97857
97858         /**
97859          * @override
97860          */
97861         TreeMap.prototype.size = function () {
97862           return this.size_
97863         };
97864
97865         var Lineal = function Lineal () {};
97866
97867         Lineal.prototype.interfaces_ = function interfaces_ () {
97868           return []
97869         };
97870         Lineal.prototype.getClass = function getClass () {
97871           return Lineal
97872         };
97873
97874         /**
97875          * @see http://download.oracle.com/javase/6/docs/api/java/util/SortedSet.html
97876          *
97877          * @extends {Set}
97878          * @constructor
97879          * @private
97880          */
97881         function SortedSet () {}
97882         SortedSet.prototype = new Set$2();
97883
97884         // import Iterator from './Iterator'
97885         /**
97886          * @see http://download.oracle.com/javase/6/docs/api/java/util/TreeSet.html
97887          *
97888          * @extends {SortedSet}
97889          * @constructor
97890          * @private
97891          */
97892         function TreeSet () {
97893           /**
97894            * @type {Array}
97895            * @private
97896           */
97897           this.array_ = [];
97898
97899           if (arguments[0] instanceof Collection) {
97900             this.addAll(arguments[0]);
97901           }
97902         }
97903         TreeSet.prototype = new SortedSet();
97904
97905         /**
97906          * @override
97907          */
97908         TreeSet.prototype.contains = function (o) {
97909           var this$1 = this;
97910
97911           for (var i = 0, len = this.array_.length; i < len; i++) {
97912             var e = this$1.array_[i];
97913             if (e['compareTo'](o) === 0) {
97914               return true
97915             }
97916           }
97917           return false
97918         };
97919
97920         /**
97921          * @override
97922          */
97923         TreeSet.prototype.add = function (o) {
97924           var this$1 = this;
97925
97926           if (this.contains(o)) {
97927             return false
97928           }
97929
97930           for (var i = 0, len = this.array_.length; i < len; i++) {
97931             var e = this$1.array_[i];
97932             if (e['compareTo'](o) === 1) {
97933               this$1.array_.splice(i, 0, o);
97934               return true
97935             }
97936           }
97937
97938           this.array_.push(o);
97939
97940           return true
97941         };
97942
97943         /**
97944          * @override
97945          */
97946         TreeSet.prototype.addAll = function (c) {
97947           var this$1 = this;
97948
97949           for (var i = c.iterator(); i.hasNext();) {
97950             this$1.add(i.next());
97951           }
97952           return true
97953         };
97954
97955         /**
97956          * @override
97957          */
97958         TreeSet.prototype.remove = function (e) {
97959           throw new OperationNotSupported()
97960         };
97961
97962         /**
97963          * @override
97964          */
97965         TreeSet.prototype.size = function () {
97966           return this.array_.length
97967         };
97968
97969         /**
97970          * @override
97971          */
97972         TreeSet.prototype.isEmpty = function () {
97973           return this.array_.length === 0
97974         };
97975
97976         /**
97977          * @override
97978          */
97979         TreeSet.prototype.toArray = function () {
97980           var this$1 = this;
97981
97982           var array = [];
97983
97984           for (var i = 0, len = this.array_.length; i < len; i++) {
97985             array.push(this$1.array_[i]);
97986           }
97987
97988           return array
97989         };
97990
97991         /**
97992          * @override
97993          */
97994         TreeSet.prototype.iterator = function () {
97995           return new Iterator_$2(this)
97996         };
97997
97998         /**
97999          * @extends {javascript.util.Iterator}
98000          * @param {javascript.util.TreeSet} treeSet
98001          * @constructor
98002          * @private
98003          */
98004         var Iterator_$2 = function (treeSet) {
98005           /**
98006            * @type {javascript.util.TreeSet}
98007            * @private
98008            */
98009           this.treeSet_ = treeSet;
98010           /**
98011            * @type {number}
98012            * @private
98013            */
98014           this.position_ = 0;
98015         };
98016
98017         /**
98018          * @override
98019          */
98020         Iterator_$2.prototype.next = function () {
98021           if (this.position_ === this.treeSet_.size()) {
98022             throw new NoSuchElementException()
98023           }
98024           return this.treeSet_.array_[this.position_++]
98025         };
98026
98027         /**
98028          * @override
98029          */
98030         Iterator_$2.prototype.hasNext = function () {
98031           if (this.position_ < this.treeSet_.size()) {
98032             return true
98033           } else {
98034             return false
98035           }
98036         };
98037
98038         /**
98039          * @override
98040          */
98041         Iterator_$2.prototype.remove = function () {
98042           throw new OperationNotSupported()
98043         };
98044
98045         /**
98046          * @see http://download.oracle.com/javase/6/docs/api/java/util/Arrays.html
98047          *
98048          * @constructor
98049          * @private
98050          */
98051         var Arrays = function Arrays () {};
98052
98053         Arrays.sort = function sort () {
98054           var a = arguments[0];
98055           var i;
98056           var t;
98057           var comparator;
98058           var compare;
98059           if (arguments.length === 1) {
98060             compare = function (a, b) {
98061               return a.compareTo(b)
98062             };
98063             a.sort(compare);
98064           } else if (arguments.length === 2) {
98065             comparator = arguments[1];
98066             compare = function (a, b) {
98067               return comparator['compare'](a, b)
98068             };
98069             a.sort(compare);
98070           } else if (arguments.length === 3) {
98071             t = a.slice(arguments[1], arguments[2]);
98072             t.sort();
98073             var 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           } else if (arguments.length === 4) {
98079             t = a.slice(arguments[1], arguments[2]);
98080             comparator = arguments[3];
98081             compare = function (a, b) {
98082               return comparator['compare'](a, b)
98083             };
98084             t.sort(compare);
98085             r = a.slice(0, arguments[1]).concat(t, a.slice(arguments[2], a.length));
98086             a.splice(0, a.length);
98087             for (i = 0; i < r.length; i++) {
98088               a.push(r[i]);
98089             }
98090           }
98091         };
98092         /**
98093          * @param {Array} array
98094          * @return {ArrayList}
98095          */
98096         Arrays.asList = function asList (array) {
98097           var arrayList = new ArrayList();
98098           for (var i = 0, len = array.length; i < len; i++) {
98099             arrayList.add(array[i]);
98100           }
98101           return arrayList
98102         };
98103
98104         var Dimension = function Dimension () {};
98105
98106         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 } };
98107
98108         staticAccessors$14.P.get = function () { return 0 };
98109         staticAccessors$14.L.get = function () { return 1 };
98110         staticAccessors$14.A.get = function () { return 2 };
98111         staticAccessors$14.FALSE.get = function () { return -1 };
98112         staticAccessors$14.TRUE.get = function () { return -2 };
98113         staticAccessors$14.DONTCARE.get = function () { return -3 };
98114         staticAccessors$14.SYM_FALSE.get = function () { return 'F' };
98115         staticAccessors$14.SYM_TRUE.get = function () { return 'T' };
98116         staticAccessors$14.SYM_DONTCARE.get = function () { return '*' };
98117         staticAccessors$14.SYM_P.get = function () { return '0' };
98118         staticAccessors$14.SYM_L.get = function () { return '1' };
98119         staticAccessors$14.SYM_A.get = function () { return '2' };
98120
98121         Dimension.prototype.interfaces_ = function interfaces_ () {
98122           return []
98123         };
98124         Dimension.prototype.getClass = function getClass () {
98125           return Dimension
98126         };
98127         Dimension.toDimensionSymbol = function toDimensionSymbol (dimensionValue) {
98128           switch (dimensionValue) {
98129             case Dimension.FALSE:
98130               return Dimension.SYM_FALSE
98131             case Dimension.TRUE:
98132               return Dimension.SYM_TRUE
98133             case Dimension.DONTCARE:
98134               return Dimension.SYM_DONTCARE
98135             case Dimension.P:
98136               return Dimension.SYM_P
98137             case Dimension.L:
98138               return Dimension.SYM_L
98139             case Dimension.A:
98140               return Dimension.SYM_A
98141           }
98142           throw new IllegalArgumentException('Unknown dimension value: ' + dimensionValue)
98143         };
98144         Dimension.toDimensionValue = function toDimensionValue (dimensionSymbol) {
98145           switch (Character.toUpperCase(dimensionSymbol)) {
98146             case Dimension.SYM_FALSE:
98147               return Dimension.FALSE
98148             case Dimension.SYM_TRUE:
98149               return Dimension.TRUE
98150             case Dimension.SYM_DONTCARE:
98151               return Dimension.DONTCARE
98152             case Dimension.SYM_P:
98153               return Dimension.P
98154             case Dimension.SYM_L:
98155               return Dimension.L
98156             case Dimension.SYM_A:
98157               return Dimension.A
98158           }
98159           throw new IllegalArgumentException('Unknown dimension symbol: ' + dimensionSymbol)
98160         };
98161
98162         Object.defineProperties( Dimension, staticAccessors$14 );
98163
98164         var GeometryFilter = function GeometryFilter () {};
98165
98166         GeometryFilter.prototype.filter = function filter (geom) {};
98167         GeometryFilter.prototype.interfaces_ = function interfaces_ () {
98168           return []
98169         };
98170         GeometryFilter.prototype.getClass = function getClass () {
98171           return GeometryFilter
98172         };
98173
98174         var CoordinateSequenceFilter = function CoordinateSequenceFilter () {};
98175
98176         CoordinateSequenceFilter.prototype.filter = function filter (seq, i) {};
98177         CoordinateSequenceFilter.prototype.isDone = function isDone () {};
98178         CoordinateSequenceFilter.prototype.isGeometryChanged = function isGeometryChanged () {};
98179         CoordinateSequenceFilter.prototype.interfaces_ = function interfaces_ () {
98180           return []
98181         };
98182         CoordinateSequenceFilter.prototype.getClass = function getClass () {
98183           return CoordinateSequenceFilter
98184         };
98185
98186         var GeometryCollection = (function (Geometry$$1) {
98187           function GeometryCollection (geometries, factory) {
98188             Geometry$$1.call(this, factory);
98189             this._geometries = geometries || [];
98190
98191             if (Geometry$$1.hasNullElements(this._geometries)) {
98192               throw new IllegalArgumentException('geometries must not contain null elements')
98193             }
98194           }
98195
98196           if ( Geometry$$1 ) { GeometryCollection.__proto__ = Geometry$$1; }
98197           GeometryCollection.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
98198           GeometryCollection.prototype.constructor = GeometryCollection;
98199
98200           var staticAccessors = { serialVersionUID: { configurable: true } };
98201           GeometryCollection.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
98202             var this$1 = this;
98203
98204             var envelope = new Envelope();
98205             for (var i = 0; i < this._geometries.length; i++) {
98206               envelope.expandToInclude(this$1._geometries[i].getEnvelopeInternal());
98207             }
98208             return envelope
98209           };
98210           GeometryCollection.prototype.getGeometryN = function getGeometryN (n) {
98211             return this._geometries[n]
98212           };
98213           GeometryCollection.prototype.getSortIndex = function getSortIndex () {
98214             return Geometry$$1.SORTINDEX_GEOMETRYCOLLECTION
98215           };
98216           GeometryCollection.prototype.getCoordinates = function getCoordinates () {
98217             var this$1 = this;
98218
98219             var coordinates = new Array(this.getNumPoints()).fill(null);
98220             var k = -1;
98221             for (var i = 0; i < this._geometries.length; i++) {
98222               var childCoordinates = this$1._geometries[i].getCoordinates();
98223               for (var j = 0; j < childCoordinates.length; j++) {
98224                 k++;
98225                 coordinates[k] = childCoordinates[j];
98226               }
98227             }
98228             return coordinates
98229           };
98230           GeometryCollection.prototype.getArea = function getArea () {
98231             var this$1 = this;
98232
98233             var area = 0.0;
98234             for (var i = 0; i < this._geometries.length; i++) {
98235               area += this$1._geometries[i].getArea();
98236             }
98237             return area
98238           };
98239           GeometryCollection.prototype.equalsExact = function equalsExact () {
98240             var this$1 = this;
98241
98242             if (arguments.length === 2) {
98243               var other = arguments[0];
98244               var tolerance = arguments[1];
98245               if (!this.isEquivalentClass(other)) {
98246                 return false
98247               }
98248               var otherCollection = other;
98249               if (this._geometries.length !== otherCollection._geometries.length) {
98250                 return false
98251               }
98252               for (var i = 0; i < this._geometries.length; i++) {
98253                 if (!this$1._geometries[i].equalsExact(otherCollection._geometries[i], tolerance)) {
98254                   return false
98255                 }
98256               }
98257               return true
98258             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
98259           };
98260           GeometryCollection.prototype.normalize = function normalize () {
98261             var this$1 = this;
98262
98263             for (var i = 0; i < this._geometries.length; i++) {
98264               this$1._geometries[i].normalize();
98265             }
98266             Arrays.sort(this._geometries);
98267           };
98268           GeometryCollection.prototype.getCoordinate = function getCoordinate () {
98269             if (this.isEmpty()) { return null }
98270             return this._geometries[0].getCoordinate()
98271           };
98272           GeometryCollection.prototype.getBoundaryDimension = function getBoundaryDimension () {
98273             var this$1 = this;
98274
98275             var dimension = Dimension.FALSE;
98276             for (var i = 0; i < this._geometries.length; i++) {
98277               dimension = Math.max(dimension, this$1._geometries[i].getBoundaryDimension());
98278             }
98279             return dimension
98280           };
98281           GeometryCollection.prototype.getDimension = function getDimension () {
98282             var this$1 = this;
98283
98284             var dimension = Dimension.FALSE;
98285             for (var i = 0; i < this._geometries.length; i++) {
98286               dimension = Math.max(dimension, this$1._geometries[i].getDimension());
98287             }
98288             return dimension
98289           };
98290           GeometryCollection.prototype.getLength = function getLength () {
98291             var this$1 = this;
98292
98293             var sum = 0.0;
98294             for (var i = 0; i < this._geometries.length; i++) {
98295               sum += this$1._geometries[i].getLength();
98296             }
98297             return sum
98298           };
98299           GeometryCollection.prototype.getNumPoints = function getNumPoints () {
98300             var this$1 = this;
98301
98302             var numPoints = 0;
98303             for (var i = 0; i < this._geometries.length; i++) {
98304               numPoints += this$1._geometries[i].getNumPoints();
98305             }
98306             return numPoints
98307           };
98308           GeometryCollection.prototype.getNumGeometries = function getNumGeometries () {
98309             return this._geometries.length
98310           };
98311           GeometryCollection.prototype.reverse = function reverse () {
98312             var this$1 = this;
98313
98314             var n = this._geometries.length;
98315             var revGeoms = new Array(n).fill(null);
98316             for (var i = 0; i < this._geometries.length; i++) {
98317               revGeoms[i] = this$1._geometries[i].reverse();
98318             }
98319             return this.getFactory().createGeometryCollection(revGeoms)
98320           };
98321           GeometryCollection.prototype.compareToSameClass = function compareToSameClass () {
98322             var this$1 = this;
98323
98324             if (arguments.length === 1) {
98325               var o = arguments[0];
98326               var theseElements = new TreeSet(Arrays.asList(this._geometries));
98327               var otherElements = new TreeSet(Arrays.asList(o._geometries));
98328               return this.compare(theseElements, otherElements)
98329             } else if (arguments.length === 2) {
98330               var o$1 = arguments[0];
98331               var comp = arguments[1];
98332               var gc = o$1;
98333               var n1 = this.getNumGeometries();
98334               var n2 = gc.getNumGeometries();
98335               var i = 0;
98336               while (i < n1 && i < n2) {
98337                 var thisGeom = this$1.getGeometryN(i);
98338                 var otherGeom = gc.getGeometryN(i);
98339                 var holeComp = thisGeom.compareToSameClass(otherGeom, comp);
98340                 if (holeComp !== 0) { return holeComp }
98341                 i++;
98342               }
98343               if (i < n1) { return 1 }
98344               if (i < n2) { return -1 }
98345               return 0
98346             }
98347           };
98348           GeometryCollection.prototype.apply = function apply () {
98349             var this$1 = this;
98350
98351             if (hasInterface(arguments[0], CoordinateFilter)) {
98352               var filter = arguments[0];
98353               for (var i = 0; i < this._geometries.length; i++) {
98354                 this$1._geometries[i].apply(filter);
98355               }
98356             } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) {
98357               var filter$1 = arguments[0];
98358               if (this._geometries.length === 0) { return null }
98359               for (var i$1 = 0; i$1 < this._geometries.length; i$1++) {
98360                 this$1._geometries[i$1].apply(filter$1);
98361                 if (filter$1.isDone()) {
98362                   break
98363                 }
98364               }
98365               if (filter$1.isGeometryChanged()) { this.geometryChanged(); }
98366             } else if (hasInterface(arguments[0], GeometryFilter)) {
98367               var filter$2 = arguments[0];
98368               filter$2.filter(this);
98369               for (var i$2 = 0; i$2 < this._geometries.length; i$2++) {
98370                 this$1._geometries[i$2].apply(filter$2);
98371               }
98372             } else if (hasInterface(arguments[0], GeometryComponentFilter)) {
98373               var filter$3 = arguments[0];
98374               filter$3.filter(this);
98375               for (var i$3 = 0; i$3 < this._geometries.length; i$3++) {
98376                 this$1._geometries[i$3].apply(filter$3);
98377               }
98378             }
98379           };
98380           GeometryCollection.prototype.getBoundary = function getBoundary () {
98381             this.checkNotGeometryCollection(this);
98382             Assert.shouldNeverReachHere();
98383             return null
98384           };
98385           GeometryCollection.prototype.clone = function clone () {
98386             var this$1 = this;
98387
98388             var gc = Geometry$$1.prototype.clone.call(this);
98389             gc._geometries = new Array(this._geometries.length).fill(null);
98390             for (var i = 0; i < this._geometries.length; i++) {
98391               gc._geometries[i] = this$1._geometries[i].clone();
98392             }
98393             return gc
98394           };
98395           GeometryCollection.prototype.getGeometryType = function getGeometryType () {
98396             return 'GeometryCollection'
98397           };
98398           GeometryCollection.prototype.copy = function copy () {
98399             var this$1 = this;
98400
98401             var geometries = new Array(this._geometries.length).fill(null);
98402             for (var i = 0; i < geometries.length; i++) {
98403               geometries[i] = this$1._geometries[i].copy();
98404             }
98405             return new GeometryCollection(geometries, this._factory)
98406           };
98407           GeometryCollection.prototype.isEmpty = function isEmpty () {
98408             var this$1 = this;
98409
98410             for (var i = 0; i < this._geometries.length; i++) {
98411               if (!this$1._geometries[i].isEmpty()) {
98412                 return false
98413               }
98414             }
98415             return true
98416           };
98417           GeometryCollection.prototype.interfaces_ = function interfaces_ () {
98418             return []
98419           };
98420           GeometryCollection.prototype.getClass = function getClass () {
98421             return GeometryCollection
98422           };
98423           staticAccessors.serialVersionUID.get = function () { return -5694727726395021467 };
98424
98425           Object.defineProperties( GeometryCollection, staticAccessors );
98426
98427           return GeometryCollection;
98428         }(Geometry));
98429
98430         var MultiLineString = (function (GeometryCollection$$1) {
98431           function MultiLineString () {
98432             GeometryCollection$$1.apply(this, arguments);
98433           }
98434
98435           if ( GeometryCollection$$1 ) { MultiLineString.__proto__ = GeometryCollection$$1; }
98436           MultiLineString.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype );
98437           MultiLineString.prototype.constructor = MultiLineString;
98438
98439           var staticAccessors = { serialVersionUID: { configurable: true } };
98440
98441           MultiLineString.prototype.getSortIndex = function getSortIndex () {
98442             return Geometry.SORTINDEX_MULTILINESTRING
98443           };
98444           MultiLineString.prototype.equalsExact = function equalsExact () {
98445             if (arguments.length === 2) {
98446               var other = arguments[0];
98447               var tolerance = arguments[1];
98448               if (!this.isEquivalentClass(other)) {
98449                 return false
98450               }
98451               return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance)
98452             } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) }
98453           };
98454           MultiLineString.prototype.getBoundaryDimension = function getBoundaryDimension () {
98455             if (this.isClosed()) {
98456               return Dimension.FALSE
98457             }
98458             return 0
98459           };
98460           MultiLineString.prototype.isClosed = function isClosed () {
98461             var this$1 = this;
98462
98463             if (this.isEmpty()) {
98464               return false
98465             }
98466             for (var i = 0; i < this._geometries.length; i++) {
98467               if (!this$1._geometries[i].isClosed()) {
98468                 return false
98469               }
98470             }
98471             return true
98472           };
98473           MultiLineString.prototype.getDimension = function getDimension () {
98474             return 1
98475           };
98476           MultiLineString.prototype.reverse = function reverse () {
98477             var this$1 = this;
98478
98479             var nLines = this._geometries.length;
98480             var revLines = new Array(nLines).fill(null);
98481             for (var i = 0; i < this._geometries.length; i++) {
98482               revLines[nLines - 1 - i] = this$1._geometries[i].reverse();
98483             }
98484             return this.getFactory().createMultiLineString(revLines)
98485           };
98486           MultiLineString.prototype.getBoundary = function getBoundary () {
98487             return new BoundaryOp(this).getBoundary()
98488           };
98489           MultiLineString.prototype.getGeometryType = function getGeometryType () {
98490             return 'MultiLineString'
98491           };
98492           MultiLineString.prototype.copy = function copy () {
98493             var this$1 = this;
98494
98495             var lineStrings = new Array(this._geometries.length).fill(null);
98496             for (var i = 0; i < lineStrings.length; i++) {
98497               lineStrings[i] = this$1._geometries[i].copy();
98498             }
98499             return new MultiLineString(lineStrings, this._factory)
98500           };
98501           MultiLineString.prototype.interfaces_ = function interfaces_ () {
98502             return [Lineal]
98503           };
98504           MultiLineString.prototype.getClass = function getClass () {
98505             return MultiLineString
98506           };
98507           staticAccessors.serialVersionUID.get = function () { return 8166665132445433741 };
98508
98509           Object.defineProperties( MultiLineString, staticAccessors );
98510
98511           return MultiLineString;
98512         }(GeometryCollection));
98513
98514         var BoundaryOp = function BoundaryOp () {
98515           this._geom = null;
98516           this._geomFact = null;
98517           this._bnRule = null;
98518           this._endpointMap = null;
98519           if (arguments.length === 1) {
98520             var geom = arguments[0];
98521             var bnRule = BoundaryNodeRule.MOD2_BOUNDARY_RULE;
98522             this._geom = geom;
98523             this._geomFact = geom.getFactory();
98524             this._bnRule = bnRule;
98525           } else if (arguments.length === 2) {
98526             var geom$1 = arguments[0];
98527             var bnRule$1 = arguments[1];
98528             this._geom = geom$1;
98529             this._geomFact = geom$1.getFactory();
98530             this._bnRule = bnRule$1;
98531           }
98532         };
98533         BoundaryOp.prototype.boundaryMultiLineString = function boundaryMultiLineString (mLine) {
98534           if (this._geom.isEmpty()) {
98535             return this.getEmptyMultiPoint()
98536           }
98537           var bdyPts = this.computeBoundaryCoordinates(mLine);
98538           if (bdyPts.length === 1) {
98539             return this._geomFact.createPoint(bdyPts[0])
98540           }
98541           return this._geomFact.createMultiPointFromCoords(bdyPts)
98542         };
98543         BoundaryOp.prototype.getBoundary = function getBoundary () {
98544           if (this._geom instanceof LineString) { return this.boundaryLineString(this._geom) }
98545           if (this._geom instanceof MultiLineString) { return this.boundaryMultiLineString(this._geom) }
98546           return this._geom.getBoundary()
98547         };
98548         BoundaryOp.prototype.boundaryLineString = function boundaryLineString (line) {
98549           if (this._geom.isEmpty()) {
98550             return this.getEmptyMultiPoint()
98551           }
98552           if (line.isClosed()) {
98553             var closedEndpointOnBoundary = this._bnRule.isInBoundary(2);
98554             if (closedEndpointOnBoundary) {
98555               return line.getStartPoint()
98556             } else {
98557               return this._geomFact.createMultiPoint()
98558             }
98559           }
98560           return this._geomFact.createMultiPoint([line.getStartPoint(), line.getEndPoint()])
98561         };
98562         BoundaryOp.prototype.getEmptyMultiPoint = function getEmptyMultiPoint () {
98563           return this._geomFact.createMultiPoint()
98564         };
98565         BoundaryOp.prototype.computeBoundaryCoordinates = function computeBoundaryCoordinates (mLine) {
98566             var this$1 = this;
98567
98568           var bdyPts = new ArrayList();
98569           this._endpointMap = new TreeMap();
98570           for (var i = 0; i < mLine.getNumGeometries(); i++) {
98571             var line = mLine.getGeometryN(i);
98572             if (line.getNumPoints() === 0) { continue }
98573             this$1.addEndpoint(line.getCoordinateN(0));
98574             this$1.addEndpoint(line.getCoordinateN(line.getNumPoints() - 1));
98575           }
98576           for (var it = this._endpointMap.entrySet().iterator(); it.hasNext();) {
98577             var entry = it.next();
98578             var counter = entry.getValue();
98579             var valence = counter.count;
98580             if (this$1._bnRule.isInBoundary(valence)) {
98581               bdyPts.add(entry.getKey());
98582             }
98583           }
98584           return CoordinateArrays.toCoordinateArray(bdyPts)
98585         };
98586         BoundaryOp.prototype.addEndpoint = function addEndpoint (pt) {
98587           var counter = this._endpointMap.get(pt);
98588           if (counter === null) {
98589             counter = new Counter();
98590             this._endpointMap.put(pt, counter);
98591           }
98592           counter.count++;
98593         };
98594         BoundaryOp.prototype.interfaces_ = function interfaces_ () {
98595           return []
98596         };
98597         BoundaryOp.prototype.getClass = function getClass () {
98598           return BoundaryOp
98599         };
98600         BoundaryOp.getBoundary = function getBoundary () {
98601           if (arguments.length === 1) {
98602             var g = arguments[0];
98603             var bop = new BoundaryOp(g);
98604             return bop.getBoundary()
98605           } else if (arguments.length === 2) {
98606             var g$1 = arguments[0];
98607             var bnRule = arguments[1];
98608             var bop$1 = new BoundaryOp(g$1, bnRule);
98609             return bop$1.getBoundary()
98610           }
98611         };
98612
98613         var Counter = function Counter () {
98614           this.count = null;
98615         };
98616         Counter.prototype.interfaces_ = function interfaces_ () {
98617           return []
98618         };
98619         Counter.prototype.getClass = function getClass () {
98620           return Counter
98621         };
98622
98623         // boundary
98624
98625         function PrintStream () {}
98626
98627         function StringReader () {}
98628
98629         var DecimalFormat = function DecimalFormat () {};
98630
98631         function ByteArrayOutputStream () {}
98632
98633         function IOException () {}
98634
98635         function LineNumberReader () {}
98636
98637         var StringUtil = function StringUtil () {};
98638
98639         var staticAccessors$15 = { NEWLINE: { configurable: true },SIMPLE_ORDINATE_FORMAT: { configurable: true } };
98640
98641         StringUtil.prototype.interfaces_ = function interfaces_ () {
98642           return []
98643         };
98644         StringUtil.prototype.getClass = function getClass () {
98645           return StringUtil
98646         };
98647         StringUtil.chars = function chars (c, n) {
98648           var ch = new Array(n).fill(null);
98649           for (var i = 0; i < n; i++) {
98650             ch[i] = c;
98651           }
98652           return String(ch)
98653         };
98654         StringUtil.getStackTrace = function getStackTrace () {
98655           if (arguments.length === 1) {
98656             var t = arguments[0];
98657             var os = new ByteArrayOutputStream();
98658             var ps = new PrintStream(os);
98659             t.printStackTrace(ps);
98660             return os.toString()
98661           } else if (arguments.length === 2) {
98662             var t$1 = arguments[0];
98663             var depth = arguments[1];
98664             var stackTrace = '';
98665             var stringReader = new StringReader(StringUtil.getStackTrace(t$1));
98666             var lineNumberReader = new LineNumberReader(stringReader);
98667             for (var i = 0; i < depth; i++) {
98668               try {
98669                 stackTrace += lineNumberReader.readLine() + StringUtil.NEWLINE;
98670               } catch (e) {
98671                 if (e instanceof IOException) {
98672                   Assert.shouldNeverReachHere();
98673                 } else { throw e }
98674               } finally {}
98675             }
98676             return stackTrace
98677           }
98678         };
98679         StringUtil.split = function split (s, separator) {
98680           var separatorlen = separator.length;
98681           var tokenList = new ArrayList();
98682           var tmpString = '' + s;
98683           var pos = tmpString.indexOf(separator);
98684           while (pos >= 0) {
98685             var token = tmpString.substring(0, pos);
98686             tokenList.add(token);
98687             tmpString = tmpString.substring(pos + separatorlen);
98688             pos = tmpString.indexOf(separator);
98689           }
98690           if (tmpString.length > 0) { tokenList.add(tmpString); }
98691           var res = new Array(tokenList.size()).fill(null);
98692           for (var i = 0; i < res.length; i++) {
98693             res[i] = tokenList.get(i);
98694           }
98695           return res
98696         };
98697         StringUtil.toString = function toString () {
98698           if (arguments.length === 1) {
98699             var d = arguments[0];
98700             return StringUtil.SIMPLE_ORDINATE_FORMAT.format(d)
98701           }
98702         };
98703         StringUtil.spaces = function spaces (n) {
98704           return StringUtil.chars(' ', n)
98705         };
98706         staticAccessors$15.NEWLINE.get = function () { return System.getProperty('line.separator') };
98707         staticAccessors$15.SIMPLE_ORDINATE_FORMAT.get = function () { return new DecimalFormat('0.#') };
98708
98709         Object.defineProperties( StringUtil, staticAccessors$15 );
98710
98711         var CoordinateSequences = function CoordinateSequences () {};
98712
98713         CoordinateSequences.prototype.interfaces_ = function interfaces_ () {
98714           return []
98715         };
98716         CoordinateSequences.prototype.getClass = function getClass () {
98717           return CoordinateSequences
98718         };
98719         CoordinateSequences.copyCoord = function copyCoord (src, srcPos, dest, destPos) {
98720           var minDim = Math.min(src.getDimension(), dest.getDimension());
98721           for (var dim = 0; dim < minDim; dim++) {
98722             dest.setOrdinate(destPos, dim, src.getOrdinate(srcPos, dim));
98723           }
98724         };
98725         CoordinateSequences.isRing = function isRing (seq) {
98726           var n = seq.size();
98727           if (n === 0) { return true }
98728           if (n <= 3) { return false }
98729           return seq.getOrdinate(0, CoordinateSequence.X) === seq.getOrdinate(n - 1, CoordinateSequence.X) && seq.getOrdinate(0, CoordinateSequence.Y) === seq.getOrdinate(n - 1, CoordinateSequence.Y)
98730         };
98731         CoordinateSequences.isEqual = function isEqual (cs1, cs2) {
98732           var cs1Size = cs1.size();
98733           var cs2Size = cs2.size();
98734           if (cs1Size !== cs2Size) { return false }
98735           var dim = Math.min(cs1.getDimension(), cs2.getDimension());
98736           for (var i = 0; i < cs1Size; i++) {
98737             for (var d = 0; d < dim; d++) {
98738               var v1 = cs1.getOrdinate(i, d);
98739               var v2 = cs2.getOrdinate(i, d);
98740               if (cs1.getOrdinate(i, d) === cs2.getOrdinate(i, d)) { continue }
98741               if (Double.isNaN(v1) && Double.isNaN(v2)) { continue }
98742               return false
98743             }
98744           }
98745           return true
98746         };
98747         CoordinateSequences.extend = function extend (fact, seq, size) {
98748           var newseq = fact.create(size, seq.getDimension());
98749           var n = seq.size();
98750           CoordinateSequences.copy(seq, 0, newseq, 0, n);
98751           if (n > 0) {
98752             for (var i = n; i < size; i++) { CoordinateSequences.copy(seq, n - 1, newseq, i, 1); }
98753           }
98754           return newseq
98755         };
98756         CoordinateSequences.reverse = function reverse (seq) {
98757           var last = seq.size() - 1;
98758           var mid = Math.trunc(last / 2);
98759           for (var i = 0; i <= mid; i++) {
98760             CoordinateSequences.swap(seq, i, last - i);
98761           }
98762         };
98763         CoordinateSequences.swap = function swap (seq, i, j) {
98764           if (i === j) { return null }
98765           for (var dim = 0; dim < seq.getDimension(); dim++) {
98766             var tmp = seq.getOrdinate(i, dim);
98767             seq.setOrdinate(i, dim, seq.getOrdinate(j, dim));
98768             seq.setOrdinate(j, dim, tmp);
98769           }
98770         };
98771         CoordinateSequences.copy = function copy (src, srcPos, dest, destPos, length) {
98772           for (var i = 0; i < length; i++) {
98773             CoordinateSequences.copyCoord(src, srcPos + i, dest, destPos + i);
98774           }
98775         };
98776         CoordinateSequences.toString = function toString () {
98777           if (arguments.length === 1) {
98778             var cs = arguments[0];
98779             var size = cs.size();
98780             if (size === 0) { return '()' }
98781             var dim = cs.getDimension();
98782             var buf = new StringBuffer();
98783             buf.append('(');
98784             for (var i = 0; i < size; i++) {
98785               if (i > 0) { buf.append(' '); }
98786               for (var d = 0; d < dim; d++) {
98787                 if (d > 0) { buf.append(','); }
98788                 buf.append(StringUtil.toString(cs.getOrdinate(i, d)));
98789               }
98790             }
98791             buf.append(')');
98792             return buf.toString()
98793           }
98794         };
98795         CoordinateSequences.ensureValidRing = function ensureValidRing (fact, seq) {
98796           var n = seq.size();
98797           if (n === 0) { return seq }
98798           if (n <= 3) { return CoordinateSequences.createClosedRing(fact, seq, 4) }
98799           var isClosed = seq.getOrdinate(0, CoordinateSequence.X) === seq.getOrdinate(n - 1, CoordinateSequence.X) && seq.getOrdinate(0, CoordinateSequence.Y) === seq.getOrdinate(n - 1, CoordinateSequence.Y);
98800           if (isClosed) { return seq }
98801           return CoordinateSequences.createClosedRing(fact, seq, n + 1)
98802         };
98803         CoordinateSequences.createClosedRing = function createClosedRing (fact, seq, size) {
98804           var newseq = fact.create(size, seq.getDimension());
98805           var n = seq.size();
98806           CoordinateSequences.copy(seq, 0, newseq, 0, n);
98807           for (var i = n; i < size; i++) { CoordinateSequences.copy(seq, 0, newseq, i, 1); }
98808           return newseq
98809         };
98810
98811         var LineString = (function (Geometry$$1) {
98812           function LineString (points, factory) {
98813             Geometry$$1.call(this, factory);
98814             this._points = null;
98815             this.init(points);
98816           }
98817
98818           if ( Geometry$$1 ) { LineString.__proto__ = Geometry$$1; }
98819           LineString.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
98820           LineString.prototype.constructor = LineString;
98821
98822           var staticAccessors = { serialVersionUID: { configurable: true } };
98823           LineString.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
98824             if (this.isEmpty()) {
98825               return new Envelope()
98826             }
98827             return this._points.expandEnvelope(new Envelope())
98828           };
98829           LineString.prototype.isRing = function isRing () {
98830             return this.isClosed() && this.isSimple()
98831           };
98832           LineString.prototype.getSortIndex = function getSortIndex () {
98833             return Geometry$$1.SORTINDEX_LINESTRING
98834           };
98835           LineString.prototype.getCoordinates = function getCoordinates () {
98836             return this._points.toCoordinateArray()
98837           };
98838           LineString.prototype.equalsExact = function equalsExact () {
98839             var this$1 = this;
98840
98841             if (arguments.length === 2) {
98842               var other = arguments[0];
98843               var tolerance = arguments[1];
98844               if (!this.isEquivalentClass(other)) {
98845                 return false
98846               }
98847               var otherLineString = other;
98848               if (this._points.size() !== otherLineString._points.size()) {
98849                 return false
98850               }
98851               for (var i = 0; i < this._points.size(); i++) {
98852                 if (!this$1.equal(this$1._points.getCoordinate(i), otherLineString._points.getCoordinate(i), tolerance)) {
98853                   return false
98854                 }
98855               }
98856               return true
98857             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
98858           };
98859           LineString.prototype.normalize = function normalize () {
98860             var this$1 = this;
98861
98862             for (var i = 0; i < Math.trunc(this._points.size() / 2); i++) {
98863               var j = this$1._points.size() - 1 - i;
98864               if (!this$1._points.getCoordinate(i).equals(this$1._points.getCoordinate(j))) {
98865                 if (this$1._points.getCoordinate(i).compareTo(this$1._points.getCoordinate(j)) > 0) {
98866                   CoordinateSequences.reverse(this$1._points);
98867                 }
98868                 return null
98869               }
98870             }
98871           };
98872           LineString.prototype.getCoordinate = function getCoordinate () {
98873             if (this.isEmpty()) { return null }
98874             return this._points.getCoordinate(0)
98875           };
98876           LineString.prototype.getBoundaryDimension = function getBoundaryDimension () {
98877             if (this.isClosed()) {
98878               return Dimension.FALSE
98879             }
98880             return 0
98881           };
98882           LineString.prototype.isClosed = function isClosed () {
98883             if (this.isEmpty()) {
98884               return false
98885             }
98886             return this.getCoordinateN(0).equals2D(this.getCoordinateN(this.getNumPoints() - 1))
98887           };
98888           LineString.prototype.getEndPoint = function getEndPoint () {
98889             if (this.isEmpty()) {
98890               return null
98891             }
98892             return this.getPointN(this.getNumPoints() - 1)
98893           };
98894           LineString.prototype.getDimension = function getDimension () {
98895             return 1
98896           };
98897           LineString.prototype.getLength = function getLength () {
98898             return CGAlgorithms.computeLength(this._points)
98899           };
98900           LineString.prototype.getNumPoints = function getNumPoints () {
98901             return this._points.size()
98902           };
98903           LineString.prototype.reverse = function reverse () {
98904             var seq = this._points.copy();
98905             CoordinateSequences.reverse(seq);
98906             var revLine = this.getFactory().createLineString(seq);
98907             return revLine
98908           };
98909           LineString.prototype.compareToSameClass = function compareToSameClass () {
98910             var this$1 = this;
98911
98912             if (arguments.length === 1) {
98913               var o = arguments[0];
98914               var line = o;
98915               var i = 0;
98916               var j = 0;
98917               while (i < this._points.size() && j < line._points.size()) {
98918                 var comparison = this$1._points.getCoordinate(i).compareTo(line._points.getCoordinate(j));
98919                 if (comparison !== 0) {
98920                   return comparison
98921                 }
98922                 i++;
98923                 j++;
98924               }
98925               if (i < this._points.size()) {
98926                 return 1
98927               }
98928               if (j < line._points.size()) {
98929                 return -1
98930               }
98931               return 0
98932             } else if (arguments.length === 2) {
98933               var o$1 = arguments[0];
98934               var comp = arguments[1];
98935               var line$1 = o$1;
98936               return comp.compare(this._points, line$1._points)
98937             }
98938           };
98939           LineString.prototype.apply = function apply () {
98940             var this$1 = this;
98941
98942             if (hasInterface(arguments[0], CoordinateFilter)) {
98943               var filter = arguments[0];
98944               for (var i = 0; i < this._points.size(); i++) {
98945                 filter.filter(this$1._points.getCoordinate(i));
98946               }
98947             } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) {
98948               var filter$1 = arguments[0];
98949               if (this._points.size() === 0) { return null }
98950               for (var i$1 = 0; i$1 < this._points.size(); i$1++) {
98951                 filter$1.filter(this$1._points, i$1);
98952                 if (filter$1.isDone()) { break }
98953               }
98954               if (filter$1.isGeometryChanged()) { this.geometryChanged(); }
98955             } else if (hasInterface(arguments[0], GeometryFilter)) {
98956               var filter$2 = arguments[0];
98957               filter$2.filter(this);
98958             } else if (hasInterface(arguments[0], GeometryComponentFilter)) {
98959               var filter$3 = arguments[0];
98960               filter$3.filter(this);
98961             }
98962           };
98963           LineString.prototype.getBoundary = function getBoundary () {
98964             return new BoundaryOp(this).getBoundary()
98965           };
98966           LineString.prototype.isEquivalentClass = function isEquivalentClass (other) {
98967             return other instanceof LineString
98968           };
98969           LineString.prototype.clone = function clone () {
98970             var ls = Geometry$$1.prototype.clone.call(this);
98971             ls._points = this._points.clone();
98972             return ls
98973           };
98974           LineString.prototype.getCoordinateN = function getCoordinateN (n) {
98975             return this._points.getCoordinate(n)
98976           };
98977           LineString.prototype.getGeometryType = function getGeometryType () {
98978             return 'LineString'
98979           };
98980           LineString.prototype.copy = function copy () {
98981             return new LineString(this._points.copy(), this._factory)
98982           };
98983           LineString.prototype.getCoordinateSequence = function getCoordinateSequence () {
98984             return this._points
98985           };
98986           LineString.prototype.isEmpty = function isEmpty () {
98987             return this._points.size() === 0
98988           };
98989           LineString.prototype.init = function init (points) {
98990             if (points === null) {
98991               points = this.getFactory().getCoordinateSequenceFactory().create([]);
98992             }
98993             if (points.size() === 1) {
98994               throw new IllegalArgumentException('Invalid number of points in LineString (found ' + points.size() + ' - must be 0 or >= 2)')
98995             }
98996             this._points = points;
98997           };
98998           LineString.prototype.isCoordinate = function isCoordinate (pt) {
98999             var this$1 = this;
99000
99001             for (var i = 0; i < this._points.size(); i++) {
99002               if (this$1._points.getCoordinate(i).equals(pt)) {
99003                 return true
99004               }
99005             }
99006             return false
99007           };
99008           LineString.prototype.getStartPoint = function getStartPoint () {
99009             if (this.isEmpty()) {
99010               return null
99011             }
99012             return this.getPointN(0)
99013           };
99014           LineString.prototype.getPointN = function getPointN (n) {
99015             return this.getFactory().createPoint(this._points.getCoordinate(n))
99016           };
99017           LineString.prototype.interfaces_ = function interfaces_ () {
99018             return [Lineal]
99019           };
99020           LineString.prototype.getClass = function getClass () {
99021             return LineString
99022           };
99023           staticAccessors.serialVersionUID.get = function () { return 3110669828065365560 };
99024
99025           Object.defineProperties( LineString, staticAccessors );
99026
99027           return LineString;
99028         }(Geometry));
99029
99030         var Puntal = function Puntal () {};
99031
99032         Puntal.prototype.interfaces_ = function interfaces_ () {
99033           return []
99034         };
99035         Puntal.prototype.getClass = function getClass () {
99036           return Puntal
99037         };
99038
99039         var Point$1 = (function (Geometry$$1) {
99040           function Point (coordinates, factory) {
99041             Geometry$$1.call(this, factory);
99042             this._coordinates = coordinates || null;
99043             this.init(this._coordinates);
99044           }
99045
99046           if ( Geometry$$1 ) { Point.__proto__ = Geometry$$1; }
99047           Point.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
99048           Point.prototype.constructor = Point;
99049
99050           var staticAccessors = { serialVersionUID: { configurable: true } };
99051           Point.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
99052             if (this.isEmpty()) {
99053               return new Envelope()
99054             }
99055             var env = new Envelope();
99056             env.expandToInclude(this._coordinates.getX(0), this._coordinates.getY(0));
99057             return env
99058           };
99059           Point.prototype.getSortIndex = function getSortIndex () {
99060             return Geometry$$1.SORTINDEX_POINT
99061           };
99062           Point.prototype.getCoordinates = function getCoordinates () {
99063             return this.isEmpty() ? [] : [this.getCoordinate()]
99064           };
99065           Point.prototype.equalsExact = function equalsExact () {
99066             if (arguments.length === 2) {
99067               var other = arguments[0];
99068               var tolerance = arguments[1];
99069               if (!this.isEquivalentClass(other)) {
99070                 return false
99071               }
99072               if (this.isEmpty() && other.isEmpty()) {
99073                 return true
99074               }
99075               if (this.isEmpty() !== other.isEmpty()) {
99076                 return false
99077               }
99078               return this.equal(other.getCoordinate(), this.getCoordinate(), tolerance)
99079             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
99080           };
99081           Point.prototype.normalize = function normalize () {};
99082           Point.prototype.getCoordinate = function getCoordinate () {
99083             return this._coordinates.size() !== 0 ? this._coordinates.getCoordinate(0) : null
99084           };
99085           Point.prototype.getBoundaryDimension = function getBoundaryDimension () {
99086             return Dimension.FALSE
99087           };
99088           Point.prototype.getDimension = function getDimension () {
99089             return 0
99090           };
99091           Point.prototype.getNumPoints = function getNumPoints () {
99092             return this.isEmpty() ? 0 : 1
99093           };
99094           Point.prototype.reverse = function reverse () {
99095             return this.copy()
99096           };
99097           Point.prototype.getX = function getX () {
99098             if (this.getCoordinate() === null) {
99099               throw new Error('getX called on empty Point')
99100             }
99101             return this.getCoordinate().x
99102           };
99103           Point.prototype.compareToSameClass = function compareToSameClass () {
99104             if (arguments.length === 1) {
99105               var other = arguments[0];
99106               var point$1 = other;
99107               return this.getCoordinate().compareTo(point$1.getCoordinate())
99108             } else if (arguments.length === 2) {
99109               var other$1 = arguments[0];
99110               var comp = arguments[1];
99111               var point = other$1;
99112               return comp.compare(this._coordinates, point._coordinates)
99113             }
99114           };
99115           Point.prototype.apply = function apply () {
99116             if (hasInterface(arguments[0], CoordinateFilter)) {
99117               var filter = arguments[0];
99118               if (this.isEmpty()) {
99119                 return null
99120               }
99121               filter.filter(this.getCoordinate());
99122             } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) {
99123               var filter$1 = arguments[0];
99124               if (this.isEmpty()) { return null }
99125               filter$1.filter(this._coordinates, 0);
99126               if (filter$1.isGeometryChanged()) { this.geometryChanged(); }
99127             } else if (hasInterface(arguments[0], GeometryFilter)) {
99128               var filter$2 = arguments[0];
99129               filter$2.filter(this);
99130             } else if (hasInterface(arguments[0], GeometryComponentFilter)) {
99131               var filter$3 = arguments[0];
99132               filter$3.filter(this);
99133             }
99134           };
99135           Point.prototype.getBoundary = function getBoundary () {
99136             return this.getFactory().createGeometryCollection(null)
99137           };
99138           Point.prototype.clone = function clone () {
99139             var p = Geometry$$1.prototype.clone.call(this);
99140             p._coordinates = this._coordinates.clone();
99141             return p
99142           };
99143           Point.prototype.getGeometryType = function getGeometryType () {
99144             return 'Point'
99145           };
99146           Point.prototype.copy = function copy () {
99147             return new Point(this._coordinates.copy(), this._factory)
99148           };
99149           Point.prototype.getCoordinateSequence = function getCoordinateSequence () {
99150             return this._coordinates
99151           };
99152           Point.prototype.getY = function getY () {
99153             if (this.getCoordinate() === null) {
99154               throw new Error('getY called on empty Point')
99155             }
99156             return this.getCoordinate().y
99157           };
99158           Point.prototype.isEmpty = function isEmpty () {
99159             return this._coordinates.size() === 0
99160           };
99161           Point.prototype.init = function init (coordinates) {
99162             if (coordinates === null) {
99163               coordinates = this.getFactory().getCoordinateSequenceFactory().create([]);
99164             }
99165             Assert.isTrue(coordinates.size() <= 1);
99166             this._coordinates = coordinates;
99167           };
99168           Point.prototype.isSimple = function isSimple () {
99169             return true
99170           };
99171           Point.prototype.interfaces_ = function interfaces_ () {
99172             return [Puntal]
99173           };
99174           Point.prototype.getClass = function getClass () {
99175             return Point
99176           };
99177           staticAccessors.serialVersionUID.get = function () { return 4902022702746614570 };
99178
99179           Object.defineProperties( Point, staticAccessors );
99180
99181           return Point;
99182         }(Geometry));
99183
99184         var Polygonal = function Polygonal () {};
99185
99186         Polygonal.prototype.interfaces_ = function interfaces_ () {
99187           return []
99188         };
99189         Polygonal.prototype.getClass = function getClass () {
99190           return Polygonal
99191         };
99192
99193         var Polygon = (function (Geometry$$1) {
99194           function Polygon (shell, holes, factory) {
99195             Geometry$$1.call(this, factory);
99196             this._shell = null;
99197             this._holes = null;
99198             if (shell === null) {
99199               shell = this.getFactory().createLinearRing();
99200             }
99201             if (holes === null) {
99202               holes = [];
99203             }
99204             if (Geometry$$1.hasNullElements(holes)) {
99205               throw new IllegalArgumentException('holes must not contain null elements')
99206             }
99207             if (shell.isEmpty() && Geometry$$1.hasNonEmptyElements(holes)) {
99208               throw new IllegalArgumentException('shell is empty but holes are not')
99209             }
99210             this._shell = shell;
99211             this._holes = holes;
99212           }
99213
99214           if ( Geometry$$1 ) { Polygon.__proto__ = Geometry$$1; }
99215           Polygon.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
99216           Polygon.prototype.constructor = Polygon;
99217
99218           var staticAccessors = { serialVersionUID: { configurable: true } };
99219           Polygon.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
99220             return this._shell.getEnvelopeInternal()
99221           };
99222           Polygon.prototype.getSortIndex = function getSortIndex () {
99223             return Geometry$$1.SORTINDEX_POLYGON
99224           };
99225           Polygon.prototype.getCoordinates = function getCoordinates () {
99226             var this$1 = this;
99227
99228             if (this.isEmpty()) {
99229               return []
99230             }
99231             var coordinates = new Array(this.getNumPoints()).fill(null);
99232             var k = -1;
99233             var shellCoordinates = this._shell.getCoordinates();
99234             for (var x = 0; x < shellCoordinates.length; x++) {
99235               k++;
99236               coordinates[k] = shellCoordinates[x];
99237             }
99238             for (var i = 0; i < this._holes.length; i++) {
99239               var childCoordinates = this$1._holes[i].getCoordinates();
99240               for (var j = 0; j < childCoordinates.length; j++) {
99241                 k++;
99242                 coordinates[k] = childCoordinates[j];
99243               }
99244             }
99245             return coordinates
99246           };
99247           Polygon.prototype.getArea = function getArea () {
99248             var this$1 = this;
99249
99250             var area = 0.0;
99251             area += Math.abs(CGAlgorithms.signedArea(this._shell.getCoordinateSequence()));
99252             for (var i = 0; i < this._holes.length; i++) {
99253               area -= Math.abs(CGAlgorithms.signedArea(this$1._holes[i].getCoordinateSequence()));
99254             }
99255             return area
99256           };
99257           Polygon.prototype.isRectangle = function isRectangle () {
99258             if (this.getNumInteriorRing() !== 0) { return false }
99259             if (this._shell === null) { return false }
99260             if (this._shell.getNumPoints() !== 5) { return false }
99261             var seq = this._shell.getCoordinateSequence();
99262             var env = this.getEnvelopeInternal();
99263             for (var i = 0; i < 5; i++) {
99264               var x = seq.getX(i);
99265               if (!(x === env.getMinX() || x === env.getMaxX())) { return false }
99266               var y = seq.getY(i);
99267               if (!(y === env.getMinY() || y === env.getMaxY())) { return false }
99268             }
99269             var prevX = seq.getX(0);
99270             var prevY = seq.getY(0);
99271             for (var i$1 = 1; i$1 <= 4; i$1++) {
99272               var x$1 = seq.getX(i$1);
99273               var y$1 = seq.getY(i$1);
99274               var xChanged = x$1 !== prevX;
99275               var yChanged = y$1 !== prevY;
99276               if (xChanged === yChanged) { return false }
99277               prevX = x$1;
99278               prevY = y$1;
99279             }
99280             return true
99281           };
99282           Polygon.prototype.equalsExact = function equalsExact () {
99283             var this$1 = this;
99284
99285             if (arguments.length === 2) {
99286               var other = arguments[0];
99287               var tolerance = arguments[1];
99288               if (!this.isEquivalentClass(other)) {
99289                 return false
99290               }
99291               var otherPolygon = other;
99292               var thisShell = this._shell;
99293               var otherPolygonShell = otherPolygon._shell;
99294               if (!thisShell.equalsExact(otherPolygonShell, tolerance)) {
99295                 return false
99296               }
99297               if (this._holes.length !== otherPolygon._holes.length) {
99298                 return false
99299               }
99300               for (var i = 0; i < this._holes.length; i++) {
99301                 if (!this$1._holes[i].equalsExact(otherPolygon._holes[i], tolerance)) {
99302                   return false
99303                 }
99304               }
99305               return true
99306             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
99307           };
99308           Polygon.prototype.normalize = function normalize () {
99309             var this$1 = this;
99310
99311             if (arguments.length === 0) {
99312               this.normalize(this._shell, true);
99313               for (var i = 0; i < this._holes.length; i++) {
99314                 this$1.normalize(this$1._holes[i], false);
99315               }
99316               Arrays.sort(this._holes);
99317             } else if (arguments.length === 2) {
99318               var ring = arguments[0];
99319               var clockwise = arguments[1];
99320               if (ring.isEmpty()) {
99321                 return null
99322               }
99323               var uniqueCoordinates = new Array(ring.getCoordinates().length - 1).fill(null);
99324               System.arraycopy(ring.getCoordinates(), 0, uniqueCoordinates, 0, uniqueCoordinates.length);
99325               var minCoordinate = CoordinateArrays.minCoordinate(ring.getCoordinates());
99326               CoordinateArrays.scroll(uniqueCoordinates, minCoordinate);
99327               System.arraycopy(uniqueCoordinates, 0, ring.getCoordinates(), 0, uniqueCoordinates.length);
99328               ring.getCoordinates()[uniqueCoordinates.length] = uniqueCoordinates[0];
99329               if (CGAlgorithms.isCCW(ring.getCoordinates()) === clockwise) {
99330                 CoordinateArrays.reverse(ring.getCoordinates());
99331               }
99332             }
99333           };
99334           Polygon.prototype.getCoordinate = function getCoordinate () {
99335             return this._shell.getCoordinate()
99336           };
99337           Polygon.prototype.getNumInteriorRing = function getNumInteriorRing () {
99338             return this._holes.length
99339           };
99340           Polygon.prototype.getBoundaryDimension = function getBoundaryDimension () {
99341             return 1
99342           };
99343           Polygon.prototype.getDimension = function getDimension () {
99344             return 2
99345           };
99346           Polygon.prototype.getLength = function getLength () {
99347             var this$1 = this;
99348
99349             var len = 0.0;
99350             len += this._shell.getLength();
99351             for (var i = 0; i < this._holes.length; i++) {
99352               len += this$1._holes[i].getLength();
99353             }
99354             return len
99355           };
99356           Polygon.prototype.getNumPoints = function getNumPoints () {
99357             var this$1 = this;
99358
99359             var numPoints = this._shell.getNumPoints();
99360             for (var i = 0; i < this._holes.length; i++) {
99361               numPoints += this$1._holes[i].getNumPoints();
99362             }
99363             return numPoints
99364           };
99365           Polygon.prototype.reverse = function reverse () {
99366             var this$1 = this;
99367
99368             var poly = this.copy();
99369             poly._shell = this._shell.copy().reverse();
99370             poly._holes = new Array(this._holes.length).fill(null);
99371             for (var i = 0; i < this._holes.length; i++) {
99372               poly._holes[i] = this$1._holes[i].copy().reverse();
99373             }
99374             return poly
99375           };
99376           Polygon.prototype.convexHull = function convexHull () {
99377             return this.getExteriorRing().convexHull()
99378           };
99379           Polygon.prototype.compareToSameClass = function compareToSameClass () {
99380             var this$1 = this;
99381
99382             if (arguments.length === 1) {
99383               var o = arguments[0];
99384               var thisShell = this._shell;
99385               var otherShell = o._shell;
99386               return thisShell.compareToSameClass(otherShell)
99387             } else if (arguments.length === 2) {
99388               var o$1 = arguments[0];
99389               var comp = arguments[1];
99390               var poly = o$1;
99391               var thisShell$1 = this._shell;
99392               var otherShell$1 = poly._shell;
99393               var shellComp = thisShell$1.compareToSameClass(otherShell$1, comp);
99394               if (shellComp !== 0) { return shellComp }
99395               var nHole1 = this.getNumInteriorRing();
99396               var nHole2 = poly.getNumInteriorRing();
99397               var i = 0;
99398               while (i < nHole1 && i < nHole2) {
99399                 var thisHole = this$1.getInteriorRingN(i);
99400                 var otherHole = poly.getInteriorRingN(i);
99401                 var holeComp = thisHole.compareToSameClass(otherHole, comp);
99402                 if (holeComp !== 0) { return holeComp }
99403                 i++;
99404               }
99405               if (i < nHole1) { return 1 }
99406               if (i < nHole2) { return -1 }
99407               return 0
99408             }
99409           };
99410           Polygon.prototype.apply = function apply (filter) {
99411             var this$1 = this;
99412
99413             if (hasInterface(filter, CoordinateFilter)) {
99414               this._shell.apply(filter);
99415               for (var i$1 = 0; i$1 < this._holes.length; i$1++) {
99416                 this$1._holes[i$1].apply(filter);
99417               }
99418             } else if (hasInterface(filter, CoordinateSequenceFilter)) {
99419               this._shell.apply(filter);
99420               if (!filter.isDone()) {
99421                 for (var i$2 = 0; i$2 < this._holes.length; i$2++) {
99422                   this$1._holes[i$2].apply(filter);
99423                   if (filter.isDone()) { break }
99424                 }
99425               }
99426               if (filter.isGeometryChanged()) { this.geometryChanged(); }
99427             } else if (hasInterface(filter, GeometryFilter)) {
99428               filter.filter(this);
99429             } else if (hasInterface(filter, GeometryComponentFilter)) {
99430               filter.filter(this);
99431               this._shell.apply(filter);
99432               for (var i = 0; i < this._holes.length; i++) {
99433                 this$1._holes[i].apply(filter);
99434               }
99435             }
99436           };
99437           Polygon.prototype.getBoundary = function getBoundary () {
99438             var this$1 = this;
99439
99440             if (this.isEmpty()) {
99441               return this.getFactory().createMultiLineString()
99442             }
99443             var rings = new Array(this._holes.length + 1).fill(null);
99444             rings[0] = this._shell;
99445             for (var i = 0; i < this._holes.length; i++) {
99446               rings[i + 1] = this$1._holes[i];
99447             }
99448             if (rings.length <= 1) { return this.getFactory().createLinearRing(rings[0].getCoordinateSequence()) }
99449             return this.getFactory().createMultiLineString(rings)
99450           };
99451           Polygon.prototype.clone = function clone () {
99452             var this$1 = this;
99453
99454             var poly = Geometry$$1.prototype.clone.call(this);
99455             poly._shell = this._shell.clone();
99456             poly._holes = new Array(this._holes.length).fill(null);
99457             for (var i = 0; i < this._holes.length; i++) {
99458               poly._holes[i] = this$1._holes[i].clone();
99459             }
99460             return poly
99461           };
99462           Polygon.prototype.getGeometryType = function getGeometryType () {
99463             return 'Polygon'
99464           };
99465           Polygon.prototype.copy = function copy () {
99466             var this$1 = this;
99467
99468             var shell = this._shell.copy();
99469             var holes = new Array(this._holes.length).fill(null);
99470             for (var i = 0; i < holes.length; i++) {
99471               holes[i] = this$1._holes[i].copy();
99472             }
99473             return new Polygon(shell, holes, this._factory)
99474           };
99475           Polygon.prototype.getExteriorRing = function getExteriorRing () {
99476             return this._shell
99477           };
99478           Polygon.prototype.isEmpty = function isEmpty () {
99479             return this._shell.isEmpty()
99480           };
99481           Polygon.prototype.getInteriorRingN = function getInteriorRingN (n) {
99482             return this._holes[n]
99483           };
99484           Polygon.prototype.interfaces_ = function interfaces_ () {
99485             return [Polygonal]
99486           };
99487           Polygon.prototype.getClass = function getClass () {
99488             return Polygon
99489           };
99490           staticAccessors.serialVersionUID.get = function () { return -3494792200821764533 };
99491
99492           Object.defineProperties( Polygon, staticAccessors );
99493
99494           return Polygon;
99495         }(Geometry));
99496
99497         var MultiPoint = (function (GeometryCollection$$1) {
99498           function MultiPoint () {
99499             GeometryCollection$$1.apply(this, arguments);
99500           }
99501
99502           if ( GeometryCollection$$1 ) { MultiPoint.__proto__ = GeometryCollection$$1; }
99503           MultiPoint.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype );
99504           MultiPoint.prototype.constructor = MultiPoint;
99505
99506           var staticAccessors = { serialVersionUID: { configurable: true } };
99507
99508           MultiPoint.prototype.getSortIndex = function getSortIndex () {
99509             return Geometry.SORTINDEX_MULTIPOINT
99510           };
99511           MultiPoint.prototype.isValid = function isValid () {
99512             return true
99513           };
99514           MultiPoint.prototype.equalsExact = function equalsExact () {
99515             if (arguments.length === 2) {
99516               var other = arguments[0];
99517               var tolerance = arguments[1];
99518               if (!this.isEquivalentClass(other)) {
99519                 return false
99520               }
99521               return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance)
99522             } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) }
99523           };
99524           MultiPoint.prototype.getCoordinate = function getCoordinate () {
99525             if (arguments.length === 1) {
99526               var n = arguments[0];
99527               return this._geometries[n].getCoordinate()
99528             } else { return GeometryCollection$$1.prototype.getCoordinate.apply(this, arguments) }
99529           };
99530           MultiPoint.prototype.getBoundaryDimension = function getBoundaryDimension () {
99531             return Dimension.FALSE
99532           };
99533           MultiPoint.prototype.getDimension = function getDimension () {
99534             return 0
99535           };
99536           MultiPoint.prototype.getBoundary = function getBoundary () {
99537             return this.getFactory().createGeometryCollection(null)
99538           };
99539           MultiPoint.prototype.getGeometryType = function getGeometryType () {
99540             return 'MultiPoint'
99541           };
99542           MultiPoint.prototype.copy = function copy () {
99543             var this$1 = this;
99544
99545             var points = new Array(this._geometries.length).fill(null);
99546             for (var i = 0; i < points.length; i++) {
99547               points[i] = this$1._geometries[i].copy();
99548             }
99549             return new MultiPoint(points, this._factory)
99550           };
99551           MultiPoint.prototype.interfaces_ = function interfaces_ () {
99552             return [Puntal]
99553           };
99554           MultiPoint.prototype.getClass = function getClass () {
99555             return MultiPoint
99556           };
99557           staticAccessors.serialVersionUID.get = function () { return -8048474874175355449 };
99558
99559           Object.defineProperties( MultiPoint, staticAccessors );
99560
99561           return MultiPoint;
99562         }(GeometryCollection));
99563
99564         var LinearRing = (function (LineString$$1) {
99565           function LinearRing (points, factory) {
99566             if (points instanceof Coordinate && factory instanceof GeometryFactory) {
99567               points = factory.getCoordinateSequenceFactory().create(points);
99568             }
99569             LineString$$1.call(this, points, factory);
99570             this.validateConstruction();
99571           }
99572
99573           if ( LineString$$1 ) { LinearRing.__proto__ = LineString$$1; }
99574           LinearRing.prototype = Object.create( LineString$$1 && LineString$$1.prototype );
99575           LinearRing.prototype.constructor = LinearRing;
99576
99577           var staticAccessors = { MINIMUM_VALID_SIZE: { configurable: true },serialVersionUID: { configurable: true } };
99578           LinearRing.prototype.getSortIndex = function getSortIndex () {
99579             return Geometry.SORTINDEX_LINEARRING
99580           };
99581           LinearRing.prototype.getBoundaryDimension = function getBoundaryDimension () {
99582             return Dimension.FALSE
99583           };
99584           LinearRing.prototype.isClosed = function isClosed () {
99585             if (this.isEmpty()) {
99586               return true
99587             }
99588             return LineString$$1.prototype.isClosed.call(this)
99589           };
99590           LinearRing.prototype.reverse = function reverse () {
99591             var seq = this._points.copy();
99592             CoordinateSequences.reverse(seq);
99593             var rev = this.getFactory().createLinearRing(seq);
99594             return rev
99595           };
99596           LinearRing.prototype.validateConstruction = function validateConstruction () {
99597             if (!this.isEmpty() && !LineString$$1.prototype.isClosed.call(this)) {
99598               throw new IllegalArgumentException('Points of LinearRing do not form a closed linestring')
99599             }
99600             if (this.getCoordinateSequence().size() >= 1 && this.getCoordinateSequence().size() < LinearRing.MINIMUM_VALID_SIZE) {
99601               throw new IllegalArgumentException('Invalid number of points in LinearRing (found ' + this.getCoordinateSequence().size() + ' - must be 0 or >= 4)')
99602             }
99603           };
99604           LinearRing.prototype.getGeometryType = function getGeometryType () {
99605             return 'LinearRing'
99606           };
99607           LinearRing.prototype.copy = function copy () {
99608             return new LinearRing(this._points.copy(), this._factory)
99609           };
99610           LinearRing.prototype.interfaces_ = function interfaces_ () {
99611             return []
99612           };
99613           LinearRing.prototype.getClass = function getClass () {
99614             return LinearRing
99615           };
99616           staticAccessors.MINIMUM_VALID_SIZE.get = function () { return 4 };
99617           staticAccessors.serialVersionUID.get = function () { return -4261142084085851829 };
99618
99619           Object.defineProperties( LinearRing, staticAccessors );
99620
99621           return LinearRing;
99622         }(LineString));
99623
99624         var MultiPolygon = (function (GeometryCollection$$1) {
99625           function MultiPolygon () {
99626             GeometryCollection$$1.apply(this, arguments);
99627           }
99628
99629           if ( GeometryCollection$$1 ) { MultiPolygon.__proto__ = GeometryCollection$$1; }
99630           MultiPolygon.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype );
99631           MultiPolygon.prototype.constructor = MultiPolygon;
99632
99633           var staticAccessors = { serialVersionUID: { configurable: true } };
99634
99635           MultiPolygon.prototype.getSortIndex = function getSortIndex () {
99636             return Geometry.SORTINDEX_MULTIPOLYGON
99637           };
99638           MultiPolygon.prototype.equalsExact = function equalsExact () {
99639             if (arguments.length === 2) {
99640               var other = arguments[0];
99641               var tolerance = arguments[1];
99642               if (!this.isEquivalentClass(other)) {
99643                 return false
99644               }
99645               return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance)
99646             } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) }
99647           };
99648           MultiPolygon.prototype.getBoundaryDimension = function getBoundaryDimension () {
99649             return 1
99650           };
99651           MultiPolygon.prototype.getDimension = function getDimension () {
99652             return 2
99653           };
99654           MultiPolygon.prototype.reverse = function reverse () {
99655             var this$1 = this;
99656
99657             var n = this._geometries.length;
99658             var revGeoms = new Array(n).fill(null);
99659             for (var i = 0; i < this._geometries.length; i++) {
99660               revGeoms[i] = this$1._geometries[i].reverse();
99661             }
99662             return this.getFactory().createMultiPolygon(revGeoms)
99663           };
99664           MultiPolygon.prototype.getBoundary = function getBoundary () {
99665             var this$1 = this;
99666
99667             if (this.isEmpty()) {
99668               return this.getFactory().createMultiLineString()
99669             }
99670             var allRings = new ArrayList();
99671             for (var i = 0; i < this._geometries.length; i++) {
99672               var polygon = this$1._geometries[i];
99673               var rings = polygon.getBoundary();
99674               for (var j = 0; j < rings.getNumGeometries(); j++) {
99675                 allRings.add(rings.getGeometryN(j));
99676               }
99677             }
99678             var allRingsArray = new Array(allRings.size()).fill(null);
99679             return this.getFactory().createMultiLineString(allRings.toArray(allRingsArray))
99680           };
99681           MultiPolygon.prototype.getGeometryType = function getGeometryType () {
99682             return 'MultiPolygon'
99683           };
99684           MultiPolygon.prototype.copy = function copy () {
99685             var this$1 = this;
99686
99687             var polygons = new Array(this._geometries.length).fill(null);
99688             for (var i = 0; i < polygons.length; i++) {
99689               polygons[i] = this$1._geometries[i].copy();
99690             }
99691             return new MultiPolygon(polygons, this._factory)
99692           };
99693           MultiPolygon.prototype.interfaces_ = function interfaces_ () {
99694             return [Polygonal]
99695           };
99696           MultiPolygon.prototype.getClass = function getClass () {
99697             return MultiPolygon
99698           };
99699           staticAccessors.serialVersionUID.get = function () { return -551033529766975875 };
99700
99701           Object.defineProperties( MultiPolygon, staticAccessors );
99702
99703           return MultiPolygon;
99704         }(GeometryCollection));
99705
99706         var GeometryEditor = function GeometryEditor (factory) {
99707           this._factory = factory || null;
99708           this._isUserDataCopied = false;
99709         };
99710
99711         var staticAccessors$16 = { NoOpGeometryOperation: { configurable: true },CoordinateOperation: { configurable: true },CoordinateSequenceOperation: { configurable: true } };
99712         GeometryEditor.prototype.setCopyUserData = function setCopyUserData (isUserDataCopied) {
99713           this._isUserDataCopied = isUserDataCopied;
99714         };
99715         GeometryEditor.prototype.edit = function edit (geometry, operation) {
99716           if (geometry === null) { return null }
99717           var result = this.editInternal(geometry, operation);
99718           if (this._isUserDataCopied) {
99719             result.setUserData(geometry.getUserData());
99720           }
99721           return result
99722         };
99723         GeometryEditor.prototype.editInternal = function editInternal (geometry, operation) {
99724           if (this._factory === null) { this._factory = geometry.getFactory(); }
99725           if (geometry instanceof GeometryCollection) {
99726             return this.editGeometryCollection(geometry, operation)
99727           }
99728           if (geometry instanceof Polygon) {
99729             return this.editPolygon(geometry, operation)
99730           }
99731           if (geometry instanceof Point$1) {
99732             return operation.edit(geometry, this._factory)
99733           }
99734           if (geometry instanceof LineString) {
99735             return operation.edit(geometry, this._factory)
99736           }
99737           Assert.shouldNeverReachHere('Unsupported Geometry class: ' + geometry.getClass().getName());
99738           return null
99739         };
99740         GeometryEditor.prototype.editGeometryCollection = function editGeometryCollection (collection, operation) {
99741             var this$1 = this;
99742
99743           var collectionForType = operation.edit(collection, this._factory);
99744           var geometries = new ArrayList();
99745           for (var i = 0; i < collectionForType.getNumGeometries(); i++) {
99746             var geometry = this$1.edit(collectionForType.getGeometryN(i), operation);
99747             if (geometry === null || geometry.isEmpty()) {
99748               continue
99749             }
99750             geometries.add(geometry);
99751           }
99752           if (collectionForType.getClass() === MultiPoint) {
99753             return this._factory.createMultiPoint(geometries.toArray([]))
99754           }
99755           if (collectionForType.getClass() === MultiLineString) {
99756             return this._factory.createMultiLineString(geometries.toArray([]))
99757           }
99758           if (collectionForType.getClass() === MultiPolygon) {
99759             return this._factory.createMultiPolygon(geometries.toArray([]))
99760           }
99761           return this._factory.createGeometryCollection(geometries.toArray([]))
99762         };
99763         GeometryEditor.prototype.editPolygon = function editPolygon (polygon, operation) {
99764             var this$1 = this;
99765
99766           var newPolygon = operation.edit(polygon, this._factory);
99767           if (newPolygon === null) { newPolygon = this._factory.createPolygon(null); }
99768           if (newPolygon.isEmpty()) {
99769             return newPolygon
99770           }
99771           var shell = this.edit(newPolygon.getExteriorRing(), operation);
99772           if (shell === null || shell.isEmpty()) {
99773             return this._factory.createPolygon()
99774           }
99775           var holes = new ArrayList();
99776           for (var i = 0; i < newPolygon.getNumInteriorRing(); i++) {
99777             var hole = this$1.edit(newPolygon.getInteriorRingN(i), operation);
99778             if (hole === null || hole.isEmpty()) {
99779               continue
99780             }
99781             holes.add(hole);
99782           }
99783           return this._factory.createPolygon(shell, holes.toArray([]))
99784         };
99785         GeometryEditor.prototype.interfaces_ = function interfaces_ () {
99786           return []
99787         };
99788         GeometryEditor.prototype.getClass = function getClass () {
99789           return GeometryEditor
99790         };
99791         GeometryEditor.GeometryEditorOperation = function GeometryEditorOperation () {};
99792         staticAccessors$16.NoOpGeometryOperation.get = function () { return NoOpGeometryOperation };
99793         staticAccessors$16.CoordinateOperation.get = function () { return CoordinateOperation };
99794         staticAccessors$16.CoordinateSequenceOperation.get = function () { return CoordinateSequenceOperation };
99795
99796         Object.defineProperties( GeometryEditor, staticAccessors$16 );
99797
99798         var NoOpGeometryOperation = function NoOpGeometryOperation () {};
99799
99800         NoOpGeometryOperation.prototype.edit = function edit (geometry, factory) {
99801           return geometry
99802         };
99803         NoOpGeometryOperation.prototype.interfaces_ = function interfaces_ () {
99804           return [GeometryEditor.GeometryEditorOperation]
99805         };
99806         NoOpGeometryOperation.prototype.getClass = function getClass () {
99807           return NoOpGeometryOperation
99808         };
99809
99810         var CoordinateOperation = function CoordinateOperation () {};
99811
99812         CoordinateOperation.prototype.edit = function edit (geometry, factory) {
99813           var coords = this.editCoordinates(geometry.getCoordinates(), geometry);
99814           if (coords === null) { return geometry }
99815           if (geometry instanceof LinearRing) {
99816             return factory.createLinearRing(coords)
99817           }
99818           if (geometry instanceof LineString) {
99819             return factory.createLineString(coords)
99820           }
99821           if (geometry instanceof Point$1) {
99822             if (coords.length > 0) {
99823               return factory.createPoint(coords[0])
99824             } else {
99825               return factory.createPoint()
99826             }
99827           }
99828           return geometry
99829         };
99830         CoordinateOperation.prototype.interfaces_ = function interfaces_ () {
99831           return [GeometryEditor.GeometryEditorOperation]
99832         };
99833         CoordinateOperation.prototype.getClass = function getClass () {
99834           return CoordinateOperation
99835         };
99836
99837         var CoordinateSequenceOperation = function CoordinateSequenceOperation () {};
99838
99839         CoordinateSequenceOperation.prototype.edit = function edit (geometry, factory) {
99840           if (geometry instanceof LinearRing) {
99841             return factory.createLinearRing(this.edit(geometry.getCoordinateSequence(), geometry))
99842           }
99843           if (geometry instanceof LineString) {
99844             return factory.createLineString(this.edit(geometry.getCoordinateSequence(), geometry))
99845           }
99846           if (geometry instanceof Point$1) {
99847             return factory.createPoint(this.edit(geometry.getCoordinateSequence(), geometry))
99848           }
99849           return geometry
99850         };
99851         CoordinateSequenceOperation.prototype.interfaces_ = function interfaces_ () {
99852           return [GeometryEditor.GeometryEditorOperation]
99853         };
99854         CoordinateSequenceOperation.prototype.getClass = function getClass () {
99855           return CoordinateSequenceOperation
99856         };
99857
99858         var CoordinateArraySequence = function CoordinateArraySequence () {
99859           var this$1 = this;
99860
99861           this._dimension = 3;
99862           this._coordinates = null;
99863           if (arguments.length === 1) {
99864             if (arguments[0] instanceof Array) {
99865               this._coordinates = arguments[0];
99866               this._dimension = 3;
99867             } else if (Number.isInteger(arguments[0])) {
99868               var size = arguments[0];
99869               this._coordinates = new Array(size).fill(null);
99870               for (var i = 0; i < size; i++) {
99871                 this$1._coordinates[i] = new Coordinate();
99872               }
99873             } else if (hasInterface(arguments[0], CoordinateSequence)) {
99874               var coordSeq = arguments[0];
99875               if (coordSeq === null) {
99876                 this._coordinates = new Array(0).fill(null);
99877                 return null
99878               }
99879               this._dimension = coordSeq.getDimension();
99880               this._coordinates = new Array(coordSeq.size()).fill(null);
99881               for (var i$1 = 0; i$1 < this._coordinates.length; i$1++) {
99882                 this$1._coordinates[i$1] = coordSeq.getCoordinateCopy(i$1);
99883               }
99884             }
99885           } else if (arguments.length === 2) {
99886             if (arguments[0] instanceof Array && Number.isInteger(arguments[1])) {
99887               var coordinates = arguments[0];
99888               var dimension = arguments[1];
99889               this._coordinates = coordinates;
99890               this._dimension = dimension;
99891               if (coordinates === null) { this._coordinates = new Array(0).fill(null); }
99892             } else if (Number.isInteger(arguments[0]) && Number.isInteger(arguments[1])) {
99893               var size$1 = arguments[0];
99894               var dimension$1 = arguments[1];
99895               this._coordinates = new Array(size$1).fill(null);
99896               this._dimension = dimension$1;
99897               for (var i$2 = 0; i$2 < size$1; i$2++) {
99898                 this$1._coordinates[i$2] = new Coordinate();
99899               }
99900             }
99901           }
99902         };
99903
99904         var staticAccessors$18 = { serialVersionUID: { configurable: true } };
99905         CoordinateArraySequence.prototype.setOrdinate = function setOrdinate (index, ordinateIndex, value) {
99906           switch (ordinateIndex) {
99907             case CoordinateSequence.X:
99908               this._coordinates[index].x = value;
99909               break
99910             case CoordinateSequence.Y:
99911               this._coordinates[index].y = value;
99912               break
99913             case CoordinateSequence.Z:
99914               this._coordinates[index].z = value;
99915               break
99916             default:
99917               throw new IllegalArgumentException('invalid ordinateIndex')
99918           }
99919         };
99920         CoordinateArraySequence.prototype.size = function size () {
99921           return this._coordinates.length
99922         };
99923         CoordinateArraySequence.prototype.getOrdinate = function getOrdinate (index, ordinateIndex) {
99924           switch (ordinateIndex) {
99925             case CoordinateSequence.X:
99926               return this._coordinates[index].x
99927             case CoordinateSequence.Y:
99928               return this._coordinates[index].y
99929             case CoordinateSequence.Z:
99930               return this._coordinates[index].z
99931           }
99932           return Double.NaN
99933         };
99934         CoordinateArraySequence.prototype.getCoordinate = function getCoordinate () {
99935           if (arguments.length === 1) {
99936             var i = arguments[0];
99937             return this._coordinates[i]
99938           } else if (arguments.length === 2) {
99939             var index = arguments[0];
99940             var coord = arguments[1];
99941             coord.x = this._coordinates[index].x;
99942             coord.y = this._coordinates[index].y;
99943             coord.z = this._coordinates[index].z;
99944           }
99945         };
99946         CoordinateArraySequence.prototype.getCoordinateCopy = function getCoordinateCopy (i) {
99947           return new Coordinate(this._coordinates[i])
99948         };
99949         CoordinateArraySequence.prototype.getDimension = function getDimension () {
99950           return this._dimension
99951         };
99952         CoordinateArraySequence.prototype.getX = function getX (index) {
99953           return this._coordinates[index].x
99954         };
99955         CoordinateArraySequence.prototype.clone = function clone () {
99956             var this$1 = this;
99957
99958           var cloneCoordinates = new Array(this.size()).fill(null);
99959           for (var i = 0; i < this._coordinates.length; i++) {
99960             cloneCoordinates[i] = this$1._coordinates[i].clone();
99961           }
99962           return new CoordinateArraySequence(cloneCoordinates, this._dimension)
99963         };
99964         CoordinateArraySequence.prototype.expandEnvelope = function expandEnvelope (env) {
99965             var this$1 = this;
99966
99967           for (var i = 0; i < this._coordinates.length; i++) {
99968             env.expandToInclude(this$1._coordinates[i]);
99969           }
99970           return env
99971         };
99972         CoordinateArraySequence.prototype.copy = function copy () {
99973             var this$1 = this;
99974
99975           var cloneCoordinates = new Array(this.size()).fill(null);
99976           for (var i = 0; i < this._coordinates.length; i++) {
99977             cloneCoordinates[i] = this$1._coordinates[i].copy();
99978           }
99979           return new CoordinateArraySequence(cloneCoordinates, this._dimension)
99980         };
99981         CoordinateArraySequence.prototype.toString = function toString () {
99982             var this$1 = this;
99983
99984           if (this._coordinates.length > 0) {
99985             var strBuf = new StringBuffer(17 * this._coordinates.length);
99986             strBuf.append('(');
99987             strBuf.append(this._coordinates[0]);
99988             for (var i = 1; i < this._coordinates.length; i++) {
99989               strBuf.append(', ');
99990               strBuf.append(this$1._coordinates[i]);
99991             }
99992             strBuf.append(')');
99993             return strBuf.toString()
99994           } else {
99995             return '()'
99996           }
99997         };
99998         CoordinateArraySequence.prototype.getY = function getY (index) {
99999           return this._coordinates[index].y
100000         };
100001         CoordinateArraySequence.prototype.toCoordinateArray = function toCoordinateArray () {
100002           return this._coordinates
100003         };
100004         CoordinateArraySequence.prototype.interfaces_ = function interfaces_ () {
100005           return [CoordinateSequence, Serializable]
100006         };
100007         CoordinateArraySequence.prototype.getClass = function getClass () {
100008           return CoordinateArraySequence
100009         };
100010         staticAccessors$18.serialVersionUID.get = function () { return -915438501601840650 };
100011
100012         Object.defineProperties( CoordinateArraySequence, staticAccessors$18 );
100013
100014         var CoordinateArraySequenceFactory = function CoordinateArraySequenceFactory () {};
100015
100016         var staticAccessors$17 = { serialVersionUID: { configurable: true },instanceObject: { configurable: true } };
100017
100018         CoordinateArraySequenceFactory.prototype.readResolve = function readResolve () {
100019           return CoordinateArraySequenceFactory.instance()
100020         };
100021         CoordinateArraySequenceFactory.prototype.create = function create () {
100022           if (arguments.length === 1) {
100023             if (arguments[0] instanceof Array) {
100024               var coordinates = arguments[0];
100025               return new CoordinateArraySequence(coordinates)
100026             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100027               var coordSeq = arguments[0];
100028               return new CoordinateArraySequence(coordSeq)
100029             }
100030           } else if (arguments.length === 2) {
100031             var size = arguments[0];
100032             var dimension = arguments[1];
100033             if (dimension > 3) { dimension = 3; }
100034             if (dimension < 2) { return new CoordinateArraySequence(size) }
100035             return new CoordinateArraySequence(size, dimension)
100036           }
100037         };
100038         CoordinateArraySequenceFactory.prototype.interfaces_ = function interfaces_ () {
100039           return [CoordinateSequenceFactory, Serializable]
100040         };
100041         CoordinateArraySequenceFactory.prototype.getClass = function getClass () {
100042           return CoordinateArraySequenceFactory
100043         };
100044         CoordinateArraySequenceFactory.instance = function instance () {
100045           return CoordinateArraySequenceFactory.instanceObject
100046         };
100047
100048         staticAccessors$17.serialVersionUID.get = function () { return -4099577099607551657 };
100049         staticAccessors$17.instanceObject.get = function () { return new CoordinateArraySequenceFactory() };
100050
100051         Object.defineProperties( CoordinateArraySequenceFactory, staticAccessors$17 );
100052
100053         /**
100054          * @see http://download.oracle.com/javase/6/docs/api/java/util/HashMap.html
100055          *
100056          * @extends {javascript.util.Map}
100057          * @constructor
100058          * @private
100059          */
100060         var HashMap = (function (MapInterface) {
100061           function HashMap () {
100062             MapInterface.call(this);
100063             this.map_ = new Map();
100064           }
100065
100066           if ( MapInterface ) { HashMap.__proto__ = MapInterface; }
100067           HashMap.prototype = Object.create( MapInterface && MapInterface.prototype );
100068           HashMap.prototype.constructor = HashMap;
100069           /**
100070            * @override
100071            */
100072           HashMap.prototype.get = function get (key) {
100073             return this.map_.get(key) || null
100074           };
100075
100076           /**
100077            * @override
100078            */
100079           HashMap.prototype.put = function put (key, value) {
100080             this.map_.set(key, value);
100081             return value
100082           };
100083
100084           /**
100085            * @override
100086            */
100087           HashMap.prototype.values = function values () {
100088             var arrayList = new ArrayList();
100089             var it = this.map_.values();
100090             var o = it.next();
100091             while (!o.done) {
100092               arrayList.add(o.value);
100093               o = it.next();
100094             }
100095             return arrayList
100096           };
100097
100098           /**
100099            * @override
100100            */
100101           HashMap.prototype.entrySet = function entrySet () {
100102             var hashSet = new HashSet();
100103             this.map_.entries().forEach(function (entry) { return hashSet.add(entry); });
100104             return hashSet
100105           };
100106
100107           /**
100108            * @override
100109            */
100110           HashMap.prototype.size = function size () {
100111             return this.map_.size()
100112           };
100113
100114           return HashMap;
100115         }(Map$1$1));
100116
100117         var PrecisionModel = function PrecisionModel () {
100118           this._modelType = null;
100119           this._scale = null;
100120           if (arguments.length === 0) {
100121             this._modelType = PrecisionModel.FLOATING;
100122           } else if (arguments.length === 1) {
100123             if (arguments[0] instanceof Type$2) {
100124               var modelType = arguments[0];
100125               this._modelType = modelType;
100126               if (modelType === PrecisionModel.FIXED) {
100127                 this.setScale(1.0);
100128               }
100129             } else if (typeof arguments[0] === 'number') {
100130               var scale = arguments[0];
100131               this._modelType = PrecisionModel.FIXED;
100132               this.setScale(scale);
100133             } else if (arguments[0] instanceof PrecisionModel) {
100134               var pm = arguments[0];
100135               this._modelType = pm._modelType;
100136               this._scale = pm._scale;
100137             }
100138           }
100139         };
100140
100141         var staticAccessors$19 = { serialVersionUID: { configurable: true },maximumPreciseValue: { configurable: true } };
100142         PrecisionModel.prototype.equals = function equals (other) {
100143           if (!(other instanceof PrecisionModel)) {
100144             return false
100145           }
100146           var otherPrecisionModel = other;
100147           return this._modelType === otherPrecisionModel._modelType && this._scale === otherPrecisionModel._scale
100148         };
100149         PrecisionModel.prototype.compareTo = function compareTo (o) {
100150           var other = o;
100151           var sigDigits = this.getMaximumSignificantDigits();
100152           var otherSigDigits = other.getMaximumSignificantDigits();
100153           return new Integer(sigDigits).compareTo(new Integer(otherSigDigits))
100154         };
100155         PrecisionModel.prototype.getScale = function getScale () {
100156           return this._scale
100157         };
100158         PrecisionModel.prototype.isFloating = function isFloating () {
100159           return this._modelType === PrecisionModel.FLOATING || this._modelType === PrecisionModel.FLOATING_SINGLE
100160         };
100161         PrecisionModel.prototype.getType = function getType () {
100162           return this._modelType
100163         };
100164         PrecisionModel.prototype.toString = function toString () {
100165           var description = 'UNKNOWN';
100166           if (this._modelType === PrecisionModel.FLOATING) {
100167             description = 'Floating';
100168           } else if (this._modelType === PrecisionModel.FLOATING_SINGLE) {
100169             description = 'Floating-Single';
100170           } else if (this._modelType === PrecisionModel.FIXED) {
100171             description = 'Fixed (Scale=' + this.getScale() + ')';
100172           }
100173           return description
100174         };
100175         PrecisionModel.prototype.makePrecise = function makePrecise () {
100176           if (typeof arguments[0] === 'number') {
100177             var val = arguments[0];
100178             if (Double.isNaN(val)) { return val }
100179             if (this._modelType === PrecisionModel.FLOATING_SINGLE) {
100180               var floatSingleVal = val;
100181               return floatSingleVal
100182             }
100183             if (this._modelType === PrecisionModel.FIXED) {
100184               return Math.round(val * this._scale) / this._scale
100185             }
100186             return val
100187           } else if (arguments[0] instanceof Coordinate) {
100188             var coord = arguments[0];
100189             if (this._modelType === PrecisionModel.FLOATING) { return null }
100190             coord.x = this.makePrecise(coord.x);
100191             coord.y = this.makePrecise(coord.y);
100192           }
100193         };
100194         PrecisionModel.prototype.getMaximumSignificantDigits = function getMaximumSignificantDigits () {
100195           var maxSigDigits = 16;
100196           if (this._modelType === PrecisionModel.FLOATING) {
100197             maxSigDigits = 16;
100198           } else if (this._modelType === PrecisionModel.FLOATING_SINGLE) {
100199             maxSigDigits = 6;
100200           } else if (this._modelType === PrecisionModel.FIXED) {
100201             maxSigDigits = 1 + Math.trunc(Math.ceil(Math.log(this.getScale()) / Math.log(10)));
100202           }
100203           return maxSigDigits
100204         };
100205         PrecisionModel.prototype.setScale = function setScale (scale) {
100206           this._scale = Math.abs(scale);
100207         };
100208         PrecisionModel.prototype.interfaces_ = function interfaces_ () {
100209           return [Serializable, Comparable]
100210         };
100211         PrecisionModel.prototype.getClass = function getClass () {
100212           return PrecisionModel
100213         };
100214         PrecisionModel.mostPrecise = function mostPrecise (pm1, pm2) {
100215           if (pm1.compareTo(pm2) >= 0) { return pm1 }
100216           return pm2
100217         };
100218         staticAccessors$19.serialVersionUID.get = function () { return 7777263578777803835 };
100219         staticAccessors$19.maximumPreciseValue.get = function () { return 9007199254740992.0 };
100220
100221         Object.defineProperties( PrecisionModel, staticAccessors$19 );
100222
100223         var Type$2 = function Type (name) {
100224           this._name = name || null;
100225           Type.nameToTypeMap.put(name, this);
100226         };
100227
100228         var staticAccessors$1$1 = { serialVersionUID: { configurable: true },nameToTypeMap: { configurable: true } };
100229         Type$2.prototype.readResolve = function readResolve () {
100230           return Type$2.nameToTypeMap.get(this._name)
100231         };
100232         Type$2.prototype.toString = function toString () {
100233           return this._name
100234         };
100235         Type$2.prototype.interfaces_ = function interfaces_ () {
100236           return [Serializable]
100237         };
100238         Type$2.prototype.getClass = function getClass () {
100239           return Type$2
100240         };
100241         staticAccessors$1$1.serialVersionUID.get = function () { return -5528602631731589822 };
100242         staticAccessors$1$1.nameToTypeMap.get = function () { return new HashMap() };
100243
100244         Object.defineProperties( Type$2, staticAccessors$1$1 );
100245
100246         PrecisionModel.Type = Type$2;
100247         PrecisionModel.FIXED = new Type$2('FIXED');
100248         PrecisionModel.FLOATING = new Type$2('FLOATING');
100249         PrecisionModel.FLOATING_SINGLE = new Type$2('FLOATING SINGLE');
100250
100251         var GeometryFactory = function GeometryFactory () {
100252           this._precisionModel = new PrecisionModel();
100253           this._SRID = 0;
100254           this._coordinateSequenceFactory = GeometryFactory.getDefaultCoordinateSequenceFactory();
100255
100256           if (arguments.length === 0) ; else if (arguments.length === 1) {
100257             if (hasInterface(arguments[0], CoordinateSequenceFactory)) {
100258               this._coordinateSequenceFactory = arguments[0];
100259             } else if (arguments[0] instanceof PrecisionModel) {
100260               this._precisionModel = arguments[0];
100261             }
100262           } else if (arguments.length === 2) {
100263             this._precisionModel = arguments[0];
100264             this._SRID = arguments[1];
100265           } else if (arguments.length === 3) {
100266             this._precisionModel = arguments[0];
100267             this._SRID = arguments[1];
100268             this._coordinateSequenceFactory = arguments[2];
100269           }
100270         };
100271
100272         var staticAccessors$2 = { serialVersionUID: { configurable: true } };
100273         GeometryFactory.prototype.toGeometry = function toGeometry (envelope) {
100274           if (envelope.isNull()) {
100275             return this.createPoint(null)
100276           }
100277           if (envelope.getMinX() === envelope.getMaxX() && envelope.getMinY() === envelope.getMaxY()) {
100278             return this.createPoint(new Coordinate(envelope.getMinX(), envelope.getMinY()))
100279           }
100280           if (envelope.getMinX() === envelope.getMaxX() || envelope.getMinY() === envelope.getMaxY()) {
100281             return this.createLineString([new Coordinate(envelope.getMinX(), envelope.getMinY()), new Coordinate(envelope.getMaxX(), envelope.getMaxY())])
100282           }
100283           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)
100284         };
100285         GeometryFactory.prototype.createLineString = function createLineString (coordinates) {
100286           if (!coordinates) { return new LineString(this.getCoordinateSequenceFactory().create([]), this) }
100287           else if (coordinates instanceof Array) { return new LineString(this.getCoordinateSequenceFactory().create(coordinates), this) }
100288           else if (hasInterface(coordinates, CoordinateSequence)) { return new LineString(coordinates, this) }
100289         };
100290         GeometryFactory.prototype.createMultiLineString = function createMultiLineString () {
100291           if (arguments.length === 0) {
100292             return new MultiLineString(null, this)
100293           } else if (arguments.length === 1) {
100294             var lineStrings = arguments[0];
100295             return new MultiLineString(lineStrings, this)
100296           }
100297         };
100298         GeometryFactory.prototype.buildGeometry = function buildGeometry (geomList) {
100299           var geomClass = null;
100300           var isHeterogeneous = false;
100301           var hasGeometryCollection = false;
100302           for (var i = geomList.iterator(); i.hasNext();) {
100303             var geom = i.next();
100304             var partClass = geom.getClass();
100305             if (geomClass === null) {
100306               geomClass = partClass;
100307             }
100308             if (partClass !== geomClass) {
100309               isHeterogeneous = true;
100310             }
100311             if (geom.isGeometryCollectionOrDerived()) { hasGeometryCollection = true; }
100312           }
100313           if (geomClass === null) {
100314             return this.createGeometryCollection()
100315           }
100316           if (isHeterogeneous || hasGeometryCollection) {
100317             return this.createGeometryCollection(GeometryFactory.toGeometryArray(geomList))
100318           }
100319           var geom0 = geomList.iterator().next();
100320           var isCollection = geomList.size() > 1;
100321           if (isCollection) {
100322             if (geom0 instanceof Polygon) {
100323               return this.createMultiPolygon(GeometryFactory.toPolygonArray(geomList))
100324             } else if (geom0 instanceof LineString) {
100325               return this.createMultiLineString(GeometryFactory.toLineStringArray(geomList))
100326             } else if (geom0 instanceof Point$1) {
100327               return this.createMultiPoint(GeometryFactory.toPointArray(geomList))
100328             }
100329             Assert.shouldNeverReachHere('Unhandled class: ' + geom0.getClass().getName());
100330           }
100331           return geom0
100332         };
100333         GeometryFactory.prototype.createMultiPointFromCoords = function createMultiPointFromCoords (coordinates) {
100334           return this.createMultiPoint(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null)
100335         };
100336         GeometryFactory.prototype.createPoint = function createPoint () {
100337           if (arguments.length === 0) {
100338             return this.createPoint(this.getCoordinateSequenceFactory().create([]))
100339           } else if (arguments.length === 1) {
100340             if (arguments[0] instanceof Coordinate) {
100341               var coordinate = arguments[0];
100342               return this.createPoint(coordinate !== null ? this.getCoordinateSequenceFactory().create([coordinate]) : null)
100343             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100344               var coordinates = arguments[0];
100345               return new Point$1(coordinates, this)
100346             }
100347           }
100348         };
100349         GeometryFactory.prototype.getCoordinateSequenceFactory = function getCoordinateSequenceFactory () {
100350           return this._coordinateSequenceFactory
100351         };
100352         GeometryFactory.prototype.createPolygon = function createPolygon () {
100353           if (arguments.length === 0) {
100354             return new Polygon(null, null, this)
100355           } else if (arguments.length === 1) {
100356             if (hasInterface(arguments[0], CoordinateSequence)) {
100357               var coordinates = arguments[0];
100358               return this.createPolygon(this.createLinearRing(coordinates))
100359             } else if (arguments[0] instanceof Array) {
100360               var coordinates$1 = arguments[0];
100361               return this.createPolygon(this.createLinearRing(coordinates$1))
100362             } else if (arguments[0] instanceof LinearRing) {
100363               var shell = arguments[0];
100364               return this.createPolygon(shell, null)
100365             }
100366           } else if (arguments.length === 2) {
100367             var shell$1 = arguments[0];
100368             var holes = arguments[1];
100369             return new Polygon(shell$1, holes, this)
100370           }
100371         };
100372         GeometryFactory.prototype.getSRID = function getSRID () {
100373           return this._SRID
100374         };
100375         GeometryFactory.prototype.createGeometryCollection = function createGeometryCollection () {
100376           if (arguments.length === 0) {
100377             return new GeometryCollection(null, this)
100378           } else if (arguments.length === 1) {
100379             var geometries = arguments[0];
100380             return new GeometryCollection(geometries, this)
100381           }
100382         };
100383         GeometryFactory.prototype.createGeometry = function createGeometry (g) {
100384           var editor = new GeometryEditor(this);
100385           return editor.edit(g, {
100386             edit: function () {
100387               if (arguments.length === 2) {
100388                 var coordSeq = arguments[0];
100389                 // const geometry = arguments[1]
100390                 return this._coordinateSequenceFactory.create(coordSeq)
100391               }
100392             }
100393           })
100394         };
100395         GeometryFactory.prototype.getPrecisionModel = function getPrecisionModel () {
100396           return this._precisionModel
100397         };
100398         GeometryFactory.prototype.createLinearRing = function createLinearRing () {
100399           if (arguments.length === 0) {
100400             return this.createLinearRing(this.getCoordinateSequenceFactory().create([]))
100401           } else if (arguments.length === 1) {
100402             if (arguments[0] instanceof Array) {
100403               var coordinates = arguments[0];
100404               return this.createLinearRing(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null)
100405             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100406               var coordinates$1 = arguments[0];
100407               return new LinearRing(coordinates$1, this)
100408             }
100409           }
100410         };
100411         GeometryFactory.prototype.createMultiPolygon = function createMultiPolygon () {
100412           if (arguments.length === 0) {
100413             return new MultiPolygon(null, this)
100414           } else if (arguments.length === 1) {
100415             var polygons = arguments[0];
100416             return new MultiPolygon(polygons, this)
100417           }
100418         };
100419         GeometryFactory.prototype.createMultiPoint = function createMultiPoint () {
100420             var this$1 = this;
100421
100422           if (arguments.length === 0) {
100423             return new MultiPoint(null, this)
100424           } else if (arguments.length === 1) {
100425             if (arguments[0] instanceof Array) {
100426               var point = arguments[0];
100427               return new MultiPoint(point, this)
100428             } else if (arguments[0] instanceof Array) {
100429               var coordinates = arguments[0];
100430               return this.createMultiPoint(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null)
100431             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100432               var coordinates$1 = arguments[0];
100433               if (coordinates$1 === null) {
100434                 return this.createMultiPoint(new Array(0).fill(null))
100435               }
100436               var points = new Array(coordinates$1.size()).fill(null);
100437               for (var i = 0; i < coordinates$1.size(); i++) {
100438                 var ptSeq = this$1.getCoordinateSequenceFactory().create(1, coordinates$1.getDimension());
100439                 CoordinateSequences.copy(coordinates$1, i, ptSeq, 0, 1);
100440                 points[i] = this$1.createPoint(ptSeq);
100441               }
100442               return this.createMultiPoint(points)
100443             }
100444           }
100445         };
100446         GeometryFactory.prototype.interfaces_ = function interfaces_ () {
100447           return [Serializable]
100448         };
100449         GeometryFactory.prototype.getClass = function getClass () {
100450           return GeometryFactory
100451         };
100452         GeometryFactory.toMultiPolygonArray = function toMultiPolygonArray (multiPolygons) {
100453           var multiPolygonArray = new Array(multiPolygons.size()).fill(null);
100454           return multiPolygons.toArray(multiPolygonArray)
100455         };
100456         GeometryFactory.toGeometryArray = function toGeometryArray (geometries) {
100457           if (geometries === null) { return null }
100458           var geometryArray = new Array(geometries.size()).fill(null);
100459           return geometries.toArray(geometryArray)
100460         };
100461         GeometryFactory.getDefaultCoordinateSequenceFactory = function getDefaultCoordinateSequenceFactory () {
100462           return CoordinateArraySequenceFactory.instance()
100463         };
100464         GeometryFactory.toMultiLineStringArray = function toMultiLineStringArray (multiLineStrings) {
100465           var multiLineStringArray = new Array(multiLineStrings.size()).fill(null);
100466           return multiLineStrings.toArray(multiLineStringArray)
100467         };
100468         GeometryFactory.toLineStringArray = function toLineStringArray (lineStrings) {
100469           var lineStringArray = new Array(lineStrings.size()).fill(null);
100470           return lineStrings.toArray(lineStringArray)
100471         };
100472         GeometryFactory.toMultiPointArray = function toMultiPointArray (multiPoints) {
100473           var multiPointArray = new Array(multiPoints.size()).fill(null);
100474           return multiPoints.toArray(multiPointArray)
100475         };
100476         GeometryFactory.toLinearRingArray = function toLinearRingArray (linearRings) {
100477           var linearRingArray = new Array(linearRings.size()).fill(null);
100478           return linearRings.toArray(linearRingArray)
100479         };
100480         GeometryFactory.toPointArray = function toPointArray (points) {
100481           var pointArray = new Array(points.size()).fill(null);
100482           return points.toArray(pointArray)
100483         };
100484         GeometryFactory.toPolygonArray = function toPolygonArray (polygons) {
100485           var polygonArray = new Array(polygons.size()).fill(null);
100486           return polygons.toArray(polygonArray)
100487         };
100488         GeometryFactory.createPointFromInternalCoord = function createPointFromInternalCoord (coord, exemplar) {
100489           exemplar.getPrecisionModel().makePrecise(coord);
100490           return exemplar.getFactory().createPoint(coord)
100491         };
100492         staticAccessors$2.serialVersionUID.get = function () { return -6820524753094095635 };
100493
100494         Object.defineProperties( GeometryFactory, staticAccessors$2 );
100495
100496         var geometryTypes = ['Point', 'MultiPoint', 'LineString', 'MultiLineString', 'Polygon', 'MultiPolygon'];
100497
100498         /**
100499          * Class for reading and writing Well-Known Text.Create a new parser for GeoJSON
100500          * NOTE: Adapted from OpenLayers 2.11 implementation.
100501          */
100502
100503         /**
100504          * Create a new parser for GeoJSON
100505          *
100506          * @param {GeometryFactory} geometryFactory
100507          * @return An instance of GeoJsonParser.
100508          * @constructor
100509          * @private
100510          */
100511         var GeoJSONParser = function GeoJSONParser (geometryFactory) {
100512           this.geometryFactory = geometryFactory || new GeometryFactory();
100513         };
100514         /**
100515          * Deserialize a GeoJSON object and return the Geometry or Feature(Collection) with JSTS Geometries
100516          *
100517          * @param {}
100518          *        A GeoJSON object.
100519          * @return {} A Geometry instance or object representing a Feature(Collection) with Geometry instances.
100520          * @private
100521          */
100522         GeoJSONParser.prototype.read = function read (json) {
100523           var obj;
100524           if (typeof json === 'string') {
100525             obj = JSON.parse(json);
100526           } else {
100527             obj = json;
100528           }
100529
100530           var type = obj.type;
100531
100532           if (!parse$2[type]) {
100533             throw new Error('Unknown GeoJSON type: ' + obj.type)
100534           }
100535
100536           if (geometryTypes.indexOf(type) !== -1) {
100537             return parse$2[type].apply(this, [obj.coordinates])
100538           } else if (type === 'GeometryCollection') {
100539             return parse$2[type].apply(this, [obj.geometries])
100540           }
100541
100542           // feature or feature collection
100543           return parse$2[type].apply(this, [obj])
100544         };
100545
100546         /**
100547          * Serialize a Geometry object into GeoJSON
100548          *
100549          * @param {Geometry}
100550          *        geometry A Geometry or array of Geometries.
100551          * @return {Object} A GeoJSON object represting the input Geometry/Geometries.
100552          * @private
100553          */
100554         GeoJSONParser.prototype.write = function write (geometry) {
100555           var type = geometry.getGeometryType();
100556
100557           if (!extract[type]) {
100558             throw new Error('Geometry is not supported')
100559           }
100560
100561           return extract[type].apply(this, [geometry])
100562         };
100563
100564         var parse$2 = {
100565           /**
100566            * Parse a GeoJSON Feature object
100567            *
100568            * @param {Object}
100569            *          obj Object to parse.
100570            *
100571            * @return {Object} Feature with geometry/bbox converted to JSTS Geometries.
100572            */
100573           Feature: function (obj) {
100574             var feature = {};
100575
100576             // copy features
100577             for (var key in obj) {
100578               feature[key] = obj[key];
100579             }
100580
100581             // parse geometry
100582             if (obj.geometry) {
100583               var type = obj.geometry.type;
100584               if (!parse$2[type]) {
100585                 throw new Error('Unknown GeoJSON type: ' + obj.type)
100586               }
100587               feature.geometry = this.read(obj.geometry);
100588             }
100589
100590             // bbox
100591             if (obj.bbox) {
100592               feature.bbox = parse$2.bbox.apply(this, [obj.bbox]);
100593             }
100594
100595             return feature
100596           },
100597
100598           /**
100599            * Parse a GeoJSON FeatureCollection object
100600            *
100601            * @param {Object}
100602            *          obj Object to parse.
100603            *
100604            * @return {Object} FeatureCollection with geometry/bbox converted to JSTS Geometries.
100605            */
100606           FeatureCollection: function (obj) {
100607             var this$1 = this;
100608
100609             var featureCollection = {};
100610
100611             if (obj.features) {
100612               featureCollection.features = [];
100613
100614               for (var i = 0; i < obj.features.length; ++i) {
100615                 featureCollection.features.push(this$1.read(obj.features[i]));
100616               }
100617             }
100618
100619             if (obj.bbox) {
100620               featureCollection.bbox = this.parse.bbox.apply(this, [obj.bbox]);
100621             }
100622
100623             return featureCollection
100624           },
100625
100626           /**
100627            * Convert the ordinates in an array to an array of Coordinates
100628            *
100629            * @param {Array}
100630            *          array Array with {Number}s.
100631            *
100632            * @return {Array} Array with Coordinates.
100633            */
100634           coordinates: function (array) {
100635             var coordinates = [];
100636             for (var i = 0; i < array.length; ++i) {
100637               var sub = array[i];
100638               coordinates.push(new Coordinate(sub[0], sub[1]));
100639             }
100640             return coordinates
100641           },
100642
100643           /**
100644            * Convert the bbox to a LinearRing
100645            *
100646            * @param {Array}
100647            *          array Array with [xMin, yMin, xMax, yMax].
100648            *
100649            * @return {Array} Array with Coordinates.
100650            */
100651           bbox: function (array) {
100652             return this.geometryFactory.createLinearRing([
100653               new Coordinate(array[0], array[1]),
100654               new Coordinate(array[2], array[1]),
100655               new Coordinate(array[2], array[3]),
100656               new Coordinate(array[0], array[3]),
100657               new Coordinate(array[0], array[1])
100658             ])
100659           },
100660
100661           /**
100662            * Convert an Array with ordinates to a Point
100663            *
100664            * @param {Array}
100665            *          array Array with ordinates.
100666            *
100667            * @return {Point} Point.
100668            */
100669           Point: function (array) {
100670             var coordinate = new Coordinate(array[0], array[1]);
100671             return this.geometryFactory.createPoint(coordinate)
100672           },
100673
100674           /**
100675            * Convert an Array with coordinates to a MultiPoint
100676            *
100677            * @param {Array}
100678            *          array Array with coordinates.
100679            *
100680            * @return {MultiPoint} MultiPoint.
100681            */
100682           MultiPoint: function (array) {
100683             var this$1 = this;
100684
100685             var points = [];
100686             for (var i = 0; i < array.length; ++i) {
100687               points.push(parse$2.Point.apply(this$1, [array[i]]));
100688             }
100689             return this.geometryFactory.createMultiPoint(points)
100690           },
100691
100692           /**
100693            * Convert an Array with coordinates to a LineString
100694            *
100695            * @param {Array}
100696            *          array Array with coordinates.
100697            *
100698            * @return {LineString} LineString.
100699            */
100700           LineString: function (array) {
100701             var coordinates = parse$2.coordinates.apply(this, [array]);
100702             return this.geometryFactory.createLineString(coordinates)
100703           },
100704
100705           /**
100706            * Convert an Array with coordinates to a MultiLineString
100707            *
100708            * @param {Array}
100709            *          array Array with coordinates.
100710            *
100711            * @return {MultiLineString} MultiLineString.
100712            */
100713           MultiLineString: function (array) {
100714             var this$1 = this;
100715
100716             var lineStrings = [];
100717             for (var i = 0; i < array.length; ++i) {
100718               lineStrings.push(parse$2.LineString.apply(this$1, [array[i]]));
100719             }
100720             return this.geometryFactory.createMultiLineString(lineStrings)
100721           },
100722
100723           /**
100724            * Convert an Array to a Polygon
100725            *
100726            * @param {Array}
100727            *          array Array with shell and holes.
100728            *
100729            * @return {Polygon} Polygon.
100730            */
100731           Polygon: function (array) {
100732             var this$1 = this;
100733
100734             var shellCoordinates = parse$2.coordinates.apply(this, [array[0]]);
100735             var shell = this.geometryFactory.createLinearRing(shellCoordinates);
100736             var holes = [];
100737             for (var i = 1; i < array.length; ++i) {
100738               var hole = array[i];
100739               var coordinates = parse$2.coordinates.apply(this$1, [hole]);
100740               var linearRing = this$1.geometryFactory.createLinearRing(coordinates);
100741               holes.push(linearRing);
100742             }
100743             return this.geometryFactory.createPolygon(shell, holes)
100744           },
100745
100746           /**
100747            * Convert an Array to a MultiPolygon
100748            *
100749            * @param {Array}
100750            *          array Array of arrays with shell and rings.
100751            *
100752            * @return {MultiPolygon} MultiPolygon.
100753            */
100754           MultiPolygon: function (array) {
100755             var this$1 = this;
100756
100757             var polygons = [];
100758             for (var i = 0; i < array.length; ++i) {
100759               var polygon = array[i];
100760               polygons.push(parse$2.Polygon.apply(this$1, [polygon]));
100761             }
100762             return this.geometryFactory.createMultiPolygon(polygons)
100763           },
100764
100765           /**
100766            * Convert an Array to a GeometryCollection
100767            *
100768            * @param {Array}
100769            *          array Array of GeoJSON geometries.
100770            *
100771            * @return {GeometryCollection} GeometryCollection.
100772            */
100773           GeometryCollection: function (array) {
100774             var this$1 = this;
100775
100776             var geometries = [];
100777             for (var i = 0; i < array.length; ++i) {
100778               var geometry = array[i];
100779               geometries.push(this$1.read(geometry));
100780             }
100781             return this.geometryFactory.createGeometryCollection(geometries)
100782           }
100783         };
100784
100785         var extract = {
100786           /**
100787            * Convert a Coordinate to an Array
100788            *
100789            * @param {Coordinate}
100790            *          coordinate Coordinate to convert.
100791            *
100792            * @return {Array} Array of ordinates.
100793            */
100794           coordinate: function (coordinate) {
100795             return [coordinate.x, coordinate.y]
100796           },
100797
100798           /**
100799            * Convert a Point to a GeoJSON object
100800            *
100801            * @param {Point}
100802            *          point Point to convert.
100803            *
100804            * @return {Array} Array of 2 ordinates (paired to a coordinate).
100805            */
100806           Point: function (point) {
100807             var array = extract.coordinate.apply(this, [point.getCoordinate()]);
100808             return {
100809               type: 'Point',
100810               coordinates: array
100811             }
100812           },
100813
100814           /**
100815            * Convert a MultiPoint to a GeoJSON object
100816            *
100817            * @param {MultiPoint}
100818            *          multipoint MultiPoint to convert.
100819            *
100820            * @return {Array} Array of coordinates.
100821            */
100822           MultiPoint: function (multipoint) {
100823             var this$1 = this;
100824
100825             var array = [];
100826             for (var i = 0; i < multipoint._geometries.length; ++i) {
100827               var point = multipoint._geometries[i];
100828               var geoJson = extract.Point.apply(this$1, [point]);
100829               array.push(geoJson.coordinates);
100830             }
100831             return {
100832               type: 'MultiPoint',
100833               coordinates: array
100834             }
100835           },
100836
100837           /**
100838            * Convert a LineString to a GeoJSON object
100839            *
100840            * @param {LineString}
100841            *          linestring LineString to convert.
100842            *
100843            * @return {Array} Array of coordinates.
100844            */
100845           LineString: function (linestring) {
100846             var this$1 = this;
100847
100848             var array = [];
100849             var coordinates = linestring.getCoordinates();
100850             for (var i = 0; i < coordinates.length; ++i) {
100851               var coordinate = coordinates[i];
100852               array.push(extract.coordinate.apply(this$1, [coordinate]));
100853             }
100854             return {
100855               type: 'LineString',
100856               coordinates: array
100857             }
100858           },
100859
100860           /**
100861            * Convert a MultiLineString to a GeoJSON object
100862            *
100863            * @param {MultiLineString}
100864            *          multilinestring MultiLineString to convert.
100865            *
100866            * @return {Array} Array of Array of coordinates.
100867            */
100868           MultiLineString: function (multilinestring) {
100869             var this$1 = this;
100870
100871             var array = [];
100872             for (var i = 0; i < multilinestring._geometries.length; ++i) {
100873               var linestring = multilinestring._geometries[i];
100874               var geoJson = extract.LineString.apply(this$1, [linestring]);
100875               array.push(geoJson.coordinates);
100876             }
100877             return {
100878               type: 'MultiLineString',
100879               coordinates: array
100880             }
100881           },
100882
100883           /**
100884            * Convert a Polygon to a GeoJSON object
100885            *
100886            * @param {Polygon}
100887            *          polygon Polygon to convert.
100888            *
100889            * @return {Array} Array with shell, holes.
100890            */
100891           Polygon: function (polygon) {
100892             var this$1 = this;
100893
100894             var array = [];
100895             var shellGeoJson = extract.LineString.apply(this, [polygon._shell]);
100896             array.push(shellGeoJson.coordinates);
100897             for (var i = 0; i < polygon._holes.length; ++i) {
100898               var hole = polygon._holes[i];
100899               var holeGeoJson = extract.LineString.apply(this$1, [hole]);
100900               array.push(holeGeoJson.coordinates);
100901             }
100902             return {
100903               type: 'Polygon',
100904               coordinates: array
100905             }
100906           },
100907
100908           /**
100909            * Convert a MultiPolygon to a GeoJSON object
100910            *
100911            * @param {MultiPolygon}
100912            *          multipolygon MultiPolygon to convert.
100913            *
100914            * @return {Array} Array of polygons.
100915            */
100916           MultiPolygon: function (multipolygon) {
100917             var this$1 = this;
100918
100919             var array = [];
100920             for (var i = 0; i < multipolygon._geometries.length; ++i) {
100921               var polygon = multipolygon._geometries[i];
100922               var geoJson = extract.Polygon.apply(this$1, [polygon]);
100923               array.push(geoJson.coordinates);
100924             }
100925             return {
100926               type: 'MultiPolygon',
100927               coordinates: array
100928             }
100929           },
100930
100931           /**
100932            * Convert a GeometryCollection to a GeoJSON object
100933            *
100934            * @param {GeometryCollection}
100935            *          collection GeometryCollection to convert.
100936            *
100937            * @return {Array} Array of geometries.
100938            */
100939           GeometryCollection: function (collection) {
100940             var this$1 = this;
100941
100942             var array = [];
100943             for (var i = 0; i < collection._geometries.length; ++i) {
100944               var geometry = collection._geometries[i];
100945               var type = geometry.getGeometryType();
100946               array.push(extract[type].apply(this$1, [geometry]));
100947             }
100948             return {
100949               type: 'GeometryCollection',
100950               geometries: array
100951             }
100952           }
100953         };
100954
100955         /**
100956          * Converts a geometry in GeoJSON to a {@link Geometry}.
100957          */
100958
100959         /**
100960          * A <code>GeoJSONReader</code> is parameterized by a <code>GeometryFactory</code>,
100961          * to allow it to create <code>Geometry</code> objects of the appropriate
100962          * implementation. In particular, the <code>GeometryFactory</code> determines
100963          * the <code>PrecisionModel</code> and <code>SRID</code> that is used.
100964          *
100965          * @param {GeometryFactory} geometryFactory
100966          * @constructor
100967          */
100968         var GeoJSONReader = function GeoJSONReader (geometryFactory) {
100969           this.geometryFactory = geometryFactory || new GeometryFactory();
100970           this.precisionModel = this.geometryFactory.getPrecisionModel();
100971           this.parser = new GeoJSONParser(this.geometryFactory);
100972         };
100973         /**
100974          * Reads a GeoJSON representation of a {@link Geometry}
100975          *
100976          * Will also parse GeoJSON Features/FeatureCollections as custom objects.
100977          *
100978          * @param {Object|String} geoJson a GeoJSON Object or String.
100979          * @return {Geometry|Object} a <code>Geometry or Feature/FeatureCollection representation.</code>
100980          * @memberof GeoJSONReader
100981          */
100982         GeoJSONReader.prototype.read = function read (geoJson) {
100983           var geometry = this.parser.read(geoJson);
100984
100985           if (this.precisionModel.getType() === PrecisionModel.FIXED) {
100986             this.reducePrecision(geometry);
100987           }
100988
100989           return geometry
100990         };
100991
100992         // NOTE: this is a hack
100993         GeoJSONReader.prototype.reducePrecision = function reducePrecision (geometry) {
100994             var this$1 = this;
100995
100996           var i, len;
100997
100998           if (geometry.coordinate) {
100999             this.precisionModel.makePrecise(geometry.coordinate);
101000           } else if (geometry.points) {
101001             for (i = 0, len = geometry.points.length; i < len; i++) {
101002               this$1.precisionModel.makePrecise(geometry.points[i]);
101003             }
101004           } else if (geometry.geometries) {
101005             for (i = 0, len = geometry.geometries.length; i < len; i++) {
101006               this$1.reducePrecision(geometry.geometries[i]);
101007             }
101008           }
101009         };
101010
101011         /**
101012          * @module GeoJSONWriter
101013          */
101014
101015         /**
101016          * Writes the GeoJSON representation of a {@link Geometry}. The
101017          * The GeoJSON format is defined <A
101018          * HREF="http://geojson.org/geojson-spec.html">here</A>.
101019          */
101020
101021         /**
101022          * The <code>GeoJSONWriter</code> outputs coordinates rounded to the precision
101023          * model. Only the maximum number of decimal places necessary to represent the
101024          * ordinates to the required precision will be output.
101025          *
101026          * @param {GeometryFactory} geometryFactory
101027          * @constructor
101028          */
101029         var GeoJSONWriter = function GeoJSONWriter () {
101030           this.parser = new GeoJSONParser(this.geometryFactory);
101031         };
101032         /**
101033          * Converts a <code>Geometry</code> to its GeoJSON representation.
101034          *
101035          * @param {Geometry}
101036          *        geometry a <code>Geometry</code> to process.
101037          * @return {Object} The GeoJSON representation of the Geometry.
101038          * @memberof GeoJSONWriter
101039          */
101040         GeoJSONWriter.prototype.write = function write (geometry) {
101041           return this.parser.write(geometry)
101042         };
101043
101044         /* eslint-disable no-undef */
101045
101046         // io
101047
101048         var Position = function Position () {};
101049
101050         var staticAccessors$20 = { ON: { configurable: true },LEFT: { configurable: true },RIGHT: { configurable: true } };
101051
101052         Position.prototype.interfaces_ = function interfaces_ () {
101053           return []
101054         };
101055         Position.prototype.getClass = function getClass () {
101056           return Position
101057         };
101058         Position.opposite = function opposite (position) {
101059           if (position === Position.LEFT) { return Position.RIGHT }
101060           if (position === Position.RIGHT) { return Position.LEFT }
101061           return position
101062         };
101063         staticAccessors$20.ON.get = function () { return 0 };
101064         staticAccessors$20.LEFT.get = function () { return 1 };
101065         staticAccessors$20.RIGHT.get = function () { return 2 };
101066
101067         Object.defineProperties( Position, staticAccessors$20 );
101068
101069         /**
101070          * @param {string=} message Optional message
101071          * @extends {Error}
101072          * @constructor
101073          * @private
101074          */
101075         function EmptyStackException (message) {
101076           this.message = message || '';
101077         }
101078         EmptyStackException.prototype = new Error();
101079
101080         /**
101081          * @type {string}
101082          */
101083         EmptyStackException.prototype.name = 'EmptyStackException';
101084
101085         /**
101086          * @see http://download.oracle.com/javase/6/docs/api/java/util/Stack.html
101087          *
101088          * @extends {List}
101089          * @constructor
101090          * @private
101091          */
101092         function Stack () {
101093           /**
101094            * @type {Array}
101095            * @private
101096            */
101097           this.array_ = [];
101098         }
101099         Stack.prototype = new List();
101100
101101         /**
101102          * @override
101103          */
101104         Stack.prototype.add = function (e) {
101105           this.array_.push(e);
101106           return true
101107         };
101108
101109         /**
101110          * @override
101111          */
101112         Stack.prototype.get = function (index) {
101113           if (index < 0 || index >= this.size()) {
101114             throw new Error()
101115           }
101116
101117           return this.array_[index]
101118         };
101119
101120         /**
101121          * Pushes an item onto the top of this stack.
101122          * @param {Object} e
101123          * @return {Object}
101124          */
101125         Stack.prototype.push = function (e) {
101126           this.array_.push(e);
101127           return e
101128         };
101129
101130         /**
101131          * Pushes an item onto the top of this stack.
101132          * @param {Object} e
101133          * @return {Object}
101134          */
101135         Stack.prototype.pop = function (e) {
101136           if (this.array_.length === 0) {
101137             throw new EmptyStackException()
101138           }
101139
101140           return this.array_.pop()
101141         };
101142
101143         /**
101144          * Looks at the object at the top of this stack without removing it from the
101145          * stack.
101146          * @return {Object}
101147          */
101148         Stack.prototype.peek = function () {
101149           if (this.array_.length === 0) {
101150             throw new EmptyStackException()
101151           }
101152
101153           return this.array_[this.array_.length - 1]
101154         };
101155
101156         /**
101157          * Tests if this stack is empty.
101158          * @return {boolean} true if and only if this stack contains no items; false
101159          *         otherwise.
101160          */
101161         Stack.prototype.empty = function () {
101162           if (this.array_.length === 0) {
101163             return true
101164           } else {
101165             return false
101166           }
101167         };
101168
101169         /**
101170          * @return {boolean}
101171          */
101172         Stack.prototype.isEmpty = function () {
101173           return this.empty()
101174         };
101175
101176         /**
101177          * Returns the 1-based position where an object is on this stack. If the object
101178          * o occurs as an item in this stack, this method returns the distance from the
101179          * top of the stack of the occurrence nearest the top of the stack; the topmost
101180          * item on the stack is considered to be at distance 1. The equals method is
101181          * used to compare o to the items in this stack.
101182          *
101183          * NOTE: does not currently actually use equals. (=== is used)
101184          *
101185          * @param {Object} o
101186          * @return {number} the 1-based position from the top of the stack where the
101187          *         object is located; the return value -1 indicates that the object is
101188          *         not on the stack.
101189          */
101190         Stack.prototype.search = function (o) {
101191           return this.array_.indexOf(o)
101192         };
101193
101194         /**
101195          * @return {number}
101196          * @export
101197          */
101198         Stack.prototype.size = function () {
101199           return this.array_.length
101200         };
101201
101202         /**
101203          * @return {Array}
101204          */
101205         Stack.prototype.toArray = function () {
101206           var this$1 = this;
101207
101208           var array = [];
101209
101210           for (var i = 0, len = this.array_.length; i < len; i++) {
101211             array.push(this$1.array_[i]);
101212           }
101213
101214           return array
101215         };
101216
101217         var RightmostEdgeFinder = function RightmostEdgeFinder () {
101218           this._minIndex = -1;
101219           this._minCoord = null;
101220           this._minDe = null;
101221           this._orientedDe = null;
101222         };
101223         RightmostEdgeFinder.prototype.getCoordinate = function getCoordinate () {
101224           return this._minCoord
101225         };
101226         RightmostEdgeFinder.prototype.getRightmostSide = function getRightmostSide (de, index) {
101227           var side = this.getRightmostSideOfSegment(de, index);
101228           if (side < 0) { side = this.getRightmostSideOfSegment(de, index - 1); }
101229           if (side < 0) {
101230             this._minCoord = null;
101231             this.checkForRightmostCoordinate(de);
101232           }
101233           return side
101234         };
101235         RightmostEdgeFinder.prototype.findRightmostEdgeAtVertex = function findRightmostEdgeAtVertex () {
101236           var pts = this._minDe.getEdge().getCoordinates();
101237           Assert.isTrue(this._minIndex > 0 && this._minIndex < pts.length, 'rightmost point expected to be interior vertex of edge');
101238           var pPrev = pts[this._minIndex - 1];
101239           var pNext = pts[this._minIndex + 1];
101240           var orientation = CGAlgorithms.computeOrientation(this._minCoord, pNext, pPrev);
101241           var usePrev = false;
101242           if (pPrev.y < this._minCoord.y && pNext.y < this._minCoord.y && orientation === CGAlgorithms.COUNTERCLOCKWISE) {
101243             usePrev = true;
101244           } else if (pPrev.y > this._minCoord.y && pNext.y > this._minCoord.y && orientation === CGAlgorithms.CLOCKWISE) {
101245             usePrev = true;
101246           }
101247           if (usePrev) {
101248             this._minIndex = this._minIndex - 1;
101249           }
101250         };
101251         RightmostEdgeFinder.prototype.getRightmostSideOfSegment = function getRightmostSideOfSegment (de, i) {
101252           var e = de.getEdge();
101253           var coord = e.getCoordinates();
101254           if (i < 0 || i + 1 >= coord.length) { return -1 }
101255           if (coord[i].y === coord[i + 1].y) { return -1 }
101256           var pos = Position.LEFT;
101257           if (coord[i].y < coord[i + 1].y) { pos = Position.RIGHT; }
101258           return pos
101259         };
101260         RightmostEdgeFinder.prototype.getEdge = function getEdge () {
101261           return this._orientedDe
101262         };
101263         RightmostEdgeFinder.prototype.checkForRightmostCoordinate = function checkForRightmostCoordinate (de) {
101264             var this$1 = this;
101265
101266           var coord = de.getEdge().getCoordinates();
101267           for (var i = 0; i < coord.length - 1; i++) {
101268             if (this$1._minCoord === null || coord[i].x > this$1._minCoord.x) {
101269               this$1._minDe = de;
101270               this$1._minIndex = i;
101271               this$1._minCoord = coord[i];
101272             }
101273           }
101274         };
101275         RightmostEdgeFinder.prototype.findRightmostEdgeAtNode = function findRightmostEdgeAtNode () {
101276           var node = this._minDe.getNode();
101277           var star = node.getEdges();
101278           this._minDe = star.getRightmostEdge();
101279           if (!this._minDe.isForward()) {
101280             this._minDe = this._minDe.getSym();
101281             this._minIndex = this._minDe.getEdge().getCoordinates().length - 1;
101282           }
101283         };
101284         RightmostEdgeFinder.prototype.findEdge = function findEdge (dirEdgeList) {
101285             var this$1 = this;
101286
101287           for (var i = dirEdgeList.iterator(); i.hasNext();) {
101288             var de = i.next();
101289             if (!de.isForward()) { continue }
101290             this$1.checkForRightmostCoordinate(de);
101291           }
101292           Assert.isTrue(this._minIndex !== 0 || this._minCoord.equals(this._minDe.getCoordinate()), 'inconsistency in rightmost processing');
101293           if (this._minIndex === 0) {
101294             this.findRightmostEdgeAtNode();
101295           } else {
101296             this.findRightmostEdgeAtVertex();
101297           }
101298           this._orientedDe = this._minDe;
101299           var rightmostSide = this.getRightmostSide(this._minDe, this._minIndex);
101300           if (rightmostSide === Position.LEFT) {
101301             this._orientedDe = this._minDe.getSym();
101302           }
101303         };
101304         RightmostEdgeFinder.prototype.interfaces_ = function interfaces_ () {
101305           return []
101306         };
101307         RightmostEdgeFinder.prototype.getClass = function getClass () {
101308           return RightmostEdgeFinder
101309         };
101310
101311         var TopologyException = (function (RuntimeException$$1) {
101312           function TopologyException (msg, pt) {
101313             RuntimeException$$1.call(this, TopologyException.msgWithCoord(msg, pt));
101314             this.pt = pt ? new Coordinate(pt) : null;
101315             this.name = 'TopologyException';
101316           }
101317
101318           if ( RuntimeException$$1 ) { TopologyException.__proto__ = RuntimeException$$1; }
101319           TopologyException.prototype = Object.create( RuntimeException$$1 && RuntimeException$$1.prototype );
101320           TopologyException.prototype.constructor = TopologyException;
101321           TopologyException.prototype.getCoordinate = function getCoordinate () {
101322             return this.pt
101323           };
101324           TopologyException.prototype.interfaces_ = function interfaces_ () {
101325             return []
101326           };
101327           TopologyException.prototype.getClass = function getClass () {
101328             return TopologyException
101329           };
101330           TopologyException.msgWithCoord = function msgWithCoord (msg, pt) {
101331             if (!pt) { return msg + ' [ ' + pt + ' ]' }
101332             return msg
101333           };
101334
101335           return TopologyException;
101336         }(RuntimeException));
101337
101338         var LinkedList = function LinkedList () {
101339           this.array_ = [];
101340         };
101341         LinkedList.prototype.addLast = function addLast (e) {
101342           this.array_.push(e);
101343         };
101344         LinkedList.prototype.removeFirst = function removeFirst () {
101345           return this.array_.shift()
101346         };
101347         LinkedList.prototype.isEmpty = function isEmpty () {
101348           return this.array_.length === 0
101349         };
101350
101351         var BufferSubgraph = function BufferSubgraph () {
101352           this._finder = null;
101353           this._dirEdgeList = new ArrayList();
101354           this._nodes = new ArrayList();
101355           this._rightMostCoord = null;
101356           this._env = null;
101357           this._finder = new RightmostEdgeFinder();
101358         };
101359         BufferSubgraph.prototype.clearVisitedEdges = function clearVisitedEdges () {
101360           for (var it = this._dirEdgeList.iterator(); it.hasNext();) {
101361             var de = it.next();
101362             de.setVisited(false);
101363           }
101364         };
101365         BufferSubgraph.prototype.getRightmostCoordinate = function getRightmostCoordinate () {
101366           return this._rightMostCoord
101367         };
101368         BufferSubgraph.prototype.computeNodeDepth = function computeNodeDepth (n) {
101369             var this$1 = this;
101370
101371           var startEdge = null;
101372           for (var i = n.getEdges().iterator(); i.hasNext();) {
101373             var de = i.next();
101374             if (de.isVisited() || de.getSym().isVisited()) {
101375               startEdge = de;
101376               break
101377             }
101378           }
101379           if (startEdge === null) { throw new TopologyException('unable to find edge to compute depths at ' + n.getCoordinate()) }
101380           n.getEdges().computeDepths(startEdge);
101381           for (var i$1 = n.getEdges().iterator(); i$1.hasNext();) {
101382             var de$1 = i$1.next();
101383             de$1.setVisited(true);
101384             this$1.copySymDepths(de$1);
101385           }
101386         };
101387         BufferSubgraph.prototype.computeDepth = function computeDepth (outsideDepth) {
101388           this.clearVisitedEdges();
101389           var de = this._finder.getEdge();
101390           // const n = de.getNode()
101391           // const label = de.getLabel()
101392           de.setEdgeDepths(Position.RIGHT, outsideDepth);
101393           this.copySymDepths(de);
101394           this.computeDepths(de);
101395         };
101396         BufferSubgraph.prototype.create = function create (node) {
101397           this.addReachable(node);
101398           this._finder.findEdge(this._dirEdgeList);
101399           this._rightMostCoord = this._finder.getCoordinate();
101400         };
101401         BufferSubgraph.prototype.findResultEdges = function findResultEdges () {
101402           for (var it = this._dirEdgeList.iterator(); it.hasNext();) {
101403             var de = it.next();
101404             if (de.getDepth(Position.RIGHT) >= 1 && de.getDepth(Position.LEFT) <= 0 && !de.isInteriorAreaEdge()) {
101405               de.setInResult(true);
101406             }
101407           }
101408         };
101409         BufferSubgraph.prototype.computeDepths = function computeDepths (startEdge) {
101410             var this$1 = this;
101411
101412           var nodesVisited = new HashSet();
101413           var nodeQueue = new LinkedList();
101414           var startNode = startEdge.getNode();
101415           nodeQueue.addLast(startNode);
101416           nodesVisited.add(startNode);
101417           startEdge.setVisited(true);
101418           while (!nodeQueue.isEmpty()) {
101419             var n = nodeQueue.removeFirst();
101420             nodesVisited.add(n);
101421             this$1.computeNodeDepth(n);
101422             for (var i = n.getEdges().iterator(); i.hasNext();) {
101423               var de = i.next();
101424               var sym = de.getSym();
101425               if (sym.isVisited()) { continue }
101426               var adjNode = sym.getNode();
101427               if (!nodesVisited.contains(adjNode)) {
101428                 nodeQueue.addLast(adjNode);
101429                 nodesVisited.add(adjNode);
101430               }
101431             }
101432           }
101433         };
101434         BufferSubgraph.prototype.compareTo = function compareTo (o) {
101435           var graph = o;
101436           if (this._rightMostCoord.x < graph._rightMostCoord.x) {
101437             return -1
101438           }
101439           if (this._rightMostCoord.x > graph._rightMostCoord.x) {
101440             return 1
101441           }
101442           return 0
101443         };
101444         BufferSubgraph.prototype.getEnvelope = function getEnvelope () {
101445           if (this._env === null) {
101446             var edgeEnv = new Envelope();
101447             for (var it = this._dirEdgeList.iterator(); it.hasNext();) {
101448               var dirEdge = it.next();
101449               var pts = dirEdge.getEdge().getCoordinates();
101450               for (var i = 0; i < pts.length - 1; i++) {
101451                 edgeEnv.expandToInclude(pts[i]);
101452               }
101453             }
101454             this._env = edgeEnv;
101455           }
101456           return this._env
101457         };
101458         BufferSubgraph.prototype.addReachable = function addReachable (startNode) {
101459             var this$1 = this;
101460
101461           var nodeStack = new Stack();
101462           nodeStack.add(startNode);
101463           while (!nodeStack.empty()) {
101464             var node = nodeStack.pop();
101465             this$1.add(node, nodeStack);
101466           }
101467         };
101468         BufferSubgraph.prototype.copySymDepths = function copySymDepths (de) {
101469           var sym = de.getSym();
101470           sym.setDepth(Position.LEFT, de.getDepth(Position.RIGHT));
101471           sym.setDepth(Position.RIGHT, de.getDepth(Position.LEFT));
101472         };
101473         BufferSubgraph.prototype.add = function add (node, nodeStack) {
101474             var this$1 = this;
101475
101476           node.setVisited(true);
101477           this._nodes.add(node);
101478           for (var i = node.getEdges().iterator(); i.hasNext();) {
101479             var de = i.next();
101480             this$1._dirEdgeList.add(de);
101481             var sym = de.getSym();
101482             var symNode = sym.getNode();
101483             if (!symNode.isVisited()) { nodeStack.push(symNode); }
101484           }
101485         };
101486         BufferSubgraph.prototype.getNodes = function getNodes () {
101487           return this._nodes
101488         };
101489         BufferSubgraph.prototype.getDirectedEdges = function getDirectedEdges () {
101490           return this._dirEdgeList
101491         };
101492         BufferSubgraph.prototype.interfaces_ = function interfaces_ () {
101493           return [Comparable]
101494         };
101495         BufferSubgraph.prototype.getClass = function getClass () {
101496           return BufferSubgraph
101497         };
101498
101499         var TopologyLocation = function TopologyLocation () {
101500           var this$1 = this;
101501
101502           this.location = null;
101503           if (arguments.length === 1) {
101504             if (arguments[0] instanceof Array) {
101505               var location = arguments[0];
101506               this.init(location.length);
101507             } else if (Number.isInteger(arguments[0])) {
101508               var on = arguments[0];
101509               this.init(1);
101510               this.location[Position.ON] = on;
101511             } else if (arguments[0] instanceof TopologyLocation) {
101512               var gl = arguments[0];
101513               this.init(gl.location.length);
101514               if (gl !== null) {
101515                 for (var i = 0; i < this.location.length; i++) {
101516                   this$1.location[i] = gl.location[i];
101517                 }
101518               }
101519             }
101520           } else if (arguments.length === 3) {
101521             var on$1 = arguments[0];
101522             var left = arguments[1];
101523             var right = arguments[2];
101524             this.init(3);
101525             this.location[Position.ON] = on$1;
101526             this.location[Position.LEFT] = left;
101527             this.location[Position.RIGHT] = right;
101528           }
101529         };
101530         TopologyLocation.prototype.setAllLocations = function setAllLocations (locValue) {
101531             var this$1 = this;
101532
101533           for (var i = 0; i < this.location.length; i++) {
101534             this$1.location[i] = locValue;
101535           }
101536         };
101537         TopologyLocation.prototype.isNull = function isNull () {
101538             var this$1 = this;
101539
101540           for (var i = 0; i < this.location.length; i++) {
101541             if (this$1.location[i] !== Location.NONE) { return false }
101542           }
101543           return true
101544         };
101545         TopologyLocation.prototype.setAllLocationsIfNull = function setAllLocationsIfNull (locValue) {
101546             var this$1 = this;
101547
101548           for (var i = 0; i < this.location.length; i++) {
101549             if (this$1.location[i] === Location.NONE) { this$1.location[i] = locValue; }
101550           }
101551         };
101552         TopologyLocation.prototype.isLine = function isLine () {
101553           return this.location.length === 1
101554         };
101555         TopologyLocation.prototype.merge = function merge (gl) {
101556             var this$1 = this;
101557
101558           if (gl.location.length > this.location.length) {
101559             var newLoc = new Array(3).fill(null);
101560             newLoc[Position.ON] = this.location[Position.ON];
101561             newLoc[Position.LEFT] = Location.NONE;
101562             newLoc[Position.RIGHT] = Location.NONE;
101563             this.location = newLoc;
101564           }
101565           for (var i = 0; i < this.location.length; i++) {
101566             if (this$1.location[i] === Location.NONE && i < gl.location.length) { this$1.location[i] = gl.location[i]; }
101567           }
101568         };
101569         TopologyLocation.prototype.getLocations = function getLocations () {
101570           return this.location
101571         };
101572         TopologyLocation.prototype.flip = function flip () {
101573           if (this.location.length <= 1) { return null }
101574           var temp = this.location[Position.LEFT];
101575           this.location[Position.LEFT] = this.location[Position.RIGHT];
101576           this.location[Position.RIGHT] = temp;
101577         };
101578         TopologyLocation.prototype.toString = function toString () {
101579           var buf = new StringBuffer();
101580           if (this.location.length > 1) { buf.append(Location.toLocationSymbol(this.location[Position.LEFT])); }
101581           buf.append(Location.toLocationSymbol(this.location[Position.ON]));
101582           if (this.location.length > 1) { buf.append(Location.toLocationSymbol(this.location[Position.RIGHT])); }
101583           return buf.toString()
101584         };
101585         TopologyLocation.prototype.setLocations = function setLocations (on, left, right) {
101586           this.location[Position.ON] = on;
101587           this.location[Position.LEFT] = left;
101588           this.location[Position.RIGHT] = right;
101589         };
101590         TopologyLocation.prototype.get = function get (posIndex) {
101591           if (posIndex < this.location.length) { return this.location[posIndex] }
101592           return Location.NONE
101593         };
101594         TopologyLocation.prototype.isArea = function isArea () {
101595           return this.location.length > 1
101596         };
101597         TopologyLocation.prototype.isAnyNull = function isAnyNull () {
101598             var this$1 = this;
101599
101600           for (var i = 0; i < this.location.length; i++) {
101601             if (this$1.location[i] === Location.NONE) { return true }
101602           }
101603           return false
101604         };
101605         TopologyLocation.prototype.setLocation = function setLocation () {
101606           if (arguments.length === 1) {
101607             var locValue = arguments[0];
101608             this.setLocation(Position.ON, locValue);
101609           } else if (arguments.length === 2) {
101610             var locIndex = arguments[0];
101611             var locValue$1 = arguments[1];
101612             this.location[locIndex] = locValue$1;
101613           }
101614         };
101615         TopologyLocation.prototype.init = function init (size) {
101616           this.location = new Array(size).fill(null);
101617           this.setAllLocations(Location.NONE);
101618         };
101619         TopologyLocation.prototype.isEqualOnSide = function isEqualOnSide (le, locIndex) {
101620           return this.location[locIndex] === le.location[locIndex]
101621         };
101622         TopologyLocation.prototype.allPositionsEqual = function allPositionsEqual (loc) {
101623             var this$1 = this;
101624
101625           for (var i = 0; i < this.location.length; i++) {
101626             if (this$1.location[i] !== loc) { return false }
101627           }
101628           return true
101629         };
101630         TopologyLocation.prototype.interfaces_ = function interfaces_ () {
101631           return []
101632         };
101633         TopologyLocation.prototype.getClass = function getClass () {
101634           return TopologyLocation
101635         };
101636
101637         var Label = function Label () {
101638           this.elt = new Array(2).fill(null);
101639           if (arguments.length === 1) {
101640             if (Number.isInteger(arguments[0])) {
101641               var onLoc = arguments[0];
101642               this.elt[0] = new TopologyLocation(onLoc);
101643               this.elt[1] = new TopologyLocation(onLoc);
101644             } else if (arguments[0] instanceof Label) {
101645               var lbl = arguments[0];
101646               this.elt[0] = new TopologyLocation(lbl.elt[0]);
101647               this.elt[1] = new TopologyLocation(lbl.elt[1]);
101648             }
101649           } else if (arguments.length === 2) {
101650             var geomIndex = arguments[0];
101651             var onLoc$1 = arguments[1];
101652             this.elt[0] = new TopologyLocation(Location.NONE);
101653             this.elt[1] = new TopologyLocation(Location.NONE);
101654             this.elt[geomIndex].setLocation(onLoc$1);
101655           } else if (arguments.length === 3) {
101656             var onLoc$2 = arguments[0];
101657             var leftLoc = arguments[1];
101658             var rightLoc = arguments[2];
101659             this.elt[0] = new TopologyLocation(onLoc$2, leftLoc, rightLoc);
101660             this.elt[1] = new TopologyLocation(onLoc$2, leftLoc, rightLoc);
101661           } else if (arguments.length === 4) {
101662             var geomIndex$1 = arguments[0];
101663             var onLoc$3 = arguments[1];
101664             var leftLoc$1 = arguments[2];
101665             var rightLoc$1 = arguments[3];
101666             this.elt[0] = new TopologyLocation(Location.NONE, Location.NONE, Location.NONE);
101667             this.elt[1] = new TopologyLocation(Location.NONE, Location.NONE, Location.NONE);
101668             this.elt[geomIndex$1].setLocations(onLoc$3, leftLoc$1, rightLoc$1);
101669           }
101670         };
101671         Label.prototype.getGeometryCount = function getGeometryCount () {
101672           var count = 0;
101673           if (!this.elt[0].isNull()) { count++; }
101674           if (!this.elt[1].isNull()) { count++; }
101675           return count
101676         };
101677         Label.prototype.setAllLocations = function setAllLocations (geomIndex, location) {
101678           this.elt[geomIndex].setAllLocations(location);
101679         };
101680         Label.prototype.isNull = function isNull (geomIndex) {
101681           return this.elt[geomIndex].isNull()
101682         };
101683         Label.prototype.setAllLocationsIfNull = function setAllLocationsIfNull () {
101684           if (arguments.length === 1) {
101685             var location = arguments[0];
101686             this.setAllLocationsIfNull(0, location);
101687             this.setAllLocationsIfNull(1, location);
101688           } else if (arguments.length === 2) {
101689             var geomIndex = arguments[0];
101690             var location$1 = arguments[1];
101691             this.elt[geomIndex].setAllLocationsIfNull(location$1);
101692           }
101693         };
101694         Label.prototype.isLine = function isLine (geomIndex) {
101695           return this.elt[geomIndex].isLine()
101696         };
101697         Label.prototype.merge = function merge (lbl) {
101698             var this$1 = this;
101699
101700           for (var i = 0; i < 2; i++) {
101701             if (this$1.elt[i] === null && lbl.elt[i] !== null) {
101702               this$1.elt[i] = new TopologyLocation(lbl.elt[i]);
101703             } else {
101704               this$1.elt[i].merge(lbl.elt[i]);
101705             }
101706           }
101707         };
101708         Label.prototype.flip = function flip () {
101709           this.elt[0].flip();
101710           this.elt[1].flip();
101711         };
101712         Label.prototype.getLocation = function getLocation () {
101713           if (arguments.length === 1) {
101714             var geomIndex = arguments[0];
101715             return this.elt[geomIndex].get(Position.ON)
101716           } else if (arguments.length === 2) {
101717             var geomIndex$1 = arguments[0];
101718             var posIndex = arguments[1];
101719             return this.elt[geomIndex$1].get(posIndex)
101720           }
101721         };
101722         Label.prototype.toString = function toString () {
101723           var buf = new StringBuffer();
101724           if (this.elt[0] !== null) {
101725             buf.append('A:');
101726             buf.append(this.elt[0].toString());
101727           }
101728           if (this.elt[1] !== null) {
101729             buf.append(' B:');
101730             buf.append(this.elt[1].toString());
101731           }
101732           return buf.toString()
101733         };
101734         Label.prototype.isArea = function isArea () {
101735           if (arguments.length === 0) {
101736             return this.elt[0].isArea() || this.elt[1].isArea()
101737           } else if (arguments.length === 1) {
101738             var geomIndex = arguments[0];
101739             return this.elt[geomIndex].isArea()
101740           }
101741         };
101742         Label.prototype.isAnyNull = function isAnyNull (geomIndex) {
101743           return this.elt[geomIndex].isAnyNull()
101744         };
101745         Label.prototype.setLocation = function setLocation () {
101746           if (arguments.length === 2) {
101747             var geomIndex = arguments[0];
101748             var location = arguments[1];
101749             this.elt[geomIndex].setLocation(Position.ON, location);
101750           } else if (arguments.length === 3) {
101751             var geomIndex$1 = arguments[0];
101752             var posIndex = arguments[1];
101753             var location$1 = arguments[2];
101754             this.elt[geomIndex$1].setLocation(posIndex, location$1);
101755           }
101756         };
101757         Label.prototype.isEqualOnSide = function isEqualOnSide (lbl, side) {
101758           return this.elt[0].isEqualOnSide(lbl.elt[0], side) && this.elt[1].isEqualOnSide(lbl.elt[1], side)
101759         };
101760         Label.prototype.allPositionsEqual = function allPositionsEqual (geomIndex, loc) {
101761           return this.elt[geomIndex].allPositionsEqual(loc)
101762         };
101763         Label.prototype.toLine = function toLine (geomIndex) {
101764           if (this.elt[geomIndex].isArea()) { this.elt[geomIndex] = new TopologyLocation(this.elt[geomIndex].location[0]); }
101765         };
101766         Label.prototype.interfaces_ = function interfaces_ () {
101767           return []
101768         };
101769         Label.prototype.getClass = function getClass () {
101770           return Label
101771         };
101772         Label.toLineLabel = function toLineLabel (label) {
101773           var lineLabel = new Label(Location.NONE);
101774           for (var i = 0; i < 2; i++) {
101775             lineLabel.setLocation(i, label.getLocation(i));
101776           }
101777           return lineLabel
101778         };
101779
101780         var EdgeRing = function EdgeRing () {
101781           this._startDe = null;
101782           this._maxNodeDegree = -1;
101783           this._edges = new ArrayList();
101784           this._pts = new ArrayList();
101785           this._label = new Label(Location.NONE);
101786           this._ring = null;
101787           this._isHole = null;
101788           this._shell = null;
101789           this._holes = new ArrayList();
101790           this._geometryFactory = null;
101791           var start = arguments[0];
101792           var geometryFactory = arguments[1];
101793           this._geometryFactory = geometryFactory;
101794           this.computePoints(start);
101795           this.computeRing();
101796         };
101797         EdgeRing.prototype.computeRing = function computeRing () {
101798             var this$1 = this;
101799
101800           if (this._ring !== null) { return null }
101801           var coord = new Array(this._pts.size()).fill(null);
101802           for (var i = 0; i < this._pts.size(); i++) {
101803             coord[i] = this$1._pts.get(i);
101804           }
101805           this._ring = this._geometryFactory.createLinearRing(coord);
101806           this._isHole = CGAlgorithms.isCCW(this._ring.getCoordinates());
101807         };
101808         EdgeRing.prototype.isIsolated = function isIsolated () {
101809           return this._label.getGeometryCount() === 1
101810         };
101811         EdgeRing.prototype.computePoints = function computePoints (start) {
101812             var this$1 = this;
101813
101814           this._startDe = start;
101815           var de = start;
101816           var isFirstEdge = true;
101817           do {
101818             if (de === null) { throw new TopologyException('Found null DirectedEdge') }
101819             if (de.getEdgeRing() === this$1) { throw new TopologyException('Directed Edge visited twice during ring-building at ' + de.getCoordinate()) }
101820             this$1._edges.add(de);
101821             var label = de.getLabel();
101822             Assert.isTrue(label.isArea());
101823             this$1.mergeLabel(label);
101824             this$1.addPoints(de.getEdge(), de.isForward(), isFirstEdge);
101825             isFirstEdge = false;
101826             this$1.setEdgeRing(de, this$1);
101827             de = this$1.getNext(de);
101828           } while (de !== this._startDe)
101829         };
101830         EdgeRing.prototype.getLinearRing = function getLinearRing () {
101831           return this._ring
101832         };
101833         EdgeRing.prototype.getCoordinate = function getCoordinate (i) {
101834           return this._pts.get(i)
101835         };
101836         EdgeRing.prototype.computeMaxNodeDegree = function computeMaxNodeDegree () {
101837             var this$1 = this;
101838
101839           this._maxNodeDegree = 0;
101840           var de = this._startDe;
101841           do {
101842             var node = de.getNode();
101843             var degree = node.getEdges().getOutgoingDegree(this$1);
101844             if (degree > this$1._maxNodeDegree) { this$1._maxNodeDegree = degree; }
101845             de = this$1.getNext(de);
101846           } while (de !== this._startDe)
101847           this._maxNodeDegree *= 2;
101848         };
101849         EdgeRing.prototype.addPoints = function addPoints (edge, isForward, isFirstEdge) {
101850             var this$1 = this;
101851
101852           var edgePts = edge.getCoordinates();
101853           if (isForward) {
101854             var startIndex = 1;
101855             if (isFirstEdge) { startIndex = 0; }
101856             for (var i = startIndex; i < edgePts.length; i++) {
101857               this$1._pts.add(edgePts[i]);
101858             }
101859           } else {
101860             var startIndex$1 = edgePts.length - 2;
101861             if (isFirstEdge) { startIndex$1 = edgePts.length - 1; }
101862             for (var i$1 = startIndex$1; i$1 >= 0; i$1--) {
101863               this$1._pts.add(edgePts[i$1]);
101864             }
101865           }
101866         };
101867         EdgeRing.prototype.isHole = function isHole () {
101868           return this._isHole
101869         };
101870         EdgeRing.prototype.setInResult = function setInResult () {
101871           var de = this._startDe;
101872           do {
101873             de.getEdge().setInResult(true);
101874             de = de.getNext();
101875           } while (de !== this._startDe)
101876         };
101877         EdgeRing.prototype.containsPoint = function containsPoint (p) {
101878           var shell = this.getLinearRing();
101879           var env = shell.getEnvelopeInternal();
101880           if (!env.contains(p)) { return false }
101881           if (!CGAlgorithms.isPointInRing(p, shell.getCoordinates())) { return false }
101882           for (var i = this._holes.iterator(); i.hasNext();) {
101883             var hole = i.next();
101884             if (hole.containsPoint(p)) { return false }
101885           }
101886           return true
101887         };
101888         EdgeRing.prototype.addHole = function addHole (ring) {
101889           this._holes.add(ring);
101890         };
101891         EdgeRing.prototype.isShell = function isShell () {
101892           return this._shell === null
101893         };
101894         EdgeRing.prototype.getLabel = function getLabel () {
101895           return this._label
101896         };
101897         EdgeRing.prototype.getEdges = function getEdges () {
101898           return this._edges
101899         };
101900         EdgeRing.prototype.getMaxNodeDegree = function getMaxNodeDegree () {
101901           if (this._maxNodeDegree < 0) { this.computeMaxNodeDegree(); }
101902           return this._maxNodeDegree
101903         };
101904         EdgeRing.prototype.getShell = function getShell () {
101905           return this._shell
101906         };
101907         EdgeRing.prototype.mergeLabel = function mergeLabel () {
101908           if (arguments.length === 1) {
101909             var deLabel = arguments[0];
101910             this.mergeLabel(deLabel, 0);
101911             this.mergeLabel(deLabel, 1);
101912           } else if (arguments.length === 2) {
101913             var deLabel$1 = arguments[0];
101914             var geomIndex = arguments[1];
101915             var loc = deLabel$1.getLocation(geomIndex, Position.RIGHT);
101916             if (loc === Location.NONE) { return null }
101917             if (this._label.getLocation(geomIndex) === Location.NONE) {
101918               this._label.setLocation(geomIndex, loc);
101919               return null
101920             }
101921           }
101922         };
101923         EdgeRing.prototype.setShell = function setShell (shell) {
101924           this._shell = shell;
101925           if (shell !== null) { shell.addHole(this); }
101926         };
101927         EdgeRing.prototype.toPolygon = function toPolygon (geometryFactory) {
101928             var this$1 = this;
101929
101930           var holeLR = new Array(this._holes.size()).fill(null);
101931           for (var i = 0; i < this._holes.size(); i++) {
101932             holeLR[i] = this$1._holes.get(i).getLinearRing();
101933           }
101934           var poly = geometryFactory.createPolygon(this.getLinearRing(), holeLR);
101935           return poly
101936         };
101937         EdgeRing.prototype.interfaces_ = function interfaces_ () {
101938           return []
101939         };
101940         EdgeRing.prototype.getClass = function getClass () {
101941           return EdgeRing
101942         };
101943
101944         var MinimalEdgeRing = (function (EdgeRing$$1) {
101945           function MinimalEdgeRing () {
101946             var start = arguments[0];
101947             var geometryFactory = arguments[1];
101948             EdgeRing$$1.call(this, start, geometryFactory);
101949           }
101950
101951           if ( EdgeRing$$1 ) { MinimalEdgeRing.__proto__ = EdgeRing$$1; }
101952           MinimalEdgeRing.prototype = Object.create( EdgeRing$$1 && EdgeRing$$1.prototype );
101953           MinimalEdgeRing.prototype.constructor = MinimalEdgeRing;
101954           MinimalEdgeRing.prototype.setEdgeRing = function setEdgeRing (de, er) {
101955             de.setMinEdgeRing(er);
101956           };
101957           MinimalEdgeRing.prototype.getNext = function getNext (de) {
101958             return de.getNextMin()
101959           };
101960           MinimalEdgeRing.prototype.interfaces_ = function interfaces_ () {
101961             return []
101962           };
101963           MinimalEdgeRing.prototype.getClass = function getClass () {
101964             return MinimalEdgeRing
101965           };
101966
101967           return MinimalEdgeRing;
101968         }(EdgeRing));
101969
101970         var MaximalEdgeRing = (function (EdgeRing$$1) {
101971           function MaximalEdgeRing () {
101972             var start = arguments[0];
101973             var geometryFactory = arguments[1];
101974             EdgeRing$$1.call(this, start, geometryFactory);
101975           }
101976
101977           if ( EdgeRing$$1 ) { MaximalEdgeRing.__proto__ = EdgeRing$$1; }
101978           MaximalEdgeRing.prototype = Object.create( EdgeRing$$1 && EdgeRing$$1.prototype );
101979           MaximalEdgeRing.prototype.constructor = MaximalEdgeRing;
101980           MaximalEdgeRing.prototype.buildMinimalRings = function buildMinimalRings () {
101981             var this$1 = this;
101982
101983             var minEdgeRings = new ArrayList();
101984             var de = this._startDe;
101985             do {
101986               if (de.getMinEdgeRing() === null) {
101987                 var minEr = new MinimalEdgeRing(de, this$1._geometryFactory);
101988                 minEdgeRings.add(minEr);
101989               }
101990               de = de.getNext();
101991             } while (de !== this._startDe)
101992             return minEdgeRings
101993           };
101994           MaximalEdgeRing.prototype.setEdgeRing = function setEdgeRing (de, er) {
101995             de.setEdgeRing(er);
101996           };
101997           MaximalEdgeRing.prototype.linkDirectedEdgesForMinimalEdgeRings = function linkDirectedEdgesForMinimalEdgeRings () {
101998             var this$1 = this;
101999
102000             var de = this._startDe;
102001             do {
102002               var node = de.getNode();
102003               node.getEdges().linkMinimalDirectedEdges(this$1);
102004               de = de.getNext();
102005             } while (de !== this._startDe)
102006           };
102007           MaximalEdgeRing.prototype.getNext = function getNext (de) {
102008             return de.getNext()
102009           };
102010           MaximalEdgeRing.prototype.interfaces_ = function interfaces_ () {
102011             return []
102012           };
102013           MaximalEdgeRing.prototype.getClass = function getClass () {
102014             return MaximalEdgeRing
102015           };
102016
102017           return MaximalEdgeRing;
102018         }(EdgeRing));
102019
102020         var GraphComponent = function GraphComponent () {
102021           this._label = null;
102022           this._isInResult = false;
102023           this._isCovered = false;
102024           this._isCoveredSet = false;
102025           this._isVisited = false;
102026           if (arguments.length === 0) ; else if (arguments.length === 1) {
102027             var label = arguments[0];
102028             this._label = label;
102029           }
102030         };
102031         GraphComponent.prototype.setVisited = function setVisited (isVisited) {
102032           this._isVisited = isVisited;
102033         };
102034         GraphComponent.prototype.setInResult = function setInResult (isInResult) {
102035           this._isInResult = isInResult;
102036         };
102037         GraphComponent.prototype.isCovered = function isCovered () {
102038           return this._isCovered
102039         };
102040         GraphComponent.prototype.isCoveredSet = function isCoveredSet () {
102041           return this._isCoveredSet
102042         };
102043         GraphComponent.prototype.setLabel = function setLabel (label) {
102044           this._label = label;
102045         };
102046         GraphComponent.prototype.getLabel = function getLabel () {
102047           return this._label
102048         };
102049         GraphComponent.prototype.setCovered = function setCovered (isCovered) {
102050           this._isCovered = isCovered;
102051           this._isCoveredSet = true;
102052         };
102053         GraphComponent.prototype.updateIM = function updateIM (im) {
102054           Assert.isTrue(this._label.getGeometryCount() >= 2, 'found partial label');
102055           this.computeIM(im);
102056         };
102057         GraphComponent.prototype.isInResult = function isInResult () {
102058           return this._isInResult
102059         };
102060         GraphComponent.prototype.isVisited = function isVisited () {
102061           return this._isVisited
102062         };
102063         GraphComponent.prototype.interfaces_ = function interfaces_ () {
102064           return []
102065         };
102066         GraphComponent.prototype.getClass = function getClass () {
102067           return GraphComponent
102068         };
102069
102070         var Node$1 = (function (GraphComponent$$1) {
102071           function Node () {
102072             GraphComponent$$1.call(this);
102073             this._coord = null;
102074             this._edges = null;
102075             var coord = arguments[0];
102076             var edges = arguments[1];
102077             this._coord = coord;
102078             this._edges = edges;
102079             this._label = new Label(0, Location.NONE);
102080           }
102081
102082           if ( GraphComponent$$1 ) { Node.__proto__ = GraphComponent$$1; }
102083           Node.prototype = Object.create( GraphComponent$$1 && GraphComponent$$1.prototype );
102084           Node.prototype.constructor = Node;
102085           Node.prototype.isIncidentEdgeInResult = function isIncidentEdgeInResult () {
102086             for (var it = this.getEdges().getEdges().iterator(); it.hasNext();) {
102087               var de = it.next();
102088               if (de.getEdge().isInResult()) { return true }
102089             }
102090             return false
102091           };
102092           Node.prototype.isIsolated = function isIsolated () {
102093             return this._label.getGeometryCount() === 1
102094           };
102095           Node.prototype.getCoordinate = function getCoordinate () {
102096             return this._coord
102097           };
102098           Node.prototype.print = function print (out) {
102099             out.println('node ' + this._coord + ' lbl: ' + this._label);
102100           };
102101           Node.prototype.computeIM = function computeIM (im) {};
102102           Node.prototype.computeMergedLocation = function computeMergedLocation (label2, eltIndex) {
102103             var loc = Location.NONE;
102104             loc = this._label.getLocation(eltIndex);
102105             if (!label2.isNull(eltIndex)) {
102106               var nLoc = label2.getLocation(eltIndex);
102107               if (loc !== Location.BOUNDARY) { loc = nLoc; }
102108             }
102109             return loc
102110           };
102111           Node.prototype.setLabel = function setLabel () {
102112             if (arguments.length === 2) {
102113               var argIndex = arguments[0];
102114               var onLocation = arguments[1];
102115               if (this._label === null) {
102116                 this._label = new Label(argIndex, onLocation);
102117               } else { this._label.setLocation(argIndex, onLocation); }
102118             } else { return GraphComponent$$1.prototype.setLabel.apply(this, arguments) }
102119           };
102120           Node.prototype.getEdges = function getEdges () {
102121             return this._edges
102122           };
102123           Node.prototype.mergeLabel = function mergeLabel () {
102124             var this$1 = this;
102125
102126             if (arguments[0] instanceof Node) {
102127               var n = arguments[0];
102128               this.mergeLabel(n._label);
102129             } else if (arguments[0] instanceof Label) {
102130               var label2 = arguments[0];
102131               for (var i = 0; i < 2; i++) {
102132                 var loc = this$1.computeMergedLocation(label2, i);
102133                 var thisLoc = this$1._label.getLocation(i);
102134                 if (thisLoc === Location.NONE) { this$1._label.setLocation(i, loc); }
102135               }
102136             }
102137           };
102138           Node.prototype.add = function add (e) {
102139             this._edges.insert(e);
102140             e.setNode(this);
102141           };
102142           Node.prototype.setLabelBoundary = function setLabelBoundary (argIndex) {
102143             if (this._label === null) { return null }
102144             var loc = Location.NONE;
102145             if (this._label !== null) { loc = this._label.getLocation(argIndex); }
102146             var newLoc = null;
102147             switch (loc) {
102148               case Location.BOUNDARY:
102149                 newLoc = Location.INTERIOR;
102150                 break
102151               case Location.INTERIOR:
102152                 newLoc = Location.BOUNDARY;
102153                 break
102154               default:
102155                 newLoc = Location.BOUNDARY;
102156                 break
102157             }
102158             this._label.setLocation(argIndex, newLoc);
102159           };
102160           Node.prototype.interfaces_ = function interfaces_ () {
102161             return []
102162           };
102163           Node.prototype.getClass = function getClass () {
102164             return Node
102165           };
102166
102167           return Node;
102168         }(GraphComponent));
102169
102170         var NodeMap = function NodeMap () {
102171           this.nodeMap = new TreeMap();
102172           this.nodeFact = null;
102173           var nodeFact = arguments[0];
102174           this.nodeFact = nodeFact;
102175         };
102176         NodeMap.prototype.find = function find (coord) {
102177           return this.nodeMap.get(coord)
102178         };
102179         NodeMap.prototype.addNode = function addNode () {
102180           if (arguments[0] instanceof Coordinate) {
102181             var coord = arguments[0];
102182             var node = this.nodeMap.get(coord);
102183             if (node === null) {
102184               node = this.nodeFact.createNode(coord);
102185               this.nodeMap.put(coord, node);
102186             }
102187             return node
102188           } else if (arguments[0] instanceof Node$1) {
102189             var n = arguments[0];
102190             var node$1 = this.nodeMap.get(n.getCoordinate());
102191             if (node$1 === null) {
102192               this.nodeMap.put(n.getCoordinate(), n);
102193               return n
102194             }
102195             node$1.mergeLabel(n);
102196             return node$1
102197           }
102198         };
102199         NodeMap.prototype.print = function print (out) {
102200           for (var it = this.iterator(); it.hasNext();) {
102201             var n = it.next();
102202             n.print(out);
102203           }
102204         };
102205         NodeMap.prototype.iterator = function iterator () {
102206           return this.nodeMap.values().iterator()
102207         };
102208         NodeMap.prototype.values = function values () {
102209           return this.nodeMap.values()
102210         };
102211         NodeMap.prototype.getBoundaryNodes = function getBoundaryNodes (geomIndex) {
102212           var bdyNodes = new ArrayList();
102213           for (var i = this.iterator(); i.hasNext();) {
102214             var node = i.next();
102215             if (node.getLabel().getLocation(geomIndex) === Location.BOUNDARY) { bdyNodes.add(node); }
102216           }
102217           return bdyNodes
102218         };
102219         NodeMap.prototype.add = function add (e) {
102220           var p = e.getCoordinate();
102221           var n = this.addNode(p);
102222           n.add(e);
102223         };
102224         NodeMap.prototype.interfaces_ = function interfaces_ () {
102225           return []
102226         };
102227         NodeMap.prototype.getClass = function getClass () {
102228           return NodeMap
102229         };
102230
102231         var Quadrant = function Quadrant () {};
102232
102233         var staticAccessors$21 = { NE: { configurable: true },NW: { configurable: true },SW: { configurable: true },SE: { configurable: true } };
102234
102235         Quadrant.prototype.interfaces_ = function interfaces_ () {
102236           return []
102237         };
102238         Quadrant.prototype.getClass = function getClass () {
102239           return Quadrant
102240         };
102241         Quadrant.isNorthern = function isNorthern (quad) {
102242           return quad === Quadrant.NE || quad === Quadrant.NW
102243         };
102244         Quadrant.isOpposite = function isOpposite (quad1, quad2) {
102245           if (quad1 === quad2) { return false }
102246           var diff = (quad1 - quad2 + 4) % 4;
102247           if (diff === 2) { return true }
102248           return false
102249         };
102250         Quadrant.commonHalfPlane = function commonHalfPlane (quad1, quad2) {
102251           if (quad1 === quad2) { return quad1 }
102252           var diff = (quad1 - quad2 + 4) % 4;
102253           if (diff === 2) { return -1 }
102254           var min = quad1 < quad2 ? quad1 : quad2;
102255           var max = quad1 > quad2 ? quad1 : quad2;
102256           if (min === 0 && max === 3) { return 3 }
102257           return min
102258         };
102259         Quadrant.isInHalfPlane = function isInHalfPlane (quad, halfPlane) {
102260           if (halfPlane === Quadrant.SE) {
102261             return quad === Quadrant.SE || quad === Quadrant.SW
102262           }
102263           return quad === halfPlane || quad === halfPlane + 1
102264         };
102265         Quadrant.quadrant = function quadrant () {
102266           if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
102267             var dx = arguments[0];
102268             var dy = arguments[1];
102269             if (dx === 0.0 && dy === 0.0) { throw new IllegalArgumentException('Cannot compute the quadrant for point ( ' + dx + ', ' + dy + ' )') }
102270             if (dx >= 0.0) {
102271               if (dy >= 0.0) { return Quadrant.NE; } else { return Quadrant.SE }
102272             } else {
102273               if (dy >= 0.0) { return Quadrant.NW; } else { return Quadrant.SW }
102274             }
102275           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
102276             var p0 = arguments[0];
102277             var p1 = arguments[1];
102278             if (p1.x === p0.x && p1.y === p0.y) { throw new IllegalArgumentException('Cannot compute the quadrant for two identical points ' + p0) }
102279             if (p1.x >= p0.x) {
102280               if (p1.y >= p0.y) { return Quadrant.NE; } else { return Quadrant.SE }
102281             } else {
102282               if (p1.y >= p0.y) { return Quadrant.NW; } else { return Quadrant.SW }
102283             }
102284           }
102285         };
102286         staticAccessors$21.NE.get = function () { return 0 };
102287         staticAccessors$21.NW.get = function () { return 1 };
102288         staticAccessors$21.SW.get = function () { return 2 };
102289         staticAccessors$21.SE.get = function () { return 3 };
102290
102291         Object.defineProperties( Quadrant, staticAccessors$21 );
102292
102293         var EdgeEnd = function EdgeEnd () {
102294           this._edge = null;
102295           this._label = null;
102296           this._node = null;
102297           this._p0 = null;
102298           this._p1 = null;
102299           this._dx = null;
102300           this._dy = null;
102301           this._quadrant = null;
102302           if (arguments.length === 1) {
102303             var edge = arguments[0];
102304             this._edge = edge;
102305           } else if (arguments.length === 3) {
102306             var edge$1 = arguments[0];
102307             var p0 = arguments[1];
102308             var p1 = arguments[2];
102309             var label = null;
102310             this._edge = edge$1;
102311             this.init(p0, p1);
102312             this._label = label;
102313           } else if (arguments.length === 4) {
102314             var edge$2 = arguments[0];
102315             var p0$1 = arguments[1];
102316             var p1$1 = arguments[2];
102317             var label$1 = arguments[3];
102318             this._edge = edge$2;
102319             this.init(p0$1, p1$1);
102320             this._label = label$1;
102321           }
102322         };
102323         EdgeEnd.prototype.compareDirection = function compareDirection (e) {
102324           if (this._dx === e._dx && this._dy === e._dy) { return 0 }
102325           if (this._quadrant > e._quadrant) { return 1 }
102326           if (this._quadrant < e._quadrant) { return -1 }
102327           return CGAlgorithms.computeOrientation(e._p0, e._p1, this._p1)
102328         };
102329         EdgeEnd.prototype.getDy = function getDy () {
102330           return this._dy
102331         };
102332         EdgeEnd.prototype.getCoordinate = function getCoordinate () {
102333           return this._p0
102334         };
102335         EdgeEnd.prototype.setNode = function setNode (node) {
102336           this._node = node;
102337         };
102338         EdgeEnd.prototype.print = function print (out) {
102339           var angle = Math.atan2(this._dy, this._dx);
102340           var className = this.getClass().getName();
102341           var lastDotPos = className.lastIndexOf('.');
102342           var name = className.substring(lastDotPos + 1);
102343           out.print('  ' + name + ': ' + this._p0 + ' - ' + this._p1 + ' ' + this._quadrant + ':' + angle + '   ' + this._label);
102344         };
102345         EdgeEnd.prototype.compareTo = function compareTo (obj) {
102346           var e = obj;
102347           return this.compareDirection(e)
102348         };
102349         EdgeEnd.prototype.getDirectedCoordinate = function getDirectedCoordinate () {
102350           return this._p1
102351         };
102352         EdgeEnd.prototype.getDx = function getDx () {
102353           return this._dx
102354         };
102355         EdgeEnd.prototype.getLabel = function getLabel () {
102356           return this._label
102357         };
102358         EdgeEnd.prototype.getEdge = function getEdge () {
102359           return this._edge
102360         };
102361         EdgeEnd.prototype.getQuadrant = function getQuadrant () {
102362           return this._quadrant
102363         };
102364         EdgeEnd.prototype.getNode = function getNode () {
102365           return this._node
102366         };
102367         EdgeEnd.prototype.toString = function toString () {
102368           var angle = Math.atan2(this._dy, this._dx);
102369           var className = this.getClass().getName();
102370           var lastDotPos = className.lastIndexOf('.');
102371           var name = className.substring(lastDotPos + 1);
102372           return '  ' + name + ': ' + this._p0 + ' - ' + this._p1 + ' ' + this._quadrant + ':' + angle + '   ' + this._label
102373         };
102374         EdgeEnd.prototype.computeLabel = function computeLabel (boundaryNodeRule) {};
102375         EdgeEnd.prototype.init = function init (p0, p1) {
102376           this._p0 = p0;
102377           this._p1 = p1;
102378           this._dx = p1.x - p0.x;
102379           this._dy = p1.y - p0.y;
102380           this._quadrant = Quadrant.quadrant(this._dx, this._dy);
102381           Assert.isTrue(!(this._dx === 0 && this._dy === 0), 'EdgeEnd with identical endpoints found');
102382         };
102383         EdgeEnd.prototype.interfaces_ = function interfaces_ () {
102384           return [Comparable]
102385         };
102386         EdgeEnd.prototype.getClass = function getClass () {
102387           return EdgeEnd
102388         };
102389
102390         var DirectedEdge = (function (EdgeEnd$$1) {
102391           function DirectedEdge () {
102392             var edge = arguments[0];
102393             var isForward = arguments[1];
102394             EdgeEnd$$1.call(this, edge);
102395             this._isForward = null;
102396             this._isInResult = false;
102397             this._isVisited = false;
102398             this._sym = null;
102399             this._next = null;
102400             this._nextMin = null;
102401             this._edgeRing = null;
102402             this._minEdgeRing = null;
102403             this._depth = [0, -999, -999];
102404             this._isForward = isForward;
102405             if (isForward) {
102406               this.init(edge.getCoordinate(0), edge.getCoordinate(1));
102407             } else {
102408               var n = edge.getNumPoints() - 1;
102409               this.init(edge.getCoordinate(n), edge.getCoordinate(n - 1));
102410             }
102411             this.computeDirectedLabel();
102412           }
102413
102414           if ( EdgeEnd$$1 ) { DirectedEdge.__proto__ = EdgeEnd$$1; }
102415           DirectedEdge.prototype = Object.create( EdgeEnd$$1 && EdgeEnd$$1.prototype );
102416           DirectedEdge.prototype.constructor = DirectedEdge;
102417           DirectedEdge.prototype.getNextMin = function getNextMin () {
102418             return this._nextMin
102419           };
102420           DirectedEdge.prototype.getDepth = function getDepth (position) {
102421             return this._depth[position]
102422           };
102423           DirectedEdge.prototype.setVisited = function setVisited (isVisited) {
102424             this._isVisited = isVisited;
102425           };
102426           DirectedEdge.prototype.computeDirectedLabel = function computeDirectedLabel () {
102427             this._label = new Label(this._edge.getLabel());
102428             if (!this._isForward) { this._label.flip(); }
102429           };
102430           DirectedEdge.prototype.getNext = function getNext () {
102431             return this._next
102432           };
102433           DirectedEdge.prototype.setDepth = function setDepth (position, depthVal) {
102434             if (this._depth[position] !== -999) {
102435               if (this._depth[position] !== depthVal) { throw new TopologyException('assigned depths do not match', this.getCoordinate()) }
102436             }
102437             this._depth[position] = depthVal;
102438           };
102439           DirectedEdge.prototype.isInteriorAreaEdge = function isInteriorAreaEdge () {
102440             var this$1 = this;
102441
102442             var isInteriorAreaEdge = true;
102443             for (var i = 0; i < 2; i++) {
102444               if (!(this$1._label.isArea(i) && this$1._label.getLocation(i, Position.LEFT) === Location.INTERIOR && this$1._label.getLocation(i, Position.RIGHT) === Location.INTERIOR)) {
102445                 isInteriorAreaEdge = false;
102446               }
102447             }
102448             return isInteriorAreaEdge
102449           };
102450           DirectedEdge.prototype.setNextMin = function setNextMin (nextMin) {
102451             this._nextMin = nextMin;
102452           };
102453           DirectedEdge.prototype.print = function print (out) {
102454             EdgeEnd$$1.prototype.print.call(this, out);
102455             out.print(' ' + this._depth[Position.LEFT] + '/' + this._depth[Position.RIGHT]);
102456             out.print(' (' + this.getDepthDelta() + ')');
102457             if (this._isInResult) { out.print(' inResult'); }
102458           };
102459           DirectedEdge.prototype.setMinEdgeRing = function setMinEdgeRing (minEdgeRing) {
102460             this._minEdgeRing = minEdgeRing;
102461           };
102462           DirectedEdge.prototype.isLineEdge = function isLineEdge () {
102463             var isLine = this._label.isLine(0) || this._label.isLine(1);
102464             var isExteriorIfArea0 = !this._label.isArea(0) || this._label.allPositionsEqual(0, Location.EXTERIOR);
102465             var isExteriorIfArea1 = !this._label.isArea(1) || this._label.allPositionsEqual(1, Location.EXTERIOR);
102466             return isLine && isExteriorIfArea0 && isExteriorIfArea1
102467           };
102468           DirectedEdge.prototype.setEdgeRing = function setEdgeRing (edgeRing) {
102469             this._edgeRing = edgeRing;
102470           };
102471           DirectedEdge.prototype.getMinEdgeRing = function getMinEdgeRing () {
102472             return this._minEdgeRing
102473           };
102474           DirectedEdge.prototype.getDepthDelta = function getDepthDelta () {
102475             var depthDelta = this._edge.getDepthDelta();
102476             if (!this._isForward) { depthDelta = -depthDelta; }
102477             return depthDelta
102478           };
102479           DirectedEdge.prototype.setInResult = function setInResult (isInResult) {
102480             this._isInResult = isInResult;
102481           };
102482           DirectedEdge.prototype.getSym = function getSym () {
102483             return this._sym
102484           };
102485           DirectedEdge.prototype.isForward = function isForward () {
102486             return this._isForward
102487           };
102488           DirectedEdge.prototype.getEdge = function getEdge () {
102489             return this._edge
102490           };
102491           DirectedEdge.prototype.printEdge = function printEdge (out) {
102492             this.print(out);
102493             out.print(' ');
102494             if (this._isForward) { this._edge.print(out); } else { this._edge.printReverse(out); }
102495           };
102496           DirectedEdge.prototype.setSym = function setSym (de) {
102497             this._sym = de;
102498           };
102499           DirectedEdge.prototype.setVisitedEdge = function setVisitedEdge (isVisited) {
102500             this.setVisited(isVisited);
102501             this._sym.setVisited(isVisited);
102502           };
102503           DirectedEdge.prototype.setEdgeDepths = function setEdgeDepths (position, depth) {
102504             var depthDelta = this.getEdge().getDepthDelta();
102505             if (!this._isForward) { depthDelta = -depthDelta; }
102506             var directionFactor = 1;
102507             if (position === Position.LEFT) { directionFactor = -1; }
102508             var oppositePos = Position.opposite(position);
102509             var delta = depthDelta * directionFactor;
102510             var oppositeDepth = depth + delta;
102511             this.setDepth(position, depth);
102512             this.setDepth(oppositePos, oppositeDepth);
102513           };
102514           DirectedEdge.prototype.getEdgeRing = function getEdgeRing () {
102515             return this._edgeRing
102516           };
102517           DirectedEdge.prototype.isInResult = function isInResult () {
102518             return this._isInResult
102519           };
102520           DirectedEdge.prototype.setNext = function setNext (next) {
102521             this._next = next;
102522           };
102523           DirectedEdge.prototype.isVisited = function isVisited () {
102524             return this._isVisited
102525           };
102526           DirectedEdge.prototype.interfaces_ = function interfaces_ () {
102527             return []
102528           };
102529           DirectedEdge.prototype.getClass = function getClass () {
102530             return DirectedEdge
102531           };
102532           DirectedEdge.depthFactor = function depthFactor (currLocation, nextLocation) {
102533             if (currLocation === Location.EXTERIOR && nextLocation === Location.INTERIOR) { return 1; } else if (currLocation === Location.INTERIOR && nextLocation === Location.EXTERIOR) { return -1 }
102534             return 0
102535           };
102536
102537           return DirectedEdge;
102538         }(EdgeEnd));
102539
102540         var NodeFactory = function NodeFactory () {};
102541
102542         NodeFactory.prototype.createNode = function createNode (coord) {
102543           return new Node$1(coord, null)
102544         };
102545         NodeFactory.prototype.interfaces_ = function interfaces_ () {
102546           return []
102547         };
102548         NodeFactory.prototype.getClass = function getClass () {
102549           return NodeFactory
102550         };
102551
102552         var PlanarGraph = function PlanarGraph () {
102553           this._edges = new ArrayList();
102554           this._nodes = null;
102555           this._edgeEndList = new ArrayList();
102556           if (arguments.length === 0) {
102557             this._nodes = new NodeMap(new NodeFactory());
102558           } else if (arguments.length === 1) {
102559             var nodeFact = arguments[0];
102560             this._nodes = new NodeMap(nodeFact);
102561           }
102562         };
102563         PlanarGraph.prototype.printEdges = function printEdges (out) {
102564             var this$1 = this;
102565
102566           out.println('Edges:');
102567           for (var i = 0; i < this._edges.size(); i++) {
102568             out.println('edge ' + i + ':');
102569             var e = this$1._edges.get(i);
102570             e.print(out);
102571             e.eiList.print(out);
102572           }
102573         };
102574         PlanarGraph.prototype.find = function find (coord) {
102575           return this._nodes.find(coord)
102576         };
102577         PlanarGraph.prototype.addNode = function addNode () {
102578           if (arguments[0] instanceof Node$1) {
102579             var node = arguments[0];
102580             return this._nodes.addNode(node)
102581           } else if (arguments[0] instanceof Coordinate) {
102582             var coord = arguments[0];
102583             return this._nodes.addNode(coord)
102584           }
102585         };
102586         PlanarGraph.prototype.getNodeIterator = function getNodeIterator () {
102587           return this._nodes.iterator()
102588         };
102589         PlanarGraph.prototype.linkResultDirectedEdges = function linkResultDirectedEdges () {
102590           for (var nodeit = this._nodes.iterator(); nodeit.hasNext();) {
102591             var node = nodeit.next();
102592             node.getEdges().linkResultDirectedEdges();
102593           }
102594         };
102595         PlanarGraph.prototype.debugPrintln = function debugPrintln (o) {
102596           System.out.println(o);
102597         };
102598         PlanarGraph.prototype.isBoundaryNode = function isBoundaryNode (geomIndex, coord) {
102599           var node = this._nodes.find(coord);
102600           if (node === null) { return false }
102601           var label = node.getLabel();
102602           if (label !== null && label.getLocation(geomIndex) === Location.BOUNDARY) { return true }
102603           return false
102604         };
102605         PlanarGraph.prototype.linkAllDirectedEdges = function linkAllDirectedEdges () {
102606           for (var nodeit = this._nodes.iterator(); nodeit.hasNext();) {
102607             var node = nodeit.next();
102608             node.getEdges().linkAllDirectedEdges();
102609           }
102610         };
102611         PlanarGraph.prototype.matchInSameDirection = function matchInSameDirection (p0, p1, ep0, ep1) {
102612           if (!p0.equals(ep0)) { return false }
102613           if (CGAlgorithms.computeOrientation(p0, p1, ep1) === CGAlgorithms.COLLINEAR && Quadrant.quadrant(p0, p1) === Quadrant.quadrant(ep0, ep1)) { return true }
102614           return false
102615         };
102616         PlanarGraph.prototype.getEdgeEnds = function getEdgeEnds () {
102617           return this._edgeEndList
102618         };
102619         PlanarGraph.prototype.debugPrint = function debugPrint (o) {
102620           System.out.print(o);
102621         };
102622         PlanarGraph.prototype.getEdgeIterator = function getEdgeIterator () {
102623           return this._edges.iterator()
102624         };
102625         PlanarGraph.prototype.findEdgeInSameDirection = function findEdgeInSameDirection (p0, p1) {
102626             var this$1 = this;
102627
102628           for (var i = 0; i < this._edges.size(); i++) {
102629             var e = this$1._edges.get(i);
102630             var eCoord = e.getCoordinates();
102631             if (this$1.matchInSameDirection(p0, p1, eCoord[0], eCoord[1])) { return e }
102632             if (this$1.matchInSameDirection(p0, p1, eCoord[eCoord.length - 1], eCoord[eCoord.length - 2])) { return e }
102633           }
102634           return null
102635         };
102636         PlanarGraph.prototype.insertEdge = function insertEdge (e) {
102637           this._edges.add(e);
102638         };
102639         PlanarGraph.prototype.findEdgeEnd = function findEdgeEnd (e) {
102640           for (var i = this.getEdgeEnds().iterator(); i.hasNext();) {
102641             var ee = i.next();
102642             if (ee.getEdge() === e) { return ee }
102643           }
102644           return null
102645         };
102646         PlanarGraph.prototype.addEdges = function addEdges (edgesToAdd) {
102647             var this$1 = this;
102648
102649           for (var it = edgesToAdd.iterator(); it.hasNext();) {
102650             var e = it.next();
102651             this$1._edges.add(e);
102652             var de1 = new DirectedEdge(e, true);
102653             var de2 = new DirectedEdge(e, false);
102654             de1.setSym(de2);
102655             de2.setSym(de1);
102656             this$1.add(de1);
102657             this$1.add(de2);
102658           }
102659         };
102660         PlanarGraph.prototype.add = function add (e) {
102661           this._nodes.add(e);
102662           this._edgeEndList.add(e);
102663         };
102664         PlanarGraph.prototype.getNodes = function getNodes () {
102665           return this._nodes.values()
102666         };
102667         PlanarGraph.prototype.findEdge = function findEdge (p0, p1) {
102668             var this$1 = this;
102669
102670           for (var i = 0; i < this._edges.size(); i++) {
102671             var e = this$1._edges.get(i);
102672             var eCoord = e.getCoordinates();
102673             if (p0.equals(eCoord[0]) && p1.equals(eCoord[1])) { return e }
102674           }
102675           return null
102676         };
102677         PlanarGraph.prototype.interfaces_ = function interfaces_ () {
102678           return []
102679         };
102680         PlanarGraph.prototype.getClass = function getClass () {
102681           return PlanarGraph
102682         };
102683         PlanarGraph.linkResultDirectedEdges = function linkResultDirectedEdges (nodes) {
102684           for (var nodeit = nodes.iterator(); nodeit.hasNext();) {
102685             var node = nodeit.next();
102686             node.getEdges().linkResultDirectedEdges();
102687           }
102688         };
102689
102690         var PolygonBuilder = function PolygonBuilder () {
102691           this._geometryFactory = null;
102692           this._shellList = new ArrayList();
102693           var geometryFactory = arguments[0];
102694           this._geometryFactory = geometryFactory;
102695         };
102696         PolygonBuilder.prototype.sortShellsAndHoles = function sortShellsAndHoles (edgeRings, shellList, freeHoleList) {
102697           for (var it = edgeRings.iterator(); it.hasNext();) {
102698             var er = it.next();
102699             if (er.isHole()) {
102700               freeHoleList.add(er);
102701             } else {
102702               shellList.add(er);
102703             }
102704           }
102705         };
102706         PolygonBuilder.prototype.computePolygons = function computePolygons (shellList) {
102707             var this$1 = this;
102708
102709           var resultPolyList = new ArrayList();
102710           for (var it = shellList.iterator(); it.hasNext();) {
102711             var er = it.next();
102712             var poly = er.toPolygon(this$1._geometryFactory);
102713             resultPolyList.add(poly);
102714           }
102715           return resultPolyList
102716         };
102717         PolygonBuilder.prototype.placeFreeHoles = function placeFreeHoles (shellList, freeHoleList) {
102718             var this$1 = this;
102719
102720           for (var it = freeHoleList.iterator(); it.hasNext();) {
102721             var hole = it.next();
102722             if (hole.getShell() === null) {
102723               var shell = this$1.findEdgeRingContaining(hole, shellList);
102724               if (shell === null) { throw new TopologyException('unable to assign hole to a shell', hole.getCoordinate(0)) }
102725               hole.setShell(shell);
102726             }
102727           }
102728         };
102729         PolygonBuilder.prototype.buildMinimalEdgeRings = function buildMinimalEdgeRings (maxEdgeRings, shellList, freeHoleList) {
102730             var this$1 = this;
102731
102732           var edgeRings = new ArrayList();
102733           for (var it = maxEdgeRings.iterator(); it.hasNext();) {
102734             var er = it.next();
102735             if (er.getMaxNodeDegree() > 2) {
102736               er.linkDirectedEdgesForMinimalEdgeRings();
102737               var minEdgeRings = er.buildMinimalRings();
102738               var shell = this$1.findShell(minEdgeRings);
102739               if (shell !== null) {
102740                 this$1.placePolygonHoles(shell, minEdgeRings);
102741                 shellList.add(shell);
102742               } else {
102743                 freeHoleList.addAll(minEdgeRings);
102744               }
102745             } else {
102746               edgeRings.add(er);
102747             }
102748           }
102749           return edgeRings
102750         };
102751         PolygonBuilder.prototype.containsPoint = function containsPoint (p) {
102752           for (var it = this._shellList.iterator(); it.hasNext();) {
102753             var er = it.next();
102754             if (er.containsPoint(p)) { return true }
102755           }
102756           return false
102757         };
102758         PolygonBuilder.prototype.buildMaximalEdgeRings = function buildMaximalEdgeRings (dirEdges) {
102759             var this$1 = this;
102760
102761           var maxEdgeRings = new ArrayList();
102762           for (var it = dirEdges.iterator(); it.hasNext();) {
102763             var de = it.next();
102764             if (de.isInResult() && de.getLabel().isArea()) {
102765               if (de.getEdgeRing() === null) {
102766                 var er = new MaximalEdgeRing(de, this$1._geometryFactory);
102767                 maxEdgeRings.add(er);
102768                 er.setInResult();
102769               }
102770             }
102771           }
102772           return maxEdgeRings
102773         };
102774         PolygonBuilder.prototype.placePolygonHoles = function placePolygonHoles (shell, minEdgeRings) {
102775           for (var it = minEdgeRings.iterator(); it.hasNext();) {
102776             var er = it.next();
102777             if (er.isHole()) {
102778               er.setShell(shell);
102779             }
102780           }
102781         };
102782         PolygonBuilder.prototype.getPolygons = function getPolygons () {
102783           var resultPolyList = this.computePolygons(this._shellList);
102784           return resultPolyList
102785         };
102786         PolygonBuilder.prototype.findEdgeRingContaining = function findEdgeRingContaining (testEr, shellList) {
102787           var testRing = testEr.getLinearRing();
102788           var testEnv = testRing.getEnvelopeInternal();
102789           var testPt = testRing.getCoordinateN(0);
102790           var minShell = null;
102791           var minEnv = null;
102792           for (var it = shellList.iterator(); it.hasNext();) {
102793             var tryShell = it.next();
102794             var tryRing = tryShell.getLinearRing();
102795             var tryEnv = tryRing.getEnvelopeInternal();
102796             if (minShell !== null) { minEnv = minShell.getLinearRing().getEnvelopeInternal(); }
102797             var isContained = false;
102798             if (tryEnv.contains(testEnv) && CGAlgorithms.isPointInRing(testPt, tryRing.getCoordinates())) { isContained = true; }
102799             if (isContained) {
102800               if (minShell === null || minEnv.contains(tryEnv)) {
102801                 minShell = tryShell;
102802               }
102803             }
102804           }
102805           return minShell
102806         };
102807         PolygonBuilder.prototype.findShell = function findShell (minEdgeRings) {
102808           var shellCount = 0;
102809           var shell = null;
102810           for (var it = minEdgeRings.iterator(); it.hasNext();) {
102811             var er = it.next();
102812             if (!er.isHole()) {
102813               shell = er;
102814               shellCount++;
102815             }
102816           }
102817           Assert.isTrue(shellCount <= 1, 'found two shells in MinimalEdgeRing list');
102818           return shell
102819         };
102820         PolygonBuilder.prototype.add = function add () {
102821           if (arguments.length === 1) {
102822             var graph = arguments[0];
102823             this.add(graph.getEdgeEnds(), graph.getNodes());
102824           } else if (arguments.length === 2) {
102825             var dirEdges = arguments[0];
102826             var nodes = arguments[1];
102827             PlanarGraph.linkResultDirectedEdges(nodes);
102828             var maxEdgeRings = this.buildMaximalEdgeRings(dirEdges);
102829             var freeHoleList = new ArrayList();
102830             var edgeRings = this.buildMinimalEdgeRings(maxEdgeRings, this._shellList, freeHoleList);
102831             this.sortShellsAndHoles(edgeRings, this._shellList, freeHoleList);
102832             this.placeFreeHoles(this._shellList, freeHoleList);
102833           }
102834         };
102835         PolygonBuilder.prototype.interfaces_ = function interfaces_ () {
102836           return []
102837         };
102838         PolygonBuilder.prototype.getClass = function getClass () {
102839           return PolygonBuilder
102840         };
102841
102842         var Boundable = function Boundable () {};
102843
102844         Boundable.prototype.getBounds = function getBounds () {};
102845         Boundable.prototype.interfaces_ = function interfaces_ () {
102846           return []
102847         };
102848         Boundable.prototype.getClass = function getClass () {
102849           return Boundable
102850         };
102851
102852         var ItemBoundable = function ItemBoundable () {
102853           this._bounds = null;
102854           this._item = null;
102855           var bounds = arguments[0];
102856           var item = arguments[1];
102857           this._bounds = bounds;
102858           this._item = item;
102859         };
102860         ItemBoundable.prototype.getItem = function getItem () {
102861           return this._item
102862         };
102863         ItemBoundable.prototype.getBounds = function getBounds () {
102864           return this._bounds
102865         };
102866         ItemBoundable.prototype.interfaces_ = function interfaces_ () {
102867           return [Boundable, Serializable]
102868         };
102869         ItemBoundable.prototype.getClass = function getClass () {
102870           return ItemBoundable
102871         };
102872
102873         var PriorityQueue = function PriorityQueue () {
102874           this._size = null;
102875           this._items = null;
102876           this._size = 0;
102877           this._items = new ArrayList();
102878           this._items.add(null);
102879         };
102880         PriorityQueue.prototype.poll = function poll () {
102881           if (this.isEmpty()) { return null }
102882           var minItem = this._items.get(1);
102883           this._items.set(1, this._items.get(this._size));
102884           this._size -= 1;
102885           this.reorder(1);
102886           return minItem
102887         };
102888         PriorityQueue.prototype.size = function size () {
102889           return this._size
102890         };
102891         PriorityQueue.prototype.reorder = function reorder (hole) {
102892             var this$1 = this;
102893
102894           var child = null;
102895           var tmp = this._items.get(hole);
102896           for (; hole * 2 <= this._size; hole = child) {
102897             child = hole * 2;
102898             if (child !== this$1._size && this$1._items.get(child + 1).compareTo(this$1._items.get(child)) < 0) { child++; }
102899             if (this$1._items.get(child).compareTo(tmp) < 0) { this$1._items.set(hole, this$1._items.get(child)); } else { break }
102900           }
102901           this._items.set(hole, tmp);
102902         };
102903         PriorityQueue.prototype.clear = function clear () {
102904           this._size = 0;
102905           this._items.clear();
102906         };
102907         PriorityQueue.prototype.isEmpty = function isEmpty () {
102908           return this._size === 0
102909         };
102910         PriorityQueue.prototype.add = function add (x) {
102911             var this$1 = this;
102912
102913           this._items.add(null);
102914           this._size += 1;
102915           var hole = this._size;
102916           this._items.set(0, x);
102917           for (; x.compareTo(this._items.get(Math.trunc(hole / 2))) < 0; hole /= 2) {
102918             this$1._items.set(hole, this$1._items.get(Math.trunc(hole / 2)));
102919           }
102920           this._items.set(hole, x);
102921         };
102922         PriorityQueue.prototype.interfaces_ = function interfaces_ () {
102923           return []
102924         };
102925         PriorityQueue.prototype.getClass = function getClass () {
102926           return PriorityQueue
102927         };
102928
102929         var ItemVisitor = function ItemVisitor () {};
102930
102931         ItemVisitor.prototype.visitItem = function visitItem (item) {};
102932         ItemVisitor.prototype.interfaces_ = function interfaces_ () {
102933           return []
102934         };
102935         ItemVisitor.prototype.getClass = function getClass () {
102936           return ItemVisitor
102937         };
102938
102939         var SpatialIndex = function SpatialIndex () {};
102940
102941         SpatialIndex.prototype.insert = function insert (itemEnv, item) {};
102942         SpatialIndex.prototype.remove = function remove (itemEnv, item) {};
102943         SpatialIndex.prototype.query = function query () {
102944           // if (arguments.length === 1) {
102945           // const searchEnv = arguments[0]
102946           // } else if (arguments.length === 2) {
102947           // const searchEnv = arguments[0]
102948           // const visitor = arguments[1]
102949           // }
102950         };
102951         SpatialIndex.prototype.interfaces_ = function interfaces_ () {
102952           return []
102953         };
102954         SpatialIndex.prototype.getClass = function getClass () {
102955           return SpatialIndex
102956         };
102957
102958         var AbstractNode = function AbstractNode () {
102959           this._childBoundables = new ArrayList();
102960           this._bounds = null;
102961           this._level = null;
102962           if (arguments.length === 0) ; else if (arguments.length === 1) {
102963             var level = arguments[0];
102964             this._level = level;
102965           }
102966         };
102967
102968         var staticAccessors$22 = { serialVersionUID: { configurable: true } };
102969         AbstractNode.prototype.getLevel = function getLevel () {
102970           return this._level
102971         };
102972         AbstractNode.prototype.size = function size () {
102973           return this._childBoundables.size()
102974         };
102975         AbstractNode.prototype.getChildBoundables = function getChildBoundables () {
102976           return this._childBoundables
102977         };
102978         AbstractNode.prototype.addChildBoundable = function addChildBoundable (childBoundable) {
102979           Assert.isTrue(this._bounds === null);
102980           this._childBoundables.add(childBoundable);
102981         };
102982         AbstractNode.prototype.isEmpty = function isEmpty () {
102983           return this._childBoundables.isEmpty()
102984         };
102985         AbstractNode.prototype.getBounds = function getBounds () {
102986           if (this._bounds === null) {
102987             this._bounds = this.computeBounds();
102988           }
102989           return this._bounds
102990         };
102991         AbstractNode.prototype.interfaces_ = function interfaces_ () {
102992           return [Boundable, Serializable]
102993         };
102994         AbstractNode.prototype.getClass = function getClass () {
102995           return AbstractNode
102996         };
102997         staticAccessors$22.serialVersionUID.get = function () { return 6493722185909573708 };
102998
102999         Object.defineProperties( AbstractNode, staticAccessors$22 );
103000
103001         var Collections = function Collections () {};
103002
103003         Collections.reverseOrder = function reverseOrder () {
103004           return {
103005             compare: function compare (a, b) {
103006               return b.compareTo(a)
103007             }
103008           }
103009         };
103010         Collections.min = function min (l) {
103011           Collections.sort(l);
103012           return l.get(0)
103013         };
103014         Collections.sort = function sort (l, c) {
103015           var a = l.toArray();
103016           if (c) {
103017             Arrays.sort(a, c);
103018           } else {
103019             Arrays.sort(a);
103020           }
103021           var i = l.iterator();
103022           for (var pos = 0, alen = a.length; pos < alen; pos++) {
103023             i.next();
103024             i.set(a[pos]);
103025           }
103026         };
103027         Collections.singletonList = function singletonList (o) {
103028           var arrayList = new ArrayList();
103029           arrayList.add(o);
103030           return arrayList
103031         };
103032
103033         var BoundablePair = function BoundablePair () {
103034           this._boundable1 = null;
103035           this._boundable2 = null;
103036           this._distance = null;
103037           this._itemDistance = null;
103038           var boundable1 = arguments[0];
103039           var boundable2 = arguments[1];
103040           var itemDistance = arguments[2];
103041           this._boundable1 = boundable1;
103042           this._boundable2 = boundable2;
103043           this._itemDistance = itemDistance;
103044           this._distance = this.distance();
103045         };
103046         BoundablePair.prototype.expandToQueue = function expandToQueue (priQ, minDistance) {
103047           var isComp1 = BoundablePair.isComposite(this._boundable1);
103048           var isComp2 = BoundablePair.isComposite(this._boundable2);
103049           if (isComp1 && isComp2) {
103050             if (BoundablePair.area(this._boundable1) > BoundablePair.area(this._boundable2)) {
103051               this.expand(this._boundable1, this._boundable2, priQ, minDistance);
103052               return null
103053             } else {
103054               this.expand(this._boundable2, this._boundable1, priQ, minDistance);
103055               return null
103056             }
103057           } else if (isComp1) {
103058             this.expand(this._boundable1, this._boundable2, priQ, minDistance);
103059             return null
103060           } else if (isComp2) {
103061             this.expand(this._boundable2, this._boundable1, priQ, minDistance);
103062             return null
103063           }
103064           throw new IllegalArgumentException('neither boundable is composite')
103065         };
103066         BoundablePair.prototype.isLeaves = function isLeaves () {
103067           return !(BoundablePair.isComposite(this._boundable1) || BoundablePair.isComposite(this._boundable2))
103068         };
103069         BoundablePair.prototype.compareTo = function compareTo (o) {
103070           var nd = o;
103071           if (this._distance < nd._distance) { return -1 }
103072           if (this._distance > nd._distance) { return 1 }
103073           return 0
103074         };
103075         BoundablePair.prototype.expand = function expand (bndComposite, bndOther, priQ, minDistance) {
103076             var this$1 = this;
103077
103078           var children = bndComposite.getChildBoundables();
103079           for (var i = children.iterator(); i.hasNext();) {
103080             var child = i.next();
103081             var bp = new BoundablePair(child, bndOther, this$1._itemDistance);
103082             if (bp.getDistance() < minDistance) {
103083               priQ.add(bp);
103084             }
103085           }
103086         };
103087         BoundablePair.prototype.getBoundable = function getBoundable (i) {
103088           if (i === 0) { return this._boundable1 }
103089           return this._boundable2
103090         };
103091         BoundablePair.prototype.getDistance = function getDistance () {
103092           return this._distance
103093         };
103094         BoundablePair.prototype.distance = function distance () {
103095           if (this.isLeaves()) {
103096             return this._itemDistance.distance(this._boundable1, this._boundable2)
103097           }
103098           return this._boundable1.getBounds().distance(this._boundable2.getBounds())
103099         };
103100         BoundablePair.prototype.interfaces_ = function interfaces_ () {
103101           return [Comparable]
103102         };
103103         BoundablePair.prototype.getClass = function getClass () {
103104           return BoundablePair
103105         };
103106         BoundablePair.area = function area (b) {
103107           return b.getBounds().getArea()
103108         };
103109         BoundablePair.isComposite = function isComposite (item) {
103110           return item instanceof AbstractNode
103111         };
103112
103113         var AbstractSTRtree = function AbstractSTRtree () {
103114           this._root = null;
103115           this._built = false;
103116           this._itemBoundables = new ArrayList();
103117           this._nodeCapacity = null;
103118           if (arguments.length === 0) {
103119             var nodeCapacity = AbstractSTRtree.DEFAULT_NODE_CAPACITY;
103120             this._nodeCapacity = nodeCapacity;
103121           } else if (arguments.length === 1) {
103122             var nodeCapacity$1 = arguments[0];
103123             Assert.isTrue(nodeCapacity$1 > 1, 'Node capacity must be greater than 1');
103124             this._nodeCapacity = nodeCapacity$1;
103125           }
103126         };
103127
103128         var staticAccessors$23 = { IntersectsOp: { configurable: true },serialVersionUID: { configurable: true },DEFAULT_NODE_CAPACITY: { configurable: true } };
103129         AbstractSTRtree.prototype.getNodeCapacity = function getNodeCapacity () {
103130           return this._nodeCapacity
103131         };
103132         AbstractSTRtree.prototype.lastNode = function lastNode (nodes) {
103133           return nodes.get(nodes.size() - 1)
103134         };
103135         AbstractSTRtree.prototype.size = function size () {
103136             var this$1 = this;
103137
103138           if (arguments.length === 0) {
103139             if (this.isEmpty()) {
103140               return 0
103141             }
103142             this.build();
103143             return this.size(this._root)
103144           } else if (arguments.length === 1) {
103145             var node = arguments[0];
103146             var size = 0;
103147             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103148               var childBoundable = i.next();
103149               if (childBoundable instanceof AbstractNode) {
103150                 size += this$1.size(childBoundable);
103151               } else if (childBoundable instanceof ItemBoundable) {
103152                 size += 1;
103153               }
103154             }
103155             return size
103156           }
103157         };
103158         AbstractSTRtree.prototype.removeItem = function removeItem (node, item) {
103159           var childToRemove = null;
103160           for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103161             var childBoundable = i.next();
103162             if (childBoundable instanceof ItemBoundable) {
103163               if (childBoundable.getItem() === item) { childToRemove = childBoundable; }
103164             }
103165           }
103166           if (childToRemove !== null) {
103167             node.getChildBoundables().remove(childToRemove);
103168             return true
103169           }
103170           return false
103171         };
103172         AbstractSTRtree.prototype.itemsTree = function itemsTree () {
103173             var this$1 = this;
103174
103175           if (arguments.length === 0) {
103176             this.build();
103177             var valuesTree = this.itemsTree(this._root);
103178             if (valuesTree === null) { return new ArrayList() }
103179             return valuesTree
103180           } else if (arguments.length === 1) {
103181             var node = arguments[0];
103182             var valuesTreeForNode = new ArrayList();
103183             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103184               var childBoundable = i.next();
103185               if (childBoundable instanceof AbstractNode) {
103186                 var valuesTreeForChild = this$1.itemsTree(childBoundable);
103187                 if (valuesTreeForChild !== null) { valuesTreeForNode.add(valuesTreeForChild); }
103188               } else if (childBoundable instanceof ItemBoundable) {
103189                 valuesTreeForNode.add(childBoundable.getItem());
103190               } else {
103191                 Assert.shouldNeverReachHere();
103192               }
103193             }
103194             if (valuesTreeForNode.size() <= 0) { return null }
103195             return valuesTreeForNode
103196           }
103197         };
103198         AbstractSTRtree.prototype.insert = function insert (bounds, item) {
103199           Assert.isTrue(!this._built, 'Cannot insert items into an STR packed R-tree after it has been built.');
103200           this._itemBoundables.add(new ItemBoundable(bounds, item));
103201         };
103202         AbstractSTRtree.prototype.boundablesAtLevel = function boundablesAtLevel () {
103203             var this$1 = this;
103204
103205           if (arguments.length === 1) {
103206             var level = arguments[0];
103207             var boundables = new ArrayList();
103208             this.boundablesAtLevel(level, this._root, boundables);
103209             return boundables
103210           } else if (arguments.length === 3) {
103211             var level$1 = arguments[0];
103212             var top = arguments[1];
103213             var boundables$1 = arguments[2];
103214             Assert.isTrue(level$1 > -2);
103215             if (top.getLevel() === level$1) {
103216               boundables$1.add(top);
103217               return null
103218             }
103219             for (var i = top.getChildBoundables().iterator(); i.hasNext();) {
103220               var boundable = i.next();
103221               if (boundable instanceof AbstractNode) {
103222                 this$1.boundablesAtLevel(level$1, boundable, boundables$1);
103223               } else {
103224                 Assert.isTrue(boundable instanceof ItemBoundable);
103225                 if (level$1 === -1) {
103226                   boundables$1.add(boundable);
103227                 }
103228               }
103229             }
103230             return null
103231           }
103232         };
103233         AbstractSTRtree.prototype.query = function query () {
103234             var this$1 = this;
103235
103236           if (arguments.length === 1) {
103237             var searchBounds = arguments[0];
103238             this.build();
103239             var matches = new ArrayList();
103240             if (this.isEmpty()) {
103241               return matches
103242             }
103243             if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds)) {
103244               this.query(searchBounds, this._root, matches);
103245             }
103246             return matches
103247           } else if (arguments.length === 2) {
103248             var searchBounds$1 = arguments[0];
103249             var visitor = arguments[1];
103250             this.build();
103251             if (this.isEmpty()) {
103252               return null
103253             }
103254             if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds$1)) {
103255               this.query(searchBounds$1, this._root, visitor);
103256             }
103257           } else if (arguments.length === 3) {
103258             if (hasInterface(arguments[2], ItemVisitor) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103259               var searchBounds$2 = arguments[0];
103260               var node = arguments[1];
103261               var visitor$1 = arguments[2];
103262               var childBoundables = node.getChildBoundables();
103263               for (var i = 0; i < childBoundables.size(); i++) {
103264                 var childBoundable = childBoundables.get(i);
103265                 if (!this$1.getIntersectsOp().intersects(childBoundable.getBounds(), searchBounds$2)) {
103266                   continue
103267                 }
103268                 if (childBoundable instanceof AbstractNode) {
103269                   this$1.query(searchBounds$2, childBoundable, visitor$1);
103270                 } else if (childBoundable instanceof ItemBoundable) {
103271                   visitor$1.visitItem(childBoundable.getItem());
103272                 } else {
103273                   Assert.shouldNeverReachHere();
103274                 }
103275               }
103276             } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103277               var searchBounds$3 = arguments[0];
103278               var node$1 = arguments[1];
103279               var matches$1 = arguments[2];
103280               var childBoundables$1 = node$1.getChildBoundables();
103281               for (var i$1 = 0; i$1 < childBoundables$1.size(); i$1++) {
103282                 var childBoundable$1 = childBoundables$1.get(i$1);
103283                 if (!this$1.getIntersectsOp().intersects(childBoundable$1.getBounds(), searchBounds$3)) {
103284                   continue
103285                 }
103286                 if (childBoundable$1 instanceof AbstractNode) {
103287                   this$1.query(searchBounds$3, childBoundable$1, matches$1);
103288                 } else if (childBoundable$1 instanceof ItemBoundable) {
103289                   matches$1.add(childBoundable$1.getItem());
103290                 } else {
103291                   Assert.shouldNeverReachHere();
103292                 }
103293               }
103294             }
103295           }
103296         };
103297         AbstractSTRtree.prototype.build = function build () {
103298           if (this._built) { return null }
103299           this._root = this._itemBoundables.isEmpty() ? this.createNode(0) : this.createHigherLevels(this._itemBoundables, -1);
103300           this._itemBoundables = null;
103301           this._built = true;
103302         };
103303         AbstractSTRtree.prototype.getRoot = function getRoot () {
103304           this.build();
103305           return this._root
103306         };
103307         AbstractSTRtree.prototype.remove = function remove () {
103308             var this$1 = this;
103309
103310           if (arguments.length === 2) {
103311             var searchBounds = arguments[0];
103312             var item = arguments[1];
103313             this.build();
103314             if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds)) {
103315               return this.remove(searchBounds, this._root, item)
103316             }
103317             return false
103318           } else if (arguments.length === 3) {
103319             var searchBounds$1 = arguments[0];
103320             var node = arguments[1];
103321             var item$1 = arguments[2];
103322             var found = this.removeItem(node, item$1);
103323             if (found) { return true }
103324             var childToPrune = null;
103325             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103326               var childBoundable = i.next();
103327               if (!this$1.getIntersectsOp().intersects(childBoundable.getBounds(), searchBounds$1)) {
103328                 continue
103329               }
103330               if (childBoundable instanceof AbstractNode) {
103331                 found = this$1.remove(searchBounds$1, childBoundable, item$1);
103332                 if (found) {
103333                   childToPrune = childBoundable;
103334                   break
103335                 }
103336               }
103337             }
103338             if (childToPrune !== null) {
103339               if (childToPrune.getChildBoundables().isEmpty()) {
103340                 node.getChildBoundables().remove(childToPrune);
103341               }
103342             }
103343             return found
103344           }
103345         };
103346         AbstractSTRtree.prototype.createHigherLevels = function createHigherLevels (boundablesOfALevel, level) {
103347           Assert.isTrue(!boundablesOfALevel.isEmpty());
103348           var parentBoundables = this.createParentBoundables(boundablesOfALevel, level + 1);
103349           if (parentBoundables.size() === 1) {
103350             return parentBoundables.get(0)
103351           }
103352           return this.createHigherLevels(parentBoundables, level + 1)
103353         };
103354         AbstractSTRtree.prototype.depth = function depth () {
103355             var this$1 = this;
103356
103357           if (arguments.length === 0) {
103358             if (this.isEmpty()) {
103359               return 0
103360             }
103361             this.build();
103362             return this.depth(this._root)
103363           } else if (arguments.length === 1) {
103364             var node = arguments[0];
103365             var maxChildDepth = 0;
103366             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103367               var childBoundable = i.next();
103368               if (childBoundable instanceof AbstractNode) {
103369                 var childDepth = this$1.depth(childBoundable);
103370                 if (childDepth > maxChildDepth) { maxChildDepth = childDepth; }
103371               }
103372             }
103373             return maxChildDepth + 1
103374           }
103375         };
103376         AbstractSTRtree.prototype.createParentBoundables = function createParentBoundables (childBoundables, newLevel) {
103377             var this$1 = this;
103378
103379           Assert.isTrue(!childBoundables.isEmpty());
103380           var parentBoundables = new ArrayList();
103381           parentBoundables.add(this.createNode(newLevel));
103382           var sortedChildBoundables = new ArrayList(childBoundables);
103383           Collections.sort(sortedChildBoundables, this.getComparator());
103384           for (var i = sortedChildBoundables.iterator(); i.hasNext();) {
103385             var childBoundable = i.next();
103386             if (this$1.lastNode(parentBoundables).getChildBoundables().size() === this$1.getNodeCapacity()) {
103387               parentBoundables.add(this$1.createNode(newLevel));
103388             }
103389             this$1.lastNode(parentBoundables).addChildBoundable(childBoundable);
103390           }
103391           return parentBoundables
103392         };
103393         AbstractSTRtree.prototype.isEmpty = function isEmpty () {
103394           if (!this._built) { return this._itemBoundables.isEmpty() }
103395           return this._root.isEmpty()
103396         };
103397         AbstractSTRtree.prototype.interfaces_ = function interfaces_ () {
103398           return [Serializable]
103399         };
103400         AbstractSTRtree.prototype.getClass = function getClass () {
103401           return AbstractSTRtree
103402         };
103403         AbstractSTRtree.compareDoubles = function compareDoubles (a, b) {
103404           return a > b ? 1 : a < b ? -1 : 0
103405         };
103406         staticAccessors$23.IntersectsOp.get = function () { return IntersectsOp };
103407         staticAccessors$23.serialVersionUID.get = function () { return -3886435814360241337 };
103408         staticAccessors$23.DEFAULT_NODE_CAPACITY.get = function () { return 10 };
103409
103410         Object.defineProperties( AbstractSTRtree, staticAccessors$23 );
103411
103412         var IntersectsOp = function IntersectsOp () {};
103413
103414         var ItemDistance = function ItemDistance () {};
103415
103416         ItemDistance.prototype.distance = function distance (item1, item2) {};
103417         ItemDistance.prototype.interfaces_ = function interfaces_ () {
103418           return []
103419         };
103420         ItemDistance.prototype.getClass = function getClass () {
103421           return ItemDistance
103422         };
103423
103424         var STRtree = (function (AbstractSTRtree$$1) {
103425           function STRtree (nodeCapacity) {
103426             nodeCapacity = nodeCapacity || STRtree.DEFAULT_NODE_CAPACITY;
103427             AbstractSTRtree$$1.call(this, nodeCapacity);
103428           }
103429
103430           if ( AbstractSTRtree$$1 ) { STRtree.__proto__ = AbstractSTRtree$$1; }
103431           STRtree.prototype = Object.create( AbstractSTRtree$$1 && AbstractSTRtree$$1.prototype );
103432           STRtree.prototype.constructor = STRtree;
103433
103434           var staticAccessors = { STRtreeNode: { configurable: true },serialVersionUID: { configurable: true },xComparator: { configurable: true },yComparator: { configurable: true },intersectsOp: { configurable: true },DEFAULT_NODE_CAPACITY: { configurable: true } };
103435           STRtree.prototype.createParentBoundablesFromVerticalSlices = function createParentBoundablesFromVerticalSlices (verticalSlices, newLevel) {
103436             var this$1 = this;
103437
103438             Assert.isTrue(verticalSlices.length > 0);
103439             var parentBoundables = new ArrayList();
103440             for (var i = 0; i < verticalSlices.length; i++) {
103441               parentBoundables.addAll(this$1.createParentBoundablesFromVerticalSlice(verticalSlices[i], newLevel));
103442             }
103443             return parentBoundables
103444           };
103445           STRtree.prototype.createNode = function createNode (level) {
103446             return new STRtreeNode(level)
103447           };
103448           STRtree.prototype.size = function size () {
103449             if (arguments.length === 0) {
103450               return AbstractSTRtree$$1.prototype.size.call(this)
103451             } else { return AbstractSTRtree$$1.prototype.size.apply(this, arguments) }
103452           };
103453           STRtree.prototype.insert = function insert () {
103454             if (arguments.length === 2) {
103455               var itemEnv = arguments[0];
103456               var item = arguments[1];
103457               if (itemEnv.isNull()) {
103458                 return null
103459               }
103460               AbstractSTRtree$$1.prototype.insert.call(this, itemEnv, item);
103461             } else { return AbstractSTRtree$$1.prototype.insert.apply(this, arguments) }
103462           };
103463           STRtree.prototype.getIntersectsOp = function getIntersectsOp () {
103464             return STRtree.intersectsOp
103465           };
103466           STRtree.prototype.verticalSlices = function verticalSlices (childBoundables, sliceCount) {
103467             var sliceCapacity = Math.trunc(Math.ceil(childBoundables.size() / sliceCount));
103468             var slices = new Array(sliceCount).fill(null);
103469             var i = childBoundables.iterator();
103470             for (var j = 0; j < sliceCount; j++) {
103471               slices[j] = new ArrayList();
103472               var boundablesAddedToSlice = 0;
103473               while (i.hasNext() && boundablesAddedToSlice < sliceCapacity) {
103474                 var childBoundable = i.next();
103475                 slices[j].add(childBoundable);
103476                 boundablesAddedToSlice++;
103477               }
103478             }
103479             return slices
103480           };
103481           STRtree.prototype.query = function query () {
103482             if (arguments.length === 1) {
103483               var searchEnv = arguments[0];
103484               return AbstractSTRtree$$1.prototype.query.call(this, searchEnv)
103485             } else if (arguments.length === 2) {
103486               var searchEnv$1 = arguments[0];
103487               var visitor = arguments[1];
103488               AbstractSTRtree$$1.prototype.query.call(this, searchEnv$1, visitor);
103489             } else if (arguments.length === 3) {
103490               if (hasInterface(arguments[2], ItemVisitor) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103491                 var searchBounds = arguments[0];
103492                 var node = arguments[1];
103493                 var visitor$1 = arguments[2];
103494                 AbstractSTRtree$$1.prototype.query.call(this, searchBounds, node, visitor$1);
103495               } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103496                 var searchBounds$1 = arguments[0];
103497                 var node$1 = arguments[1];
103498                 var matches = arguments[2];
103499                 AbstractSTRtree$$1.prototype.query.call(this, searchBounds$1, node$1, matches);
103500               }
103501             }
103502           };
103503           STRtree.prototype.getComparator = function getComparator () {
103504             return STRtree.yComparator
103505           };
103506           STRtree.prototype.createParentBoundablesFromVerticalSlice = function createParentBoundablesFromVerticalSlice (childBoundables, newLevel) {
103507             return AbstractSTRtree$$1.prototype.createParentBoundables.call(this, childBoundables, newLevel)
103508           };
103509           STRtree.prototype.remove = function remove () {
103510             if (arguments.length === 2) {
103511               var itemEnv = arguments[0];
103512               var item = arguments[1];
103513               return AbstractSTRtree$$1.prototype.remove.call(this, itemEnv, item)
103514             } else { return AbstractSTRtree$$1.prototype.remove.apply(this, arguments) }
103515           };
103516           STRtree.prototype.depth = function depth () {
103517             if (arguments.length === 0) {
103518               return AbstractSTRtree$$1.prototype.depth.call(this)
103519             } else { return AbstractSTRtree$$1.prototype.depth.apply(this, arguments) }
103520           };
103521           STRtree.prototype.createParentBoundables = function createParentBoundables (childBoundables, newLevel) {
103522             Assert.isTrue(!childBoundables.isEmpty());
103523             var minLeafCount = Math.trunc(Math.ceil(childBoundables.size() / this.getNodeCapacity()));
103524             var sortedChildBoundables = new ArrayList(childBoundables);
103525             Collections.sort(sortedChildBoundables, STRtree.xComparator);
103526             var verticalSlices = this.verticalSlices(sortedChildBoundables, Math.trunc(Math.ceil(Math.sqrt(minLeafCount))));
103527             return this.createParentBoundablesFromVerticalSlices(verticalSlices, newLevel)
103528           };
103529           STRtree.prototype.nearestNeighbour = function nearestNeighbour () {
103530             if (arguments.length === 1) {
103531               if (hasInterface(arguments[0], ItemDistance)) {
103532                 var itemDist = arguments[0];
103533                 var bp = new BoundablePair(this.getRoot(), this.getRoot(), itemDist);
103534                 return this.nearestNeighbour(bp)
103535               } else if (arguments[0] instanceof BoundablePair) {
103536                 var initBndPair = arguments[0];
103537                 return this.nearestNeighbour(initBndPair, Double.POSITIVE_INFINITY)
103538               }
103539             } else if (arguments.length === 2) {
103540               if (arguments[0] instanceof STRtree && hasInterface(arguments[1], ItemDistance)) {
103541                 var tree = arguments[0];
103542                 var itemDist$1 = arguments[1];
103543                 var bp$1 = new BoundablePair(this.getRoot(), tree.getRoot(), itemDist$1);
103544                 return this.nearestNeighbour(bp$1)
103545               } else if (arguments[0] instanceof BoundablePair && typeof arguments[1] === 'number') {
103546                 var initBndPair$1 = arguments[0];
103547                 var maxDistance = arguments[1];
103548                 var distanceLowerBound = maxDistance;
103549                 var minPair = null;
103550                 var priQ = new PriorityQueue();
103551                 priQ.add(initBndPair$1);
103552                 while (!priQ.isEmpty() && distanceLowerBound > 0.0) {
103553                   var bndPair = priQ.poll();
103554                   var currentDistance = bndPair.getDistance();
103555                   if (currentDistance >= distanceLowerBound) { break }
103556                   if (bndPair.isLeaves()) {
103557                     distanceLowerBound = currentDistance;
103558                     minPair = bndPair;
103559                   } else {
103560                     bndPair.expandToQueue(priQ, distanceLowerBound);
103561                   }
103562                 }
103563                 return [minPair.getBoundable(0).getItem(), minPair.getBoundable(1).getItem()]
103564               }
103565             } else if (arguments.length === 3) {
103566               var env = arguments[0];
103567               var item = arguments[1];
103568               var itemDist$2 = arguments[2];
103569               var bnd = new ItemBoundable(env, item);
103570               var bp$2 = new BoundablePair(this.getRoot(), bnd, itemDist$2);
103571               return this.nearestNeighbour(bp$2)[0]
103572             }
103573           };
103574           STRtree.prototype.interfaces_ = function interfaces_ () {
103575             return [SpatialIndex, Serializable]
103576           };
103577           STRtree.prototype.getClass = function getClass () {
103578             return STRtree
103579           };
103580           STRtree.centreX = function centreX (e) {
103581             return STRtree.avg(e.getMinX(), e.getMaxX())
103582           };
103583           STRtree.avg = function avg (a, b) {
103584             return (a + b) / 2
103585           };
103586           STRtree.centreY = function centreY (e) {
103587             return STRtree.avg(e.getMinY(), e.getMaxY())
103588           };
103589           staticAccessors.STRtreeNode.get = function () { return STRtreeNode };
103590           staticAccessors.serialVersionUID.get = function () { return 259274702368956900 };
103591           staticAccessors.xComparator.get = function () {
103592             return {
103593               interfaces_: function () {
103594                 return [Comparator]
103595               },
103596               compare: function (o1, o2) {
103597                 return AbstractSTRtree$$1.compareDoubles(STRtree.centreX(o1.getBounds()), STRtree.centreX(o2.getBounds()))
103598               }
103599             }
103600           };
103601           staticAccessors.yComparator.get = function () {
103602             return {
103603               interfaces_: function () {
103604                 return [Comparator]
103605               },
103606               compare: function (o1, o2) {
103607                 return AbstractSTRtree$$1.compareDoubles(STRtree.centreY(o1.getBounds()), STRtree.centreY(o2.getBounds()))
103608               }
103609             }
103610           };
103611           staticAccessors.intersectsOp.get = function () {
103612             return {
103613               interfaces_: function () {
103614                 return [AbstractSTRtree$$1.IntersectsOp]
103615               },
103616               intersects: function (aBounds, bBounds) {
103617                 return aBounds.intersects(bBounds)
103618               }
103619             }
103620           };
103621           staticAccessors.DEFAULT_NODE_CAPACITY.get = function () { return 10 };
103622
103623           Object.defineProperties( STRtree, staticAccessors );
103624
103625           return STRtree;
103626         }(AbstractSTRtree));
103627
103628         var STRtreeNode = (function (AbstractNode$$1) {
103629           function STRtreeNode () {
103630             var level = arguments[0];
103631             AbstractNode$$1.call(this, level);
103632           }
103633
103634           if ( AbstractNode$$1 ) { STRtreeNode.__proto__ = AbstractNode$$1; }
103635           STRtreeNode.prototype = Object.create( AbstractNode$$1 && AbstractNode$$1.prototype );
103636           STRtreeNode.prototype.constructor = STRtreeNode;
103637           STRtreeNode.prototype.computeBounds = function computeBounds () {
103638             var bounds = null;
103639             for (var i = this.getChildBoundables().iterator(); i.hasNext();) {
103640               var childBoundable = i.next();
103641               if (bounds === null) {
103642                 bounds = new Envelope(childBoundable.getBounds());
103643               } else {
103644                 bounds.expandToInclude(childBoundable.getBounds());
103645               }
103646             }
103647             return bounds
103648           };
103649           STRtreeNode.prototype.interfaces_ = function interfaces_ () {
103650             return []
103651           };
103652           STRtreeNode.prototype.getClass = function getClass () {
103653             return STRtreeNode
103654           };
103655
103656           return STRtreeNode;
103657         }(AbstractNode));
103658
103659         var SegmentPointComparator = function SegmentPointComparator () {};
103660
103661         SegmentPointComparator.prototype.interfaces_ = function interfaces_ () {
103662           return []
103663         };
103664         SegmentPointComparator.prototype.getClass = function getClass () {
103665           return SegmentPointComparator
103666         };
103667         SegmentPointComparator.relativeSign = function relativeSign (x0, x1) {
103668           if (x0 < x1) { return -1 }
103669           if (x0 > x1) { return 1 }
103670           return 0
103671         };
103672         SegmentPointComparator.compare = function compare (octant, p0, p1) {
103673           if (p0.equals2D(p1)) { return 0 }
103674           var xSign = SegmentPointComparator.relativeSign(p0.x, p1.x);
103675           var ySign = SegmentPointComparator.relativeSign(p0.y, p1.y);
103676           switch (octant) {
103677             case 0:
103678               return SegmentPointComparator.compareValue(xSign, ySign)
103679             case 1:
103680               return SegmentPointComparator.compareValue(ySign, xSign)
103681             case 2:
103682               return SegmentPointComparator.compareValue(ySign, -xSign)
103683             case 3:
103684               return SegmentPointComparator.compareValue(-xSign, ySign)
103685             case 4:
103686               return SegmentPointComparator.compareValue(-xSign, -ySign)
103687             case 5:
103688               return SegmentPointComparator.compareValue(-ySign, -xSign)
103689             case 6:
103690               return SegmentPointComparator.compareValue(-ySign, xSign)
103691             case 7:
103692               return SegmentPointComparator.compareValue(xSign, -ySign)
103693           }
103694           Assert.shouldNeverReachHere('invalid octant value');
103695           return 0
103696         };
103697         SegmentPointComparator.compareValue = function compareValue (compareSign0, compareSign1) {
103698           if (compareSign0 < 0) { return -1 }
103699           if (compareSign0 > 0) { return 1 }
103700           if (compareSign1 < 0) { return -1 }
103701           if (compareSign1 > 0) { return 1 }
103702           return 0
103703         };
103704
103705         var SegmentNode = function SegmentNode () {
103706           this._segString = null;
103707           this.coord = null;
103708           this.segmentIndex = null;
103709           this._segmentOctant = null;
103710           this._isInterior = null;
103711           var segString = arguments[0];
103712           var coord = arguments[1];
103713           var segmentIndex = arguments[2];
103714           var segmentOctant = arguments[3];
103715           this._segString = segString;
103716           this.coord = new Coordinate(coord);
103717           this.segmentIndex = segmentIndex;
103718           this._segmentOctant = segmentOctant;
103719           this._isInterior = !coord.equals2D(segString.getCoordinate(segmentIndex));
103720         };
103721         SegmentNode.prototype.getCoordinate = function getCoordinate () {
103722           return this.coord
103723         };
103724         SegmentNode.prototype.print = function print (out) {
103725           out.print(this.coord);
103726           out.print(' seg # = ' + this.segmentIndex);
103727         };
103728         SegmentNode.prototype.compareTo = function compareTo (obj) {
103729           var other = obj;
103730           if (this.segmentIndex < other.segmentIndex) { return -1 }
103731           if (this.segmentIndex > other.segmentIndex) { return 1 }
103732           if (this.coord.equals2D(other.coord)) { return 0 }
103733           return SegmentPointComparator.compare(this._segmentOctant, this.coord, other.coord)
103734         };
103735         SegmentNode.prototype.isEndPoint = function isEndPoint (maxSegmentIndex) {
103736           if (this.segmentIndex === 0 && !this._isInterior) { return true }
103737           if (this.segmentIndex === maxSegmentIndex) { return true }
103738           return false
103739         };
103740         SegmentNode.prototype.isInterior = function isInterior () {
103741           return this._isInterior
103742         };
103743         SegmentNode.prototype.interfaces_ = function interfaces_ () {
103744           return [Comparable]
103745         };
103746         SegmentNode.prototype.getClass = function getClass () {
103747           return SegmentNode
103748         };
103749
103750         // import Iterator from '../../../../java/util/Iterator'
103751         var SegmentNodeList = function SegmentNodeList () {
103752           this._nodeMap = new TreeMap();
103753           this._edge = null;
103754           var edge = arguments[0];
103755           this._edge = edge;
103756         };
103757         SegmentNodeList.prototype.getSplitCoordinates = function getSplitCoordinates () {
103758             var this$1 = this;
103759
103760           var coordList = new CoordinateList();
103761           this.addEndpoints();
103762           var it = this.iterator();
103763           var eiPrev = it.next();
103764           while (it.hasNext()) {
103765             var ei = it.next();
103766             this$1.addEdgeCoordinates(eiPrev, ei, coordList);
103767             eiPrev = ei;
103768           }
103769           return coordList.toCoordinateArray()
103770         };
103771         SegmentNodeList.prototype.addCollapsedNodes = function addCollapsedNodes () {
103772             var this$1 = this;
103773
103774           var collapsedVertexIndexes = new ArrayList();
103775           this.findCollapsesFromInsertedNodes(collapsedVertexIndexes);
103776           this.findCollapsesFromExistingVertices(collapsedVertexIndexes);
103777           for (var it = collapsedVertexIndexes.iterator(); it.hasNext();) {
103778             var vertexIndex = it.next().intValue();
103779             this$1.add(this$1._edge.getCoordinate(vertexIndex), vertexIndex);
103780           }
103781         };
103782         SegmentNodeList.prototype.print = function print (out) {
103783           out.println('Intersections:');
103784           for (var it = this.iterator(); it.hasNext();) {
103785             var ei = it.next();
103786             ei.print(out);
103787           }
103788         };
103789         SegmentNodeList.prototype.findCollapsesFromExistingVertices = function findCollapsesFromExistingVertices (collapsedVertexIndexes) {
103790             var this$1 = this;
103791
103792           for (var i = 0; i < this._edge.size() - 2; i++) {
103793             var p0 = this$1._edge.getCoordinate(i);
103794             // const p1 = this._edge.getCoordinate(i + 1)
103795             var p2 = this$1._edge.getCoordinate(i + 2);
103796             if (p0.equals2D(p2)) {
103797               collapsedVertexIndexes.add(new Integer(i + 1));
103798             }
103799           }
103800         };
103801         SegmentNodeList.prototype.addEdgeCoordinates = function addEdgeCoordinates (ei0, ei1, coordList) {
103802             var this$1 = this;
103803
103804           // let npts = ei1.segmentIndex - ei0.segmentIndex + 2
103805           var lastSegStartPt = this._edge.getCoordinate(ei1.segmentIndex);
103806           var useIntPt1 = ei1.isInterior() || !ei1.coord.equals2D(lastSegStartPt);
103807           // if (!useIntPt1) {
103808           // npts--
103809           // }
103810           // const ipt = 0
103811           coordList.add(new Coordinate(ei0.coord), false);
103812           for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
103813             coordList.add(this$1._edge.getCoordinate(i));
103814           }
103815           if (useIntPt1) {
103816             coordList.add(new Coordinate(ei1.coord));
103817           }
103818         };
103819         SegmentNodeList.prototype.iterator = function iterator () {
103820           return this._nodeMap.values().iterator()
103821         };
103822         SegmentNodeList.prototype.addSplitEdges = function addSplitEdges (edgeList) {
103823             var this$1 = this;
103824
103825           this.addEndpoints();
103826           this.addCollapsedNodes();
103827           var it = this.iterator();
103828           var eiPrev = it.next();
103829           while (it.hasNext()) {
103830             var ei = it.next();
103831             var newEdge = this$1.createSplitEdge(eiPrev, ei);
103832             edgeList.add(newEdge);
103833             eiPrev = ei;
103834           }
103835         };
103836         SegmentNodeList.prototype.findCollapseIndex = function findCollapseIndex (ei0, ei1, collapsedVertexIndex) {
103837           if (!ei0.coord.equals2D(ei1.coord)) { return false }
103838           var numVerticesBetween = ei1.segmentIndex - ei0.segmentIndex;
103839           if (!ei1.isInterior()) {
103840             numVerticesBetween--;
103841           }
103842           if (numVerticesBetween === 1) {
103843             collapsedVertexIndex[0] = ei0.segmentIndex + 1;
103844             return true
103845           }
103846           return false
103847         };
103848         SegmentNodeList.prototype.findCollapsesFromInsertedNodes = function findCollapsesFromInsertedNodes (collapsedVertexIndexes) {
103849             var this$1 = this;
103850
103851           var collapsedVertexIndex = new Array(1).fill(null);
103852           var it = this.iterator();
103853           var eiPrev = it.next();
103854           while (it.hasNext()) {
103855             var ei = it.next();
103856             var isCollapsed = this$1.findCollapseIndex(eiPrev, ei, collapsedVertexIndex);
103857             if (isCollapsed) { collapsedVertexIndexes.add(new Integer(collapsedVertexIndex[0])); }
103858             eiPrev = ei;
103859           }
103860         };
103861         SegmentNodeList.prototype.getEdge = function getEdge () {
103862           return this._edge
103863         };
103864         SegmentNodeList.prototype.addEndpoints = function addEndpoints () {
103865           var maxSegIndex = this._edge.size() - 1;
103866           this.add(this._edge.getCoordinate(0), 0);
103867           this.add(this._edge.getCoordinate(maxSegIndex), maxSegIndex);
103868         };
103869         SegmentNodeList.prototype.createSplitEdge = function createSplitEdge (ei0, ei1) {
103870             var this$1 = this;
103871
103872           var npts = ei1.segmentIndex - ei0.segmentIndex + 2;
103873           var lastSegStartPt = this._edge.getCoordinate(ei1.segmentIndex);
103874           var useIntPt1 = ei1.isInterior() || !ei1.coord.equals2D(lastSegStartPt);
103875           if (!useIntPt1) {
103876             npts--;
103877           }
103878           var pts = new Array(npts).fill(null);
103879           var ipt = 0;
103880           pts[ipt++] = new Coordinate(ei0.coord);
103881           for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
103882             pts[ipt++] = this$1._edge.getCoordinate(i);
103883           }
103884           if (useIntPt1) { pts[ipt] = new Coordinate(ei1.coord); }
103885           return new NodedSegmentString(pts, this._edge.getData())
103886         };
103887         SegmentNodeList.prototype.add = function add (intPt, segmentIndex) {
103888           var eiNew = new SegmentNode(this._edge, intPt, segmentIndex, this._edge.getSegmentOctant(segmentIndex));
103889           var ei = this._nodeMap.get(eiNew);
103890           if (ei !== null) {
103891             Assert.isTrue(ei.coord.equals2D(intPt), 'Found equal nodes with different coordinates');
103892             return ei
103893           }
103894           this._nodeMap.put(eiNew, eiNew);
103895           return eiNew
103896         };
103897         SegmentNodeList.prototype.checkSplitEdgesCorrectness = function checkSplitEdgesCorrectness (splitEdges) {
103898           var edgePts = this._edge.getCoordinates();
103899           var split0 = splitEdges.get(0);
103900           var pt0 = split0.getCoordinate(0);
103901           if (!pt0.equals2D(edgePts[0])) { throw new RuntimeException('bad split edge start point at ' + pt0) }
103902           var splitn = splitEdges.get(splitEdges.size() - 1);
103903           var splitnPts = splitn.getCoordinates();
103904           var ptn = splitnPts[splitnPts.length - 1];
103905           if (!ptn.equals2D(edgePts[edgePts.length - 1])) { throw new RuntimeException('bad split edge end point at ' + ptn) }
103906         };
103907         SegmentNodeList.prototype.interfaces_ = function interfaces_ () {
103908           return []
103909         };
103910         SegmentNodeList.prototype.getClass = function getClass () {
103911           return SegmentNodeList
103912         };
103913
103914
103915
103916         // class NodeVertexIterator {
103917         //   constructor () {
103918         //     this._nodeList = null
103919         //     this._edge = null
103920         //     this._nodeIt = null
103921         //     this._currNode = null
103922         //     this._nextNode = null
103923         //     this._currSegIndex = 0
103924         //     let nodeList = arguments[0]
103925         //     this._nodeList = nodeList
103926         //     this._edge = nodeList.getEdge()
103927         //     this._nodeIt = nodeList.iterator()
103928         //     this.readNextNode()
103929         //   }
103930         //   next () {
103931         //     if (this._currNode === null) {
103932         //       this._currNode = this._nextNode
103933         //       this._currSegIndex = this._currNode.segmentIndex
103934         //       this.readNextNode()
103935         //       return this._currNode
103936         //     }
103937         //     if (this._nextNode === null) return null
103938         //     if (this._nextNode.segmentIndex === this._currNode.segmentIndex) {
103939         //       this._currNode = this._nextNode
103940         //       this._currSegIndex = this._currNode.segmentIndex
103941         //       this.readNextNode()
103942         //       return this._currNode
103943         //     }
103944         //     if (this._nextNode.segmentIndex > this._currNode.segmentIndex) {}
103945         //     return null
103946         //   }
103947         //   remove () {
103948         //     // throw new UnsupportedOperationException(this.getClass().getName())
103949         //   }
103950         //   hasNext () {
103951         //     if (this._nextNode === null) return false
103952         //     return true
103953         //   }
103954         //   readNextNode () {
103955         //     if (this._nodeIt.hasNext()) this._nextNode = this._nodeIt.next(); else this._nextNode = null
103956         //   }
103957         //   interfaces_ () {
103958         //     return [Iterator]
103959         //   }
103960         //   getClass () {
103961         //     return NodeVertexIterator
103962         //   }
103963         // }
103964
103965         var Octant = function Octant () {};
103966
103967         Octant.prototype.interfaces_ = function interfaces_ () {
103968           return []
103969         };
103970         Octant.prototype.getClass = function getClass () {
103971           return Octant
103972         };
103973         Octant.octant = function octant () {
103974           if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
103975             var dx = arguments[0];
103976             var dy = arguments[1];
103977             if (dx === 0.0 && dy === 0.0) { throw new IllegalArgumentException('Cannot compute the octant for point ( ' + dx + ', ' + dy + ' )') }
103978             var adx = Math.abs(dx);
103979             var ady = Math.abs(dy);
103980             if (dx >= 0) {
103981               if (dy >= 0) {
103982                 if (adx >= ady) { return 0; } else { return 1 }
103983               } else {
103984                 if (adx >= ady) { return 7; } else { return 6 }
103985               }
103986             } else {
103987               if (dy >= 0) {
103988                 if (adx >= ady) { return 3; } else { return 2 }
103989               } else {
103990                 if (adx >= ady) { return 4; } else { return 5 }
103991               }
103992             }
103993           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
103994             var p0 = arguments[0];
103995             var p1 = arguments[1];
103996             var dx$1 = p1.x - p0.x;
103997             var dy$1 = p1.y - p0.y;
103998             if (dx$1 === 0.0 && dy$1 === 0.0) { throw new IllegalArgumentException('Cannot compute the octant for two identical points ' + p0) }
103999             return Octant.octant(dx$1, dy$1)
104000           }
104001         };
104002
104003         var SegmentString = function SegmentString () {};
104004
104005         SegmentString.prototype.getCoordinates = function getCoordinates () {};
104006         SegmentString.prototype.size = function size () {};
104007         SegmentString.prototype.getCoordinate = function getCoordinate (i) {};
104008         SegmentString.prototype.isClosed = function isClosed () {};
104009         SegmentString.prototype.setData = function setData (data) {};
104010         SegmentString.prototype.getData = function getData () {};
104011         SegmentString.prototype.interfaces_ = function interfaces_ () {
104012           return []
104013         };
104014         SegmentString.prototype.getClass = function getClass () {
104015           return SegmentString
104016         };
104017
104018         var NodableSegmentString = function NodableSegmentString () {};
104019
104020         NodableSegmentString.prototype.addIntersection = function addIntersection (intPt, segmentIndex) {};
104021         NodableSegmentString.prototype.interfaces_ = function interfaces_ () {
104022           return [SegmentString]
104023         };
104024         NodableSegmentString.prototype.getClass = function getClass () {
104025           return NodableSegmentString
104026         };
104027
104028         var NodedSegmentString = function NodedSegmentString () {
104029           this._nodeList = new SegmentNodeList(this);
104030           this._pts = null;
104031           this._data = null;
104032           var pts = arguments[0];
104033           var data = arguments[1];
104034           this._pts = pts;
104035           this._data = data;
104036         };
104037         NodedSegmentString.prototype.getCoordinates = function getCoordinates () {
104038           return this._pts
104039         };
104040         NodedSegmentString.prototype.size = function size () {
104041           return this._pts.length
104042         };
104043         NodedSegmentString.prototype.getCoordinate = function getCoordinate (i) {
104044           return this._pts[i]
104045         };
104046         NodedSegmentString.prototype.isClosed = function isClosed () {
104047           return this._pts[0].equals(this._pts[this._pts.length - 1])
104048         };
104049         NodedSegmentString.prototype.getSegmentOctant = function getSegmentOctant (index) {
104050           if (index === this._pts.length - 1) { return -1 }
104051           return this.safeOctant(this.getCoordinate(index), this.getCoordinate(index + 1))
104052         };
104053         NodedSegmentString.prototype.setData = function setData (data) {
104054           this._data = data;
104055         };
104056         NodedSegmentString.prototype.safeOctant = function safeOctant (p0, p1) {
104057           if (p0.equals2D(p1)) { return 0 }
104058           return Octant.octant(p0, p1)
104059         };
104060         NodedSegmentString.prototype.getData = function getData () {
104061           return this._data
104062         };
104063         NodedSegmentString.prototype.addIntersection = function addIntersection () {
104064           if (arguments.length === 2) {
104065             var intPt$1 = arguments[0];
104066             var segmentIndex = arguments[1];
104067             this.addIntersectionNode(intPt$1, segmentIndex);
104068           } else if (arguments.length === 4) {
104069             var li = arguments[0];
104070             var segmentIndex$1 = arguments[1];
104071             // const geomIndex = arguments[2]
104072             var intIndex = arguments[3];
104073             var intPt = new Coordinate(li.getIntersection(intIndex));
104074             this.addIntersection(intPt, segmentIndex$1);
104075           }
104076         };
104077         NodedSegmentString.prototype.toString = function toString () {
104078           return WKTWriter.toLineString(new CoordinateArraySequence(this._pts))
104079         };
104080         NodedSegmentString.prototype.getNodeList = function getNodeList () {
104081           return this._nodeList
104082         };
104083         NodedSegmentString.prototype.addIntersectionNode = function addIntersectionNode (intPt, segmentIndex) {
104084           var normalizedSegmentIndex = segmentIndex;
104085           var nextSegIndex = normalizedSegmentIndex + 1;
104086           if (nextSegIndex < this._pts.length) {
104087             var nextPt = this._pts[nextSegIndex];
104088             if (intPt.equals2D(nextPt)) {
104089               normalizedSegmentIndex = nextSegIndex;
104090             }
104091           }
104092           var ei = this._nodeList.add(intPt, normalizedSegmentIndex);
104093           return ei
104094         };
104095         NodedSegmentString.prototype.addIntersections = function addIntersections (li, segmentIndex, geomIndex) {
104096             var this$1 = this;
104097
104098           for (var i = 0; i < li.getIntersectionNum(); i++) {
104099             this$1.addIntersection(li, segmentIndex, geomIndex, i);
104100           }
104101         };
104102         NodedSegmentString.prototype.interfaces_ = function interfaces_ () {
104103           return [NodableSegmentString]
104104         };
104105         NodedSegmentString.prototype.getClass = function getClass () {
104106           return NodedSegmentString
104107         };
104108         NodedSegmentString.getNodedSubstrings = function getNodedSubstrings () {
104109           if (arguments.length === 1) {
104110             var segStrings = arguments[0];
104111             var resultEdgelist = new ArrayList();
104112             NodedSegmentString.getNodedSubstrings(segStrings, resultEdgelist);
104113             return resultEdgelist
104114           } else if (arguments.length === 2) {
104115             var segStrings$1 = arguments[0];
104116             var resultEdgelist$1 = arguments[1];
104117             for (var i = segStrings$1.iterator(); i.hasNext();) {
104118               var ss = i.next();
104119               ss.getNodeList().addSplitEdges(resultEdgelist$1);
104120             }
104121           }
104122         };
104123
104124         var LineSegment = function LineSegment () {
104125           this.p0 = null;
104126           this.p1 = null;
104127           if (arguments.length === 0) {
104128             this.p0 = new Coordinate();
104129             this.p1 = new Coordinate();
104130           } else if (arguments.length === 1) {
104131             var ls = arguments[0];
104132             this.p0 = new Coordinate(ls.p0);
104133             this.p1 = new Coordinate(ls.p1);
104134           } else if (arguments.length === 2) {
104135             this.p0 = arguments[0];
104136             this.p1 = arguments[1];
104137           } else if (arguments.length === 4) {
104138             var x0 = arguments[0];
104139             var y0 = arguments[1];
104140             var x1 = arguments[2];
104141             var y1 = arguments[3];
104142             this.p0 = new Coordinate(x0, y0);
104143             this.p1 = new Coordinate(x1, y1);
104144           }
104145         };
104146
104147         var staticAccessors$24 = { serialVersionUID: { configurable: true } };
104148         LineSegment.prototype.minX = function minX () {
104149           return Math.min(this.p0.x, this.p1.x)
104150         };
104151         LineSegment.prototype.orientationIndex = function orientationIndex () {
104152           if (arguments[0] instanceof LineSegment) {
104153             var seg = arguments[0];
104154             var orient0 = CGAlgorithms.orientationIndex(this.p0, this.p1, seg.p0);
104155             var orient1 = CGAlgorithms.orientationIndex(this.p0, this.p1, seg.p1);
104156             if (orient0 >= 0 && orient1 >= 0) { return Math.max(orient0, orient1) }
104157             if (orient0 <= 0 && orient1 <= 0) { return Math.max(orient0, orient1) }
104158             return 0
104159           } else if (arguments[0] instanceof Coordinate) {
104160             var p = arguments[0];
104161             return CGAlgorithms.orientationIndex(this.p0, this.p1, p)
104162           }
104163         };
104164         LineSegment.prototype.toGeometry = function toGeometry (geomFactory) {
104165           return geomFactory.createLineString([this.p0, this.p1])
104166         };
104167         LineSegment.prototype.isVertical = function isVertical () {
104168           return this.p0.x === this.p1.x
104169         };
104170         LineSegment.prototype.equals = function equals (o) {
104171           if (!(o instanceof LineSegment)) {
104172             return false
104173           }
104174           var other = o;
104175           return this.p0.equals(other.p0) && this.p1.equals(other.p1)
104176         };
104177         LineSegment.prototype.intersection = function intersection (line) {
104178           var li = new RobustLineIntersector();
104179           li.computeIntersection(this.p0, this.p1, line.p0, line.p1);
104180           if (li.hasIntersection()) { return li.getIntersection(0) }
104181           return null
104182         };
104183         LineSegment.prototype.project = function project () {
104184           if (arguments[0] instanceof Coordinate) {
104185             var p = arguments[0];
104186             if (p.equals(this.p0) || p.equals(this.p1)) { return new Coordinate(p) }
104187             var r = this.projectionFactor(p);
104188             var coord = new Coordinate();
104189             coord.x = this.p0.x + r * (this.p1.x - this.p0.x);
104190             coord.y = this.p0.y + r * (this.p1.y - this.p0.y);
104191             return coord
104192           } else if (arguments[0] instanceof LineSegment) {
104193             var seg = arguments[0];
104194             var pf0 = this.projectionFactor(seg.p0);
104195             var pf1 = this.projectionFactor(seg.p1);
104196             if (pf0 >= 1.0 && pf1 >= 1.0) { return null }
104197             if (pf0 <= 0.0 && pf1 <= 0.0) { return null }
104198             var newp0 = this.project(seg.p0);
104199             if (pf0 < 0.0) { newp0 = this.p0; }
104200             if (pf0 > 1.0) { newp0 = this.p1; }
104201             var newp1 = this.project(seg.p1);
104202             if (pf1 < 0.0) { newp1 = this.p0; }
104203             if (pf1 > 1.0) { newp1 = this.p1; }
104204             return new LineSegment(newp0, newp1)
104205           }
104206         };
104207         LineSegment.prototype.normalize = function normalize () {
104208           if (this.p1.compareTo(this.p0) < 0) { this.reverse(); }
104209         };
104210         LineSegment.prototype.angle = function angle () {
104211           return Math.atan2(this.p1.y - this.p0.y, this.p1.x - this.p0.x)
104212         };
104213         LineSegment.prototype.getCoordinate = function getCoordinate (i) {
104214           if (i === 0) { return this.p0 }
104215           return this.p1
104216         };
104217         LineSegment.prototype.distancePerpendicular = function distancePerpendicular (p) {
104218           return CGAlgorithms.distancePointLinePerpendicular(p, this.p0, this.p1)
104219         };
104220         LineSegment.prototype.minY = function minY () {
104221           return Math.min(this.p0.y, this.p1.y)
104222         };
104223         LineSegment.prototype.midPoint = function midPoint () {
104224           return LineSegment.midPoint(this.p0, this.p1)
104225         };
104226         LineSegment.prototype.projectionFactor = function projectionFactor (p) {
104227           if (p.equals(this.p0)) { return 0.0 }
104228           if (p.equals(this.p1)) { return 1.0 }
104229           var dx = this.p1.x - this.p0.x;
104230           var dy = this.p1.y - this.p0.y;
104231           var len = dx * dx + dy * dy;
104232           if (len <= 0.0) { return Double.NaN }
104233           var r = ((p.x - this.p0.x) * dx + (p.y - this.p0.y) * dy) / len;
104234           return r
104235         };
104236         LineSegment.prototype.closestPoints = function closestPoints (line) {
104237           var intPt = this.intersection(line);
104238           if (intPt !== null) {
104239             return [intPt, intPt]
104240           }
104241           var closestPt = new Array(2).fill(null);
104242           var minDistance = Double.MAX_VALUE;
104243           var dist = null;
104244           var close00 = this.closestPoint(line.p0);
104245           minDistance = close00.distance(line.p0);
104246           closestPt[0] = close00;
104247           closestPt[1] = line.p0;
104248           var close01 = this.closestPoint(line.p1);
104249           dist = close01.distance(line.p1);
104250           if (dist < minDistance) {
104251             minDistance = dist;
104252             closestPt[0] = close01;
104253             closestPt[1] = line.p1;
104254           }
104255           var close10 = line.closestPoint(this.p0);
104256           dist = close10.distance(this.p0);
104257           if (dist < minDistance) {
104258             minDistance = dist;
104259             closestPt[0] = this.p0;
104260             closestPt[1] = close10;
104261           }
104262           var close11 = line.closestPoint(this.p1);
104263           dist = close11.distance(this.p1);
104264           if (dist < minDistance) {
104265             minDistance = dist;
104266             closestPt[0] = this.p1;
104267             closestPt[1] = close11;
104268           }
104269           return closestPt
104270         };
104271         LineSegment.prototype.closestPoint = function closestPoint (p) {
104272           var factor = this.projectionFactor(p);
104273           if (factor > 0 && factor < 1) {
104274             return this.project(p)
104275           }
104276           var dist0 = this.p0.distance(p);
104277           var dist1 = this.p1.distance(p);
104278           if (dist0 < dist1) { return this.p0 }
104279           return this.p1
104280         };
104281         LineSegment.prototype.maxX = function maxX () {
104282           return Math.max(this.p0.x, this.p1.x)
104283         };
104284         LineSegment.prototype.getLength = function getLength () {
104285           return this.p0.distance(this.p1)
104286         };
104287         LineSegment.prototype.compareTo = function compareTo (o) {
104288           var other = o;
104289           var comp0 = this.p0.compareTo(other.p0);
104290           if (comp0 !== 0) { return comp0 }
104291           return this.p1.compareTo(other.p1)
104292         };
104293         LineSegment.prototype.reverse = function reverse () {
104294           var temp = this.p0;
104295           this.p0 = this.p1;
104296           this.p1 = temp;
104297         };
104298         LineSegment.prototype.equalsTopo = function equalsTopo (other) {
104299           return this.p0.equals(other.p0) &&
104300                 (this.p1.equals(other.p1) || this.p0.equals(other.p1)) &&
104301                  this.p1.equals(other.p0)
104302         };
104303         LineSegment.prototype.lineIntersection = function lineIntersection (line) {
104304           try {
104305             var intPt = HCoordinate.intersection(this.p0, this.p1, line.p0, line.p1);
104306             return intPt
104307           } catch (ex) {
104308             if (ex instanceof NotRepresentableException) ; else { throw ex }
104309           } finally {}
104310           return null
104311         };
104312         LineSegment.prototype.maxY = function maxY () {
104313           return Math.max(this.p0.y, this.p1.y)
104314         };
104315         LineSegment.prototype.pointAlongOffset = function pointAlongOffset (segmentLengthFraction, offsetDistance) {
104316           var segx = this.p0.x + segmentLengthFraction * (this.p1.x - this.p0.x);
104317           var segy = this.p0.y + segmentLengthFraction * (this.p1.y - this.p0.y);
104318           var dx = this.p1.x - this.p0.x;
104319           var dy = this.p1.y - this.p0.y;
104320           var len = Math.sqrt(dx * dx + dy * dy);
104321           var ux = 0.0;
104322           var uy = 0.0;
104323           if (offsetDistance !== 0.0) {
104324             if (len <= 0.0) { throw new Error('Cannot compute offset from zero-length line segment') }
104325             ux = offsetDistance * dx / len;
104326             uy = offsetDistance * dy / len;
104327           }
104328           var offsetx = segx - uy;
104329           var offsety = segy + ux;
104330           var coord = new Coordinate(offsetx, offsety);
104331           return coord
104332         };
104333         LineSegment.prototype.setCoordinates = function setCoordinates () {
104334           if (arguments.length === 1) {
104335             var ls = arguments[0];
104336             this.setCoordinates(ls.p0, ls.p1);
104337           } else if (arguments.length === 2) {
104338             var p0 = arguments[0];
104339             var p1 = arguments[1];
104340             this.p0.x = p0.x;
104341             this.p0.y = p0.y;
104342             this.p1.x = p1.x;
104343             this.p1.y = p1.y;
104344           }
104345         };
104346         LineSegment.prototype.segmentFraction = function segmentFraction (inputPt) {
104347           var segFrac = this.projectionFactor(inputPt);
104348           if (segFrac < 0.0) { segFrac = 0.0; } else if (segFrac > 1.0 || Double.isNaN(segFrac)) { segFrac = 1.0; }
104349           return segFrac
104350         };
104351         LineSegment.prototype.toString = function toString () {
104352           return 'LINESTRING( ' + this.p0.x + ' ' + this.p0.y + ', ' + this.p1.x + ' ' + this.p1.y + ')'
104353         };
104354         LineSegment.prototype.isHorizontal = function isHorizontal () {
104355           return this.p0.y === this.p1.y
104356         };
104357         LineSegment.prototype.distance = function distance () {
104358           if (arguments[0] instanceof LineSegment) {
104359             var ls = arguments[0];
104360             return CGAlgorithms.distanceLineLine(this.p0, this.p1, ls.p0, ls.p1)
104361           } else if (arguments[0] instanceof Coordinate) {
104362             var p = arguments[0];
104363             return CGAlgorithms.distancePointLine(p, this.p0, this.p1)
104364           }
104365         };
104366         LineSegment.prototype.pointAlong = function pointAlong (segmentLengthFraction) {
104367           var coord = new Coordinate();
104368           coord.x = this.p0.x + segmentLengthFraction * (this.p1.x - this.p0.x);
104369           coord.y = this.p0.y + segmentLengthFraction * (this.p1.y - this.p0.y);
104370           return coord
104371         };
104372         LineSegment.prototype.hashCode = function hashCode () {
104373           var bits0 = Double.doubleToLongBits(this.p0.x);
104374           bits0 ^= Double.doubleToLongBits(this.p0.y) * 31;
104375           var hash0 = Math.trunc(bits0) ^ Math.trunc(bits0 >> 32);
104376           var bits1 = Double.doubleToLongBits(this.p1.x);
104377           bits1 ^= Double.doubleToLongBits(this.p1.y) * 31;
104378           var hash1 = Math.trunc(bits1) ^ Math.trunc(bits1 >> 32);
104379           return hash0 ^ hash1
104380         };
104381         LineSegment.prototype.interfaces_ = function interfaces_ () {
104382           return [Comparable, Serializable]
104383         };
104384         LineSegment.prototype.getClass = function getClass () {
104385           return LineSegment
104386         };
104387         LineSegment.midPoint = function midPoint (p0, p1) {
104388           return new Coordinate((p0.x + p1.x) / 2, (p0.y + p1.y) / 2)
104389         };
104390         staticAccessors$24.serialVersionUID.get = function () { return 3252005833466256227 };
104391
104392         Object.defineProperties( LineSegment, staticAccessors$24 );
104393
104394         var MonotoneChainOverlapAction = function MonotoneChainOverlapAction () {
104395           this.tempEnv1 = new Envelope();
104396           this.tempEnv2 = new Envelope();
104397           this._overlapSeg1 = new LineSegment();
104398           this._overlapSeg2 = new LineSegment();
104399         };
104400         MonotoneChainOverlapAction.prototype.overlap = function overlap () {
104401           if (arguments.length === 2) ; else if (arguments.length === 4) {
104402             var mc1 = arguments[0];
104403             var start1 = arguments[1];
104404             var mc2 = arguments[2];
104405             var start2 = arguments[3];
104406             mc1.getLineSegment(start1, this._overlapSeg1);
104407             mc2.getLineSegment(start2, this._overlapSeg2);
104408             this.overlap(this._overlapSeg1, this._overlapSeg2);
104409           }
104410         };
104411         MonotoneChainOverlapAction.prototype.interfaces_ = function interfaces_ () {
104412           return []
104413         };
104414         MonotoneChainOverlapAction.prototype.getClass = function getClass () {
104415           return MonotoneChainOverlapAction
104416         };
104417
104418         var MonotoneChain = function MonotoneChain () {
104419           this._pts = null;
104420           this._start = null;
104421           this._end = null;
104422           this._env = null;
104423           this._context = null;
104424           this._id = null;
104425           var pts = arguments[0];
104426           var start = arguments[1];
104427           var end = arguments[2];
104428           var context = arguments[3];
104429           this._pts = pts;
104430           this._start = start;
104431           this._end = end;
104432           this._context = context;
104433         };
104434         MonotoneChain.prototype.getLineSegment = function getLineSegment (index, ls) {
104435           ls.p0 = this._pts[index];
104436           ls.p1 = this._pts[index + 1];
104437         };
104438         MonotoneChain.prototype.computeSelect = function computeSelect (searchEnv, start0, end0, mcs) {
104439           var p0 = this._pts[start0];
104440           var p1 = this._pts[end0];
104441           mcs.tempEnv1.init(p0, p1);
104442           if (end0 - start0 === 1) {
104443             mcs.select(this, start0);
104444             return null
104445           }
104446           if (!searchEnv.intersects(mcs.tempEnv1)) { return null }
104447           var mid = Math.trunc((start0 + end0) / 2);
104448           if (start0 < mid) {
104449             this.computeSelect(searchEnv, start0, mid, mcs);
104450           }
104451           if (mid < end0) {
104452             this.computeSelect(searchEnv, mid, end0, mcs);
104453           }
104454         };
104455         MonotoneChain.prototype.getCoordinates = function getCoordinates () {
104456             var this$1 = this;
104457
104458           var coord = new Array(this._end - this._start + 1).fill(null);
104459           var index = 0;
104460           for (var i = this._start; i <= this._end; i++) {
104461             coord[index++] = this$1._pts[i];
104462           }
104463           return coord
104464         };
104465         MonotoneChain.prototype.computeOverlaps = function computeOverlaps (mc, mco) {
104466           this.computeOverlapsInternal(this._start, this._end, mc, mc._start, mc._end, mco);
104467         };
104468         MonotoneChain.prototype.setId = function setId (id) {
104469           this._id = id;
104470         };
104471         MonotoneChain.prototype.select = function select (searchEnv, mcs) {
104472           this.computeSelect(searchEnv, this._start, this._end, mcs);
104473         };
104474         MonotoneChain.prototype.getEnvelope = function getEnvelope () {
104475           if (this._env === null) {
104476             var p0 = this._pts[this._start];
104477             var p1 = this._pts[this._end];
104478             this._env = new Envelope(p0, p1);
104479           }
104480           return this._env
104481         };
104482         MonotoneChain.prototype.getEndIndex = function getEndIndex () {
104483           return this._end
104484         };
104485         MonotoneChain.prototype.getStartIndex = function getStartIndex () {
104486           return this._start
104487         };
104488         MonotoneChain.prototype.getContext = function getContext () {
104489           return this._context
104490         };
104491         MonotoneChain.prototype.getId = function getId () {
104492           return this._id
104493         };
104494         MonotoneChain.prototype.computeOverlapsInternal = function computeOverlapsInternal (start0, end0, mc, start1, end1, mco) {
104495           var p00 = this._pts[start0];
104496           var p01 = this._pts[end0];
104497           var p10 = mc._pts[start1];
104498           var p11 = mc._pts[end1];
104499           if (end0 - start0 === 1 && end1 - start1 === 1) {
104500             mco.overlap(this, start0, mc, start1);
104501             return null
104502           }
104503           mco.tempEnv1.init(p00, p01);
104504           mco.tempEnv2.init(p10, p11);
104505           if (!mco.tempEnv1.intersects(mco.tempEnv2)) { return null }
104506           var mid0 = Math.trunc((start0 + end0) / 2);
104507           var mid1 = Math.trunc((start1 + end1) / 2);
104508           if (start0 < mid0) {
104509             if (start1 < mid1) { this.computeOverlapsInternal(start0, mid0, mc, start1, mid1, mco); }
104510             if (mid1 < end1) { this.computeOverlapsInternal(start0, mid0, mc, mid1, end1, mco); }
104511           }
104512           if (mid0 < end0) {
104513             if (start1 < mid1) { this.computeOverlapsInternal(mid0, end0, mc, start1, mid1, mco); }
104514             if (mid1 < end1) { this.computeOverlapsInternal(mid0, end0, mc, mid1, end1, mco); }
104515           }
104516         };
104517         MonotoneChain.prototype.interfaces_ = function interfaces_ () {
104518           return []
104519         };
104520         MonotoneChain.prototype.getClass = function getClass () {
104521           return MonotoneChain
104522         };
104523
104524         var MonotoneChainBuilder = function MonotoneChainBuilder () {};
104525
104526         MonotoneChainBuilder.prototype.interfaces_ = function interfaces_ () {
104527           return []
104528         };
104529         MonotoneChainBuilder.prototype.getClass = function getClass () {
104530           return MonotoneChainBuilder
104531         };
104532         MonotoneChainBuilder.getChainStartIndices = function getChainStartIndices (pts) {
104533           var start = 0;
104534           var startIndexList = new ArrayList();
104535           startIndexList.add(new Integer(start));
104536           do {
104537             var last = MonotoneChainBuilder.findChainEnd(pts, start);
104538             startIndexList.add(new Integer(last));
104539             start = last;
104540           } while (start < pts.length - 1)
104541           var startIndex = MonotoneChainBuilder.toIntArray(startIndexList);
104542           return startIndex
104543         };
104544         MonotoneChainBuilder.findChainEnd = function findChainEnd (pts, start) {
104545           var safeStart = start;
104546           while (safeStart < pts.length - 1 && pts[safeStart].equals2D(pts[safeStart + 1])) {
104547             safeStart++;
104548           }
104549           if (safeStart >= pts.length - 1) {
104550             return pts.length - 1
104551           }
104552           var chainQuad = Quadrant.quadrant(pts[safeStart], pts[safeStart + 1]);
104553           var last = start + 1;
104554           while (last < pts.length) {
104555             if (!pts[last - 1].equals2D(pts[last])) {
104556               var quad = Quadrant.quadrant(pts[last - 1], pts[last]);
104557               if (quad !== chainQuad) { break }
104558             }
104559             last++;
104560           }
104561           return last - 1
104562         };
104563         MonotoneChainBuilder.getChains = function getChains () {
104564           if (arguments.length === 1) {
104565             var pts = arguments[0];
104566             return MonotoneChainBuilder.getChains(pts, null)
104567           } else if (arguments.length === 2) {
104568             var pts$1 = arguments[0];
104569             var context = arguments[1];
104570             var mcList = new ArrayList();
104571             var startIndex = MonotoneChainBuilder.getChainStartIndices(pts$1);
104572             for (var i = 0; i < startIndex.length - 1; i++) {
104573               var mc = new MonotoneChain(pts$1, startIndex[i], startIndex[i + 1], context);
104574               mcList.add(mc);
104575             }
104576             return mcList
104577           }
104578         };
104579         MonotoneChainBuilder.toIntArray = function toIntArray (list) {
104580           var array = new Array(list.size()).fill(null);
104581           for (var i = 0; i < array.length; i++) {
104582             array[i] = list.get(i).intValue();
104583           }
104584           return array
104585         };
104586
104587         var Noder = function Noder () {};
104588
104589         Noder.prototype.computeNodes = function computeNodes (segStrings) {};
104590         Noder.prototype.getNodedSubstrings = function getNodedSubstrings () {};
104591         Noder.prototype.interfaces_ = function interfaces_ () {
104592           return []
104593         };
104594         Noder.prototype.getClass = function getClass () {
104595           return Noder
104596         };
104597
104598         var SinglePassNoder = function SinglePassNoder () {
104599           this._segInt = null;
104600           if (arguments.length === 0) ; else if (arguments.length === 1) {
104601             var segInt = arguments[0];
104602             this.setSegmentIntersector(segInt);
104603           }
104604         };
104605         SinglePassNoder.prototype.setSegmentIntersector = function setSegmentIntersector (segInt) {
104606           this._segInt = segInt;
104607         };
104608         SinglePassNoder.prototype.interfaces_ = function interfaces_ () {
104609           return [Noder]
104610         };
104611         SinglePassNoder.prototype.getClass = function getClass () {
104612           return SinglePassNoder
104613         };
104614
104615         var MCIndexNoder = (function (SinglePassNoder$$1) {
104616           function MCIndexNoder (si) {
104617             if (si) { SinglePassNoder$$1.call(this, si); }
104618             else { SinglePassNoder$$1.call(this); }
104619             this._monoChains = new ArrayList();
104620             this._index = new STRtree();
104621             this._idCounter = 0;
104622             this._nodedSegStrings = null;
104623             this._nOverlaps = 0;
104624           }
104625
104626           if ( SinglePassNoder$$1 ) { MCIndexNoder.__proto__ = SinglePassNoder$$1; }
104627           MCIndexNoder.prototype = Object.create( SinglePassNoder$$1 && SinglePassNoder$$1.prototype );
104628           MCIndexNoder.prototype.constructor = MCIndexNoder;
104629
104630           var staticAccessors = { SegmentOverlapAction: { configurable: true } };
104631           MCIndexNoder.prototype.getMonotoneChains = function getMonotoneChains () {
104632             return this._monoChains
104633           };
104634           MCIndexNoder.prototype.getNodedSubstrings = function getNodedSubstrings () {
104635             return NodedSegmentString.getNodedSubstrings(this._nodedSegStrings)
104636           };
104637           MCIndexNoder.prototype.getIndex = function getIndex () {
104638             return this._index
104639           };
104640           MCIndexNoder.prototype.add = function add (segStr) {
104641             var this$1 = this;
104642
104643             var segChains = MonotoneChainBuilder.getChains(segStr.getCoordinates(), segStr);
104644             for (var i = segChains.iterator(); i.hasNext();) {
104645               var mc = i.next();
104646               mc.setId(this$1._idCounter++);
104647               this$1._index.insert(mc.getEnvelope(), mc);
104648               this$1._monoChains.add(mc);
104649             }
104650           };
104651           MCIndexNoder.prototype.computeNodes = function computeNodes (inputSegStrings) {
104652             var this$1 = this;
104653
104654             this._nodedSegStrings = inputSegStrings;
104655             for (var i = inputSegStrings.iterator(); i.hasNext();) {
104656               this$1.add(i.next());
104657             }
104658             this.intersectChains();
104659           };
104660           MCIndexNoder.prototype.intersectChains = function intersectChains () {
104661             var this$1 = this;
104662
104663             var overlapAction = new SegmentOverlapAction(this._segInt);
104664             for (var i = this._monoChains.iterator(); i.hasNext();) {
104665               var queryChain = i.next();
104666               var overlapChains = this$1._index.query(queryChain.getEnvelope());
104667               for (var j = overlapChains.iterator(); j.hasNext();) {
104668                 var testChain = j.next();
104669                 if (testChain.getId() > queryChain.getId()) {
104670                   queryChain.computeOverlaps(testChain, overlapAction);
104671                   this$1._nOverlaps++;
104672                 }
104673                 if (this$1._segInt.isDone()) { return null }
104674               }
104675             }
104676           };
104677           MCIndexNoder.prototype.interfaces_ = function interfaces_ () {
104678             return []
104679           };
104680           MCIndexNoder.prototype.getClass = function getClass () {
104681             return MCIndexNoder
104682           };
104683           staticAccessors.SegmentOverlapAction.get = function () { return SegmentOverlapAction };
104684
104685           Object.defineProperties( MCIndexNoder, staticAccessors );
104686
104687           return MCIndexNoder;
104688         }(SinglePassNoder));
104689
104690         var SegmentOverlapAction = (function (MonotoneChainOverlapAction$$1) {
104691           function SegmentOverlapAction () {
104692             MonotoneChainOverlapAction$$1.call(this);
104693             this._si = null;
104694             var si = arguments[0];
104695             this._si = si;
104696           }
104697
104698           if ( MonotoneChainOverlapAction$$1 ) { SegmentOverlapAction.__proto__ = MonotoneChainOverlapAction$$1; }
104699           SegmentOverlapAction.prototype = Object.create( MonotoneChainOverlapAction$$1 && MonotoneChainOverlapAction$$1.prototype );
104700           SegmentOverlapAction.prototype.constructor = SegmentOverlapAction;
104701           SegmentOverlapAction.prototype.overlap = function overlap () {
104702             if (arguments.length === 4) {
104703               var mc1 = arguments[0];
104704               var start1 = arguments[1];
104705               var mc2 = arguments[2];
104706               var start2 = arguments[3];
104707               var ss1 = mc1.getContext();
104708               var ss2 = mc2.getContext();
104709               this._si.processIntersections(ss1, start1, ss2, start2);
104710             } else { return MonotoneChainOverlapAction$$1.prototype.overlap.apply(this, arguments) }
104711           };
104712           SegmentOverlapAction.prototype.interfaces_ = function interfaces_ () {
104713             return []
104714           };
104715           SegmentOverlapAction.prototype.getClass = function getClass () {
104716             return SegmentOverlapAction
104717           };
104718
104719           return SegmentOverlapAction;
104720         }(MonotoneChainOverlapAction));
104721
104722         var BufferParameters = function BufferParameters () {
104723           this._quadrantSegments = BufferParameters.DEFAULT_QUADRANT_SEGMENTS;
104724           this._endCapStyle = BufferParameters.CAP_ROUND;
104725           this._joinStyle = BufferParameters.JOIN_ROUND;
104726           this._mitreLimit = BufferParameters.DEFAULT_MITRE_LIMIT;
104727           this._isSingleSided = false;
104728           this._simplifyFactor = BufferParameters.DEFAULT_SIMPLIFY_FACTOR;
104729
104730           if (arguments.length === 0) ; else if (arguments.length === 1) {
104731             var quadrantSegments = arguments[0];
104732             this.setQuadrantSegments(quadrantSegments);
104733           } else if (arguments.length === 2) {
104734             var quadrantSegments$1 = arguments[0];
104735             var endCapStyle = arguments[1];
104736             this.setQuadrantSegments(quadrantSegments$1);
104737             this.setEndCapStyle(endCapStyle);
104738           } else if (arguments.length === 4) {
104739             var quadrantSegments$2 = arguments[0];
104740             var endCapStyle$1 = arguments[1];
104741             var joinStyle = arguments[2];
104742             var mitreLimit = arguments[3];
104743             this.setQuadrantSegments(quadrantSegments$2);
104744             this.setEndCapStyle(endCapStyle$1);
104745             this.setJoinStyle(joinStyle);
104746             this.setMitreLimit(mitreLimit);
104747           }
104748         };
104749
104750         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 } };
104751         BufferParameters.prototype.getEndCapStyle = function getEndCapStyle () {
104752           return this._endCapStyle
104753         };
104754         BufferParameters.prototype.isSingleSided = function isSingleSided () {
104755           return this._isSingleSided
104756         };
104757         BufferParameters.prototype.setQuadrantSegments = function setQuadrantSegments (quadSegs) {
104758           this._quadrantSegments = quadSegs;
104759           if (this._quadrantSegments === 0) { this._joinStyle = BufferParameters.JOIN_BEVEL; }
104760           if (this._quadrantSegments < 0) {
104761             this._joinStyle = BufferParameters.JOIN_MITRE;
104762             this._mitreLimit = Math.abs(this._quadrantSegments);
104763           }
104764           if (quadSegs <= 0) {
104765             this._quadrantSegments = 1;
104766           }
104767           if (this._joinStyle !== BufferParameters.JOIN_ROUND) {
104768             this._quadrantSegments = BufferParameters.DEFAULT_QUADRANT_SEGMENTS;
104769           }
104770         };
104771         BufferParameters.prototype.getJoinStyle = function getJoinStyle () {
104772           return this._joinStyle
104773         };
104774         BufferParameters.prototype.setJoinStyle = function setJoinStyle (joinStyle) {
104775           this._joinStyle = joinStyle;
104776         };
104777         BufferParameters.prototype.setSimplifyFactor = function setSimplifyFactor (simplifyFactor) {
104778           this._simplifyFactor = simplifyFactor < 0 ? 0 : simplifyFactor;
104779         };
104780         BufferParameters.prototype.getSimplifyFactor = function getSimplifyFactor () {
104781           return this._simplifyFactor
104782         };
104783         BufferParameters.prototype.getQuadrantSegments = function getQuadrantSegments () {
104784           return this._quadrantSegments
104785         };
104786         BufferParameters.prototype.setEndCapStyle = function setEndCapStyle (endCapStyle) {
104787           this._endCapStyle = endCapStyle;
104788         };
104789         BufferParameters.prototype.getMitreLimit = function getMitreLimit () {
104790           return this._mitreLimit
104791         };
104792         BufferParameters.prototype.setMitreLimit = function setMitreLimit (mitreLimit) {
104793           this._mitreLimit = mitreLimit;
104794         };
104795         BufferParameters.prototype.setSingleSided = function setSingleSided (isSingleSided) {
104796           this._isSingleSided = isSingleSided;
104797         };
104798         BufferParameters.prototype.interfaces_ = function interfaces_ () {
104799           return []
104800         };
104801         BufferParameters.prototype.getClass = function getClass () {
104802           return BufferParameters
104803         };
104804         BufferParameters.bufferDistanceError = function bufferDistanceError (quadSegs) {
104805           var alpha = Math.PI / 2.0 / quadSegs;
104806           return 1 - Math.cos(alpha / 2.0)
104807         };
104808         staticAccessors$25.CAP_ROUND.get = function () { return 1 };
104809         staticAccessors$25.CAP_FLAT.get = function () { return 2 };
104810         staticAccessors$25.CAP_SQUARE.get = function () { return 3 };
104811         staticAccessors$25.JOIN_ROUND.get = function () { return 1 };
104812         staticAccessors$25.JOIN_MITRE.get = function () { return 2 };
104813         staticAccessors$25.JOIN_BEVEL.get = function () { return 3 };
104814         staticAccessors$25.DEFAULT_QUADRANT_SEGMENTS.get = function () { return 8 };
104815         staticAccessors$25.DEFAULT_MITRE_LIMIT.get = function () { return 5.0 };
104816         staticAccessors$25.DEFAULT_SIMPLIFY_FACTOR.get = function () { return 0.01 };
104817
104818         Object.defineProperties( BufferParameters, staticAccessors$25 );
104819
104820         var BufferInputLineSimplifier = function BufferInputLineSimplifier (inputLine) {
104821           this._distanceTol = null;
104822           this._isDeleted = null;
104823           this._angleOrientation = CGAlgorithms.COUNTERCLOCKWISE;
104824           this._inputLine = inputLine || null;
104825         };
104826
104827         var staticAccessors$26 = { INIT: { configurable: true },DELETE: { configurable: true },KEEP: { configurable: true },NUM_PTS_TO_CHECK: { configurable: true } };
104828         BufferInputLineSimplifier.prototype.isDeletable = function isDeletable (i0, i1, i2, distanceTol) {
104829           var p0 = this._inputLine[i0];
104830           var p1 = this._inputLine[i1];
104831           var p2 = this._inputLine[i2];
104832           if (!this.isConcave(p0, p1, p2)) { return false }
104833           if (!this.isShallow(p0, p1, p2, distanceTol)) { return false }
104834           return this.isShallowSampled(p0, p1, i0, i2, distanceTol)
104835         };
104836         BufferInputLineSimplifier.prototype.deleteShallowConcavities = function deleteShallowConcavities () {
104837             var this$1 = this;
104838
104839           var index = 1;
104840           // const maxIndex = this._inputLine.length - 1
104841           var midIndex = this.findNextNonDeletedIndex(index);
104842           var lastIndex = this.findNextNonDeletedIndex(midIndex);
104843           var isChanged = false;
104844           while (lastIndex < this._inputLine.length) {
104845             var isMiddleVertexDeleted = false;
104846             if (this$1.isDeletable(index, midIndex, lastIndex, this$1._distanceTol)) {
104847               this$1._isDeleted[midIndex] = BufferInputLineSimplifier.DELETE;
104848               isMiddleVertexDeleted = true;
104849               isChanged = true;
104850             }
104851             if (isMiddleVertexDeleted) { index = lastIndex; } else { index = midIndex; }
104852             midIndex = this$1.findNextNonDeletedIndex(index);
104853             lastIndex = this$1.findNextNonDeletedIndex(midIndex);
104854           }
104855           return isChanged
104856         };
104857         BufferInputLineSimplifier.prototype.isShallowConcavity = function isShallowConcavity (p0, p1, p2, distanceTol) {
104858           var orientation = CGAlgorithms.computeOrientation(p0, p1, p2);
104859           var isAngleToSimplify = orientation === this._angleOrientation;
104860           if (!isAngleToSimplify) { return false }
104861           var dist = CGAlgorithms.distancePointLine(p1, p0, p2);
104862           return dist < distanceTol
104863         };
104864         BufferInputLineSimplifier.prototype.isShallowSampled = function isShallowSampled (p0, p2, i0, i2, distanceTol) {
104865             var this$1 = this;
104866
104867           var inc = Math.trunc((i2 - i0) / BufferInputLineSimplifier.NUM_PTS_TO_CHECK);
104868           if (inc <= 0) { inc = 1; }
104869           for (var i = i0; i < i2; i += inc) {
104870             if (!this$1.isShallow(p0, p2, this$1._inputLine[i], distanceTol)) { return false }
104871           }
104872           return true
104873         };
104874         BufferInputLineSimplifier.prototype.isConcave = function isConcave (p0, p1, p2) {
104875           var orientation = CGAlgorithms.computeOrientation(p0, p1, p2);
104876           var isConcave = orientation === this._angleOrientation;
104877           return isConcave
104878         };
104879         BufferInputLineSimplifier.prototype.simplify = function simplify (distanceTol) {
104880             var this$1 = this;
104881
104882           this._distanceTol = Math.abs(distanceTol);
104883           if (distanceTol < 0) { this._angleOrientation = CGAlgorithms.CLOCKWISE; }
104884           this._isDeleted = new Array(this._inputLine.length).fill(null);
104885           var isChanged = false;
104886           do {
104887             isChanged = this$1.deleteShallowConcavities();
104888           } while (isChanged)
104889           return this.collapseLine()
104890         };
104891         BufferInputLineSimplifier.prototype.findNextNonDeletedIndex = function findNextNonDeletedIndex (index) {
104892           var next = index + 1;
104893           while (next < this._inputLine.length && this._isDeleted[next] === BufferInputLineSimplifier.DELETE) { next++; }
104894           return next
104895         };
104896         BufferInputLineSimplifier.prototype.isShallow = function isShallow (p0, p1, p2, distanceTol) {
104897           var dist = CGAlgorithms.distancePointLine(p1, p0, p2);
104898           return dist < distanceTol
104899         };
104900         BufferInputLineSimplifier.prototype.collapseLine = function collapseLine () {
104901             var this$1 = this;
104902
104903           var coordList = new CoordinateList();
104904           for (var i = 0; i < this._inputLine.length; i++) {
104905             if (this$1._isDeleted[i] !== BufferInputLineSimplifier.DELETE) { coordList.add(this$1._inputLine[i]); }
104906           }
104907           return coordList.toCoordinateArray()
104908         };
104909         BufferInputLineSimplifier.prototype.interfaces_ = function interfaces_ () {
104910           return []
104911         };
104912         BufferInputLineSimplifier.prototype.getClass = function getClass () {
104913           return BufferInputLineSimplifier
104914         };
104915         BufferInputLineSimplifier.simplify = function simplify (inputLine, distanceTol) {
104916           var simp = new BufferInputLineSimplifier(inputLine);
104917           return simp.simplify(distanceTol)
104918         };
104919         staticAccessors$26.INIT.get = function () { return 0 };
104920         staticAccessors$26.DELETE.get = function () { return 1 };
104921         staticAccessors$26.KEEP.get = function () { return 1 };
104922         staticAccessors$26.NUM_PTS_TO_CHECK.get = function () { return 10 };
104923
104924         Object.defineProperties( BufferInputLineSimplifier, staticAccessors$26 );
104925
104926         var OffsetSegmentString = function OffsetSegmentString () {
104927           this._ptList = null;
104928           this._precisionModel = null;
104929           this._minimimVertexDistance = 0.0;
104930           this._ptList = new ArrayList();
104931         };
104932
104933         var staticAccessors$28 = { COORDINATE_ARRAY_TYPE: { configurable: true } };
104934         OffsetSegmentString.prototype.getCoordinates = function getCoordinates () {
104935           var coord = this._ptList.toArray(OffsetSegmentString.COORDINATE_ARRAY_TYPE);
104936           return coord
104937         };
104938         OffsetSegmentString.prototype.setPrecisionModel = function setPrecisionModel (precisionModel) {
104939           this._precisionModel = precisionModel;
104940         };
104941         OffsetSegmentString.prototype.addPt = function addPt (pt) {
104942           var bufPt = new Coordinate(pt);
104943           this._precisionModel.makePrecise(bufPt);
104944           if (this.isRedundant(bufPt)) { return null }
104945           this._ptList.add(bufPt);
104946         };
104947         OffsetSegmentString.prototype.revere = function revere () {};
104948         OffsetSegmentString.prototype.addPts = function addPts (pt, isForward) {
104949             var this$1 = this;
104950
104951           if (isForward) {
104952             for (var i = 0; i < pt.length; i++) {
104953               this$1.addPt(pt[i]);
104954             }
104955           } else {
104956             for (var i$1 = pt.length - 1; i$1 >= 0; i$1--) {
104957               this$1.addPt(pt[i$1]);
104958             }
104959           }
104960         };
104961         OffsetSegmentString.prototype.isRedundant = function isRedundant (pt) {
104962           if (this._ptList.size() < 1) { return false }
104963           var lastPt = this._ptList.get(this._ptList.size() - 1);
104964           var ptDist = pt.distance(lastPt);
104965           if (ptDist < this._minimimVertexDistance) { return true }
104966           return false
104967         };
104968         OffsetSegmentString.prototype.toString = function toString () {
104969           var fact = new GeometryFactory();
104970           var line = fact.createLineString(this.getCoordinates());
104971           return line.toString()
104972         };
104973         OffsetSegmentString.prototype.closeRing = function closeRing () {
104974           if (this._ptList.size() < 1) { return null }
104975           var startPt = new Coordinate(this._ptList.get(0));
104976           var lastPt = this._ptList.get(this._ptList.size() - 1);
104977           // const last2Pt = null
104978           // if (this._ptList.size() >= 2) last2Pt = this._ptList.get(this._ptList.size() - 2)
104979           if (startPt.equals(lastPt)) { return null }
104980           this._ptList.add(startPt);
104981         };
104982         OffsetSegmentString.prototype.setMinimumVertexDistance = function setMinimumVertexDistance (minimimVertexDistance) {
104983           this._minimimVertexDistance = minimimVertexDistance;
104984         };
104985         OffsetSegmentString.prototype.interfaces_ = function interfaces_ () {
104986           return []
104987         };
104988         OffsetSegmentString.prototype.getClass = function getClass () {
104989           return OffsetSegmentString
104990         };
104991         staticAccessors$28.COORDINATE_ARRAY_TYPE.get = function () { return new Array(0).fill(null) };
104992
104993         Object.defineProperties( OffsetSegmentString, staticAccessors$28 );
104994
104995         var Angle = function Angle () {};
104996
104997         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 } };
104998
104999         Angle.prototype.interfaces_ = function interfaces_ () {
105000           return []
105001         };
105002         Angle.prototype.getClass = function getClass () {
105003           return Angle
105004         };
105005         Angle.toDegrees = function toDegrees (radians) {
105006           return radians * 180 / Math.PI
105007         };
105008         Angle.normalize = function normalize (angle) {
105009           while (angle > Math.PI) { angle -= Angle.PI_TIMES_2; }
105010           while (angle <= -Math.PI) { angle += Angle.PI_TIMES_2; }
105011           return angle
105012         };
105013         Angle.angle = function angle () {
105014           if (arguments.length === 1) {
105015             var p = arguments[0];
105016             return Math.atan2(p.y, p.x)
105017           } else if (arguments.length === 2) {
105018             var p0 = arguments[0];
105019             var p1 = arguments[1];
105020             var dx = p1.x - p0.x;
105021             var dy = p1.y - p0.y;
105022             return Math.atan2(dy, dx)
105023           }
105024         };
105025         Angle.isAcute = function isAcute (p0, p1, p2) {
105026           var dx0 = p0.x - p1.x;
105027           var dy0 = p0.y - p1.y;
105028           var dx1 = p2.x - p1.x;
105029           var dy1 = p2.y - p1.y;
105030           var dotprod = dx0 * dx1 + dy0 * dy1;
105031           return dotprod > 0
105032         };
105033         Angle.isObtuse = function isObtuse (p0, p1, p2) {
105034           var dx0 = p0.x - p1.x;
105035           var dy0 = p0.y - p1.y;
105036           var dx1 = p2.x - p1.x;
105037           var dy1 = p2.y - p1.y;
105038           var dotprod = dx0 * dx1 + dy0 * dy1;
105039           return dotprod < 0
105040         };
105041         Angle.interiorAngle = function interiorAngle (p0, p1, p2) {
105042           var anglePrev = Angle.angle(p1, p0);
105043           var angleNext = Angle.angle(p1, p2);
105044           return Math.abs(angleNext - anglePrev)
105045         };
105046         Angle.normalizePositive = function normalizePositive (angle) {
105047           if (angle < 0.0) {
105048             while (angle < 0.0) { angle += Angle.PI_TIMES_2; }
105049             if (angle >= Angle.PI_TIMES_2) { angle = 0.0; }
105050           } else {
105051             while (angle >= Angle.PI_TIMES_2) { angle -= Angle.PI_TIMES_2; }
105052             if (angle < 0.0) { angle = 0.0; }
105053           }
105054           return angle
105055         };
105056         Angle.angleBetween = function angleBetween (tip1, tail, tip2) {
105057           var a1 = Angle.angle(tail, tip1);
105058           var a2 = Angle.angle(tail, tip2);
105059           return Angle.diff(a1, a2)
105060         };
105061         Angle.diff = function diff (ang1, ang2) {
105062           var delAngle = null;
105063           if (ang1 < ang2) {
105064             delAngle = ang2 - ang1;
105065           } else {
105066             delAngle = ang1 - ang2;
105067           }
105068           if (delAngle > Math.PI) {
105069             delAngle = 2 * Math.PI - delAngle;
105070           }
105071           return delAngle
105072         };
105073         Angle.toRadians = function toRadians (angleDegrees) {
105074           return angleDegrees * Math.PI / 180.0
105075         };
105076         Angle.getTurn = function getTurn (ang1, ang2) {
105077           var crossproduct = Math.sin(ang2 - ang1);
105078           if (crossproduct > 0) {
105079             return Angle.COUNTERCLOCKWISE
105080           }
105081           if (crossproduct < 0) {
105082             return Angle.CLOCKWISE
105083           }
105084           return Angle.NONE
105085         };
105086         Angle.angleBetweenOriented = function angleBetweenOriented (tip1, tail, tip2) {
105087           var a1 = Angle.angle(tail, tip1);
105088           var a2 = Angle.angle(tail, tip2);
105089           var angDel = a2 - a1;
105090           if (angDel <= -Math.PI) { return angDel + Angle.PI_TIMES_2 }
105091           if (angDel > Math.PI) { return angDel - Angle.PI_TIMES_2 }
105092           return angDel
105093         };
105094         staticAccessors$29.PI_TIMES_2.get = function () { return 2.0 * Math.PI };
105095         staticAccessors$29.PI_OVER_2.get = function () { return Math.PI / 2.0 };
105096         staticAccessors$29.PI_OVER_4.get = function () { return Math.PI / 4.0 };
105097         staticAccessors$29.COUNTERCLOCKWISE.get = function () { return CGAlgorithms.COUNTERCLOCKWISE };
105098         staticAccessors$29.CLOCKWISE.get = function () { return CGAlgorithms.CLOCKWISE };
105099         staticAccessors$29.NONE.get = function () { return CGAlgorithms.COLLINEAR };
105100
105101         Object.defineProperties( Angle, staticAccessors$29 );
105102
105103         var OffsetSegmentGenerator = function OffsetSegmentGenerator () {
105104           this._maxCurveSegmentError = 0.0;
105105           this._filletAngleQuantum = null;
105106           this._closingSegLengthFactor = 1;
105107           this._segList = null;
105108           this._distance = 0.0;
105109           this._precisionModel = null;
105110           this._bufParams = null;
105111           this._li = null;
105112           this._s0 = null;
105113           this._s1 = null;
105114           this._s2 = null;
105115           this._seg0 = new LineSegment();
105116           this._seg1 = new LineSegment();
105117           this._offset0 = new LineSegment();
105118           this._offset1 = new LineSegment();
105119           this._side = 0;
105120           this._hasNarrowConcaveAngle = false;
105121           var precisionModel = arguments[0];
105122           var bufParams = arguments[1];
105123           var distance = arguments[2];
105124           this._precisionModel = precisionModel;
105125           this._bufParams = bufParams;
105126           this._li = new RobustLineIntersector();
105127           this._filletAngleQuantum = Math.PI / 2.0 / bufParams.getQuadrantSegments();
105128           if (bufParams.getQuadrantSegments() >= 8 && bufParams.getJoinStyle() === BufferParameters.JOIN_ROUND) { this._closingSegLengthFactor = OffsetSegmentGenerator.MAX_CLOSING_SEG_LEN_FACTOR; }
105129           this.init(distance);
105130         };
105131
105132         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 } };
105133         OffsetSegmentGenerator.prototype.addNextSegment = function addNextSegment (p, addStartPoint) {
105134           this._s0 = this._s1;
105135           this._s1 = this._s2;
105136           this._s2 = p;
105137           this._seg0.setCoordinates(this._s0, this._s1);
105138           this.computeOffsetSegment(this._seg0, this._side, this._distance, this._offset0);
105139           this._seg1.setCoordinates(this._s1, this._s2);
105140           this.computeOffsetSegment(this._seg1, this._side, this._distance, this._offset1);
105141           if (this._s1.equals(this._s2)) { return null }
105142           var orientation = CGAlgorithms.computeOrientation(this._s0, this._s1, this._s2);
105143           var outsideTurn = (orientation === CGAlgorithms.CLOCKWISE && this._side === Position.LEFT) || (orientation === CGAlgorithms.COUNTERCLOCKWISE && this._side === Position.RIGHT);
105144           if (orientation === 0) {
105145             this.addCollinear(addStartPoint);
105146           } else if (outsideTurn) {
105147             this.addOutsideTurn(orientation, addStartPoint);
105148           } else {
105149             this.addInsideTurn(orientation, addStartPoint);
105150           }
105151         };
105152         OffsetSegmentGenerator.prototype.addLineEndCap = function addLineEndCap (p0, p1) {
105153           var seg = new LineSegment(p0, p1);
105154           var offsetL = new LineSegment();
105155           this.computeOffsetSegment(seg, Position.LEFT, this._distance, offsetL);
105156           var offsetR = new LineSegment();
105157           this.computeOffsetSegment(seg, Position.RIGHT, this._distance, offsetR);
105158           var dx = p1.x - p0.x;
105159           var dy = p1.y - p0.y;
105160           var angle = Math.atan2(dy, dx);
105161           switch (this._bufParams.getEndCapStyle()) {
105162             case BufferParameters.CAP_ROUND:
105163               this._segList.addPt(offsetL.p1);
105164               this.addFilletArc(p1, angle + Math.PI / 2, angle - Math.PI / 2, CGAlgorithms.CLOCKWISE, this._distance);
105165               this._segList.addPt(offsetR.p1);
105166               break
105167             case BufferParameters.CAP_FLAT:
105168               this._segList.addPt(offsetL.p1);
105169               this._segList.addPt(offsetR.p1);
105170               break
105171             case BufferParameters.CAP_SQUARE:
105172               var squareCapSideOffset = new Coordinate();
105173               squareCapSideOffset.x = Math.abs(this._distance) * Math.cos(angle);
105174               squareCapSideOffset.y = Math.abs(this._distance) * Math.sin(angle);
105175               var squareCapLOffset = new Coordinate(offsetL.p1.x + squareCapSideOffset.x, offsetL.p1.y + squareCapSideOffset.y);
105176               var squareCapROffset = new Coordinate(offsetR.p1.x + squareCapSideOffset.x, offsetR.p1.y + squareCapSideOffset.y);
105177               this._segList.addPt(squareCapLOffset);
105178               this._segList.addPt(squareCapROffset);
105179               break
105180           }
105181         };
105182         OffsetSegmentGenerator.prototype.getCoordinates = function getCoordinates () {
105183           var pts = this._segList.getCoordinates();
105184           return pts
105185         };
105186         OffsetSegmentGenerator.prototype.addMitreJoin = function addMitreJoin (p, offset0, offset1, distance) {
105187           var isMitreWithinLimit = true;
105188           var intPt = null;
105189           try {
105190             intPt = HCoordinate.intersection(offset0.p0, offset0.p1, offset1.p0, offset1.p1);
105191             var mitreRatio = distance <= 0.0 ? 1.0 : intPt.distance(p) / Math.abs(distance);
105192             if (mitreRatio > this._bufParams.getMitreLimit()) { isMitreWithinLimit = false; }
105193           } catch (ex) {
105194             if (ex instanceof NotRepresentableException) {
105195               intPt = new Coordinate(0, 0);
105196               isMitreWithinLimit = false;
105197             } else { throw ex }
105198           } finally {}
105199           if (isMitreWithinLimit) {
105200             this._segList.addPt(intPt);
105201           } else {
105202             this.addLimitedMitreJoin(offset0, offset1, distance, this._bufParams.getMitreLimit());
105203           }
105204         };
105205         OffsetSegmentGenerator.prototype.addFilletCorner = function addFilletCorner (p, p0, p1, direction, radius) {
105206           var dx0 = p0.x - p.x;
105207           var dy0 = p0.y - p.y;
105208           var startAngle = Math.atan2(dy0, dx0);
105209           var dx1 = p1.x - p.x;
105210           var dy1 = p1.y - p.y;
105211           var endAngle = Math.atan2(dy1, dx1);
105212           if (direction === CGAlgorithms.CLOCKWISE) {
105213             if (startAngle <= endAngle) { startAngle += 2.0 * Math.PI; }
105214           } else {
105215             if (startAngle >= endAngle) { startAngle -= 2.0 * Math.PI; }
105216           }
105217           this._segList.addPt(p0);
105218           this.addFilletArc(p, startAngle, endAngle, direction, radius);
105219           this._segList.addPt(p1);
105220         };
105221         OffsetSegmentGenerator.prototype.addOutsideTurn = function addOutsideTurn (orientation, addStartPoint) {
105222           if (this._offset0.p1.distance(this._offset1.p0) < this._distance * OffsetSegmentGenerator.OFFSET_SEGMENT_SEPARATION_FACTOR) {
105223             this._segList.addPt(this._offset0.p1);
105224             return null
105225           }
105226           if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_MITRE) {
105227             this.addMitreJoin(this._s1, this._offset0, this._offset1, this._distance);
105228           } else if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_BEVEL) {
105229             this.addBevelJoin(this._offset0, this._offset1);
105230           } else {
105231             if (addStartPoint) { this._segList.addPt(this._offset0.p1); }
105232             this.addFilletCorner(this._s1, this._offset0.p1, this._offset1.p0, orientation, this._distance);
105233             this._segList.addPt(this._offset1.p0);
105234           }
105235         };
105236         OffsetSegmentGenerator.prototype.createSquare = function createSquare (p) {
105237           this._segList.addPt(new Coordinate(p.x + this._distance, p.y + this._distance));
105238           this._segList.addPt(new Coordinate(p.x + this._distance, p.y - this._distance));
105239           this._segList.addPt(new Coordinate(p.x - this._distance, p.y - this._distance));
105240           this._segList.addPt(new Coordinate(p.x - this._distance, p.y + this._distance));
105241           this._segList.closeRing();
105242         };
105243         OffsetSegmentGenerator.prototype.addSegments = function addSegments (pt, isForward) {
105244           this._segList.addPts(pt, isForward);
105245         };
105246         OffsetSegmentGenerator.prototype.addFirstSegment = function addFirstSegment () {
105247           this._segList.addPt(this._offset1.p0);
105248         };
105249         OffsetSegmentGenerator.prototype.addLastSegment = function addLastSegment () {
105250           this._segList.addPt(this._offset1.p1);
105251         };
105252         OffsetSegmentGenerator.prototype.initSideSegments = function initSideSegments (s1, s2, side) {
105253           this._s1 = s1;
105254           this._s2 = s2;
105255           this._side = side;
105256           this._seg1.setCoordinates(s1, s2);
105257           this.computeOffsetSegment(this._seg1, side, this._distance, this._offset1);
105258         };
105259         OffsetSegmentGenerator.prototype.addLimitedMitreJoin = function addLimitedMitreJoin (offset0, offset1, distance, mitreLimit) {
105260           var basePt = this._seg0.p1;
105261           var ang0 = Angle.angle(basePt, this._seg0.p0);
105262           // const ang1 = Angle.angle(basePt, this._seg1.p1)
105263           var angDiff = Angle.angleBetweenOriented(this._seg0.p0, basePt, this._seg1.p1);
105264           var angDiffHalf = angDiff / 2;
105265           var midAng = Angle.normalize(ang0 + angDiffHalf);
105266           var mitreMidAng = Angle.normalize(midAng + Math.PI);
105267           var mitreDist = mitreLimit * distance;
105268           var bevelDelta = mitreDist * Math.abs(Math.sin(angDiffHalf));
105269           var bevelHalfLen = distance - bevelDelta;
105270           var bevelMidX = basePt.x + mitreDist * Math.cos(mitreMidAng);
105271           var bevelMidY = basePt.y + mitreDist * Math.sin(mitreMidAng);
105272           var bevelMidPt = new Coordinate(bevelMidX, bevelMidY);
105273           var mitreMidLine = new LineSegment(basePt, bevelMidPt);
105274           var bevelEndLeft = mitreMidLine.pointAlongOffset(1.0, bevelHalfLen);
105275           var bevelEndRight = mitreMidLine.pointAlongOffset(1.0, -bevelHalfLen);
105276           if (this._side === Position.LEFT) {
105277             this._segList.addPt(bevelEndLeft);
105278             this._segList.addPt(bevelEndRight);
105279           } else {
105280             this._segList.addPt(bevelEndRight);
105281             this._segList.addPt(bevelEndLeft);
105282           }
105283         };
105284         OffsetSegmentGenerator.prototype.computeOffsetSegment = function computeOffsetSegment (seg, side, distance, offset) {
105285           var sideSign = side === Position.LEFT ? 1 : -1;
105286           var dx = seg.p1.x - seg.p0.x;
105287           var dy = seg.p1.y - seg.p0.y;
105288           var len = Math.sqrt(dx * dx + dy * dy);
105289           var ux = sideSign * distance * dx / len;
105290           var uy = sideSign * distance * dy / len;
105291           offset.p0.x = seg.p0.x - uy;
105292           offset.p0.y = seg.p0.y + ux;
105293           offset.p1.x = seg.p1.x - uy;
105294           offset.p1.y = seg.p1.y + ux;
105295         };
105296         OffsetSegmentGenerator.prototype.addFilletArc = function addFilletArc (p, startAngle, endAngle, direction, radius) {
105297             var this$1 = this;
105298
105299           var directionFactor = direction === CGAlgorithms.CLOCKWISE ? -1 : 1;
105300           var totalAngle = Math.abs(startAngle - endAngle);
105301           var nSegs = Math.trunc(totalAngle / this._filletAngleQuantum + 0.5);
105302           if (nSegs < 1) { return null }
105303           var initAngle = 0.0;
105304           var currAngleInc = totalAngle / nSegs;
105305           var currAngle = initAngle;
105306           var pt = new Coordinate();
105307           while (currAngle < totalAngle) {
105308             var angle = startAngle + directionFactor * currAngle;
105309             pt.x = p.x + radius * Math.cos(angle);
105310             pt.y = p.y + radius * Math.sin(angle);
105311             this$1._segList.addPt(pt);
105312             currAngle += currAngleInc;
105313           }
105314         };
105315         OffsetSegmentGenerator.prototype.addInsideTurn = function addInsideTurn (orientation, addStartPoint) {
105316           this._li.computeIntersection(this._offset0.p0, this._offset0.p1, this._offset1.p0, this._offset1.p1);
105317           if (this._li.hasIntersection()) {
105318             this._segList.addPt(this._li.getIntersection(0));
105319           } else {
105320             this._hasNarrowConcaveAngle = true;
105321             if (this._offset0.p1.distance(this._offset1.p0) < this._distance * OffsetSegmentGenerator.INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR) {
105322               this._segList.addPt(this._offset0.p1);
105323             } else {
105324               this._segList.addPt(this._offset0.p1);
105325               if (this._closingSegLengthFactor > 0) {
105326                 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));
105327                 this._segList.addPt(mid0);
105328                 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));
105329                 this._segList.addPt(mid1);
105330               } else {
105331                 this._segList.addPt(this._s1);
105332               }
105333               this._segList.addPt(this._offset1.p0);
105334             }
105335           }
105336         };
105337         OffsetSegmentGenerator.prototype.createCircle = function createCircle (p) {
105338           var pt = new Coordinate(p.x + this._distance, p.y);
105339           this._segList.addPt(pt);
105340           this.addFilletArc(p, 0.0, 2.0 * Math.PI, -1, this._distance);
105341           this._segList.closeRing();
105342         };
105343         OffsetSegmentGenerator.prototype.addBevelJoin = function addBevelJoin (offset0, offset1) {
105344           this._segList.addPt(offset0.p1);
105345           this._segList.addPt(offset1.p0);
105346         };
105347         OffsetSegmentGenerator.prototype.init = function init (distance) {
105348           this._distance = distance;
105349           this._maxCurveSegmentError = distance * (1 - Math.cos(this._filletAngleQuantum / 2.0));
105350           this._segList = new OffsetSegmentString();
105351           this._segList.setPrecisionModel(this._precisionModel);
105352           this._segList.setMinimumVertexDistance(distance * OffsetSegmentGenerator.CURVE_VERTEX_SNAP_DISTANCE_FACTOR);
105353         };
105354         OffsetSegmentGenerator.prototype.addCollinear = function addCollinear (addStartPoint) {
105355           this._li.computeIntersection(this._s0, this._s1, this._s1, this._s2);
105356           var numInt = this._li.getIntersectionNum();
105357           if (numInt >= 2) {
105358             if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_BEVEL || this._bufParams.getJoinStyle() === BufferParameters.JOIN_MITRE) {
105359               if (addStartPoint) { this._segList.addPt(this._offset0.p1); }
105360               this._segList.addPt(this._offset1.p0);
105361             } else {
105362               this.addFilletCorner(this._s1, this._offset0.p1, this._offset1.p0, CGAlgorithms.CLOCKWISE, this._distance);
105363             }
105364           }
105365         };
105366         OffsetSegmentGenerator.prototype.closeRing = function closeRing () {
105367           this._segList.closeRing();
105368         };
105369         OffsetSegmentGenerator.prototype.hasNarrowConcaveAngle = function hasNarrowConcaveAngle () {
105370           return this._hasNarrowConcaveAngle
105371         };
105372         OffsetSegmentGenerator.prototype.interfaces_ = function interfaces_ () {
105373           return []
105374         };
105375         OffsetSegmentGenerator.prototype.getClass = function getClass () {
105376           return OffsetSegmentGenerator
105377         };
105378         staticAccessors$27.OFFSET_SEGMENT_SEPARATION_FACTOR.get = function () { return 1.0E-3 };
105379         staticAccessors$27.INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR.get = function () { return 1.0E-3 };
105380         staticAccessors$27.CURVE_VERTEX_SNAP_DISTANCE_FACTOR.get = function () { return 1.0E-6 };
105381         staticAccessors$27.MAX_CLOSING_SEG_LEN_FACTOR.get = function () { return 80 };
105382
105383         Object.defineProperties( OffsetSegmentGenerator, staticAccessors$27 );
105384
105385         var OffsetCurveBuilder = function OffsetCurveBuilder () {
105386           this._distance = 0.0;
105387           this._precisionModel = null;
105388           this._bufParams = null;
105389           var precisionModel = arguments[0];
105390           var bufParams = arguments[1];
105391           this._precisionModel = precisionModel;
105392           this._bufParams = bufParams;
105393         };
105394         OffsetCurveBuilder.prototype.getOffsetCurve = function getOffsetCurve (inputPts, distance) {
105395           this._distance = distance;
105396           if (distance === 0.0) { return null }
105397           var isRightSide = distance < 0.0;
105398           var posDistance = Math.abs(distance);
105399           var segGen = this.getSegGen(posDistance);
105400           if (inputPts.length <= 1) {
105401             this.computePointCurve(inputPts[0], segGen);
105402           } else {
105403             this.computeOffsetCurve(inputPts, isRightSide, segGen);
105404           }
105405           var curvePts = segGen.getCoordinates();
105406           if (isRightSide) { CoordinateArrays.reverse(curvePts); }
105407           return curvePts
105408         };
105409         OffsetCurveBuilder.prototype.computeSingleSidedBufferCurve = function computeSingleSidedBufferCurve (inputPts, isRightSide, segGen) {
105410           var distTol = this.simplifyTolerance(this._distance);
105411           if (isRightSide) {
105412             segGen.addSegments(inputPts, true);
105413             var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
105414             var n2 = simp2.length - 1;
105415             segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
105416             segGen.addFirstSegment();
105417             for (var i = n2 - 2; i >= 0; i--) {
105418               segGen.addNextSegment(simp2[i], true);
105419             }
105420           } else {
105421             segGen.addSegments(inputPts, false);
105422             var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
105423             var n1 = simp1.length - 1;
105424             segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
105425             segGen.addFirstSegment();
105426             for (var i$1 = 2; i$1 <= n1; i$1++) {
105427               segGen.addNextSegment(simp1[i$1], true);
105428             }
105429           }
105430           segGen.addLastSegment();
105431           segGen.closeRing();
105432         };
105433         OffsetCurveBuilder.prototype.computeRingBufferCurve = function computeRingBufferCurve (inputPts, side, segGen) {
105434           var distTol = this.simplifyTolerance(this._distance);
105435           if (side === Position.RIGHT) { distTol = -distTol; }
105436           var simp = BufferInputLineSimplifier.simplify(inputPts, distTol);
105437           var n = simp.length - 1;
105438           segGen.initSideSegments(simp[n - 1], simp[0], side);
105439           for (var i = 1; i <= n; i++) {
105440             var addStartPoint = i !== 1;
105441             segGen.addNextSegment(simp[i], addStartPoint);
105442           }
105443           segGen.closeRing();
105444         };
105445         OffsetCurveBuilder.prototype.computeLineBufferCurve = function computeLineBufferCurve (inputPts, segGen) {
105446           var distTol = this.simplifyTolerance(this._distance);
105447           var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
105448           var n1 = simp1.length - 1;
105449           segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
105450           for (var i = 2; i <= n1; i++) {
105451             segGen.addNextSegment(simp1[i], true);
105452           }
105453           segGen.addLastSegment();
105454           segGen.addLineEndCap(simp1[n1 - 1], simp1[n1]);
105455           var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
105456           var n2 = simp2.length - 1;
105457           segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
105458           for (var i$1 = n2 - 2; i$1 >= 0; i$1--) {
105459             segGen.addNextSegment(simp2[i$1], true);
105460           }
105461           segGen.addLastSegment();
105462           segGen.addLineEndCap(simp2[1], simp2[0]);
105463           segGen.closeRing();
105464         };
105465         OffsetCurveBuilder.prototype.computePointCurve = function computePointCurve (pt, segGen) {
105466           switch (this._bufParams.getEndCapStyle()) {
105467             case BufferParameters.CAP_ROUND:
105468               segGen.createCircle(pt);
105469               break
105470             case BufferParameters.CAP_SQUARE:
105471               segGen.createSquare(pt);
105472               break
105473           }
105474         };
105475         OffsetCurveBuilder.prototype.getLineCurve = function getLineCurve (inputPts, distance) {
105476           this._distance = distance;
105477           if (distance < 0.0 && !this._bufParams.isSingleSided()) { return null }
105478           if (distance === 0.0) { return null }
105479           var posDistance = Math.abs(distance);
105480           var segGen = this.getSegGen(posDistance);
105481           if (inputPts.length <= 1) {
105482             this.computePointCurve(inputPts[0], segGen);
105483           } else {
105484             if (this._bufParams.isSingleSided()) {
105485               var isRightSide = distance < 0.0;
105486               this.computeSingleSidedBufferCurve(inputPts, isRightSide, segGen);
105487             } else { this.computeLineBufferCurve(inputPts, segGen); }
105488           }
105489           var lineCoord = segGen.getCoordinates();
105490           return lineCoord
105491         };
105492         OffsetCurveBuilder.prototype.getBufferParameters = function getBufferParameters () {
105493           return this._bufParams
105494         };
105495         OffsetCurveBuilder.prototype.simplifyTolerance = function simplifyTolerance (bufDistance) {
105496           return bufDistance * this._bufParams.getSimplifyFactor()
105497         };
105498         OffsetCurveBuilder.prototype.getRingCurve = function getRingCurve (inputPts, side, distance) {
105499           this._distance = distance;
105500           if (inputPts.length <= 2) { return this.getLineCurve(inputPts, distance) }
105501           if (distance === 0.0) {
105502             return OffsetCurveBuilder.copyCoordinates(inputPts)
105503           }
105504           var segGen = this.getSegGen(distance);
105505           this.computeRingBufferCurve(inputPts, side, segGen);
105506           return segGen.getCoordinates()
105507         };
105508         OffsetCurveBuilder.prototype.computeOffsetCurve = function computeOffsetCurve (inputPts, isRightSide, segGen) {
105509           var distTol = this.simplifyTolerance(this._distance);
105510           if (isRightSide) {
105511             var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
105512             var n2 = simp2.length - 1;
105513             segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
105514             segGen.addFirstSegment();
105515             for (var i = n2 - 2; i >= 0; i--) {
105516               segGen.addNextSegment(simp2[i], true);
105517             }
105518           } else {
105519             var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
105520             var n1 = simp1.length - 1;
105521             segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
105522             segGen.addFirstSegment();
105523             for (var i$1 = 2; i$1 <= n1; i$1++) {
105524               segGen.addNextSegment(simp1[i$1], true);
105525             }
105526           }
105527           segGen.addLastSegment();
105528         };
105529         OffsetCurveBuilder.prototype.getSegGen = function getSegGen (distance) {
105530           return new OffsetSegmentGenerator(this._precisionModel, this._bufParams, distance)
105531         };
105532         OffsetCurveBuilder.prototype.interfaces_ = function interfaces_ () {
105533           return []
105534         };
105535         OffsetCurveBuilder.prototype.getClass = function getClass () {
105536           return OffsetCurveBuilder
105537         };
105538         OffsetCurveBuilder.copyCoordinates = function copyCoordinates (pts) {
105539           var copy = new Array(pts.length).fill(null);
105540           for (var i = 0; i < copy.length; i++) {
105541             copy[i] = new Coordinate(pts[i]);
105542           }
105543           return copy
105544         };
105545
105546         var SubgraphDepthLocater = function SubgraphDepthLocater () {
105547           this._subgraphs = null;
105548           this._seg = new LineSegment();
105549           this._cga = new CGAlgorithms();
105550           var subgraphs = arguments[0];
105551           this._subgraphs = subgraphs;
105552         };
105553
105554         var staticAccessors$30 = { DepthSegment: { configurable: true } };
105555         SubgraphDepthLocater.prototype.findStabbedSegments = function findStabbedSegments () {
105556             var this$1 = this;
105557
105558           if (arguments.length === 1) {
105559             var stabbingRayLeftPt = arguments[0];
105560             var stabbedSegments = new ArrayList();
105561             for (var i = this._subgraphs.iterator(); i.hasNext();) {
105562               var bsg = i.next();
105563               var env = bsg.getEnvelope();
105564               if (stabbingRayLeftPt.y < env.getMinY() || stabbingRayLeftPt.y > env.getMaxY()) { continue }
105565               this$1.findStabbedSegments(stabbingRayLeftPt, bsg.getDirectedEdges(), stabbedSegments);
105566             }
105567             return stabbedSegments
105568           } else if (arguments.length === 3) {
105569             if (hasInterface(arguments[2], List) && (arguments[0] instanceof Coordinate && arguments[1] instanceof DirectedEdge)) {
105570               var stabbingRayLeftPt$1 = arguments[0];
105571               var dirEdge = arguments[1];
105572               var stabbedSegments$1 = arguments[2];
105573               var pts = dirEdge.getEdge().getCoordinates();
105574               for (var i$1 = 0; i$1 < pts.length - 1; i$1++) {
105575                 this$1._seg.p0 = pts[i$1];
105576                 this$1._seg.p1 = pts[i$1 + 1];
105577                 if (this$1._seg.p0.y > this$1._seg.p1.y) { this$1._seg.reverse(); }
105578                 var maxx = Math.max(this$1._seg.p0.x, this$1._seg.p1.x);
105579                 if (maxx < stabbingRayLeftPt$1.x) { continue }
105580                 if (this$1._seg.isHorizontal()) { continue }
105581                 if (stabbingRayLeftPt$1.y < this$1._seg.p0.y || stabbingRayLeftPt$1.y > this$1._seg.p1.y) { continue }
105582                 if (CGAlgorithms.computeOrientation(this$1._seg.p0, this$1._seg.p1, stabbingRayLeftPt$1) === CGAlgorithms.RIGHT) { continue }
105583                 var depth = dirEdge.getDepth(Position.LEFT);
105584                 if (!this$1._seg.p0.equals(pts[i$1])) { depth = dirEdge.getDepth(Position.RIGHT); }
105585                 var ds = new DepthSegment(this$1._seg, depth);
105586                 stabbedSegments$1.add(ds);
105587               }
105588             } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Coordinate && hasInterface(arguments[1], List))) {
105589               var stabbingRayLeftPt$2 = arguments[0];
105590               var dirEdges = arguments[1];
105591               var stabbedSegments$2 = arguments[2];
105592               for (var i$2 = dirEdges.iterator(); i$2.hasNext();) {
105593                 var de = i$2.next();
105594                 if (!de.isForward()) { continue }
105595                 this$1.findStabbedSegments(stabbingRayLeftPt$2, de, stabbedSegments$2);
105596               }
105597             }
105598           }
105599         };
105600         SubgraphDepthLocater.prototype.getDepth = function getDepth (p) {
105601           var stabbedSegments = this.findStabbedSegments(p);
105602           if (stabbedSegments.size() === 0) { return 0 }
105603           var ds = Collections.min(stabbedSegments);
105604           return ds._leftDepth
105605         };
105606         SubgraphDepthLocater.prototype.interfaces_ = function interfaces_ () {
105607           return []
105608         };
105609         SubgraphDepthLocater.prototype.getClass = function getClass () {
105610           return SubgraphDepthLocater
105611         };
105612         staticAccessors$30.DepthSegment.get = function () { return DepthSegment };
105613
105614         Object.defineProperties( SubgraphDepthLocater, staticAccessors$30 );
105615
105616         var DepthSegment = function DepthSegment () {
105617           this._upwardSeg = null;
105618           this._leftDepth = null;
105619           var seg = arguments[0];
105620           var depth = arguments[1];
105621           this._upwardSeg = new LineSegment(seg);
105622           this._leftDepth = depth;
105623         };
105624         DepthSegment.prototype.compareTo = function compareTo (obj) {
105625           var other = obj;
105626           if (this._upwardSeg.minX() >= other._upwardSeg.maxX()) { return 1 }
105627           if (this._upwardSeg.maxX() <= other._upwardSeg.minX()) { return -1 }
105628           var orientIndex = this._upwardSeg.orientationIndex(other._upwardSeg);
105629           if (orientIndex !== 0) { return orientIndex }
105630           orientIndex = -1 * other._upwardSeg.orientationIndex(this._upwardSeg);
105631           if (orientIndex !== 0) { return orientIndex }
105632           return this._upwardSeg.compareTo(other._upwardSeg)
105633         };
105634         DepthSegment.prototype.compareX = function compareX (seg0, seg1) {
105635           var compare0 = seg0.p0.compareTo(seg1.p0);
105636           if (compare0 !== 0) { return compare0 }
105637           return seg0.p1.compareTo(seg1.p1)
105638         };
105639         DepthSegment.prototype.toString = function toString () {
105640           return this._upwardSeg.toString()
105641         };
105642         DepthSegment.prototype.interfaces_ = function interfaces_ () {
105643           return [Comparable]
105644         };
105645         DepthSegment.prototype.getClass = function getClass () {
105646           return DepthSegment
105647         };
105648
105649         var Triangle = function Triangle (p0, p1, p2) {
105650           this.p0 = p0 || null;
105651           this.p1 = p1 || null;
105652           this.p2 = p2 || null;
105653         };
105654         Triangle.prototype.area = function area () {
105655           return Triangle.area(this.p0, this.p1, this.p2)
105656         };
105657         Triangle.prototype.signedArea = function signedArea () {
105658           return Triangle.signedArea(this.p0, this.p1, this.p2)
105659         };
105660         Triangle.prototype.interpolateZ = function interpolateZ (p) {
105661           if (p === null) { throw new IllegalArgumentException('Supplied point is null.') }
105662           return Triangle.interpolateZ(p, this.p0, this.p1, this.p2)
105663         };
105664         Triangle.prototype.longestSideLength = function longestSideLength () {
105665           return Triangle.longestSideLength(this.p0, this.p1, this.p2)
105666         };
105667         Triangle.prototype.isAcute = function isAcute () {
105668           return Triangle.isAcute(this.p0, this.p1, this.p2)
105669         };
105670         Triangle.prototype.circumcentre = function circumcentre () {
105671           return Triangle.circumcentre(this.p0, this.p1, this.p2)
105672         };
105673         Triangle.prototype.area3D = function area3D () {
105674           return Triangle.area3D(this.p0, this.p1, this.p2)
105675         };
105676         Triangle.prototype.centroid = function centroid () {
105677           return Triangle.centroid(this.p0, this.p1, this.p2)
105678         };
105679         Triangle.prototype.inCentre = function inCentre () {
105680           return Triangle.inCentre(this.p0, this.p1, this.p2)
105681         };
105682         Triangle.prototype.interfaces_ = function interfaces_ () {
105683           return []
105684         };
105685         Triangle.prototype.getClass = function getClass () {
105686           return Triangle
105687         };
105688         Triangle.area = function area (a, b, c) {
105689           return Math.abs(((c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y)) / 2)
105690         };
105691         Triangle.signedArea = function signedArea (a, b, c) {
105692           return ((c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y)) / 2
105693         };
105694         Triangle.det = function det (m00, m01, m10, m11) {
105695           return m00 * m11 - m01 * m10
105696         };
105697         Triangle.interpolateZ = function interpolateZ (p, v0, v1, v2) {
105698           var x0 = v0.x;
105699           var y0 = v0.y;
105700           var a = v1.x - x0;
105701           var b = v2.x - x0;
105702           var c = v1.y - y0;
105703           var d = v2.y - y0;
105704           var det = a * d - b * c;
105705           var dx = p.x - x0;
105706           var dy = p.y - y0;
105707           var t = (d * dx - b * dy) / det;
105708           var u = (-c * dx + a * dy) / det;
105709           var z = v0.z + t * (v1.z - v0.z) + u * (v2.z - v0.z);
105710           return z
105711         };
105712         Triangle.longestSideLength = function longestSideLength (a, b, c) {
105713           var lenAB = a.distance(b);
105714           var lenBC = b.distance(c);
105715           var lenCA = c.distance(a);
105716           var maxLen = lenAB;
105717           if (lenBC > maxLen) { maxLen = lenBC; }
105718           if (lenCA > maxLen) { maxLen = lenCA; }
105719           return maxLen
105720         };
105721         Triangle.isAcute = function isAcute (a, b, c) {
105722           if (!Angle.isAcute(a, b, c)) { return false }
105723           if (!Angle.isAcute(b, c, a)) { return false }
105724           if (!Angle.isAcute(c, a, b)) { return false }
105725           return true
105726         };
105727         Triangle.circumcentre = function circumcentre (a, b, c) {
105728           var cx = c.x;
105729           var cy = c.y;
105730           var ax = a.x - cx;
105731           var ay = a.y - cy;
105732           var bx = b.x - cx;
105733           var by = b.y - cy;
105734           var denom = 2 * Triangle.det(ax, ay, bx, by);
105735           var numx = Triangle.det(ay, ax * ax + ay * ay, by, bx * bx + by * by);
105736           var numy = Triangle.det(ax, ax * ax + ay * ay, bx, bx * bx + by * by);
105737           var ccx = cx - numx / denom;
105738           var ccy = cy + numy / denom;
105739           return new Coordinate(ccx, ccy)
105740         };
105741         Triangle.perpendicularBisector = function perpendicularBisector (a, b) {
105742           var dx = b.x - a.x;
105743           var dy = b.y - a.y;
105744           var l1 = new HCoordinate(a.x + dx / 2.0, a.y + dy / 2.0, 1.0);
105745           var l2 = new HCoordinate(a.x - dy + dx / 2.0, a.y + dx + dy / 2.0, 1.0);
105746           return new HCoordinate(l1, l2)
105747         };
105748         Triangle.angleBisector = function angleBisector (a, b, c) {
105749           var len0 = b.distance(a);
105750           var len2 = b.distance(c);
105751           var frac = len0 / (len0 + len2);
105752           var dx = c.x - a.x;
105753           var dy = c.y - a.y;
105754           var splitPt = new Coordinate(a.x + frac * dx, a.y + frac * dy);
105755           return splitPt
105756         };
105757         Triangle.area3D = function area3D (a, b, c) {
105758           var ux = b.x - a.x;
105759           var uy = b.y - a.y;
105760           var uz = b.z - a.z;
105761           var vx = c.x - a.x;
105762           var vy = c.y - a.y;
105763           var vz = c.z - a.z;
105764           var crossx = uy * vz - uz * vy;
105765           var crossy = uz * vx - ux * vz;
105766           var crossz = ux * vy - uy * vx;
105767           var absSq = crossx * crossx + crossy * crossy + crossz * crossz;
105768           var area3D = Math.sqrt(absSq) / 2;
105769           return area3D
105770         };
105771         Triangle.centroid = function centroid (a, b, c) {
105772           var x = (a.x + b.x + c.x) / 3;
105773           var y = (a.y + b.y + c.y) / 3;
105774           return new Coordinate(x, y)
105775         };
105776         Triangle.inCentre = function inCentre (a, b, c) {
105777           var len0 = b.distance(c);
105778           var len1 = a.distance(c);
105779           var len2 = a.distance(b);
105780           var circum = len0 + len1 + len2;
105781           var inCentreX = (len0 * a.x + len1 * b.x + len2 * c.x) / circum;
105782           var inCentreY = (len0 * a.y + len1 * b.y + len2 * c.y) / circum;
105783           return new Coordinate(inCentreX, inCentreY)
105784         };
105785
105786         var OffsetCurveSetBuilder = function OffsetCurveSetBuilder () {
105787           this._inputGeom = null;
105788           this._distance = null;
105789           this._curveBuilder = null;
105790           this._curveList = new ArrayList();
105791           var inputGeom = arguments[0];
105792           var distance = arguments[1];
105793           var curveBuilder = arguments[2];
105794           this._inputGeom = inputGeom;
105795           this._distance = distance;
105796           this._curveBuilder = curveBuilder;
105797         };
105798         OffsetCurveSetBuilder.prototype.addPoint = function addPoint (p) {
105799           if (this._distance <= 0.0) { return null }
105800           var coord = p.getCoordinates();
105801           var curve = this._curveBuilder.getLineCurve(coord, this._distance);
105802           this.addCurve(curve, Location.EXTERIOR, Location.INTERIOR);
105803         };
105804         OffsetCurveSetBuilder.prototype.addPolygon = function addPolygon (p) {
105805             var this$1 = this;
105806
105807           var offsetDistance = this._distance;
105808           var offsetSide = Position.LEFT;
105809           if (this._distance < 0.0) {
105810             offsetDistance = -this._distance;
105811             offsetSide = Position.RIGHT;
105812           }
105813           var shell = p.getExteriorRing();
105814           var shellCoord = CoordinateArrays.removeRepeatedPoints(shell.getCoordinates());
105815           if (this._distance < 0.0 && this.isErodedCompletely(shell, this._distance)) { return null }
105816           if (this._distance <= 0.0 && shellCoord.length < 3) { return null }
105817           this.addPolygonRing(shellCoord, offsetDistance, offsetSide, Location.EXTERIOR, Location.INTERIOR);
105818           for (var i = 0; i < p.getNumInteriorRing(); i++) {
105819             var hole = p.getInteriorRingN(i);
105820             var holeCoord = CoordinateArrays.removeRepeatedPoints(hole.getCoordinates());
105821             if (this$1._distance > 0.0 && this$1.isErodedCompletely(hole, -this$1._distance)) { continue }
105822             this$1.addPolygonRing(holeCoord, offsetDistance, Position.opposite(offsetSide), Location.INTERIOR, Location.EXTERIOR);
105823           }
105824         };
105825         OffsetCurveSetBuilder.prototype.isTriangleErodedCompletely = function isTriangleErodedCompletely (triangleCoord, bufferDistance) {
105826           var tri = new Triangle(triangleCoord[0], triangleCoord[1], triangleCoord[2]);
105827           var inCentre = tri.inCentre();
105828           var distToCentre = CGAlgorithms.distancePointLine(inCentre, tri.p0, tri.p1);
105829           return distToCentre < Math.abs(bufferDistance)
105830         };
105831         OffsetCurveSetBuilder.prototype.addLineString = function addLineString (line) {
105832           if (this._distance <= 0.0 && !this._curveBuilder.getBufferParameters().isSingleSided()) { return null }
105833           var coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
105834           var curve = this._curveBuilder.getLineCurve(coord, this._distance);
105835           this.addCurve(curve, Location.EXTERIOR, Location.INTERIOR);
105836         };
105837         OffsetCurveSetBuilder.prototype.addCurve = function addCurve (coord, leftLoc, rightLoc) {
105838           if (coord === null || coord.length < 2) { return null }
105839           var e = new NodedSegmentString(coord, new Label(0, Location.BOUNDARY, leftLoc, rightLoc));
105840           this._curveList.add(e);
105841         };
105842         OffsetCurveSetBuilder.prototype.getCurves = function getCurves () {
105843           this.add(this._inputGeom);
105844           return this._curveList
105845         };
105846         OffsetCurveSetBuilder.prototype.addPolygonRing = function addPolygonRing (coord, offsetDistance, side, cwLeftLoc, cwRightLoc) {
105847           if (offsetDistance === 0.0 && coord.length < LinearRing.MINIMUM_VALID_SIZE) { return null }
105848           var leftLoc = cwLeftLoc;
105849           var rightLoc = cwRightLoc;
105850           if (coord.length >= LinearRing.MINIMUM_VALID_SIZE && CGAlgorithms.isCCW(coord)) {
105851             leftLoc = cwRightLoc;
105852             rightLoc = cwLeftLoc;
105853             side = Position.opposite(side);
105854           }
105855           var curve = this._curveBuilder.getRingCurve(coord, side, offsetDistance);
105856           this.addCurve(curve, leftLoc, rightLoc);
105857         };
105858         OffsetCurveSetBuilder.prototype.add = function add (g) {
105859           if (g.isEmpty()) { return null }
105860           if (g instanceof Polygon) { this.addPolygon(g); }
105861           else if (g instanceof LineString) { this.addLineString(g); }
105862           else if (g instanceof Point$1) { this.addPoint(g); }
105863           else if (g instanceof MultiPoint) { this.addCollection(g); }
105864           else if (g instanceof MultiLineString) { this.addCollection(g); }
105865           else if (g instanceof MultiPolygon) { this.addCollection(g); }
105866           else if (g instanceof GeometryCollection) { this.addCollection(g); }
105867           // else throw new UnsupportedOperationException(g.getClass().getName())
105868         };
105869         OffsetCurveSetBuilder.prototype.isErodedCompletely = function isErodedCompletely (ring, bufferDistance) {
105870           var ringCoord = ring.getCoordinates();
105871           // const minDiam = 0.0
105872           if (ringCoord.length < 4) { return bufferDistance < 0 }
105873           if (ringCoord.length === 4) { return this.isTriangleErodedCompletely(ringCoord, bufferDistance) }
105874           var env = ring.getEnvelopeInternal();
105875           var envMinDimension = Math.min(env.getHeight(), env.getWidth());
105876           if (bufferDistance < 0.0 && 2 * Math.abs(bufferDistance) > envMinDimension) { return true }
105877           return false
105878         };
105879         OffsetCurveSetBuilder.prototype.addCollection = function addCollection (gc) {
105880             var this$1 = this;
105881
105882           for (var i = 0; i < gc.getNumGeometries(); i++) {
105883             var g = gc.getGeometryN(i);
105884             this$1.add(g);
105885           }
105886         };
105887         OffsetCurveSetBuilder.prototype.interfaces_ = function interfaces_ () {
105888           return []
105889         };
105890         OffsetCurveSetBuilder.prototype.getClass = function getClass () {
105891           return OffsetCurveSetBuilder
105892         };
105893
105894         var PointOnGeometryLocator = function PointOnGeometryLocator () {};
105895
105896         PointOnGeometryLocator.prototype.locate = function locate (p) {};
105897         PointOnGeometryLocator.prototype.interfaces_ = function interfaces_ () {
105898           return []
105899         };
105900         PointOnGeometryLocator.prototype.getClass = function getClass () {
105901           return PointOnGeometryLocator
105902         };
105903
105904         var GeometryCollectionIterator = function GeometryCollectionIterator () {
105905           this._parent = null;
105906           this._atStart = null;
105907           this._max = null;
105908           this._index = null;
105909           this._subcollectionIterator = null;
105910           var parent = arguments[0];
105911           this._parent = parent;
105912           this._atStart = true;
105913           this._index = 0;
105914           this._max = parent.getNumGeometries();
105915         };
105916         GeometryCollectionIterator.prototype.next = function next () {
105917           if (this._atStart) {
105918             this._atStart = false;
105919             if (GeometryCollectionIterator.isAtomic(this._parent)) { this._index++; }
105920             return this._parent
105921           }
105922           if (this._subcollectionIterator !== null) {
105923             if (this._subcollectionIterator.hasNext()) {
105924               return this._subcollectionIterator.next()
105925             } else {
105926               this._subcollectionIterator = null;
105927             }
105928           }
105929           if (this._index >= this._max) {
105930             throw new NoSuchElementException()
105931           }
105932           var obj = this._parent.getGeometryN(this._index++);
105933           if (obj instanceof GeometryCollection) {
105934             this._subcollectionIterator = new GeometryCollectionIterator(obj);
105935             return this._subcollectionIterator.next()
105936           }
105937           return obj
105938         };
105939         GeometryCollectionIterator.prototype.remove = function remove () {
105940           throw new Error(this.getClass().getName())
105941         };
105942         GeometryCollectionIterator.prototype.hasNext = function hasNext () {
105943           if (this._atStart) {
105944             return true
105945           }
105946           if (this._subcollectionIterator !== null) {
105947             if (this._subcollectionIterator.hasNext()) {
105948               return true
105949             }
105950             this._subcollectionIterator = null;
105951           }
105952           if (this._index >= this._max) {
105953             return false
105954           }
105955           return true
105956         };
105957         GeometryCollectionIterator.prototype.interfaces_ = function interfaces_ () {
105958           return [Iterator$1]
105959         };
105960         GeometryCollectionIterator.prototype.getClass = function getClass () {
105961           return GeometryCollectionIterator
105962         };
105963         GeometryCollectionIterator.isAtomic = function isAtomic (geom) {
105964           return !(geom instanceof GeometryCollection)
105965         };
105966
105967         var SimplePointInAreaLocator = function SimplePointInAreaLocator () {
105968           this._geom = null;
105969           var geom = arguments[0];
105970           this._geom = geom;
105971         };
105972         SimplePointInAreaLocator.prototype.locate = function locate (p) {
105973           return SimplePointInAreaLocator.locate(p, this._geom)
105974         };
105975         SimplePointInAreaLocator.prototype.interfaces_ = function interfaces_ () {
105976           return [PointOnGeometryLocator]
105977         };
105978         SimplePointInAreaLocator.prototype.getClass = function getClass () {
105979           return SimplePointInAreaLocator
105980         };
105981         SimplePointInAreaLocator.isPointInRing = function isPointInRing (p, ring) {
105982           if (!ring.getEnvelopeInternal().intersects(p)) { return false }
105983           return CGAlgorithms.isPointInRing(p, ring.getCoordinates())
105984         };
105985         SimplePointInAreaLocator.containsPointInPolygon = function containsPointInPolygon (p, poly) {
105986           if (poly.isEmpty()) { return false }
105987           var shell = poly.getExteriorRing();
105988           if (!SimplePointInAreaLocator.isPointInRing(p, shell)) { return false }
105989           for (var i = 0; i < poly.getNumInteriorRing(); i++) {
105990             var hole = poly.getInteriorRingN(i);
105991             if (SimplePointInAreaLocator.isPointInRing(p, hole)) { return false }
105992           }
105993           return true
105994         };
105995         SimplePointInAreaLocator.containsPoint = function containsPoint (p, geom) {
105996           if (geom instanceof Polygon) {
105997             return SimplePointInAreaLocator.containsPointInPolygon(p, geom)
105998           } else if (geom instanceof GeometryCollection) {
105999             var geomi = new GeometryCollectionIterator(geom);
106000             while (geomi.hasNext()) {
106001               var g2 = geomi.next();
106002               if (g2 !== geom) { if (SimplePointInAreaLocator.containsPoint(p, g2)) { return true } }
106003             }
106004           }
106005           return false
106006         };
106007         SimplePointInAreaLocator.locate = function locate (p, geom) {
106008           if (geom.isEmpty()) { return Location.EXTERIOR }
106009           if (SimplePointInAreaLocator.containsPoint(p, geom)) { return Location.INTERIOR }
106010           return Location.EXTERIOR
106011         };
106012
106013         var EdgeEndStar = function EdgeEndStar () {
106014           this._edgeMap = new TreeMap();
106015           this._edgeList = null;
106016           this._ptInAreaLocation = [Location.NONE, Location.NONE];
106017         };
106018         EdgeEndStar.prototype.getNextCW = function getNextCW (ee) {
106019           this.getEdges();
106020           var i = this._edgeList.indexOf(ee);
106021           var iNextCW = i - 1;
106022           if (i === 0) { iNextCW = this._edgeList.size() - 1; }
106023           return this._edgeList.get(iNextCW)
106024         };
106025         EdgeEndStar.prototype.propagateSideLabels = function propagateSideLabels (geomIndex) {
106026           var startLoc = Location.NONE;
106027           for (var it = this.iterator(); it.hasNext();) {
106028             var e = it.next();
106029             var label = e.getLabel();
106030             if (label.isArea(geomIndex) && label.getLocation(geomIndex, Position.LEFT) !== Location.NONE) { startLoc = label.getLocation(geomIndex, Position.LEFT); }
106031           }
106032           if (startLoc === Location.NONE) { return null }
106033           var currLoc = startLoc;
106034           for (var it$1 = this.iterator(); it$1.hasNext();) {
106035             var e$1 = it$1.next();
106036             var label$1 = e$1.getLabel();
106037             if (label$1.getLocation(geomIndex, Position.ON) === Location.NONE) { label$1.setLocation(geomIndex, Position.ON, currLoc); }
106038             if (label$1.isArea(geomIndex)) {
106039               var leftLoc = label$1.getLocation(geomIndex, Position.LEFT);
106040               var rightLoc = label$1.getLocation(geomIndex, Position.RIGHT);
106041               if (rightLoc !== Location.NONE) {
106042                 if (rightLoc !== currLoc) { throw new TopologyException('side location conflict', e$1.getCoordinate()) }
106043                 if (leftLoc === Location.NONE) {
106044                   Assert.shouldNeverReachHere('found single null side (at ' + e$1.getCoordinate() + ')');
106045                 }
106046                 currLoc = leftLoc;
106047               } else {
106048                 Assert.isTrue(label$1.getLocation(geomIndex, Position.LEFT) === Location.NONE, 'found single null side');
106049                 label$1.setLocation(geomIndex, Position.RIGHT, currLoc);
106050                 label$1.setLocation(geomIndex, Position.LEFT, currLoc);
106051               }
106052             }
106053           }
106054         };
106055         EdgeEndStar.prototype.getCoordinate = function getCoordinate () {
106056           var it = this.iterator();
106057           if (!it.hasNext()) { return null }
106058           var e = it.next();
106059           return e.getCoordinate()
106060         };
106061         EdgeEndStar.prototype.print = function print (out) {
106062           System.out.println('EdgeEndStar:   ' + this.getCoordinate());
106063           for (var it = this.iterator(); it.hasNext();) {
106064             var e = it.next();
106065             e.print(out);
106066           }
106067         };
106068         EdgeEndStar.prototype.isAreaLabelsConsistent = function isAreaLabelsConsistent (geomGraph) {
106069           this.computeEdgeEndLabels(geomGraph.getBoundaryNodeRule());
106070           return this.checkAreaLabelsConsistent(0)
106071         };
106072         EdgeEndStar.prototype.checkAreaLabelsConsistent = function checkAreaLabelsConsistent (geomIndex) {
106073           var edges = this.getEdges();
106074           if (edges.size() <= 0) { return true }
106075           var lastEdgeIndex = edges.size() - 1;
106076           var startLabel = edges.get(lastEdgeIndex).getLabel();
106077           var startLoc = startLabel.getLocation(geomIndex, Position.LEFT);
106078           Assert.isTrue(startLoc !== Location.NONE, 'Found unlabelled area edge');
106079           var currLoc = startLoc;
106080           for (var it = this.iterator(); it.hasNext();) {
106081             var e = it.next();
106082             var label = e.getLabel();
106083             Assert.isTrue(label.isArea(geomIndex), 'Found non-area edge');
106084             var leftLoc = label.getLocation(geomIndex, Position.LEFT);
106085             var rightLoc = label.getLocation(geomIndex, Position.RIGHT);
106086             if (leftLoc === rightLoc) {
106087               return false
106088             }
106089             if (rightLoc !== currLoc) {
106090               return false
106091             }
106092             currLoc = leftLoc;
106093           }
106094           return true
106095         };
106096         EdgeEndStar.prototype.findIndex = function findIndex (eSearch) {
106097             var this$1 = this;
106098
106099           this.iterator();
106100           for (var i = 0; i < this._edgeList.size(); i++) {
106101             var e = this$1._edgeList.get(i);
106102             if (e === eSearch) { return i }
106103           }
106104           return -1
106105         };
106106         EdgeEndStar.prototype.iterator = function iterator () {
106107           return this.getEdges().iterator()
106108         };
106109         EdgeEndStar.prototype.getEdges = function getEdges () {
106110           if (this._edgeList === null) {
106111             this._edgeList = new ArrayList(this._edgeMap.values());
106112           }
106113           return this._edgeList
106114         };
106115         EdgeEndStar.prototype.getLocation = function getLocation (geomIndex, p, geom) {
106116           if (this._ptInAreaLocation[geomIndex] === Location.NONE) {
106117             this._ptInAreaLocation[geomIndex] = SimplePointInAreaLocator.locate(p, geom[geomIndex].getGeometry());
106118           }
106119           return this._ptInAreaLocation[geomIndex]
106120         };
106121         EdgeEndStar.prototype.toString = function toString () {
106122           var buf = new StringBuffer();
106123           buf.append('EdgeEndStar:   ' + this.getCoordinate());
106124           buf.append('\n');
106125           for (var it = this.iterator(); it.hasNext();) {
106126             var e = it.next();
106127             buf.append(e);
106128             buf.append('\n');
106129           }
106130           return buf.toString()
106131         };
106132         EdgeEndStar.prototype.computeEdgeEndLabels = function computeEdgeEndLabels (boundaryNodeRule) {
106133           for (var it = this.iterator(); it.hasNext();) {
106134             var ee = it.next();
106135             ee.computeLabel(boundaryNodeRule);
106136           }
106137         };
106138         EdgeEndStar.prototype.computeLabelling = function computeLabelling (geomGraph) {
106139             var this$1 = this;
106140
106141           this.computeEdgeEndLabels(geomGraph[0].getBoundaryNodeRule());
106142           this.propagateSideLabels(0);
106143           this.propagateSideLabels(1);
106144           var hasDimensionalCollapseEdge = [false, false];
106145           for (var it = this.iterator(); it.hasNext();) {
106146             var e = it.next();
106147             var label = e.getLabel();
106148             for (var geomi = 0; geomi < 2; geomi++) {
106149               if (label.isLine(geomi) && label.getLocation(geomi) === Location.BOUNDARY) { hasDimensionalCollapseEdge[geomi] = true; }
106150             }
106151           }
106152           for (var it$1 = this.iterator(); it$1.hasNext();) {
106153             var e$1 = it$1.next();
106154             var label$1 = e$1.getLabel();
106155             for (var geomi$1 = 0; geomi$1 < 2; geomi$1++) {
106156               if (label$1.isAnyNull(geomi$1)) {
106157                 var loc = Location.NONE;
106158                 if (hasDimensionalCollapseEdge[geomi$1]) {
106159                   loc = Location.EXTERIOR;
106160                 } else {
106161                   var p = e$1.getCoordinate();
106162                   loc = this$1.getLocation(geomi$1, p, geomGraph);
106163                 }
106164                 label$1.setAllLocationsIfNull(geomi$1, loc);
106165               }
106166             }
106167           }
106168         };
106169         EdgeEndStar.prototype.getDegree = function getDegree () {
106170           return this._edgeMap.size()
106171         };
106172         EdgeEndStar.prototype.insertEdgeEnd = function insertEdgeEnd (e, obj) {
106173           this._edgeMap.put(e, obj);
106174           this._edgeList = null;
106175         };
106176         EdgeEndStar.prototype.interfaces_ = function interfaces_ () {
106177           return []
106178         };
106179         EdgeEndStar.prototype.getClass = function getClass () {
106180           return EdgeEndStar
106181         };
106182
106183         var DirectedEdgeStar = (function (EdgeEndStar$$1) {
106184           function DirectedEdgeStar () {
106185             EdgeEndStar$$1.call(this);
106186             this._resultAreaEdgeList = null;
106187             this._label = null;
106188             this._SCANNING_FOR_INCOMING = 1;
106189             this._LINKING_TO_OUTGOING = 2;
106190           }
106191
106192           if ( EdgeEndStar$$1 ) { DirectedEdgeStar.__proto__ = EdgeEndStar$$1; }
106193           DirectedEdgeStar.prototype = Object.create( EdgeEndStar$$1 && EdgeEndStar$$1.prototype );
106194           DirectedEdgeStar.prototype.constructor = DirectedEdgeStar;
106195           DirectedEdgeStar.prototype.linkResultDirectedEdges = function linkResultDirectedEdges () {
106196             var this$1 = this;
106197
106198             this.getResultAreaEdges();
106199             var firstOut = null;
106200             var incoming = null;
106201             var state = this._SCANNING_FOR_INCOMING;
106202             for (var i = 0; i < this._resultAreaEdgeList.size(); i++) {
106203               var nextOut = this$1._resultAreaEdgeList.get(i);
106204               var nextIn = nextOut.getSym();
106205               if (!nextOut.getLabel().isArea()) { continue }
106206               if (firstOut === null && nextOut.isInResult()) { firstOut = nextOut; }
106207               switch (state) {
106208                 case this$1._SCANNING_FOR_INCOMING:
106209                   if (!nextIn.isInResult()) { continue }
106210                   incoming = nextIn;
106211                   state = this$1._LINKING_TO_OUTGOING;
106212                   break
106213                 case this$1._LINKING_TO_OUTGOING:
106214                   if (!nextOut.isInResult()) { continue }
106215                   incoming.setNext(nextOut);
106216                   state = this$1._SCANNING_FOR_INCOMING;
106217                   break
106218               }
106219             }
106220             if (state === this._LINKING_TO_OUTGOING) {
106221               if (firstOut === null) { throw new TopologyException('no outgoing dirEdge found', this.getCoordinate()) }
106222               Assert.isTrue(firstOut.isInResult(), 'unable to link last incoming dirEdge');
106223               incoming.setNext(firstOut);
106224             }
106225           };
106226           DirectedEdgeStar.prototype.insert = function insert (ee) {
106227             var de = ee;
106228             this.insertEdgeEnd(de, de);
106229           };
106230           DirectedEdgeStar.prototype.getRightmostEdge = function getRightmostEdge () {
106231             var edges = this.getEdges();
106232             var size = edges.size();
106233             if (size < 1) { return null }
106234             var de0 = edges.get(0);
106235             if (size === 1) { return de0 }
106236             var deLast = edges.get(size - 1);
106237             var quad0 = de0.getQuadrant();
106238             var quad1 = deLast.getQuadrant();
106239             if (Quadrant.isNorthern(quad0) && Quadrant.isNorthern(quad1)) { return de0; } else if (!Quadrant.isNorthern(quad0) && !Quadrant.isNorthern(quad1)) { return deLast; } else {
106240               // const nonHorizontalEdge = null
106241               if (de0.getDy() !== 0) { return de0; } else if (deLast.getDy() !== 0) { return deLast }
106242             }
106243             Assert.shouldNeverReachHere('found two horizontal edges incident on node');
106244             return null
106245           };
106246           DirectedEdgeStar.prototype.print = function print (out) {
106247             System.out.println('DirectedEdgeStar: ' + this.getCoordinate());
106248             for (var it = this.iterator(); it.hasNext();) {
106249               var de = it.next();
106250               out.print('out ');
106251               de.print(out);
106252               out.println();
106253               out.print('in ');
106254               de.getSym().print(out);
106255               out.println();
106256             }
106257           };
106258           DirectedEdgeStar.prototype.getResultAreaEdges = function getResultAreaEdges () {
106259             var this$1 = this;
106260
106261             if (this._resultAreaEdgeList !== null) { return this._resultAreaEdgeList }
106262             this._resultAreaEdgeList = new ArrayList();
106263             for (var it = this.iterator(); it.hasNext();) {
106264               var de = it.next();
106265               if (de.isInResult() || de.getSym().isInResult()) { this$1._resultAreaEdgeList.add(de); }
106266             }
106267             return this._resultAreaEdgeList
106268           };
106269           DirectedEdgeStar.prototype.updateLabelling = function updateLabelling (nodeLabel) {
106270             for (var it = this.iterator(); it.hasNext();) {
106271               var de = it.next();
106272               var label = de.getLabel();
106273               label.setAllLocationsIfNull(0, nodeLabel.getLocation(0));
106274               label.setAllLocationsIfNull(1, nodeLabel.getLocation(1));
106275             }
106276           };
106277           DirectedEdgeStar.prototype.linkAllDirectedEdges = function linkAllDirectedEdges () {
106278             var this$1 = this;
106279
106280             this.getEdges();
106281             var prevOut = null;
106282             var firstIn = null;
106283             for (var i = this._edgeList.size() - 1; i >= 0; i--) {
106284               var nextOut = this$1._edgeList.get(i);
106285               var nextIn = nextOut.getSym();
106286               if (firstIn === null) { firstIn = nextIn; }
106287               if (prevOut !== null) { nextIn.setNext(prevOut); }
106288               prevOut = nextOut;
106289             }
106290             firstIn.setNext(prevOut);
106291           };
106292           DirectedEdgeStar.prototype.computeDepths = function computeDepths () {
106293             var this$1 = this;
106294
106295             if (arguments.length === 1) {
106296               var de = arguments[0];
106297               var edgeIndex = this.findIndex(de);
106298               // const label = de.getLabel()
106299               var startDepth = de.getDepth(Position.LEFT);
106300               var targetLastDepth = de.getDepth(Position.RIGHT);
106301               var nextDepth = this.computeDepths(edgeIndex + 1, this._edgeList.size(), startDepth);
106302               var lastDepth = this.computeDepths(0, edgeIndex, nextDepth);
106303               if (lastDepth !== targetLastDepth) { throw new TopologyException('depth mismatch at ' + de.getCoordinate()) }
106304             } else if (arguments.length === 3) {
106305               var startIndex = arguments[0];
106306               var endIndex = arguments[1];
106307               var startDepth$1 = arguments[2];
106308               var currDepth = startDepth$1;
106309               for (var i = startIndex; i < endIndex; i++) {
106310                 var nextDe = this$1._edgeList.get(i);
106311                 // const label = nextDe.getLabel()
106312                 nextDe.setEdgeDepths(Position.RIGHT, currDepth);
106313                 currDepth = nextDe.getDepth(Position.LEFT);
106314               }
106315               return currDepth
106316             }
106317           };
106318           DirectedEdgeStar.prototype.mergeSymLabels = function mergeSymLabels () {
106319             for (var it = this.iterator(); it.hasNext();) {
106320               var de = it.next();
106321               var label = de.getLabel();
106322               label.merge(de.getSym().getLabel());
106323             }
106324           };
106325           DirectedEdgeStar.prototype.linkMinimalDirectedEdges = function linkMinimalDirectedEdges (er) {
106326             var this$1 = this;
106327
106328             var firstOut = null;
106329             var incoming = null;
106330             var state = this._SCANNING_FOR_INCOMING;
106331             for (var i = this._resultAreaEdgeList.size() - 1; i >= 0; i--) {
106332               var nextOut = this$1._resultAreaEdgeList.get(i);
106333               var nextIn = nextOut.getSym();
106334               if (firstOut === null && nextOut.getEdgeRing() === er) { firstOut = nextOut; }
106335               switch (state) {
106336                 case this$1._SCANNING_FOR_INCOMING:
106337                   if (nextIn.getEdgeRing() !== er) { continue }
106338                   incoming = nextIn;
106339                   state = this$1._LINKING_TO_OUTGOING;
106340                   break
106341                 case this$1._LINKING_TO_OUTGOING:
106342                   if (nextOut.getEdgeRing() !== er) { continue }
106343                   incoming.setNextMin(nextOut);
106344                   state = this$1._SCANNING_FOR_INCOMING;
106345                   break
106346               }
106347             }
106348             if (state === this._LINKING_TO_OUTGOING) {
106349               Assert.isTrue(firstOut !== null, 'found null for first outgoing dirEdge');
106350               Assert.isTrue(firstOut.getEdgeRing() === er, 'unable to link last incoming dirEdge');
106351               incoming.setNextMin(firstOut);
106352             }
106353           };
106354           DirectedEdgeStar.prototype.getOutgoingDegree = function getOutgoingDegree () {
106355             if (arguments.length === 0) {
106356               var degree = 0;
106357               for (var it = this.iterator(); it.hasNext();) {
106358                 var de = it.next();
106359                 if (de.isInResult()) { degree++; }
106360               }
106361               return degree
106362             } else if (arguments.length === 1) {
106363               var er = arguments[0];
106364               var degree$1 = 0;
106365               for (var it$1 = this.iterator(); it$1.hasNext();) {
106366                 var de$1 = it$1.next();
106367                 if (de$1.getEdgeRing() === er) { degree$1++; }
106368               }
106369               return degree$1
106370             }
106371           };
106372           DirectedEdgeStar.prototype.getLabel = function getLabel () {
106373             return this._label
106374           };
106375           DirectedEdgeStar.prototype.findCoveredLineEdges = function findCoveredLineEdges () {
106376             var startLoc = Location.NONE;
106377             for (var it = this.iterator(); it.hasNext();) {
106378               var nextOut = it.next();
106379               var nextIn = nextOut.getSym();
106380               if (!nextOut.isLineEdge()) {
106381                 if (nextOut.isInResult()) {
106382                   startLoc = Location.INTERIOR;
106383                   break
106384                 }
106385                 if (nextIn.isInResult()) {
106386                   startLoc = Location.EXTERIOR;
106387                   break
106388                 }
106389               }
106390             }
106391             if (startLoc === Location.NONE) { return null }
106392             var currLoc = startLoc;
106393             for (var it$1 = this.iterator(); it$1.hasNext();) {
106394               var nextOut$1 = it$1.next();
106395               var nextIn$1 = nextOut$1.getSym();
106396               if (nextOut$1.isLineEdge()) {
106397                 nextOut$1.getEdge().setCovered(currLoc === Location.INTERIOR);
106398               } else {
106399                 if (nextOut$1.isInResult()) { currLoc = Location.EXTERIOR; }
106400                 if (nextIn$1.isInResult()) { currLoc = Location.INTERIOR; }
106401               }
106402             }
106403           };
106404           DirectedEdgeStar.prototype.computeLabelling = function computeLabelling (geom) {
106405             var this$1 = this;
106406
106407             EdgeEndStar$$1.prototype.computeLabelling.call(this, geom);
106408             this._label = new Label(Location.NONE);
106409             for (var it = this.iterator(); it.hasNext();) {
106410               var ee = it.next();
106411               var e = ee.getEdge();
106412               var eLabel = e.getLabel();
106413               for (var i = 0; i < 2; i++) {
106414                 var eLoc = eLabel.getLocation(i);
106415                 if (eLoc === Location.INTERIOR || eLoc === Location.BOUNDARY) { this$1._label.setLocation(i, Location.INTERIOR); }
106416               }
106417             }
106418           };
106419           DirectedEdgeStar.prototype.interfaces_ = function interfaces_ () {
106420             return []
106421           };
106422           DirectedEdgeStar.prototype.getClass = function getClass () {
106423             return DirectedEdgeStar
106424           };
106425
106426           return DirectedEdgeStar;
106427         }(EdgeEndStar));
106428
106429         var OverlayNodeFactory = (function (NodeFactory$$1) {
106430           function OverlayNodeFactory () {
106431             NodeFactory$$1.apply(this, arguments);
106432           }
106433
106434           if ( NodeFactory$$1 ) { OverlayNodeFactory.__proto__ = NodeFactory$$1; }
106435           OverlayNodeFactory.prototype = Object.create( NodeFactory$$1 && NodeFactory$$1.prototype );
106436           OverlayNodeFactory.prototype.constructor = OverlayNodeFactory;
106437
106438           OverlayNodeFactory.prototype.createNode = function createNode (coord) {
106439             return new Node$1(coord, new DirectedEdgeStar())
106440           };
106441           OverlayNodeFactory.prototype.interfaces_ = function interfaces_ () {
106442             return []
106443           };
106444           OverlayNodeFactory.prototype.getClass = function getClass () {
106445             return OverlayNodeFactory
106446           };
106447
106448           return OverlayNodeFactory;
106449         }(NodeFactory));
106450
106451         var OrientedCoordinateArray = function OrientedCoordinateArray () {
106452           this._pts = null;
106453           this._orientation = null;
106454           var pts = arguments[0];
106455           this._pts = pts;
106456           this._orientation = OrientedCoordinateArray.orientation(pts);
106457         };
106458         OrientedCoordinateArray.prototype.compareTo = function compareTo (o1) {
106459           var oca = o1;
106460           var comp = OrientedCoordinateArray.compareOriented(this._pts, this._orientation, oca._pts, oca._orientation);
106461           return comp
106462         };
106463         OrientedCoordinateArray.prototype.interfaces_ = function interfaces_ () {
106464           return [Comparable]
106465         };
106466         OrientedCoordinateArray.prototype.getClass = function getClass () {
106467           return OrientedCoordinateArray
106468         };
106469         OrientedCoordinateArray.orientation = function orientation (pts) {
106470           return CoordinateArrays.increasingDirection(pts) === 1
106471         };
106472         OrientedCoordinateArray.compareOriented = function compareOriented (pts1, orientation1, pts2, orientation2) {
106473           var dir1 = orientation1 ? 1 : -1;
106474           var dir2 = orientation2 ? 1 : -1;
106475           var limit1 = orientation1 ? pts1.length : -1;
106476           var limit2 = orientation2 ? pts2.length : -1;
106477           var i1 = orientation1 ? 0 : pts1.length - 1;
106478           var i2 = orientation2 ? 0 : pts2.length - 1;
106479           // const comp = 0
106480           while (true) {
106481             var compPt = pts1[i1].compareTo(pts2[i2]);
106482             if (compPt !== 0) { return compPt }
106483             i1 += dir1;
106484             i2 += dir2;
106485             var done1 = i1 === limit1;
106486             var done2 = i2 === limit2;
106487             if (done1 && !done2) { return -1 }
106488             if (!done1 && done2) { return 1 }
106489             if (done1 && done2) { return 0 }
106490           }
106491         };
106492
106493         var EdgeList = function EdgeList () {
106494           this._edges = new ArrayList();
106495           this._ocaMap = new TreeMap();
106496         };
106497         EdgeList.prototype.print = function print (out) {
106498             var this$1 = this;
106499
106500           out.print('MULTILINESTRING ( ');
106501           for (var j = 0; j < this._edges.size(); j++) {
106502             var e = this$1._edges.get(j);
106503             if (j > 0) { out.print(','); }
106504             out.print('(');
106505             var pts = e.getCoordinates();
106506             for (var i = 0; i < pts.length; i++) {
106507               if (i > 0) { out.print(','); }
106508               out.print(pts[i].x + ' ' + pts[i].y);
106509             }
106510             out.println(')');
106511           }
106512           out.print(')  ');
106513         };
106514         EdgeList.prototype.addAll = function addAll (edgeColl) {
106515             var this$1 = this;
106516
106517           for (var i = edgeColl.iterator(); i.hasNext();) {
106518             this$1.add(i.next());
106519           }
106520         };
106521         EdgeList.prototype.findEdgeIndex = function findEdgeIndex (e) {
106522             var this$1 = this;
106523
106524           for (var i = 0; i < this._edges.size(); i++) {
106525             if (this$1._edges.get(i).equals(e)) { return i }
106526           }
106527           return -1
106528         };
106529         EdgeList.prototype.iterator = function iterator () {
106530           return this._edges.iterator()
106531         };
106532         EdgeList.prototype.getEdges = function getEdges () {
106533           return this._edges
106534         };
106535         EdgeList.prototype.get = function get (i) {
106536           return this._edges.get(i)
106537         };
106538         EdgeList.prototype.findEqualEdge = function findEqualEdge (e) {
106539           var oca = new OrientedCoordinateArray(e.getCoordinates());
106540           var matchEdge = this._ocaMap.get(oca);
106541           return matchEdge
106542         };
106543         EdgeList.prototype.add = function add (e) {
106544           this._edges.add(e);
106545           var oca = new OrientedCoordinateArray(e.getCoordinates());
106546           this._ocaMap.put(oca, e);
106547         };
106548         EdgeList.prototype.interfaces_ = function interfaces_ () {
106549           return []
106550         };
106551         EdgeList.prototype.getClass = function getClass () {
106552           return EdgeList
106553         };
106554
106555         var SegmentIntersector = function SegmentIntersector () {};
106556
106557         SegmentIntersector.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {};
106558         SegmentIntersector.prototype.isDone = function isDone () {};
106559         SegmentIntersector.prototype.interfaces_ = function interfaces_ () {
106560           return []
106561         };
106562         SegmentIntersector.prototype.getClass = function getClass () {
106563           return SegmentIntersector
106564         };
106565
106566         var IntersectionAdder = function IntersectionAdder () {
106567           this._hasIntersection = false;
106568           this._hasProper = false;
106569           this._hasProperInterior = false;
106570           this._hasInterior = false;
106571           this._properIntersectionPoint = null;
106572           this._li = null;
106573           this._isSelfIntersection = null;
106574           this.numIntersections = 0;
106575           this.numInteriorIntersections = 0;
106576           this.numProperIntersections = 0;
106577           this.numTests = 0;
106578           var li = arguments[0];
106579           this._li = li;
106580         };
106581         IntersectionAdder.prototype.isTrivialIntersection = function isTrivialIntersection (e0, segIndex0, e1, segIndex1) {
106582           if (e0 === e1) {
106583             if (this._li.getIntersectionNum() === 1) {
106584               if (IntersectionAdder.isAdjacentSegments(segIndex0, segIndex1)) { return true }
106585               if (e0.isClosed()) {
106586                 var maxSegIndex = e0.size() - 1;
106587                 if ((segIndex0 === 0 && segIndex1 === maxSegIndex) ||
106588                     (segIndex1 === 0 && segIndex0 === maxSegIndex)) {
106589                   return true
106590                 }
106591               }
106592             }
106593           }
106594           return false
106595         };
106596         IntersectionAdder.prototype.getProperIntersectionPoint = function getProperIntersectionPoint () {
106597           return this._properIntersectionPoint
106598         };
106599         IntersectionAdder.prototype.hasProperInteriorIntersection = function hasProperInteriorIntersection () {
106600           return this._hasProperInterior
106601         };
106602         IntersectionAdder.prototype.getLineIntersector = function getLineIntersector () {
106603           return this._li
106604         };
106605         IntersectionAdder.prototype.hasProperIntersection = function hasProperIntersection () {
106606           return this._hasProper
106607         };
106608         IntersectionAdder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {
106609           if (e0 === e1 && segIndex0 === segIndex1) { return null }
106610           this.numTests++;
106611           var p00 = e0.getCoordinates()[segIndex0];
106612           var p01 = e0.getCoordinates()[segIndex0 + 1];
106613           var p10 = e1.getCoordinates()[segIndex1];
106614           var p11 = e1.getCoordinates()[segIndex1 + 1];
106615           this._li.computeIntersection(p00, p01, p10, p11);
106616           if (this._li.hasIntersection()) {
106617             this.numIntersections++;
106618             if (this._li.isInteriorIntersection()) {
106619               this.numInteriorIntersections++;
106620               this._hasInterior = true;
106621             }
106622             if (!this.isTrivialIntersection(e0, segIndex0, e1, segIndex1)) {
106623               this._hasIntersection = true;
106624               e0.addIntersections(this._li, segIndex0, 0);
106625               e1.addIntersections(this._li, segIndex1, 1);
106626               if (this._li.isProper()) {
106627                 this.numProperIntersections++;
106628                 this._hasProper = true;
106629                 this._hasProperInterior = true;
106630               }
106631             }
106632           }
106633         };
106634         IntersectionAdder.prototype.hasIntersection = function hasIntersection () {
106635           return this._hasIntersection
106636         };
106637         IntersectionAdder.prototype.isDone = function isDone () {
106638           return false
106639         };
106640         IntersectionAdder.prototype.hasInteriorIntersection = function hasInteriorIntersection () {
106641           return this._hasInterior
106642         };
106643         IntersectionAdder.prototype.interfaces_ = function interfaces_ () {
106644           return [SegmentIntersector]
106645         };
106646         IntersectionAdder.prototype.getClass = function getClass () {
106647           return IntersectionAdder
106648         };
106649         IntersectionAdder.isAdjacentSegments = function isAdjacentSegments (i1, i2) {
106650           return Math.abs(i1 - i2) === 1
106651         };
106652
106653         var EdgeIntersection = function EdgeIntersection () {
106654           this.coord = null;
106655           this.segmentIndex = null;
106656           this.dist = null;
106657           var coord = arguments[0];
106658           var segmentIndex = arguments[1];
106659           var dist = arguments[2];
106660           this.coord = new Coordinate(coord);
106661           this.segmentIndex = segmentIndex;
106662           this.dist = dist;
106663         };
106664         EdgeIntersection.prototype.getSegmentIndex = function getSegmentIndex () {
106665           return this.segmentIndex
106666         };
106667         EdgeIntersection.prototype.getCoordinate = function getCoordinate () {
106668           return this.coord
106669         };
106670         EdgeIntersection.prototype.print = function print (out) {
106671           out.print(this.coord);
106672           out.print(' seg # = ' + this.segmentIndex);
106673           out.println(' dist = ' + this.dist);
106674         };
106675         EdgeIntersection.prototype.compareTo = function compareTo (obj) {
106676           var other = obj;
106677           return this.compare(other.segmentIndex, other.dist)
106678         };
106679         EdgeIntersection.prototype.isEndPoint = function isEndPoint (maxSegmentIndex) {
106680           if (this.segmentIndex === 0 && this.dist === 0.0) { return true }
106681           if (this.segmentIndex === maxSegmentIndex) { return true }
106682           return false
106683         };
106684         EdgeIntersection.prototype.toString = function toString () {
106685           return this.coord + ' seg # = ' + this.segmentIndex + ' dist = ' + this.dist
106686         };
106687         EdgeIntersection.prototype.getDistance = function getDistance () {
106688           return this.dist
106689         };
106690         EdgeIntersection.prototype.compare = function compare (segmentIndex, dist) {
106691           if (this.segmentIndex < segmentIndex) { return -1 }
106692           if (this.segmentIndex > segmentIndex) { return 1 }
106693           if (this.dist < dist) { return -1 }
106694           if (this.dist > dist) { return 1 }
106695           return 0
106696         };
106697         EdgeIntersection.prototype.interfaces_ = function interfaces_ () {
106698           return [Comparable]
106699         };
106700         EdgeIntersection.prototype.getClass = function getClass () {
106701           return EdgeIntersection
106702         };
106703
106704         var EdgeIntersectionList = function EdgeIntersectionList () {
106705           this._nodeMap = new TreeMap();
106706           this.edge = null;
106707           var edge = arguments[0];
106708           this.edge = edge;
106709         };
106710         EdgeIntersectionList.prototype.print = function print (out) {
106711           out.println('Intersections:');
106712           for (var it = this.iterator(); it.hasNext();) {
106713             var ei = it.next();
106714             ei.print(out);
106715           }
106716         };
106717         EdgeIntersectionList.prototype.iterator = function iterator () {
106718           return this._nodeMap.values().iterator()
106719         };
106720         EdgeIntersectionList.prototype.addSplitEdges = function addSplitEdges (edgeList) {
106721             var this$1 = this;
106722
106723           this.addEndpoints();
106724           var it = this.iterator();
106725           var eiPrev = it.next();
106726           while (it.hasNext()) {
106727             var ei = it.next();
106728             var newEdge = this$1.createSplitEdge(eiPrev, ei);
106729             edgeList.add(newEdge);
106730             eiPrev = ei;
106731           }
106732         };
106733         EdgeIntersectionList.prototype.addEndpoints = function addEndpoints () {
106734           var maxSegIndex = this.edge.pts.length - 1;
106735           this.add(this.edge.pts[0], 0, 0.0);
106736           this.add(this.edge.pts[maxSegIndex], maxSegIndex, 0.0);
106737         };
106738         EdgeIntersectionList.prototype.createSplitEdge = function createSplitEdge (ei0, ei1) {
106739             var this$1 = this;
106740
106741           var npts = ei1.segmentIndex - ei0.segmentIndex + 2;
106742           var lastSegStartPt = this.edge.pts[ei1.segmentIndex];
106743           var useIntPt1 = ei1.dist > 0.0 || !ei1.coord.equals2D(lastSegStartPt);
106744           if (!useIntPt1) {
106745             npts--;
106746           }
106747           var pts = new Array(npts).fill(null);
106748           var ipt = 0;
106749           pts[ipt++] = new Coordinate(ei0.coord);
106750           for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
106751             pts[ipt++] = this$1.edge.pts[i];
106752           }
106753           if (useIntPt1) { pts[ipt] = ei1.coord; }
106754           return new Edge(pts, new Label(this.edge._label))
106755         };
106756         EdgeIntersectionList.prototype.add = function add (intPt, segmentIndex, dist) {
106757           var eiNew = new EdgeIntersection(intPt, segmentIndex, dist);
106758           var ei = this._nodeMap.get(eiNew);
106759           if (ei !== null) {
106760             return ei
106761           }
106762           this._nodeMap.put(eiNew, eiNew);
106763           return eiNew
106764         };
106765         EdgeIntersectionList.prototype.isIntersection = function isIntersection (pt) {
106766           for (var it = this.iterator(); it.hasNext();) {
106767             var ei = it.next();
106768             if (ei.coord.equals(pt)) { return true }
106769           }
106770           return false
106771         };
106772         EdgeIntersectionList.prototype.interfaces_ = function interfaces_ () {
106773           return []
106774         };
106775         EdgeIntersectionList.prototype.getClass = function getClass () {
106776           return EdgeIntersectionList
106777         };
106778
106779         var MonotoneChainIndexer = function MonotoneChainIndexer () {};
106780
106781         MonotoneChainIndexer.prototype.getChainStartIndices = function getChainStartIndices (pts) {
106782             var this$1 = this;
106783
106784           var start = 0;
106785           var startIndexList = new ArrayList();
106786           startIndexList.add(new Integer(start));
106787           do {
106788             var last = this$1.findChainEnd(pts, start);
106789             startIndexList.add(new Integer(last));
106790             start = last;
106791           } while (start < pts.length - 1)
106792           var startIndex = MonotoneChainIndexer.toIntArray(startIndexList);
106793           return startIndex
106794         };
106795         MonotoneChainIndexer.prototype.findChainEnd = function findChainEnd (pts, start) {
106796           var chainQuad = Quadrant.quadrant(pts[start], pts[start + 1]);
106797           var last = start + 1;
106798           while (last < pts.length) {
106799             var quad = Quadrant.quadrant(pts[last - 1], pts[last]);
106800             if (quad !== chainQuad) { break }
106801             last++;
106802           }
106803           return last - 1
106804         };
106805         MonotoneChainIndexer.prototype.interfaces_ = function interfaces_ () {
106806           return []
106807         };
106808         MonotoneChainIndexer.prototype.getClass = function getClass () {
106809           return MonotoneChainIndexer
106810         };
106811         MonotoneChainIndexer.toIntArray = function toIntArray (list) {
106812           var array = new Array(list.size()).fill(null);
106813           for (var i = 0; i < array.length; i++) {
106814             array[i] = list.get(i).intValue();
106815           }
106816           return array
106817         };
106818
106819         var MonotoneChainEdge = function MonotoneChainEdge () {
106820           this.e = null;
106821           this.pts = null;
106822           this.startIndex = null;
106823           this.env1 = new Envelope();
106824           this.env2 = new Envelope();
106825           var e = arguments[0];
106826           this.e = e;
106827           this.pts = e.getCoordinates();
106828           var mcb = new MonotoneChainIndexer();
106829           this.startIndex = mcb.getChainStartIndices(this.pts);
106830         };
106831         MonotoneChainEdge.prototype.getCoordinates = function getCoordinates () {
106832           return this.pts
106833         };
106834         MonotoneChainEdge.prototype.getMaxX = function getMaxX (chainIndex) {
106835           var x1 = this.pts[this.startIndex[chainIndex]].x;
106836           var x2 = this.pts[this.startIndex[chainIndex + 1]].x;
106837           return x1 > x2 ? x1 : x2
106838         };
106839         MonotoneChainEdge.prototype.getMinX = function getMinX (chainIndex) {
106840           var x1 = this.pts[this.startIndex[chainIndex]].x;
106841           var x2 = this.pts[this.startIndex[chainIndex + 1]].x;
106842           return x1 < x2 ? x1 : x2
106843         };
106844         MonotoneChainEdge.prototype.computeIntersectsForChain = function computeIntersectsForChain () {
106845           if (arguments.length === 4) {
106846             var chainIndex0 = arguments[0];
106847             var mce = arguments[1];
106848             var chainIndex1 = arguments[2];
106849             var si = arguments[3];
106850             this.computeIntersectsForChain(this.startIndex[chainIndex0], this.startIndex[chainIndex0 + 1], mce, mce.startIndex[chainIndex1], mce.startIndex[chainIndex1 + 1], si);
106851           } else if (arguments.length === 6) {
106852             var start0 = arguments[0];
106853             var end0 = arguments[1];
106854             var mce$1 = arguments[2];
106855             var start1 = arguments[3];
106856             var end1 = arguments[4];
106857             var ei = arguments[5];
106858             var p00 = this.pts[start0];
106859             var p01 = this.pts[end0];
106860             var p10 = mce$1.pts[start1];
106861             var p11 = mce$1.pts[end1];
106862             if (end0 - start0 === 1 && end1 - start1 === 1) {
106863               ei.addIntersections(this.e, start0, mce$1.e, start1);
106864               return null
106865             }
106866             this.env1.init(p00, p01);
106867             this.env2.init(p10, p11);
106868             if (!this.env1.intersects(this.env2)) { return null }
106869             var mid0 = Math.trunc((start0 + end0) / 2);
106870             var mid1 = Math.trunc((start1 + end1) / 2);
106871             if (start0 < mid0) {
106872               if (start1 < mid1) { this.computeIntersectsForChain(start0, mid0, mce$1, start1, mid1, ei); }
106873               if (mid1 < end1) { this.computeIntersectsForChain(start0, mid0, mce$1, mid1, end1, ei); }
106874             }
106875             if (mid0 < end0) {
106876               if (start1 < mid1) { this.computeIntersectsForChain(mid0, end0, mce$1, start1, mid1, ei); }
106877               if (mid1 < end1) { this.computeIntersectsForChain(mid0, end0, mce$1, mid1, end1, ei); }
106878             }
106879           }
106880         };
106881         MonotoneChainEdge.prototype.getStartIndexes = function getStartIndexes () {
106882           return this.startIndex
106883         };
106884         MonotoneChainEdge.prototype.computeIntersects = function computeIntersects (mce, si) {
106885             var this$1 = this;
106886
106887           for (var i = 0; i < this.startIndex.length - 1; i++) {
106888             for (var j = 0; j < mce.startIndex.length - 1; j++) {
106889               this$1.computeIntersectsForChain(i, mce, j, si);
106890             }
106891           }
106892         };
106893         MonotoneChainEdge.prototype.interfaces_ = function interfaces_ () {
106894           return []
106895         };
106896         MonotoneChainEdge.prototype.getClass = function getClass () {
106897           return MonotoneChainEdge
106898         };
106899
106900         var Depth = function Depth () {
106901           var this$1 = this;
106902
106903           this._depth = Array(2).fill().map(function () { return Array(3); });
106904           for (var i = 0; i < 2; i++) {
106905             for (var j = 0; j < 3; j++) {
106906               this$1._depth[i][j] = Depth.NULL_VALUE;
106907             }
106908           }
106909         };
106910
106911         var staticAccessors$31 = { NULL_VALUE: { configurable: true } };
106912         Depth.prototype.getDepth = function getDepth (geomIndex, posIndex) {
106913           return this._depth[geomIndex][posIndex]
106914         };
106915         Depth.prototype.setDepth = function setDepth (geomIndex, posIndex, depthValue) {
106916           this._depth[geomIndex][posIndex] = depthValue;
106917         };
106918         Depth.prototype.isNull = function isNull () {
106919             var this$1 = this;
106920
106921           if (arguments.length === 0) {
106922             for (var i = 0; i < 2; i++) {
106923               for (var j = 0; j < 3; j++) {
106924                 if (this$1._depth[i][j] !== Depth.NULL_VALUE) { return false }
106925               }
106926             }
106927             return true
106928           } else if (arguments.length === 1) {
106929             var geomIndex = arguments[0];
106930             return this._depth[geomIndex][1] === Depth.NULL_VALUE
106931           } else if (arguments.length === 2) {
106932             var geomIndex$1 = arguments[0];
106933             var posIndex = arguments[1];
106934             return this._depth[geomIndex$1][posIndex] === Depth.NULL_VALUE
106935           }
106936         };
106937         Depth.prototype.normalize = function normalize () {
106938             var this$1 = this;
106939
106940           for (var i = 0; i < 2; i++) {
106941             if (!this$1.isNull(i)) {
106942               var minDepth = this$1._depth[i][1];
106943               if (this$1._depth[i][2] < minDepth) { minDepth = this$1._depth[i][2]; }
106944               if (minDepth < 0) { minDepth = 0; }
106945               for (var j = 1; j < 3; j++) {
106946                 var newValue = 0;
106947                 if (this$1._depth[i][j] > minDepth) { newValue = 1; }
106948                 this$1._depth[i][j] = newValue;
106949               }
106950             }
106951           }
106952         };
106953         Depth.prototype.getDelta = function getDelta (geomIndex) {
106954           return this._depth[geomIndex][Position.RIGHT] - this._depth[geomIndex][Position.LEFT]
106955         };
106956         Depth.prototype.getLocation = function getLocation (geomIndex, posIndex) {
106957           if (this._depth[geomIndex][posIndex] <= 0) { return Location.EXTERIOR }
106958           return Location.INTERIOR
106959         };
106960         Depth.prototype.toString = function toString () {
106961           return 'A: ' + this._depth[0][1] + ',' + this._depth[0][2] + ' B: ' + this._depth[1][1] + ',' + this._depth[1][2]
106962         };
106963         Depth.prototype.add = function add () {
106964             var this$1 = this;
106965
106966           if (arguments.length === 1) {
106967             var lbl = arguments[0];
106968             for (var i = 0; i < 2; i++) {
106969               for (var j = 1; j < 3; j++) {
106970                 var loc = lbl.getLocation(i, j);
106971                 if (loc === Location.EXTERIOR || loc === Location.INTERIOR) {
106972                   if (this$1.isNull(i, j)) {
106973                     this$1._depth[i][j] = Depth.depthAtLocation(loc);
106974                   } else { this$1._depth[i][j] += Depth.depthAtLocation(loc); }
106975                 }
106976               }
106977             }
106978           } else if (arguments.length === 3) {
106979             var geomIndex = arguments[0];
106980             var posIndex = arguments[1];
106981             var location = arguments[2];
106982             if (location === Location.INTERIOR) { this._depth[geomIndex][posIndex]++; }
106983           }
106984         };
106985         Depth.prototype.interfaces_ = function interfaces_ () {
106986           return []
106987         };
106988         Depth.prototype.getClass = function getClass () {
106989           return Depth
106990         };
106991         Depth.depthAtLocation = function depthAtLocation (location) {
106992           if (location === Location.EXTERIOR) { return 0 }
106993           if (location === Location.INTERIOR) { return 1 }
106994           return Depth.NULL_VALUE
106995         };
106996         staticAccessors$31.NULL_VALUE.get = function () { return -1 };
106997
106998         Object.defineProperties( Depth, staticAccessors$31 );
106999
107000         var Edge = (function (GraphComponent$$1) {
107001           function Edge () {
107002             GraphComponent$$1.call(this);
107003             this.pts = null;
107004             this._env = null;
107005             this.eiList = new EdgeIntersectionList(this);
107006             this._name = null;
107007             this._mce = null;
107008             this._isIsolated = true;
107009             this._depth = new Depth();
107010             this._depthDelta = 0;
107011             if (arguments.length === 1) {
107012               var pts = arguments[0];
107013               Edge.call(this, pts, null);
107014             } else if (arguments.length === 2) {
107015               var pts$1 = arguments[0];
107016               var label = arguments[1];
107017               this.pts = pts$1;
107018               this._label = label;
107019             }
107020           }
107021
107022           if ( GraphComponent$$1 ) { Edge.__proto__ = GraphComponent$$1; }
107023           Edge.prototype = Object.create( GraphComponent$$1 && GraphComponent$$1.prototype );
107024           Edge.prototype.constructor = Edge;
107025           Edge.prototype.getDepth = function getDepth () {
107026             return this._depth
107027           };
107028           Edge.prototype.getCollapsedEdge = function getCollapsedEdge () {
107029             var newPts = new Array(2).fill(null);
107030             newPts[0] = this.pts[0];
107031             newPts[1] = this.pts[1];
107032             var newe = new Edge(newPts, Label.toLineLabel(this._label));
107033             return newe
107034           };
107035           Edge.prototype.isIsolated = function isIsolated () {
107036             return this._isIsolated
107037           };
107038           Edge.prototype.getCoordinates = function getCoordinates () {
107039             return this.pts
107040           };
107041           Edge.prototype.setIsolated = function setIsolated (isIsolated) {
107042             this._isIsolated = isIsolated;
107043           };
107044           Edge.prototype.setName = function setName (name) {
107045             this._name = name;
107046           };
107047           Edge.prototype.equals = function equals (o) {
107048             var this$1 = this;
107049
107050             if (!(o instanceof Edge)) { return false }
107051             var e = o;
107052             if (this.pts.length !== e.pts.length) { return false }
107053             var isEqualForward = true;
107054             var isEqualReverse = true;
107055             var iRev = this.pts.length;
107056             for (var i = 0; i < this.pts.length; i++) {
107057               if (!this$1.pts[i].equals2D(e.pts[i])) {
107058                 isEqualForward = false;
107059               }
107060               if (!this$1.pts[i].equals2D(e.pts[--iRev])) {
107061                 isEqualReverse = false;
107062               }
107063               if (!isEqualForward && !isEqualReverse) { return false }
107064             }
107065             return true
107066           };
107067           Edge.prototype.getCoordinate = function getCoordinate () {
107068             if (arguments.length === 0) {
107069               if (this.pts.length > 0) { return this.pts[0] }
107070               return null
107071             } else if (arguments.length === 1) {
107072               var i = arguments[0];
107073               return this.pts[i]
107074             }
107075           };
107076           Edge.prototype.print = function print (out) {
107077             var this$1 = this;
107078
107079             out.print('edge ' + this._name + ': ');
107080             out.print('LINESTRING (');
107081             for (var i = 0; i < this.pts.length; i++) {
107082               if (i > 0) { out.print(','); }
107083               out.print(this$1.pts[i].x + ' ' + this$1.pts[i].y);
107084             }
107085             out.print(')  ' + this._label + ' ' + this._depthDelta);
107086           };
107087           Edge.prototype.computeIM = function computeIM (im) {
107088             Edge.updateIM(this._label, im);
107089           };
107090           Edge.prototype.isCollapsed = function isCollapsed () {
107091             if (!this._label.isArea()) { return false }
107092             if (this.pts.length !== 3) { return false }
107093             if (this.pts[0].equals(this.pts[2])) { return true }
107094             return false
107095           };
107096           Edge.prototype.isClosed = function isClosed () {
107097             return this.pts[0].equals(this.pts[this.pts.length - 1])
107098           };
107099           Edge.prototype.getMaximumSegmentIndex = function getMaximumSegmentIndex () {
107100             return this.pts.length - 1
107101           };
107102           Edge.prototype.getDepthDelta = function getDepthDelta () {
107103             return this._depthDelta
107104           };
107105           Edge.prototype.getNumPoints = function getNumPoints () {
107106             return this.pts.length
107107           };
107108           Edge.prototype.printReverse = function printReverse (out) {
107109             var this$1 = this;
107110
107111             out.print('edge ' + this._name + ': ');
107112             for (var i = this.pts.length - 1; i >= 0; i--) {
107113               out.print(this$1.pts[i] + ' ');
107114             }
107115             out.println('');
107116           };
107117           Edge.prototype.getMonotoneChainEdge = function getMonotoneChainEdge () {
107118             if (this._mce === null) { this._mce = new MonotoneChainEdge(this); }
107119             return this._mce
107120           };
107121           Edge.prototype.getEnvelope = function getEnvelope () {
107122             var this$1 = this;
107123
107124             if (this._env === null) {
107125               this._env = new Envelope();
107126               for (var i = 0; i < this.pts.length; i++) {
107127                 this$1._env.expandToInclude(this$1.pts[i]);
107128               }
107129             }
107130             return this._env
107131           };
107132           Edge.prototype.addIntersection = function addIntersection (li, segmentIndex, geomIndex, intIndex) {
107133             var intPt = new Coordinate(li.getIntersection(intIndex));
107134             var normalizedSegmentIndex = segmentIndex;
107135             var dist = li.getEdgeDistance(geomIndex, intIndex);
107136             var nextSegIndex = normalizedSegmentIndex + 1;
107137             if (nextSegIndex < this.pts.length) {
107138               var nextPt = this.pts[nextSegIndex];
107139               if (intPt.equals2D(nextPt)) {
107140                 normalizedSegmentIndex = nextSegIndex;
107141                 dist = 0.0;
107142               }
107143             }
107144             this.eiList.add(intPt, normalizedSegmentIndex, dist);
107145           };
107146           Edge.prototype.toString = function toString () {
107147             var this$1 = this;
107148
107149             var buf = new StringBuffer();
107150             buf.append('edge ' + this._name + ': ');
107151             buf.append('LINESTRING (');
107152             for (var i = 0; i < this.pts.length; i++) {
107153               if (i > 0) { buf.append(','); }
107154               buf.append(this$1.pts[i].x + ' ' + this$1.pts[i].y);
107155             }
107156             buf.append(')  ' + this._label + ' ' + this._depthDelta);
107157             return buf.toString()
107158           };
107159           Edge.prototype.isPointwiseEqual = function isPointwiseEqual (e) {
107160             var this$1 = this;
107161
107162             if (this.pts.length !== e.pts.length) { return false }
107163             for (var i = 0; i < this.pts.length; i++) {
107164               if (!this$1.pts[i].equals2D(e.pts[i])) {
107165                 return false
107166               }
107167             }
107168             return true
107169           };
107170           Edge.prototype.setDepthDelta = function setDepthDelta (depthDelta) {
107171             this._depthDelta = depthDelta;
107172           };
107173           Edge.prototype.getEdgeIntersectionList = function getEdgeIntersectionList () {
107174             return this.eiList
107175           };
107176           Edge.prototype.addIntersections = function addIntersections (li, segmentIndex, geomIndex) {
107177             var this$1 = this;
107178
107179             for (var i = 0; i < li.getIntersectionNum(); i++) {
107180               this$1.addIntersection(li, segmentIndex, geomIndex, i);
107181             }
107182           };
107183           Edge.prototype.interfaces_ = function interfaces_ () {
107184             return []
107185           };
107186           Edge.prototype.getClass = function getClass () {
107187             return Edge
107188           };
107189           Edge.updateIM = function updateIM () {
107190             if (arguments.length === 2) {
107191               var label = arguments[0];
107192               var im = arguments[1];
107193               im.setAtLeastIfValid(label.getLocation(0, Position.ON), label.getLocation(1, Position.ON), 1);
107194               if (label.isArea()) {
107195                 im.setAtLeastIfValid(label.getLocation(0, Position.LEFT), label.getLocation(1, Position.LEFT), 2);
107196                 im.setAtLeastIfValid(label.getLocation(0, Position.RIGHT), label.getLocation(1, Position.RIGHT), 2);
107197               }
107198             } else { return GraphComponent$$1.prototype.updateIM.apply(this, arguments) }
107199           };
107200
107201           return Edge;
107202         }(GraphComponent));
107203
107204         var BufferBuilder = function BufferBuilder (bufParams) {
107205           this._workingPrecisionModel = null;
107206           this._workingNoder = null;
107207           this._geomFact = null;
107208           this._graph = null;
107209           this._edgeList = new EdgeList();
107210           this._bufParams = bufParams || null;
107211         };
107212         BufferBuilder.prototype.setWorkingPrecisionModel = function setWorkingPrecisionModel (pm) {
107213           this._workingPrecisionModel = pm;
107214         };
107215         BufferBuilder.prototype.insertUniqueEdge = function insertUniqueEdge (e) {
107216           var existingEdge = this._edgeList.findEqualEdge(e);
107217           if (existingEdge !== null) {
107218             var existingLabel = existingEdge.getLabel();
107219             var labelToMerge = e.getLabel();
107220             if (!existingEdge.isPointwiseEqual(e)) {
107221               labelToMerge = new Label(e.getLabel());
107222               labelToMerge.flip();
107223             }
107224             existingLabel.merge(labelToMerge);
107225             var mergeDelta = BufferBuilder.depthDelta(labelToMerge);
107226             var existingDelta = existingEdge.getDepthDelta();
107227             var newDelta = existingDelta + mergeDelta;
107228             existingEdge.setDepthDelta(newDelta);
107229           } else {
107230             this._edgeList.add(e);
107231             e.setDepthDelta(BufferBuilder.depthDelta(e.getLabel()));
107232           }
107233         };
107234         BufferBuilder.prototype.buildSubgraphs = function buildSubgraphs (subgraphList, polyBuilder) {
107235           var processedGraphs = new ArrayList();
107236           for (var i = subgraphList.iterator(); i.hasNext();) {
107237             var subgraph = i.next();
107238             var p = subgraph.getRightmostCoordinate();
107239             var locater = new SubgraphDepthLocater(processedGraphs);
107240             var outsideDepth = locater.getDepth(p);
107241             subgraph.computeDepth(outsideDepth);
107242             subgraph.findResultEdges();
107243             processedGraphs.add(subgraph);
107244             polyBuilder.add(subgraph.getDirectedEdges(), subgraph.getNodes());
107245           }
107246         };
107247         BufferBuilder.prototype.createSubgraphs = function createSubgraphs (graph) {
107248           var subgraphList = new ArrayList();
107249           for (var i = graph.getNodes().iterator(); i.hasNext();) {
107250             var node = i.next();
107251             if (!node.isVisited()) {
107252               var subgraph = new BufferSubgraph();
107253               subgraph.create(node);
107254               subgraphList.add(subgraph);
107255             }
107256           }
107257           Collections.sort(subgraphList, Collections.reverseOrder());
107258           return subgraphList
107259         };
107260         BufferBuilder.prototype.createEmptyResultGeometry = function createEmptyResultGeometry () {
107261           var emptyGeom = this._geomFact.createPolygon();
107262           return emptyGeom
107263         };
107264         BufferBuilder.prototype.getNoder = function getNoder (precisionModel) {
107265           if (this._workingNoder !== null) { return this._workingNoder }
107266           var noder = new MCIndexNoder();
107267           var li = new RobustLineIntersector();
107268           li.setPrecisionModel(precisionModel);
107269           noder.setSegmentIntersector(new IntersectionAdder(li));
107270           return noder
107271         };
107272         BufferBuilder.prototype.buffer = function buffer (g, distance) {
107273           var precisionModel = this._workingPrecisionModel;
107274           if (precisionModel === null) { precisionModel = g.getPrecisionModel(); }
107275           this._geomFact = g.getFactory();
107276           var curveBuilder = new OffsetCurveBuilder(precisionModel, this._bufParams);
107277           var curveSetBuilder = new OffsetCurveSetBuilder(g, distance, curveBuilder);
107278           var bufferSegStrList = curveSetBuilder.getCurves();
107279           if (bufferSegStrList.size() <= 0) {
107280             return this.createEmptyResultGeometry()
107281           }
107282           this.computeNodedEdges(bufferSegStrList, precisionModel);
107283           this._graph = new PlanarGraph(new OverlayNodeFactory());
107284           this._graph.addEdges(this._edgeList.getEdges());
107285           var subgraphList = this.createSubgraphs(this._graph);
107286           var polyBuilder = new PolygonBuilder(this._geomFact);
107287           this.buildSubgraphs(subgraphList, polyBuilder);
107288           var resultPolyList = polyBuilder.getPolygons();
107289           if (resultPolyList.size() <= 0) {
107290             return this.createEmptyResultGeometry()
107291           }
107292           var resultGeom = this._geomFact.buildGeometry(resultPolyList);
107293           return resultGeom
107294         };
107295         BufferBuilder.prototype.computeNodedEdges = function computeNodedEdges (bufferSegStrList, precisionModel) {
107296             var this$1 = this;
107297
107298           var noder = this.getNoder(precisionModel);
107299           noder.computeNodes(bufferSegStrList);
107300           var nodedSegStrings = noder.getNodedSubstrings();
107301           for (var i = nodedSegStrings.iterator(); i.hasNext();) {
107302             var segStr = i.next();
107303             var pts = segStr.getCoordinates();
107304             if (pts.length === 2 && pts[0].equals2D(pts[1])) { continue }
107305             var oldLabel = segStr.getData();
107306             var edge = new Edge(segStr.getCoordinates(), new Label(oldLabel));
107307             this$1.insertUniqueEdge(edge);
107308           }
107309         };
107310         BufferBuilder.prototype.setNoder = function setNoder (noder) {
107311           this._workingNoder = noder;
107312         };
107313         BufferBuilder.prototype.interfaces_ = function interfaces_ () {
107314           return []
107315         };
107316         BufferBuilder.prototype.getClass = function getClass () {
107317           return BufferBuilder
107318         };
107319         BufferBuilder.depthDelta = function depthDelta (label) {
107320           var lLoc = label.getLocation(0, Position.LEFT);
107321           var rLoc = label.getLocation(0, Position.RIGHT);
107322           if (lLoc === Location.INTERIOR && rLoc === Location.EXTERIOR) { return 1; } else if (lLoc === Location.EXTERIOR && rLoc === Location.INTERIOR) { return -1 }
107323           return 0
107324         };
107325         BufferBuilder.convertSegStrings = function convertSegStrings (it) {
107326           var fact = new GeometryFactory();
107327           var lines = new ArrayList();
107328           while (it.hasNext()) {
107329             var ss = it.next();
107330             var line = fact.createLineString(ss.getCoordinates());
107331             lines.add(line);
107332           }
107333           return fact.buildGeometry(lines)
107334         };
107335
107336         var ScaledNoder = function ScaledNoder () {
107337           this._noder = null;
107338           this._scaleFactor = null;
107339           this._offsetX = null;
107340           this._offsetY = null;
107341           this._isScaled = false;
107342           if (arguments.length === 2) {
107343             var noder = arguments[0];
107344             var scaleFactor = arguments[1];
107345             this._noder = noder;
107346             this._scaleFactor = scaleFactor;
107347             this._offsetX = 0.0;
107348             this._offsetY = 0.0;
107349             this._isScaled = !this.isIntegerPrecision();
107350           } else if (arguments.length === 4) {
107351             var noder$1 = arguments[0];
107352             var scaleFactor$1 = arguments[1];
107353             var offsetX = arguments[2];
107354             var offsetY = arguments[3];
107355             this._noder = noder$1;
107356             this._scaleFactor = scaleFactor$1;
107357             this._offsetX = offsetX;
107358             this._offsetY = offsetY;
107359             this._isScaled = !this.isIntegerPrecision();
107360           }
107361         };
107362         ScaledNoder.prototype.rescale = function rescale () {
107363             var this$1 = this;
107364
107365           if (hasInterface(arguments[0], Collection)) {
107366             var segStrings = arguments[0];
107367             for (var i = segStrings.iterator(); i.hasNext();) {
107368               var ss = i.next();
107369               this$1.rescale(ss.getCoordinates());
107370             }
107371           } else if (arguments[0] instanceof Array) {
107372             var pts = arguments[0];
107373             // let p0 = null
107374             // let p1 = null
107375             // if (pts.length === 2) {
107376             // p0 = new Coordinate(pts[0])
107377             // p1 = new Coordinate(pts[1])
107378             // }
107379             for (var i$1 = 0; i$1 < pts.length; i$1++) {
107380               pts[i$1].x = pts[i$1].x / this$1._scaleFactor + this$1._offsetX;
107381               pts[i$1].y = pts[i$1].y / this$1._scaleFactor + this$1._offsetY;
107382             }
107383             if (pts.length === 2 && pts[0].equals2D(pts[1])) {
107384               System.out.println(pts);
107385             }
107386           }
107387         };
107388         ScaledNoder.prototype.scale = function scale () {
107389             var this$1 = this;
107390
107391           if (hasInterface(arguments[0], Collection)) {
107392             var segStrings = arguments[0];
107393             var nodedSegmentStrings = new ArrayList();
107394             for (var i = segStrings.iterator(); i.hasNext();) {
107395               var ss = i.next();
107396               nodedSegmentStrings.add(new NodedSegmentString(this$1.scale(ss.getCoordinates()), ss.getData()));
107397             }
107398             return nodedSegmentStrings
107399           } else if (arguments[0] instanceof Array) {
107400             var pts = arguments[0];
107401             var roundPts = new Array(pts.length).fill(null);
107402             for (var i$1 = 0; i$1 < pts.length; i$1++) {
107403               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);
107404             }
107405             var roundPtsNoDup = CoordinateArrays.removeRepeatedPoints(roundPts);
107406             return roundPtsNoDup
107407           }
107408         };
107409         ScaledNoder.prototype.isIntegerPrecision = function isIntegerPrecision () {
107410           return this._scaleFactor === 1.0
107411         };
107412         ScaledNoder.prototype.getNodedSubstrings = function getNodedSubstrings () {
107413           var splitSS = this._noder.getNodedSubstrings();
107414           if (this._isScaled) { this.rescale(splitSS); }
107415           return splitSS
107416         };
107417         ScaledNoder.prototype.computeNodes = function computeNodes (inputSegStrings) {
107418           var intSegStrings = inputSegStrings;
107419           if (this._isScaled) { intSegStrings = this.scale(inputSegStrings); }
107420           this._noder.computeNodes(intSegStrings);
107421         };
107422         ScaledNoder.prototype.interfaces_ = function interfaces_ () {
107423           return [Noder]
107424         };
107425         ScaledNoder.prototype.getClass = function getClass () {
107426           return ScaledNoder
107427         };
107428
107429         var NodingValidator = function NodingValidator () {
107430           this._li = new RobustLineIntersector();
107431           this._segStrings = null;
107432           var segStrings = arguments[0];
107433           this._segStrings = segStrings;
107434         };
107435
107436         var staticAccessors$33 = { fact: { configurable: true } };
107437         NodingValidator.prototype.checkEndPtVertexIntersections = function checkEndPtVertexIntersections () {
107438             var this$1 = this;
107439
107440           if (arguments.length === 0) {
107441             for (var i = this._segStrings.iterator(); i.hasNext();) {
107442               var ss = i.next();
107443               var pts = ss.getCoordinates();
107444               this$1.checkEndPtVertexIntersections(pts[0], this$1._segStrings);
107445               this$1.checkEndPtVertexIntersections(pts[pts.length - 1], this$1._segStrings);
107446             }
107447           } else if (arguments.length === 2) {
107448             var testPt = arguments[0];
107449             var segStrings = arguments[1];
107450             for (var i$1 = segStrings.iterator(); i$1.hasNext();) {
107451               var ss$1 = i$1.next();
107452               var pts$1 = ss$1.getCoordinates();
107453               for (var j = 1; j < pts$1.length - 1; j++) {
107454                 if (pts$1[j].equals(testPt)) { throw new RuntimeException('found endpt/interior pt intersection at index ' + j + ' :pt ' + testPt) }
107455               }
107456             }
107457           }
107458         };
107459         NodingValidator.prototype.checkInteriorIntersections = function checkInteriorIntersections () {
107460             var this$1 = this;
107461
107462           if (arguments.length === 0) {
107463             for (var i = this._segStrings.iterator(); i.hasNext();) {
107464               var ss0 = i.next();
107465               for (var j = this._segStrings.iterator(); j.hasNext();) {
107466                 var ss1 = j.next();
107467                 this$1.checkInteriorIntersections(ss0, ss1);
107468               }
107469             }
107470           } else if (arguments.length === 2) {
107471             var ss0$1 = arguments[0];
107472             var ss1$1 = arguments[1];
107473             var pts0 = ss0$1.getCoordinates();
107474             var pts1 = ss1$1.getCoordinates();
107475             for (var i0 = 0; i0 < pts0.length - 1; i0++) {
107476               for (var i1 = 0; i1 < pts1.length - 1; i1++) {
107477                 this$1.checkInteriorIntersections(ss0$1, i0, ss1$1, i1);
107478               }
107479             }
107480           } else if (arguments.length === 4) {
107481             var e0 = arguments[0];
107482             var segIndex0 = arguments[1];
107483             var e1 = arguments[2];
107484             var segIndex1 = arguments[3];
107485             if (e0 === e1 && segIndex0 === segIndex1) { return null }
107486             var p00 = e0.getCoordinates()[segIndex0];
107487             var p01 = e0.getCoordinates()[segIndex0 + 1];
107488             var p10 = e1.getCoordinates()[segIndex1];
107489             var p11 = e1.getCoordinates()[segIndex1 + 1];
107490             this._li.computeIntersection(p00, p01, p10, p11);
107491             if (this._li.hasIntersection()) {
107492               if (this._li.isProper() || this.hasInteriorIntersection(this._li, p00, p01) || this.hasInteriorIntersection(this._li, p10, p11)) {
107493                 throw new RuntimeException('found non-noded intersection at ' + p00 + '-' + p01 + ' and ' + p10 + '-' + p11)
107494               }
107495             }
107496           }
107497         };
107498         NodingValidator.prototype.checkValid = function checkValid () {
107499           this.checkEndPtVertexIntersections();
107500           this.checkInteriorIntersections();
107501           this.checkCollapses();
107502         };
107503         NodingValidator.prototype.checkCollapses = function checkCollapses () {
107504             var this$1 = this;
107505
107506           if (arguments.length === 0) {
107507             for (var i = this._segStrings.iterator(); i.hasNext();) {
107508               var ss = i.next();
107509               this$1.checkCollapses(ss);
107510             }
107511           } else if (arguments.length === 1) {
107512             var ss$1 = arguments[0];
107513             var pts = ss$1.getCoordinates();
107514             for (var i$1 = 0; i$1 < pts.length - 2; i$1++) {
107515               this$1.checkCollapse(pts[i$1], pts[i$1 + 1], pts[i$1 + 2]);
107516             }
107517           }
107518         };
107519         NodingValidator.prototype.hasInteriorIntersection = function hasInteriorIntersection (li, p0, p1) {
107520           for (var i = 0; i < li.getIntersectionNum(); i++) {
107521             var intPt = li.getIntersection(i);
107522             if (!(intPt.equals(p0) || intPt.equals(p1))) { return true }
107523           }
107524           return false
107525         };
107526         NodingValidator.prototype.checkCollapse = function checkCollapse (p0, p1, p2) {
107527           if (p0.equals(p2)) { throw new RuntimeException('found non-noded collapse at ' + NodingValidator.fact.createLineString([p0, p1, p2])) }
107528         };
107529         NodingValidator.prototype.interfaces_ = function interfaces_ () {
107530           return []
107531         };
107532         NodingValidator.prototype.getClass = function getClass () {
107533           return NodingValidator
107534         };
107535         staticAccessors$33.fact.get = function () { return new GeometryFactory() };
107536
107537         Object.defineProperties( NodingValidator, staticAccessors$33 );
107538
107539         var HotPixel = function HotPixel () {
107540           this._li = null;
107541           this._pt = null;
107542           this._originalPt = null;
107543           this._ptScaled = null;
107544           this._p0Scaled = null;
107545           this._p1Scaled = null;
107546           this._scaleFactor = null;
107547           this._minx = null;
107548           this._maxx = null;
107549           this._miny = null;
107550           this._maxy = null;
107551           this._corner = new Array(4).fill(null);
107552           this._safeEnv = null;
107553           var pt = arguments[0];
107554           var scaleFactor = arguments[1];
107555           var li = arguments[2];
107556           this._originalPt = pt;
107557           this._pt = pt;
107558           this._scaleFactor = scaleFactor;
107559           this._li = li;
107560           if (scaleFactor <= 0) { throw new IllegalArgumentException('Scale factor must be non-zero') }
107561           if (scaleFactor !== 1.0) {
107562             this._pt = new Coordinate(this.scale(pt.x), this.scale(pt.y));
107563             this._p0Scaled = new Coordinate();
107564             this._p1Scaled = new Coordinate();
107565           }
107566           this.initCorners(this._pt);
107567         };
107568
107569         var staticAccessors$34 = { SAFE_ENV_EXPANSION_FACTOR: { configurable: true } };
107570         HotPixel.prototype.intersectsScaled = function intersectsScaled (p0, p1) {
107571           var segMinx = Math.min(p0.x, p1.x);
107572           var segMaxx = Math.max(p0.x, p1.x);
107573           var segMiny = Math.min(p0.y, p1.y);
107574           var segMaxy = Math.max(p0.y, p1.y);
107575           var isOutsidePixelEnv = this._maxx < segMinx || this._minx > segMaxx || this._maxy < segMiny || this._miny > segMaxy;
107576           if (isOutsidePixelEnv) { return false }
107577           var intersects = this.intersectsToleranceSquare(p0, p1);
107578           Assert.isTrue(!(isOutsidePixelEnv && intersects), 'Found bad envelope test');
107579           return intersects
107580         };
107581         HotPixel.prototype.initCorners = function initCorners (pt) {
107582           var tolerance = 0.5;
107583           this._minx = pt.x - tolerance;
107584           this._maxx = pt.x + tolerance;
107585           this._miny = pt.y - tolerance;
107586           this._maxy = pt.y + tolerance;
107587           this._corner[0] = new Coordinate(this._maxx, this._maxy);
107588           this._corner[1] = new Coordinate(this._minx, this._maxy);
107589           this._corner[2] = new Coordinate(this._minx, this._miny);
107590           this._corner[3] = new Coordinate(this._maxx, this._miny);
107591         };
107592         HotPixel.prototype.intersects = function intersects (p0, p1) {
107593           if (this._scaleFactor === 1.0) { return this.intersectsScaled(p0, p1) }
107594           this.copyScaled(p0, this._p0Scaled);
107595           this.copyScaled(p1, this._p1Scaled);
107596           return this.intersectsScaled(this._p0Scaled, this._p1Scaled)
107597         };
107598         HotPixel.prototype.scale = function scale (val) {
107599           return Math.round(val * this._scaleFactor)
107600         };
107601         HotPixel.prototype.getCoordinate = function getCoordinate () {
107602           return this._originalPt
107603         };
107604         HotPixel.prototype.copyScaled = function copyScaled (p, pScaled) {
107605           pScaled.x = this.scale(p.x);
107606           pScaled.y = this.scale(p.y);
107607         };
107608         HotPixel.prototype.getSafeEnvelope = function getSafeEnvelope () {
107609           if (this._safeEnv === null) {
107610             var safeTolerance = HotPixel.SAFE_ENV_EXPANSION_FACTOR / this._scaleFactor;
107611             this._safeEnv = new Envelope(this._originalPt.x - safeTolerance, this._originalPt.x + safeTolerance, this._originalPt.y - safeTolerance, this._originalPt.y + safeTolerance);
107612           }
107613           return this._safeEnv
107614         };
107615         HotPixel.prototype.intersectsPixelClosure = function intersectsPixelClosure (p0, p1) {
107616           this._li.computeIntersection(p0, p1, this._corner[0], this._corner[1]);
107617           if (this._li.hasIntersection()) { return true }
107618           this._li.computeIntersection(p0, p1, this._corner[1], this._corner[2]);
107619           if (this._li.hasIntersection()) { return true }
107620           this._li.computeIntersection(p0, p1, this._corner[2], this._corner[3]);
107621           if (this._li.hasIntersection()) { return true }
107622           this._li.computeIntersection(p0, p1, this._corner[3], this._corner[0]);
107623           if (this._li.hasIntersection()) { return true }
107624           return false
107625         };
107626         HotPixel.prototype.intersectsToleranceSquare = function intersectsToleranceSquare (p0, p1) {
107627           var intersectsLeft = false;
107628           var intersectsBottom = false;
107629           this._li.computeIntersection(p0, p1, this._corner[0], this._corner[1]);
107630           if (this._li.isProper()) { return true }
107631           this._li.computeIntersection(p0, p1, this._corner[1], this._corner[2]);
107632           if (this._li.isProper()) { return true }
107633           if (this._li.hasIntersection()) { intersectsLeft = true; }
107634           this._li.computeIntersection(p0, p1, this._corner[2], this._corner[3]);
107635           if (this._li.isProper()) { return true }
107636           if (this._li.hasIntersection()) { intersectsBottom = true; }
107637           this._li.computeIntersection(p0, p1, this._corner[3], this._corner[0]);
107638           if (this._li.isProper()) { return true }
107639           if (intersectsLeft && intersectsBottom) { return true }
107640           if (p0.equals(this._pt)) { return true }
107641           if (p1.equals(this._pt)) { return true }
107642           return false
107643         };
107644         HotPixel.prototype.addSnappedNode = function addSnappedNode (segStr, segIndex) {
107645           var p0 = segStr.getCoordinate(segIndex);
107646           var p1 = segStr.getCoordinate(segIndex + 1);
107647           if (this.intersects(p0, p1)) {
107648             segStr.addIntersection(this.getCoordinate(), segIndex);
107649             return true
107650           }
107651           return false
107652         };
107653         HotPixel.prototype.interfaces_ = function interfaces_ () {
107654           return []
107655         };
107656         HotPixel.prototype.getClass = function getClass () {
107657           return HotPixel
107658         };
107659         staticAccessors$34.SAFE_ENV_EXPANSION_FACTOR.get = function () { return 0.75 };
107660
107661         Object.defineProperties( HotPixel, staticAccessors$34 );
107662
107663         var MonotoneChainSelectAction = function MonotoneChainSelectAction () {
107664           this.tempEnv1 = new Envelope();
107665           this.selectedSegment = new LineSegment();
107666         };
107667         MonotoneChainSelectAction.prototype.select = function select () {
107668           if (arguments.length === 1) ; else if (arguments.length === 2) {
107669             var mc = arguments[0];
107670             var startIndex = arguments[1];
107671             mc.getLineSegment(startIndex, this.selectedSegment);
107672             this.select(this.selectedSegment);
107673           }
107674         };
107675         MonotoneChainSelectAction.prototype.interfaces_ = function interfaces_ () {
107676           return []
107677         };
107678         MonotoneChainSelectAction.prototype.getClass = function getClass () {
107679           return MonotoneChainSelectAction
107680         };
107681
107682         var MCIndexPointSnapper = function MCIndexPointSnapper () {
107683           this._index = null;
107684           var index = arguments[0];
107685           this._index = index;
107686         };
107687
107688         var staticAccessors$35 = { HotPixelSnapAction: { configurable: true } };
107689         MCIndexPointSnapper.prototype.snap = function snap () {
107690           if (arguments.length === 1) {
107691             var hotPixel = arguments[0];
107692             return this.snap(hotPixel, null, -1)
107693           } else if (arguments.length === 3) {
107694             var hotPixel$1 = arguments[0];
107695             var parentEdge = arguments[1];
107696             var hotPixelVertexIndex = arguments[2];
107697             var pixelEnv = hotPixel$1.getSafeEnvelope();
107698             var hotPixelSnapAction = new HotPixelSnapAction(hotPixel$1, parentEdge, hotPixelVertexIndex);
107699             this._index.query(pixelEnv, {
107700               interfaces_: function () {
107701                 return [ItemVisitor]
107702               },
107703               visitItem: function (item) {
107704                 var testChain = item;
107705                 testChain.select(pixelEnv, hotPixelSnapAction);
107706               }
107707             });
107708             return hotPixelSnapAction.isNodeAdded()
107709           }
107710         };
107711         MCIndexPointSnapper.prototype.interfaces_ = function interfaces_ () {
107712           return []
107713         };
107714         MCIndexPointSnapper.prototype.getClass = function getClass () {
107715           return MCIndexPointSnapper
107716         };
107717         staticAccessors$35.HotPixelSnapAction.get = function () { return HotPixelSnapAction };
107718
107719         Object.defineProperties( MCIndexPointSnapper, staticAccessors$35 );
107720
107721         var HotPixelSnapAction = (function (MonotoneChainSelectAction$$1) {
107722           function HotPixelSnapAction () {
107723             MonotoneChainSelectAction$$1.call(this);
107724             this._hotPixel = null;
107725             this._parentEdge = null;
107726             this._hotPixelVertexIndex = null;
107727             this._isNodeAdded = false;
107728             var hotPixel = arguments[0];
107729             var parentEdge = arguments[1];
107730             var hotPixelVertexIndex = arguments[2];
107731             this._hotPixel = hotPixel;
107732             this._parentEdge = parentEdge;
107733             this._hotPixelVertexIndex = hotPixelVertexIndex;
107734           }
107735
107736           if ( MonotoneChainSelectAction$$1 ) { HotPixelSnapAction.__proto__ = MonotoneChainSelectAction$$1; }
107737           HotPixelSnapAction.prototype = Object.create( MonotoneChainSelectAction$$1 && MonotoneChainSelectAction$$1.prototype );
107738           HotPixelSnapAction.prototype.constructor = HotPixelSnapAction;
107739           HotPixelSnapAction.prototype.isNodeAdded = function isNodeAdded () {
107740             return this._isNodeAdded
107741           };
107742           HotPixelSnapAction.prototype.select = function select () {
107743             if (arguments.length === 2) {
107744               var mc = arguments[0];
107745               var startIndex = arguments[1];
107746               var ss = mc.getContext();
107747               if (this._parentEdge !== null) {
107748                 if (ss === this._parentEdge && startIndex === this._hotPixelVertexIndex) { return null }
107749               }
107750               this._isNodeAdded = this._hotPixel.addSnappedNode(ss, startIndex);
107751             } else { return MonotoneChainSelectAction$$1.prototype.select.apply(this, arguments) }
107752           };
107753           HotPixelSnapAction.prototype.interfaces_ = function interfaces_ () {
107754             return []
107755           };
107756           HotPixelSnapAction.prototype.getClass = function getClass () {
107757             return HotPixelSnapAction
107758           };
107759
107760           return HotPixelSnapAction;
107761         }(MonotoneChainSelectAction));
107762
107763         var InteriorIntersectionFinderAdder = function InteriorIntersectionFinderAdder () {
107764           this._li = null;
107765           this._interiorIntersections = null;
107766           var li = arguments[0];
107767           this._li = li;
107768           this._interiorIntersections = new ArrayList();
107769         };
107770         InteriorIntersectionFinderAdder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {
107771             var this$1 = this;
107772
107773           if (e0 === e1 && segIndex0 === segIndex1) { return null }
107774           var p00 = e0.getCoordinates()[segIndex0];
107775           var p01 = e0.getCoordinates()[segIndex0 + 1];
107776           var p10 = e1.getCoordinates()[segIndex1];
107777           var p11 = e1.getCoordinates()[segIndex1 + 1];
107778           this._li.computeIntersection(p00, p01, p10, p11);
107779           if (this._li.hasIntersection()) {
107780             if (this._li.isInteriorIntersection()) {
107781               for (var intIndex = 0; intIndex < this._li.getIntersectionNum(); intIndex++) {
107782                 this$1._interiorIntersections.add(this$1._li.getIntersection(intIndex));
107783               }
107784               e0.addIntersections(this._li, segIndex0, 0);
107785               e1.addIntersections(this._li, segIndex1, 1);
107786             }
107787           }
107788         };
107789         InteriorIntersectionFinderAdder.prototype.isDone = function isDone () {
107790           return false
107791         };
107792         InteriorIntersectionFinderAdder.prototype.getInteriorIntersections = function getInteriorIntersections () {
107793           return this._interiorIntersections
107794         };
107795         InteriorIntersectionFinderAdder.prototype.interfaces_ = function interfaces_ () {
107796           return [SegmentIntersector]
107797         };
107798         InteriorIntersectionFinderAdder.prototype.getClass = function getClass () {
107799           return InteriorIntersectionFinderAdder
107800         };
107801
107802         var MCIndexSnapRounder = function MCIndexSnapRounder () {
107803           this._pm = null;
107804           this._li = null;
107805           this._scaleFactor = null;
107806           this._noder = null;
107807           this._pointSnapper = null;
107808           this._nodedSegStrings = null;
107809           var pm = arguments[0];
107810           this._pm = pm;
107811           this._li = new RobustLineIntersector();
107812           this._li.setPrecisionModel(pm);
107813           this._scaleFactor = pm.getScale();
107814         };
107815         MCIndexSnapRounder.prototype.checkCorrectness = function checkCorrectness (inputSegmentStrings) {
107816           var resultSegStrings = NodedSegmentString.getNodedSubstrings(inputSegmentStrings);
107817           var nv = new NodingValidator(resultSegStrings);
107818           try {
107819             nv.checkValid();
107820           } catch (ex) {
107821             if (ex instanceof Exception) {
107822               ex.printStackTrace();
107823             } else { throw ex }
107824           } finally {}
107825         };
107826         MCIndexSnapRounder.prototype.getNodedSubstrings = function getNodedSubstrings () {
107827           return NodedSegmentString.getNodedSubstrings(this._nodedSegStrings)
107828         };
107829         MCIndexSnapRounder.prototype.snapRound = function snapRound (segStrings, li) {
107830           var intersections = this.findInteriorIntersections(segStrings, li);
107831           this.computeIntersectionSnaps(intersections);
107832           this.computeVertexSnaps(segStrings);
107833         };
107834         MCIndexSnapRounder.prototype.findInteriorIntersections = function findInteriorIntersections (segStrings, li) {
107835           var intFinderAdder = new InteriorIntersectionFinderAdder(li);
107836           this._noder.setSegmentIntersector(intFinderAdder);
107837           this._noder.computeNodes(segStrings);
107838           return intFinderAdder.getInteriorIntersections()
107839         };
107840         MCIndexSnapRounder.prototype.computeVertexSnaps = function computeVertexSnaps () {
107841             var this$1 = this;
107842
107843           if (hasInterface(arguments[0], Collection)) {
107844             var edges = arguments[0];
107845             for (var i0 = edges.iterator(); i0.hasNext();) {
107846               var edge0 = i0.next();
107847               this$1.computeVertexSnaps(edge0);
107848             }
107849           } else if (arguments[0] instanceof NodedSegmentString) {
107850             var e = arguments[0];
107851             var pts0 = e.getCoordinates();
107852             for (var i = 0; i < pts0.length; i++) {
107853               var hotPixel = new HotPixel(pts0[i], this$1._scaleFactor, this$1._li);
107854               var isNodeAdded = this$1._pointSnapper.snap(hotPixel, e, i);
107855               if (isNodeAdded) {
107856                 e.addIntersection(pts0[i], i);
107857               }
107858             }
107859           }
107860         };
107861         MCIndexSnapRounder.prototype.computeNodes = function computeNodes (inputSegmentStrings) {
107862           this._nodedSegStrings = inputSegmentStrings;
107863           this._noder = new MCIndexNoder();
107864           this._pointSnapper = new MCIndexPointSnapper(this._noder.getIndex());
107865           this.snapRound(inputSegmentStrings, this._li);
107866         };
107867         MCIndexSnapRounder.prototype.computeIntersectionSnaps = function computeIntersectionSnaps (snapPts) {
107868             var this$1 = this;
107869
107870           for (var it = snapPts.iterator(); it.hasNext();) {
107871             var snapPt = it.next();
107872             var hotPixel = new HotPixel(snapPt, this$1._scaleFactor, this$1._li);
107873             this$1._pointSnapper.snap(hotPixel);
107874           }
107875         };
107876         MCIndexSnapRounder.prototype.interfaces_ = function interfaces_ () {
107877           return [Noder]
107878         };
107879         MCIndexSnapRounder.prototype.getClass = function getClass () {
107880           return MCIndexSnapRounder
107881         };
107882
107883         var BufferOp = function BufferOp () {
107884           this._argGeom = null;
107885           this._distance = null;
107886           this._bufParams = new BufferParameters();
107887           this._resultGeometry = null;
107888           this._saveException = null;
107889           if (arguments.length === 1) {
107890             var g = arguments[0];
107891             this._argGeom = g;
107892           } else if (arguments.length === 2) {
107893             var g$1 = arguments[0];
107894             var bufParams = arguments[1];
107895             this._argGeom = g$1;
107896             this._bufParams = bufParams;
107897           }
107898         };
107899
107900         var staticAccessors$32 = { CAP_ROUND: { configurable: true },CAP_BUTT: { configurable: true },CAP_FLAT: { configurable: true },CAP_SQUARE: { configurable: true },MAX_PRECISION_DIGITS: { configurable: true } };
107901         BufferOp.prototype.bufferFixedPrecision = function bufferFixedPrecision (fixedPM) {
107902           var noder = new ScaledNoder(new MCIndexSnapRounder(new PrecisionModel(1.0)), fixedPM.getScale());
107903           var bufBuilder = new BufferBuilder(this._bufParams);
107904           bufBuilder.setWorkingPrecisionModel(fixedPM);
107905           bufBuilder.setNoder(noder);
107906           this._resultGeometry = bufBuilder.buffer(this._argGeom, this._distance);
107907         };
107908         BufferOp.prototype.bufferReducedPrecision = function bufferReducedPrecision () {
107909             var this$1 = this;
107910
107911           if (arguments.length === 0) {
107912             for (var precDigits = BufferOp.MAX_PRECISION_DIGITS; precDigits >= 0; precDigits--) {
107913               try {
107914                 this$1.bufferReducedPrecision(precDigits);
107915               } catch (ex) {
107916                 if (ex instanceof TopologyException) {
107917                   this$1._saveException = ex;
107918                 } else { throw ex }
107919               } finally {}
107920               if (this$1._resultGeometry !== null) { return null }
107921             }
107922             throw this._saveException
107923           } else if (arguments.length === 1) {
107924             var precisionDigits = arguments[0];
107925             var sizeBasedScaleFactor = BufferOp.precisionScaleFactor(this._argGeom, this._distance, precisionDigits);
107926             var fixedPM = new PrecisionModel(sizeBasedScaleFactor);
107927             this.bufferFixedPrecision(fixedPM);
107928           }
107929         };
107930         BufferOp.prototype.computeGeometry = function computeGeometry () {
107931           this.bufferOriginalPrecision();
107932           if (this._resultGeometry !== null) { return null }
107933           var argPM = this._argGeom.getFactory().getPrecisionModel();
107934           if (argPM.getType() === PrecisionModel.FIXED) { this.bufferFixedPrecision(argPM); } else { this.bufferReducedPrecision(); }
107935         };
107936         BufferOp.prototype.setQuadrantSegments = function setQuadrantSegments (quadrantSegments) {
107937           this._bufParams.setQuadrantSegments(quadrantSegments);
107938         };
107939         BufferOp.prototype.bufferOriginalPrecision = function bufferOriginalPrecision () {
107940           try {
107941             var bufBuilder = new BufferBuilder(this._bufParams);
107942             this._resultGeometry = bufBuilder.buffer(this._argGeom, this._distance);
107943           } catch (ex) {
107944             if (ex instanceof RuntimeException) {
107945               this._saveException = ex;
107946             } else { throw ex }
107947           } finally {}
107948         };
107949         BufferOp.prototype.getResultGeometry = function getResultGeometry (distance) {
107950           this._distance = distance;
107951           this.computeGeometry();
107952           return this._resultGeometry
107953         };
107954         BufferOp.prototype.setEndCapStyle = function setEndCapStyle (endCapStyle) {
107955           this._bufParams.setEndCapStyle(endCapStyle);
107956         };
107957         BufferOp.prototype.interfaces_ = function interfaces_ () {
107958           return []
107959         };
107960         BufferOp.prototype.getClass = function getClass () {
107961           return BufferOp
107962         };
107963         BufferOp.bufferOp = function bufferOp () {
107964           if (arguments.length === 2) {
107965             var g = arguments[0];
107966             var distance = arguments[1];
107967             var gBuf = new BufferOp(g);
107968             var geomBuf = gBuf.getResultGeometry(distance);
107969             return geomBuf
107970           } else if (arguments.length === 3) {
107971             if (Number.isInteger(arguments[2]) && (arguments[0] instanceof Geometry && typeof arguments[1] === 'number')) {
107972               var g$1 = arguments[0];
107973               var distance$1 = arguments[1];
107974               var quadrantSegments = arguments[2];
107975               var bufOp = new BufferOp(g$1);
107976               bufOp.setQuadrantSegments(quadrantSegments);
107977               var geomBuf$1 = bufOp.getResultGeometry(distance$1);
107978               return geomBuf$1
107979             } else if (arguments[2] instanceof BufferParameters && (arguments[0] instanceof Geometry && typeof arguments[1] === 'number')) {
107980               var g$2 = arguments[0];
107981               var distance$2 = arguments[1];
107982               var params = arguments[2];
107983               var bufOp$1 = new BufferOp(g$2, params);
107984               var geomBuf$2 = bufOp$1.getResultGeometry(distance$2);
107985               return geomBuf$2
107986             }
107987           } else if (arguments.length === 4) {
107988             var g$3 = arguments[0];
107989             var distance$3 = arguments[1];
107990             var quadrantSegments$1 = arguments[2];
107991             var endCapStyle = arguments[3];
107992             var bufOp$2 = new BufferOp(g$3);
107993             bufOp$2.setQuadrantSegments(quadrantSegments$1);
107994             bufOp$2.setEndCapStyle(endCapStyle);
107995             var geomBuf$3 = bufOp$2.getResultGeometry(distance$3);
107996             return geomBuf$3
107997           }
107998         };
107999         BufferOp.precisionScaleFactor = function precisionScaleFactor (g, distance, maxPrecisionDigits) {
108000           var env = g.getEnvelopeInternal();
108001           var envMax = MathUtil.max(Math.abs(env.getMaxX()), Math.abs(env.getMaxY()), Math.abs(env.getMinX()), Math.abs(env.getMinY()));
108002           var expandByDistance = distance > 0.0 ? distance : 0.0;
108003           var bufEnvMax = envMax + 2 * expandByDistance;
108004           var bufEnvPrecisionDigits = Math.trunc(Math.log(bufEnvMax) / Math.log(10) + 1.0);
108005           var minUnitLog10 = maxPrecisionDigits - bufEnvPrecisionDigits;
108006           var scaleFactor = Math.pow(10.0, minUnitLog10);
108007           return scaleFactor
108008         };
108009         staticAccessors$32.CAP_ROUND.get = function () { return BufferParameters.CAP_ROUND };
108010         staticAccessors$32.CAP_BUTT.get = function () { return BufferParameters.CAP_FLAT };
108011         staticAccessors$32.CAP_FLAT.get = function () { return BufferParameters.CAP_FLAT };
108012         staticAccessors$32.CAP_SQUARE.get = function () { return BufferParameters.CAP_SQUARE };
108013         staticAccessors$32.MAX_PRECISION_DIGITS.get = function () { return 12 };
108014
108015         Object.defineProperties( BufferOp, staticAccessors$32 );
108016
108017         var PointPairDistance = function PointPairDistance () {
108018           this._pt = [new Coordinate(), new Coordinate()];
108019           this._distance = Double.NaN;
108020           this._isNull = true;
108021         };
108022         PointPairDistance.prototype.getCoordinates = function getCoordinates () {
108023           return this._pt
108024         };
108025         PointPairDistance.prototype.getCoordinate = function getCoordinate (i) {
108026           return this._pt[i]
108027         };
108028         PointPairDistance.prototype.setMinimum = function setMinimum () {
108029           if (arguments.length === 1) {
108030             var ptDist = arguments[0];
108031             this.setMinimum(ptDist._pt[0], ptDist._pt[1]);
108032           } else if (arguments.length === 2) {
108033             var p0 = arguments[0];
108034             var p1 = arguments[1];
108035             if (this._isNull) {
108036               this.initialize(p0, p1);
108037               return null
108038             }
108039             var dist = p0.distance(p1);
108040             if (dist < this._distance) { this.initialize(p0, p1, dist); }
108041           }
108042         };
108043         PointPairDistance.prototype.initialize = function initialize () {
108044           if (arguments.length === 0) {
108045             this._isNull = true;
108046           } else if (arguments.length === 2) {
108047             var p0 = arguments[0];
108048             var p1 = arguments[1];
108049             this._pt[0].setCoordinate(p0);
108050             this._pt[1].setCoordinate(p1);
108051             this._distance = p0.distance(p1);
108052             this._isNull = false;
108053           } else if (arguments.length === 3) {
108054             var p0$1 = arguments[0];
108055             var p1$1 = arguments[1];
108056             var distance = arguments[2];
108057             this._pt[0].setCoordinate(p0$1);
108058             this._pt[1].setCoordinate(p1$1);
108059             this._distance = distance;
108060             this._isNull = false;
108061           }
108062         };
108063         PointPairDistance.prototype.getDistance = function getDistance () {
108064           return this._distance
108065         };
108066         PointPairDistance.prototype.setMaximum = function setMaximum () {
108067           if (arguments.length === 1) {
108068             var ptDist = arguments[0];
108069             this.setMaximum(ptDist._pt[0], ptDist._pt[1]);
108070           } else if (arguments.length === 2) {
108071             var p0 = arguments[0];
108072             var p1 = arguments[1];
108073             if (this._isNull) {
108074               this.initialize(p0, p1);
108075               return null
108076             }
108077             var dist = p0.distance(p1);
108078             if (dist > this._distance) { this.initialize(p0, p1, dist); }
108079           }
108080         };
108081         PointPairDistance.prototype.interfaces_ = function interfaces_ () {
108082           return []
108083         };
108084         PointPairDistance.prototype.getClass = function getClass () {
108085           return PointPairDistance
108086         };
108087
108088         var DistanceToPointFinder = function DistanceToPointFinder () {};
108089
108090         DistanceToPointFinder.prototype.interfaces_ = function interfaces_ () {
108091           return []
108092         };
108093         DistanceToPointFinder.prototype.getClass = function getClass () {
108094           return DistanceToPointFinder
108095         };
108096         DistanceToPointFinder.computeDistance = function computeDistance () {
108097           if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof LineString && arguments[1] instanceof Coordinate)) {
108098             var line = arguments[0];
108099             var pt = arguments[1];
108100             var ptDist = arguments[2];
108101             var coords = line.getCoordinates();
108102             var tempSegment = new LineSegment();
108103             for (var i = 0; i < coords.length - 1; i++) {
108104               tempSegment.setCoordinates(coords[i], coords[i + 1]);
108105               var closestPt = tempSegment.closestPoint(pt);
108106               ptDist.setMinimum(closestPt, pt);
108107             }
108108           } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof Polygon && arguments[1] instanceof Coordinate)) {
108109             var poly = arguments[0];
108110             var pt$1 = arguments[1];
108111             var ptDist$1 = arguments[2];
108112             DistanceToPointFinder.computeDistance(poly.getExteriorRing(), pt$1, ptDist$1);
108113             for (var i$1 = 0; i$1 < poly.getNumInteriorRing(); i$1++) {
108114               DistanceToPointFinder.computeDistance(poly.getInteriorRingN(i$1), pt$1, ptDist$1);
108115             }
108116           } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof Geometry && arguments[1] instanceof Coordinate)) {
108117             var geom = arguments[0];
108118             var pt$2 = arguments[1];
108119             var ptDist$2 = arguments[2];
108120             if (geom instanceof LineString) {
108121               DistanceToPointFinder.computeDistance(geom, pt$2, ptDist$2);
108122             } else if (geom instanceof Polygon) {
108123               DistanceToPointFinder.computeDistance(geom, pt$2, ptDist$2);
108124             } else if (geom instanceof GeometryCollection) {
108125               var gc = geom;
108126               for (var i$2 = 0; i$2 < gc.getNumGeometries(); i$2++) {
108127                 var g = gc.getGeometryN(i$2);
108128                 DistanceToPointFinder.computeDistance(g, pt$2, ptDist$2);
108129               }
108130             } else {
108131               ptDist$2.setMinimum(geom.getCoordinate(), pt$2);
108132             }
108133           } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof LineSegment && arguments[1] instanceof Coordinate)) {
108134             var segment = arguments[0];
108135             var pt$3 = arguments[1];
108136             var ptDist$3 = arguments[2];
108137             var closestPt$1 = segment.closestPoint(pt$3);
108138             ptDist$3.setMinimum(closestPt$1, pt$3);
108139           }
108140         };
108141
108142         var BufferCurveMaximumDistanceFinder = function BufferCurveMaximumDistanceFinder (inputGeom) {
108143           this._maxPtDist = new PointPairDistance();
108144           this._inputGeom = inputGeom || null;
108145         };
108146
108147         var staticAccessors$36 = { MaxPointDistanceFilter: { configurable: true },MaxMidpointDistanceFilter: { configurable: true } };
108148         BufferCurveMaximumDistanceFinder.prototype.computeMaxMidpointDistance = function computeMaxMidpointDistance (curve) {
108149           var distFilter = new MaxMidpointDistanceFilter(this._inputGeom);
108150           curve.apply(distFilter);
108151           this._maxPtDist.setMaximum(distFilter.getMaxPointDistance());
108152         };
108153         BufferCurveMaximumDistanceFinder.prototype.computeMaxVertexDistance = function computeMaxVertexDistance (curve) {
108154           var distFilter = new MaxPointDistanceFilter(this._inputGeom);
108155           curve.apply(distFilter);
108156           this._maxPtDist.setMaximum(distFilter.getMaxPointDistance());
108157         };
108158         BufferCurveMaximumDistanceFinder.prototype.findDistance = function findDistance (bufferCurve) {
108159           this.computeMaxVertexDistance(bufferCurve);
108160           this.computeMaxMidpointDistance(bufferCurve);
108161           return this._maxPtDist.getDistance()
108162         };
108163         BufferCurveMaximumDistanceFinder.prototype.getDistancePoints = function getDistancePoints () {
108164           return this._maxPtDist
108165         };
108166         BufferCurveMaximumDistanceFinder.prototype.interfaces_ = function interfaces_ () {
108167           return []
108168         };
108169         BufferCurveMaximumDistanceFinder.prototype.getClass = function getClass () {
108170           return BufferCurveMaximumDistanceFinder
108171         };
108172         staticAccessors$36.MaxPointDistanceFilter.get = function () { return MaxPointDistanceFilter };
108173         staticAccessors$36.MaxMidpointDistanceFilter.get = function () { return MaxMidpointDistanceFilter };
108174
108175         Object.defineProperties( BufferCurveMaximumDistanceFinder, staticAccessors$36 );
108176
108177         var MaxPointDistanceFilter = function MaxPointDistanceFilter (geom) {
108178           this._maxPtDist = new PointPairDistance();
108179           this._minPtDist = new PointPairDistance();
108180           this._geom = geom || null;
108181         };
108182         MaxPointDistanceFilter.prototype.filter = function filter (pt) {
108183           this._minPtDist.initialize();
108184           DistanceToPointFinder.computeDistance(this._geom, pt, this._minPtDist);
108185           this._maxPtDist.setMaximum(this._minPtDist);
108186         };
108187         MaxPointDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () {
108188           return this._maxPtDist
108189         };
108190         MaxPointDistanceFilter.prototype.interfaces_ = function interfaces_ () {
108191           return [CoordinateFilter]
108192         };
108193         MaxPointDistanceFilter.prototype.getClass = function getClass () {
108194           return MaxPointDistanceFilter
108195         };
108196
108197         var MaxMidpointDistanceFilter = function MaxMidpointDistanceFilter (geom) {
108198           this._maxPtDist = new PointPairDistance();
108199           this._minPtDist = new PointPairDistance();
108200           this._geom = geom || null;
108201         };
108202         MaxMidpointDistanceFilter.prototype.filter = function filter (seq, index) {
108203           if (index === 0) { return null }
108204           var p0 = seq.getCoordinate(index - 1);
108205           var p1 = seq.getCoordinate(index);
108206           var midPt = new Coordinate((p0.x + p1.x) / 2, (p0.y + p1.y) / 2);
108207           this._minPtDist.initialize();
108208           DistanceToPointFinder.computeDistance(this._geom, midPt, this._minPtDist);
108209           this._maxPtDist.setMaximum(this._minPtDist);
108210         };
108211         MaxMidpointDistanceFilter.prototype.isDone = function isDone () {
108212           return false
108213         };
108214         MaxMidpointDistanceFilter.prototype.isGeometryChanged = function isGeometryChanged () {
108215           return false
108216         };
108217         MaxMidpointDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () {
108218           return this._maxPtDist
108219         };
108220         MaxMidpointDistanceFilter.prototype.interfaces_ = function interfaces_ () {
108221           return [CoordinateSequenceFilter]
108222         };
108223         MaxMidpointDistanceFilter.prototype.getClass = function getClass () {
108224           return MaxMidpointDistanceFilter
108225         };
108226
108227         var PolygonExtracter = function PolygonExtracter (comps) {
108228           this._comps = comps || null;
108229         };
108230         PolygonExtracter.prototype.filter = function filter (geom) {
108231           if (geom instanceof Polygon) { this._comps.add(geom); }
108232         };
108233         PolygonExtracter.prototype.interfaces_ = function interfaces_ () {
108234           return [GeometryFilter]
108235         };
108236         PolygonExtracter.prototype.getClass = function getClass () {
108237           return PolygonExtracter
108238         };
108239         PolygonExtracter.getPolygons = function getPolygons () {
108240           if (arguments.length === 1) {
108241             var geom = arguments[0];
108242             return PolygonExtracter.getPolygons(geom, new ArrayList())
108243           } else if (arguments.length === 2) {
108244             var geom$1 = arguments[0];
108245             var list = arguments[1];
108246             if (geom$1 instanceof Polygon) {
108247               list.add(geom$1);
108248             } else if (geom$1 instanceof GeometryCollection) {
108249               geom$1.apply(new PolygonExtracter(list));
108250             }
108251             return list
108252           }
108253         };
108254
108255         var LinearComponentExtracter = function LinearComponentExtracter () {
108256           this._lines = null;
108257           this._isForcedToLineString = false;
108258           if (arguments.length === 1) {
108259             var lines = arguments[0];
108260             this._lines = lines;
108261           } else if (arguments.length === 2) {
108262             var lines$1 = arguments[0];
108263             var isForcedToLineString = arguments[1];
108264             this._lines = lines$1;
108265             this._isForcedToLineString = isForcedToLineString;
108266           }
108267         };
108268         LinearComponentExtracter.prototype.filter = function filter (geom) {
108269           if (this._isForcedToLineString && geom instanceof LinearRing) {
108270             var line = geom.getFactory().createLineString(geom.getCoordinateSequence());
108271             this._lines.add(line);
108272             return null
108273           }
108274           if (geom instanceof LineString) { this._lines.add(geom); }
108275         };
108276         LinearComponentExtracter.prototype.setForceToLineString = function setForceToLineString (isForcedToLineString) {
108277           this._isForcedToLineString = isForcedToLineString;
108278         };
108279         LinearComponentExtracter.prototype.interfaces_ = function interfaces_ () {
108280           return [GeometryComponentFilter]
108281         };
108282         LinearComponentExtracter.prototype.getClass = function getClass () {
108283           return LinearComponentExtracter
108284         };
108285         LinearComponentExtracter.getGeometry = function getGeometry () {
108286           if (arguments.length === 1) {
108287             var geom = arguments[0];
108288             return geom.getFactory().buildGeometry(LinearComponentExtracter.getLines(geom))
108289           } else if (arguments.length === 2) {
108290             var geom$1 = arguments[0];
108291             var forceToLineString = arguments[1];
108292             return geom$1.getFactory().buildGeometry(LinearComponentExtracter.getLines(geom$1, forceToLineString))
108293           }
108294         };
108295         LinearComponentExtracter.getLines = function getLines () {
108296           if (arguments.length === 1) {
108297             var geom = arguments[0];
108298             return LinearComponentExtracter.getLines(geom, false)
108299           } else if (arguments.length === 2) {
108300             if (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], Collection)) {
108301               var geoms = arguments[0];
108302               var lines$1 = arguments[1];
108303               for (var i = geoms.iterator(); i.hasNext();) {
108304                 var g = i.next();
108305                 LinearComponentExtracter.getLines(g, lines$1);
108306               }
108307               return lines$1
108308             } else if (arguments[0] instanceof Geometry && typeof arguments[1] === 'boolean') {
108309               var geom$1 = arguments[0];
108310               var forceToLineString = arguments[1];
108311               var lines = new ArrayList();
108312               geom$1.apply(new LinearComponentExtracter(lines, forceToLineString));
108313               return lines
108314             } else if (arguments[0] instanceof Geometry && hasInterface(arguments[1], Collection)) {
108315               var geom$2 = arguments[0];
108316               var lines$2 = arguments[1];
108317               if (geom$2 instanceof LineString) {
108318                 lines$2.add(geom$2);
108319               } else {
108320                 geom$2.apply(new LinearComponentExtracter(lines$2));
108321               }
108322               return lines$2
108323             }
108324           } else if (arguments.length === 3) {
108325             if (typeof arguments[2] === 'boolean' && (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], Collection))) {
108326               var geoms$1 = arguments[0];
108327               var lines$3 = arguments[1];
108328               var forceToLineString$1 = arguments[2];
108329               for (var i$1 = geoms$1.iterator(); i$1.hasNext();) {
108330                 var g$1 = i$1.next();
108331                 LinearComponentExtracter.getLines(g$1, lines$3, forceToLineString$1);
108332               }
108333               return lines$3
108334             } else if (typeof arguments[2] === 'boolean' && (arguments[0] instanceof Geometry && hasInterface(arguments[1], Collection))) {
108335               var geom$3 = arguments[0];
108336               var lines$4 = arguments[1];
108337               var forceToLineString$2 = arguments[2];
108338               geom$3.apply(new LinearComponentExtracter(lines$4, forceToLineString$2));
108339               return lines$4
108340             }
108341           }
108342         };
108343
108344         var PointLocator = function PointLocator () {
108345           this._boundaryRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE;
108346           this._isIn = null;
108347           this._numBoundaries = null;
108348           if (arguments.length === 0) ; else if (arguments.length === 1) {
108349             var boundaryRule = arguments[0];
108350             if (boundaryRule === null) { throw new IllegalArgumentException('Rule must be non-null') }
108351             this._boundaryRule = boundaryRule;
108352           }
108353         };
108354         PointLocator.prototype.locateInternal = function locateInternal () {
108355             var this$1 = this;
108356
108357           if (arguments[0] instanceof Coordinate && arguments[1] instanceof Polygon) {
108358             var p = arguments[0];
108359             var poly = arguments[1];
108360             if (poly.isEmpty()) { return Location.EXTERIOR }
108361             var shell = poly.getExteriorRing();
108362             var shellLoc = this.locateInPolygonRing(p, shell);
108363             if (shellLoc === Location.EXTERIOR) { return Location.EXTERIOR }
108364             if (shellLoc === Location.BOUNDARY) { return Location.BOUNDARY }
108365             for (var i = 0; i < poly.getNumInteriorRing(); i++) {
108366               var hole = poly.getInteriorRingN(i);
108367               var holeLoc = this$1.locateInPolygonRing(p, hole);
108368               if (holeLoc === Location.INTERIOR) { return Location.EXTERIOR }
108369               if (holeLoc === Location.BOUNDARY) { return Location.BOUNDARY }
108370             }
108371             return Location.INTERIOR
108372           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof LineString) {
108373             var p$1 = arguments[0];
108374             var l = arguments[1];
108375             if (!l.getEnvelopeInternal().intersects(p$1)) { return Location.EXTERIOR }
108376             var pt = l.getCoordinates();
108377             if (!l.isClosed()) {
108378               if (p$1.equals(pt[0]) || p$1.equals(pt[pt.length - 1])) {
108379                 return Location.BOUNDARY
108380               }
108381             }
108382             if (CGAlgorithms.isOnLine(p$1, pt)) { return Location.INTERIOR }
108383             return Location.EXTERIOR
108384           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Point$1) {
108385             var p$2 = arguments[0];
108386             var pt$1 = arguments[1];
108387             var ptCoord = pt$1.getCoordinate();
108388             if (ptCoord.equals2D(p$2)) { return Location.INTERIOR }
108389             return Location.EXTERIOR
108390           }
108391         };
108392         PointLocator.prototype.locateInPolygonRing = function locateInPolygonRing (p, ring) {
108393           if (!ring.getEnvelopeInternal().intersects(p)) { return Location.EXTERIOR }
108394           return CGAlgorithms.locatePointInRing(p, ring.getCoordinates())
108395         };
108396         PointLocator.prototype.intersects = function intersects (p, geom) {
108397           return this.locate(p, geom) !== Location.EXTERIOR
108398         };
108399         PointLocator.prototype.updateLocationInfo = function updateLocationInfo (loc) {
108400           if (loc === Location.INTERIOR) { this._isIn = true; }
108401           if (loc === Location.BOUNDARY) { this._numBoundaries++; }
108402         };
108403         PointLocator.prototype.computeLocation = function computeLocation (p, geom) {
108404             var this$1 = this;
108405
108406           if (geom instanceof Point$1) {
108407             this.updateLocationInfo(this.locateInternal(p, geom));
108408           }
108409           if (geom instanceof LineString) {
108410             this.updateLocationInfo(this.locateInternal(p, geom));
108411           } else if (geom instanceof Polygon) {
108412             this.updateLocationInfo(this.locateInternal(p, geom));
108413           } else if (geom instanceof MultiLineString) {
108414             var ml = geom;
108415             for (var i = 0; i < ml.getNumGeometries(); i++) {
108416               var l = ml.getGeometryN(i);
108417               this$1.updateLocationInfo(this$1.locateInternal(p, l));
108418             }
108419           } else if (geom instanceof MultiPolygon) {
108420             var mpoly = geom;
108421             for (var i$1 = 0; i$1 < mpoly.getNumGeometries(); i$1++) {
108422               var poly = mpoly.getGeometryN(i$1);
108423               this$1.updateLocationInfo(this$1.locateInternal(p, poly));
108424             }
108425           } else if (geom instanceof GeometryCollection) {
108426             var geomi = new GeometryCollectionIterator(geom);
108427             while (geomi.hasNext()) {
108428               var g2 = geomi.next();
108429               if (g2 !== geom) { this$1.computeLocation(p, g2); }
108430             }
108431           }
108432         };
108433         PointLocator.prototype.locate = function locate (p, geom) {
108434           if (geom.isEmpty()) { return Location.EXTERIOR }
108435           if (geom instanceof LineString) {
108436             return this.locateInternal(p, geom)
108437           } else if (geom instanceof Polygon) {
108438             return this.locateInternal(p, geom)
108439           }
108440           this._isIn = false;
108441           this._numBoundaries = 0;
108442           this.computeLocation(p, geom);
108443           if (this._boundaryRule.isInBoundary(this._numBoundaries)) { return Location.BOUNDARY }
108444           if (this._numBoundaries > 0 || this._isIn) { return Location.INTERIOR }
108445           return Location.EXTERIOR
108446         };
108447         PointLocator.prototype.interfaces_ = function interfaces_ () {
108448           return []
108449         };
108450         PointLocator.prototype.getClass = function getClass () {
108451           return PointLocator
108452         };
108453
108454         var GeometryLocation = function GeometryLocation () {
108455           this._component = null;
108456           this._segIndex = null;
108457           this._pt = null;
108458           if (arguments.length === 2) {
108459             var component = arguments[0];
108460             var pt = arguments[1];
108461             GeometryLocation.call(this, component, GeometryLocation.INSIDE_AREA, pt);
108462           } else if (arguments.length === 3) {
108463             var component$1 = arguments[0];
108464             var segIndex = arguments[1];
108465             var pt$1 = arguments[2];
108466             this._component = component$1;
108467             this._segIndex = segIndex;
108468             this._pt = pt$1;
108469           }
108470         };
108471
108472         var staticAccessors$38 = { INSIDE_AREA: { configurable: true } };
108473         GeometryLocation.prototype.isInsideArea = function isInsideArea () {
108474           return this._segIndex === GeometryLocation.INSIDE_AREA
108475         };
108476         GeometryLocation.prototype.getCoordinate = function getCoordinate () {
108477           return this._pt
108478         };
108479         GeometryLocation.prototype.getGeometryComponent = function getGeometryComponent () {
108480           return this._component
108481         };
108482         GeometryLocation.prototype.getSegmentIndex = function getSegmentIndex () {
108483           return this._segIndex
108484         };
108485         GeometryLocation.prototype.interfaces_ = function interfaces_ () {
108486           return []
108487         };
108488         GeometryLocation.prototype.getClass = function getClass () {
108489           return GeometryLocation
108490         };
108491         staticAccessors$38.INSIDE_AREA.get = function () { return -1 };
108492
108493         Object.defineProperties( GeometryLocation, staticAccessors$38 );
108494
108495         var PointExtracter = function PointExtracter (pts) {
108496           this._pts = pts || null;
108497         };
108498         PointExtracter.prototype.filter = function filter (geom) {
108499           if (geom instanceof Point$1) { this._pts.add(geom); }
108500         };
108501         PointExtracter.prototype.interfaces_ = function interfaces_ () {
108502           return [GeometryFilter]
108503         };
108504         PointExtracter.prototype.getClass = function getClass () {
108505           return PointExtracter
108506         };
108507         PointExtracter.getPoints = function getPoints () {
108508           if (arguments.length === 1) {
108509             var geom = arguments[0];
108510             if (geom instanceof Point$1) {
108511               return Collections.singletonList(geom)
108512             }
108513             return PointExtracter.getPoints(geom, new ArrayList())
108514           } else if (arguments.length === 2) {
108515             var geom$1 = arguments[0];
108516             var list = arguments[1];
108517             if (geom$1 instanceof Point$1) {
108518               list.add(geom$1);
108519             } else if (geom$1 instanceof GeometryCollection) {
108520               geom$1.apply(new PointExtracter(list));
108521             }
108522             return list
108523           }
108524         };
108525
108526         var ConnectedElementLocationFilter = function ConnectedElementLocationFilter () {
108527           this._locations = null;
108528           var locations = arguments[0];
108529           this._locations = locations;
108530         };
108531         ConnectedElementLocationFilter.prototype.filter = function filter (geom) {
108532           if (geom instanceof Point$1 || geom instanceof LineString || geom instanceof Polygon) { this._locations.add(new GeometryLocation(geom, 0, geom.getCoordinate())); }
108533         };
108534         ConnectedElementLocationFilter.prototype.interfaces_ = function interfaces_ () {
108535           return [GeometryFilter]
108536         };
108537         ConnectedElementLocationFilter.prototype.getClass = function getClass () {
108538           return ConnectedElementLocationFilter
108539         };
108540         ConnectedElementLocationFilter.getLocations = function getLocations (geom) {
108541           var locations = new ArrayList();
108542           geom.apply(new ConnectedElementLocationFilter(locations));
108543           return locations
108544         };
108545
108546         var DistanceOp = function DistanceOp () {
108547           this._geom = null;
108548           this._terminateDistance = 0.0;
108549           this._ptLocator = new PointLocator();
108550           this._minDistanceLocation = null;
108551           this._minDistance = Double.MAX_VALUE;
108552           if (arguments.length === 2) {
108553             var g0 = arguments[0];
108554             var g1 = arguments[1];
108555             this._geom = [g0, g1];
108556             this._terminateDistance = 0.0;
108557           } else if (arguments.length === 3) {
108558             var g0$1 = arguments[0];
108559             var g1$1 = arguments[1];
108560             var terminateDistance = arguments[2];
108561             this._geom = new Array(2).fill(null);
108562             this._geom[0] = g0$1;
108563             this._geom[1] = g1$1;
108564             this._terminateDistance = terminateDistance;
108565           }
108566         };
108567         DistanceOp.prototype.computeContainmentDistance = function computeContainmentDistance () {
108568             var this$1 = this;
108569
108570           if (arguments.length === 0) {
108571             var locPtPoly = new Array(2).fill(null);
108572             this.computeContainmentDistance(0, locPtPoly);
108573             if (this._minDistance <= this._terminateDistance) { return null }
108574             this.computeContainmentDistance(1, locPtPoly);
108575           } else if (arguments.length === 2) {
108576             var polyGeomIndex = arguments[0];
108577             var locPtPoly$1 = arguments[1];
108578             var locationsIndex = 1 - polyGeomIndex;
108579             var polys = PolygonExtracter.getPolygons(this._geom[polyGeomIndex]);
108580             if (polys.size() > 0) {
108581               var insideLocs = ConnectedElementLocationFilter.getLocations(this._geom[locationsIndex]);
108582               this.computeContainmentDistance(insideLocs, polys, locPtPoly$1);
108583               if (this._minDistance <= this._terminateDistance) {
108584                 this._minDistanceLocation[locationsIndex] = locPtPoly$1[0];
108585                 this._minDistanceLocation[polyGeomIndex] = locPtPoly$1[1];
108586                 return null
108587               }
108588             }
108589           } else if (arguments.length === 3) {
108590             if (arguments[2] instanceof Array && (hasInterface(arguments[0], List) && hasInterface(arguments[1], List))) {
108591               var locs = arguments[0];
108592               var polys$1 = arguments[1];
108593               var locPtPoly$2 = arguments[2];
108594               for (var i = 0; i < locs.size(); i++) {
108595                 var loc = locs.get(i);
108596                 for (var j = 0; j < polys$1.size(); j++) {
108597                   this$1.computeContainmentDistance(loc, polys$1.get(j), locPtPoly$2);
108598                   if (this$1._minDistance <= this$1._terminateDistance) { return null }
108599                 }
108600               }
108601             } else if (arguments[2] instanceof Array && (arguments[0] instanceof GeometryLocation && arguments[1] instanceof Polygon)) {
108602               var ptLoc = arguments[0];
108603               var poly = arguments[1];
108604               var locPtPoly$3 = arguments[2];
108605               var pt = ptLoc.getCoordinate();
108606               if (Location.EXTERIOR !== this._ptLocator.locate(pt, poly)) {
108607                 this._minDistance = 0.0;
108608                 locPtPoly$3[0] = ptLoc;
108609                 locPtPoly$3[1] = new GeometryLocation(poly, pt);
108610
108611                 return null
108612               }
108613             }
108614           }
108615         };
108616         DistanceOp.prototype.computeMinDistanceLinesPoints = function computeMinDistanceLinesPoints (lines, points, locGeom) {
108617             var this$1 = this;
108618
108619           for (var i = 0; i < lines.size(); i++) {
108620             var line = lines.get(i);
108621             for (var j = 0; j < points.size(); j++) {
108622               var pt = points.get(j);
108623               this$1.computeMinDistance(line, pt, locGeom);
108624               if (this$1._minDistance <= this$1._terminateDistance) { return null }
108625             }
108626           }
108627         };
108628         DistanceOp.prototype.computeFacetDistance = function computeFacetDistance () {
108629           var locGeom = new Array(2).fill(null);
108630           var lines0 = LinearComponentExtracter.getLines(this._geom[0]);
108631           var lines1 = LinearComponentExtracter.getLines(this._geom[1]);
108632           var pts0 = PointExtracter.getPoints(this._geom[0]);
108633           var pts1 = PointExtracter.getPoints(this._geom[1]);
108634           this.computeMinDistanceLines(lines0, lines1, locGeom);
108635           this.updateMinDistance(locGeom, false);
108636           if (this._minDistance <= this._terminateDistance) { return null }
108637           locGeom[0] = null;
108638           locGeom[1] = null;
108639           this.computeMinDistanceLinesPoints(lines0, pts1, locGeom);
108640           this.updateMinDistance(locGeom, false);
108641           if (this._minDistance <= this._terminateDistance) { return null }
108642           locGeom[0] = null;
108643           locGeom[1] = null;
108644           this.computeMinDistanceLinesPoints(lines1, pts0, locGeom);
108645           this.updateMinDistance(locGeom, true);
108646           if (this._minDistance <= this._terminateDistance) { return null }
108647           locGeom[0] = null;
108648           locGeom[1] = null;
108649           this.computeMinDistancePoints(pts0, pts1, locGeom);
108650           this.updateMinDistance(locGeom, false);
108651         };
108652         DistanceOp.prototype.nearestLocations = function nearestLocations () {
108653           this.computeMinDistance();
108654           return this._minDistanceLocation
108655         };
108656         DistanceOp.prototype.updateMinDistance = function updateMinDistance (locGeom, flip) {
108657           if (locGeom[0] === null) { return null }
108658           if (flip) {
108659             this._minDistanceLocation[0] = locGeom[1];
108660             this._minDistanceLocation[1] = locGeom[0];
108661           } else {
108662             this._minDistanceLocation[0] = locGeom[0];
108663             this._minDistanceLocation[1] = locGeom[1];
108664           }
108665         };
108666         DistanceOp.prototype.nearestPoints = function nearestPoints () {
108667           this.computeMinDistance();
108668           var nearestPts = [this._minDistanceLocation[0].getCoordinate(), this._minDistanceLocation[1].getCoordinate()];
108669           return nearestPts
108670         };
108671         DistanceOp.prototype.computeMinDistance = function computeMinDistance () {
108672             var this$1 = this;
108673
108674           if (arguments.length === 0) {
108675             if (this._minDistanceLocation !== null) { return null }
108676             this._minDistanceLocation = new Array(2).fill(null);
108677             this.computeContainmentDistance();
108678             if (this._minDistance <= this._terminateDistance) { return null }
108679             this.computeFacetDistance();
108680           } else if (arguments.length === 3) {
108681             if (arguments[2] instanceof Array && (arguments[0] instanceof LineString && arguments[1] instanceof Point$1)) {
108682               var line = arguments[0];
108683               var pt = arguments[1];
108684               var locGeom = arguments[2];
108685               if (line.getEnvelopeInternal().distance(pt.getEnvelopeInternal()) > this._minDistance) { return null }
108686               var coord0 = line.getCoordinates();
108687               var coord = pt.getCoordinate();
108688               for (var i = 0; i < coord0.length - 1; i++) {
108689                 var dist = CGAlgorithms.distancePointLine(coord, coord0[i], coord0[i + 1]);
108690                 if (dist < this$1._minDistance) {
108691                   this$1._minDistance = dist;
108692                   var seg = new LineSegment(coord0[i], coord0[i + 1]);
108693                   var segClosestPoint = seg.closestPoint(coord);
108694                   locGeom[0] = new GeometryLocation(line, i, segClosestPoint);
108695                   locGeom[1] = new GeometryLocation(pt, 0, coord);
108696                 }
108697                 if (this$1._minDistance <= this$1._terminateDistance) { return null }
108698               }
108699             } else if (arguments[2] instanceof Array && (arguments[0] instanceof LineString && arguments[1] instanceof LineString)) {
108700               var line0 = arguments[0];
108701               var line1 = arguments[1];
108702               var locGeom$1 = arguments[2];
108703               if (line0.getEnvelopeInternal().distance(line1.getEnvelopeInternal()) > this._minDistance) { return null }
108704               var coord0$1 = line0.getCoordinates();
108705               var coord1 = line1.getCoordinates();
108706               for (var i$1 = 0; i$1 < coord0$1.length - 1; i$1++) {
108707                 for (var j = 0; j < coord1.length - 1; j++) {
108708                   var dist$1 = CGAlgorithms.distanceLineLine(coord0$1[i$1], coord0$1[i$1 + 1], coord1[j], coord1[j + 1]);
108709                   if (dist$1 < this$1._minDistance) {
108710                     this$1._minDistance = dist$1;
108711                     var seg0 = new LineSegment(coord0$1[i$1], coord0$1[i$1 + 1]);
108712                     var seg1 = new LineSegment(coord1[j], coord1[j + 1]);
108713                     var closestPt = seg0.closestPoints(seg1);
108714                     locGeom$1[0] = new GeometryLocation(line0, i$1, closestPt[0]);
108715                     locGeom$1[1] = new GeometryLocation(line1, j, closestPt[1]);
108716                   }
108717                   if (this$1._minDistance <= this$1._terminateDistance) { return null }
108718                 }
108719               }
108720             }
108721           }
108722         };
108723         DistanceOp.prototype.computeMinDistancePoints = function computeMinDistancePoints (points0, points1, locGeom) {
108724             var this$1 = this;
108725
108726           for (var i = 0; i < points0.size(); i++) {
108727             var pt0 = points0.get(i);
108728             for (var j = 0; j < points1.size(); j++) {
108729               var pt1 = points1.get(j);
108730               var dist = pt0.getCoordinate().distance(pt1.getCoordinate());
108731               if (dist < this$1._minDistance) {
108732                 this$1._minDistance = dist;
108733                 locGeom[0] = new GeometryLocation(pt0, 0, pt0.getCoordinate());
108734                 locGeom[1] = new GeometryLocation(pt1, 0, pt1.getCoordinate());
108735               }
108736               if (this$1._minDistance <= this$1._terminateDistance) { return null }
108737             }
108738           }
108739         };
108740         DistanceOp.prototype.distance = function distance () {
108741           if (this._geom[0] === null || this._geom[1] === null) { throw new IllegalArgumentException('null geometries are not supported') }
108742           if (this._geom[0].isEmpty() || this._geom[1].isEmpty()) { return 0.0 }
108743           this.computeMinDistance();
108744           return this._minDistance
108745         };
108746         DistanceOp.prototype.computeMinDistanceLines = function computeMinDistanceLines (lines0, lines1, locGeom) {
108747             var this$1 = this;
108748
108749           for (var i = 0; i < lines0.size(); i++) {
108750             var line0 = lines0.get(i);
108751             for (var j = 0; j < lines1.size(); j++) {
108752               var line1 = lines1.get(j);
108753               this$1.computeMinDistance(line0, line1, locGeom);
108754               if (this$1._minDistance <= this$1._terminateDistance) { return null }
108755             }
108756           }
108757         };
108758         DistanceOp.prototype.interfaces_ = function interfaces_ () {
108759           return []
108760         };
108761         DistanceOp.prototype.getClass = function getClass () {
108762           return DistanceOp
108763         };
108764         DistanceOp.distance = function distance (g0, g1) {
108765           var distOp = new DistanceOp(g0, g1);
108766           return distOp.distance()
108767         };
108768         DistanceOp.isWithinDistance = function isWithinDistance (g0, g1, distance) {
108769           var distOp = new DistanceOp(g0, g1, distance);
108770           return distOp.distance() <= distance
108771         };
108772         DistanceOp.nearestPoints = function nearestPoints (g0, g1) {
108773           var distOp = new DistanceOp(g0, g1);
108774           return distOp.nearestPoints()
108775         };
108776
108777         var PointPairDistance$2 = function PointPairDistance () {
108778           this._pt = [new Coordinate(), new Coordinate()];
108779           this._distance = Double.NaN;
108780           this._isNull = true;
108781         };
108782         PointPairDistance$2.prototype.getCoordinates = function getCoordinates () {
108783           return this._pt
108784         };
108785         PointPairDistance$2.prototype.getCoordinate = function getCoordinate (i) {
108786           return this._pt[i]
108787         };
108788         PointPairDistance$2.prototype.setMinimum = function setMinimum () {
108789           if (arguments.length === 1) {
108790             var ptDist = arguments[0];
108791             this.setMinimum(ptDist._pt[0], ptDist._pt[1]);
108792           } else if (arguments.length === 2) {
108793             var p0 = arguments[0];
108794             var p1 = arguments[1];
108795             if (this._isNull) {
108796               this.initialize(p0, p1);
108797               return null
108798             }
108799             var dist = p0.distance(p1);
108800             if (dist < this._distance) { this.initialize(p0, p1, dist); }
108801           }
108802         };
108803         PointPairDistance$2.prototype.initialize = function initialize () {
108804           if (arguments.length === 0) {
108805             this._isNull = true;
108806           } else if (arguments.length === 2) {
108807             var p0 = arguments[0];
108808             var p1 = arguments[1];
108809             this._pt[0].setCoordinate(p0);
108810             this._pt[1].setCoordinate(p1);
108811             this._distance = p0.distance(p1);
108812             this._isNull = false;
108813           } else if (arguments.length === 3) {
108814             var p0$1 = arguments[0];
108815             var p1$1 = arguments[1];
108816             var distance = arguments[2];
108817             this._pt[0].setCoordinate(p0$1);
108818             this._pt[1].setCoordinate(p1$1);
108819             this._distance = distance;
108820             this._isNull = false;
108821           }
108822         };
108823         PointPairDistance$2.prototype.toString = function toString () {
108824           return WKTWriter.toLineString(this._pt[0], this._pt[1])
108825         };
108826         PointPairDistance$2.prototype.getDistance = function getDistance () {
108827           return this._distance
108828         };
108829         PointPairDistance$2.prototype.setMaximum = function setMaximum () {
108830           if (arguments.length === 1) {
108831             var ptDist = arguments[0];
108832             this.setMaximum(ptDist._pt[0], ptDist._pt[1]);
108833           } else if (arguments.length === 2) {
108834             var p0 = arguments[0];
108835             var p1 = arguments[1];
108836             if (this._isNull) {
108837               this.initialize(p0, p1);
108838               return null
108839             }
108840             var dist = p0.distance(p1);
108841             if (dist > this._distance) { this.initialize(p0, p1, dist); }
108842           }
108843         };
108844         PointPairDistance$2.prototype.interfaces_ = function interfaces_ () {
108845           return []
108846         };
108847         PointPairDistance$2.prototype.getClass = function getClass () {
108848           return PointPairDistance$2
108849         };
108850
108851         var DistanceToPoint = function DistanceToPoint () {};
108852
108853         DistanceToPoint.prototype.interfaces_ = function interfaces_ () {
108854           return []
108855         };
108856         DistanceToPoint.prototype.getClass = function getClass () {
108857           return DistanceToPoint
108858         };
108859         DistanceToPoint.computeDistance = function computeDistance () {
108860           if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof LineString && arguments[1] instanceof Coordinate)) {
108861             var line = arguments[0];
108862             var pt = arguments[1];
108863             var ptDist = arguments[2];
108864             var tempSegment = new LineSegment();
108865             var coords = line.getCoordinates();
108866             for (var i = 0; i < coords.length - 1; i++) {
108867               tempSegment.setCoordinates(coords[i], coords[i + 1]);
108868               var closestPt = tempSegment.closestPoint(pt);
108869               ptDist.setMinimum(closestPt, pt);
108870             }
108871           } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof Polygon && arguments[1] instanceof Coordinate)) {
108872             var poly = arguments[0];
108873             var pt$1 = arguments[1];
108874             var ptDist$1 = arguments[2];
108875             DistanceToPoint.computeDistance(poly.getExteriorRing(), pt$1, ptDist$1);
108876             for (var i$1 = 0; i$1 < poly.getNumInteriorRing(); i$1++) {
108877               DistanceToPoint.computeDistance(poly.getInteriorRingN(i$1), pt$1, ptDist$1);
108878             }
108879           } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof Geometry && arguments[1] instanceof Coordinate)) {
108880             var geom = arguments[0];
108881             var pt$2 = arguments[1];
108882             var ptDist$2 = arguments[2];
108883             if (geom instanceof LineString) {
108884               DistanceToPoint.computeDistance(geom, pt$2, ptDist$2);
108885             } else if (geom instanceof Polygon) {
108886               DistanceToPoint.computeDistance(geom, pt$2, ptDist$2);
108887             } else if (geom instanceof GeometryCollection) {
108888               var gc = geom;
108889               for (var i$2 = 0; i$2 < gc.getNumGeometries(); i$2++) {
108890                 var g = gc.getGeometryN(i$2);
108891                 DistanceToPoint.computeDistance(g, pt$2, ptDist$2);
108892               }
108893             } else {
108894               ptDist$2.setMinimum(geom.getCoordinate(), pt$2);
108895             }
108896           } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof LineSegment && arguments[1] instanceof Coordinate)) {
108897             var segment = arguments[0];
108898             var pt$3 = arguments[1];
108899             var ptDist$3 = arguments[2];
108900             var closestPt$1 = segment.closestPoint(pt$3);
108901             ptDist$3.setMinimum(closestPt$1, pt$3);
108902           }
108903         };
108904
108905         var DiscreteHausdorffDistance = function DiscreteHausdorffDistance () {
108906           this._g0 = null;
108907           this._g1 = null;
108908           this._ptDist = new PointPairDistance$2();
108909           this._densifyFrac = 0.0;
108910           var g0 = arguments[0];
108911           var g1 = arguments[1];
108912           this._g0 = g0;
108913           this._g1 = g1;
108914         };
108915
108916         var staticAccessors$39 = { MaxPointDistanceFilter: { configurable: true },MaxDensifiedByFractionDistanceFilter: { configurable: true } };
108917         DiscreteHausdorffDistance.prototype.getCoordinates = function getCoordinates () {
108918           return this._ptDist.getCoordinates()
108919         };
108920         DiscreteHausdorffDistance.prototype.setDensifyFraction = function setDensifyFraction (densifyFrac) {
108921           if (densifyFrac > 1.0 || densifyFrac <= 0.0) { throw new IllegalArgumentException('Fraction is not in range (0.0 - 1.0]') }
108922           this._densifyFrac = densifyFrac;
108923         };
108924         DiscreteHausdorffDistance.prototype.compute = function compute (g0, g1) {
108925           this.computeOrientedDistance(g0, g1, this._ptDist);
108926           this.computeOrientedDistance(g1, g0, this._ptDist);
108927         };
108928         DiscreteHausdorffDistance.prototype.distance = function distance () {
108929           this.compute(this._g0, this._g1);
108930           return this._ptDist.getDistance()
108931         };
108932         DiscreteHausdorffDistance.prototype.computeOrientedDistance = function computeOrientedDistance (discreteGeom, geom, ptDist) {
108933           var distFilter = new MaxPointDistanceFilter$1(geom);
108934           discreteGeom.apply(distFilter);
108935           ptDist.setMaximum(distFilter.getMaxPointDistance());
108936           if (this._densifyFrac > 0) {
108937             var fracFilter = new MaxDensifiedByFractionDistanceFilter(geom, this._densifyFrac);
108938             discreteGeom.apply(fracFilter);
108939             ptDist.setMaximum(fracFilter.getMaxPointDistance());
108940           }
108941         };
108942         DiscreteHausdorffDistance.prototype.orientedDistance = function orientedDistance () {
108943           this.computeOrientedDistance(this._g0, this._g1, this._ptDist);
108944           return this._ptDist.getDistance()
108945         };
108946         DiscreteHausdorffDistance.prototype.interfaces_ = function interfaces_ () {
108947           return []
108948         };
108949         DiscreteHausdorffDistance.prototype.getClass = function getClass () {
108950           return DiscreteHausdorffDistance
108951         };
108952         DiscreteHausdorffDistance.distance = function distance () {
108953           if (arguments.length === 2) {
108954             var g0 = arguments[0];
108955             var g1 = arguments[1];
108956             var dist = new DiscreteHausdorffDistance(g0, g1);
108957             return dist.distance()
108958           } else if (arguments.length === 3) {
108959             var g0$1 = arguments[0];
108960             var g1$1 = arguments[1];
108961             var densifyFrac = arguments[2];
108962             var dist$1 = new DiscreteHausdorffDistance(g0$1, g1$1);
108963             dist$1.setDensifyFraction(densifyFrac);
108964             return dist$1.distance()
108965           }
108966         };
108967         staticAccessors$39.MaxPointDistanceFilter.get = function () { return MaxPointDistanceFilter$1 };
108968         staticAccessors$39.MaxDensifiedByFractionDistanceFilter.get = function () { return MaxDensifiedByFractionDistanceFilter };
108969
108970         Object.defineProperties( DiscreteHausdorffDistance, staticAccessors$39 );
108971
108972         var MaxPointDistanceFilter$1 = function MaxPointDistanceFilter () {
108973           this._maxPtDist = new PointPairDistance$2();
108974           this._minPtDist = new PointPairDistance$2();
108975           this._euclideanDist = new DistanceToPoint();
108976           this._geom = null;
108977           var geom = arguments[0];
108978           this._geom = geom;
108979         };
108980         MaxPointDistanceFilter$1.prototype.filter = function filter (pt) {
108981           this._minPtDist.initialize();
108982           DistanceToPoint.computeDistance(this._geom, pt, this._minPtDist);
108983           this._maxPtDist.setMaximum(this._minPtDist);
108984         };
108985         MaxPointDistanceFilter$1.prototype.getMaxPointDistance = function getMaxPointDistance () {
108986           return this._maxPtDist
108987         };
108988         MaxPointDistanceFilter$1.prototype.interfaces_ = function interfaces_ () {
108989           return [CoordinateFilter]
108990         };
108991         MaxPointDistanceFilter$1.prototype.getClass = function getClass () {
108992           return MaxPointDistanceFilter$1
108993         };
108994
108995         var MaxDensifiedByFractionDistanceFilter = function MaxDensifiedByFractionDistanceFilter () {
108996           this._maxPtDist = new PointPairDistance$2();
108997           this._minPtDist = new PointPairDistance$2();
108998           this._geom = null;
108999           this._numSubSegs = 0;
109000           var geom = arguments[0];
109001           var fraction = arguments[1];
109002           this._geom = geom;
109003           this._numSubSegs = Math.trunc(Math.round(1.0 / fraction));
109004         };
109005         MaxDensifiedByFractionDistanceFilter.prototype.filter = function filter (seq, index) {
109006             var this$1 = this;
109007
109008           if (index === 0) { return null }
109009           var p0 = seq.getCoordinate(index - 1);
109010           var p1 = seq.getCoordinate(index);
109011           var delx = (p1.x - p0.x) / this._numSubSegs;
109012           var dely = (p1.y - p0.y) / this._numSubSegs;
109013           for (var i = 0; i < this._numSubSegs; i++) {
109014             var x = p0.x + i * delx;
109015             var y = p0.y + i * dely;
109016             var pt = new Coordinate(x, y);
109017             this$1._minPtDist.initialize();
109018             DistanceToPoint.computeDistance(this$1._geom, pt, this$1._minPtDist);
109019             this$1._maxPtDist.setMaximum(this$1._minPtDist);
109020           }
109021         };
109022         MaxDensifiedByFractionDistanceFilter.prototype.isDone = function isDone () {
109023           return false
109024         };
109025         MaxDensifiedByFractionDistanceFilter.prototype.isGeometryChanged = function isGeometryChanged () {
109026           return false
109027         };
109028         MaxDensifiedByFractionDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () {
109029           return this._maxPtDist
109030         };
109031         MaxDensifiedByFractionDistanceFilter.prototype.interfaces_ = function interfaces_ () {
109032           return [CoordinateSequenceFilter]
109033         };
109034         MaxDensifiedByFractionDistanceFilter.prototype.getClass = function getClass () {
109035           return MaxDensifiedByFractionDistanceFilter
109036         };
109037
109038         var BufferDistanceValidator = function BufferDistanceValidator (input, bufDistance, result) {
109039           this._minValidDistance = null;
109040           this._maxValidDistance = null;
109041           this._minDistanceFound = null;
109042           this._maxDistanceFound = null;
109043           this._isValid = true;
109044           this._errMsg = null;
109045           this._errorLocation = null;
109046           this._errorIndicator = null;
109047           this._input = input || null;
109048           this._bufDistance = bufDistance || null;
109049           this._result = result || null;
109050         };
109051
109052         var staticAccessors$37 = { VERBOSE: { configurable: true },MAX_DISTANCE_DIFF_FRAC: { configurable: true } };
109053         BufferDistanceValidator.prototype.checkMaximumDistance = function checkMaximumDistance (input, bufCurve, maxDist) {
109054           var haus = new DiscreteHausdorffDistance(bufCurve, input);
109055           haus.setDensifyFraction(0.25);
109056           this._maxDistanceFound = haus.orientedDistance();
109057           if (this._maxDistanceFound > maxDist) {
109058             this._isValid = false;
109059             var pts = haus.getCoordinates();
109060             this._errorLocation = pts[1];
109061             this._errorIndicator = input.getFactory().createLineString(pts);
109062             this._errMsg = 'Distance between buffer curve and input is too large (' + this._maxDistanceFound + ' at ' + WKTWriter.toLineString(pts[0], pts[1]) + ')';
109063           }
109064         };
109065         BufferDistanceValidator.prototype.isValid = function isValid () {
109066           var posDistance = Math.abs(this._bufDistance);
109067           var distDelta = BufferDistanceValidator.MAX_DISTANCE_DIFF_FRAC * posDistance;
109068           this._minValidDistance = posDistance - distDelta;
109069           this._maxValidDistance = posDistance + distDelta;
109070           if (this._input.isEmpty() || this._result.isEmpty()) { return true }
109071           if (this._bufDistance > 0.0) {
109072             this.checkPositiveValid();
109073           } else {
109074             this.checkNegativeValid();
109075           }
109076           if (BufferDistanceValidator.VERBOSE) {
109077             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));
109078           }
109079           return this._isValid
109080         };
109081         BufferDistanceValidator.prototype.checkNegativeValid = function checkNegativeValid () {
109082           if (!(this._input instanceof Polygon || this._input instanceof MultiPolygon || this._input instanceof GeometryCollection)) {
109083             return null
109084           }
109085           var inputCurve = this.getPolygonLines(this._input);
109086           this.checkMinimumDistance(inputCurve, this._result, this._minValidDistance);
109087           if (!this._isValid) { return null }
109088           this.checkMaximumDistance(inputCurve, this._result, this._maxValidDistance);
109089         };
109090         BufferDistanceValidator.prototype.getErrorIndicator = function getErrorIndicator () {
109091           return this._errorIndicator
109092         };
109093         BufferDistanceValidator.prototype.checkMinimumDistance = function checkMinimumDistance (g1, g2, minDist) {
109094           var distOp = new DistanceOp(g1, g2, minDist);
109095           this._minDistanceFound = distOp.distance();
109096           if (this._minDistanceFound < minDist) {
109097             this._isValid = false;
109098             var pts = distOp.nearestPoints();
109099             this._errorLocation = distOp.nearestPoints()[1];
109100             this._errorIndicator = g1.getFactory().createLineString(pts);
109101             this._errMsg = 'Distance between buffer curve and input is too small (' + this._minDistanceFound + ' at ' + WKTWriter.toLineString(pts[0], pts[1]) + ' )';
109102           }
109103         };
109104         BufferDistanceValidator.prototype.checkPositiveValid = function checkPositiveValid () {
109105           var bufCurve = this._result.getBoundary();
109106           this.checkMinimumDistance(this._input, bufCurve, this._minValidDistance);
109107           if (!this._isValid) { return null }
109108           this.checkMaximumDistance(this._input, bufCurve, this._maxValidDistance);
109109         };
109110         BufferDistanceValidator.prototype.getErrorLocation = function getErrorLocation () {
109111           return this._errorLocation
109112         };
109113         BufferDistanceValidator.prototype.getPolygonLines = function getPolygonLines (g) {
109114           var lines = new ArrayList();
109115           var lineExtracter = new LinearComponentExtracter(lines);
109116           var polys = PolygonExtracter.getPolygons(g);
109117           for (var i = polys.iterator(); i.hasNext();) {
109118             var poly = i.next();
109119             poly.apply(lineExtracter);
109120           }
109121           return g.getFactory().buildGeometry(lines)
109122         };
109123         BufferDistanceValidator.prototype.getErrorMessage = function getErrorMessage () {
109124           return this._errMsg
109125         };
109126         BufferDistanceValidator.prototype.interfaces_ = function interfaces_ () {
109127           return []
109128         };
109129         BufferDistanceValidator.prototype.getClass = function getClass () {
109130           return BufferDistanceValidator
109131         };
109132         staticAccessors$37.VERBOSE.get = function () { return false };
109133         staticAccessors$37.MAX_DISTANCE_DIFF_FRAC.get = function () { return 0.012 };
109134
109135         Object.defineProperties( BufferDistanceValidator, staticAccessors$37 );
109136
109137         var BufferResultValidator = function BufferResultValidator (input, distance, result) {
109138           this._isValid = true;
109139           this._errorMsg = null;
109140           this._errorLocation = null;
109141           this._errorIndicator = null;
109142           this._input = input || null;
109143           this._distance = distance || null;
109144           this._result = result || null;
109145         };
109146
109147         var staticAccessors$40 = { VERBOSE: { configurable: true },MAX_ENV_DIFF_FRAC: { configurable: true } };
109148         BufferResultValidator.prototype.isValid = function isValid () {
109149           this.checkPolygonal();
109150           if (!this._isValid) { return this._isValid }
109151           this.checkExpectedEmpty();
109152           if (!this._isValid) { return this._isValid }
109153           this.checkEnvelope();
109154           if (!this._isValid) { return this._isValid }
109155           this.checkArea();
109156           if (!this._isValid) { return this._isValid }
109157           this.checkDistance();
109158           return this._isValid
109159         };
109160         BufferResultValidator.prototype.checkEnvelope = function checkEnvelope () {
109161           if (this._distance < 0.0) { return null }
109162           var padding = this._distance * BufferResultValidator.MAX_ENV_DIFF_FRAC;
109163           if (padding === 0.0) { padding = 0.001; }
109164           var expectedEnv = new Envelope(this._input.getEnvelopeInternal());
109165           expectedEnv.expandBy(this._distance);
109166           var bufEnv = new Envelope(this._result.getEnvelopeInternal());
109167           bufEnv.expandBy(padding);
109168           if (!bufEnv.contains(expectedEnv)) {
109169             this._isValid = false;
109170             this._errorMsg = 'Buffer envelope is incorrect';
109171             this._errorIndicator = this._input.getFactory().toGeometry(bufEnv);
109172           }
109173           this.report('Envelope');
109174         };
109175         BufferResultValidator.prototype.checkDistance = function checkDistance () {
109176           var distValid = new BufferDistanceValidator(this._input, this._distance, this._result);
109177           if (!distValid.isValid()) {
109178             this._isValid = false;
109179             this._errorMsg = distValid.getErrorMessage();
109180             this._errorLocation = distValid.getErrorLocation();
109181             this._errorIndicator = distValid.getErrorIndicator();
109182           }
109183           this.report('Distance');
109184         };
109185         BufferResultValidator.prototype.checkArea = function checkArea () {
109186           var inputArea = this._input.getArea();
109187           var resultArea = this._result.getArea();
109188           if (this._distance > 0.0 && inputArea > resultArea) {
109189             this._isValid = false;
109190             this._errorMsg = 'Area of positive buffer is smaller than input';
109191             this._errorIndicator = this._result;
109192           }
109193           if (this._distance < 0.0 && inputArea < resultArea) {
109194             this._isValid = false;
109195             this._errorMsg = 'Area of negative buffer is larger than input';
109196             this._errorIndicator = this._result;
109197           }
109198           this.report('Area');
109199         };
109200         BufferResultValidator.prototype.checkPolygonal = function checkPolygonal () {
109201           if (!(this._result instanceof Polygon || this._result instanceof MultiPolygon)) { this._isValid = false; }
109202           this._errorMsg = 'Result is not polygonal';
109203           this._errorIndicator = this._result;
109204           this.report('Polygonal');
109205         };
109206         BufferResultValidator.prototype.getErrorIndicator = function getErrorIndicator () {
109207           return this._errorIndicator
109208         };
109209         BufferResultValidator.prototype.getErrorLocation = function getErrorLocation () {
109210           return this._errorLocation
109211         };
109212         BufferResultValidator.prototype.checkExpectedEmpty = function checkExpectedEmpty () {
109213           if (this._input.getDimension() >= 2) { return null }
109214           if (this._distance > 0.0) { return null }
109215           if (!this._result.isEmpty()) {
109216             this._isValid = false;
109217             this._errorMsg = 'Result is non-empty';
109218             this._errorIndicator = this._result;
109219           }
109220           this.report('ExpectedEmpty');
109221         };
109222         BufferResultValidator.prototype.report = function report (checkName) {
109223           if (!BufferResultValidator.VERBOSE) { return null }
109224           System.out.println('Check ' + checkName + ': ' + (this._isValid ? 'passed' : 'FAILED'));
109225         };
109226         BufferResultValidator.prototype.getErrorMessage = function getErrorMessage () {
109227           return this._errorMsg
109228         };
109229         BufferResultValidator.prototype.interfaces_ = function interfaces_ () {
109230           return []
109231         };
109232         BufferResultValidator.prototype.getClass = function getClass () {
109233           return BufferResultValidator
109234         };
109235         BufferResultValidator.isValidMsg = function isValidMsg (g, distance, result) {
109236           var validator = new BufferResultValidator(g, distance, result);
109237           if (!validator.isValid()) { return validator.getErrorMessage() }
109238           return null
109239         };
109240         BufferResultValidator.isValid = function isValid (g, distance, result) {
109241           var validator = new BufferResultValidator(g, distance, result);
109242           if (validator.isValid()) { return true }
109243           return false
109244         };
109245         staticAccessors$40.VERBOSE.get = function () { return false };
109246         staticAccessors$40.MAX_ENV_DIFF_FRAC.get = function () { return 0.012 };
109247
109248         Object.defineProperties( BufferResultValidator, staticAccessors$40 );
109249
109250         // operation.buffer
109251
109252         var BasicSegmentString = function BasicSegmentString () {
109253           this._pts = null;
109254           this._data = null;
109255           var pts = arguments[0];
109256           var data = arguments[1];
109257           this._pts = pts;
109258           this._data = data;
109259         };
109260         BasicSegmentString.prototype.getCoordinates = function getCoordinates () {
109261           return this._pts
109262         };
109263         BasicSegmentString.prototype.size = function size () {
109264           return this._pts.length
109265         };
109266         BasicSegmentString.prototype.getCoordinate = function getCoordinate (i) {
109267           return this._pts[i]
109268         };
109269         BasicSegmentString.prototype.isClosed = function isClosed () {
109270           return this._pts[0].equals(this._pts[this._pts.length - 1])
109271         };
109272         BasicSegmentString.prototype.getSegmentOctant = function getSegmentOctant (index) {
109273           if (index === this._pts.length - 1) { return -1 }
109274           return Octant.octant(this.getCoordinate(index), this.getCoordinate(index + 1))
109275         };
109276         BasicSegmentString.prototype.setData = function setData (data) {
109277           this._data = data;
109278         };
109279         BasicSegmentString.prototype.getData = function getData () {
109280           return this._data
109281         };
109282         BasicSegmentString.prototype.toString = function toString () {
109283           return WKTWriter.toLineString(new CoordinateArraySequence(this._pts))
109284         };
109285         BasicSegmentString.prototype.interfaces_ = function interfaces_ () {
109286           return [SegmentString]
109287         };
109288         BasicSegmentString.prototype.getClass = function getClass () {
109289           return BasicSegmentString
109290         };
109291
109292         var InteriorIntersectionFinder = function InteriorIntersectionFinder () {
109293           this._findAllIntersections = false;
109294           this._isCheckEndSegmentsOnly = false;
109295           this._li = null;
109296           this._interiorIntersection = null;
109297           this._intSegments = null;
109298           this._intersections = new ArrayList();
109299           this._intersectionCount = 0;
109300           this._keepIntersections = true;
109301           var li = arguments[0];
109302           this._li = li;
109303           this._interiorIntersection = null;
109304         };
109305         InteriorIntersectionFinder.prototype.getInteriorIntersection = function getInteriorIntersection () {
109306           return this._interiorIntersection
109307         };
109308         InteriorIntersectionFinder.prototype.setCheckEndSegmentsOnly = function setCheckEndSegmentsOnly (isCheckEndSegmentsOnly) {
109309           this._isCheckEndSegmentsOnly = isCheckEndSegmentsOnly;
109310         };
109311         InteriorIntersectionFinder.prototype.getIntersectionSegments = function getIntersectionSegments () {
109312           return this._intSegments
109313         };
109314         InteriorIntersectionFinder.prototype.count = function count () {
109315           return this._intersectionCount
109316         };
109317         InteriorIntersectionFinder.prototype.getIntersections = function getIntersections () {
109318           return this._intersections
109319         };
109320         InteriorIntersectionFinder.prototype.setFindAllIntersections = function setFindAllIntersections (findAllIntersections) {
109321           this._findAllIntersections = findAllIntersections;
109322         };
109323         InteriorIntersectionFinder.prototype.setKeepIntersections = function setKeepIntersections (keepIntersections) {
109324           this._keepIntersections = keepIntersections;
109325         };
109326         InteriorIntersectionFinder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {
109327           if (!this._findAllIntersections && this.hasIntersection()) { return null }
109328           if (e0 === e1 && segIndex0 === segIndex1) { return null }
109329           if (this._isCheckEndSegmentsOnly) {
109330             var isEndSegPresent = this.isEndSegment(e0, segIndex0) || this.isEndSegment(e1, segIndex1);
109331             if (!isEndSegPresent) { return null }
109332           }
109333           var p00 = e0.getCoordinates()[segIndex0];
109334           var p01 = e0.getCoordinates()[segIndex0 + 1];
109335           var p10 = e1.getCoordinates()[segIndex1];
109336           var p11 = e1.getCoordinates()[segIndex1 + 1];
109337           this._li.computeIntersection(p00, p01, p10, p11);
109338           if (this._li.hasIntersection()) {
109339             if (this._li.isInteriorIntersection()) {
109340               this._intSegments = new Array(4).fill(null);
109341               this._intSegments[0] = p00;
109342               this._intSegments[1] = p01;
109343               this._intSegments[2] = p10;
109344               this._intSegments[3] = p11;
109345               this._interiorIntersection = this._li.getIntersection(0);
109346               if (this._keepIntersections) { this._intersections.add(this._interiorIntersection); }
109347               this._intersectionCount++;
109348             }
109349           }
109350         };
109351         InteriorIntersectionFinder.prototype.isEndSegment = function isEndSegment (segStr, index) {
109352           if (index === 0) { return true }
109353           if (index >= segStr.size() - 2) { return true }
109354           return false
109355         };
109356         InteriorIntersectionFinder.prototype.hasIntersection = function hasIntersection () {
109357           return this._interiorIntersection !== null
109358         };
109359         InteriorIntersectionFinder.prototype.isDone = function isDone () {
109360           if (this._findAllIntersections) { return false }
109361           return this._interiorIntersection !== null
109362         };
109363         InteriorIntersectionFinder.prototype.interfaces_ = function interfaces_ () {
109364           return [SegmentIntersector]
109365         };
109366         InteriorIntersectionFinder.prototype.getClass = function getClass () {
109367           return InteriorIntersectionFinder
109368         };
109369         InteriorIntersectionFinder.createAllIntersectionsFinder = function createAllIntersectionsFinder (li) {
109370           var finder = new InteriorIntersectionFinder(li);
109371           finder.setFindAllIntersections(true);
109372           return finder
109373         };
109374         InteriorIntersectionFinder.createAnyIntersectionFinder = function createAnyIntersectionFinder (li) {
109375           return new InteriorIntersectionFinder(li)
109376         };
109377         InteriorIntersectionFinder.createIntersectionCounter = function createIntersectionCounter (li) {
109378           var finder = new InteriorIntersectionFinder(li);
109379           finder.setFindAllIntersections(true);
109380           finder.setKeepIntersections(false);
109381           return finder
109382         };
109383
109384         var FastNodingValidator = function FastNodingValidator () {
109385           this._li = new RobustLineIntersector();
109386           this._segStrings = null;
109387           this._findAllIntersections = false;
109388           this._segInt = null;
109389           this._isValid = true;
109390           var segStrings = arguments[0];
109391           this._segStrings = segStrings;
109392         };
109393         FastNodingValidator.prototype.execute = function execute () {
109394           if (this._segInt !== null) { return null }
109395           this.checkInteriorIntersections();
109396         };
109397         FastNodingValidator.prototype.getIntersections = function getIntersections () {
109398           return this._segInt.getIntersections()
109399         };
109400         FastNodingValidator.prototype.isValid = function isValid () {
109401           this.execute();
109402           return this._isValid
109403         };
109404         FastNodingValidator.prototype.setFindAllIntersections = function setFindAllIntersections (findAllIntersections) {
109405           this._findAllIntersections = findAllIntersections;
109406         };
109407         FastNodingValidator.prototype.checkInteriorIntersections = function checkInteriorIntersections () {
109408           this._isValid = true;
109409           this._segInt = new InteriorIntersectionFinder(this._li);
109410           this._segInt.setFindAllIntersections(this._findAllIntersections);
109411           var noder = new MCIndexNoder();
109412           noder.setSegmentIntersector(this._segInt);
109413           noder.computeNodes(this._segStrings);
109414           if (this._segInt.hasIntersection()) {
109415             this._isValid = false;
109416             return null
109417           }
109418         };
109419         FastNodingValidator.prototype.checkValid = function checkValid () {
109420           this.execute();
109421           if (!this._isValid) { throw new TopologyException(this.getErrorMessage(), this._segInt.getInteriorIntersection()) }
109422         };
109423         FastNodingValidator.prototype.getErrorMessage = function getErrorMessage () {
109424           if (this._isValid) { return 'no intersections found' }
109425           var intSegs = this._segInt.getIntersectionSegments();
109426           return 'found non-noded intersection between ' + WKTWriter.toLineString(intSegs[0], intSegs[1]) + ' and ' + WKTWriter.toLineString(intSegs[2], intSegs[3])
109427         };
109428         FastNodingValidator.prototype.interfaces_ = function interfaces_ () {
109429           return []
109430         };
109431         FastNodingValidator.prototype.getClass = function getClass () {
109432           return FastNodingValidator
109433         };
109434         FastNodingValidator.computeIntersections = function computeIntersections (segStrings) {
109435           var nv = new FastNodingValidator(segStrings);
109436           nv.setFindAllIntersections(true);
109437           nv.isValid();
109438           return nv.getIntersections()
109439         };
109440
109441         var EdgeNodingValidator = function EdgeNodingValidator () {
109442           this._nv = null;
109443           var edges = arguments[0];
109444           this._nv = new FastNodingValidator(EdgeNodingValidator.toSegmentStrings(edges));
109445         };
109446         EdgeNodingValidator.prototype.checkValid = function checkValid () {
109447           this._nv.checkValid();
109448         };
109449         EdgeNodingValidator.prototype.interfaces_ = function interfaces_ () {
109450           return []
109451         };
109452         EdgeNodingValidator.prototype.getClass = function getClass () {
109453           return EdgeNodingValidator
109454         };
109455         EdgeNodingValidator.toSegmentStrings = function toSegmentStrings (edges) {
109456           var segStrings = new ArrayList();
109457           for (var i = edges.iterator(); i.hasNext();) {
109458             var e = i.next();
109459             segStrings.add(new BasicSegmentString(e.getCoordinates(), e));
109460           }
109461           return segStrings
109462         };
109463         EdgeNodingValidator.checkValid = function checkValid (edges) {
109464           var validator = new EdgeNodingValidator(edges);
109465           validator.checkValid();
109466         };
109467
109468         var GeometryCollectionMapper = function GeometryCollectionMapper (mapOp) {
109469           this._mapOp = mapOp;
109470         };
109471         GeometryCollectionMapper.prototype.map = function map (gc) {
109472             var this$1 = this;
109473
109474           var mapped = new ArrayList();
109475           for (var i = 0; i < gc.getNumGeometries(); i++) {
109476             var g = this$1._mapOp.map(gc.getGeometryN(i));
109477             if (!g.isEmpty()) { mapped.add(g); }
109478           }
109479           return gc.getFactory().createGeometryCollection(GeometryFactory.toGeometryArray(mapped))
109480         };
109481         GeometryCollectionMapper.prototype.interfaces_ = function interfaces_ () {
109482           return []
109483         };
109484         GeometryCollectionMapper.prototype.getClass = function getClass () {
109485           return GeometryCollectionMapper
109486         };
109487         GeometryCollectionMapper.map = function map (gc, op) {
109488           var mapper = new GeometryCollectionMapper(op);
109489           return mapper.map(gc)
109490         };
109491
109492         var LineBuilder = function LineBuilder () {
109493           this._op = null;
109494           this._geometryFactory = null;
109495           this._ptLocator = null;
109496           this._lineEdgesList = new ArrayList();
109497           this._resultLineList = new ArrayList();
109498           var op = arguments[0];
109499           var geometryFactory = arguments[1];
109500           var ptLocator = arguments[2];
109501           this._op = op;
109502           this._geometryFactory = geometryFactory;
109503           this._ptLocator = ptLocator;
109504         };
109505         LineBuilder.prototype.collectLines = function collectLines (opCode) {
109506             var this$1 = this;
109507
109508           for (var it = this._op.getGraph().getEdgeEnds().iterator(); it.hasNext();) {
109509             var de = it.next();
109510             this$1.collectLineEdge(de, opCode, this$1._lineEdgesList);
109511             this$1.collectBoundaryTouchEdge(de, opCode, this$1._lineEdgesList);
109512           }
109513         };
109514         LineBuilder.prototype.labelIsolatedLine = function labelIsolatedLine (e, targetIndex) {
109515           var loc = this._ptLocator.locate(e.getCoordinate(), this._op.getArgGeometry(targetIndex));
109516           e.getLabel().setLocation(targetIndex, loc);
109517         };
109518         LineBuilder.prototype.build = function build (opCode) {
109519           this.findCoveredLineEdges();
109520           this.collectLines(opCode);
109521           this.buildLines(opCode);
109522           return this._resultLineList
109523         };
109524         LineBuilder.prototype.collectLineEdge = function collectLineEdge (de, opCode, edges) {
109525           var label = de.getLabel();
109526           var e = de.getEdge();
109527           if (de.isLineEdge()) {
109528             if (!de.isVisited() && OverlayOp.isResultOfOp(label, opCode) && !e.isCovered()) {
109529               edges.add(e);
109530               de.setVisitedEdge(true);
109531             }
109532           }
109533         };
109534         LineBuilder.prototype.findCoveredLineEdges = function findCoveredLineEdges () {
109535             var this$1 = this;
109536
109537           for (var nodeit = this._op.getGraph().getNodes().iterator(); nodeit.hasNext();) {
109538             var node = nodeit.next();
109539             node.getEdges().findCoveredLineEdges();
109540           }
109541           for (var it = this._op.getGraph().getEdgeEnds().iterator(); it.hasNext();) {
109542             var de = it.next();
109543             var e = de.getEdge();
109544             if (de.isLineEdge() && !e.isCoveredSet()) {
109545               var isCovered = this$1._op.isCoveredByA(de.getCoordinate());
109546               e.setCovered(isCovered);
109547             }
109548           }
109549         };
109550         LineBuilder.prototype.labelIsolatedLines = function labelIsolatedLines (edgesList) {
109551             var this$1 = this;
109552
109553           for (var it = edgesList.iterator(); it.hasNext();) {
109554             var e = it.next();
109555             var label = e.getLabel();
109556             if (e.isIsolated()) {
109557               if (label.isNull(0)) { this$1.labelIsolatedLine(e, 0); } else { this$1.labelIsolatedLine(e, 1); }
109558             }
109559           }
109560         };
109561         LineBuilder.prototype.buildLines = function buildLines (opCode) {
109562             var this$1 = this;
109563
109564           for (var it = this._lineEdgesList.iterator(); it.hasNext();) {
109565             var e = it.next();
109566             // const label = e.getLabel()
109567             var line = this$1._geometryFactory.createLineString(e.getCoordinates());
109568             this$1._resultLineList.add(line);
109569             e.setInResult(true);
109570           }
109571         };
109572         LineBuilder.prototype.collectBoundaryTouchEdge = function collectBoundaryTouchEdge (de, opCode, edges) {
109573           var label = de.getLabel();
109574           if (de.isLineEdge()) { return null }
109575           if (de.isVisited()) { return null }
109576           if (de.isInteriorAreaEdge()) { return null }
109577           if (de.getEdge().isInResult()) { return null }
109578           Assert.isTrue(!(de.isInResult() || de.getSym().isInResult()) || !de.getEdge().isInResult());
109579           if (OverlayOp.isResultOfOp(label, opCode) && opCode === OverlayOp.INTERSECTION) {
109580             edges.add(de.getEdge());
109581             de.setVisitedEdge(true);
109582           }
109583         };
109584         LineBuilder.prototype.interfaces_ = function interfaces_ () {
109585           return []
109586         };
109587         LineBuilder.prototype.getClass = function getClass () {
109588           return LineBuilder
109589         };
109590
109591         var PointBuilder = function PointBuilder () {
109592           this._op = null;
109593           this._geometryFactory = null;
109594           this._resultPointList = new ArrayList();
109595           var op = arguments[0];
109596           var geometryFactory = arguments[1];
109597           // const ptLocator = arguments[2]
109598           this._op = op;
109599           this._geometryFactory = geometryFactory;
109600         };
109601         PointBuilder.prototype.filterCoveredNodeToPoint = function filterCoveredNodeToPoint (n) {
109602           var coord = n.getCoordinate();
109603           if (!this._op.isCoveredByLA(coord)) {
109604             var pt = this._geometryFactory.createPoint(coord);
109605             this._resultPointList.add(pt);
109606           }
109607         };
109608         PointBuilder.prototype.extractNonCoveredResultNodes = function extractNonCoveredResultNodes (opCode) {
109609             var this$1 = this;
109610
109611           for (var nodeit = this._op.getGraph().getNodes().iterator(); nodeit.hasNext();) {
109612             var n = nodeit.next();
109613             if (n.isInResult()) { continue }
109614             if (n.isIncidentEdgeInResult()) { continue }
109615             if (n.getEdges().getDegree() === 0 || opCode === OverlayOp.INTERSECTION) {
109616               var label = n.getLabel();
109617               if (OverlayOp.isResultOfOp(label, opCode)) {
109618                 this$1.filterCoveredNodeToPoint(n);
109619               }
109620             }
109621           }
109622         };
109623         PointBuilder.prototype.build = function build (opCode) {
109624           this.extractNonCoveredResultNodes(opCode);
109625           return this._resultPointList
109626         };
109627         PointBuilder.prototype.interfaces_ = function interfaces_ () {
109628           return []
109629         };
109630         PointBuilder.prototype.getClass = function getClass () {
109631           return PointBuilder
109632         };
109633
109634         var GeometryTransformer = function GeometryTransformer () {
109635           this._inputGeom = null;
109636           this._factory = null;
109637           this._pruneEmptyGeometry = true;
109638           this._preserveGeometryCollectionType = true;
109639           this._preserveCollections = false;
109640           this._preserveType = false;
109641         };
109642         GeometryTransformer.prototype.transformPoint = function transformPoint (geom, parent) {
109643           return this._factory.createPoint(this.transformCoordinates(geom.getCoordinateSequence(), geom))
109644         };
109645         GeometryTransformer.prototype.transformPolygon = function transformPolygon (geom, parent) {
109646             var this$1 = this;
109647
109648           var isAllValidLinearRings = true;
109649           var shell = this.transformLinearRing(geom.getExteriorRing(), geom);
109650           if (shell === null || !(shell instanceof LinearRing) || shell.isEmpty()) { isAllValidLinearRings = false; }
109651           var holes = new ArrayList();
109652           for (var i = 0; i < geom.getNumInteriorRing(); i++) {
109653             var hole = this$1.transformLinearRing(geom.getInteriorRingN(i), geom);
109654             if (hole === null || hole.isEmpty()) {
109655               continue
109656             }
109657             if (!(hole instanceof LinearRing)) { isAllValidLinearRings = false; }
109658             holes.add(hole);
109659           }
109660           if (isAllValidLinearRings) { return this._factory.createPolygon(shell, holes.toArray([])); } else {
109661             var components = new ArrayList();
109662             if (shell !== null) { components.add(shell); }
109663             components.addAll(holes);
109664             return this._factory.buildGeometry(components)
109665           }
109666         };
109667         GeometryTransformer.prototype.createCoordinateSequence = function createCoordinateSequence (coords) {
109668           return this._factory.getCoordinateSequenceFactory().create(coords)
109669         };
109670         GeometryTransformer.prototype.getInputGeometry = function getInputGeometry () {
109671           return this._inputGeom
109672         };
109673         GeometryTransformer.prototype.transformMultiLineString = function transformMultiLineString (geom, parent) {
109674             var this$1 = this;
109675
109676           var transGeomList = new ArrayList();
109677           for (var i = 0; i < geom.getNumGeometries(); i++) {
109678             var transformGeom = this$1.transformLineString(geom.getGeometryN(i), geom);
109679             if (transformGeom === null) { continue }
109680             if (transformGeom.isEmpty()) { continue }
109681             transGeomList.add(transformGeom);
109682           }
109683           return this._factory.buildGeometry(transGeomList)
109684         };
109685         GeometryTransformer.prototype.transformCoordinates = function transformCoordinates (coords, parent) {
109686           return this.copy(coords)
109687         };
109688         GeometryTransformer.prototype.transformLineString = function transformLineString (geom, parent) {
109689           return this._factory.createLineString(this.transformCoordinates(geom.getCoordinateSequence(), geom))
109690         };
109691         GeometryTransformer.prototype.transformMultiPoint = function transformMultiPoint (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.transformPoint(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.transformMultiPolygon = function transformMultiPolygon (geom, parent) {
109704             var this$1 = this;
109705
109706           var transGeomList = new ArrayList();
109707           for (var i = 0; i < geom.getNumGeometries(); i++) {
109708             var transformGeom = this$1.transformPolygon(geom.getGeometryN(i), geom);
109709             if (transformGeom === null) { continue }
109710             if (transformGeom.isEmpty()) { continue }
109711             transGeomList.add(transformGeom);
109712           }
109713           return this._factory.buildGeometry(transGeomList)
109714         };
109715         GeometryTransformer.prototype.copy = function copy (seq) {
109716           return seq.copy()
109717         };
109718         GeometryTransformer.prototype.transformGeometryCollection = function transformGeometryCollection (geom, parent) {
109719             var this$1 = this;
109720
109721           var transGeomList = new ArrayList();
109722           for (var i = 0; i < geom.getNumGeometries(); i++) {
109723             var transformGeom = this$1.transform(geom.getGeometryN(i));
109724             if (transformGeom === null) { continue }
109725             if (this$1._pruneEmptyGeometry && transformGeom.isEmpty()) { continue }
109726             transGeomList.add(transformGeom);
109727           }
109728           if (this._preserveGeometryCollectionType) { return this._factory.createGeometryCollection(GeometryFactory.toGeometryArray(transGeomList)) }
109729           return this._factory.buildGeometry(transGeomList)
109730         };
109731         GeometryTransformer.prototype.transform = function transform (inputGeom) {
109732           this._inputGeom = inputGeom;
109733           this._factory = inputGeom.getFactory();
109734           if (inputGeom instanceof Point$1) { return this.transformPoint(inputGeom, null) }
109735           if (inputGeom instanceof MultiPoint) { return this.transformMultiPoint(inputGeom, null) }
109736           if (inputGeom instanceof LinearRing) { return this.transformLinearRing(inputGeom, null) }
109737           if (inputGeom instanceof LineString) { return this.transformLineString(inputGeom, null) }
109738           if (inputGeom instanceof MultiLineString) { return this.transformMultiLineString(inputGeom, null) }
109739           if (inputGeom instanceof Polygon) { return this.transformPolygon(inputGeom, null) }
109740           if (inputGeom instanceof MultiPolygon) { return this.transformMultiPolygon(inputGeom, null) }
109741           if (inputGeom instanceof GeometryCollection) { return this.transformGeometryCollection(inputGeom, null) }
109742           throw new IllegalArgumentException('Unknown Geometry subtype: ' + inputGeom.getClass().getName())
109743         };
109744         GeometryTransformer.prototype.transformLinearRing = function transformLinearRing (geom, parent) {
109745           var seq = this.transformCoordinates(geom.getCoordinateSequence(), geom);
109746           if (seq === null) { return this._factory.createLinearRing(null) }
109747           var seqSize = seq.size();
109748           if (seqSize > 0 && seqSize < 4 && !this._preserveType) { return this._factory.createLineString(seq) }
109749           return this._factory.createLinearRing(seq)
109750         };
109751         GeometryTransformer.prototype.interfaces_ = function interfaces_ () {
109752           return []
109753         };
109754         GeometryTransformer.prototype.getClass = function getClass () {
109755           return GeometryTransformer
109756         };
109757
109758         var LineStringSnapper = function LineStringSnapper () {
109759           this._snapTolerance = 0.0;
109760           this._srcPts = null;
109761           this._seg = new LineSegment();
109762           this._allowSnappingToSourceVertices = false;
109763           this._isClosed = false;
109764           if (arguments[0] instanceof LineString && typeof arguments[1] === 'number') {
109765             var srcLine = arguments[0];
109766             var snapTolerance = arguments[1];
109767             LineStringSnapper.call(this, srcLine.getCoordinates(), snapTolerance);
109768           } else if (arguments[0] instanceof Array && typeof arguments[1] === 'number') {
109769             var srcPts = arguments[0];
109770             var snapTolerance$1 = arguments[1];
109771             this._srcPts = srcPts;
109772             this._isClosed = LineStringSnapper.isClosed(srcPts);
109773             this._snapTolerance = snapTolerance$1;
109774           }
109775         };
109776         LineStringSnapper.prototype.snapVertices = function snapVertices (srcCoords, snapPts) {
109777             var this$1 = this;
109778
109779           var end = this._isClosed ? srcCoords.size() - 1 : srcCoords.size();
109780           for (var i = 0; i < end; i++) {
109781             var srcPt = srcCoords.get(i);
109782             var snapVert = this$1.findSnapForVertex(srcPt, snapPts);
109783             if (snapVert !== null) {
109784               srcCoords.set(i, new Coordinate(snapVert));
109785               if (i === 0 && this$1._isClosed) { srcCoords.set(srcCoords.size() - 1, new Coordinate(snapVert)); }
109786             }
109787           }
109788         };
109789         LineStringSnapper.prototype.findSnapForVertex = function findSnapForVertex (pt, snapPts) {
109790             var this$1 = this;
109791
109792           for (var i = 0; i < snapPts.length; i++) {
109793             if (pt.equals2D(snapPts[i])) { return null }
109794             if (pt.distance(snapPts[i]) < this$1._snapTolerance) { return snapPts[i] }
109795           }
109796           return null
109797         };
109798         LineStringSnapper.prototype.snapTo = function snapTo (snapPts) {
109799           var coordList = new CoordinateList(this._srcPts);
109800           this.snapVertices(coordList, snapPts);
109801           this.snapSegments(coordList, snapPts);
109802           var newPts = coordList.toCoordinateArray();
109803           return newPts
109804         };
109805         LineStringSnapper.prototype.snapSegments = function snapSegments (srcCoords, snapPts) {
109806             var this$1 = this;
109807
109808           if (snapPts.length === 0) { return null }
109809           var distinctPtCount = snapPts.length;
109810           if (snapPts[0].equals2D(snapPts[snapPts.length - 1])) { distinctPtCount = snapPts.length - 1; }
109811           for (var i = 0; i < distinctPtCount; i++) {
109812             var snapPt = snapPts[i];
109813             var index = this$1.findSegmentIndexToSnap(snapPt, srcCoords);
109814             if (index >= 0) {
109815               srcCoords.add(index + 1, new Coordinate(snapPt), false);
109816             }
109817           }
109818         };
109819         LineStringSnapper.prototype.findSegmentIndexToSnap = function findSegmentIndexToSnap (snapPt, srcCoords) {
109820             var this$1 = this;
109821
109822           var minDist = Double.MAX_VALUE;
109823           var snapIndex = -1;
109824           for (var i = 0; i < srcCoords.size() - 1; i++) {
109825             this$1._seg.p0 = srcCoords.get(i);
109826             this$1._seg.p1 = srcCoords.get(i + 1);
109827             if (this$1._seg.p0.equals2D(snapPt) || this$1._seg.p1.equals2D(snapPt)) {
109828               if (this$1._allowSnappingToSourceVertices) { continue; } else { return -1 }
109829             }
109830             var dist = this$1._seg.distance(snapPt);
109831             if (dist < this$1._snapTolerance && dist < minDist) {
109832               minDist = dist;
109833               snapIndex = i;
109834             }
109835           }
109836           return snapIndex
109837         };
109838         LineStringSnapper.prototype.setAllowSnappingToSourceVertices = function setAllowSnappingToSourceVertices (allowSnappingToSourceVertices) {
109839           this._allowSnappingToSourceVertices = allowSnappingToSourceVertices;
109840         };
109841         LineStringSnapper.prototype.interfaces_ = function interfaces_ () {
109842           return []
109843         };
109844         LineStringSnapper.prototype.getClass = function getClass () {
109845           return LineStringSnapper
109846         };
109847         LineStringSnapper.isClosed = function isClosed (pts) {
109848           if (pts.length <= 1) { return false }
109849           return pts[0].equals2D(pts[pts.length - 1])
109850         };
109851
109852         var GeometrySnapper = function GeometrySnapper (srcGeom) {
109853           this._srcGeom = srcGeom || null;
109854         };
109855
109856         var staticAccessors$41 = { SNAP_PRECISION_FACTOR: { configurable: true } };
109857         GeometrySnapper.prototype.snapTo = function snapTo (snapGeom, snapTolerance) {
109858           var snapPts = this.extractTargetCoordinates(snapGeom);
109859           var snapTrans = new SnapTransformer(snapTolerance, snapPts);
109860           return snapTrans.transform(this._srcGeom)
109861         };
109862         GeometrySnapper.prototype.snapToSelf = function snapToSelf (snapTolerance, cleanResult) {
109863           var snapPts = this.extractTargetCoordinates(this._srcGeom);
109864           var snapTrans = new SnapTransformer(snapTolerance, snapPts, true);
109865           var snappedGeom = snapTrans.transform(this._srcGeom);
109866           var result = snappedGeom;
109867           if (cleanResult && hasInterface(result, Polygonal)) {
109868             result = snappedGeom.buffer(0);
109869           }
109870           return result
109871         };
109872         GeometrySnapper.prototype.computeSnapTolerance = function computeSnapTolerance (ringPts) {
109873           var minSegLen = this.computeMinimumSegmentLength(ringPts);
109874           var snapTol = minSegLen / 10;
109875           return snapTol
109876         };
109877         GeometrySnapper.prototype.extractTargetCoordinates = function extractTargetCoordinates (g) {
109878           var ptSet = new TreeSet();
109879           var pts = g.getCoordinates();
109880           for (var i = 0; i < pts.length; i++) {
109881             ptSet.add(pts[i]);
109882           }
109883           return ptSet.toArray(new Array(0).fill(null))
109884         };
109885         GeometrySnapper.prototype.computeMinimumSegmentLength = function computeMinimumSegmentLength (pts) {
109886           var minSegLen = Double.MAX_VALUE;
109887           for (var i = 0; i < pts.length - 1; i++) {
109888             var segLen = pts[i].distance(pts[i + 1]);
109889             if (segLen < minSegLen) { minSegLen = segLen; }
109890           }
109891           return minSegLen
109892         };
109893         GeometrySnapper.prototype.interfaces_ = function interfaces_ () {
109894           return []
109895         };
109896         GeometrySnapper.prototype.getClass = function getClass () {
109897           return GeometrySnapper
109898         };
109899         GeometrySnapper.snap = function snap (g0, g1, snapTolerance) {
109900           var snapGeom = new Array(2).fill(null);
109901           var snapper0 = new GeometrySnapper(g0);
109902           snapGeom[0] = snapper0.snapTo(g1, snapTolerance);
109903           var snapper1 = new GeometrySnapper(g1);
109904           snapGeom[1] = snapper1.snapTo(snapGeom[0], snapTolerance);
109905           return snapGeom
109906         };
109907         GeometrySnapper.computeOverlaySnapTolerance = function computeOverlaySnapTolerance () {
109908           if (arguments.length === 1) {
109909             var g = arguments[0];
109910             var snapTolerance = GeometrySnapper.computeSizeBasedSnapTolerance(g);
109911             var pm = g.getPrecisionModel();
109912             if (pm.getType() === PrecisionModel.FIXED) {
109913               var fixedSnapTol = 1 / pm.getScale() * 2 / 1.415;
109914               if (fixedSnapTol > snapTolerance) { snapTolerance = fixedSnapTol; }
109915             }
109916             return snapTolerance
109917           } else if (arguments.length === 2) {
109918             var g0 = arguments[0];
109919             var g1 = arguments[1];
109920             return Math.min(GeometrySnapper.computeOverlaySnapTolerance(g0), GeometrySnapper.computeOverlaySnapTolerance(g1))
109921           }
109922         };
109923         GeometrySnapper.computeSizeBasedSnapTolerance = function computeSizeBasedSnapTolerance (g) {
109924           var env = g.getEnvelopeInternal();
109925           var minDimension = Math.min(env.getHeight(), env.getWidth());
109926           var snapTol = minDimension * GeometrySnapper.SNAP_PRECISION_FACTOR;
109927           return snapTol
109928         };
109929         GeometrySnapper.snapToSelf = function snapToSelf (geom, snapTolerance, cleanResult) {
109930           var snapper0 = new GeometrySnapper(geom);
109931           return snapper0.snapToSelf(snapTolerance, cleanResult)
109932         };
109933         staticAccessors$41.SNAP_PRECISION_FACTOR.get = function () { return 1e-9 };
109934
109935         Object.defineProperties( GeometrySnapper, staticAccessors$41 );
109936
109937         var SnapTransformer = (function (GeometryTransformer$$1) {
109938           function SnapTransformer (snapTolerance, snapPts, isSelfSnap) {
109939             GeometryTransformer$$1.call(this);
109940             this._snapTolerance = snapTolerance || null;
109941             this._snapPts = snapPts || null;
109942             this._isSelfSnap = (isSelfSnap !== undefined) ? isSelfSnap : false;
109943           }
109944
109945           if ( GeometryTransformer$$1 ) { SnapTransformer.__proto__ = GeometryTransformer$$1; }
109946           SnapTransformer.prototype = Object.create( GeometryTransformer$$1 && GeometryTransformer$$1.prototype );
109947           SnapTransformer.prototype.constructor = SnapTransformer;
109948           SnapTransformer.prototype.snapLine = function snapLine (srcPts, snapPts) {
109949             var snapper = new LineStringSnapper(srcPts, this._snapTolerance);
109950             snapper.setAllowSnappingToSourceVertices(this._isSelfSnap);
109951             return snapper.snapTo(snapPts)
109952           };
109953           SnapTransformer.prototype.transformCoordinates = function transformCoordinates (coords, parent) {
109954             var srcPts = coords.toCoordinateArray();
109955             var newPts = this.snapLine(srcPts, this._snapPts);
109956             return this._factory.getCoordinateSequenceFactory().create(newPts)
109957           };
109958           SnapTransformer.prototype.interfaces_ = function interfaces_ () {
109959             return []
109960           };
109961           SnapTransformer.prototype.getClass = function getClass () {
109962             return SnapTransformer
109963           };
109964
109965           return SnapTransformer;
109966         }(GeometryTransformer));
109967
109968         var CommonBits = function CommonBits () {
109969           this._isFirst = true;
109970           this._commonMantissaBitsCount = 53;
109971           this._commonBits = 0;
109972           this._commonSignExp = null;
109973         };
109974         CommonBits.prototype.getCommon = function getCommon () {
109975           return Double.longBitsToDouble(this._commonBits)
109976         };
109977         CommonBits.prototype.add = function add (num) {
109978           var numBits = Double.doubleToLongBits(num);
109979           if (this._isFirst) {
109980             this._commonBits = numBits;
109981             this._commonSignExp = CommonBits.signExpBits(this._commonBits);
109982             this._isFirst = false;
109983             return null
109984           }
109985           var numSignExp = CommonBits.signExpBits(numBits);
109986           if (numSignExp !== this._commonSignExp) {
109987             this._commonBits = 0;
109988             return null
109989           }
109990           this._commonMantissaBitsCount = CommonBits.numCommonMostSigMantissaBits(this._commonBits, numBits);
109991           this._commonBits = CommonBits.zeroLowerBits(this._commonBits, 64 - (12 + this._commonMantissaBitsCount));
109992         };
109993         CommonBits.prototype.toString = function toString () {
109994           if (arguments.length === 1) {
109995             var bits = arguments[0];
109996             var x = Double.longBitsToDouble(bits);
109997             var numStr = Double.toBinaryString(bits);
109998             var padStr = '0000000000000000000000000000000000000000000000000000000000000000' + numStr;
109999             var bitStr = padStr.substring(padStr.length - 64);
110000             var str = bitStr.substring(0, 1) + '  ' + bitStr.substring(1, 12) + '(exp) ' + bitStr.substring(12) + ' [ ' + x + ' ]';
110001             return str
110002           }
110003         };
110004         CommonBits.prototype.interfaces_ = function interfaces_ () {
110005           return []
110006         };
110007         CommonBits.prototype.getClass = function getClass () {
110008           return CommonBits
110009         };
110010         CommonBits.getBit = function getBit (bits, i) {
110011           var mask = 1 << i;
110012           return (bits & mask) !== 0 ? 1 : 0
110013         };
110014         CommonBits.signExpBits = function signExpBits (num) {
110015           return num >> 52
110016         };
110017         CommonBits.zeroLowerBits = function zeroLowerBits (bits, nBits) {
110018           var invMask = (1 << nBits) - 1;
110019           var mask = ~invMask;
110020           var zeroed = bits & mask;
110021           return zeroed
110022         };
110023         CommonBits.numCommonMostSigMantissaBits = function numCommonMostSigMantissaBits (num1, num2) {
110024           var count = 0;
110025           for (var i = 52; i >= 0; i--) {
110026             if (CommonBits.getBit(num1, i) !== CommonBits.getBit(num2, i)) { return count }
110027             count++;
110028           }
110029           return 52
110030         };
110031
110032         var CommonBitsRemover = function CommonBitsRemover () {
110033           this._commonCoord = null;
110034           this._ccFilter = new CommonCoordinateFilter();
110035         };
110036
110037         var staticAccessors$42 = { CommonCoordinateFilter: { configurable: true },Translater: { configurable: true } };
110038         CommonBitsRemover.prototype.addCommonBits = function addCommonBits (geom) {
110039           var trans = new Translater(this._commonCoord);
110040           geom.apply(trans);
110041           geom.geometryChanged();
110042         };
110043         CommonBitsRemover.prototype.removeCommonBits = function removeCommonBits (geom) {
110044           if (this._commonCoord.x === 0.0 && this._commonCoord.y === 0.0) { return geom }
110045           var invCoord = new Coordinate(this._commonCoord);
110046           invCoord.x = -invCoord.x;
110047           invCoord.y = -invCoord.y;
110048           var trans = new Translater(invCoord);
110049           geom.apply(trans);
110050           geom.geometryChanged();
110051           return geom
110052         };
110053         CommonBitsRemover.prototype.getCommonCoordinate = function getCommonCoordinate () {
110054           return this._commonCoord
110055         };
110056         CommonBitsRemover.prototype.add = function add (geom) {
110057           geom.apply(this._ccFilter);
110058           this._commonCoord = this._ccFilter.getCommonCoordinate();
110059         };
110060         CommonBitsRemover.prototype.interfaces_ = function interfaces_ () {
110061           return []
110062         };
110063         CommonBitsRemover.prototype.getClass = function getClass () {
110064           return CommonBitsRemover
110065         };
110066         staticAccessors$42.CommonCoordinateFilter.get = function () { return CommonCoordinateFilter };
110067         staticAccessors$42.Translater.get = function () { return Translater };
110068
110069         Object.defineProperties( CommonBitsRemover, staticAccessors$42 );
110070
110071         var CommonCoordinateFilter = function CommonCoordinateFilter () {
110072           this._commonBitsX = new CommonBits();
110073           this._commonBitsY = new CommonBits();
110074         };
110075         CommonCoordinateFilter.prototype.filter = function filter (coord) {
110076           this._commonBitsX.add(coord.x);
110077           this._commonBitsY.add(coord.y);
110078         };
110079         CommonCoordinateFilter.prototype.getCommonCoordinate = function getCommonCoordinate () {
110080           return new Coordinate(this._commonBitsX.getCommon(), this._commonBitsY.getCommon())
110081         };
110082         CommonCoordinateFilter.prototype.interfaces_ = function interfaces_ () {
110083           return [CoordinateFilter]
110084         };
110085         CommonCoordinateFilter.prototype.getClass = function getClass () {
110086           return CommonCoordinateFilter
110087         };
110088
110089         var Translater = function Translater () {
110090           this.trans = null;
110091           var trans = arguments[0];
110092           this.trans = trans;
110093         };
110094         Translater.prototype.filter = function filter (seq, i) {
110095           var xp = seq.getOrdinate(i, 0) + this.trans.x;
110096           var yp = seq.getOrdinate(i, 1) + this.trans.y;
110097           seq.setOrdinate(i, 0, xp);
110098           seq.setOrdinate(i, 1, yp);
110099         };
110100         Translater.prototype.isDone = function isDone () {
110101           return false
110102         };
110103         Translater.prototype.isGeometryChanged = function isGeometryChanged () {
110104           return true
110105         };
110106         Translater.prototype.interfaces_ = function interfaces_ () {
110107           return [CoordinateSequenceFilter]
110108         };
110109         Translater.prototype.getClass = function getClass () {
110110           return Translater
110111         };
110112
110113         var SnapOverlayOp = function SnapOverlayOp (g1, g2) {
110114           this._geom = new Array(2).fill(null);
110115           this._snapTolerance = null;
110116           this._cbr = null;
110117           this._geom[0] = g1;
110118           this._geom[1] = g2;
110119           this.computeSnapTolerance();
110120         };
110121         SnapOverlayOp.prototype.selfSnap = function selfSnap (geom) {
110122           var snapper0 = new GeometrySnapper(geom);
110123           var snapGeom = snapper0.snapTo(geom, this._snapTolerance);
110124           return snapGeom
110125         };
110126         SnapOverlayOp.prototype.removeCommonBits = function removeCommonBits (geom) {
110127           this._cbr = new CommonBitsRemover();
110128           this._cbr.add(geom[0]);
110129           this._cbr.add(geom[1]);
110130           var remGeom = new Array(2).fill(null);
110131           remGeom[0] = this._cbr.removeCommonBits(geom[0].copy());
110132           remGeom[1] = this._cbr.removeCommonBits(geom[1].copy());
110133           return remGeom
110134         };
110135         SnapOverlayOp.prototype.prepareResult = function prepareResult (geom) {
110136           this._cbr.addCommonBits(geom);
110137           return geom
110138         };
110139         SnapOverlayOp.prototype.getResultGeometry = function getResultGeometry (opCode) {
110140           var prepGeom = this.snap(this._geom);
110141           var result = OverlayOp.overlayOp(prepGeom[0], prepGeom[1], opCode);
110142           return this.prepareResult(result)
110143         };
110144         SnapOverlayOp.prototype.checkValid = function checkValid (g) {
110145           if (!g.isValid()) {
110146             System.out.println('Snapped geometry is invalid');
110147           }
110148         };
110149         SnapOverlayOp.prototype.computeSnapTolerance = function computeSnapTolerance () {
110150           this._snapTolerance = GeometrySnapper.computeOverlaySnapTolerance(this._geom[0], this._geom[1]);
110151         };
110152         SnapOverlayOp.prototype.snap = function snap (geom) {
110153           var remGeom = this.removeCommonBits(geom);
110154           var snapGeom = GeometrySnapper.snap(remGeom[0], remGeom[1], this._snapTolerance);
110155           return snapGeom
110156         };
110157         SnapOverlayOp.prototype.interfaces_ = function interfaces_ () {
110158           return []
110159         };
110160         SnapOverlayOp.prototype.getClass = function getClass () {
110161           return SnapOverlayOp
110162         };
110163         SnapOverlayOp.overlayOp = function overlayOp (g0, g1, opCode) {
110164           var op = new SnapOverlayOp(g0, g1);
110165           return op.getResultGeometry(opCode)
110166         };
110167         SnapOverlayOp.union = function union (g0, g1) {
110168           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.UNION)
110169         };
110170         SnapOverlayOp.intersection = function intersection (g0, g1) {
110171           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.INTERSECTION)
110172         };
110173         SnapOverlayOp.symDifference = function symDifference (g0, g1) {
110174           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.SYMDIFFERENCE)
110175         };
110176         SnapOverlayOp.difference = function difference (g0, g1) {
110177           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.DIFFERENCE)
110178         };
110179
110180         var SnapIfNeededOverlayOp = function SnapIfNeededOverlayOp (g1, g2) {
110181           this._geom = new Array(2).fill(null);
110182           this._geom[0] = g1;
110183           this._geom[1] = g2;
110184         };
110185         SnapIfNeededOverlayOp.prototype.getResultGeometry = function getResultGeometry (opCode) {
110186           var result = null;
110187           var isSuccess = false;
110188           var savedException = null;
110189           try {
110190             result = OverlayOp.overlayOp(this._geom[0], this._geom[1], opCode);
110191             var isValid = true;
110192             if (isValid) { isSuccess = true; }
110193           } catch (ex) {
110194             if (ex instanceof RuntimeException) {
110195               savedException = ex;
110196             } else { throw ex }
110197           } finally {}
110198           if (!isSuccess) {
110199             try {
110200               result = SnapOverlayOp.overlayOp(this._geom[0], this._geom[1], opCode);
110201             } catch (ex$1) {
110202               if (ex$1 instanceof RuntimeException) {
110203                 throw savedException
110204               } else { throw ex$1 }
110205             } finally {}
110206           }
110207           return result
110208         };
110209         SnapIfNeededOverlayOp.prototype.interfaces_ = function interfaces_ () {
110210           return []
110211         };
110212         SnapIfNeededOverlayOp.prototype.getClass = function getClass () {
110213           return SnapIfNeededOverlayOp
110214         };
110215         SnapIfNeededOverlayOp.overlayOp = function overlayOp (g0, g1, opCode) {
110216           var op = new SnapIfNeededOverlayOp(g0, g1);
110217           return op.getResultGeometry(opCode)
110218         };
110219         SnapIfNeededOverlayOp.union = function union (g0, g1) {
110220           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.UNION)
110221         };
110222         SnapIfNeededOverlayOp.intersection = function intersection (g0, g1) {
110223           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.INTERSECTION)
110224         };
110225         SnapIfNeededOverlayOp.symDifference = function symDifference (g0, g1) {
110226           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.SYMDIFFERENCE)
110227         };
110228         SnapIfNeededOverlayOp.difference = function difference (g0, g1) {
110229           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.DIFFERENCE)
110230         };
110231
110232         var MonotoneChain$2 = function MonotoneChain () {
110233           this.mce = null;
110234           this.chainIndex = null;
110235           var mce = arguments[0];
110236           var chainIndex = arguments[1];
110237           this.mce = mce;
110238           this.chainIndex = chainIndex;
110239         };
110240         MonotoneChain$2.prototype.computeIntersections = function computeIntersections (mc, si) {
110241           this.mce.computeIntersectsForChain(this.chainIndex, mc.mce, mc.chainIndex, si);
110242         };
110243         MonotoneChain$2.prototype.interfaces_ = function interfaces_ () {
110244           return []
110245         };
110246         MonotoneChain$2.prototype.getClass = function getClass () {
110247           return MonotoneChain$2
110248         };
110249
110250         var SweepLineEvent = function SweepLineEvent () {
110251           this._label = null;
110252           this._xValue = null;
110253           this._eventType = null;
110254           this._insertEvent = null;
110255           this._deleteEventIndex = null;
110256           this._obj = null;
110257           if (arguments.length === 2) {
110258             var x = arguments[0];
110259             var insertEvent = arguments[1];
110260             this._eventType = SweepLineEvent.DELETE;
110261             this._xValue = x;
110262             this._insertEvent = insertEvent;
110263           } else if (arguments.length === 3) {
110264             var label = arguments[0];
110265             var x$1 = arguments[1];
110266             var obj = arguments[2];
110267             this._eventType = SweepLineEvent.INSERT;
110268             this._label = label;
110269             this._xValue = x$1;
110270             this._obj = obj;
110271           }
110272         };
110273
110274         var staticAccessors$43 = { INSERT: { configurable: true },DELETE: { configurable: true } };
110275         SweepLineEvent.prototype.isDelete = function isDelete () {
110276           return this._eventType === SweepLineEvent.DELETE
110277         };
110278         SweepLineEvent.prototype.setDeleteEventIndex = function setDeleteEventIndex (deleteEventIndex) {
110279           this._deleteEventIndex = deleteEventIndex;
110280         };
110281         SweepLineEvent.prototype.getObject = function getObject () {
110282           return this._obj
110283         };
110284         SweepLineEvent.prototype.compareTo = function compareTo (o) {
110285           var pe = o;
110286           if (this._xValue < pe._xValue) { return -1 }
110287           if (this._xValue > pe._xValue) { return 1 }
110288           if (this._eventType < pe._eventType) { return -1 }
110289           if (this._eventType > pe._eventType) { return 1 }
110290           return 0
110291         };
110292         SweepLineEvent.prototype.getInsertEvent = function getInsertEvent () {
110293           return this._insertEvent
110294         };
110295         SweepLineEvent.prototype.isInsert = function isInsert () {
110296           return this._eventType === SweepLineEvent.INSERT
110297         };
110298         SweepLineEvent.prototype.isSameLabel = function isSameLabel (ev) {
110299           if (this._label === null) { return false }
110300           return this._label === ev._label
110301         };
110302         SweepLineEvent.prototype.getDeleteEventIndex = function getDeleteEventIndex () {
110303           return this._deleteEventIndex
110304         };
110305         SweepLineEvent.prototype.interfaces_ = function interfaces_ () {
110306           return [Comparable]
110307         };
110308         SweepLineEvent.prototype.getClass = function getClass () {
110309           return SweepLineEvent
110310         };
110311         staticAccessors$43.INSERT.get = function () { return 1 };
110312         staticAccessors$43.DELETE.get = function () { return 2 };
110313
110314         Object.defineProperties( SweepLineEvent, staticAccessors$43 );
110315
110316         var EdgeSetIntersector = function EdgeSetIntersector () {};
110317
110318         EdgeSetIntersector.prototype.interfaces_ = function interfaces_ () {
110319           return []
110320         };
110321         EdgeSetIntersector.prototype.getClass = function getClass () {
110322           return EdgeSetIntersector
110323         };
110324
110325         var SegmentIntersector$2 = function SegmentIntersector () {
110326           this._hasIntersection = false;
110327           this._hasProper = false;
110328           this._hasProperInterior = false;
110329           this._properIntersectionPoint = null;
110330           this._li = null;
110331           this._includeProper = null;
110332           this._recordIsolated = null;
110333           this._isSelfIntersection = null;
110334           this._numIntersections = 0;
110335           this.numTests = 0;
110336           this._bdyNodes = null;
110337           this._isDone = false;
110338           this._isDoneWhenProperInt = false;
110339           var li = arguments[0];
110340           var includeProper = arguments[1];
110341           var recordIsolated = arguments[2];
110342           this._li = li;
110343           this._includeProper = includeProper;
110344           this._recordIsolated = recordIsolated;
110345         };
110346         SegmentIntersector$2.prototype.isTrivialIntersection = function isTrivialIntersection (e0, segIndex0, e1, segIndex1) {
110347           if (e0 === e1) {
110348             if (this._li.getIntersectionNum() === 1) {
110349               if (SegmentIntersector$2.isAdjacentSegments(segIndex0, segIndex1)) { return true }
110350               if (e0.isClosed()) {
110351                 var maxSegIndex = e0.getNumPoints() - 1;
110352                 if ((segIndex0 === 0 && segIndex1 === maxSegIndex) ||
110353                     (segIndex1 === 0 && segIndex0 === maxSegIndex)) {
110354                   return true
110355                 }
110356               }
110357             }
110358           }
110359           return false
110360         };
110361         SegmentIntersector$2.prototype.getProperIntersectionPoint = function getProperIntersectionPoint () {
110362           return this._properIntersectionPoint
110363         };
110364         SegmentIntersector$2.prototype.setIsDoneIfProperInt = function setIsDoneIfProperInt (isDoneWhenProperInt) {
110365           this._isDoneWhenProperInt = isDoneWhenProperInt;
110366         };
110367         SegmentIntersector$2.prototype.hasProperInteriorIntersection = function hasProperInteriorIntersection () {
110368           return this._hasProperInterior
110369         };
110370         SegmentIntersector$2.prototype.isBoundaryPointInternal = function isBoundaryPointInternal (li, bdyNodes) {
110371           for (var i = bdyNodes.iterator(); i.hasNext();) {
110372             var node = i.next();
110373             var pt = node.getCoordinate();
110374             if (li.isIntersection(pt)) { return true }
110375           }
110376           return false
110377         };
110378         SegmentIntersector$2.prototype.hasProperIntersection = function hasProperIntersection () {
110379           return this._hasProper
110380         };
110381         SegmentIntersector$2.prototype.hasIntersection = function hasIntersection () {
110382           return this._hasIntersection
110383         };
110384         SegmentIntersector$2.prototype.isDone = function isDone () {
110385           return this._isDone
110386         };
110387         SegmentIntersector$2.prototype.isBoundaryPoint = function isBoundaryPoint (li, bdyNodes) {
110388           if (bdyNodes === null) { return false }
110389           if (this.isBoundaryPointInternal(li, bdyNodes[0])) { return true }
110390           if (this.isBoundaryPointInternal(li, bdyNodes[1])) { return true }
110391           return false
110392         };
110393         SegmentIntersector$2.prototype.setBoundaryNodes = function setBoundaryNodes (bdyNodes0, bdyNodes1) {
110394           this._bdyNodes = new Array(2).fill(null);
110395           this._bdyNodes[0] = bdyNodes0;
110396           this._bdyNodes[1] = bdyNodes1;
110397         };
110398         SegmentIntersector$2.prototype.addIntersections = function addIntersections (e0, segIndex0, e1, segIndex1) {
110399           if (e0 === e1 && segIndex0 === segIndex1) { return null }
110400           this.numTests++;
110401           var p00 = e0.getCoordinates()[segIndex0];
110402           var p01 = e0.getCoordinates()[segIndex0 + 1];
110403           var p10 = e1.getCoordinates()[segIndex1];
110404           var p11 = e1.getCoordinates()[segIndex1 + 1];
110405           this._li.computeIntersection(p00, p01, p10, p11);
110406           if (this._li.hasIntersection()) {
110407             if (this._recordIsolated) {
110408               e0.setIsolated(false);
110409               e1.setIsolated(false);
110410             }
110411             this._numIntersections++;
110412             if (!this.isTrivialIntersection(e0, segIndex0, e1, segIndex1)) {
110413               this._hasIntersection = true;
110414               if (this._includeProper || !this._li.isProper()) {
110415                 e0.addIntersections(this._li, segIndex0, 0);
110416                 e1.addIntersections(this._li, segIndex1, 1);
110417               }
110418               if (this._li.isProper()) {
110419                 this._properIntersectionPoint = this._li.getIntersection(0).copy();
110420                 this._hasProper = true;
110421                 if (this._isDoneWhenProperInt) {
110422                   this._isDone = true;
110423                 }
110424                 if (!this.isBoundaryPoint(this._li, this._bdyNodes)) { this._hasProperInterior = true; }
110425               }
110426             }
110427           }
110428         };
110429         SegmentIntersector$2.prototype.interfaces_ = function interfaces_ () {
110430           return []
110431         };
110432         SegmentIntersector$2.prototype.getClass = function getClass () {
110433           return SegmentIntersector$2
110434         };
110435         SegmentIntersector$2.isAdjacentSegments = function isAdjacentSegments (i1, i2) {
110436           return Math.abs(i1 - i2) === 1
110437         };
110438
110439         var SimpleMCSweepLineIntersector = (function (EdgeSetIntersector$$1) {
110440           function SimpleMCSweepLineIntersector () {
110441             EdgeSetIntersector$$1.call(this);
110442             this.events = new ArrayList();
110443             this.nOverlaps = null;
110444           }
110445
110446           if ( EdgeSetIntersector$$1 ) { SimpleMCSweepLineIntersector.__proto__ = EdgeSetIntersector$$1; }
110447           SimpleMCSweepLineIntersector.prototype = Object.create( EdgeSetIntersector$$1 && EdgeSetIntersector$$1.prototype );
110448           SimpleMCSweepLineIntersector.prototype.constructor = SimpleMCSweepLineIntersector;
110449           SimpleMCSweepLineIntersector.prototype.prepareEvents = function prepareEvents () {
110450             var this$1 = this;
110451
110452             Collections.sort(this.events);
110453             for (var i = 0; i < this.events.size(); i++) {
110454               var ev = this$1.events.get(i);
110455               if (ev.isDelete()) {
110456                 ev.getInsertEvent().setDeleteEventIndex(i);
110457               }
110458             }
110459           };
110460           SimpleMCSweepLineIntersector.prototype.computeIntersections = function computeIntersections () {
110461             var this$1 = this;
110462
110463             if (arguments.length === 1) {
110464               var si = arguments[0];
110465               this.nOverlaps = 0;
110466               this.prepareEvents();
110467               for (var i = 0; i < this.events.size(); i++) {
110468                 var ev = this$1.events.get(i);
110469                 if (ev.isInsert()) {
110470                   this$1.processOverlaps(i, ev.getDeleteEventIndex(), ev, si);
110471                 }
110472                 if (si.isDone()) {
110473                   break
110474                 }
110475               }
110476             } else if (arguments.length === 3) {
110477               if (arguments[2] instanceof SegmentIntersector$2 && (hasInterface(arguments[0], List) && hasInterface(arguments[1], List))) {
110478                 var edges0 = arguments[0];
110479                 var edges1 = arguments[1];
110480                 var si$1 = arguments[2];
110481                 this.addEdges(edges0, edges0);
110482                 this.addEdges(edges1, edges1);
110483                 this.computeIntersections(si$1);
110484               } else if (typeof arguments[2] === 'boolean' && (hasInterface(arguments[0], List) && arguments[1] instanceof SegmentIntersector$2)) {
110485                 var edges = arguments[0];
110486                 var si$2 = arguments[1];
110487                 var testAllSegments = arguments[2];
110488                 if (testAllSegments) { this.addEdges(edges, null); } else { this.addEdges(edges); }
110489                 this.computeIntersections(si$2);
110490               }
110491             }
110492           };
110493           SimpleMCSweepLineIntersector.prototype.addEdge = function addEdge (edge, edgeSet) {
110494             var this$1 = this;
110495
110496             var mce = edge.getMonotoneChainEdge();
110497             var startIndex = mce.getStartIndexes();
110498             for (var i = 0; i < startIndex.length - 1; i++) {
110499               var mc = new MonotoneChain$2(mce, i);
110500               var insertEvent = new SweepLineEvent(edgeSet, mce.getMinX(i), mc);
110501               this$1.events.add(insertEvent);
110502               this$1.events.add(new SweepLineEvent(mce.getMaxX(i), insertEvent));
110503             }
110504           };
110505           SimpleMCSweepLineIntersector.prototype.processOverlaps = function processOverlaps (start, end, ev0, si) {
110506             var this$1 = this;
110507
110508             var mc0 = ev0.getObject();
110509             for (var i = start; i < end; i++) {
110510               var ev1 = this$1.events.get(i);
110511               if (ev1.isInsert()) {
110512                 var mc1 = ev1.getObject();
110513                 if (!ev0.isSameLabel(ev1)) {
110514                   mc0.computeIntersections(mc1, si);
110515                   this$1.nOverlaps++;
110516                 }
110517               }
110518             }
110519           };
110520           SimpleMCSweepLineIntersector.prototype.addEdges = function addEdges () {
110521             var this$1 = this;
110522
110523             if (arguments.length === 1) {
110524               var edges = arguments[0];
110525               for (var i = edges.iterator(); i.hasNext();) {
110526                 var edge = i.next();
110527                 this$1.addEdge(edge, edge);
110528               }
110529             } else if (arguments.length === 2) {
110530               var edges$1 = arguments[0];
110531               var edgeSet = arguments[1];
110532               for (var i$1 = edges$1.iterator(); i$1.hasNext();) {
110533                 var edge$1 = i$1.next();
110534                 this$1.addEdge(edge$1, edgeSet);
110535               }
110536             }
110537           };
110538           SimpleMCSweepLineIntersector.prototype.interfaces_ = function interfaces_ () {
110539             return []
110540           };
110541           SimpleMCSweepLineIntersector.prototype.getClass = function getClass () {
110542             return SimpleMCSweepLineIntersector
110543           };
110544
110545           return SimpleMCSweepLineIntersector;
110546         }(EdgeSetIntersector));
110547
110548         var IntervalRTreeNode = function IntervalRTreeNode () {
110549           this._min = Double.POSITIVE_INFINITY;
110550           this._max = Double.NEGATIVE_INFINITY;
110551         };
110552
110553         var staticAccessors$45 = { NodeComparator: { configurable: true } };
110554         IntervalRTreeNode.prototype.getMin = function getMin () {
110555           return this._min
110556         };
110557         IntervalRTreeNode.prototype.intersects = function intersects (queryMin, queryMax) {
110558           if (this._min > queryMax || this._max < queryMin) { return false }
110559           return true
110560         };
110561         IntervalRTreeNode.prototype.getMax = function getMax () {
110562           return this._max
110563         };
110564         IntervalRTreeNode.prototype.toString = function toString () {
110565           return WKTWriter.toLineString(new Coordinate(this._min, 0), new Coordinate(this._max, 0))
110566         };
110567         IntervalRTreeNode.prototype.interfaces_ = function interfaces_ () {
110568           return []
110569         };
110570         IntervalRTreeNode.prototype.getClass = function getClass () {
110571           return IntervalRTreeNode
110572         };
110573         staticAccessors$45.NodeComparator.get = function () { return NodeComparator };
110574
110575         Object.defineProperties( IntervalRTreeNode, staticAccessors$45 );
110576
110577         var NodeComparator = function NodeComparator () {};
110578
110579         NodeComparator.prototype.compare = function compare (o1, o2) {
110580           var n1 = o1;
110581           var n2 = o2;
110582           var mid1 = (n1._min + n1._max) / 2;
110583           var mid2 = (n2._min + n2._max) / 2;
110584           if (mid1 < mid2) { return -1 }
110585           if (mid1 > mid2) { return 1 }
110586           return 0
110587         };
110588         NodeComparator.prototype.interfaces_ = function interfaces_ () {
110589           return [Comparator]
110590         };
110591         NodeComparator.prototype.getClass = function getClass () {
110592           return NodeComparator
110593         };
110594
110595         var IntervalRTreeLeafNode = (function (IntervalRTreeNode$$1) {
110596           function IntervalRTreeLeafNode () {
110597             IntervalRTreeNode$$1.call(this);
110598             this._item = null;
110599             var min = arguments[0];
110600             var max = arguments[1];
110601             var item = arguments[2];
110602             this._min = min;
110603             this._max = max;
110604             this._item = item;
110605           }
110606
110607           if ( IntervalRTreeNode$$1 ) { IntervalRTreeLeafNode.__proto__ = IntervalRTreeNode$$1; }
110608           IntervalRTreeLeafNode.prototype = Object.create( IntervalRTreeNode$$1 && IntervalRTreeNode$$1.prototype );
110609           IntervalRTreeLeafNode.prototype.constructor = IntervalRTreeLeafNode;
110610           IntervalRTreeLeafNode.prototype.query = function query (queryMin, queryMax, visitor) {
110611             if (!this.intersects(queryMin, queryMax)) { return null }
110612             visitor.visitItem(this._item);
110613           };
110614           IntervalRTreeLeafNode.prototype.interfaces_ = function interfaces_ () {
110615             return []
110616           };
110617           IntervalRTreeLeafNode.prototype.getClass = function getClass () {
110618             return IntervalRTreeLeafNode
110619           };
110620
110621           return IntervalRTreeLeafNode;
110622         }(IntervalRTreeNode));
110623
110624         var IntervalRTreeBranchNode = (function (IntervalRTreeNode$$1) {
110625           function IntervalRTreeBranchNode () {
110626             IntervalRTreeNode$$1.call(this);
110627             this._node1 = null;
110628             this._node2 = null;
110629             var n1 = arguments[0];
110630             var n2 = arguments[1];
110631             this._node1 = n1;
110632             this._node2 = n2;
110633             this.buildExtent(this._node1, this._node2);
110634           }
110635
110636           if ( IntervalRTreeNode$$1 ) { IntervalRTreeBranchNode.__proto__ = IntervalRTreeNode$$1; }
110637           IntervalRTreeBranchNode.prototype = Object.create( IntervalRTreeNode$$1 && IntervalRTreeNode$$1.prototype );
110638           IntervalRTreeBranchNode.prototype.constructor = IntervalRTreeBranchNode;
110639           IntervalRTreeBranchNode.prototype.buildExtent = function buildExtent (n1, n2) {
110640             this._min = Math.min(n1._min, n2._min);
110641             this._max = Math.max(n1._max, n2._max);
110642           };
110643           IntervalRTreeBranchNode.prototype.query = function query (queryMin, queryMax, visitor) {
110644             if (!this.intersects(queryMin, queryMax)) {
110645               return null
110646             }
110647             if (this._node1 !== null) { this._node1.query(queryMin, queryMax, visitor); }
110648             if (this._node2 !== null) { this._node2.query(queryMin, queryMax, visitor); }
110649           };
110650           IntervalRTreeBranchNode.prototype.interfaces_ = function interfaces_ () {
110651             return []
110652           };
110653           IntervalRTreeBranchNode.prototype.getClass = function getClass () {
110654             return IntervalRTreeBranchNode
110655           };
110656
110657           return IntervalRTreeBranchNode;
110658         }(IntervalRTreeNode));
110659
110660         var SortedPackedIntervalRTree = function SortedPackedIntervalRTree () {
110661           this._leaves = new ArrayList();
110662           this._root = null;
110663           this._level = 0;
110664         };
110665         SortedPackedIntervalRTree.prototype.buildTree = function buildTree () {
110666             var this$1 = this;
110667
110668           Collections.sort(this._leaves, new IntervalRTreeNode.NodeComparator());
110669           var src = this._leaves;
110670           var temp = null;
110671           var dest = new ArrayList();
110672           while (true) {
110673             this$1.buildLevel(src, dest);
110674             if (dest.size() === 1) { return dest.get(0) }
110675             temp = src;
110676             src = dest;
110677             dest = temp;
110678           }
110679         };
110680         SortedPackedIntervalRTree.prototype.insert = function insert (min, max, item) {
110681           if (this._root !== null) { throw new Error('Index cannot be added to once it has been queried') }
110682           this._leaves.add(new IntervalRTreeLeafNode(min, max, item));
110683         };
110684         SortedPackedIntervalRTree.prototype.query = function query (min, max, visitor) {
110685           this.init();
110686           this._root.query(min, max, visitor);
110687         };
110688         SortedPackedIntervalRTree.prototype.buildRoot = function buildRoot () {
110689           if (this._root !== null) { return null }
110690           this._root = this.buildTree();
110691         };
110692         SortedPackedIntervalRTree.prototype.printNode = function printNode (node) {
110693           System.out.println(WKTWriter.toLineString(new Coordinate(node._min, this._level), new Coordinate(node._max, this._level)));
110694         };
110695         SortedPackedIntervalRTree.prototype.init = function init () {
110696           if (this._root !== null) { return null }
110697           this.buildRoot();
110698         };
110699         SortedPackedIntervalRTree.prototype.buildLevel = function buildLevel (src, dest) {
110700           this._level++;
110701           dest.clear();
110702           for (var i = 0; i < src.size(); i += 2) {
110703             var n1 = src.get(i);
110704             var n2 = i + 1 < src.size() ? src.get(i) : null;
110705             if (n2 === null) {
110706               dest.add(n1);
110707             } else {
110708               var node = new IntervalRTreeBranchNode(src.get(i), src.get(i + 1));
110709               dest.add(node);
110710             }
110711           }
110712         };
110713         SortedPackedIntervalRTree.prototype.interfaces_ = function interfaces_ () {
110714           return []
110715         };
110716         SortedPackedIntervalRTree.prototype.getClass = function getClass () {
110717           return SortedPackedIntervalRTree
110718         };
110719
110720         var ArrayListVisitor = function ArrayListVisitor () {
110721           this._items = new ArrayList();
110722         };
110723         ArrayListVisitor.prototype.visitItem = function visitItem (item) {
110724           this._items.add(item);
110725         };
110726         ArrayListVisitor.prototype.getItems = function getItems () {
110727           return this._items
110728         };
110729         ArrayListVisitor.prototype.interfaces_ = function interfaces_ () {
110730           return [ItemVisitor]
110731         };
110732         ArrayListVisitor.prototype.getClass = function getClass () {
110733           return ArrayListVisitor
110734         };
110735
110736         var IndexedPointInAreaLocator = function IndexedPointInAreaLocator () {
110737           this._index = null;
110738           var g = arguments[0];
110739           if (!hasInterface(g, Polygonal)) { throw new IllegalArgumentException('Argument must be Polygonal') }
110740           this._index = new IntervalIndexedGeometry(g);
110741         };
110742
110743         var staticAccessors$44 = { SegmentVisitor: { configurable: true },IntervalIndexedGeometry: { configurable: true } };
110744         IndexedPointInAreaLocator.prototype.locate = function locate (p) {
110745           var rcc = new RayCrossingCounter(p);
110746           var visitor = new SegmentVisitor(rcc);
110747           this._index.query(p.y, p.y, visitor);
110748           return rcc.getLocation()
110749         };
110750         IndexedPointInAreaLocator.prototype.interfaces_ = function interfaces_ () {
110751           return [PointOnGeometryLocator]
110752         };
110753         IndexedPointInAreaLocator.prototype.getClass = function getClass () {
110754           return IndexedPointInAreaLocator
110755         };
110756         staticAccessors$44.SegmentVisitor.get = function () { return SegmentVisitor };
110757         staticAccessors$44.IntervalIndexedGeometry.get = function () { return IntervalIndexedGeometry };
110758
110759         Object.defineProperties( IndexedPointInAreaLocator, staticAccessors$44 );
110760
110761         var SegmentVisitor = function SegmentVisitor () {
110762           this._counter = null;
110763           var counter = arguments[0];
110764           this._counter = counter;
110765         };
110766         SegmentVisitor.prototype.visitItem = function visitItem (item) {
110767           var seg = item;
110768           this._counter.countSegment(seg.getCoordinate(0), seg.getCoordinate(1));
110769         };
110770         SegmentVisitor.prototype.interfaces_ = function interfaces_ () {
110771           return [ItemVisitor]
110772         };
110773         SegmentVisitor.prototype.getClass = function getClass () {
110774           return SegmentVisitor
110775         };
110776
110777         var IntervalIndexedGeometry = function IntervalIndexedGeometry () {
110778           this._index = new SortedPackedIntervalRTree();
110779           var geom = arguments[0];
110780           this.init(geom);
110781         };
110782         IntervalIndexedGeometry.prototype.init = function init (geom) {
110783             var this$1 = this;
110784
110785           var lines = LinearComponentExtracter.getLines(geom);
110786           for (var i = lines.iterator(); i.hasNext();) {
110787             var line = i.next();
110788             var pts = line.getCoordinates();
110789             this$1.addLine(pts);
110790           }
110791         };
110792         IntervalIndexedGeometry.prototype.addLine = function addLine (pts) {
110793             var this$1 = this;
110794
110795           for (var i = 1; i < pts.length; i++) {
110796             var seg = new LineSegment(pts[i - 1], pts[i]);
110797             var min = Math.min(seg.p0.y, seg.p1.y);
110798             var max = Math.max(seg.p0.y, seg.p1.y);
110799             this$1._index.insert(min, max, seg);
110800           }
110801         };
110802         IntervalIndexedGeometry.prototype.query = function query () {
110803           if (arguments.length === 2) {
110804             var min = arguments[0];
110805             var max = arguments[1];
110806             var visitor = new ArrayListVisitor();
110807             this._index.query(min, max, visitor);
110808             return visitor.getItems()
110809           } else if (arguments.length === 3) {
110810             var min$1 = arguments[0];
110811             var max$1 = arguments[1];
110812             var visitor$1 = arguments[2];
110813             this._index.query(min$1, max$1, visitor$1);
110814           }
110815         };
110816         IntervalIndexedGeometry.prototype.interfaces_ = function interfaces_ () {
110817           return []
110818         };
110819         IntervalIndexedGeometry.prototype.getClass = function getClass () {
110820           return IntervalIndexedGeometry
110821         };
110822
110823         var GeometryGraph = (function (PlanarGraph$$1) {
110824           function GeometryGraph () {
110825             PlanarGraph$$1.call(this);
110826             this._parentGeom = null;
110827             this._lineEdgeMap = new HashMap();
110828             this._boundaryNodeRule = null;
110829             this._useBoundaryDeterminationRule = true;
110830             this._argIndex = null;
110831             this._boundaryNodes = null;
110832             this._hasTooFewPoints = false;
110833             this._invalidPoint = null;
110834             this._areaPtLocator = null;
110835             this._ptLocator = new PointLocator();
110836             if (arguments.length === 2) {
110837               var argIndex = arguments[0];
110838               var parentGeom = arguments[1];
110839               var boundaryNodeRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE;
110840               this._argIndex = argIndex;
110841               this._parentGeom = parentGeom;
110842               this._boundaryNodeRule = boundaryNodeRule;
110843               if (parentGeom !== null) {
110844                 this.add(parentGeom);
110845               }
110846             } else if (arguments.length === 3) {
110847               var argIndex$1 = arguments[0];
110848               var parentGeom$1 = arguments[1];
110849               var boundaryNodeRule$1 = arguments[2];
110850               this._argIndex = argIndex$1;
110851               this._parentGeom = parentGeom$1;
110852               this._boundaryNodeRule = boundaryNodeRule$1;
110853               if (parentGeom$1 !== null) {
110854                 this.add(parentGeom$1);
110855               }
110856             }
110857           }
110858
110859           if ( PlanarGraph$$1 ) { GeometryGraph.__proto__ = PlanarGraph$$1; }
110860           GeometryGraph.prototype = Object.create( PlanarGraph$$1 && PlanarGraph$$1.prototype );
110861           GeometryGraph.prototype.constructor = GeometryGraph;
110862           GeometryGraph.prototype.insertBoundaryPoint = function insertBoundaryPoint (argIndex, coord) {
110863             var n = this._nodes.addNode(coord);
110864             var lbl = n.getLabel();
110865             var boundaryCount = 1;
110866             var loc = Location.NONE;
110867             loc = lbl.getLocation(argIndex, Position.ON);
110868             if (loc === Location.BOUNDARY) { boundaryCount++; }
110869             var newLoc = GeometryGraph.determineBoundary(this._boundaryNodeRule, boundaryCount);
110870             lbl.setLocation(argIndex, newLoc);
110871           };
110872           GeometryGraph.prototype.computeSelfNodes = function computeSelfNodes () {
110873             if (arguments.length === 2) {
110874               var li = arguments[0];
110875               var computeRingSelfNodes = arguments[1];
110876               return this.computeSelfNodes(li, computeRingSelfNodes, false)
110877             } else if (arguments.length === 3) {
110878               var li$1 = arguments[0];
110879               var computeRingSelfNodes$1 = arguments[1];
110880               var isDoneIfProperInt = arguments[2];
110881               var si = new SegmentIntersector$2(li$1, true, false);
110882               si.setIsDoneIfProperInt(isDoneIfProperInt);
110883               var esi = this.createEdgeSetIntersector();
110884               var isRings = this._parentGeom instanceof LinearRing || this._parentGeom instanceof Polygon || this._parentGeom instanceof MultiPolygon;
110885               var computeAllSegments = computeRingSelfNodes$1 || !isRings;
110886               esi.computeIntersections(this._edges, si, computeAllSegments);
110887               this.addSelfIntersectionNodes(this._argIndex);
110888               return si
110889             }
110890           };
110891           GeometryGraph.prototype.computeSplitEdges = function computeSplitEdges (edgelist) {
110892             for (var i = this._edges.iterator(); i.hasNext();) {
110893               var e = i.next();
110894               e.eiList.addSplitEdges(edgelist);
110895             }
110896           };
110897           GeometryGraph.prototype.computeEdgeIntersections = function computeEdgeIntersections (g, li, includeProper) {
110898             var si = new SegmentIntersector$2(li, includeProper, true);
110899             si.setBoundaryNodes(this.getBoundaryNodes(), g.getBoundaryNodes());
110900             var esi = this.createEdgeSetIntersector();
110901             esi.computeIntersections(this._edges, g._edges, si);
110902             return si
110903           };
110904           GeometryGraph.prototype.getGeometry = function getGeometry () {
110905             return this._parentGeom
110906           };
110907           GeometryGraph.prototype.getBoundaryNodeRule = function getBoundaryNodeRule () {
110908             return this._boundaryNodeRule
110909           };
110910           GeometryGraph.prototype.hasTooFewPoints = function hasTooFewPoints () {
110911             return this._hasTooFewPoints
110912           };
110913           GeometryGraph.prototype.addPoint = function addPoint () {
110914             if (arguments[0] instanceof Point$1) {
110915               var p = arguments[0];
110916               var coord = p.getCoordinate();
110917               this.insertPoint(this._argIndex, coord, Location.INTERIOR);
110918             } else if (arguments[0] instanceof Coordinate) {
110919               var pt = arguments[0];
110920               this.insertPoint(this._argIndex, pt, Location.INTERIOR);
110921             }
110922           };
110923           GeometryGraph.prototype.addPolygon = function addPolygon (p) {
110924             var this$1 = this;
110925
110926             this.addPolygonRing(p.getExteriorRing(), Location.EXTERIOR, Location.INTERIOR);
110927             for (var i = 0; i < p.getNumInteriorRing(); i++) {
110928               var hole = p.getInteriorRingN(i);
110929               this$1.addPolygonRing(hole, Location.INTERIOR, Location.EXTERIOR);
110930             }
110931           };
110932           GeometryGraph.prototype.addEdge = function addEdge (e) {
110933             this.insertEdge(e);
110934             var coord = e.getCoordinates();
110935             this.insertPoint(this._argIndex, coord[0], Location.BOUNDARY);
110936             this.insertPoint(this._argIndex, coord[coord.length - 1], Location.BOUNDARY);
110937           };
110938           GeometryGraph.prototype.addLineString = function addLineString (line) {
110939             var coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
110940             if (coord.length < 2) {
110941               this._hasTooFewPoints = true;
110942               this._invalidPoint = coord[0];
110943               return null
110944             }
110945             var e = new Edge(coord, new Label(this._argIndex, Location.INTERIOR));
110946             this._lineEdgeMap.put(line, e);
110947             this.insertEdge(e);
110948             Assert.isTrue(coord.length >= 2, 'found LineString with single point');
110949             this.insertBoundaryPoint(this._argIndex, coord[0]);
110950             this.insertBoundaryPoint(this._argIndex, coord[coord.length - 1]);
110951           };
110952           GeometryGraph.prototype.getInvalidPoint = function getInvalidPoint () {
110953             return this._invalidPoint
110954           };
110955           GeometryGraph.prototype.getBoundaryPoints = function getBoundaryPoints () {
110956             var coll = this.getBoundaryNodes();
110957             var pts = new Array(coll.size()).fill(null);
110958             var i = 0;
110959             for (var it = coll.iterator(); it.hasNext();) {
110960               var node = it.next();
110961               pts[i++] = node.getCoordinate().copy();
110962             }
110963             return pts
110964           };
110965           GeometryGraph.prototype.getBoundaryNodes = function getBoundaryNodes () {
110966             if (this._boundaryNodes === null) { this._boundaryNodes = this._nodes.getBoundaryNodes(this._argIndex); }
110967             return this._boundaryNodes
110968           };
110969           GeometryGraph.prototype.addSelfIntersectionNode = function addSelfIntersectionNode (argIndex, coord, loc) {
110970             if (this.isBoundaryNode(argIndex, coord)) { return null }
110971             if (loc === Location.BOUNDARY && this._useBoundaryDeterminationRule) { this.insertBoundaryPoint(argIndex, coord); } else { this.insertPoint(argIndex, coord, loc); }
110972           };
110973           GeometryGraph.prototype.addPolygonRing = function addPolygonRing (lr, cwLeft, cwRight) {
110974             if (lr.isEmpty()) { return null }
110975             var coord = CoordinateArrays.removeRepeatedPoints(lr.getCoordinates());
110976             if (coord.length < 4) {
110977               this._hasTooFewPoints = true;
110978               this._invalidPoint = coord[0];
110979               return null
110980             }
110981             var left = cwLeft;
110982             var right = cwRight;
110983             if (CGAlgorithms.isCCW(coord)) {
110984               left = cwRight;
110985               right = cwLeft;
110986             }
110987             var e = new Edge(coord, new Label(this._argIndex, Location.BOUNDARY, left, right));
110988             this._lineEdgeMap.put(lr, e);
110989             this.insertEdge(e);
110990             this.insertPoint(this._argIndex, coord[0], Location.BOUNDARY);
110991           };
110992           GeometryGraph.prototype.insertPoint = function insertPoint (argIndex, coord, onLocation) {
110993             var n = this._nodes.addNode(coord);
110994             var lbl = n.getLabel();
110995             if (lbl === null) {
110996               n._label = new Label(argIndex, onLocation);
110997             } else { lbl.setLocation(argIndex, onLocation); }
110998           };
110999           GeometryGraph.prototype.createEdgeSetIntersector = function createEdgeSetIntersector () {
111000             return new SimpleMCSweepLineIntersector()
111001           };
111002           GeometryGraph.prototype.addSelfIntersectionNodes = function addSelfIntersectionNodes (argIndex) {
111003             var this$1 = this;
111004
111005             for (var i = this._edges.iterator(); i.hasNext();) {
111006               var e = i.next();
111007               var eLoc = e.getLabel().getLocation(argIndex);
111008               for (var eiIt = e.eiList.iterator(); eiIt.hasNext();) {
111009                 var ei = eiIt.next();
111010                 this$1.addSelfIntersectionNode(argIndex, ei.coord, eLoc);
111011               }
111012             }
111013           };
111014           GeometryGraph.prototype.add = function add () {
111015             if (arguments.length === 1) {
111016               var g = arguments[0];
111017               if (g.isEmpty()) { return null }
111018               if (g instanceof MultiPolygon) { this._useBoundaryDeterminationRule = false; }
111019               if (g instanceof Polygon) { this.addPolygon(g); }
111020               else if (g instanceof LineString) { this.addLineString(g); }
111021               else if (g instanceof Point$1) { this.addPoint(g); }
111022               else if (g instanceof MultiPoint) { this.addCollection(g); }
111023               else if (g instanceof MultiLineString) { this.addCollection(g); }
111024               else if (g instanceof MultiPolygon) { this.addCollection(g); }
111025               else if (g instanceof GeometryCollection) { this.addCollection(g); }
111026               else { throw new Error(g.getClass().getName()) }
111027             } else { return PlanarGraph$$1.prototype.add.apply(this, arguments) }
111028           };
111029           GeometryGraph.prototype.addCollection = function addCollection (gc) {
111030             var this$1 = this;
111031
111032             for (var i = 0; i < gc.getNumGeometries(); i++) {
111033               var g = gc.getGeometryN(i);
111034               this$1.add(g);
111035             }
111036           };
111037           GeometryGraph.prototype.locate = function locate (pt) {
111038             if (hasInterface(this._parentGeom, Polygonal) && this._parentGeom.getNumGeometries() > 50) {
111039               if (this._areaPtLocator === null) {
111040                 this._areaPtLocator = new IndexedPointInAreaLocator(this._parentGeom);
111041               }
111042               return this._areaPtLocator.locate(pt)
111043             }
111044             return this._ptLocator.locate(pt, this._parentGeom)
111045           };
111046           GeometryGraph.prototype.findEdge = function findEdge () {
111047             if (arguments.length === 1) {
111048               var line = arguments[0];
111049               return this._lineEdgeMap.get(line)
111050             } else { return PlanarGraph$$1.prototype.findEdge.apply(this, arguments) }
111051           };
111052           GeometryGraph.prototype.interfaces_ = function interfaces_ () {
111053             return []
111054           };
111055           GeometryGraph.prototype.getClass = function getClass () {
111056             return GeometryGraph
111057           };
111058           GeometryGraph.determineBoundary = function determineBoundary (boundaryNodeRule, boundaryCount) {
111059             return boundaryNodeRule.isInBoundary(boundaryCount) ? Location.BOUNDARY : Location.INTERIOR
111060           };
111061
111062           return GeometryGraph;
111063         }(PlanarGraph));
111064
111065         var GeometryGraphOp = function GeometryGraphOp () {
111066           this._li = new RobustLineIntersector();
111067           this._resultPrecisionModel = null;
111068           this._arg = null;
111069           if (arguments.length === 1) {
111070             var g0 = arguments[0];
111071             this.setComputationPrecision(g0.getPrecisionModel());
111072             this._arg = new Array(1).fill(null);
111073             this._arg[0] = new GeometryGraph(0, g0);
111074           } else if (arguments.length === 2) {
111075             var g0$1 = arguments[0];
111076             var g1 = arguments[1];
111077             var boundaryNodeRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE;
111078             if (g0$1.getPrecisionModel().compareTo(g1.getPrecisionModel()) >= 0) { this.setComputationPrecision(g0$1.getPrecisionModel()); } else { this.setComputationPrecision(g1.getPrecisionModel()); }
111079             this._arg = new Array(2).fill(null);
111080             this._arg[0] = new GeometryGraph(0, g0$1, boundaryNodeRule);
111081             this._arg[1] = new GeometryGraph(1, g1, boundaryNodeRule);
111082           } else if (arguments.length === 3) {
111083             var g0$2 = arguments[0];
111084             var g1$1 = arguments[1];
111085             var boundaryNodeRule$1 = arguments[2];
111086             if (g0$2.getPrecisionModel().compareTo(g1$1.getPrecisionModel()) >= 0) { this.setComputationPrecision(g0$2.getPrecisionModel()); } else { this.setComputationPrecision(g1$1.getPrecisionModel()); }
111087             this._arg = new Array(2).fill(null);
111088             this._arg[0] = new GeometryGraph(0, g0$2, boundaryNodeRule$1);
111089             this._arg[1] = new GeometryGraph(1, g1$1, boundaryNodeRule$1);
111090           }
111091         };
111092         GeometryGraphOp.prototype.getArgGeometry = function getArgGeometry (i) {
111093           return this._arg[i].getGeometry()
111094         };
111095         GeometryGraphOp.prototype.setComputationPrecision = function setComputationPrecision (pm) {
111096           this._resultPrecisionModel = pm;
111097           this._li.setPrecisionModel(this._resultPrecisionModel);
111098         };
111099         GeometryGraphOp.prototype.interfaces_ = function interfaces_ () {
111100           return []
111101         };
111102         GeometryGraphOp.prototype.getClass = function getClass () {
111103           return GeometryGraphOp
111104         };
111105
111106         // operation.geometrygraph
111107
111108         var GeometryMapper = function GeometryMapper () {};
111109
111110         GeometryMapper.prototype.interfaces_ = function interfaces_ () {
111111           return []
111112         };
111113         GeometryMapper.prototype.getClass = function getClass () {
111114           return GeometryMapper
111115         };
111116         GeometryMapper.map = function map () {
111117           if (arguments[0] instanceof Geometry && hasInterface(arguments[1], GeometryMapper.MapOp)) {
111118             var geom = arguments[0];
111119             var op = arguments[1];
111120             var mapped = new ArrayList();
111121             for (var i = 0; i < geom.getNumGeometries(); i++) {
111122               var g = op.map(geom.getGeometryN(i));
111123               if (g !== null) { mapped.add(g); }
111124             }
111125             return geom.getFactory().buildGeometry(mapped)
111126           } else if (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], GeometryMapper.MapOp)) {
111127             var geoms = arguments[0];
111128             var op$1 = arguments[1];
111129             var mapped$1 = new ArrayList();
111130             for (var i$1 = geoms.iterator(); i$1.hasNext();) {
111131               var g$1 = i$1.next();
111132               var gr = op$1.map(g$1);
111133               if (gr !== null) { mapped$1.add(gr); }
111134             }
111135             return mapped$1
111136           }
111137         };
111138         GeometryMapper.MapOp = function MapOp () {};
111139
111140         var OverlayOp = (function (GeometryGraphOp) {
111141           function OverlayOp () {
111142             var g0 = arguments[0];
111143             var g1 = arguments[1];
111144             GeometryGraphOp.call(this, g0, g1);
111145             this._ptLocator = new PointLocator();
111146             this._geomFact = null;
111147             this._resultGeom = null;
111148             this._graph = null;
111149             this._edgeList = new EdgeList();
111150             this._resultPolyList = new ArrayList();
111151             this._resultLineList = new ArrayList();
111152             this._resultPointList = new ArrayList();
111153             this._graph = new PlanarGraph(new OverlayNodeFactory());
111154             this._geomFact = g0.getFactory();
111155           }
111156
111157           if ( GeometryGraphOp ) { OverlayOp.__proto__ = GeometryGraphOp; }
111158           OverlayOp.prototype = Object.create( GeometryGraphOp && GeometryGraphOp.prototype );
111159           OverlayOp.prototype.constructor = OverlayOp;
111160           OverlayOp.prototype.insertUniqueEdge = function insertUniqueEdge (e) {
111161             var existingEdge = this._edgeList.findEqualEdge(e);
111162             if (existingEdge !== null) {
111163               var existingLabel = existingEdge.getLabel();
111164               var labelToMerge = e.getLabel();
111165               if (!existingEdge.isPointwiseEqual(e)) {
111166                 labelToMerge = new Label(e.getLabel());
111167                 labelToMerge.flip();
111168               }
111169               var depth = existingEdge.getDepth();
111170               if (depth.isNull()) {
111171                 depth.add(existingLabel);
111172               }
111173               depth.add(labelToMerge);
111174               existingLabel.merge(labelToMerge);
111175             } else {
111176               this._edgeList.add(e);
111177             }
111178           };
111179           OverlayOp.prototype.getGraph = function getGraph () {
111180             return this._graph
111181           };
111182           OverlayOp.prototype.cancelDuplicateResultEdges = function cancelDuplicateResultEdges () {
111183             for (var it = this._graph.getEdgeEnds().iterator(); it.hasNext();) {
111184               var de = it.next();
111185               var sym = de.getSym();
111186               if (de.isInResult() && sym.isInResult()) {
111187                 de.setInResult(false);
111188                 sym.setInResult(false);
111189               }
111190             }
111191           };
111192           OverlayOp.prototype.isCoveredByLA = function isCoveredByLA (coord) {
111193             if (this.isCovered(coord, this._resultLineList)) { return true }
111194             if (this.isCovered(coord, this._resultPolyList)) { return true }
111195             return false
111196           };
111197           OverlayOp.prototype.computeGeometry = function computeGeometry (resultPointList, resultLineList, resultPolyList, opcode) {
111198             var geomList = new ArrayList();
111199             geomList.addAll(resultPointList);
111200             geomList.addAll(resultLineList);
111201             geomList.addAll(resultPolyList);
111202             if (geomList.isEmpty()) { return OverlayOp.createEmptyResult(opcode, this._arg[0].getGeometry(), this._arg[1].getGeometry(), this._geomFact) }
111203             return this._geomFact.buildGeometry(geomList)
111204           };
111205           OverlayOp.prototype.mergeSymLabels = function mergeSymLabels () {
111206             for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
111207               var node = nodeit.next();
111208               node.getEdges().mergeSymLabels();
111209             }
111210           };
111211           OverlayOp.prototype.isCovered = function isCovered (coord, geomList) {
111212             var this$1 = this;
111213
111214             for (var it = geomList.iterator(); it.hasNext();) {
111215               var geom = it.next();
111216               var loc = this$1._ptLocator.locate(coord, geom);
111217               if (loc !== Location.EXTERIOR) { return true }
111218             }
111219             return false
111220           };
111221           OverlayOp.prototype.replaceCollapsedEdges = function replaceCollapsedEdges () {
111222             var newEdges = new ArrayList();
111223             for (var it = this._edgeList.iterator(); it.hasNext();) {
111224               var e = it.next();
111225               if (e.isCollapsed()) {
111226                 it.remove();
111227                 newEdges.add(e.getCollapsedEdge());
111228               }
111229             }
111230             this._edgeList.addAll(newEdges);
111231           };
111232           OverlayOp.prototype.updateNodeLabelling = function updateNodeLabelling () {
111233             for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
111234               var node = nodeit.next();
111235               var lbl = node.getEdges().getLabel();
111236               node.getLabel().merge(lbl);
111237             }
111238           };
111239           OverlayOp.prototype.getResultGeometry = function getResultGeometry (overlayOpCode) {
111240             this.computeOverlay(overlayOpCode);
111241             return this._resultGeom
111242           };
111243           OverlayOp.prototype.insertUniqueEdges = function insertUniqueEdges (edges) {
111244             var this$1 = this;
111245
111246             for (var i = edges.iterator(); i.hasNext();) {
111247               var e = i.next();
111248               this$1.insertUniqueEdge(e);
111249             }
111250           };
111251           OverlayOp.prototype.computeOverlay = function computeOverlay (opCode) {
111252             this.copyPoints(0);
111253             this.copyPoints(1);
111254             this._arg[0].computeSelfNodes(this._li, false);
111255             this._arg[1].computeSelfNodes(this._li, false);
111256             this._arg[0].computeEdgeIntersections(this._arg[1], this._li, true);
111257             var baseSplitEdges = new ArrayList();
111258             this._arg[0].computeSplitEdges(baseSplitEdges);
111259             this._arg[1].computeSplitEdges(baseSplitEdges);
111260             // const splitEdges = baseSplitEdges
111261             this.insertUniqueEdges(baseSplitEdges);
111262             this.computeLabelsFromDepths();
111263             this.replaceCollapsedEdges();
111264             EdgeNodingValidator.checkValid(this._edgeList.getEdges());
111265             this._graph.addEdges(this._edgeList.getEdges());
111266             this.computeLabelling();
111267             this.labelIncompleteNodes();
111268             this.findResultAreaEdges(opCode);
111269             this.cancelDuplicateResultEdges();
111270             var polyBuilder = new PolygonBuilder(this._geomFact);
111271             polyBuilder.add(this._graph);
111272             this._resultPolyList = polyBuilder.getPolygons();
111273             var lineBuilder = new LineBuilder(this, this._geomFact, this._ptLocator);
111274             this._resultLineList = lineBuilder.build(opCode);
111275             var pointBuilder = new PointBuilder(this, this._geomFact, this._ptLocator);
111276             this._resultPointList = pointBuilder.build(opCode);
111277             this._resultGeom = this.computeGeometry(this._resultPointList, this._resultLineList, this._resultPolyList, opCode);
111278           };
111279           OverlayOp.prototype.labelIncompleteNode = function labelIncompleteNode (n, targetIndex) {
111280             var loc = this._ptLocator.locate(n.getCoordinate(), this._arg[targetIndex].getGeometry());
111281             n.getLabel().setLocation(targetIndex, loc);
111282           };
111283           OverlayOp.prototype.copyPoints = function copyPoints (argIndex) {
111284             var this$1 = this;
111285
111286             for (var i = this._arg[argIndex].getNodeIterator(); i.hasNext();) {
111287               var graphNode = i.next();
111288               var newNode = this$1._graph.addNode(graphNode.getCoordinate());
111289               newNode.setLabel(argIndex, graphNode.getLabel().getLocation(argIndex));
111290             }
111291           };
111292           OverlayOp.prototype.findResultAreaEdges = function findResultAreaEdges (opCode) {
111293             for (var it = this._graph.getEdgeEnds().iterator(); it.hasNext();) {
111294               var de = it.next();
111295               var label = de.getLabel();
111296               if (label.isArea() && !de.isInteriorAreaEdge() && OverlayOp.isResultOfOp(label.getLocation(0, Position.RIGHT), label.getLocation(1, Position.RIGHT), opCode)) {
111297                 de.setInResult(true);
111298               }
111299             }
111300           };
111301           OverlayOp.prototype.computeLabelsFromDepths = function computeLabelsFromDepths () {
111302             for (var it = this._edgeList.iterator(); it.hasNext();) {
111303               var e = it.next();
111304               var lbl = e.getLabel();
111305               var depth = e.getDepth();
111306               if (!depth.isNull()) {
111307                 depth.normalize();
111308                 for (var i = 0; i < 2; i++) {
111309                   if (!lbl.isNull(i) && lbl.isArea() && !depth.isNull(i)) {
111310                     if (depth.getDelta(i) === 0) {
111311                       lbl.toLine(i);
111312                     } else {
111313                       Assert.isTrue(!depth.isNull(i, Position.LEFT), 'depth of LEFT side has not been initialized');
111314                       lbl.setLocation(i, Position.LEFT, depth.getLocation(i, Position.LEFT));
111315                       Assert.isTrue(!depth.isNull(i, Position.RIGHT), 'depth of RIGHT side has not been initialized');
111316                       lbl.setLocation(i, Position.RIGHT, depth.getLocation(i, Position.RIGHT));
111317                     }
111318                   }
111319                 }
111320               }
111321             }
111322           };
111323           OverlayOp.prototype.computeLabelling = function computeLabelling () {
111324             var this$1 = this;
111325
111326             for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
111327               var node = nodeit.next();
111328               node.getEdges().computeLabelling(this$1._arg);
111329             }
111330             this.mergeSymLabels();
111331             this.updateNodeLabelling();
111332           };
111333           OverlayOp.prototype.labelIncompleteNodes = function labelIncompleteNodes () {
111334             var this$1 = this;
111335
111336             // let nodeCount = 0
111337             for (var ni = this._graph.getNodes().iterator(); ni.hasNext();) {
111338               var n = ni.next();
111339               var label = n.getLabel();
111340               if (n.isIsolated()) {
111341                 // nodeCount++
111342                 if (label.isNull(0)) { this$1.labelIncompleteNode(n, 0); } else { this$1.labelIncompleteNode(n, 1); }
111343               }
111344               n.getEdges().updateLabelling(label);
111345             }
111346           };
111347           OverlayOp.prototype.isCoveredByA = function isCoveredByA (coord) {
111348             if (this.isCovered(coord, this._resultPolyList)) { return true }
111349             return false
111350           };
111351           OverlayOp.prototype.interfaces_ = function interfaces_ () {
111352             return []
111353           };
111354           OverlayOp.prototype.getClass = function getClass () {
111355             return OverlayOp
111356           };
111357
111358           return OverlayOp;
111359         }(GeometryGraphOp));
111360
111361         OverlayOp.overlayOp = function (geom0, geom1, opCode) {
111362           var gov = new OverlayOp(geom0, geom1);
111363           var geomOv = gov.getResultGeometry(opCode);
111364           return geomOv
111365         };
111366         OverlayOp.intersection = function (g, other) {
111367           if (g.isEmpty() || other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.INTERSECTION, g, other, g.getFactory()) }
111368           if (g.isGeometryCollection()) {
111369             var g2 = other;
111370             return GeometryCollectionMapper.map(g, {
111371               interfaces_: function () {
111372                 return [GeometryMapper.MapOp]
111373               },
111374               map: function (g) {
111375                 return g.intersection(g2)
111376               }
111377             })
111378           }
111379           g.checkNotGeometryCollection(g);
111380           g.checkNotGeometryCollection(other);
111381           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.INTERSECTION)
111382         };
111383         OverlayOp.symDifference = function (g, other) {
111384           if (g.isEmpty() || other.isEmpty()) {
111385             if (g.isEmpty() && other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.SYMDIFFERENCE, g, other, g.getFactory()) }
111386             if (g.isEmpty()) { return other.copy() }
111387             if (other.isEmpty()) { return g.copy() }
111388           }
111389           g.checkNotGeometryCollection(g);
111390           g.checkNotGeometryCollection(other);
111391           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.SYMDIFFERENCE)
111392         };
111393         OverlayOp.resultDimension = function (opCode, g0, g1) {
111394           var dim0 = g0.getDimension();
111395           var dim1 = g1.getDimension();
111396           var resultDimension = -1;
111397           switch (opCode) {
111398             case OverlayOp.INTERSECTION:
111399               resultDimension = Math.min(dim0, dim1);
111400               break
111401             case OverlayOp.UNION:
111402               resultDimension = Math.max(dim0, dim1);
111403               break
111404             case OverlayOp.DIFFERENCE:
111405               resultDimension = dim0;
111406               break
111407             case OverlayOp.SYMDIFFERENCE:
111408               resultDimension = Math.max(dim0, dim1);
111409               break
111410           }
111411           return resultDimension
111412         };
111413         OverlayOp.createEmptyResult = function (overlayOpCode, a, b, geomFact) {
111414           var result = null;
111415           switch (OverlayOp.resultDimension(overlayOpCode, a, b)) {
111416             case -1:
111417               result = geomFact.createGeometryCollection(new Array(0).fill(null));
111418               break
111419             case 0:
111420               result = geomFact.createPoint();
111421               break
111422             case 1:
111423               result = geomFact.createLineString();
111424               break
111425             case 2:
111426               result = geomFact.createPolygon();
111427               break
111428           }
111429           return result
111430         };
111431         OverlayOp.difference = function (g, other) {
111432           if (g.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.DIFFERENCE, g, other, g.getFactory()) }
111433           if (other.isEmpty()) { return g.copy() }
111434           g.checkNotGeometryCollection(g);
111435           g.checkNotGeometryCollection(other);
111436           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.DIFFERENCE)
111437         };
111438         OverlayOp.isResultOfOp = function () {
111439           if (arguments.length === 2) {
111440             var label = arguments[0];
111441             var opCode = arguments[1];
111442             var loc0 = label.getLocation(0);
111443             var loc1 = label.getLocation(1);
111444             return OverlayOp.isResultOfOp(loc0, loc1, opCode)
111445           } else if (arguments.length === 3) {
111446             var loc0$1 = arguments[0];
111447             var loc1$1 = arguments[1];
111448             var overlayOpCode = arguments[2];
111449             if (loc0$1 === Location.BOUNDARY) { loc0$1 = Location.INTERIOR; }
111450             if (loc1$1 === Location.BOUNDARY) { loc1$1 = Location.INTERIOR; }
111451             switch (overlayOpCode) {
111452               case OverlayOp.INTERSECTION:
111453                 return loc0$1 === Location.INTERIOR && loc1$1 === Location.INTERIOR
111454               case OverlayOp.UNION:
111455                 return loc0$1 === Location.INTERIOR || loc1$1 === Location.INTERIOR
111456               case OverlayOp.DIFFERENCE:
111457                 return loc0$1 === Location.INTERIOR && loc1$1 !== Location.INTERIOR
111458               case OverlayOp.SYMDIFFERENCE:
111459                 return (loc0$1 === Location.INTERIOR && loc1$1 !== Location.INTERIOR) || (loc0$1 !== Location.INTERIOR && loc1$1 === Location.INTERIOR)
111460             }
111461             return false
111462           }
111463         };
111464         OverlayOp.INTERSECTION = 1;
111465         OverlayOp.UNION = 2;
111466         OverlayOp.DIFFERENCE = 3;
111467         OverlayOp.SYMDIFFERENCE = 4;
111468
111469         var FuzzyPointLocator = function FuzzyPointLocator () {
111470           this._g = null;
111471           this._boundaryDistanceTolerance = null;
111472           this._linework = null;
111473           this._ptLocator = new PointLocator();
111474           this._seg = new LineSegment();
111475           var g = arguments[0];
111476           var boundaryDistanceTolerance = arguments[1];
111477           this._g = g;
111478           this._boundaryDistanceTolerance = boundaryDistanceTolerance;
111479           this._linework = this.extractLinework(g);
111480         };
111481         FuzzyPointLocator.prototype.isWithinToleranceOfBoundary = function isWithinToleranceOfBoundary (pt) {
111482             var this$1 = this;
111483
111484           for (var i = 0; i < this._linework.getNumGeometries(); i++) {
111485             var line = this$1._linework.getGeometryN(i);
111486             var seq = line.getCoordinateSequence();
111487             for (var j = 0; j < seq.size() - 1; j++) {
111488               seq.getCoordinate(j, this$1._seg.p0);
111489               seq.getCoordinate(j + 1, this$1._seg.p1);
111490               var dist = this$1._seg.distance(pt);
111491               if (dist <= this$1._boundaryDistanceTolerance) { return true }
111492             }
111493           }
111494           return false
111495         };
111496         FuzzyPointLocator.prototype.getLocation = function getLocation (pt) {
111497           if (this.isWithinToleranceOfBoundary(pt)) { return Location.BOUNDARY }
111498           return this._ptLocator.locate(pt, this._g)
111499         };
111500         FuzzyPointLocator.prototype.extractLinework = function extractLinework (g) {
111501           var extracter = new PolygonalLineworkExtracter();
111502           g.apply(extracter);
111503           var linework = extracter.getLinework();
111504           var lines = GeometryFactory.toLineStringArray(linework);
111505           return g.getFactory().createMultiLineString(lines)
111506         };
111507         FuzzyPointLocator.prototype.interfaces_ = function interfaces_ () {
111508           return []
111509         };
111510         FuzzyPointLocator.prototype.getClass = function getClass () {
111511           return FuzzyPointLocator
111512         };
111513
111514         var PolygonalLineworkExtracter = function PolygonalLineworkExtracter () {
111515           this._linework = null;
111516           this._linework = new ArrayList();
111517         };
111518         PolygonalLineworkExtracter.prototype.getLinework = function getLinework () {
111519           return this._linework
111520         };
111521         PolygonalLineworkExtracter.prototype.filter = function filter (g) {
111522             var this$1 = this;
111523
111524           if (g instanceof Polygon) {
111525             var poly = g;
111526             this._linework.add(poly.getExteriorRing());
111527             for (var i = 0; i < poly.getNumInteriorRing(); i++) {
111528               this$1._linework.add(poly.getInteriorRingN(i));
111529             }
111530           }
111531         };
111532         PolygonalLineworkExtracter.prototype.interfaces_ = function interfaces_ () {
111533           return [GeometryFilter]
111534         };
111535         PolygonalLineworkExtracter.prototype.getClass = function getClass () {
111536           return PolygonalLineworkExtracter
111537         };
111538
111539         var OffsetPointGenerator = function OffsetPointGenerator () {
111540           this._g = null;
111541           this._doLeft = true;
111542           this._doRight = true;
111543           var g = arguments[0];
111544           this._g = g;
111545         };
111546         OffsetPointGenerator.prototype.extractPoints = function extractPoints (line, offsetDistance, offsetPts) {
111547             var this$1 = this;
111548
111549           var pts = line.getCoordinates();
111550           for (var i = 0; i < pts.length - 1; i++) {
111551             this$1.computeOffsetPoints(pts[i], pts[i + 1], offsetDistance, offsetPts);
111552           }
111553         };
111554         OffsetPointGenerator.prototype.setSidesToGenerate = function setSidesToGenerate (doLeft, doRight) {
111555           this._doLeft = doLeft;
111556           this._doRight = doRight;
111557         };
111558         OffsetPointGenerator.prototype.getPoints = function getPoints (offsetDistance) {
111559             var this$1 = this;
111560
111561           var offsetPts = new ArrayList();
111562           var lines = LinearComponentExtracter.getLines(this._g);
111563           for (var i = lines.iterator(); i.hasNext();) {
111564             var line = i.next();
111565             this$1.extractPoints(line, offsetDistance, offsetPts);
111566           }
111567           return offsetPts
111568         };
111569         OffsetPointGenerator.prototype.computeOffsetPoints = function computeOffsetPoints (p0, p1, offsetDistance, offsetPts) {
111570           var dx = p1.x - p0.x;
111571           var dy = p1.y - p0.y;
111572           var len = Math.sqrt(dx * dx + dy * dy);
111573           var ux = offsetDistance * dx / len;
111574           var uy = offsetDistance * dy / len;
111575           var midX = (p1.x + p0.x) / 2;
111576           var midY = (p1.y + p0.y) / 2;
111577           if (this._doLeft) {
111578             var offsetLeft = new Coordinate(midX - uy, midY + ux);
111579             offsetPts.add(offsetLeft);
111580           }
111581           if (this._doRight) {
111582             var offsetRight = new Coordinate(midX + uy, midY - ux);
111583             offsetPts.add(offsetRight);
111584           }
111585         };
111586         OffsetPointGenerator.prototype.interfaces_ = function interfaces_ () {
111587           return []
111588         };
111589         OffsetPointGenerator.prototype.getClass = function getClass () {
111590           return OffsetPointGenerator
111591         };
111592
111593         var OverlayResultValidator = function OverlayResultValidator () {
111594           this._geom = null;
111595           this._locFinder = null;
111596           this._location = new Array(3).fill(null);
111597           this._invalidLocation = null;
111598           this._boundaryDistanceTolerance = OverlayResultValidator.TOLERANCE;
111599           this._testCoords = new ArrayList();
111600           var a = arguments[0];
111601           var b = arguments[1];
111602           var result = arguments[2];
111603           this._boundaryDistanceTolerance = OverlayResultValidator.computeBoundaryDistanceTolerance(a, b);
111604           this._geom = [a, b, result];
111605           this._locFinder = [new FuzzyPointLocator(this._geom[0], this._boundaryDistanceTolerance), new FuzzyPointLocator(this._geom[1], this._boundaryDistanceTolerance), new FuzzyPointLocator(this._geom[2], this._boundaryDistanceTolerance)];
111606         };
111607
111608         var staticAccessors$46 = { TOLERANCE: { configurable: true } };
111609         OverlayResultValidator.prototype.reportResult = function reportResult (overlayOp, location, expectedInterior) {
111610           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]));
111611         };
111612         OverlayResultValidator.prototype.isValid = function isValid (overlayOp) {
111613           this.addTestPts(this._geom[0]);
111614           this.addTestPts(this._geom[1]);
111615           var isValid = this.checkValid(overlayOp);
111616           return isValid
111617         };
111618         OverlayResultValidator.prototype.checkValid = function checkValid () {
111619             var this$1 = this;
111620
111621           if (arguments.length === 1) {
111622             var overlayOp = arguments[0];
111623             for (var i = 0; i < this._testCoords.size(); i++) {
111624               var pt = this$1._testCoords.get(i);
111625               if (!this$1.checkValid(overlayOp, pt)) {
111626                 this$1._invalidLocation = pt;
111627                 return false
111628               }
111629             }
111630             return true
111631           } else if (arguments.length === 2) {
111632             var overlayOp$1 = arguments[0];
111633             var pt$1 = arguments[1];
111634             this._location[0] = this._locFinder[0].getLocation(pt$1);
111635             this._location[1] = this._locFinder[1].getLocation(pt$1);
111636             this._location[2] = this._locFinder[2].getLocation(pt$1);
111637             if (OverlayResultValidator.hasLocation(this._location, Location.BOUNDARY)) { return true }
111638             return this.isValidResult(overlayOp$1, this._location)
111639           }
111640         };
111641         OverlayResultValidator.prototype.addTestPts = function addTestPts (g) {
111642           var ptGen = new OffsetPointGenerator(g);
111643           this._testCoords.addAll(ptGen.getPoints(5 * this._boundaryDistanceTolerance));
111644         };
111645         OverlayResultValidator.prototype.isValidResult = function isValidResult (overlayOp, location) {
111646           var expectedInterior = OverlayOp.isResultOfOp(location[0], location[1], overlayOp);
111647           var resultInInterior = location[2] === Location.INTERIOR;
111648           var isValid = !(expectedInterior ^ resultInInterior);
111649           if (!isValid) { this.reportResult(overlayOp, location, expectedInterior); }
111650           return isValid
111651         };
111652         OverlayResultValidator.prototype.getInvalidLocation = function getInvalidLocation () {
111653           return this._invalidLocation
111654         };
111655         OverlayResultValidator.prototype.interfaces_ = function interfaces_ () {
111656           return []
111657         };
111658         OverlayResultValidator.prototype.getClass = function getClass () {
111659           return OverlayResultValidator
111660         };
111661         OverlayResultValidator.hasLocation = function hasLocation (location, loc) {
111662           for (var i = 0; i < 3; i++) {
111663             if (location[i] === loc) { return true }
111664           }
111665           return false
111666         };
111667         OverlayResultValidator.computeBoundaryDistanceTolerance = function computeBoundaryDistanceTolerance (g0, g1) {
111668           return Math.min(GeometrySnapper.computeSizeBasedSnapTolerance(g0), GeometrySnapper.computeSizeBasedSnapTolerance(g1))
111669         };
111670         OverlayResultValidator.isValid = function isValid (a, b, overlayOp, result) {
111671           var validator = new OverlayResultValidator(a, b, result);
111672           return validator.isValid(overlayOp)
111673         };
111674         staticAccessors$46.TOLERANCE.get = function () { return 0.000001 };
111675
111676         Object.defineProperties( OverlayResultValidator, staticAccessors$46 );
111677
111678         // operation.overlay
111679
111680         var GeometryCombiner = function GeometryCombiner (geoms) {
111681           this._geomFactory = null;
111682           this._skipEmpty = false;
111683           this._inputGeoms = null;
111684           this._geomFactory = GeometryCombiner.extractFactory(geoms);
111685           this._inputGeoms = geoms;
111686         };
111687         GeometryCombiner.prototype.extractElements = function extractElements (geom, elems) {
111688             var this$1 = this;
111689
111690           if (geom === null) { return null }
111691           for (var i = 0; i < geom.getNumGeometries(); i++) {
111692             var elemGeom = geom.getGeometryN(i);
111693             if (this$1._skipEmpty && elemGeom.isEmpty()) { continue }
111694             elems.add(elemGeom);
111695           }
111696         };
111697         GeometryCombiner.prototype.combine = function combine () {
111698             var this$1 = this;
111699
111700           var elems = new ArrayList();
111701           for (var i = this._inputGeoms.iterator(); i.hasNext();) {
111702             var g = i.next();
111703             this$1.extractElements(g, elems);
111704           }
111705           if (elems.size() === 0) {
111706             if (this._geomFactory !== null) {
111707               return this._geomFactory.createGeometryCollection(null)
111708             }
111709             return null
111710           }
111711           return this._geomFactory.buildGeometry(elems)
111712         };
111713         GeometryCombiner.prototype.interfaces_ = function interfaces_ () {
111714           return []
111715         };
111716         GeometryCombiner.prototype.getClass = function getClass () {
111717           return GeometryCombiner
111718         };
111719         GeometryCombiner.combine = function combine () {
111720           if (arguments.length === 1) {
111721             var geoms = arguments[0];
111722             var combiner = new GeometryCombiner(geoms);
111723             return combiner.combine()
111724           } else if (arguments.length === 2) {
111725             var g0 = arguments[0];
111726             var g1 = arguments[1];
111727             var combiner$1 = new GeometryCombiner(GeometryCombiner.createList(g0, g1));
111728             return combiner$1.combine()
111729           } else if (arguments.length === 3) {
111730             var g0$1 = arguments[0];
111731             var g1$1 = arguments[1];
111732             var g2 = arguments[2];
111733             var combiner$2 = new GeometryCombiner(GeometryCombiner.createList(g0$1, g1$1, g2));
111734             return combiner$2.combine()
111735           }
111736         };
111737         GeometryCombiner.extractFactory = function extractFactory (geoms) {
111738           if (geoms.isEmpty()) { return null }
111739           return geoms.iterator().next().getFactory()
111740         };
111741         GeometryCombiner.createList = function createList () {
111742           if (arguments.length === 2) {
111743             var obj0 = arguments[0];
111744             var obj1 = arguments[1];
111745             var list = new ArrayList();
111746             list.add(obj0);
111747             list.add(obj1);
111748             return list
111749           } else if (arguments.length === 3) {
111750             var obj0$1 = arguments[0];
111751             var obj1$1 = arguments[1];
111752             var obj2 = arguments[2];
111753             var list$1 = new ArrayList();
111754             list$1.add(obj0$1);
111755             list$1.add(obj1$1);
111756             list$1.add(obj2);
111757             return list$1
111758           }
111759         };
111760
111761         var CascadedPolygonUnion = function CascadedPolygonUnion () {
111762           this._inputPolys = null;
111763           this._geomFactory = null;
111764           var polys = arguments[0];
111765           this._inputPolys = polys;
111766           if (this._inputPolys === null) { this._inputPolys = new ArrayList(); }
111767         };
111768
111769         var staticAccessors$47 = { STRTREE_NODE_CAPACITY: { configurable: true } };
111770         CascadedPolygonUnion.prototype.reduceToGeometries = function reduceToGeometries (geomTree) {
111771             var this$1 = this;
111772
111773           var geoms = new ArrayList();
111774           for (var i = geomTree.iterator(); i.hasNext();) {
111775             var o = i.next();
111776             var geom = null;
111777             if (hasInterface(o, List)) {
111778               geom = this$1.unionTree(o);
111779             } else if (o instanceof Geometry) {
111780               geom = o;
111781             }
111782             geoms.add(geom);
111783           }
111784           return geoms
111785         };
111786         CascadedPolygonUnion.prototype.extractByEnvelope = function extractByEnvelope (env, geom, disjointGeoms) {
111787           var intersectingGeoms = new ArrayList();
111788           for (var i = 0; i < geom.getNumGeometries(); i++) {
111789             var elem = geom.getGeometryN(i);
111790             if (elem.getEnvelopeInternal().intersects(env)) { intersectingGeoms.add(elem); } else { disjointGeoms.add(elem); }
111791           }
111792           return this._geomFactory.buildGeometry(intersectingGeoms)
111793         };
111794         CascadedPolygonUnion.prototype.unionOptimized = function unionOptimized (g0, g1) {
111795           var g0Env = g0.getEnvelopeInternal();
111796           var g1Env = g1.getEnvelopeInternal();
111797           if (!g0Env.intersects(g1Env)) {
111798             var combo = GeometryCombiner.combine(g0, g1);
111799             return combo
111800           }
111801           if (g0.getNumGeometries() <= 1 && g1.getNumGeometries() <= 1) { return this.unionActual(g0, g1) }
111802           var commonEnv = g0Env.intersection(g1Env);
111803           return this.unionUsingEnvelopeIntersection(g0, g1, commonEnv)
111804         };
111805         CascadedPolygonUnion.prototype.union = function union () {
111806           if (this._inputPolys === null) { throw new Error('union() method cannot be called twice') }
111807           if (this._inputPolys.isEmpty()) { return null }
111808           this._geomFactory = this._inputPolys.iterator().next().getFactory();
111809           var index = new STRtree(CascadedPolygonUnion.STRTREE_NODE_CAPACITY);
111810           for (var i = this._inputPolys.iterator(); i.hasNext();) {
111811             var item = i.next();
111812             index.insert(item.getEnvelopeInternal(), item);
111813           }
111814           this._inputPolys = null;
111815           var itemTree = index.itemsTree();
111816           var unionAll = this.unionTree(itemTree);
111817           return unionAll
111818         };
111819         CascadedPolygonUnion.prototype.binaryUnion = function binaryUnion () {
111820           if (arguments.length === 1) {
111821             var geoms = arguments[0];
111822             return this.binaryUnion(geoms, 0, geoms.size())
111823           } else if (arguments.length === 3) {
111824             var geoms$1 = arguments[0];
111825             var start = arguments[1];
111826             var end = arguments[2];
111827             if (end - start <= 1) {
111828               var g0 = CascadedPolygonUnion.getGeometry(geoms$1, start);
111829               return this.unionSafe(g0, null)
111830             } else if (end - start === 2) {
111831               return this.unionSafe(CascadedPolygonUnion.getGeometry(geoms$1, start), CascadedPolygonUnion.getGeometry(geoms$1, start + 1))
111832             } else {
111833               var mid = Math.trunc((end + start) / 2);
111834               var g0$1 = this.binaryUnion(geoms$1, start, mid);
111835               var g1 = this.binaryUnion(geoms$1, mid, end);
111836               return this.unionSafe(g0$1, g1)
111837             }
111838           }
111839         };
111840         CascadedPolygonUnion.prototype.repeatedUnion = function repeatedUnion (geoms) {
111841           var union = null;
111842           for (var i = geoms.iterator(); i.hasNext();) {
111843             var g = i.next();
111844             if (union === null) { union = g.copy(); } else { union = union.union(g); }
111845           }
111846           return union
111847         };
111848         CascadedPolygonUnion.prototype.unionSafe = function unionSafe (g0, g1) {
111849           if (g0 === null && g1 === null) { return null }
111850           if (g0 === null) { return g1.copy() }
111851           if (g1 === null) { return g0.copy() }
111852           return this.unionOptimized(g0, g1)
111853         };
111854         CascadedPolygonUnion.prototype.unionActual = function unionActual (g0, g1) {
111855           return CascadedPolygonUnion.restrictToPolygons(g0.union(g1))
111856         };
111857         CascadedPolygonUnion.prototype.unionTree = function unionTree (geomTree) {
111858           var geoms = this.reduceToGeometries(geomTree);
111859           var union = this.binaryUnion(geoms);
111860           return union
111861         };
111862         CascadedPolygonUnion.prototype.unionUsingEnvelopeIntersection = function unionUsingEnvelopeIntersection (g0, g1, common) {
111863           var disjointPolys = new ArrayList();
111864           var g0Int = this.extractByEnvelope(common, g0, disjointPolys);
111865           var g1Int = this.extractByEnvelope(common, g1, disjointPolys);
111866           var union = this.unionActual(g0Int, g1Int);
111867           disjointPolys.add(union);
111868           var overallUnion = GeometryCombiner.combine(disjointPolys);
111869           return overallUnion
111870         };
111871         CascadedPolygonUnion.prototype.bufferUnion = function bufferUnion () {
111872           if (arguments.length === 1) {
111873             var geoms = arguments[0];
111874             var factory = geoms.get(0).getFactory();
111875             var gColl = factory.buildGeometry(geoms);
111876             var unionAll = gColl.buffer(0.0);
111877             return unionAll
111878           } else if (arguments.length === 2) {
111879             var g0 = arguments[0];
111880             var g1 = arguments[1];
111881             var factory$1 = g0.getFactory();
111882             var gColl$1 = factory$1.createGeometryCollection([g0, g1]);
111883             var unionAll$1 = gColl$1.buffer(0.0);
111884             return unionAll$1
111885           }
111886         };
111887         CascadedPolygonUnion.prototype.interfaces_ = function interfaces_ () {
111888           return []
111889         };
111890         CascadedPolygonUnion.prototype.getClass = function getClass () {
111891           return CascadedPolygonUnion
111892         };
111893         CascadedPolygonUnion.restrictToPolygons = function restrictToPolygons (g) {
111894           if (hasInterface(g, Polygonal)) {
111895             return g
111896           }
111897           var polygons = PolygonExtracter.getPolygons(g);
111898           if (polygons.size() === 1) { return polygons.get(0) }
111899           return g.getFactory().createMultiPolygon(GeometryFactory.toPolygonArray(polygons))
111900         };
111901         CascadedPolygonUnion.getGeometry = function getGeometry (list, index) {
111902           if (index >= list.size()) { return null }
111903           return list.get(index)
111904         };
111905         CascadedPolygonUnion.union = function union (polys) {
111906           var op = new CascadedPolygonUnion(polys);
111907           return op.union()
111908         };
111909         staticAccessors$47.STRTREE_NODE_CAPACITY.get = function () { return 4 };
111910
111911         Object.defineProperties( CascadedPolygonUnion, staticAccessors$47 );
111912
111913         var UnionOp = function UnionOp () {};
111914
111915         UnionOp.prototype.interfaces_ = function interfaces_ () {
111916           return []
111917         };
111918         UnionOp.prototype.getClass = function getClass () {
111919           return UnionOp
111920         };
111921         UnionOp.union = function union (g, other) {
111922           if (g.isEmpty() || other.isEmpty()) {
111923             if (g.isEmpty() && other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.UNION, g, other, g.getFactory()) }
111924             if (g.isEmpty()) { return other.copy() }
111925             if (other.isEmpty()) { return g.copy() }
111926           }
111927           g.checkNotGeometryCollection(g);
111928           g.checkNotGeometryCollection(other);
111929           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.UNION)
111930         };
111931
111932         /**
111933          * Earth Radius used with the Harvesine formula and approximates using a spherical (non-ellipsoid) Earth.
111934          */
111935
111936         /**
111937          * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}.
111938          *
111939          * @name feature
111940          * @param {Geometry} geometry input geometry
111941          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
111942          * @param {Object} [options={}] Optional Parameters
111943          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
111944          * @param {string|number} [options.id] Identifier associated with the Feature
111945          * @returns {Feature} a GeoJSON Feature
111946          * @example
111947          * var geometry = {
111948          *   "type": "Point",
111949          *   "coordinates": [110, 50]
111950          * };
111951          *
111952          * var feature = turf.feature(geometry);
111953          *
111954          * //=feature
111955          */
111956         function feature$1(geometry, properties, options) {
111957             // Optional Parameters
111958             options = options || {};
111959             if (!isObject$4(options)) { throw new Error('options is invalid'); }
111960             var bbox = options.bbox;
111961             var id = options.id;
111962
111963             // Validation
111964             if (geometry === undefined) { throw new Error('geometry is required'); }
111965             if (properties && properties.constructor !== Object) { throw new Error('properties must be an Object'); }
111966             if (bbox) { validateBBox(bbox); }
111967             if (id) { validateId(id); }
111968
111969             // Main
111970             var feat = {type: 'Feature'};
111971             if (id) { feat.id = id; }
111972             if (bbox) { feat.bbox = bbox; }
111973             feat.properties = properties || {};
111974             feat.geometry = geometry;
111975             return feat;
111976         }
111977
111978         /**
111979          * isNumber
111980          *
111981          * @param {*} num Number to validate
111982          * @returns {boolean} true/false
111983          * @example
111984          * turf.isNumber(123)
111985          * //=true
111986          * turf.isNumber('foo')
111987          * //=false
111988          */
111989         function isNumber$1(num) {
111990             return !isNaN(num) && num !== null && !Array.isArray(num);
111991         }
111992
111993         /**
111994          * isObject
111995          *
111996          * @param {*} input variable to validate
111997          * @returns {boolean} true/false
111998          * @example
111999          * turf.isObject({elevation: 10})
112000          * //=true
112001          * turf.isObject('foo')
112002          * //=false
112003          */
112004         function isObject$4(input) {
112005             return (!!input) && (input.constructor === Object);
112006         }
112007
112008         /**
112009          * Validate BBox
112010          *
112011          * @private
112012          * @param {Array<number>} bbox BBox to validate
112013          * @returns {void}
112014          * @throws Error if BBox is not valid
112015          * @example
112016          * validateBBox([-180, -40, 110, 50])
112017          * //=OK
112018          * validateBBox([-180, -40])
112019          * //=Error
112020          * validateBBox('Foo')
112021          * //=Error
112022          * validateBBox(5)
112023          * //=Error
112024          * validateBBox(null)
112025          * //=Error
112026          * validateBBox(undefined)
112027          * //=Error
112028          */
112029         function validateBBox(bbox) {
112030             if (!bbox) { throw new Error('bbox is required'); }
112031             if (!Array.isArray(bbox)) { throw new Error('bbox must be an Array'); }
112032             if (bbox.length !== 4 && bbox.length !== 6) { throw new Error('bbox must be an Array of 4 or 6 numbers'); }
112033             bbox.forEach(function (num) {
112034                 if (!isNumber$1(num)) { throw new Error('bbox must only contain numbers'); }
112035             });
112036         }
112037
112038         /**
112039          * Validate Id
112040          *
112041          * @private
112042          * @param {string|number} id Id to validate
112043          * @returns {void}
112044          * @throws Error if Id is not valid
112045          * @example
112046          * validateId([-180, -40, 110, 50])
112047          * //=Error
112048          * validateId([-180, -40])
112049          * //=Error
112050          * validateId('Foo')
112051          * //=OK
112052          * validateId(5)
112053          * //=OK
112054          * validateId(null)
112055          * //=Error
112056          * validateId(undefined)
112057          * //=Error
112058          */
112059         function validateId(id) {
112060             if (!id) { throw new Error('id is required'); }
112061             if (['string', 'number'].indexOf(typeof id) === -1) { throw new Error('id must be a number or a string'); }
112062         }
112063
112064         /**
112065          * Callback for geomEach
112066          *
112067          * @callback geomEachCallback
112068          * @param {Geometry} currentGeometry The current Geometry being processed.
112069          * @param {number} featureIndex The current index of the Feature being processed.
112070          * @param {Object} featureProperties The current Feature Properties being processed.
112071          * @param {Array<number>} featureBBox The current Feature BBox being processed.
112072          * @param {number|string} featureId The current Feature Id being processed.
112073          */
112074
112075         /**
112076          * Iterate over each geometry in any GeoJSON object, similar to Array.forEach()
112077          *
112078          * @name geomEach
112079          * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
112080          * @param {Function} callback a method that takes (currentGeometry, featureIndex, featureProperties, featureBBox, featureId)
112081          * @returns {void}
112082          * @example
112083          * var features = turf.featureCollection([
112084          *     turf.point([26, 37], {foo: 'bar'}),
112085          *     turf.point([36, 53], {hello: 'world'})
112086          * ]);
112087          *
112088          * turf.geomEach(features, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
112089          *   //=currentGeometry
112090          *   //=featureIndex
112091          *   //=featureProperties
112092          *   //=featureBBox
112093          *   //=featureId
112094          * });
112095          */
112096         function geomEach(geojson, callback) {
112097             var i, j, g, geometry, stopG,
112098                 geometryMaybeCollection,
112099                 isGeometryCollection,
112100                 featureProperties,
112101                 featureBBox,
112102                 featureId,
112103                 featureIndex = 0,
112104                 isFeatureCollection = geojson.type === 'FeatureCollection',
112105                 isFeature = geojson.type === 'Feature',
112106                 stop = isFeatureCollection ? geojson.features.length : 1;
112107
112108             // This logic may look a little weird. The reason why it is that way
112109             // is because it's trying to be fast. GeoJSON supports multiple kinds
112110             // of objects at its root: FeatureCollection, Features, Geometries.
112111             // This function has the responsibility of handling all of them, and that
112112             // means that some of the `for` loops you see below actually just don't apply
112113             // to certain inputs. For instance, if you give this just a
112114             // Point geometry, then both loops are short-circuited and all we do
112115             // is gradually rename the input until it's called 'geometry'.
112116             //
112117             // This also aims to allocate as few resources as possible: just a
112118             // few numbers and booleans, rather than any temporary arrays as would
112119             // be required with the normalization approach.
112120             for (i = 0; i < stop; i++) {
112121
112122                 geometryMaybeCollection = (isFeatureCollection ? geojson.features[i].geometry :
112123                     (isFeature ? geojson.geometry : geojson));
112124                 featureProperties = (isFeatureCollection ? geojson.features[i].properties :
112125                     (isFeature ? geojson.properties : {}));
112126                 featureBBox = (isFeatureCollection ? geojson.features[i].bbox :
112127                     (isFeature ? geojson.bbox : undefined));
112128                 featureId = (isFeatureCollection ? geojson.features[i].id :
112129                     (isFeature ? geojson.id : undefined));
112130                 isGeometryCollection = (geometryMaybeCollection) ? geometryMaybeCollection.type === 'GeometryCollection' : false;
112131                 stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
112132
112133                 for (g = 0; g < stopG; g++) {
112134                     geometry = isGeometryCollection ?
112135                         geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
112136
112137                     // Handle null Geometry
112138                     if (geometry === null) {
112139                         if (callback(null, featureIndex, featureProperties, featureBBox, featureId) === false) { return false; }
112140                         continue;
112141                     }
112142                     switch (geometry.type) {
112143                     case 'Point':
112144                     case 'LineString':
112145                     case 'MultiPoint':
112146                     case 'Polygon':
112147                     case 'MultiLineString':
112148                     case 'MultiPolygon': {
112149                         if (callback(geometry, featureIndex, featureProperties, featureBBox, featureId) === false) { return false; }
112150                         break;
112151                     }
112152                     case 'GeometryCollection': {
112153                         for (j = 0; j < geometry.geometries.length; j++) {
112154                             if (callback(geometry.geometries[j], featureIndex, featureProperties, featureBBox, featureId) === false) { return false; }
112155                         }
112156                         break;
112157                     }
112158                     default:
112159                         throw new Error('Unknown Geometry Type');
112160                     }
112161                 }
112162                 // Only increase `featureIndex` per each feature
112163                 featureIndex++;
112164             }
112165         }
112166
112167         /**
112168          * Callback for geomReduce
112169          *
112170          * The first time the callback function is called, the values provided as arguments depend
112171          * on whether the reduce method has an initialValue argument.
112172          *
112173          * If an initialValue is provided to the reduce method:
112174          *  - The previousValue argument is initialValue.
112175          *  - The currentValue argument is the value of the first element present in the array.
112176          *
112177          * If an initialValue is not provided:
112178          *  - The previousValue argument is the value of the first element present in the array.
112179          *  - The currentValue argument is the value of the second element present in the array.
112180          *
112181          * @callback geomReduceCallback
112182          * @param {*} previousValue The accumulated value previously returned in the last invocation
112183          * of the callback, or initialValue, if supplied.
112184          * @param {Geometry} currentGeometry The current Geometry being processed.
112185          * @param {number} featureIndex The current index of the Feature being processed.
112186          * @param {Object} featureProperties The current Feature Properties being processed.
112187          * @param {Array<number>} featureBBox The current Feature BBox being processed.
112188          * @param {number|string} featureId The current Feature Id being processed.
112189          */
112190
112191         /**
112192          * Reduce geometry in any GeoJSON object, similar to Array.reduce().
112193          *
112194          * @name geomReduce
112195          * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
112196          * @param {Function} callback a method that takes (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId)
112197          * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
112198          * @returns {*} The value that results from the reduction.
112199          * @example
112200          * var features = turf.featureCollection([
112201          *     turf.point([26, 37], {foo: 'bar'}),
112202          *     turf.point([36, 53], {hello: 'world'})
112203          * ]);
112204          *
112205          * turf.geomReduce(features, function (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
112206          *   //=previousValue
112207          *   //=currentGeometry
112208          *   //=featureIndex
112209          *   //=featureProperties
112210          *   //=featureBBox
112211          *   //=featureId
112212          *   return currentGeometry
112213          * });
112214          */
112215         function geomReduce(geojson, callback, initialValue) {
112216             var previousValue = initialValue;
112217             geomEach(geojson, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
112218                 if (featureIndex === 0 && initialValue === undefined) { previousValue = currentGeometry; }
112219                 else { previousValue = callback(previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId); }
112220             });
112221             return previousValue;
112222         }
112223
112224         /**
112225          * Callback for flattenEach
112226          *
112227          * @callback flattenEachCallback
112228          * @param {Feature} currentFeature The current flattened feature being processed.
112229          * @param {number} featureIndex The current index of the Feature being processed.
112230          * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed.
112231          */
112232
112233         /**
112234          * Iterate over flattened features in any GeoJSON object, similar to
112235          * Array.forEach.
112236          *
112237          * @name flattenEach
112238          * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
112239          * @param {Function} callback a method that takes (currentFeature, featureIndex, multiFeatureIndex)
112240          * @example
112241          * var features = turf.featureCollection([
112242          *     turf.point([26, 37], {foo: 'bar'}),
112243          *     turf.multiPoint([[40, 30], [36, 53]], {hello: 'world'})
112244          * ]);
112245          *
112246          * turf.flattenEach(features, function (currentFeature, featureIndex, multiFeatureIndex) {
112247          *   //=currentFeature
112248          *   //=featureIndex
112249          *   //=multiFeatureIndex
112250          * });
112251          */
112252         function flattenEach(geojson, callback) {
112253             geomEach(geojson, function (geometry, featureIndex, properties, bbox, id) {
112254                 // Callback for single geometry
112255                 var type = (geometry === null) ? null : geometry.type;
112256                 switch (type) {
112257                 case null:
112258                 case 'Point':
112259                 case 'LineString':
112260                 case 'Polygon':
112261                     if (callback(feature$1(geometry, properties, {bbox: bbox, id: id}), featureIndex, 0) === false) { return false; }
112262                     return;
112263                 }
112264
112265                 var geomType;
112266
112267                 // Callback for multi-geometry
112268                 switch (type) {
112269                 case 'MultiPoint':
112270                     geomType = 'Point';
112271                     break;
112272                 case 'MultiLineString':
112273                     geomType = 'LineString';
112274                     break;
112275                 case 'MultiPolygon':
112276                     geomType = 'Polygon';
112277                     break;
112278                 }
112279
112280                 for (var multiFeatureIndex = 0; multiFeatureIndex < geometry.coordinates.length; multiFeatureIndex++) {
112281                     var coordinate = geometry.coordinates[multiFeatureIndex];
112282                     var geom = {
112283                         type: geomType,
112284                         coordinates: coordinate
112285                     };
112286                     if (callback(feature$1(geom, properties), featureIndex, multiFeatureIndex) === false) { return false; }
112287                 }
112288             });
112289         }
112290
112291         /**
112292          * Takes one or more features and returns their area in square meters.
112293          *
112294          * @name area
112295          * @param {GeoJSON} geojson input GeoJSON feature(s)
112296          * @returns {number} area in square meters
112297          * @example
112298          * var polygon = turf.polygon([[[125, -15], [113, -22], [154, -27], [144, -15], [125, -15]]]);
112299          *
112300          * var area = turf.area(polygon);
112301          *
112302          * //addToMap
112303          * var addToMap = [polygon]
112304          * polygon.properties.area = area
112305          */
112306         function area(geojson) {
112307             return geomReduce(geojson, function (value, geom) {
112308                 return value + calculateArea(geom);
112309             }, 0);
112310         }
112311
112312         var RADIUS$1 = 6378137;
112313         // var FLATTENING_DENOM = 298.257223563;
112314         // var FLATTENING = 1 / FLATTENING_DENOM;
112315         // var POLAR_RADIUS = RADIUS * (1 - FLATTENING);
112316
112317         /**
112318          * Calculate Area
112319          *
112320          * @private
112321          * @param {GeoJSON} geojson GeoJSON
112322          * @returns {number} area
112323          */
112324         function calculateArea(geojson) {
112325             var area = 0, i;
112326             switch (geojson.type) {
112327             case 'Polygon':
112328                 return polygonArea$1(geojson.coordinates);
112329             case 'MultiPolygon':
112330                 for (i = 0; i < geojson.coordinates.length; i++) {
112331                     area += polygonArea$1(geojson.coordinates[i]);
112332                 }
112333                 return area;
112334             case 'Point':
112335             case 'MultiPoint':
112336             case 'LineString':
112337             case 'MultiLineString':
112338                 return 0;
112339             case 'GeometryCollection':
112340                 for (i = 0; i < geojson.geometries.length; i++) {
112341                     area += calculateArea(geojson.geometries[i]);
112342                 }
112343                 return area;
112344             }
112345         }
112346
112347         function polygonArea$1(coords) {
112348             var area = 0;
112349             if (coords && coords.length > 0) {
112350                 area += Math.abs(ringArea$1(coords[0]));
112351                 for (var i = 1; i < coords.length; i++) {
112352                     area -= Math.abs(ringArea$1(coords[i]));
112353                 }
112354             }
112355             return area;
112356         }
112357
112358         /**
112359          * @private
112360          * Calculate the approximate area of the polygon were it projected onto the earth.
112361          * Note that this area will be positive if ring is oriented clockwise, otherwise it will be negative.
112362          *
112363          * Reference:
112364          * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
112365          * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
112366          *
112367          * @param {Array<Array<number>>} coords Ring Coordinates
112368          * @returns {number} The approximate signed geodesic area of the polygon in square meters.
112369          */
112370         function ringArea$1(coords) {
112371             var p1;
112372             var p2;
112373             var p3;
112374             var lowerIndex;
112375             var middleIndex;
112376             var upperIndex;
112377             var i;
112378             var area = 0;
112379             var coordsLength = coords.length;
112380
112381             if (coordsLength > 2) {
112382                 for (i = 0; i < coordsLength; i++) {
112383                     if (i === coordsLength - 2) { // i = N-2
112384                         lowerIndex = coordsLength - 2;
112385                         middleIndex = coordsLength - 1;
112386                         upperIndex = 0;
112387                     } else if (i === coordsLength - 1) { // i = N-1
112388                         lowerIndex = coordsLength - 1;
112389                         middleIndex = 0;
112390                         upperIndex = 1;
112391                     } else { // i = 0 to N-3
112392                         lowerIndex = i;
112393                         middleIndex = i + 1;
112394                         upperIndex = i + 2;
112395                     }
112396                     p1 = coords[lowerIndex];
112397                     p2 = coords[middleIndex];
112398                     p3 = coords[upperIndex];
112399                     area += (rad$1(p3[0]) - rad$1(p1[0])) * Math.sin(rad$1(p2[1]));
112400                 }
112401
112402                 area = area * RADIUS$1 * RADIUS$1 / 2;
112403             }
112404
112405             return area;
112406         }
112407
112408         function rad$1(_) {
112409             return _ * Math.PI / 180;
112410         }
112411
112412         /**
112413          * Get Geometry from Feature or Geometry Object
112414          *
112415          * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object
112416          * @returns {Geometry|null} GeoJSON Geometry Object
112417          * @throws {Error} if geojson is not a Feature or Geometry Object
112418          * @example
112419          * var point = {
112420          *   "type": "Feature",
112421          *   "properties": {},
112422          *   "geometry": {
112423          *     "type": "Point",
112424          *     "coordinates": [110, 40]
112425          *   }
112426          * }
112427          * var geom = turf.getGeom(point)
112428          * //={"type": "Point", "coordinates": [110, 40]}
112429          */
112430         function getGeom(geojson) {
112431             if (!geojson) { throw new Error('geojson is required'); }
112432             if (geojson.geometry !== undefined) { return geojson.geometry; }
112433             if (geojson.coordinates || geojson.geometries) { return geojson; }
112434             throw new Error('geojson must be a valid Feature or Geometry Object');
112435         }
112436
112437         /**
112438          * Finds the difference between two {@link Polygon|polygons} by clipping the second polygon from the first.
112439          *
112440          * @name difference
112441          * @param {Feature<Polygon|MultiPolygon>} polygon1 input Polygon feature
112442          * @param {Feature<Polygon|MultiPolygon>} polygon2 Polygon feature to difference from polygon1
112443          * @returns {Feature<Polygon|MultiPolygon>|null} a Polygon or MultiPolygon feature showing the area of `polygon1` excluding the area of `polygon2` (if empty returns `null`)
112444          * @example
112445          * var polygon1 = turf.polygon([[
112446          *   [128, -26],
112447          *   [141, -26],
112448          *   [141, -21],
112449          *   [128, -21],
112450          *   [128, -26]
112451          * ]], {
112452          *   "fill": "#F00",
112453          *   "fill-opacity": 0.1
112454          * });
112455          * var polygon2 = turf.polygon([[
112456          *   [126, -28],
112457          *   [140, -28],
112458          *   [140, -20],
112459          *   [126, -20],
112460          *   [126, -28]
112461          * ]], {
112462          *   "fill": "#00F",
112463          *   "fill-opacity": 0.1
112464          * });
112465          *
112466          * var difference = turf.difference(polygon1, polygon2);
112467          *
112468          * //addToMap
112469          * var addToMap = [polygon1, polygon2, difference];
112470          */
112471         function difference(polygon1, polygon2) {
112472             var geom1 = getGeom(polygon1);
112473             var geom2 = getGeom(polygon2);
112474             var properties = polygon1.properties || {};
112475
112476             // Issue #721 - JSTS can't handle empty polygons
112477             geom1 = removeEmptyPolygon(geom1);
112478             geom2 = removeEmptyPolygon(geom2);
112479             if (!geom1) { return null; }
112480             if (!geom2) { return feature$1(geom1, properties); }
112481
112482             // JSTS difference operation
112483             var reader = new GeoJSONReader();
112484             var a = reader.read(geom1);
112485             var b = reader.read(geom2);
112486             var differenced = OverlayOp.difference(a, b);
112487             if (differenced.isEmpty()) { return null; }
112488             var writer = new GeoJSONWriter();
112489             var geom = writer.write(differenced);
112490
112491             return feature$1(geom, properties);
112492         }
112493
112494         /**
112495          * Detect Empty Polygon
112496          *
112497          * @private
112498          * @param {Geometry<Polygon|MultiPolygon>} geom Geometry Object
112499          * @returns {Geometry<Polygon|MultiPolygon>|null} removed any polygons with no areas
112500          */
112501         function removeEmptyPolygon(geom) {
112502             switch (geom.type) {
112503             case 'Polygon':
112504                 if (area(geom) > 1) { return geom; }
112505                 return null;
112506             case 'MultiPolygon':
112507                 var coordinates = [];
112508                 flattenEach(geom, function (feature$$1) {
112509                     if (area(feature$$1) > 1) { coordinates.push(feature$$1.geometry.coordinates); }
112510                 });
112511                 if (coordinates.length) { return {type: 'MultiPolygon', coordinates: coordinates}; }
112512             }
112513         }
112514
112515         /**
112516          * 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.
112517          *
112518          * @name union
112519          * @param {...Feature<Polygon>} A polygon to combine
112520          * @returns {Feature<(Polygon|MultiPolygon)>} a combined {@link Polygon} or {@link MultiPolygon} feature
112521          * @example
112522          * var poly1 = turf.polygon([[
112523          *     [-82.574787, 35.594087],
112524          *     [-82.574787, 35.615581],
112525          *     [-82.545261, 35.615581],
112526          *     [-82.545261, 35.594087],
112527          *     [-82.574787, 35.594087]
112528          * ]], {"fill": "#0f0"});
112529          * var poly2 = turf.polygon([[
112530          *     [-82.560024, 35.585153],
112531          *     [-82.560024, 35.602602],
112532          *     [-82.52964, 35.602602],
112533          *     [-82.52964, 35.585153],
112534          *     [-82.560024, 35.585153]
112535          * ]], {"fill": "#00f"});
112536          *
112537          * var union = turf.union(poly1, poly2);
112538          *
112539          * //addToMap
112540          * var addToMap = [poly1, poly2, union];
112541          */
112542         function union$1() {
112543             var arguments$1 = arguments;
112544
112545             var reader = new GeoJSONReader();
112546             var result = reader.read(JSON.stringify(arguments[0].geometry));
112547
112548             for (var i = 1; i < arguments.length; i++) {
112549                 result = UnionOp.union(result, reader.read(JSON.stringify(arguments$1[i].geometry)));
112550             }
112551
112552             var writer = new GeoJSONWriter();
112553             result = writer.write(result);
112554
112555             return {
112556                 type: 'Feature',
112557                 geometry: result,
112558                 properties: arguments[0].properties
112559             };
112560         }
112561
112562         // Reduce an array of locations into a single GeoJSON feature
112563         function _locationReducer(accumulator, location) {
112564           /* eslint-disable no-console, no-invalid-this */
112565           var result;
112566           try {
112567             var resolved = this.resolveLocation(location);
112568             if (!resolved || !resolved.feature) {
112569               console.warn(("Warning:  Couldn't resolve location \"" + location + "\""));
112570               return accumulator;
112571             }
112572             result = !accumulator ? resolved.feature : union$1(accumulator, resolved.feature);
112573           } catch (e) {
112574             console.warn(("Warning:  Error resolving location \"" + location + "\""));
112575             console.warn(e);
112576             result = accumulator;
112577           }
112578
112579           return result;
112580           /* eslint-enable no-console, no-invalid-this */
112581         }
112582
112583
112584
112585         function _cloneDeep(obj) {
112586           return JSON.parse(JSON.stringify(obj));
112587         }
112588
112589
112590         var defaultExport = function defaultExport(fc) {
112591           var this$1 = this;
112592
112593           this._cache = {};
112594
112595           // process input FeatureCollection
112596           if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) {
112597             fc.features.forEach(function (feature) {
112598               feature.properties = feature.properties || {};
112599               var props = feature.properties;
112600
112601               // get `id` from either `id` or `properties`
112602               var id = feature.id || props.id;
112603               if (!id || !/^\S+\.geojson$/i.test(id)) { return; }
112604
112605               // ensure id exists and is lowercase
112606               id = id.toLowerCase();
112607               feature.id = id;
112608               props.id = id;
112609
112610               // ensure area property exists
112611               if (!props.area) {
112612                 var area = geojsonArea.geometry(feature.geometry) / 1e6;// m² to km²
112613                 props.area = Number(area.toFixed(2));
112614               }
112615
112616               this$1._cache[id] = feature;
112617             });
112618           }
112619
112620           // Replace CountryCoder world geometry to have a polygon covering the world.
112621           var world = _cloneDeep(feature('Q2'));
112622           world.geometry = {
112623             type: 'Polygon',
112624             coordinates: [[[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]]
112625           };
112626           world.id = 'Q2';
112627           world.properties.id = 'Q2';
112628           world.properties.area = geojsonArea.geometry(world.geometry) / 1e6;// m² to km²
112629           this._cache.Q2 = world;
112630         };
112631
112632
112633         // validateLocation
112634         //
112635         // Pass a `location` identifier
112636         // Returns a result like
112637         // {
112638         //   type:   'point', 'geojson', or 'countrycoder'
112639         //   location:the queried location
112640         //   id:      a unique identifier
112641         // }
112642         //or `null` if the location is invalid
112643         //
112644         defaultExport.prototype.validateLocation = function validateLocation (location) {
112645           if (Array.isArray(location)) { // a [lon,lat] coordinate pair?
112646             if (location.length === 2 && Number.isFinite(location[0]) && Number.isFinite(location[1]) &&
112647               location[0] >= -180 && location[0] <= 180 && location[1] >= -90 && location[1] <= 90
112648             ) {
112649               var id = '[' + location.toString() + ']';
112650               return { type: 'point', location: location, id: id };
112651             }
112652
112653           } else if (typeof location === 'string' && /^\S+\.geojson$/i.test(location)) { // a .geojson filename?
112654             var id$1 = location.toLowerCase();
112655             if (this._cache[id$1]) {
112656               return { type: 'geojson', location: location, id: id$1 };
112657             }
112658
112659           } else if (typeof location === 'string' || typeof location === 'number') { // a country-coder value?
112660             var feature$1 = feature(location);
112661             if (feature$1) {
112662               // Use wikidata QID as the identifier, since that seems to be the only
112663               // property that everything in CountryCoder is guaranteed to have.
112664               var id$2 = feature$1.properties.wikidata;
112665               return { type: 'countrycoder', location: location, id: id$2 };
112666             }
112667           }
112668
112669           return null;
112670         };
112671
112672
112673         // resolveLocation
112674         //
112675         // Pass a `location` identifier
112676         // Returns a result like
112677         // {
112678         //   type:    'point', 'geojson', or 'countrycoder'
112679         //   location:the queried location
112680         //   id:      a unique identifier
112681         //   feature: the geojson feature
112682         // }
112683         //or `null` if the location is invalid
112684         //
112685         defaultExport.prototype.resolveLocation = function resolveLocation (location) {
112686           var valid = this.validateLocation(location);
112687           if (!valid) { return null; }
112688
112689           // return a result from cache if we can
112690           if (this._cache[valid.id]) {
112691             return Object.assign(valid, { feature: this._cache[valid.id] });
112692           }
112693
112694           // a [lon,lat] coordinate pair?
112695           if (valid.type === 'point') {
112696             var RADIUS = 25000;// meters
112697             var EDGES = 10;
112698             var PRECISION = 3;
112699             var area = Math.PI * RADIUS * RADIUS / 1e6;   // m² to km²
112700             var feature$1 = this._cache[valid.id] = geojsonPrecision({
112701               type: 'Feature',
112702               id: valid.id,
112703               properties: { id: valid.id, area: Number(area.toFixed(2)) },
112704               geometry: circleToPolygon(location, RADIUS, EDGES)
112705             }, PRECISION);
112706             return Object.assign(valid, { feature: feature$1 });
112707
112708           // a .geojson filename?
112709           } else if (valid.type === 'geojson') ; else if (valid.type === 'countrycoder') {
112710             var feature$1$1 = _cloneDeep(feature(valid.id));
112711             var props = feature$1$1.properties;
112712
112713             // -> This block of code is weird and requires some explanation. <-
112714             // CountryCoder includes higher level features which are made up of members.
112715             // These features don't have their own geometry, but CountryCoder provides an
112716             // `aggregateFeature` method to combine these members into a MultiPolygon.
112717             // BUT, when we try to actually work with these aggregated MultiPolygons,
112718             // Turf/JSTS gets crashy because of topography bugs.
112719             // SO, we'll aggregate the features ourselves by unioning them together.
112720             // This approach also has the benefit of removing all the internal boaders and
112721             // simplifying the regional polygons a lot.
112722             if (Array.isArray(props.members)) {
112723               var seed = feature$1$1.geometry ? feature$1$1 : null;
112724               var aggregate = props.members.reduce(_locationReducer.bind(this), seed);
112725               feature$1$1.geometry = aggregate.geometry;
112726             }
112727
112728             // ensure area property exists
112729             if (!props.area) {
112730               var area$1 = geojsonArea.geometry(feature$1$1.geometry) / 1e6;// m² to km²
112731               props.area = Number(area$1.toFixed(2));
112732             }
112733
112734             // ensure id property exists
112735             feature$1$1.id = valid.id;
112736             props.id = valid.id;
112737
112738             this._cache[valid.id] = feature$1$1;
112739             return Object.assign(valid, { feature: feature$1$1 });
112740           }
112741
112742           return null;
112743         };
112744
112745
112746         // resolveLocationSet
112747         //
112748         // Pass a `locationSet` Object like:
112749         // `{ include: [ Array ], exclude: [ Array ] }`
112750         // Returns a stable identifier string of the form:
112751         // "+[included]-[excluded]"
112752         //
112753         defaultExport.prototype.resolveLocationSet = function resolveLocationSet (locationSet) {
112754           locationSet = locationSet || {};
112755           var resolve = this.resolveLocation.bind(this);
112756           var include = (locationSet.include || []).map(resolve).filter(Boolean);
112757           var exclude = (locationSet.exclude || []).map(resolve).filter(Boolean);
112758
112759           if (!include.length) {
112760             include = [resolve('Q2')]; // default to 'the world'
112761           }
112762
112763           // return quickly if it's a single included location..
112764           if (include.length === 1 && exclude.length === 0) {
112765             return include[0].feature;
112766           }
112767
112768           // generate stable identifier
112769           include.sort(sortFeatures);
112770           var id = '+[' + include.map(function (d) { return d.id; }).join(',') + ']';
112771           if (exclude.length) {
112772             exclude.sort(sortFeatures);
112773             id += '-[' + exclude.map(function (d) { return d.id; }).join(',') + ']';
112774           }
112775
112776           // return cached?
112777           if (this._cache[id]) {
112778             return this._cache[id];
112779           }
112780
112781           // calculate unions
112782           var includeGeoJSON = include.map(function (d) { return d.location; }).reduce(_locationReducer.bind(this), null);
112783           var excludeGeoJSON = exclude.map(function (d) { return d.location; }).reduce(_locationReducer.bind(this), null);
112784
112785           // calculate difference, update area and return result
112786           var resultGeoJSON = excludeGeoJSON ? difference(includeGeoJSON, excludeGeoJSON) : includeGeoJSON;
112787           var area = geojsonArea.geometry(resultGeoJSON.geometry) / 1e6;// m² to km²
112788           resultGeoJSON.id = id;
112789           resultGeoJSON.properties = { id: id, area: Number(area.toFixed(2)) };
112790
112791           return this._cache[id] = resultGeoJSON;
112792
112793
112794           // Sorting the location lists is ok because they end up unioned together.
112795           // This sorting makes it possible to generate a deterministic id.
112796           function sortFeatures(a, b) {
112797             var rank = { countrycoder: 1, geojson: 2, point: 3 };
112798             var aRank = rank[a.type];
112799             var bRank = rank[b.type];
112800
112801             return (aRank > bRank) ? 1
112802               : (aRank < bRank) ? -1
112803               : a.id.localeCompare(b.id);
112804           }
112805         };
112806
112807
112808         defaultExport.prototype.cache = function cache () {
112809           return this._cache;
112810         };
112811
112812         var _oci = null;
112813
112814         function uiSuccess(context) {
112815           var MAXEVENTS = 2;
112816           var dispatch$1 = dispatch('cancel');
112817           var _changeset;
112818           var _location;
112819           ensureOSMCommunityIndex();   // start fetching the data
112820
112821
112822           function ensureOSMCommunityIndex() {
112823             var data = _mainFileFetcher;
112824             return Promise.all([ data.get('oci_resources'), data.get('oci_features') ])
112825               .then(function (vals) {
112826                 if (_oci) { return _oci; }
112827
112828                 var ociResources = vals[0].resources;
112829                 var loco = new defaultExport(vals[1]);
112830                 var ociFeatures = {};
112831
112832                 Object.values(ociResources).forEach(function (resource) {
112833                   var feature = loco.resolveLocationSet(resource.locationSet);
112834                   var ociFeature = ociFeatures[feature.id];
112835                   if (!ociFeature) {
112836                     ociFeature = JSON.parse(JSON.stringify(feature));  // deep clone
112837                     ociFeature.properties.resourceIDs = new Set();
112838                     ociFeatures[feature.id] = ociFeature;
112839                   }
112840                   ociFeature.properties.resourceIDs.add(resource.id);
112841                 });
112842
112843                 return _oci = {
112844                   features: ociFeatures,
112845                   resources: ociResources,
112846                   query: whichPolygon_1({ type: 'FeatureCollection', features: Object.values(ociFeatures) })
112847                 };
112848               });
112849           }
112850
112851
112852           // string-to-date parsing in JavaScript is weird
112853           function parseEventDate(when) {
112854             if (!when) { return; }
112855
112856             var raw = when.trim();
112857             if (!raw) { return; }
112858
112859             if (!/Z$/.test(raw)) {   // if no trailing 'Z', add one
112860               raw += 'Z';            // this forces date to be parsed as a UTC date
112861             }
112862
112863             var parsed = new Date(raw);
112864             return new Date(parsed.toUTCString().substr(0, 25));  // convert to local timezone
112865           }
112866
112867
112868           function success(selection) {
112869             var header = selection
112870               .append('div')
112871               .attr('class', 'header fillL');
112872
112873             header
112874               .append('h3')
112875               .text(_t('success.just_edited'));
112876
112877             header
112878               .append('button')
112879               .attr('class', 'close')
112880               .on('click', function () { return dispatch$1.call('cancel'); })
112881               .call(svgIcon('#iD-icon-close'));
112882
112883             var body = selection
112884               .append('div')
112885               .attr('class', 'body save-success fillL');
112886
112887             var summary = body
112888               .append('div')
112889               .attr('class', 'save-summary');
112890
112891             summary
112892               .append('h3')
112893               .text(_t('success.thank_you' + (_location ? '_location' : ''), { where: _location }));
112894
112895             summary
112896               .append('p')
112897               .text(_t('success.help_html'))
112898               .append('a')
112899               .attr('class', 'link-out')
112900               .attr('target', '_blank')
112901               .attr('tabindex', -1)
112902               .attr('href', _t('success.help_link_url'))
112903               .call(svgIcon('#iD-icon-out-link', 'inline'))
112904               .append('span')
112905               .text(_t('success.help_link_text'));
112906
112907             var osm = context.connection();
112908             if (!osm) { return; }
112909
112910             var changesetURL = osm.changesetURL(_changeset.id);
112911
112912             var table = summary
112913               .append('table')
112914               .attr('class', 'summary-table');
112915
112916             var row = table
112917               .append('tr')
112918               .attr('class', 'summary-row');
112919
112920             row
112921               .append('td')
112922               .attr('class', 'cell-icon summary-icon')
112923               .append('a')
112924               .attr('target', '_blank')
112925               .attr('href', changesetURL)
112926               .append('svg')
112927               .attr('class', 'logo-small')
112928               .append('use')
112929               .attr('xlink:href', '#iD-logo-osm');
112930
112931             var summaryDetail = row
112932               .append('td')
112933               .attr('class', 'cell-detail summary-detail');
112934
112935             summaryDetail
112936               .append('a')
112937               .attr('class', 'cell-detail summary-view-on-osm')
112938               .attr('target', '_blank')
112939               .attr('href', changesetURL)
112940               .text(_t('success.view_on_osm'));
112941
112942             summaryDetail
112943               .append('div')
112944               .html(_t('success.changeset_id', {
112945                 changeset_id: ("<a href=\"" + changesetURL + "\" target=\"_blank\">" + (_changeset.id) + "</a>")
112946               }));
112947
112948
112949             // Get OSM community index features intersecting the map..
112950             ensureOSMCommunityIndex()
112951               .then(function (oci) {
112952                 var communities = [];
112953                 var properties = oci.query(context.map().center(), true) || [];
112954
112955                 // Gather the communities from the result
112956                 properties.forEach(function (props) {
112957                   var resourceIDs = Array.from(props.resourceIDs);
112958                   resourceIDs.forEach(function (resourceID) {
112959                     var resource = oci.resources[resourceID];
112960                     communities.push({
112961                       area: props.area || Infinity,
112962                       order: resource.order || 0,
112963                       resource: resource
112964                     });
112965                   });
112966                 });
112967
112968                 // sort communities by feature area ascending, community order descending
112969                 communities.sort(function (a, b) { return a.area - b.area || b.order - a.order; });
112970
112971                 body
112972                   .call(showCommunityLinks, communities.map(function (c) { return c.resource; }));
112973               });
112974           }
112975
112976
112977           function showCommunityLinks(selection, resources) {
112978             var communityLinks = selection
112979               .append('div')
112980               .attr('class', 'save-communityLinks');
112981
112982             communityLinks
112983               .append('h3')
112984               .text(_t('success.like_osm'));
112985
112986             var table = communityLinks
112987               .append('table')
112988               .attr('class', 'community-table');
112989
112990             var row = table.selectAll('.community-row')
112991               .data(resources);
112992
112993             var rowEnter = row.enter()
112994               .append('tr')
112995               .attr('class', 'community-row');
112996
112997             rowEnter
112998               .append('td')
112999               .attr('class', 'cell-icon community-icon')
113000               .append('a')
113001               .attr('target', '_blank')
113002               .attr('href', function (d) { return d.url; })
113003               .append('svg')
113004               .attr('class', 'logo-small')
113005               .append('use')
113006               .attr('xlink:href', function (d) { return ("#community-" + (d.type)); });
113007
113008             var communityDetail = rowEnter
113009               .append('td')
113010               .attr('class', 'cell-detail community-detail');
113011
113012             communityDetail
113013               .each(showCommunityDetails);
113014
113015             communityLinks
113016               .append('div')
113017               .attr('class', 'community-missing')
113018               .text(_t('success.missing'))
113019               .append('a')
113020               .attr('class', 'link-out')
113021               .attr('target', '_blank')
113022               .attr('tabindex', -1)
113023               .call(svgIcon('#iD-icon-out-link', 'inline'))
113024               .attr('href', 'https://github.com/osmlab/osm-community-index/issues')
113025               .append('span')
113026               .text(_t('success.tell_us'));
113027           }
113028
113029
113030           function showCommunityDetails(d) {
113031             var selection = select(this);
113032             var communityID = d.id;
113033             var replacements = {
113034               url: linkify(d.url),
113035               signupUrl: linkify(d.signupUrl || d.url)
113036             };
113037
113038             selection
113039               .append('div')
113040               .attr('class', 'community-name')
113041               .append('a')
113042               .attr('target', '_blank')
113043               .attr('href', d.url)
113044               .text(_t(("community." + (d.id) + ".name")));
113045
113046             var descriptionHTML = _t(("community." + (d.id) + ".description"), replacements);
113047
113048             if (d.type === 'reddit') {   // linkify subreddits  #4997
113049               descriptionHTML = descriptionHTML
113050                 .replace(/(\/r\/\w*\/*)/i, function (match) { return linkify(d.url, match); });
113051             }
113052
113053             selection
113054               .append('div')
113055               .attr('class', 'community-description')
113056               .html(descriptionHTML);
113057
113058             if (d.extendedDescription || (d.languageCodes && d.languageCodes.length)) {
113059               selection
113060                 .append('div')
113061                 .call(uiDisclosure(context, ("community-more-" + (d.id)), false)
113062                   .expanded(false)
113063                   .updatePreference(false)
113064                   .title(_t('success.more'))
113065                   .content(showMore)
113066                 );
113067             }
113068
113069             var nextEvents = (d.events || [])
113070               .map(function (event) {
113071                 event.date = parseEventDate(event.when);
113072                 return event;
113073               })
113074               .filter(function (event) {      // date is valid and future (or today)
113075                 var t = event.date.getTime();
113076                 var now = (new Date()).setHours(0,0,0,0);
113077                 return !isNaN(t) && t >= now;
113078               })
113079               .sort(function (a, b) {       // sort by date ascending
113080                 return a.date < b.date ? -1 : a.date > b.date ? 1 : 0;
113081               })
113082               .slice(0, MAXEVENTS);   // limit number of events shown
113083
113084             if (nextEvents.length) {
113085               selection
113086                 .append('div')
113087                 .call(uiDisclosure(context, ("community-events-" + (d.id)), false)
113088                   .expanded(false)
113089                   .updatePreference(false)
113090                   .title(_t('success.events'))
113091                   .content(showNextEvents)
113092                 )
113093                 .select('.hide-toggle')
113094                 .append('span')
113095                 .attr('class', 'badge-text')
113096                 .text(nextEvents.length);
113097             }
113098
113099
113100             function showMore(selection) {
113101               var more = selection.selectAll('.community-more')
113102                 .data([0]);
113103
113104               var moreEnter = more.enter()
113105                 .append('div')
113106                 .attr('class', 'community-more');
113107
113108               if (d.extendedDescription) {
113109                 moreEnter
113110                   .append('div')
113111                   .attr('class', 'community-extended-description')
113112                   .html(_t(("community." + (d.id) + ".extendedDescription"), replacements));
113113               }
113114
113115               if (d.languageCodes && d.languageCodes.length) {
113116                 var languageList = d.languageCodes
113117                   .map(function (code) { return _mainLocalizer.languageName(code); })
113118                   .join(', ');
113119
113120                 moreEnter
113121                   .append('div')
113122                   .attr('class', 'community-languages')
113123                   .text(_t('success.languages', { languages: languageList }));
113124               }
113125             }
113126
113127
113128             function showNextEvents(selection) {
113129               var events = selection
113130                 .append('div')
113131                 .attr('class', 'community-events');
113132
113133               var item = events.selectAll('.community-event')
113134                 .data(nextEvents);
113135
113136               var itemEnter = item.enter()
113137                 .append('div')
113138                 .attr('class', 'community-event');
113139
113140               itemEnter
113141                 .append('div')
113142                 .attr('class', 'community-event-name')
113143                 .append('a')
113144                 .attr('target', '_blank')
113145                 .attr('href', function (d) { return d.url; })
113146                 .text(function (d) {
113147                   var name = d.name;
113148                   if (d.i18n && d.id) {
113149                     name = _t(("community." + communityID + ".events." + (d.id) + ".name"), { default: name });
113150                   }
113151                   return name;
113152                 });
113153
113154               itemEnter
113155                 .append('div')
113156                 .attr('class', 'community-event-when')
113157                 .text(function (d) {
113158                   var options = { weekday: 'short', day: 'numeric', month: 'short', year: 'numeric' };
113159                   if (d.date.getHours() || d.date.getMinutes()) {   // include time if it has one
113160                     options.hour = 'numeric';
113161                     options.minute = 'numeric';
113162                   }
113163                   return d.date.toLocaleString(_mainLocalizer.localeCode(), options);
113164                 });
113165
113166               itemEnter
113167                 .append('div')
113168                 .attr('class', 'community-event-where')
113169                 .text(function (d) {
113170                   var where = d.where;
113171                   if (d.i18n && d.id) {
113172                     where = _t(("community." + communityID + ".events." + (d.id) + ".where"), { default: where });
113173                   }
113174                   return where;
113175                 });
113176
113177               itemEnter
113178                 .append('div')
113179                 .attr('class', 'community-event-description')
113180                 .text(function (d) {
113181                   var description = d.description;
113182                   if (d.i18n && d.id) {
113183                     description = _t(("community." + communityID + ".events." + (d.id) + ".description"), { default: description });
113184                   }
113185                   return description;
113186                 });
113187             }
113188
113189
113190             function linkify(url, text) {
113191               text = text || url;
113192               return ("<a target=\"_blank\" href=\"" + url + "\">" + text + "</a>");
113193             }
113194           }
113195
113196
113197           success.changeset = function(val) {
113198             if (!arguments.length) { return _changeset; }
113199             _changeset = val;
113200             return success;
113201           };
113202
113203
113204           success.location = function(val) {
113205             if (!arguments.length) { return _location; }
113206             _location = val;
113207             return success;
113208           };
113209
113210
113211           return utilRebind(success, dispatch$1, 'on');
113212         }
113213
113214         function modeSave(context) {
113215             var mode = { id: 'save' };
113216             var keybinding = utilKeybinding('modeSave');
113217
113218             var commit = uiCommit(context)
113219                 .on('cancel', cancel);
113220             var _conflictsUi; // uiConflicts
113221
113222             var _location;
113223             var _success;
113224
113225             var uploader = context.uploader()
113226                 .on('saveStarted.modeSave', function() {
113227                     keybindingOff();
113228                 })
113229                 // fire off some async work that we want to be ready later
113230                 .on('willAttemptUpload.modeSave', prepareForSuccess)
113231                 .on('progressChanged.modeSave', showProgress)
113232                 .on('resultNoChanges.modeSave', function() {
113233                     cancel();
113234                 })
113235                 .on('resultErrors.modeSave', showErrors)
113236                 .on('resultConflicts.modeSave', showConflicts)
113237                 .on('resultSuccess.modeSave', showSuccess);
113238
113239
113240             function cancel() {
113241                 context.enter(modeBrowse(context));
113242             }
113243
113244
113245             function showProgress(num, total) {
113246                 var modal = context.container().select('.loading-modal .modal-section');
113247                 var progress = modal.selectAll('.progress')
113248                     .data([0]);
113249
113250                 // enter/update
113251                 progress.enter()
113252                     .append('div')
113253                     .attr('class', 'progress')
113254                     .merge(progress)
113255                     .text(_t('save.conflict_progress', { num: num, total: total }));
113256             }
113257
113258
113259             function showConflicts(changeset, conflicts, origChanges) {
113260
113261                 var selection = context.container()
113262                     .select('.sidebar')
113263                     .append('div')
113264                     .attr('class','sidebar-component');
113265
113266                 context.container().selectAll('.main-content')
113267                     .classed('active', true)
113268                     .classed('inactive', false);
113269
113270                 _conflictsUi = uiConflicts(context)
113271                     .conflictList(conflicts)
113272                     .origChanges(origChanges)
113273                     .on('cancel', function() {
113274                         context.container().selectAll('.main-content')
113275                             .classed('active', false)
113276                             .classed('inactive', true);
113277                         selection.remove();
113278                         keybindingOn();
113279
113280                         uploader.cancelConflictResolution();
113281                     })
113282                     .on('save', function() {
113283                         context.container().selectAll('.main-content')
113284                             .classed('active', false)
113285                             .classed('inactive', true);
113286                         selection.remove();
113287
113288                         uploader.processResolvedConflicts(changeset);
113289                     });
113290
113291                 selection.call(_conflictsUi);
113292             }
113293
113294
113295             function showErrors(errors) {
113296                 keybindingOn();
113297
113298                 var selection = uiConfirm(context.container());
113299                 selection
113300                     .select('.modal-section.header')
113301                     .append('h3')
113302                     .text(_t('save.error'));
113303
113304                 addErrors(selection, errors);
113305                 selection.okButton();
113306             }
113307
113308
113309             function addErrors(selection, data) {
113310                 var message = selection
113311                     .select('.modal-section.message-text');
113312
113313                 var items = message
113314                     .selectAll('.error-container')
113315                     .data(data);
113316
113317                 var enter = items.enter()
113318                     .append('div')
113319                     .attr('class', 'error-container');
113320
113321                 enter
113322                     .append('a')
113323                     .attr('class', 'error-description')
113324                     .attr('href', '#')
113325                     .classed('hide-toggle', true)
113326                     .text(function(d) { return d.msg || _t('save.unknown_error_details'); })
113327                     .on('click', function() {
113328                         event.preventDefault();
113329
113330                         var error = select(this);
113331                         var detail = select(this.nextElementSibling);
113332                         var exp = error.classed('expanded');
113333
113334                         detail.style('display', exp ? 'none' : 'block');
113335                         error.classed('expanded', !exp);
113336                     });
113337
113338                 var details = enter
113339                     .append('div')
113340                     .attr('class', 'error-detail-container')
113341                     .style('display', 'none');
113342
113343                 details
113344                     .append('ul')
113345                     .attr('class', 'error-detail-list')
113346                     .selectAll('li')
113347                     .data(function(d) { return d.details || []; })
113348                     .enter()
113349                     .append('li')
113350                     .attr('class', 'error-detail-item')
113351                     .text(function(d) { return d; });
113352
113353                 items.exit()
113354                     .remove();
113355             }
113356
113357
113358             function showSuccess(changeset) {
113359                 commit.reset();
113360
113361                 var ui = _success
113362                     .changeset(changeset)
113363                     .location(_location)
113364                     .on('cancel', function() { context.ui().sidebar.hide(); });
113365
113366                 context.enter(modeBrowse(context).sidebar(ui));
113367             }
113368
113369
113370             function keybindingOn() {
113371                 select(document)
113372                     .call(keybinding.on('⎋', cancel, true));
113373             }
113374
113375
113376             function keybindingOff() {
113377                 select(document)
113378                     .call(keybinding.unbind);
113379             }
113380
113381
113382             // Reverse geocode current map location so we can display a message on
113383             // the success screen like "Thank you for editing around place, region."
113384             function prepareForSuccess() {
113385                 _success = uiSuccess(context);
113386                 _location = null;
113387                 if (!services.geocoder) { return; }
113388
113389                 services.geocoder.reverse(context.map().center(), function(err, result) {
113390                     if (err || !result || !result.address) { return; }
113391
113392                     var addr = result.address;
113393                     var place = (addr && (addr.town || addr.city || addr.county)) || '';
113394                     var region = (addr && (addr.state || addr.country)) || '';
113395                     var separator = (place && region) ? _t('success.thank_you_where.separator') : '';
113396
113397                     _location = _t('success.thank_you_where.format',
113398                         { place: place, separator: separator, region: region }
113399                     );
113400                 });
113401             }
113402
113403
113404             mode.selectedIDs = function() {
113405                 return _conflictsUi ? _conflictsUi.shownEntityIds() : [];
113406             };
113407
113408
113409             mode.enter = function() {
113410                 // Show sidebar
113411                 context.ui().sidebar.expand();
113412
113413                 function done() {
113414                     context.ui().sidebar.show(commit);
113415                 }
113416
113417                 keybindingOn();
113418
113419                 context.container().selectAll('.main-content')
113420                     .classed('active', false)
113421                     .classed('inactive', true);
113422
113423                 var osm = context.connection();
113424                 if (!osm) {
113425                     cancel();
113426                     return;
113427                 }
113428
113429                 if (osm.authenticated()) {
113430                     done();
113431                 } else {
113432                     osm.authenticate(function(err) {
113433                         if (err) {
113434                             cancel();
113435                         } else {
113436                             done();
113437                         }
113438                     });
113439                 }
113440             };
113441
113442
113443             mode.exit = function() {
113444
113445                 keybindingOff();
113446
113447                 context.container().selectAll('.main-content')
113448                     .classed('active', true)
113449                     .classed('inactive', false);
113450
113451                 context.ui().sidebar.hide();
113452             };
113453
113454             return mode;
113455         }
113456
113457         function uiToolOldDrawModes(context) {
113458
113459             var tool = {
113460                 id: 'old_modes',
113461                 label: _t('toolbar.add_feature')
113462             };
113463
113464             var modes = [
113465                 modeAddPoint(context, {
113466                     title: _t('modes.add_point.title'),
113467                     button: 'point',
113468                     description: _t('modes.add_point.description'),
113469                     preset: _mainPresetIndex.item('point'),
113470                     key: '1'
113471                 }),
113472                 modeAddLine(context, {
113473                     title: _t('modes.add_line.title'),
113474                     button: 'line',
113475                     description: _t('modes.add_line.description'),
113476                     preset: _mainPresetIndex.item('line'),
113477                     key: '2'
113478                 }),
113479                 modeAddArea(context, {
113480                     title: _t('modes.add_area.title'),
113481                     button: 'area',
113482                     description: _t('modes.add_area.description'),
113483                     preset: _mainPresetIndex.item('area'),
113484                     key: '3'
113485                 })
113486             ];
113487
113488
113489             function enabled() {
113490                 return osmEditable();
113491             }
113492
113493             function osmEditable() {
113494                 return context.editable();
113495             }
113496
113497             modes.forEach(function(mode) {
113498                 context.keybinding().on(mode.key, function() {
113499                     if (!enabled()) { return; }
113500
113501                     if (mode.id === context.mode().id) {
113502                         context.enter(modeBrowse(context));
113503                     } else {
113504                         context.enter(mode);
113505                     }
113506                 });
113507             });
113508
113509             tool.render = function(selection) {
113510
113511                 var wrap = selection
113512                     .append('div')
113513                     .attr('class', 'joined')
113514                     .style('display', 'flex');
113515
113516                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
113517
113518                 context.map()
113519                     .on('move.modes', debouncedUpdate)
113520                     .on('drawn.modes', debouncedUpdate);
113521
113522                 context
113523                     .on('enter.modes', update);
113524
113525                 update();
113526
113527
113528                 function update() {
113529
113530                     var buttons = wrap.selectAll('button.add-button')
113531                         .data(modes, function(d) { return d.id; });
113532
113533                     // exit
113534                     buttons.exit()
113535                         .remove();
113536
113537                     // enter
113538                     var buttonsEnter = buttons.enter()
113539                         .append('button')
113540                         .attr('class', function(d) { return d.id + ' add-button bar-button'; })
113541                         .on('click.mode-buttons', function(d) {
113542                             if (!enabled()) { return; }
113543
113544                             // When drawing, ignore accidental clicks on mode buttons - #4042
113545                             var currMode = context.mode().id;
113546                             if (/^draw/.test(currMode)) { return; }
113547
113548                             if (d.id === currMode) {
113549                                 context.enter(modeBrowse(context));
113550                             } else {
113551                                 context.enter(d);
113552                             }
113553                         })
113554                         .call(uiTooltip()
113555                             .placement('bottom')
113556                             .title(function(d) { return d.description; })
113557                             .keys(function(d) { return [d.key]; })
113558                             .scrollContainer(context.container().select('.top-toolbar'))
113559                         );
113560
113561                     buttonsEnter
113562                         .each(function(d) {
113563                             select(this)
113564                                 .call(svgIcon('#iD-icon-' + d.button));
113565                         });
113566
113567                     buttonsEnter
113568                         .append('span')
113569                         .attr('class', 'label')
113570                         .text(function(mode) { return mode.title; });
113571
113572                     // if we are adding/removing the buttons, check if toolbar has overflowed
113573                     if (buttons.enter().size() || buttons.exit().size()) {
113574                         context.ui().checkOverflow('.top-toolbar', true);
113575                     }
113576
113577                     // update
113578                     buttons = buttons
113579                         .merge(buttonsEnter)
113580                         .classed('disabled', function(d) { return !enabled(); })
113581                         .classed('active', function(d) { return context.mode() && context.mode().button === d.button; });
113582                 }
113583             };
113584
113585             return tool;
113586         }
113587
113588         function uiToolNotes(context) {
113589
113590             var tool = {
113591                 id: 'notes',
113592                 label: _t('modes.add_note.label')
113593             };
113594
113595             var mode = modeAddNote(context);
113596
113597             function enabled() {
113598                 return notesEnabled() && notesEditable();
113599             }
113600
113601             function notesEnabled() {
113602                 var noteLayer = context.layers().layer('notes');
113603                 return noteLayer && noteLayer.enabled();
113604             }
113605
113606             function notesEditable() {
113607                 var mode = context.mode();
113608                 return context.map().notesEditable() && mode && mode.id !== 'save';
113609             }
113610
113611             context.keybinding().on(mode.key, function() {
113612                 if (!enabled()) { return; }
113613
113614                 if (mode.id === context.mode().id) {
113615                     context.enter(modeBrowse(context));
113616                 } else {
113617                     context.enter(mode);
113618                 }
113619             });
113620
113621             tool.render = function(selection) {
113622
113623
113624                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
113625
113626                 context.map()
113627                     .on('move.notes', debouncedUpdate)
113628                     .on('drawn.notes', debouncedUpdate);
113629
113630                 context
113631                     .on('enter.notes', update);
113632
113633                 update();
113634
113635
113636                 function update() {
113637                     var showNotes = notesEnabled();
113638                     var data = showNotes ? [mode] : [];
113639
113640                     var buttons = selection.selectAll('button.add-button')
113641                         .data(data, function(d) { return d.id; });
113642
113643                     // exit
113644                     buttons.exit()
113645                         .remove();
113646
113647                     // enter
113648                     var buttonsEnter = buttons.enter()
113649                         .append('button')
113650                         .attr('tabindex', -1)
113651                         .attr('class', function(d) { return d.id + ' add-button bar-button'; })
113652                         .on('click.notes', function(d) {
113653                             if (!enabled()) { return; }
113654
113655                             // When drawing, ignore accidental clicks on mode buttons - #4042
113656                             var currMode = context.mode().id;
113657                             if (/^draw/.test(currMode)) { return; }
113658
113659                             if (d.id === currMode) {
113660                                 context.enter(modeBrowse(context));
113661                             } else {
113662                                 context.enter(d);
113663                             }
113664                         })
113665                         .call(uiTooltip()
113666                             .placement('bottom')
113667                             .title(function(d) { return d.description; })
113668                             .keys(function(d) { return [d.key]; })
113669                             .scrollContainer(context.container().select('.top-toolbar'))
113670                         );
113671
113672                     buttonsEnter
113673                         .each(function(d) {
113674                             select(this)
113675                                 .call(svgIcon(d.icon || '#iD-icon-' + d.button));
113676                         });
113677
113678                     // if we are adding/removing the buttons, check if toolbar has overflowed
113679                     if (buttons.enter().size() || buttons.exit().size()) {
113680                         context.ui().checkOverflow('.top-toolbar', true);
113681                     }
113682
113683                     // update
113684                     buttons = buttons
113685                         .merge(buttonsEnter)
113686                         .classed('disabled', function(d) { return !enabled(); })
113687                         .classed('active', function(d) { return context.mode() && context.mode().button === d.button; });
113688                 }
113689             };
113690
113691             tool.uninstall = function() {
113692                 context
113693                     .on('enter.editor.notes', null)
113694                     .on('exit.editor.notes', null)
113695                     .on('enter.notes', null);
113696
113697                 context.map()
113698                     .on('move.notes', null)
113699                     .on('drawn.notes', null);
113700             };
113701
113702             return tool;
113703         }
113704
113705         function uiToolSave(context) {
113706
113707             var tool = {
113708                 id: 'save',
113709                 label: _t('save.title')
113710             };
113711
113712             var button = null;
113713             var tooltipBehavior = null;
113714             var history = context.history();
113715             var key = uiCmd('⌘S');
113716             var _numChanges = 0;
113717
113718             function isSaving() {
113719                 var mode = context.mode();
113720                 return mode && mode.id === 'save';
113721             }
113722
113723             function isDisabled() {
113724                 return _numChanges === 0 || isSaving();
113725             }
113726
113727             function save() {
113728                 event.preventDefault();
113729                 if (!context.inIntro() && !isSaving() && history.hasChanges()) {
113730                     context.enter(modeSave(context));
113731                 }
113732             }
113733
113734             function bgColor() {
113735                 var step;
113736                 if (_numChanges === 0) {
113737                     return null;
113738                 } else if (_numChanges <= 50) {
113739                     step = _numChanges / 50;
113740                     return d3_interpolateRgb('#fff', '#ff8')(step);  // white -> yellow
113741                 } else {
113742                     step = Math.min((_numChanges - 50) / 50, 1.0);
113743                     return d3_interpolateRgb('#ff8', '#f88')(step);  // yellow -> red
113744                 }
113745             }
113746
113747             function updateCount() {
113748                 var val = history.difference().summary().length;
113749                 if (val === _numChanges) { return; }
113750
113751                 _numChanges = val;
113752
113753                 if (tooltipBehavior) {
113754                     tooltipBehavior
113755                         .title(_t(_numChanges > 0 ? 'save.help' : 'save.no_changes'))
113756                         .keys([key]);
113757                 }
113758
113759                 if (button) {
113760                     button
113761                         .classed('disabled', isDisabled())
113762                         .style('background', bgColor());
113763
113764                     button.select('span.count')
113765                         .text(_numChanges);
113766                 }
113767             }
113768
113769
113770             tool.render = function(selection) {
113771                 tooltipBehavior = uiTooltip()
113772                     .placement('bottom')
113773                     .title(_t('save.no_changes'))
113774                     .keys([key])
113775                     .scrollContainer(context.container().select('.top-toolbar'));
113776
113777                 var lastPointerUpType;
113778
113779                 button = selection
113780                     .append('button')
113781                     .attr('class', 'save disabled bar-button')
113782                     .on('pointerup', function() {
113783                         lastPointerUpType = event.pointerType;
113784                     })
113785                     .on('click', function() {
113786                         event.preventDefault();
113787
113788                         save();
113789
113790                         if (_numChanges === 0 && (
113791                             lastPointerUpType === 'touch' ||
113792                             lastPointerUpType === 'pen')
113793                         ) {
113794                             // there are no tooltips for touch interactions so flash feedback instead
113795                             context.ui().flash
113796                                 .duration(2000)
113797                                 .iconName('#iD-icon-save')
113798                                 .iconClass('disabled')
113799                                 .text(_t('save.no_changes'))();
113800                         }
113801                         lastPointerUpType = null;
113802                     })
113803                     .call(tooltipBehavior);
113804
113805                 button
113806                     .call(svgIcon('#iD-icon-save'));
113807
113808                 button
113809                     .append('span')
113810                     .attr('class', 'count')
113811                     .attr('aria-hidden', 'true')
113812                     .text('0');
113813
113814                 updateCount();
113815
113816
113817                 context.keybinding()
113818                     .on(key, save, true);
113819
113820
113821                 context.history()
113822                     .on('change.save', updateCount);
113823
113824                 context
113825                     .on('enter.save', function() {
113826                         if (button) {
113827                             button
113828                                 .classed('disabled', isDisabled());
113829
113830                             if (isSaving()) {
113831                                 button.call(tooltipBehavior.hide);
113832                             }
113833                         }
113834                     });
113835             };
113836
113837
113838             tool.uninstall = function() {
113839                 context.keybinding()
113840                     .off(key, true);
113841
113842                 context.history()
113843                     .on('change.save', null);
113844
113845                 context
113846                     .on('enter.save', null);
113847
113848                 button = null;
113849                 tooltipBehavior = null;
113850             };
113851
113852             return tool;
113853         }
113854
113855         function uiToolSidebarToggle(context) {
113856
113857             var tool = {
113858                 id: 'sidebar_toggle',
113859                 label: _t('toolbar.inspect')
113860             };
113861
113862             tool.render = function(selection) {
113863                 selection
113864                     .append('button')
113865                     .attr('class', 'bar-button')
113866                     .on('click', function() {
113867                         context.ui().sidebar.toggle();
113868                     })
113869                     .call(uiTooltip()
113870                         .placement('bottom')
113871                         .title(_t('sidebar.tooltip'))
113872                         .keys([_t('sidebar.key')])
113873                         .scrollContainer(context.container().select('.top-toolbar'))
113874                     )
113875                     .call(svgIcon('#iD-icon-sidebar-' + (_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')));
113876             };
113877
113878             return tool;
113879         }
113880
113881         function uiToolUndoRedo(context) {
113882
113883             var tool = {
113884                 id: 'undo_redo',
113885                 label: _t('toolbar.undo_redo')
113886             };
113887
113888             var commands = [{
113889                 id: 'undo',
113890                 cmd: uiCmd('⌘Z'),
113891                 action: function() {
113892                     context.undo();
113893                 },
113894                 annotation: function() {
113895                     return context.history().undoAnnotation();
113896                 },
113897                 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')
113898             }, {
113899                 id: 'redo',
113900                 cmd: uiCmd('⌘⇧Z'),
113901                 action: function() {
113902                     context.redo();
113903                 },
113904                 annotation: function() {
113905                     return context.history().redoAnnotation();
113906                 },
113907                 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'undo' : 'redo')
113908             }];
113909
113910
113911             function editable() {
113912                 return context.mode() && context.mode().id !== 'save' && context.map().editableDataEnabled(true /* ignore min zoom */);
113913             }
113914
113915
113916             tool.render = function(selection) {
113917                 var tooltipBehavior = uiTooltip()
113918                     .placement('bottom')
113919                     .title(function (d) {
113920                         return d.annotation() ?
113921                             _t(d.id + '.tooltip', { action: d.annotation() }) :
113922                             _t(d.id + '.nothing');
113923                     })
113924                     .keys(function(d) {
113925                         return [d.cmd];
113926                     })
113927                     .scrollContainer(context.container().select('.top-toolbar'));
113928
113929                 var lastPointerUpType;
113930
113931                 var buttons = selection.selectAll('button')
113932                     .data(commands)
113933                     .enter()
113934                     .append('button')
113935                     .attr('class', function(d) { return 'disabled ' + d.id + '-button bar-button'; })
113936                     .on('pointerup', function() {
113937                         // `pointerup` is always called before `click`
113938                         lastPointerUpType = event.pointerType;
113939                     })
113940                     .on('click', function(d) {
113941                         event.preventDefault();
113942
113943                         var annotation = d.annotation();
113944
113945                         if (editable() && annotation) {
113946                             d.action();
113947                         }
113948
113949                         if (editable() && (
113950                             lastPointerUpType === 'touch' ||
113951                             lastPointerUpType === 'pen')
113952                         ) {
113953                             // there are no tooltips for touch interactions so flash feedback instead
113954
113955                             var text = annotation ?
113956                                 _t(d.id + '.tooltip', { action: annotation }) :
113957                                 _t(d.id + '.nothing');
113958                             context.ui().flash
113959                                 .duration(2000)
113960                                 .iconName('#' + d.icon)
113961                                 .iconClass(annotation ? '' : 'disabled')
113962                                 .text(text)();
113963                         }
113964                         lastPointerUpType = null;
113965                     })
113966                     .call(tooltipBehavior);
113967
113968                 buttons.each(function(d) {
113969                     select(this)
113970                         .call(svgIcon('#' + d.icon));
113971                 });
113972
113973                 context.keybinding()
113974                     .on(commands[0].cmd, function() {
113975                         event.preventDefault();
113976                         if (editable()) { commands[0].action(); }
113977                     })
113978                     .on(commands[1].cmd, function() {
113979                         event.preventDefault();
113980                         if (editable()) { commands[1].action(); }
113981                     });
113982
113983
113984                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
113985
113986                 context.map()
113987                     .on('move.undo_redo', debouncedUpdate)
113988                     .on('drawn.undo_redo', debouncedUpdate);
113989
113990                 context.history()
113991                     .on('change.undo_redo', function(difference) {
113992                         if (difference) { update(); }
113993                     });
113994
113995                 context
113996                     .on('enter.undo_redo', update);
113997
113998
113999                 function update() {
114000                     buttons
114001                         .classed('disabled', function(d) {
114002                             return !editable() || !d.annotation();
114003                         })
114004                         .each(function() {
114005                             var selection = select(this);
114006                             if (!selection.select('.tooltip.in').empty()) {
114007                                 selection.call(tooltipBehavior.updateContent);
114008                             }
114009                         });
114010                 }
114011             };
114012
114013             tool.uninstall = function() {
114014                 context.keybinding()
114015                     .off(commands[0].cmd)
114016                     .off(commands[1].cmd);
114017
114018                 context.map()
114019                     .on('move.undo_redo', null)
114020                     .on('drawn.undo_redo', null);
114021
114022                 context.history()
114023                     .on('change.undo_redo', null);
114024
114025                 context
114026                     .on('enter.undo_redo', null);
114027             };
114028
114029             return tool;
114030         }
114031
114032         function uiTopToolbar(context) {
114033
114034             var sidebarToggle = uiToolSidebarToggle(context),
114035                 modes = uiToolOldDrawModes(context),
114036                 notes = uiToolNotes(context),
114037                 undoRedo = uiToolUndoRedo(context),
114038                 save = uiToolSave(context);
114039
114040             function notesEnabled() {
114041                 var noteLayer = context.layers().layer('notes');
114042                 return noteLayer && noteLayer.enabled();
114043             }
114044
114045             function topToolbar(bar) {
114046
114047                 bar.on('wheel.topToolbar', function() {
114048                     if (!event.deltaX) {
114049                         // translate vertical scrolling into horizontal scrolling in case
114050                         // the user doesn't have an input device that can scroll horizontally
114051                         bar.node().scrollLeft += event.deltaY;
114052                     }
114053                 });
114054
114055                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
114056                 context.layers()
114057                     .on('change.topToolbar', debouncedUpdate);
114058
114059                 update();
114060
114061                 function update() {
114062
114063                     var tools = [
114064                         sidebarToggle,
114065                         'spacer',
114066                         modes
114067                     ];
114068
114069                     tools.push('spacer');
114070
114071                     if (notesEnabled()) {
114072                         tools = tools.concat([notes, 'spacer']);
114073                     }
114074
114075                     tools = tools.concat([undoRedo, save]);
114076
114077                     var toolbarItems = bar.selectAll('.toolbar-item')
114078                         .data(tools, function(d) {
114079                             return d.id || d;
114080                         });
114081
114082                     toolbarItems.exit()
114083                         .each(function(d) {
114084                             if (d.uninstall) {
114085                                 d.uninstall();
114086                             }
114087                         })
114088                         .remove();
114089
114090                     var itemsEnter = toolbarItems
114091                         .enter()
114092                         .append('div')
114093                         .attr('class', function(d) {
114094                             var classes = 'toolbar-item ' + (d.id || d).replace('_', '-');
114095                             if (d.klass) { classes += ' ' + d.klass; }
114096                             return classes;
114097                         });
114098
114099                     var actionableItems = itemsEnter.filter(function(d) { return d !== 'spacer'; });
114100
114101                     actionableItems
114102                         .append('div')
114103                         .attr('class', 'item-content')
114104                         .each(function(d) {
114105                             select(this).call(d.render, bar);
114106                         });
114107
114108                     actionableItems
114109                         .append('div')
114110                         .attr('class', 'item-label')
114111                         .text(function(d) {
114112                             return d.label;
114113                         });
114114                 }
114115
114116             }
114117
114118             return topToolbar;
114119         }
114120
114121         // these are module variables so they are preserved through a ui.restart()
114122         var sawVersion = null;
114123         var isNewVersion = false;
114124         var isNewUser = false;
114125
114126
114127         function uiVersion(context) {
114128
114129             var currVersion = context.version;
114130             var matchedVersion = currVersion.match(/\d+\.\d+\.\d+.*/);
114131
114132             if (sawVersion === null && matchedVersion !== null) {
114133                 if (corePreferences('sawVersion')) {
114134                     isNewUser = false;
114135                     isNewVersion = corePreferences('sawVersion') !== currVersion && currVersion.indexOf('-') === -1;
114136                 } else {
114137                     isNewUser = true;
114138                     isNewVersion = true;
114139                 }
114140                 corePreferences('sawVersion', currVersion);
114141                 sawVersion = currVersion;
114142             }
114143
114144             return function(selection) {
114145                 selection
114146                     .append('a')
114147                     .attr('target', '_blank')
114148                     .attr('href', 'https://github.com/openstreetmap/iD')
114149                     .text(currVersion);
114150
114151                 // only show new version indicator to users that have used iD before
114152                 if (isNewVersion && !isNewUser) {
114153                     selection
114154                         .append('div')
114155                         .attr('class', 'badge')
114156                         .append('a')
114157                         .attr('target', '_blank')
114158                         .attr('href', 'https://github.com/openstreetmap/iD/blob/release/CHANGELOG.md#whats-new')
114159                         .call(svgIcon('#maki-gift-11'))
114160                         .call(uiTooltip()
114161                             .title(_t('version.whats_new', { version: currVersion }))
114162                             .placement('top')
114163                         );
114164                 }
114165             };
114166         }
114167
114168         function uiZoom(context) {
114169
114170             var zooms = [{
114171                 id: 'zoom-in',
114172                 icon: 'iD-icon-plus',
114173                 title: _t('zoom.in'),
114174                 action: zoomIn,
114175                 disabled: function() {
114176                     return !context.map().canZoomIn();
114177                 },
114178                 disabledTitle: _t('zoom.disabled.in'),
114179                 key: '+'
114180             }, {
114181                 id: 'zoom-out',
114182                 icon: 'iD-icon-minus',
114183                 title: _t('zoom.out'),
114184                 action: zoomOut,
114185                 disabled: function() {
114186                     return !context.map().canZoomOut();
114187                 },
114188                 disabledTitle: _t('zoom.disabled.out'),
114189                 key: '-'
114190             }];
114191
114192             function zoomIn() {
114193                 event.preventDefault();
114194                 context.map().zoomIn();
114195             }
114196
114197             function zoomOut() {
114198                 event.preventDefault();
114199                 context.map().zoomOut();
114200             }
114201
114202             function zoomInFurther() {
114203                 event.preventDefault();
114204                 context.map().zoomInFurther();
114205             }
114206
114207             function zoomOutFurther() {
114208                 event.preventDefault();
114209                 context.map().zoomOutFurther();
114210             }
114211
114212             return function(selection) {
114213                 var tooltipBehavior = uiTooltip()
114214                     .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114215                     .title(function(d) {
114216                         if (d.disabled()) {
114217                             return d.disabledTitle;
114218                         }
114219                         return d.title;
114220                     })
114221                     .keys(function(d) {
114222                         return [d.key];
114223                     });
114224
114225                 var lastPointerUpType;
114226
114227                 var buttons = selection.selectAll('button')
114228                     .data(zooms)
114229                     .enter()
114230                     .append('button')
114231                     .attr('class', function(d) { return d.id; })
114232                     .on('pointerup.editor', function() {
114233                         lastPointerUpType = event.pointerType;
114234                     })
114235                     .on('click.editor', function(d) {
114236                         if (!d.disabled()) {
114237                             d.action();
114238                         } else if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
114239                             context.ui().flash
114240                                 .duration(2000)
114241                                 .iconName('#' + d.icon)
114242                                 .iconClass('disabled')
114243                                 .text(d.disabledTitle)();
114244                         }
114245                         lastPointerUpType = null;
114246                     })
114247                     .call(tooltipBehavior);
114248
114249                 buttons.each(function(d) {
114250                     select(this)
114251                         .call(svgIcon('#' + d.icon, 'light'));
114252                 });
114253
114254                 ['plus', 'ffplus', '=', 'ffequals'].forEach(function(key) {
114255                     context.keybinding().on([key], zoomIn);
114256                     context.keybinding().on([uiCmd('⌘' + key)], zoomInFurther);
114257                 });
114258
114259                 ['_', '-', 'ffminus', 'dash'].forEach(function(key) {
114260                     context.keybinding().on([key], zoomOut);
114261                     context.keybinding().on([uiCmd('⌘' + key)], zoomOutFurther);
114262                 });
114263
114264                 function updateButtonStates() {
114265                     buttons
114266                         .classed('disabled', function(d) {
114267                             return d.disabled();
114268                         })
114269                         .each(function() {
114270                             var selection = select(this);
114271                             if (!selection.select('.tooltip.in').empty()) {
114272                                 selection.call(tooltipBehavior.updateContent);
114273                             }
114274                         });
114275                 }
114276
114277                 updateButtonStates();
114278
114279                 context.map().on('move.uiZoom', updateButtonStates);
114280             };
114281         }
114282
114283         function uiZoomToSelection(context) {
114284
114285             function isDisabled() {
114286                 var mode = context.mode();
114287                 return !mode || !mode.zoomToSelected;
114288             }
114289
114290             var _lastPointerUpType;
114291
114292             function pointerup() {
114293                 _lastPointerUpType = event.pointerType;
114294             }
114295
114296             function click() {
114297                 event.preventDefault();
114298
114299                 if (isDisabled()) {
114300                     if (_lastPointerUpType === 'touch' || _lastPointerUpType === 'pen') {
114301                         context.ui().flash
114302                             .duration(2000)
114303                             .iconName('#iD-icon-framed-dot')
114304                             .iconClass('disabled')
114305                             .text(_t('inspector.zoom_to.no_selection'))();
114306                     }
114307                 } else {
114308                     var mode = context.mode();
114309                     if (mode && mode.zoomToSelected) {
114310                         mode.zoomToSelected();
114311                     }
114312                 }
114313
114314                 _lastPointerUpType = null;
114315             }
114316
114317             return function(selection) {
114318
114319                 var tooltipBehavior = uiTooltip()
114320                     .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114321                     .title(function() {
114322                         if (isDisabled()) {
114323                             return _t('inspector.zoom_to.no_selection');
114324                         }
114325                         return _t('inspector.zoom_to.title');
114326                     })
114327                     .keys([_t('inspector.zoom_to.key')]);
114328
114329                 var button = selection
114330                     .append('button')
114331                     .on('pointerup', pointerup)
114332                     .on('click', click)
114333                     .call(svgIcon('#iD-icon-framed-dot', 'light'))
114334                     .call(tooltipBehavior);
114335
114336                 function setEnabledState() {
114337                     button.classed('disabled', isDisabled());
114338                     if (!button.select('.tooltip.in').empty()) {
114339                         button.call(tooltipBehavior.updateContent);
114340                     }
114341                 }
114342
114343                 context.on('enter.uiZoomToSelection', setEnabledState);
114344
114345                 setEnabledState();
114346             };
114347         }
114348
114349         function uiPane(id, context) {
114350
114351             var _key;
114352             var _title = '';
114353             var _description = '';
114354             var _iconName = '';
114355             var _sections; // array of uiSection objects
114356
114357             var _paneSelection = select(null);
114358
114359             var _paneTooltip;
114360
114361             var pane = {
114362                 id: id
114363             };
114364
114365             pane.title = function(val) {
114366                 if (!arguments.length) { return _title; }
114367                 _title = val;
114368                 return pane;
114369             };
114370
114371             pane.key = function(val) {
114372                 if (!arguments.length) { return _key; }
114373                 _key = val;
114374                 return pane;
114375             };
114376
114377             pane.description = function(val) {
114378                 if (!arguments.length) { return _description; }
114379                 _description = val;
114380                 return pane;
114381             };
114382
114383             pane.iconName = function(val) {
114384                 if (!arguments.length) { return _iconName; }
114385                 _iconName = val;
114386                 return pane;
114387             };
114388
114389             pane.sections = function(val) {
114390                 if (!arguments.length) { return _sections; }
114391                 _sections = val;
114392                 return pane;
114393             };
114394
114395             pane.selection = function() {
114396                 return _paneSelection;
114397             };
114398
114399             function hidePane() {
114400                 context.ui().togglePanes();
114401             }
114402
114403             pane.togglePane = function() {
114404                 if (event) { event.preventDefault(); }
114405                 _paneTooltip.hide();
114406                 context.ui().togglePanes(!_paneSelection.classed('shown') ? _paneSelection : undefined);
114407             };
114408
114409             pane.renderToggleButton = function(selection) {
114410
114411                 if (!_paneTooltip) {
114412                     _paneTooltip = uiTooltip()
114413                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114414                         .title(_description)
114415                         .keys([_key]);
114416                 }
114417
114418                 selection
114419                     .append('button')
114420                     .on('click', pane.togglePane)
114421                     .call(svgIcon('#' + _iconName, 'light'))
114422                     .call(_paneTooltip);
114423             };
114424
114425             pane.renderContent = function(selection) {
114426                 // override to fully customize content
114427
114428                 if (_sections) {
114429                     _sections.forEach(function(section) {
114430                         selection.call(section.render);
114431                     });
114432                 }
114433             };
114434
114435             pane.renderPane = function(selection) {
114436
114437                 _paneSelection = selection
114438                     .append('div')
114439                     .attr('class', 'fillL map-pane hide ' + id + '-pane')
114440                     .attr('pane', id);
114441
114442                 var heading = _paneSelection
114443                     .append('div')
114444                     .attr('class', 'pane-heading');
114445
114446                 heading
114447                     .append('h2')
114448                     .text(_title);
114449
114450                 heading
114451                     .append('button')
114452                     .on('click', hidePane)
114453                     .call(svgIcon('#iD-icon-close'));
114454
114455
114456                 _paneSelection
114457                     .append('div')
114458                     .attr('class', 'pane-content')
114459                     .call(pane.renderContent);
114460
114461                 if (_key) {
114462                     context.keybinding()
114463                         .on(_key, pane.togglePane);
114464                 }
114465             };
114466
114467             return pane;
114468         }
114469
114470         function uiSectionBackgroundDisplayOptions(context) {
114471
114472             var section = uiSection('background-display-options', context)
114473                 .title(_t('background.display_options'))
114474                 .disclosureContent(renderDisclosureContent);
114475
114476             var _detected = utilDetect();
114477             var _storedOpacity = corePreferences('background-opacity');
114478             var _minVal = 0.25;
114479             var _maxVal = _detected.cssfilters ? 2 : 1;
114480
114481             var _sliders = _detected.cssfilters
114482                 ? ['brightness', 'contrast', 'saturation', 'sharpness']
114483                 : ['brightness'];
114484
114485             var _options = {
114486                 brightness: (_storedOpacity !== null ? (+_storedOpacity) : 1),
114487                 contrast: 1,
114488                 saturation: 1,
114489                 sharpness: 1
114490             };
114491
114492             function clamp(x, min, max) {
114493                 return Math.max(min, Math.min(x, max));
114494             }
114495
114496             function updateValue(d, val) {
114497                 if (!val && event && event.target) {
114498                     val = event.target.value;
114499                 }
114500
114501                 val = clamp(val, _minVal, _maxVal);
114502
114503                 _options[d] = val;
114504                 context.background()[d](val);
114505
114506                 if (d === 'brightness') {
114507                     corePreferences('background-opacity', val);
114508                 }
114509
114510                 section.reRender();
114511             }
114512
114513             function renderDisclosureContent(selection) {
114514                 var container = selection.selectAll('.display-options-container')
114515                     .data([0]);
114516
114517                 var containerEnter = container.enter()
114518                     .append('div')
114519                     .attr('class', 'display-options-container controls-list');
114520
114521                 // add slider controls
114522                 var slidersEnter = containerEnter.selectAll('.display-control')
114523                     .data(_sliders)
114524                     .enter()
114525                     .append('div')
114526                     .attr('class', function(d) { return 'display-control display-control-' + d; });
114527
114528                 slidersEnter
114529                     .append('h5')
114530                     .text(function(d) { return _t('background.' + d); })
114531                     .append('span')
114532                     .attr('class', function(d) { return 'display-option-value display-option-value-' + d; });
114533
114534                 slidersEnter
114535                     .append('input')
114536                     .attr('class', function(d) { return 'display-option-input display-option-input-' + d; })
114537                     .attr('type', 'range')
114538                     .attr('min', _minVal)
114539                     .attr('max', _maxVal)
114540                     .attr('step', '0.05')
114541                     .on('input', function(d) {
114542                         var val = select(this).property('value');
114543                         updateValue(d, val);
114544                     });
114545
114546                 slidersEnter
114547                     .append('button')
114548                     .attr('title', _t('background.reset'))
114549                     .attr('class', function(d) { return 'display-option-reset display-option-reset-' + d; })
114550                     .on('click', function(d) {
114551                         if (event.button !== 0) { return; }
114552                         updateValue(d, 1);
114553                     })
114554                     .call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')));
114555
114556                 // reset all button
114557                 containerEnter
114558                     .append('a')
114559                     .attr('class', 'display-option-resetlink')
114560                     .attr('href', '#')
114561                     .text(_t('background.reset_all'))
114562                     .on('click', function() {
114563                         for (var i = 0; i < _sliders.length; i++) {
114564                             updateValue(_sliders[i],1);
114565                         }
114566                     });
114567
114568                 // update
114569                 container = containerEnter
114570                     .merge(container);
114571
114572                 container.selectAll('.display-option-input')
114573                     .property('value', function(d) { return _options[d]; });
114574
114575                 container.selectAll('.display-option-value')
114576                     .text(function(d) { return Math.floor(_options[d] * 100) + '%'; });
114577
114578                 container.selectAll('.display-option-reset')
114579                     .classed('disabled', function(d) { return _options[d] === 1; });
114580
114581                 // first time only, set brightness if needed
114582                 if (containerEnter.size() && _options.brightness !== 1) {
114583                     context.background().brightness(_options.brightness);
114584                 }
114585             }
114586
114587             return section;
114588         }
114589
114590         function uiSettingsCustomBackground() {
114591             var dispatch$1 = dispatch('change');
114592
114593             function render(selection) {
114594                 // keep separate copies of original and current settings
114595                 var _origSettings = {
114596                     template: corePreferences('background-custom-template')
114597                 };
114598                 var _currSettings = {
114599                     template: corePreferences('background-custom-template')
114600                 };
114601
114602                 var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
114603                 var modal = uiConfirm(selection).okButton();
114604
114605                 modal
114606                     .classed('settings-modal settings-custom-background', true);
114607
114608                 modal.select('.modal-section.header')
114609                     .append('h3')
114610                     .text(_t('settings.custom_background.header'));
114611
114612
114613                 var textSection = modal.select('.modal-section.message-text');
114614
114615                 var instructions =
114616                     (_t('settings.custom_background.instructions.info')) + "\n" +
114617                     '\n' +
114618                     "#### " + (_t('settings.custom_background.instructions.wms.tokens_label')) + "\n" +
114619                     "* " + (_t('settings.custom_background.instructions.wms.tokens.proj')) + "\n" +
114620                     "* " + (_t('settings.custom_background.instructions.wms.tokens.wkid')) + "\n" +
114621                     "* " + (_t('settings.custom_background.instructions.wms.tokens.dimensions')) + "\n" +
114622                     "* " + (_t('settings.custom_background.instructions.wms.tokens.bbox')) + "\n" +
114623                     '\n' +
114624                     "#### " + (_t('settings.custom_background.instructions.tms.tokens_label')) + "\n" +
114625                     "* " + (_t('settings.custom_background.instructions.tms.tokens.xyz')) + "\n" +
114626                     "* " + (_t('settings.custom_background.instructions.tms.tokens.flipped_y')) + "\n" +
114627                     "* " + (_t('settings.custom_background.instructions.tms.tokens.switch')) + "\n" +
114628                     "* " + (_t('settings.custom_background.instructions.tms.tokens.quadtile')) + "\n" +
114629                     "* " + (_t('settings.custom_background.instructions.tms.tokens.scale_factor')) + "\n" +
114630                     '\n' +
114631                     "#### " + (_t('settings.custom_background.instructions.example')) + "\n" +
114632                     "`" + example + "`";
114633
114634                 textSection
114635                     .append('div')
114636                     .attr('class', 'instructions-template')
114637                     .html(marked_1(instructions));
114638
114639                 textSection
114640                     .append('textarea')
114641                     .attr('class', 'field-template')
114642                     .attr('placeholder', _t('settings.custom_background.template.placeholder'))
114643                     .call(utilNoAuto)
114644                     .property('value', _currSettings.template);
114645
114646
114647                 // insert a cancel button
114648                 var buttonSection = modal.select('.modal-section.buttons');
114649
114650                 buttonSection
114651                     .insert('button', '.ok-button')
114652                     .attr('class', 'button cancel-button secondary-action')
114653                     .text(_t('confirm.cancel'));
114654
114655
114656                 buttonSection.select('.cancel-button')
114657                     .on('click.cancel', clickCancel);
114658
114659                 buttonSection.select('.ok-button')
114660                     .attr('disabled', isSaveDisabled)
114661                     .on('click.save', clickSave);
114662
114663
114664                 function isSaveDisabled() {
114665                     return null;
114666                 }
114667
114668
114669                 // restore the original template
114670                 function clickCancel() {
114671                     textSection.select('.field-template').property('value', _origSettings.template);
114672                     corePreferences('background-custom-template', _origSettings.template);
114673                     this.blur();
114674                     modal.close();
114675                 }
114676
114677                 // accept the current template
114678                 function clickSave() {
114679                     _currSettings.template = textSection.select('.field-template').property('value');
114680                     corePreferences('background-custom-template', _currSettings.template);
114681                     this.blur();
114682                     modal.close();
114683                     dispatch$1.call('change', this, _currSettings);
114684                 }
114685             }
114686
114687             return utilRebind(render, dispatch$1, 'on');
114688         }
114689
114690         function uiSectionBackgroundList(context) {
114691
114692             var _backgroundList = select(null);
114693
114694             var _customSource = context.background().findSource('custom');
114695
114696             var _settingsCustomBackground = uiSettingsCustomBackground()
114697                 .on('change', customChanged);
114698
114699             var section = uiSection('background-list', context)
114700                 .title(_t('background.backgrounds'))
114701                 .disclosureContent(renderDisclosureContent);
114702
114703             function previousBackgroundID() {
114704                 return corePreferences('background-last-used-toggle');
114705             }
114706
114707             function renderDisclosureContent(selection) {
114708
114709                 // the background list
114710                 var container = selection.selectAll('.layer-background-list')
114711                     .data([0]);
114712
114713                 _backgroundList = container.enter()
114714                     .append('ul')
114715                     .attr('class', 'layer-list layer-background-list')
114716                     .attr('dir', 'auto')
114717                     .merge(container);
114718
114719
114720                 // add minimap toggle below list
114721                 var bgExtrasListEnter = selection.selectAll('.bg-extras-list')
114722                     .data([0])
114723                     .enter()
114724                     .append('ul')
114725                     .attr('class', 'layer-list bg-extras-list');
114726
114727                 var minimapLabelEnter = bgExtrasListEnter
114728                     .append('li')
114729                     .attr('class', 'minimap-toggle-item')
114730                     .append('label')
114731                     .call(uiTooltip()
114732                         .title(_t('background.minimap.tooltip'))
114733                         .keys([_t('background.minimap.key')])
114734                         .placement('top')
114735                     );
114736
114737                 minimapLabelEnter
114738                     .append('input')
114739                     .attr('type', 'checkbox')
114740                     .on('change', function() {
114741                         event.preventDefault();
114742                         uiMapInMap.toggle();
114743                     });
114744
114745                 minimapLabelEnter
114746                     .append('span')
114747                     .text(_t('background.minimap.description'));
114748
114749
114750                 var panelLabelEnter = bgExtrasListEnter
114751                     .append('li')
114752                     .attr('class', 'background-panel-toggle-item')
114753                     .append('label')
114754                     .call(uiTooltip()
114755                         .title(_t('background.panel.tooltip'))
114756                         .keys([uiCmd('⌘⇧' + _t('info_panels.background.key'))])
114757                         .placement('top')
114758                     );
114759
114760                 panelLabelEnter
114761                     .append('input')
114762                     .attr('type', 'checkbox')
114763                     .on('change', function() {
114764                         event.preventDefault();
114765                         context.ui().info.toggle('background');
114766                     });
114767
114768                 panelLabelEnter
114769                     .append('span')
114770                     .text(_t('background.panel.description'));
114771
114772                 var locPanelLabelEnter = bgExtrasListEnter
114773                     .append('li')
114774                     .attr('class', 'location-panel-toggle-item')
114775                     .append('label')
114776                     .call(uiTooltip()
114777                         .title(_t('background.location_panel.tooltip'))
114778                         .keys([uiCmd('⌘⇧' + _t('info_panels.location.key'))])
114779                         .placement('top')
114780                     );
114781
114782                 locPanelLabelEnter
114783                     .append('input')
114784                     .attr('type', 'checkbox')
114785                     .on('change', function() {
114786                         event.preventDefault();
114787                         context.ui().info.toggle('location');
114788                     });
114789
114790                 locPanelLabelEnter
114791                     .append('span')
114792                     .text(_t('background.location_panel.description'));
114793
114794
114795                 // "Info / Report a Problem" link
114796                 selection.selectAll('.imagery-faq')
114797                     .data([0])
114798                     .enter()
114799                     .append('div')
114800                     .attr('class', 'imagery-faq')
114801                     .append('a')
114802                     .attr('target', '_blank')
114803                     .call(svgIcon('#iD-icon-out-link', 'inline'))
114804                     .attr('href', 'https://github.com/openstreetmap/iD/blob/develop/FAQ.md#how-can-i-report-an-issue-with-background-imagery')
114805                     .append('span')
114806                     .text(_t('background.imagery_problem_faq'));
114807
114808                 _backgroundList
114809                     .call(drawListItems, 'radio', chooseBackground, function(d) { return !d.isHidden() && !d.overlay; });
114810             }
114811
114812             function setTooltips(selection) {
114813                 selection.each(function(d, i, nodes) {
114814                     var item = select(this).select('label');
114815                     var span = item.select('span');
114816                     var placement = (i < nodes.length / 2) ? 'bottom' : 'top';
114817                     var description = d.description();
114818                     var isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth'));
114819
114820                     item.call(uiTooltip().destroyAny);
114821
114822                     if (d.id === previousBackgroundID()) {
114823                         item.call(uiTooltip()
114824                             .placement(placement)
114825                             .title('<div>' + _t('background.switch') + '</div>')
114826                             .keys([uiCmd('⌘' + _t('background.key'))])
114827                         );
114828                     } else if (description || isOverflowing) {
114829                         item.call(uiTooltip()
114830                             .placement(placement)
114831                             .title(description || d.name())
114832                         );
114833                     }
114834                 });
114835             }
114836
114837             function drawListItems(layerList, type, change, filter) {
114838                 var sources = context.background()
114839                     .sources(context.map().extent(), context.map().zoom(), true)
114840                     .filter(filter);
114841
114842                 var layerLinks = layerList.selectAll('li')
114843                     .data(sources, function(d) { return d.name(); });
114844
114845                 layerLinks.exit()
114846                     .remove();
114847
114848                 var enter = layerLinks.enter()
114849                     .append('li')
114850                     .classed('layer-custom', function(d) { return d.id === 'custom'; })
114851                     .classed('best', function(d) { return d.best(); });
114852
114853                 var label = enter
114854                     .append('label');
114855
114856                 label
114857                     .append('input')
114858                     .attr('type', type)
114859                     .attr('name', 'layers')
114860                     .on('change', change);
114861
114862                 label
114863                     .append('span')
114864                     .text(function(d) { return d.name(); });
114865
114866                 enter.filter(function(d) { return d.id === 'custom'; })
114867                     .append('button')
114868                     .attr('class', 'layer-browse')
114869                     .call(uiTooltip()
114870                         .title(_t('settings.custom_background.tooltip'))
114871                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114872                     )
114873                     .on('click', editCustom)
114874                     .call(svgIcon('#iD-icon-more'));
114875
114876                 enter.filter(function(d) { return d.best(); })
114877                     .append('div')
114878                     .attr('class', 'best')
114879                     .call(uiTooltip()
114880                         .title(_t('background.best_imagery'))
114881                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114882                     )
114883                     .append('span')
114884                     .html('&#9733;');
114885
114886
114887                 layerList.selectAll('li')
114888                     .sort(sortSources);
114889
114890                 layerList
114891                     .call(updateLayerSelections);
114892
114893
114894                 function sortSources(a, b) {
114895                     return a.best() && !b.best() ? -1
114896                         : b.best() && !a.best() ? 1
114897                         : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
114898                 }
114899             }
114900
114901             function updateLayerSelections(selection) {
114902                 function active(d) {
114903                     return context.background().showsLayer(d);
114904                 }
114905
114906                 selection.selectAll('li')
114907                     .classed('active', active)
114908                     .classed('switch', function(d) { return d.id === previousBackgroundID(); })
114909                     .call(setTooltips)
114910                     .selectAll('input')
114911                     .property('checked', active);
114912             }
114913
114914
114915             function chooseBackground(d) {
114916                 if (d.id === 'custom' && !d.template()) {
114917                     return editCustom();
114918                 }
114919
114920                 event.preventDefault();
114921                 var previousBackground = context.background().baseLayerSource();
114922                 corePreferences('background-last-used-toggle', previousBackground.id);
114923                 corePreferences('background-last-used', d.id);
114924                 context.background().baseLayerSource(d);
114925                 document.activeElement.blur();
114926             }
114927
114928
114929             function customChanged(d) {
114930                 if (d && d.template) {
114931                     _customSource.template(d.template);
114932                     chooseBackground(_customSource);
114933                 } else {
114934                     _customSource.template('');
114935                     chooseBackground(context.background().findSource('none'));
114936                 }
114937             }
114938
114939
114940             function editCustom() {
114941                 event.preventDefault();
114942                 context.container()
114943                     .call(_settingsCustomBackground);
114944             }
114945
114946
114947             context.background()
114948                 .on('change.background_list', function() {
114949                     _backgroundList.call(updateLayerSelections);
114950                 });
114951
114952             context.map()
114953                 .on('move.background_list',
114954                     debounce(function() {
114955                         // layers in-view may have changed due to map move
114956                         window.requestIdleCallback(section.reRender);
114957                     }, 1000)
114958                 );
114959
114960             return section;
114961         }
114962
114963         function uiSectionBackgroundOffset(context) {
114964
114965             var section = uiSection('background-offset', context)
114966                 .title(_t('background.fix_misalignment'))
114967                 .disclosureContent(renderDisclosureContent)
114968                 .expandedByDefault(false);
114969
114970             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
114971
114972             var _directions = [
114973                 ['right', [0.5, 0]],
114974                 ['top', [0, -0.5]],
114975                 ['left', [-0.5, 0]],
114976                 ['bottom', [0, 0.5]]
114977             ];
114978
114979
114980             function cancelEvent() {
114981                 event.stopPropagation();
114982                 event.preventDefault();
114983             }
114984
114985
114986             function updateValue() {
114987                 var meters = geoOffsetToMeters(context.background().offset());
114988                 var x = +meters[0].toFixed(2);
114989                 var y = +meters[1].toFixed(2);
114990
114991                 context.container().selectAll('.nudge-inner-rect')
114992                     .select('input')
114993                     .classed('error', false)
114994                     .property('value', x + ', ' + y);
114995
114996                 context.container().selectAll('.nudge-reset')
114997                     .classed('disabled', function() {
114998                         return (x === 0 && y === 0);
114999                     });
115000             }
115001
115002
115003             function resetOffset() {
115004                 context.background().offset([0, 0]);
115005                 updateValue();
115006             }
115007
115008
115009             function nudge(d) {
115010                 context.background().nudge(d, context.map().zoom());
115011                 updateValue();
115012             }
115013
115014
115015             function pointerdownNudgeButton(d) {
115016                 var interval;
115017                 var timeout = window.setTimeout(function() {
115018                         interval = window.setInterval(nudge.bind(null, d), 100);
115019                     }, 500);
115020
115021                 function doneNudge() {
115022                     window.clearTimeout(timeout);
115023                     window.clearInterval(interval);
115024                     select(window)
115025                         .on(_pointerPrefix + 'up.buttonoffset', null, true)
115026                         .on(_pointerPrefix + 'down.buttonoffset', null, true);
115027                 }
115028
115029                 select(window)
115030                     .on(_pointerPrefix + 'up.buttonoffset', doneNudge, true)
115031                     .on(_pointerPrefix + 'down.buttonoffset', doneNudge, true);
115032
115033                 nudge(d);
115034             }
115035
115036
115037             function inputOffset() {
115038                 var input = select(this);
115039                 var d = input.node().value;
115040
115041                 if (d === '') { return resetOffset(); }
115042
115043                 d = d.replace(/;/g, ',').split(',').map(function(n) {
115044                     // if n is NaN, it will always get mapped to false.
115045                     return !isNaN(n) && n;
115046                 });
115047
115048                 if (d.length !== 2 || !d[0] || !d[1]) {
115049                     input.classed('error', true);
115050                     return;
115051                 }
115052
115053                 context.background().offset(geoMetersToOffset(d));
115054                 updateValue();
115055             }
115056
115057
115058             function dragOffset() {
115059                 if (event.button !== 0) { return; }
115060
115061                 var origin = [event.clientX, event.clientY];
115062
115063                 var pointerId = event.pointerId || 'mouse';
115064
115065                 context.container()
115066                     .append('div')
115067                     .attr('class', 'nudge-surface');
115068
115069                 select(window)
115070                     .on(_pointerPrefix + 'move.drag-bg-offset', pointermove)
115071                     .on(_pointerPrefix + 'up.drag-bg-offset', pointerup);
115072
115073                 if (_pointerPrefix === 'pointer') {
115074                     select(window)
115075                         .on('pointercancel.drag-bg-offset', pointerup);
115076                 }
115077
115078                 function pointermove() {
115079                     if (pointerId !== (event.pointerId || 'mouse')) { return; }
115080
115081                     var latest = [event.clientX, event.clientY];
115082                     var d = [
115083                         -(origin[0] - latest[0]) / 4,
115084                         -(origin[1] - latest[1]) / 4
115085                     ];
115086
115087                     origin = latest;
115088                     nudge(d);
115089                 }
115090
115091                 function pointerup() {
115092                     if (pointerId !== (event.pointerId || 'mouse')) { return; }
115093                     if (event.button !== 0) { return; }
115094
115095                     context.container().selectAll('.nudge-surface')
115096                         .remove();
115097
115098                     select(window)
115099                         .on('.drag-bg-offset', null);
115100                 }
115101             }
115102
115103
115104             function renderDisclosureContent(selection) {
115105                 var container = selection.selectAll('.nudge-container')
115106                     .data([0]);
115107
115108                 var containerEnter = container.enter()
115109                     .append('div')
115110                     .attr('class', 'nudge-container cf');
115111
115112                 containerEnter
115113                     .append('div')
115114                     .attr('class', 'nudge-instructions')
115115                     .text(_t('background.offset'));
115116
115117                 var nudgeEnter = containerEnter
115118                     .append('div')
115119                     .attr('class', 'nudge-outer-rect')
115120                     .on(_pointerPrefix + 'down', dragOffset);
115121
115122                 nudgeEnter
115123                     .append('div')
115124                     .attr('class', 'nudge-inner-rect')
115125                     .append('input')
115126                     .on('change', inputOffset);
115127
115128                 containerEnter
115129                     .append('div')
115130                     .selectAll('button')
115131                     .data(_directions).enter()
115132                     .append('button')
115133                     .attr('class', function(d) { return d[0] + ' nudge'; })
115134                     .on('contextmenu', cancelEvent)
115135                     .on(_pointerPrefix + 'down', function(d) {
115136                         if (event.button !== 0) { return; }
115137                         pointerdownNudgeButton(d[1]);
115138                     });
115139
115140                 containerEnter
115141                     .append('button')
115142                     .attr('title', _t('background.reset'))
115143                     .attr('class', 'nudge-reset disabled')
115144                     .on('contextmenu', cancelEvent)
115145                     .on('click', function() {
115146                         event.preventDefault();
115147                         if (event.button !== 0) { return; }
115148                         resetOffset();
115149                     })
115150                     .call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')));
115151
115152                 updateValue();
115153             }
115154
115155             context.background()
115156                 .on('change.backgroundOffset-update', updateValue);
115157
115158             return section;
115159         }
115160
115161         function uiSectionOverlayList(context) {
115162
115163             var section = uiSection('overlay-list', context)
115164                 .title(_t('background.overlays'))
115165                 .disclosureContent(renderDisclosureContent);
115166
115167             var _overlayList = select(null);
115168
115169             function setTooltips(selection) {
115170                 selection.each(function(d, i, nodes) {
115171                     var item = select(this).select('label');
115172                     var span = item.select('span');
115173                     var placement = (i < nodes.length / 2) ? 'bottom' : 'top';
115174                     var description = d.description();
115175                     var isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth'));
115176
115177                     item.call(uiTooltip().destroyAny);
115178
115179                     if (description || isOverflowing) {
115180                         item.call(uiTooltip()
115181                             .placement(placement)
115182                             .title(description || d.name())
115183                         );
115184                     }
115185                 });
115186             }
115187
115188             function updateLayerSelections(selection) {
115189                 function active(d) {
115190                     return context.background().showsLayer(d);
115191                 }
115192
115193                 selection.selectAll('li')
115194                     .classed('active', active)
115195                     .call(setTooltips)
115196                     .selectAll('input')
115197                     .property('checked', active);
115198             }
115199
115200
115201             function chooseOverlay(d) {
115202                 event.preventDefault();
115203                 context.background().toggleOverlayLayer(d);
115204                 _overlayList.call(updateLayerSelections);
115205                 document.activeElement.blur();
115206             }
115207
115208             function drawListItems(layerList, type, change, filter) {
115209                 var sources = context.background()
115210                     .sources(context.map().extent(), context.map().zoom(), true)
115211                     .filter(filter);
115212
115213                 var layerLinks = layerList.selectAll('li')
115214                     .data(sources, function(d) { return d.name(); });
115215
115216                 layerLinks.exit()
115217                     .remove();
115218
115219                 var enter = layerLinks.enter()
115220                     .append('li');
115221
115222                 var label = enter
115223                     .append('label');
115224
115225                 label
115226                     .append('input')
115227                     .attr('type', type)
115228                     .attr('name', 'layers')
115229                     .on('change', change);
115230
115231                 label
115232                     .append('span')
115233                     .text(function(d) { return d.name(); });
115234
115235
115236                 layerList.selectAll('li')
115237                     .sort(sortSources);
115238
115239                 layerList
115240                     .call(updateLayerSelections);
115241
115242
115243                 function sortSources(a, b) {
115244                     return a.best() && !b.best() ? -1
115245                         : b.best() && !a.best() ? 1
115246                         : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
115247                 }
115248             }
115249
115250             function renderDisclosureContent(selection) {
115251
115252                 var container = selection.selectAll('.layer-overlay-list')
115253                     .data([0]);
115254
115255                 _overlayList = container.enter()
115256                     .append('ul')
115257                     .attr('class', 'layer-list layer-overlay-list')
115258                     .attr('dir', 'auto')
115259                     .merge(container);
115260
115261                 _overlayList
115262                     .call(drawListItems, 'checkbox', chooseOverlay, function(d) { return !d.isHidden() && d.overlay; });
115263             }
115264
115265             context.map()
115266                 .on('move.overlay_list',
115267                     debounce(function() {
115268                         // layers in-view may have changed due to map move
115269                         window.requestIdleCallback(section.reRender);
115270                     }, 1000)
115271                 );
115272
115273             return section;
115274         }
115275
115276         function uiPaneBackground(context) {
115277
115278             var backgroundPane = uiPane('background', context)
115279                 .key(_t('background.key'))
115280                 .title(_t('background.title'))
115281                 .description(_t('background.description'))
115282                 .iconName('iD-icon-layers')
115283                 .sections([
115284                     uiSectionBackgroundList(context),
115285                     uiSectionOverlayList(context),
115286                     uiSectionBackgroundDisplayOptions(context),
115287                     uiSectionBackgroundOffset(context)
115288                 ]);
115289
115290             return backgroundPane;
115291         }
115292
115293         function uiPaneHelp(context) {
115294
115295             var docKeys = [
115296                 ['help', [
115297                     'welcome',
115298                     'open_data_h',
115299                     'open_data',
115300                     'before_start_h',
115301                     'before_start',
115302                     'open_source_h',
115303                     'open_source',
115304                     'open_source_help'
115305                 ]],
115306                 ['overview', [
115307                     'navigation_h',
115308                     'navigation_drag',
115309                     'navigation_zoom',
115310                     'features_h',
115311                     'features',
115312                     'nodes_ways'
115313                 ]],
115314                 ['editing', [
115315                     'select_h',
115316                     'select_left_click',
115317                     'select_right_click',
115318                     'select_space',
115319                     'multiselect_h',
115320                     'multiselect',
115321                     'multiselect_shift_click',
115322                     'multiselect_lasso',
115323                     'undo_redo_h',
115324                     'undo_redo',
115325                     'save_h',
115326                     'save',
115327                     'save_validation',
115328                     'upload_h',
115329                     'upload',
115330                     'backups_h',
115331                     'backups',
115332                     'keyboard_h',
115333                     'keyboard'
115334                 ]],
115335                 ['feature_editor', [
115336                     'intro',
115337                     'definitions',
115338                     'type_h',
115339                     'type',
115340                     'type_picker',
115341                     'fields_h',
115342                     'fields_all_fields',
115343                     'fields_example',
115344                     'fields_add_field',
115345                     'tags_h',
115346                     'tags_all_tags',
115347                     'tags_resources'
115348                 ]],
115349                 ['points', [
115350                     'intro',
115351                     'add_point_h',
115352                     'add_point',
115353                     'add_point_finish',
115354                     'move_point_h',
115355                     'move_point',
115356                     'delete_point_h',
115357                     'delete_point',
115358                     'delete_point_command'
115359                 ]],
115360                 ['lines', [
115361                     'intro',
115362                     'add_line_h',
115363                     'add_line',
115364                     'add_line_draw',
115365                     'add_line_continue',
115366                     'add_line_finish',
115367                     'modify_line_h',
115368                     'modify_line_dragnode',
115369                     'modify_line_addnode',
115370                     'connect_line_h',
115371                     'connect_line',
115372                     'connect_line_display',
115373                     'connect_line_drag',
115374                     'connect_line_tag',
115375                     'disconnect_line_h',
115376                     'disconnect_line_command',
115377                     'move_line_h',
115378                     'move_line_command',
115379                     'move_line_connected',
115380                     'delete_line_h',
115381                     'delete_line',
115382                     'delete_line_command'
115383                 ]],
115384                 ['areas', [
115385                     'intro',
115386                     'point_or_area_h',
115387                     'point_or_area',
115388                     'add_area_h',
115389                     'add_area_command',
115390                     'add_area_draw',
115391                     'add_area_continue',
115392                     'add_area_finish',
115393                     'square_area_h',
115394                     'square_area_command',
115395                     'modify_area_h',
115396                     'modify_area_dragnode',
115397                     'modify_area_addnode',
115398                     'delete_area_h',
115399                     'delete_area',
115400                     'delete_area_command'
115401                 ]],
115402                 ['relations', [
115403                     'intro',
115404                     'edit_relation_h',
115405                     'edit_relation',
115406                     'edit_relation_add',
115407                     'edit_relation_delete',
115408                     'maintain_relation_h',
115409                     'maintain_relation',
115410                     'relation_types_h',
115411                     'multipolygon_h',
115412                     'multipolygon',
115413                     'multipolygon_create',
115414                     'multipolygon_merge',
115415                     'turn_restriction_h',
115416                     'turn_restriction',
115417                     'turn_restriction_field',
115418                     'turn_restriction_editing',
115419                     'route_h',
115420                     'route',
115421                     'route_add',
115422                     'boundary_h',
115423                     'boundary',
115424                     'boundary_add'
115425                 ]],
115426                 ['notes', [
115427                     'intro',
115428                     'add_note_h',
115429                     'add_note',
115430                     'place_note',
115431                     'move_note',
115432                     'update_note_h',
115433                     'update_note',
115434                     'save_note_h',
115435                     'save_note'
115436                 ]],
115437                 ['imagery', [
115438                     'intro',
115439                     'sources_h',
115440                     'choosing',
115441                     'sources',
115442                     'offsets_h',
115443                     'offset',
115444                     'offset_change'
115445                 ]],
115446                 ['streetlevel', [
115447                     'intro',
115448                     'using_h',
115449                     'using',
115450                     'photos',
115451                     'viewer'
115452                 ]],
115453                 ['gps', [
115454                     'intro',
115455                     'survey',
115456                     'using_h',
115457                     'using',
115458                     'tracing',
115459                     'upload'
115460                 ]],
115461                 ['qa', [
115462                     'intro',
115463                     'tools_h',
115464                     'tools',
115465                     'issues_h',
115466                     'issues'
115467                 ]]
115468             ];
115469
115470             var headings = {
115471                 'help.help.open_data_h': 3,
115472                 'help.help.before_start_h': 3,
115473                 'help.help.open_source_h': 3,
115474                 'help.overview.navigation_h': 3,
115475                 'help.overview.features_h': 3,
115476                 'help.editing.select_h': 3,
115477                 'help.editing.multiselect_h': 3,
115478                 'help.editing.undo_redo_h': 3,
115479                 'help.editing.save_h': 3,
115480                 'help.editing.upload_h': 3,
115481                 'help.editing.backups_h': 3,
115482                 'help.editing.keyboard_h': 3,
115483                 'help.feature_editor.type_h': 3,
115484                 'help.feature_editor.fields_h': 3,
115485                 'help.feature_editor.tags_h': 3,
115486                 'help.points.add_point_h': 3,
115487                 'help.points.move_point_h': 3,
115488                 'help.points.delete_point_h': 3,
115489                 'help.lines.add_line_h': 3,
115490                 'help.lines.modify_line_h': 3,
115491                 'help.lines.connect_line_h': 3,
115492                 'help.lines.disconnect_line_h': 3,
115493                 'help.lines.move_line_h': 3,
115494                 'help.lines.delete_line_h': 3,
115495                 'help.areas.point_or_area_h': 3,
115496                 'help.areas.add_area_h': 3,
115497                 'help.areas.square_area_h': 3,
115498                 'help.areas.modify_area_h': 3,
115499                 'help.areas.delete_area_h': 3,
115500                 'help.relations.edit_relation_h': 3,
115501                 'help.relations.maintain_relation_h': 3,
115502                 'help.relations.relation_types_h': 2,
115503                 'help.relations.multipolygon_h': 3,
115504                 'help.relations.turn_restriction_h': 3,
115505                 'help.relations.route_h': 3,
115506                 'help.relations.boundary_h': 3,
115507                 'help.notes.add_note_h': 3,
115508                 'help.notes.update_note_h': 3,
115509                 'help.notes.save_note_h': 3,
115510                 'help.imagery.sources_h': 3,
115511                 'help.imagery.offsets_h': 3,
115512                 'help.streetlevel.using_h': 3,
115513                 'help.gps.using_h': 3,
115514                 'help.qa.tools_h': 3,
115515                 'help.qa.issues_h': 3
115516             };
115517
115518             // For each section, squash all the texts into a single markdown document
115519             var docs = docKeys.map(function(key) {
115520                 var helpkey = 'help.' + key[0];
115521                 var helpPaneReplacements = { version: context.version };
115522                 var text = key[1].reduce(function(all, part) {
115523                     var subkey = helpkey + '.' + part;
115524                     var depth = headings[subkey];                              // is this subkey a heading?
115525                     var hhh = depth ? Array(depth + 1).join('#') + ' ' : '';   // if so, prepend with some ##'s
115526                     return all + hhh + helpString(subkey, helpPaneReplacements) + '\n\n';
115527                 }, '');
115528
115529                 return {
115530                     title: _t(helpkey + '.title'),
115531                     html: marked_1(text.trim())
115532                         // use keyboard key styling for shortcuts
115533                         .replace(/<code>/g, '<kbd>')
115534                         .replace(/<\/code>/g, '<\/kbd>')
115535                 };
115536             });
115537
115538             var helpPane = uiPane('help', context)
115539                 .key(_t('help.key'))
115540                 .title(_t('help.title'))
115541                 .description(_t('help.title'))
115542                 .iconName('iD-icon-help');
115543
115544             helpPane.renderContent = function(content) {
115545
115546                 function clickHelp(d, i) {
115547                     var rtl = (_mainLocalizer.textDirection() === 'rtl');
115548                     content.property('scrollTop', 0);
115549                     helpPane.selection().select('.pane-heading h2').html(d.title);
115550
115551                     body.html(d.html);
115552                     body.selectAll('a')
115553                         .attr('target', '_blank');
115554                     menuItems.classed('selected', function(m) {
115555                         return m.title === d.title;
115556                     });
115557
115558                     nav.html('');
115559                     if (rtl) {
115560                         nav.call(drawNext).call(drawPrevious);
115561                     } else {
115562                         nav.call(drawPrevious).call(drawNext);
115563                     }
115564
115565
115566                     function drawNext(selection) {
115567                         if (i < docs.length - 1) {
115568                             var nextLink = selection
115569                                 .append('a')
115570                                 .attr('class', 'next')
115571                                 .on('click', function() {
115572                                     clickHelp(docs[i + 1], i + 1);
115573                                 });
115574
115575                             nextLink
115576                                 .append('span')
115577                                 .text(docs[i + 1].title)
115578                                 .call(svgIcon((rtl ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'));
115579                         }
115580                     }
115581
115582
115583                     function drawPrevious(selection) {
115584                         if (i > 0) {
115585                             var prevLink = selection
115586                                 .append('a')
115587                                 .attr('class', 'previous')
115588                                 .on('click', function() {
115589                                     clickHelp(docs[i - 1], i - 1);
115590                                 });
115591
115592                             prevLink
115593                                 .call(svgIcon((rtl ? '#iD-icon-forward' : '#iD-icon-backward'), 'inline'))
115594                                 .append('span')
115595                                 .text(docs[i - 1].title);
115596                         }
115597                     }
115598                 }
115599
115600
115601                 function clickWalkthrough() {
115602                     if (context.inIntro()) { return; }
115603                     context.container().call(uiIntro(context));
115604                     context.ui().togglePanes();
115605                 }
115606
115607
115608                 function clickShortcuts() {
115609                     context.container().call(uiShortcuts(context), true);
115610                 }
115611
115612                 var toc = content
115613                     .append('ul')
115614                     .attr('class', 'toc');
115615
115616                 var menuItems = toc.selectAll('li')
115617                     .data(docs)
115618                     .enter()
115619                     .append('li')
115620                     .append('a')
115621                     .html(function(d) { return d.title; })
115622                     .on('click', clickHelp);
115623
115624                 var shortcuts = toc
115625                     .append('li')
115626                     .attr('class', 'shortcuts')
115627                     .call(uiTooltip()
115628                         .title(_t('shortcuts.tooltip'))
115629                         .keys(['?'])
115630                         .placement('top')
115631                     )
115632                     .append('a')
115633                     .on('click', clickShortcuts);
115634
115635                 shortcuts
115636                     .append('div')
115637                     .text(_t('shortcuts.title'));
115638
115639                 var walkthrough = toc
115640                     .append('li')
115641                     .attr('class', 'walkthrough')
115642                     .append('a')
115643                     .on('click', clickWalkthrough);
115644
115645                 walkthrough
115646                     .append('svg')
115647                     .attr('class', 'logo logo-walkthrough')
115648                     .append('use')
115649                     .attr('xlink:href', '#iD-logo-walkthrough');
115650
115651                 walkthrough
115652                     .append('div')
115653                     .text(_t('splash.walkthrough'));
115654
115655
115656                 var helpContent = content
115657                     .append('div')
115658                     .attr('class', 'left-content');
115659
115660                 var body = helpContent
115661                     .append('div')
115662                     .attr('class', 'body');
115663
115664                 var nav = helpContent
115665                     .append('div')
115666                     .attr('class', 'nav');
115667
115668                 clickHelp(docs[0], 0);
115669
115670             };
115671
115672             return helpPane;
115673         }
115674
115675         function uiSectionValidationIssues(id, severity, context) {
115676
115677             var _issues = [];
115678
115679             var section = uiSection(id, context)
115680                 .title(function() {
115681                     if (!_issues) { return ''; }
115682                     var issueCountText = _issues.length > 1000 ? '1000+' : String(_issues.length);
115683                     return _t('issues.' + severity + 's.list_title', { count: issueCountText });
115684                 })
115685                 .disclosureContent(renderDisclosureContent)
115686                 .shouldDisplay(function() {
115687                     return _issues && _issues.length;
115688                 });
115689
115690             function getOptions() {
115691                 return {
115692                     what: corePreferences('validate-what') || 'edited',
115693                     where: corePreferences('validate-where') || 'all'
115694                 };
115695             }
115696
115697             // get and cache the issues to display, unordered
115698             function reloadIssues() {
115699                 _issues = context.validator().getIssuesBySeverity(getOptions())[severity];
115700             }
115701
115702             function renderDisclosureContent(selection) {
115703
115704                 var center = context.map().center();
115705                 var graph = context.graph();
115706
115707                 // sort issues by distance away from the center of the map
115708                 var issues = _issues.map(function withDistance(issue) {
115709                         var extent = issue.extent(graph);
115710                         var dist = extent ? geoSphericalDistance(center, extent.center()) : 0;
115711                         return Object.assign(issue, { dist: dist });
115712                     })
115713                     .sort(function byDistance(a, b) {
115714                         return a.dist - b.dist;
115715                     });
115716
115717                 // cut off at 1000
115718                 issues = issues.slice(0, 1000);
115719
115720                 //renderIgnoredIssuesReset(_warningsSelection);
115721
115722                 selection
115723                     .call(drawIssuesList, issues);
115724             }
115725
115726             function drawIssuesList(selection, issues) {
115727                 var list = selection.selectAll('.issues-list')
115728                     .data([0]);
115729
115730                 list = list.enter()
115731                     .append('ul')
115732                     .attr('class', 'layer-list issues-list ' + severity + 's-list')
115733                     .merge(list);
115734
115735
115736                 var items = list.selectAll('li')
115737                     .data(issues, function(d) { return d.id; });
115738
115739                 // Exit
115740                 items.exit()
115741                     .remove();
115742
115743                 // Enter
115744                 var itemsEnter = items.enter()
115745                     .append('li')
115746                     .attr('class', function (d) { return 'issue severity-' + d.severity; })
115747                     .on('click', function(d) {
115748                         context.validator().focusIssue(d);
115749                     })
115750                     .on('mouseover', function(d) {
115751                         utilHighlightEntities(d.entityIds, true, context);
115752                     })
115753                     .on('mouseout', function(d) {
115754                         utilHighlightEntities(d.entityIds, false, context);
115755                     });
115756
115757
115758                 var labelsEnter = itemsEnter
115759                     .append('div')
115760                     .attr('class', 'issue-label');
115761
115762                 var textEnter = labelsEnter
115763                     .append('span')
115764                     .attr('class', 'issue-text');
115765
115766                 textEnter
115767                     .append('span')
115768                     .attr('class', 'issue-icon')
115769                     .each(function(d) {
115770                         var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
115771                         select(this)
115772                             .call(svgIcon(iconName));
115773                     });
115774
115775                 textEnter
115776                     .append('span')
115777                     .attr('class', 'issue-message');
115778
115779                 /*
115780                 labelsEnter
115781                     .append('span')
115782                     .attr('class', 'issue-autofix')
115783                     .each(function(d) {
115784                         if (!d.autoFix) return;
115785
115786                         d3_select(this)
115787                             .append('button')
115788                             .attr('title', t('issues.fix_one.title'))
115789                             .datum(d.autoFix)  // set button datum to the autofix
115790                             .attr('class', 'autofix action')
115791                             .on('click', function(d) {
115792                                 d3_event.preventDefault();
115793                                 d3_event.stopPropagation();
115794
115795                                 var issuesEntityIDs = d.issue.entityIds;
115796                                 utilHighlightEntities(issuesEntityIDs.concat(d.entityIds), false, context);
115797
115798                                 context.perform.apply(context, d.autoArgs);
115799                                 context.validator().validate();
115800                             })
115801                             .call(svgIcon('#iD-icon-wrench'));
115802                     });
115803                 */
115804
115805                 // Update
115806                 items = items
115807                     .merge(itemsEnter)
115808                     .order();
115809
115810                 items.selectAll('.issue-message')
115811                     .text(function(d) {
115812                         return d.message(context);
115813                     });
115814
115815                 /*
115816                 // autofix
115817                 var canAutoFix = issues.filter(function(issue) { return issue.autoFix; });
115818
115819                 var autoFixAll = selection.selectAll('.autofix-all')
115820                     .data(canAutoFix.length ? [0] : []);
115821
115822                 // exit
115823                 autoFixAll.exit()
115824                     .remove();
115825
115826                 // enter
115827                 var autoFixAllEnter = autoFixAll.enter()
115828                     .insert('div', '.issues-list')
115829                     .attr('class', 'autofix-all');
115830
115831                 var linkEnter = autoFixAllEnter
115832                     .append('a')
115833                     .attr('class', 'autofix-all-link')
115834                     .attr('href', '#');
115835
115836                 linkEnter
115837                     .append('span')
115838                     .attr('class', 'autofix-all-link-text')
115839                     .text(t('issues.fix_all.title'));
115840
115841                 linkEnter
115842                     .append('span')
115843                     .attr('class', 'autofix-all-link-icon')
115844                     .call(svgIcon('#iD-icon-wrench'));
115845
115846                 if (severity === 'warning') {
115847                     renderIgnoredIssuesReset(selection);
115848                 }
115849
115850                 // update
115851                 autoFixAll = autoFixAll
115852                     .merge(autoFixAllEnter);
115853
115854                 autoFixAll.selectAll('.autofix-all-link')
115855                     .on('click', function() {
115856                         context.pauseChangeDispatch();
115857                         context.perform(actionNoop());
115858                         canAutoFix.forEach(function(issue) {
115859                             var args = issue.autoFix.autoArgs.slice();  // copy
115860                             if (typeof args[args.length - 1] !== 'function') {
115861                                 args.pop();
115862                             }
115863                             args.push(t('issues.fix_all.annotation'));
115864                             context.replace.apply(context, args);
115865                         });
115866                         context.resumeChangeDispatch();
115867                         context.validator().validate();
115868                     });
115869                 */
115870             }
115871
115872             context.validator().on('validated.uiSectionValidationIssues' + id, function() {
115873                 window.requestIdleCallback(function() {
115874                     reloadIssues();
115875                     section.reRender();
115876                 });
115877             });
115878
115879             context.map().on('move.uiSectionValidationIssues' + id,
115880                 debounce(function() {
115881                     window.requestIdleCallback(function() {
115882                         if (getOptions().where === 'visible') {
115883                             // must refetch issues if they are viewport-dependent
115884                             reloadIssues();
115885                         }
115886                         // always reload list to re-sort-by-distance
115887                         section.reRender();
115888                     });
115889                 }, 1000)
115890             );
115891
115892             return section;
115893         }
115894
115895         function uiSectionValidationOptions(context) {
115896
115897             var section = uiSection('issues-options', context)
115898                 .content(renderContent);
115899
115900             function renderContent(selection) {
115901
115902                 var container = selection.selectAll('.issues-options-container')
115903                     .data([0]);
115904
115905                 container = container.enter()
115906                     .append('div')
115907                     .attr('class', 'issues-options-container')
115908                     .merge(container);
115909
115910                 var data = [
115911                     { key: 'what', values: ['edited', 'all'] },
115912                     { key: 'where', values: ['visible', 'all'] }
115913                 ];
115914
115915                 var options = container.selectAll('.issues-option')
115916                     .data(data, function(d) { return d.key; });
115917
115918                 var optionsEnter = options.enter()
115919                     .append('div')
115920                     .attr('class', function(d) { return 'issues-option issues-option-' + d.key; });
115921
115922                 optionsEnter
115923                     .append('div')
115924                     .attr('class', 'issues-option-title')
115925                     .text(function(d) { return _t('issues.options.' + d.key + '.title'); });
115926
115927                 var valuesEnter = optionsEnter.selectAll('label')
115928                     .data(function(d) {
115929                         return d.values.map(function(val) { return { value: val, key: d.key }; });
115930                     })
115931                     .enter()
115932                     .append('label');
115933
115934                 valuesEnter
115935                     .append('input')
115936                     .attr('type', 'radio')
115937                     .attr('name', function(d) { return 'issues-option-' + d.key; })
115938                     .attr('value', function(d) { return d.value; })
115939                     .property('checked', function(d) { return getOptions()[d.key] === d.value; })
115940                     .on('change', function(d) { updateOptionValue(d.key, d.value); });
115941
115942                 valuesEnter
115943                     .append('span')
115944                     .text(function(d) { return _t('issues.options.' + d.key + '.' + d.value); });
115945             }
115946
115947             function getOptions() {
115948                 return {
115949                     what: corePreferences('validate-what') || 'edited',  // 'all', 'edited'
115950                     where: corePreferences('validate-where') || 'all'    // 'all', 'visible'
115951                 };
115952             }
115953
115954             function updateOptionValue(d, val) {
115955                 if (!val && event && event.target) {
115956                     val = event.target.value;
115957                 }
115958
115959                 corePreferences('validate-' + d, val);
115960                 context.validator().validate();
115961             }
115962
115963             return section;
115964         }
115965
115966         function uiSectionValidationRules(context) {
115967
115968             var MINSQUARE = 0;
115969             var MAXSQUARE = 20;
115970             var DEFAULTSQUARE = 5;  // see also unsquare_way.js
115971
115972             var section = uiSection('issues-rules', context)
115973                 .disclosureContent(renderDisclosureContent)
115974                 .title(_t('issues.rules.title'));
115975
115976             var _ruleKeys = context.validator().getRuleKeys()
115977                 .filter(function(key) { return key !== 'maprules'; })
115978                 .sort(function(key1, key2) {
115979                     // alphabetize by localized title
115980                     return _t('issues.' + key1 + '.title') < _t('issues.' + key2 + '.title') ? -1 : 1;
115981                 });
115982
115983             function renderDisclosureContent(selection) {
115984                 var container = selection.selectAll('.issues-rulelist-container')
115985                     .data([0]);
115986
115987                 var containerEnter = container.enter()
115988                     .append('div')
115989                     .attr('class', 'issues-rulelist-container');
115990
115991                 containerEnter
115992                     .append('ul')
115993                     .attr('class', 'layer-list issue-rules-list');
115994
115995                 var ruleLinks = containerEnter
115996                     .append('div')
115997                     .attr('class', 'issue-rules-links section-footer');
115998
115999                 ruleLinks
116000                     .append('a')
116001                     .attr('class', 'issue-rules-link')
116002                     .attr('href', '#')
116003                     .text(_t('issues.enable_all'))
116004                     .on('click', function() {
116005                         context.validator().disableRules([]);
116006                     });
116007
116008                 ruleLinks
116009                     .append('a')
116010                     .attr('class', 'issue-rules-link')
116011                     .attr('href', '#')
116012                     .text(_t('issues.disable_all'))
116013                     .on('click', function() {
116014                         context.validator().disableRules(_ruleKeys);
116015                     });
116016
116017
116018                 // Update
116019                 container = container
116020                     .merge(containerEnter);
116021
116022                 container.selectAll('.issue-rules-list')
116023                     .call(drawListItems, _ruleKeys, 'checkbox', 'rule', toggleRule, isRuleEnabled);
116024             }
116025
116026             function drawListItems(selection, data, type, name, change, active) {
116027                 var items = selection.selectAll('li')
116028                     .data(data);
116029
116030                 // Exit
116031                 items.exit()
116032                     .remove();
116033
116034                 // Enter
116035                 var enter = items.enter()
116036                     .append('li');
116037
116038                 if (name === 'rule') {
116039                     enter
116040                         .call(uiTooltip()
116041                             .title(function(d) { return _t('issues.' + d + '.tip'); })
116042                             .placement('top')
116043                         );
116044                 }
116045
116046                 var label = enter
116047                     .append('label');
116048
116049                 label
116050                     .append('input')
116051                     .attr('type', type)
116052                     .attr('name', name)
116053                     .on('change', change);
116054
116055                 label
116056                     .append('span')
116057                     .html(function(d) {
116058                         var params = {};
116059                         if (d === 'unsquare_way') {
116060                             params.val = '<span class="square-degrees"></span>';
116061                         }
116062                         return _t('issues.' + d + '.title', params);
116063                     });
116064
116065                 // Update
116066                 items = items
116067                     .merge(enter);
116068
116069                 items
116070                     .classed('active', active)
116071                     .selectAll('input')
116072                     .property('checked', active)
116073                     .property('indeterminate', false);
116074
116075
116076                 // user-configurable square threshold
116077                 var degStr = corePreferences('validate-square-degrees');
116078                 if (degStr === null) {
116079                     degStr = '' + DEFAULTSQUARE;
116080                 }
116081
116082                 var span = items.selectAll('.square-degrees');
116083                 var input = span.selectAll('.square-degrees-input')
116084                     .data([0]);
116085
116086                 // enter / update
116087                 input.enter()
116088                     .append('input')
116089                     .attr('type', 'number')
116090                     .attr('min', '' + MINSQUARE)
116091                     .attr('max', '' + MAXSQUARE)
116092                     .attr('step', '0.5')
116093                     .attr('class', 'square-degrees-input')
116094                     .call(utilNoAuto)
116095                     .on('click', function () {
116096                         event.preventDefault();
116097                         event.stopPropagation();
116098                         this.select();
116099                     })
116100                     .on('keyup', function () {
116101                         if (event.keyCode === 13) { // enter
116102                             this.blur();
116103                             this.select();
116104                         }
116105                     })
116106                     .on('blur', changeSquare)
116107                     .merge(input)
116108                     .property('value', degStr);
116109             }
116110
116111             function changeSquare() {
116112                 var input = select(this);
116113                 var degStr = utilGetSetValue(input).trim();
116114                 var degNum = parseFloat(degStr, 10);
116115
116116                 if (!isFinite(degNum)) {
116117                     degNum = DEFAULTSQUARE;
116118                 } else if (degNum > MAXSQUARE) {
116119                     degNum = MAXSQUARE;
116120                 } else if (degNum < MINSQUARE) {
116121                     degNum = MINSQUARE;
116122                 }
116123
116124                 degNum = Math.round(degNum * 10 ) / 10;   // round to 1 decimal
116125                 degStr = '' + degNum;
116126
116127                 input
116128                     .property('value', degStr);
116129
116130                 corePreferences('validate-square-degrees', degStr);
116131                 context.validator().reloadUnsquareIssues();
116132             }
116133
116134             function isRuleEnabled(d) {
116135                 return context.validator().isRuleEnabled(d);
116136             }
116137
116138             function toggleRule(d) {
116139                 context.validator().toggleRule(d);
116140             }
116141
116142             context.validator().on('validated.uiSectionValidationRules', function() {
116143                 window.requestIdleCallback(section.reRender);
116144             });
116145
116146             return section;
116147         }
116148
116149         function uiSectionValidationStatus(context) {
116150
116151             var section = uiSection('issues-status', context)
116152                 .content(renderContent)
116153                 .shouldDisplay(function() {
116154                     var issues = context.validator().getIssues(getOptions());
116155                     return issues.length === 0;
116156                 });
116157
116158             function getOptions() {
116159                 return {
116160                     what: corePreferences('validate-what') || 'edited',
116161                     where: corePreferences('validate-where') || 'all'
116162                 };
116163             }
116164
116165             function renderContent(selection) {
116166
116167                 var box = selection.selectAll('.box')
116168                     .data([0]);
116169
116170                 var boxEnter = box.enter()
116171                     .append('div')
116172                     .attr('class', 'box');
116173
116174                 boxEnter
116175                     .append('div')
116176                     .call(svgIcon('#iD-icon-apply', 'pre-text'));
116177
116178                 var noIssuesMessage = boxEnter
116179                     .append('span');
116180
116181                 noIssuesMessage
116182                     .append('strong')
116183                     .attr('class', 'message');
116184
116185                 noIssuesMessage
116186                     .append('br');
116187
116188                 noIssuesMessage
116189                     .append('span')
116190                     .attr('class', 'details');
116191
116192                 renderIgnoredIssuesReset(selection);
116193                 setNoIssuesText(selection);
116194             }
116195
116196             function renderIgnoredIssuesReset(selection) {
116197
116198                 var ignoredIssues = context.validator()
116199                     .getIssues({ what: 'all', where: 'all', includeDisabledRules: true, includeIgnored: 'only' });
116200
116201                 var resetIgnored = selection.selectAll('.reset-ignored')
116202                     .data(ignoredIssues.length ? [0] : []);
116203
116204                 // exit
116205                 resetIgnored.exit()
116206                     .remove();
116207
116208                 // enter
116209                 var resetIgnoredEnter = resetIgnored.enter()
116210                     .append('div')
116211                     .attr('class', 'reset-ignored section-footer');
116212
116213                 resetIgnoredEnter
116214                     .append('a')
116215                     .attr('href', '#');
116216
116217                 // update
116218                 resetIgnored = resetIgnored
116219                     .merge(resetIgnoredEnter);
116220
116221                 resetIgnored.select('a')
116222                     .text(_t('issues.reset_ignored', { count: ignoredIssues.length.toString() }));
116223
116224                 resetIgnored.on('click', function() {
116225                     context.validator().resetIgnoredIssues();
116226                 });
116227             }
116228
116229             function setNoIssuesText(selection) {
116230
116231                 var opts = getOptions();
116232
116233                 function checkForHiddenIssues(cases) {
116234                     for (var type in cases) {
116235                         var hiddenOpts = cases[type];
116236                         var hiddenIssues = context.validator().getIssues(hiddenOpts);
116237                         if (hiddenIssues.length) {
116238                             selection.select('.box .details')
116239                                 .text(_t(
116240                                     'issues.no_issues.hidden_issues.' + type,
116241                                     { count: hiddenIssues.length.toString() }
116242                                 ));
116243                             return;
116244                         }
116245                     }
116246                     selection.select('.box .details')
116247                         .text(_t('issues.no_issues.hidden_issues.none'));
116248                 }
116249
116250                 var messageType;
116251
116252                 if (opts.what === 'edited' && opts.where === 'visible') {
116253
116254                     messageType = 'edits_in_view';
116255
116256                     checkForHiddenIssues({
116257                         elsewhere: { what: 'edited', where: 'all' },
116258                         everything_else: { what: 'all', where: 'visible' },
116259                         disabled_rules: { what: 'edited', where: 'visible', includeDisabledRules: 'only' },
116260                         everything_else_elsewhere: { what: 'all', where: 'all' },
116261                         disabled_rules_elsewhere: { what: 'edited', where: 'all', includeDisabledRules: 'only' },
116262                         ignored_issues: { what: 'edited', where: 'visible', includeIgnored: 'only' },
116263                         ignored_issues_elsewhere: { what: 'edited', where: 'all', includeIgnored: 'only' }
116264                     });
116265
116266                 } else if (opts.what === 'edited' && opts.where === 'all') {
116267
116268                     messageType = 'edits';
116269
116270                     checkForHiddenIssues({
116271                         everything_else: { what: 'all', where: 'all' },
116272                         disabled_rules: { what: 'edited', where: 'all', includeDisabledRules: 'only' },
116273                         ignored_issues: { what: 'edited', where: 'all', includeIgnored: 'only' }
116274                     });
116275
116276                 } else if (opts.what === 'all' && opts.where === 'visible') {
116277
116278                     messageType = 'everything_in_view';
116279
116280                     checkForHiddenIssues({
116281                         elsewhere: { what: 'all', where: 'all' },
116282                         disabled_rules: { what: 'all', where: 'visible', includeDisabledRules: 'only' },
116283                         disabled_rules_elsewhere: { what: 'all', where: 'all', includeDisabledRules: 'only' },
116284                         ignored_issues: { what: 'all', where: 'visible', includeIgnored: 'only' },
116285                         ignored_issues_elsewhere: { what: 'all', where: 'all', includeIgnored: 'only' }
116286                     });
116287                 } else if (opts.what === 'all' && opts.where === 'all') {
116288
116289                     messageType = 'everything';
116290
116291                     checkForHiddenIssues({
116292                         disabled_rules: { what: 'all', where: 'all', includeDisabledRules: 'only' },
116293                         ignored_issues: { what: 'all', where: 'all', includeIgnored: 'only' }
116294                     });
116295                 }
116296
116297                 if (opts.what === 'edited' && context.history().difference().summary().length === 0) {
116298                     messageType = 'no_edits';
116299                 }
116300
116301                 selection.select('.box .message')
116302                     .text(_t('issues.no_issues.message.' + messageType));
116303
116304             }
116305
116306             context.validator().on('validated.uiSectionValidationStatus', function() {
116307                 window.requestIdleCallback(section.reRender);
116308             });
116309
116310             context.map().on('move.uiSectionValidationStatus',
116311                 debounce(function() {
116312                     window.requestIdleCallback(section.reRender);
116313                 }, 1000)
116314             );
116315
116316             return section;
116317         }
116318
116319         function uiPaneIssues(context) {
116320
116321             var issuesPane = uiPane('issues', context)
116322                 .key(_t('issues.key'))
116323                 .title(_t('issues.title'))
116324                 .description(_t('issues.title'))
116325                 .iconName('iD-icon-alert')
116326                 .sections([
116327                     uiSectionValidationOptions(context),
116328                     uiSectionValidationStatus(context),
116329                     uiSectionValidationIssues('issues-errors', 'error', context),
116330                     uiSectionValidationIssues('issues-warnings', 'warning', context),
116331                     uiSectionValidationRules(context)
116332                 ]);
116333
116334             return issuesPane;
116335         }
116336
116337         function uiSettingsCustomData(context) {
116338             var dispatch$1 = dispatch('change');
116339
116340             function render(selection) {
116341                 var dataLayer = context.layers().layer('data');
116342
116343                 // keep separate copies of original and current settings
116344                 var _origSettings = {
116345                     fileList: (dataLayer && dataLayer.fileList()) || null,
116346                     url: corePreferences('settings-custom-data-url')
116347                 };
116348                 var _currSettings = {
116349                     fileList: (dataLayer && dataLayer.fileList()) || null,
116350                     url: corePreferences('settings-custom-data-url')
116351                 };
116352
116353                 // var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
116354                 var modal = uiConfirm(selection).okButton();
116355
116356                 modal
116357                     .classed('settings-modal settings-custom-data', true);
116358
116359                 modal.select('.modal-section.header')
116360                     .append('h3')
116361                     .text(_t('settings.custom_data.header'));
116362
116363
116364                 var textSection = modal.select('.modal-section.message-text');
116365
116366                 textSection
116367                     .append('pre')
116368                     .attr('class', 'instructions-file')
116369                     .text(_t('settings.custom_data.file.instructions'));
116370
116371                 textSection
116372                     .append('input')
116373                     .attr('class', 'field-file')
116374                     .attr('type', 'file')
116375                     .property('files', _currSettings.fileList)  // works for all except IE11
116376                     .on('change', function() {
116377                         var files = event.target.files;
116378                         if (files && files.length) {
116379                             _currSettings.url = '';
116380                             textSection.select('.field-url').property('value', '');
116381                             _currSettings.fileList = files;
116382                         } else {
116383                             _currSettings.fileList = null;
116384                         }
116385                     });
116386
116387                 textSection
116388                     .append('h4')
116389                     .text(_t('settings.custom_data.or'));
116390
116391                 textSection
116392                     .append('pre')
116393                     .attr('class', 'instructions-url')
116394                     .text(_t('settings.custom_data.url.instructions'));
116395
116396                 textSection
116397                     .append('textarea')
116398                     .attr('class', 'field-url')
116399                     .attr('placeholder', _t('settings.custom_data.url.placeholder'))
116400                     .call(utilNoAuto)
116401                     .property('value', _currSettings.url);
116402
116403
116404                 // insert a cancel button
116405                 var buttonSection = modal.select('.modal-section.buttons');
116406
116407                 buttonSection
116408                     .insert('button', '.ok-button')
116409                     .attr('class', 'button cancel-button secondary-action')
116410                     .text(_t('confirm.cancel'));
116411
116412
116413                 buttonSection.select('.cancel-button')
116414                     .on('click.cancel', clickCancel);
116415
116416                 buttonSection.select('.ok-button')
116417                     .attr('disabled', isSaveDisabled)
116418                     .on('click.save', clickSave);
116419
116420
116421                 function isSaveDisabled() {
116422                     return null;
116423                 }
116424
116425
116426                 // restore the original url
116427                 function clickCancel() {
116428                     textSection.select('.field-url').property('value', _origSettings.url);
116429                     corePreferences('settings-custom-data-url', _origSettings.url);
116430                     this.blur();
116431                     modal.close();
116432                 }
116433
116434                 // accept the current url
116435                 function clickSave() {
116436                     _currSettings.url = textSection.select('.field-url').property('value').trim();
116437
116438                     // one or the other but not both
116439                     if (_currSettings.url) { _currSettings.fileList = null; }
116440                     if (_currSettings.fileList) { _currSettings.url = ''; }
116441
116442                     corePreferences('settings-custom-data-url', _currSettings.url);
116443                     this.blur();
116444                     modal.close();
116445                     dispatch$1.call('change', this, _currSettings);
116446                 }
116447             }
116448
116449             return utilRebind(render, dispatch$1, 'on');
116450         }
116451
116452         function uiSectionDataLayers(context) {
116453
116454             var settingsCustomData = uiSettingsCustomData(context)
116455                 .on('change', customChanged);
116456
116457             var layers = context.layers();
116458
116459             var section = uiSection('data-layers', context)
116460                 .title(_t('map_data.data_layers'))
116461                 .disclosureContent(renderDisclosureContent);
116462
116463             function renderDisclosureContent(selection) {
116464                 var container = selection.selectAll('.data-layer-container')
116465                     .data([0]);
116466
116467                 container.enter()
116468                     .append('div')
116469                     .attr('class', 'data-layer-container')
116470                     .merge(container)
116471                     .call(drawOsmItems)
116472                     .call(drawQAItems)
116473                     .call(drawCustomDataItems)
116474                     .call(drawVectorItems)      // Beta - Detroit mapping challenge
116475                     .call(drawPanelItems);
116476             }
116477
116478             function showsLayer(which) {
116479                 var layer = layers.layer(which);
116480                 if (layer) {
116481                     return layer.enabled();
116482                 }
116483                 return false;
116484             }
116485
116486             function setLayer(which, enabled) {
116487                 // Don't allow layer changes while drawing - #6584
116488                 var mode = context.mode();
116489                 if (mode && /^draw/.test(mode.id)) { return; }
116490
116491                 var layer = layers.layer(which);
116492                 if (layer) {
116493                     layer.enabled(enabled);
116494
116495                     if (!enabled && (which === 'osm' || which === 'notes')) {
116496                         context.enter(modeBrowse(context));
116497                     }
116498                 }
116499             }
116500
116501             function toggleLayer(which) {
116502                 setLayer(which, !showsLayer(which));
116503             }
116504
116505             function drawOsmItems(selection) {
116506                 var osmKeys = ['osm', 'notes'];
116507                 var osmLayers = layers.all().filter(function(obj) { return osmKeys.indexOf(obj.id) !== -1; });
116508
116509                 var ul = selection
116510                     .selectAll('.layer-list-osm')
116511                     .data([0]);
116512
116513                 ul = ul.enter()
116514                     .append('ul')
116515                     .attr('class', 'layer-list layer-list-osm')
116516                     .merge(ul);
116517
116518                 var li = ul.selectAll('.list-item')
116519                     .data(osmLayers);
116520
116521                 li.exit()
116522                     .remove();
116523
116524                 var liEnter = li.enter()
116525                     .append('li')
116526                     .attr('class', function(d) { return 'list-item list-item-' + d.id; });
116527
116528                 var labelEnter = liEnter
116529                     .append('label')
116530                     .each(function(d) {
116531                         if (d.id === 'osm') {
116532                             select(this)
116533                                 .call(uiTooltip()
116534                                     .title(_t('map_data.layers.' + d.id + '.tooltip'))
116535                                     .keys([uiCmd('⌥' + _t('area_fill.wireframe.key'))])
116536                                     .placement('bottom')
116537                                 );
116538                         } else {
116539                             select(this)
116540                                 .call(uiTooltip()
116541                                     .title(_t('map_data.layers.' + d.id + '.tooltip'))
116542                                     .placement('bottom')
116543                                 );
116544                         }
116545                     });
116546
116547                 labelEnter
116548                     .append('input')
116549                     .attr('type', 'checkbox')
116550                     .on('change', function(d) { toggleLayer(d.id); });
116551
116552                 labelEnter
116553                     .append('span')
116554                     .text(function(d) { return _t('map_data.layers.' + d.id + '.title'); });
116555
116556
116557                 // Update
116558                 li
116559                     .merge(liEnter)
116560                     .classed('active', function (d) { return d.layer.enabled(); })
116561                     .selectAll('input')
116562                     .property('checked', function (d) { return d.layer.enabled(); });
116563             }
116564
116565             function drawQAItems(selection) {
116566                 var qaKeys = ['keepRight', 'improveOSM', 'osmose'];
116567                 var qaLayers = layers.all().filter(function(obj) { return qaKeys.indexOf(obj.id) !== -1; });
116568
116569                 var ul = selection
116570                     .selectAll('.layer-list-qa')
116571                     .data([0]);
116572
116573                 ul = ul.enter()
116574                     .append('ul')
116575                     .attr('class', 'layer-list layer-list-qa')
116576                     .merge(ul);
116577
116578                 var li = ul.selectAll('.list-item')
116579                     .data(qaLayers);
116580
116581                 li.exit()
116582                     .remove();
116583
116584                 var liEnter = li.enter()
116585                     .append('li')
116586                     .attr('class', function(d) { return 'list-item list-item-' + d.id; });
116587
116588                 var labelEnter = liEnter
116589                     .append('label')
116590                     .each(function(d) {
116591                         select(this)
116592                             .call(uiTooltip()
116593                                 .title(_t('map_data.layers.' + d.id + '.tooltip'))
116594                                 .placement('bottom')
116595                             );
116596                     });
116597
116598                 labelEnter
116599                     .append('input')
116600                     .attr('type', 'checkbox')
116601                     .on('change', function(d) { toggleLayer(d.id); });
116602
116603                 labelEnter
116604                     .append('span')
116605                     .text(function(d) { return _t('map_data.layers.' + d.id + '.title'); });
116606
116607
116608                 // Update
116609                 li
116610                     .merge(liEnter)
116611                     .classed('active', function (d) { return d.layer.enabled(); })
116612                     .selectAll('input')
116613                     .property('checked', function (d) { return d.layer.enabled(); });
116614             }
116615
116616             // Beta feature - sample vector layers to support Detroit Mapping Challenge
116617             // https://github.com/osmus/detroit-mapping-challenge
116618             function drawVectorItems(selection) {
116619                 var dataLayer = layers.layer('data');
116620                 var vtData = [
116621                     {
116622                         name: 'Detroit Neighborhoods/Parks',
116623                         src: 'neighborhoods-parks',
116624                         tooltip: 'Neighborhood boundaries and parks as compiled by City of Detroit in concert with community groups.',
116625                         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'
116626                     }, {
116627                         name: 'Detroit Composite POIs',
116628                         src: 'composite-poi',
116629                         tooltip: 'Fire Inspections, Business Licenses, and other public location data collated from the City of Detroit.',
116630                         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'
116631                     }, {
116632                         name: 'Detroit All-The-Places POIs',
116633                         src: 'alltheplaces-poi',
116634                         tooltip: 'Public domain business location data created by web scrapers.',
116635                         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'
116636                     }
116637                 ];
116638
116639                 // Only show this if the map is around Detroit..
116640                 var detroit = geoExtent([-83.5, 42.1], [-82.8, 42.5]);
116641                 var showVectorItems = (context.map().zoom() > 9 && detroit.contains(context.map().center()));
116642
116643                 var container = selection.selectAll('.vectortile-container')
116644                     .data(showVectorItems ? [0] : []);
116645
116646                 container.exit()
116647                     .remove();
116648
116649                 var containerEnter = container.enter()
116650                     .append('div')
116651                     .attr('class', 'vectortile-container');
116652
116653                 containerEnter
116654                     .append('h4')
116655                     .attr('class', 'vectortile-header')
116656                     .text('Detroit Vector Tiles (Beta)');
116657
116658                 containerEnter
116659                     .append('ul')
116660                     .attr('class', 'layer-list layer-list-vectortile');
116661
116662                 containerEnter
116663                     .append('div')
116664                     .attr('class', 'vectortile-footer')
116665                     .append('a')
116666                     .attr('target', '_blank')
116667                     .attr('tabindex', -1)
116668                     .call(svgIcon('#iD-icon-out-link', 'inline'))
116669                     .attr('href', 'https://github.com/osmus/detroit-mapping-challenge')
116670                     .append('span')
116671                     .text('About these layers');
116672
116673                 container = container
116674                     .merge(containerEnter);
116675
116676
116677                 var ul = container.selectAll('.layer-list-vectortile');
116678
116679                 var li = ul.selectAll('.list-item')
116680                     .data(vtData);
116681
116682                 li.exit()
116683                     .remove();
116684
116685                 var liEnter = li.enter()
116686                     .append('li')
116687                     .attr('class', function(d) { return 'list-item list-item-' + d.src; });
116688
116689                 var labelEnter = liEnter
116690                     .append('label')
116691                     .each(function(d) {
116692                         select(this).call(
116693                             uiTooltip().title(d.tooltip).placement('top')
116694                         );
116695                     });
116696
116697                 labelEnter
116698                     .append('input')
116699                     .attr('type', 'radio')
116700                     .attr('name', 'vectortile')
116701                     .on('change', selectVTLayer);
116702
116703                 labelEnter
116704                     .append('span')
116705                     .text(function(d) { return d.name; });
116706
116707                 // Update
116708                 li
116709                     .merge(liEnter)
116710                     .classed('active', isVTLayerSelected)
116711                     .selectAll('input')
116712                     .property('checked', isVTLayerSelected);
116713
116714
116715                 function isVTLayerSelected(d) {
116716                     return dataLayer && dataLayer.template() === d.template;
116717                 }
116718
116719                 function selectVTLayer(d) {
116720                     corePreferences('settings-custom-data-url', d.template);
116721                     if (dataLayer) {
116722                         dataLayer.template(d.template, d.src);
116723                         dataLayer.enabled(true);
116724                     }
116725                 }
116726             }
116727
116728             function drawCustomDataItems(selection) {
116729                 var dataLayer = layers.layer('data');
116730                 var hasData = dataLayer && dataLayer.hasData();
116731                 var showsData = hasData && dataLayer.enabled();
116732
116733                 var ul = selection
116734                     .selectAll('.layer-list-data')
116735                     .data(dataLayer ? [0] : []);
116736
116737                 // Exit
116738                 ul.exit()
116739                     .remove();
116740
116741                 // Enter
116742                 var ulEnter = ul.enter()
116743                     .append('ul')
116744                     .attr('class', 'layer-list layer-list-data');
116745
116746                 var liEnter = ulEnter
116747                     .append('li')
116748                     .attr('class', 'list-item-data');
116749
116750                 var labelEnter = liEnter
116751                     .append('label')
116752                     .call(uiTooltip()
116753                         .title(_t('map_data.layers.custom.tooltip'))
116754                         .placement('top')
116755                     );
116756
116757                 labelEnter
116758                     .append('input')
116759                     .attr('type', 'checkbox')
116760                     .on('change', function() { toggleLayer('data'); });
116761
116762                 labelEnter
116763                     .append('span')
116764                     .text(_t('map_data.layers.custom.title'));
116765
116766                 liEnter
116767                     .append('button')
116768                     .call(uiTooltip()
116769                         .title(_t('settings.custom_data.tooltip'))
116770                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
116771                     )
116772                     .on('click', editCustom)
116773                     .call(svgIcon('#iD-icon-more'));
116774
116775                 liEnter
116776                     .append('button')
116777                     .call(uiTooltip()
116778                         .title(_t('map_data.layers.custom.zoom'))
116779                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
116780                     )
116781                     .on('click', function() {
116782                         event.preventDefault();
116783                         event.stopPropagation();
116784                         dataLayer.fitZoom();
116785                     })
116786                     .call(svgIcon('#iD-icon-framed-dot'));
116787
116788                 // Update
116789                 ul = ul
116790                     .merge(ulEnter);
116791
116792                 ul.selectAll('.list-item-data')
116793                     .classed('active', showsData)
116794                     .selectAll('label')
116795                     .classed('deemphasize', !hasData)
116796                     .selectAll('input')
116797                     .property('disabled', !hasData)
116798                     .property('checked', showsData);
116799             }
116800
116801             function editCustom() {
116802                 event.preventDefault();
116803                 context.container()
116804                     .call(settingsCustomData);
116805             }
116806
116807             function customChanged(d) {
116808                 var dataLayer = layers.layer('data');
116809
116810                 if (d && d.url) {
116811                     dataLayer.url(d.url);
116812                 } else if (d && d.fileList) {
116813                     dataLayer.fileList(d.fileList);
116814                 }
116815             }
116816
116817
116818             function drawPanelItems(selection) {
116819
116820                 var panelsListEnter = selection.selectAll('.md-extras-list')
116821                     .data([0])
116822                     .enter()
116823                     .append('ul')
116824                     .attr('class', 'layer-list md-extras-list');
116825
116826                 var historyPanelLabelEnter = panelsListEnter
116827                     .append('li')
116828                     .attr('class', 'history-panel-toggle-item')
116829                     .append('label')
116830                     .call(uiTooltip()
116831                         .title(_t('map_data.history_panel.tooltip'))
116832                         .keys([uiCmd('⌘⇧' + _t('info_panels.history.key'))])
116833                         .placement('top')
116834                     );
116835
116836                 historyPanelLabelEnter
116837                     .append('input')
116838                     .attr('type', 'checkbox')
116839                     .on('change', function() {
116840                         event.preventDefault();
116841                         context.ui().info.toggle('history');
116842                     });
116843
116844                 historyPanelLabelEnter
116845                     .append('span')
116846                     .text(_t('map_data.history_panel.title'));
116847
116848                 var measurementPanelLabelEnter = panelsListEnter
116849                     .append('li')
116850                     .attr('class', 'measurement-panel-toggle-item')
116851                     .append('label')
116852                     .call(uiTooltip()
116853                         .title(_t('map_data.measurement_panel.tooltip'))
116854                         .keys([uiCmd('⌘⇧' + _t('info_panels.measurement.key'))])
116855                         .placement('top')
116856                     );
116857
116858                 measurementPanelLabelEnter
116859                     .append('input')
116860                     .attr('type', 'checkbox')
116861                     .on('change', function() {
116862                         event.preventDefault();
116863                         context.ui().info.toggle('measurement');
116864                     });
116865
116866                 measurementPanelLabelEnter
116867                     .append('span')
116868                     .text(_t('map_data.measurement_panel.title'));
116869             }
116870
116871             context.layers().on('change.uiSectionDataLayers', section.reRender);
116872
116873             context.map()
116874                 .on('move.uiSectionDataLayers',
116875                     debounce(function() {
116876                         // Detroit layers may have moved in or out of view
116877                         window.requestIdleCallback(section.reRender);
116878                     }, 1000)
116879                 );
116880
116881             return section;
116882         }
116883
116884         function uiSectionMapFeatures(context) {
116885
116886             var _features = context.features().keys();
116887
116888             var section = uiSection('map-features', context)
116889                 .title(_t('map_data.map_features'))
116890                 .disclosureContent(renderDisclosureContent)
116891                 .expandedByDefault(false);
116892
116893             function renderDisclosureContent(selection) {
116894
116895                 var container = selection.selectAll('.layer-feature-list-container')
116896                     .data([0]);
116897
116898                 var containerEnter = container.enter()
116899                     .append('div')
116900                     .attr('class', 'layer-feature-list-container');
116901
116902                 containerEnter
116903                     .append('ul')
116904                     .attr('class', 'layer-list layer-feature-list');
116905
116906                 var footer = containerEnter
116907                     .append('div')
116908                     .attr('class', 'feature-list-links section-footer');
116909
116910                 footer
116911                     .append('a')
116912                     .attr('class', 'feature-list-link')
116913                     .attr('href', '#')
116914                     .text(_t('issues.enable_all'))
116915                     .on('click', function() {
116916                         context.features().enableAll();
116917                     });
116918
116919                 footer
116920                     .append('a')
116921                     .attr('class', 'feature-list-link')
116922                     .attr('href', '#')
116923                     .text(_t('issues.disable_all'))
116924                     .on('click', function() {
116925                         context.features().disableAll();
116926                     });
116927
116928                 // Update
116929                 container = container
116930                     .merge(containerEnter);
116931
116932                 container.selectAll('.layer-feature-list')
116933                     .call(drawListItems, _features, 'checkbox', 'feature', clickFeature, showsFeature);
116934             }
116935
116936             function drawListItems(selection, data, type, name, change, active) {
116937                 var items = selection.selectAll('li')
116938                     .data(data);
116939
116940                 // Exit
116941                 items.exit()
116942                     .remove();
116943
116944                 // Enter
116945                 var enter = items.enter()
116946                     .append('li')
116947                     .call(uiTooltip()
116948                         .title(function(d) {
116949                             var tip = _t(name + '.' + d + '.tooltip');
116950                             if (autoHiddenFeature(d)) {
116951                                 var msg = showsLayer('osm') ? _t('map_data.autohidden') : _t('map_data.osmhidden');
116952                                 tip += '<div>' + msg + '</div>';
116953                             }
116954                             return tip;
116955                         })
116956                         .placement('top')
116957                     );
116958
116959                 var label = enter
116960                     .append('label');
116961
116962                 label
116963                     .append('input')
116964                     .attr('type', type)
116965                     .attr('name', name)
116966                     .on('change', change);
116967
116968                 label
116969                     .append('span')
116970                     .text(function(d) { return _t(name + '.' + d + '.description'); });
116971
116972                 // Update
116973                 items = items
116974                     .merge(enter);
116975
116976                 items
116977                     .classed('active', active)
116978                     .selectAll('input')
116979                     .property('checked', active)
116980                     .property('indeterminate', autoHiddenFeature);
116981             }
116982
116983             function autoHiddenFeature(d) {
116984                 return context.features().autoHidden(d);
116985             }
116986
116987             function showsFeature(d) {
116988                 return context.features().enabled(d);
116989             }
116990
116991             function clickFeature(d) {
116992                 context.features().toggle(d);
116993             }
116994
116995             function showsLayer(id) {
116996                 var layer = context.layers().layer(id);
116997                 return layer && layer.enabled();
116998             }
116999
117000             // add listeners
117001             context.features()
117002                 .on('change.map_features', section.reRender);
117003
117004             return section;
117005         }
117006
117007         function uiSectionMapStyleOptions(context) {
117008
117009             var section = uiSection('fill-area', context)
117010                 .title(_t('map_data.style_options'))
117011                 .disclosureContent(renderDisclosureContent)
117012                 .expandedByDefault(false);
117013
117014             function renderDisclosureContent(selection) {
117015                 var container = selection.selectAll('.layer-fill-list')
117016                     .data([0]);
117017
117018                 container.enter()
117019                     .append('ul')
117020                     .attr('class', 'layer-list layer-fill-list')
117021                     .merge(container)
117022                     .call(drawListItems, context.map().areaFillOptions, 'radio', 'area_fill', setFill, isActiveFill);
117023
117024                 var container2 = selection.selectAll('.layer-visual-diff-list')
117025                     .data([0]);
117026
117027                 container2.enter()
117028                     .append('ul')
117029                     .attr('class', 'layer-list layer-visual-diff-list')
117030                     .merge(container2)
117031                     .call(drawListItems, ['highlight_edits'], 'checkbox', 'visual_diff', toggleHighlightEdited, function() {
117032                         return context.surface().classed('highlight-edited');
117033                     });
117034             }
117035
117036             function drawListItems(selection, data, type, name, change, active) {
117037                 var items = selection.selectAll('li')
117038                     .data(data);
117039
117040                 // Exit
117041                 items.exit()
117042                     .remove();
117043
117044                 // Enter
117045                 var enter = items.enter()
117046                     .append('li')
117047                     .call(uiTooltip()
117048                         .title(function(d) {
117049                             return _t(name + '.' + d + '.tooltip');
117050                         })
117051                         .keys(function(d) {
117052                             var key = (d === 'wireframe' ? _t('area_fill.wireframe.key') : null);
117053                             if (d === 'highlight_edits') { key = _t('map_data.highlight_edits.key'); }
117054                             return key ? [key] : null;
117055                         })
117056                         .placement('top')
117057                     );
117058
117059                 var label = enter
117060                     .append('label');
117061
117062                 label
117063                     .append('input')
117064                     .attr('type', type)
117065                     .attr('name', name)
117066                     .on('change', change);
117067
117068                 label
117069                     .append('span')
117070                     .text(function(d) { return _t(name + '.' + d + '.description'); });
117071
117072                 // Update
117073                 items = items
117074                     .merge(enter);
117075
117076                 items
117077                     .classed('active', active)
117078                     .selectAll('input')
117079                     .property('checked', active)
117080                     .property('indeterminate', false);
117081             }
117082
117083             function isActiveFill(d) {
117084                 return context.map().activeAreaFill() === d;
117085             }
117086
117087             function toggleHighlightEdited() {
117088                 event.preventDefault();
117089                 context.map().toggleHighlightEdited();
117090             }
117091
117092             function setFill(d) {
117093                 context.map().activeAreaFill(d);
117094             }
117095
117096             context.map()
117097                 .on('changeHighlighting.ui_style, changeAreaFill.ui_style', section.reRender);
117098
117099             return section;
117100         }
117101
117102         function uiSectionPhotoOverlays(context) {
117103
117104             var layers = context.layers();
117105
117106             var section = uiSection('photo-overlays', context)
117107                 .title(_t('photo_overlays.title'))
117108                 .disclosureContent(renderDisclosureContent)
117109                 .expandedByDefault(false);
117110
117111             function renderDisclosureContent(selection) {
117112                 var container = selection.selectAll('.photo-overlay-container')
117113                     .data([0]);
117114
117115                 container.enter()
117116                     .append('div')
117117                     .attr('class', 'photo-overlay-container')
117118                     .merge(container)
117119                     .call(drawPhotoItems)
117120                     .call(drawPhotoTypeItems);
117121             }
117122
117123             function drawPhotoItems(selection) {
117124                 var photoKeys = context.photos().overlayLayerIDs();
117125                 var photoLayers = layers.all().filter(function(obj) { return photoKeys.indexOf(obj.id) !== -1; });
117126                 var data = photoLayers.filter(function(obj) { return obj.layer.supported(); });
117127
117128                 function layerSupported(d) {
117129                     return d.layer && d.layer.supported();
117130                 }
117131                 function layerEnabled(d) {
117132                     return layerSupported(d) && d.layer.enabled();
117133                 }
117134
117135                 var ul = selection
117136                     .selectAll('.layer-list-photos')
117137                     .data([0]);
117138
117139                 ul = ul.enter()
117140                     .append('ul')
117141                     .attr('class', 'layer-list layer-list-photos')
117142                     .merge(ul);
117143
117144                 var li = ul.selectAll('.list-item-photos')
117145                     .data(data);
117146
117147                 li.exit()
117148                     .remove();
117149
117150                 var liEnter = li.enter()
117151                     .append('li')
117152                     .attr('class', function(d) {
117153                         var classes = 'list-item-photos list-item-' + d.id;
117154                         if (d.id === 'mapillary-signs' || d.id === 'mapillary-map-features') {
117155                             classes += ' indented';
117156                         }
117157                         return classes;
117158                     });
117159
117160                 var labelEnter = liEnter
117161                     .append('label')
117162                     .each(function(d) {
117163                         var titleID;
117164                         if (d.id === 'mapillary-signs') { titleID = 'mapillary.signs.tooltip'; }
117165                         else if (d.id === 'mapillary') { titleID = 'mapillary_images.tooltip'; }
117166                         else if (d.id === 'openstreetcam') { titleID = 'openstreetcam_images.tooltip'; }
117167                         else { titleID = d.id.replace(/-/g, '_') + '.tooltip'; }
117168                         select(this)
117169                             .call(uiTooltip()
117170                                 .title(_t(titleID))
117171                                 .placement('top')
117172                             );
117173                     });
117174
117175                 labelEnter
117176                     .append('input')
117177                     .attr('type', 'checkbox')
117178                     .on('change', function(d) { toggleLayer(d.id); });
117179
117180                 labelEnter
117181                     .append('span')
117182                     .text(function(d) {
117183                         var id = d.id;
117184                         if (id === 'mapillary-signs') { id = 'photo_overlays.traffic_signs'; }
117185                         return _t(id.replace(/-/g, '_') + '.title');
117186                     });
117187
117188
117189                 // Update
117190                 li
117191                     .merge(liEnter)
117192                     .classed('active', layerEnabled)
117193                     .selectAll('input')
117194                     .property('checked', layerEnabled);
117195             }
117196
117197             function drawPhotoTypeItems(selection) {
117198                 var data = context.photos().allPhotoTypes();
117199
117200                 function typeEnabled(d) {
117201                     return context.photos().showsPhotoType(d);
117202                 }
117203
117204                 var ul = selection
117205                     .selectAll('.layer-list-photo-types')
117206                     .data(context.photos().shouldFilterByPhotoType() ? [0] : []);
117207
117208                 ul.exit()
117209                     .remove();
117210
117211                 ul = ul.enter()
117212                     .append('ul')
117213                     .attr('class', 'layer-list layer-list-photo-types')
117214                     .merge(ul);
117215
117216                 var li = ul.selectAll('.list-item-photo-types')
117217                     .data(data);
117218
117219                 li.exit()
117220                     .remove();
117221
117222                 var liEnter = li.enter()
117223                     .append('li')
117224                     .attr('class', function(d) {
117225                         return 'list-item-photo-types list-item-' + d;
117226                     });
117227
117228                 var labelEnter = liEnter
117229                     .append('label')
117230                     .each(function(d) {
117231                         select(this)
117232                             .call(uiTooltip()
117233                                 .title(_t('photo_overlays.photo_type.' + d + '.tooltip'))
117234                                 .placement('top')
117235                             );
117236                     });
117237
117238                 labelEnter
117239                     .append('input')
117240                     .attr('type', 'checkbox')
117241                     .on('change', function(d) {
117242                         context.photos().togglePhotoType(d);
117243                     });
117244
117245                 labelEnter
117246                     .append('span')
117247                     .text(function(d) {
117248                         return _t('photo_overlays.photo_type.' + d + '.title');
117249                     });
117250
117251
117252                 // Update
117253                 li
117254                     .merge(liEnter)
117255                     .classed('active', typeEnabled)
117256                     .selectAll('input')
117257                     .property('checked', typeEnabled);
117258             }
117259
117260             function toggleLayer(which) {
117261                 setLayer(which, !showsLayer(which));
117262             }
117263
117264             function showsLayer(which) {
117265                 var layer = layers.layer(which);
117266                 if (layer) {
117267                     return layer.enabled();
117268                 }
117269                 return false;
117270             }
117271
117272             function setLayer(which, enabled) {
117273                 var layer = layers.layer(which);
117274                 if (layer) {
117275                     layer.enabled(enabled);
117276                 }
117277             }
117278
117279             context.layers().on('change.uiSectionPhotoOverlays', section.reRender);
117280             context.photos().on('change.uiSectionPhotoOverlays', section.reRender);
117281
117282             return section;
117283         }
117284
117285         function uiPaneMapData(context) {
117286
117287             var mapDataPane = uiPane('map-data', context)
117288                 .key(_t('map_data.key'))
117289                 .title(_t('map_data.title'))
117290                 .description(_t('map_data.description'))
117291                 .iconName('iD-icon-data')
117292                 .sections([
117293                     uiSectionDataLayers(context),
117294                     uiSectionPhotoOverlays(context),
117295                     uiSectionMapStyleOptions(context),
117296                     uiSectionMapFeatures(context)
117297                 ]);
117298
117299             return mapDataPane;
117300         }
117301
117302         function uiSectionPrivacy(context) {
117303
117304             var section = uiSection('preferences-third-party', context)
117305               .title(_t('preferences.privacy.title'))
117306               .disclosureContent(renderDisclosureContent);
117307
117308             var _showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
117309
117310             function renderDisclosureContent(selection) {
117311               // enter
117312               var privacyOptionsListEnter = selection.selectAll('.privacy-options-list')
117313                 .data([0])
117314                 .enter()
117315                 .append('ul')
117316                 .attr('class', 'layer-list privacy-options-list');
117317
117318               var thirdPartyIconsEnter = privacyOptionsListEnter
117319                 .append('li')
117320                 .attr('class', 'privacy-third-party-icons-item')
117321                 .append('label')
117322                 .call(uiTooltip()
117323                   .title(_t('preferences.privacy.third_party_icons.tooltip'))
117324                   .placement('bottom')
117325                 );
117326
117327               thirdPartyIconsEnter
117328                 .append('input')
117329                 .attr('type', 'checkbox')
117330                 .on('change', function () {
117331                   event.preventDefault();
117332                   _showThirdPartyIcons = (_showThirdPartyIcons === 'true') ? 'false' : 'true';
117333                   corePreferences('preferences.privacy.thirdpartyicons', _showThirdPartyIcons);
117334                   update();
117335                 });
117336
117337               thirdPartyIconsEnter
117338                 .append('span')
117339                 .text(_t('preferences.privacy.third_party_icons.description'));
117340
117341
117342               // Privacy Policy link
117343               selection.selectAll('.privacy-link')
117344                 .data([0])
117345                 .enter()
117346                 .append('div')
117347                 .attr('class', 'privacy-link')
117348                 .append('a')
117349                 .attr('target', '_blank')
117350                 .call(svgIcon('#iD-icon-out-link', 'inline'))
117351                 .attr('href', 'https://github.com/openstreetmap/iD/blob/release/PRIVACY.md')
117352                 .append('span')
117353                 .text(_t('preferences.privacy.privacy_link'));
117354
117355               update();
117356
117357
117358               function update() {
117359                 selection.selectAll('.privacy-third-party-icons-item')
117360                   .classed('active', (_showThirdPartyIcons === 'true'))
117361                   .select('input')
117362                   .property('checked', (_showThirdPartyIcons === 'true'));
117363               }
117364             }
117365
117366             return section;
117367         }
117368
117369         function uiPanePreferences(context) {
117370
117371           var preferencesPane = uiPane('preferences', context)
117372             .key(_t('preferences.key'))
117373             .title(_t('preferences.title'))
117374             .description(_t('preferences.description'))
117375             .iconName('fas-user-cog')
117376             .sections([
117377                 uiSectionPrivacy(context)
117378             ]);
117379
117380           return preferencesPane;
117381         }
117382
117383         function uiInit(context) {
117384             var _initCounter = 0;
117385             var _needWidth = {};
117386
117387             var _lastPointerType;
117388
117389
117390             function render(container) {
117391
117392                 container
117393                     .on('click.ui', function() {
117394                         // we're only concerned with the primary mouse button
117395                         if (event.button !== 0) { return; }
117396
117397                         if (!event.composedPath) { return; }
117398
117399                         // some targets have default click events we don't want to override
117400                         var isOkayTarget = event.composedPath().some(function(node) {
117401                             // we only care about element nodes
117402                             return node.nodeType === 1 &&
117403                                 // clicking <input> focuses it and/or changes a value
117404                                 (node.nodeName === 'INPUT' ||
117405                                 // clicking <label> affects its <input> by default
117406                                 node.nodeName === 'LABEL' ||
117407                                 // clicking <a> opens a hyperlink by default
117408                                 node.nodeName === 'A');
117409                         });
117410                         if (isOkayTarget) { return; }
117411
117412                         // disable double-tap-to-zoom on touchscreens
117413                         event.preventDefault();
117414                     });
117415
117416                 var detected = utilDetect();
117417
117418                 // only WebKit supports gesture events
117419                 if ('GestureEvent' in window &&
117420                     // Listening for gesture events on iOS 13.4+ breaks double-tapping,
117421                     // but we only need to do this on desktop Safari anyway. – #7694
117422                     !detected.isMobileWebKit) {
117423
117424                     // On iOS we disable pinch-to-zoom of the UI via the `touch-action`
117425                     // CSS property, but on desktop Safari we need to manually cancel the
117426                     // default gesture events.
117427                     container.on('gesturestart.ui gesturechange.ui gestureend.ui', function() {
117428                         // disable pinch-to-zoom of the UI via multitouch trackpads on macOS Safari
117429                         event.preventDefault();
117430                     });
117431                 }
117432
117433                 if ('PointerEvent' in window) {
117434                     select(window)
117435                         .on('pointerdown.ui pointerup.ui', function() {
117436                             var pointerType = event.pointerType || 'mouse';
117437                             if (_lastPointerType !== pointerType) {
117438                                 _lastPointerType = pointerType;
117439                                 container
117440                                     .attr('pointer', pointerType);
117441                             }
117442                         }, true);
117443                 } else {
117444                     _lastPointerType = 'mouse';
117445                     container
117446                         .attr('pointer', 'mouse');
117447                 }
117448
117449                 container
117450                     .attr('dir', _mainLocalizer.textDirection());
117451
117452                 // setup fullscreen keybindings (no button shown at this time)
117453                 container
117454                     .call(uiFullScreen(context));
117455
117456                 var map = context.map();
117457                 map.redrawEnable(false);  // don't draw until we've set zoom/lat/long
117458
117459                 map
117460                     .on('hitMinZoom.ui', function() {
117461                         ui.flash.text(_t('cannot_zoom'))();
117462                     });
117463
117464                 container
117465                     .append('svg')
117466                     .attr('id', 'ideditor-defs')
117467                     .call(svgDefs(context));
117468
117469                 container
117470                     .append('div')
117471                     .attr('class', 'sidebar')
117472                     .call(ui.sidebar);
117473
117474                 var content = container
117475                     .append('div')
117476                     .attr('class', 'main-content active');
117477
117478                 // Top toolbar
117479                 content
117480                     .append('div')
117481                     .attr('class', 'top-toolbar-wrap')
117482                     .append('div')
117483                     .attr('class', 'top-toolbar fillD')
117484                     .call(uiTopToolbar(context));
117485
117486                 content
117487                     .append('div')
117488                     .attr('class', 'main-map')
117489                     .attr('dir', 'ltr')
117490                     .call(map);
117491
117492                 content
117493                     .append('div')
117494                     .attr('class', 'spinner')
117495                     .call(uiSpinner(context));
117496
117497                 // Add attribution and footer
117498                 var about = content
117499                     .append('div')
117500                     .attr('class', 'map-footer');
117501
117502                 about
117503                     .append('div')
117504                     .attr('class', 'attribution-wrap')
117505                     .attr('dir', 'ltr')
117506                     .call(uiAttribution(context));
117507
117508                 about
117509                     .append('div')
117510                     .attr('class', 'api-status')
117511                     .call(uiStatus(context));
117512
117513
117514                 var footer = about
117515                     .append('div')
117516                     .attr('class', 'map-footer-bar fillD');
117517
117518                 footer
117519                     .append('div')
117520                     .attr('class', 'flash-wrap footer-hide');
117521
117522                 var footerWrap = footer
117523                     .append('div')
117524                     .attr('class', 'main-footer-wrap footer-show');
117525
117526                 footerWrap
117527                     .append('div')
117528                     .attr('class', 'scale-block')
117529                     .call(uiScale(context));
117530
117531                 var aboutList = footerWrap
117532                     .append('div')
117533                     .attr('class', 'info-block')
117534                     .append('ul')
117535                     .attr('class', 'map-footer-list');
117536
117537                 if (!context.embed()) {
117538                     aboutList
117539                         .call(uiAccount(context));
117540                 }
117541
117542                 aboutList
117543                     .append('li')
117544                     .attr('class', 'version')
117545                     .call(uiVersion(context));
117546
117547                 var issueLinks = aboutList
117548                     .append('li');
117549
117550                 issueLinks
117551                     .append('a')
117552                     .attr('target', '_blank')
117553                     .attr('href', 'https://github.com/openstreetmap/iD/issues')
117554                     .call(svgIcon('#iD-icon-bug', 'light'))
117555                     .call(uiTooltip().title(_t('report_a_bug')).placement('top'));
117556
117557                 issueLinks
117558                     .append('a')
117559                     .attr('target', '_blank')
117560                     .attr('href', 'https://github.com/openstreetmap/iD/blob/develop/CONTRIBUTING.md#translating')
117561                     .call(svgIcon('#iD-icon-translate', 'light'))
117562                     .call(uiTooltip().title(_t('help_translate')).placement('top'));
117563
117564                 aboutList
117565                     .append('li')
117566                     .attr('class', 'feature-warning')
117567                     .attr('tabindex', -1)
117568                     .call(uiFeatureInfo(context));
117569
117570                 aboutList
117571                     .append('li')
117572                     .attr('class', 'issues-info')
117573                     .attr('tabindex', -1)
117574                     .call(uiIssuesInfo(context));
117575
117576                 var apiConnections = context.apiConnections();
117577                 if (apiConnections && apiConnections.length > 1) {
117578                     aboutList
117579                         .append('li')
117580                         .attr('class', 'source-switch')
117581                         .attr('tabindex', -1)
117582                         .call(uiSourceSwitch(context)
117583                             .keys(apiConnections)
117584                         );
117585                 }
117586
117587                 aboutList
117588                     .append('li')
117589                     .attr('class', 'user-list')
117590                     .attr('tabindex', -1)
117591                     .call(uiContributors(context));
117592
117593
117594                 // Setup map dimensions and move map to initial center/zoom.
117595                 // This should happen after .main-content and toolbars exist.
117596                 ui.onResize();
117597                 map.redrawEnable(true);
117598
117599                 ui.hash = behaviorHash(context);
117600                 ui.hash();
117601                 if (!ui.hash.hadHash) {
117602                     map.centerZoom([0, 0], 2);
117603                 }
117604
117605
117606                 var overMap = content
117607                     .append('div')
117608                     .attr('class', 'over-map');
117609
117610                 // Map controls
117611                 var controls = overMap
117612                     .append('div')
117613                     .attr('class', 'map-controls');
117614
117615                 controls
117616                     .append('div')
117617                     .attr('class', 'map-control zoombuttons')
117618                     .call(uiZoom(context));
117619
117620                 controls
117621                     .append('div')
117622                     .attr('class', 'map-control zoom-to-selection-control')
117623                     .call(uiZoomToSelection(context));
117624
117625                 controls
117626                     .append('div')
117627                     .attr('class', 'map-control geolocate-control')
117628                     .call(uiGeolocate(context));
117629
117630                 // Add panes
117631                 // This should happen after map is initialized, as some require surface()
117632                 var panes = overMap
117633                     .append('div')
117634                     .attr('class', 'map-panes');
117635
117636                 var uiPanes = [
117637                     uiPaneBackground(context),
117638                     uiPaneMapData(context),
117639                     uiPaneIssues(context),
117640                     uiPanePreferences(context),
117641                     uiPaneHelp(context)
117642                 ];
117643
117644                 uiPanes.forEach(function(pane) {
117645                     controls
117646                         .append('div')
117647                         .attr('class', 'map-control map-pane-control ' + pane.id + '-control')
117648                         .call(pane.renderToggleButton);
117649
117650                     panes
117651                         .call(pane.renderPane);
117652                 });
117653
117654                 ui.info = uiInfo(context);
117655
117656                 // Add absolutely-positioned elements that sit on top of the map
117657                 // This should happen after the map is ready (center/zoom)
117658                 overMap
117659                     .call(uiMapInMap(context))
117660                     .call(ui.info)
117661                     .call(uiNotice(context));
117662
117663
117664                 overMap
117665                     .append('div')
117666                     .attr('class', 'photoviewer')
117667                     .classed('al', true)       // 'al'=left,  'ar'=right
117668                     .classed('hide', true)
117669                     .call(ui.photoviewer);
117670
117671
117672                 // Bind events
117673                 window.onbeforeunload = function() {
117674                     return context.save();
117675                 };
117676                 window.onunload = function() {
117677                     context.history().unlock();
117678                 };
117679
117680                 select(window)
117681                     .on('resize.editor', ui.onResize);
117682
117683
117684                 var panPixels = 80;
117685                 context.keybinding()
117686                     .on('⌫', function() { event.preventDefault(); })
117687                     .on([_t('sidebar.key'), '`', '²', '@'], ui.sidebar.toggle)   // #5663, #6864 - common QWERTY, AZERTY
117688                     .on('←', pan([panPixels, 0]))
117689                     .on('↑', pan([0, panPixels]))
117690                     .on('→', pan([-panPixels, 0]))
117691                     .on('↓', pan([0, -panPixels]))
117692                     .on(uiCmd('⌘←'), pan([map.dimensions()[0], 0]))
117693                     .on(uiCmd('⌘↑'), pan([0, map.dimensions()[1]]))
117694                     .on(uiCmd('⌘→'), pan([-map.dimensions()[0], 0]))
117695                     .on(uiCmd('⌘↓'), pan([0, -map.dimensions()[1]]))
117696                     .on(uiCmd('⌘' + _t('background.key')), function quickSwitch() {
117697                         if (event) {
117698                             event.stopImmediatePropagation();
117699                             event.preventDefault();
117700                         }
117701                         var previousBackground = context.background().findSource(corePreferences('background-last-used-toggle'));
117702                         if (previousBackground) {
117703                             var currentBackground = context.background().baseLayerSource();
117704                             corePreferences('background-last-used-toggle', currentBackground.id);
117705                             corePreferences('background-last-used', previousBackground.id);
117706                             context.background().baseLayerSource(previousBackground);
117707                         }
117708                     })
117709                     .on(_t('area_fill.wireframe.key'), function toggleWireframe() {
117710                         event.preventDefault();
117711                         event.stopPropagation();
117712                         context.map().toggleWireframe();
117713                     })
117714                     .on(uiCmd('⌥' + _t('area_fill.wireframe.key')), function toggleOsmData() {
117715                         event.preventDefault();
117716                         event.stopPropagation();
117717
117718                         // Don't allow layer changes while drawing - #6584
117719                         var mode = context.mode();
117720                         if (mode && /^draw/.test(mode.id)) { return; }
117721
117722                         var layer = context.layers().layer('osm');
117723                         if (layer) {
117724                             layer.enabled(!layer.enabled());
117725                             if (!layer.enabled()) {
117726                                 context.enter(modeBrowse(context));
117727                             }
117728                         }
117729                     })
117730                     .on(_t('map_data.highlight_edits.key'), function toggleHighlightEdited() {
117731                         event.preventDefault();
117732                         context.map().toggleHighlightEdited();
117733                     });
117734
117735                 context
117736                     .on('enter.editor', function(entered) {
117737                         container
117738                             .classed('mode-' + entered.id, true);
117739                     })
117740                     .on('exit.editor', function(exited) {
117741                         container
117742                             .classed('mode-' + exited.id, false);
117743                     });
117744
117745                 context.enter(modeBrowse(context));
117746
117747                 if (!_initCounter++) {
117748                     if (!ui.hash.startWalkthrough) {
117749                         context.container()
117750                             .call(uiSplash(context))
117751                             .call(uiRestore(context));
117752                     }
117753
117754                     context.container()
117755                         .call(uiShortcuts(context));
117756                 }
117757
117758                 var osm = context.connection();
117759                 var auth = uiLoading(context).message(_t('loading_auth')).blocking(true);
117760
117761                 if (osm && auth) {
117762                     osm
117763                         .on('authLoading.ui', function() {
117764                             context.container()
117765                                 .call(auth);
117766                         })
117767                         .on('authDone.ui', function() {
117768                             auth.close();
117769                         });
117770                 }
117771
117772                 _initCounter++;
117773
117774                 if (ui.hash.startWalkthrough) {
117775                     ui.hash.startWalkthrough = false;
117776                     context.container().call(uiIntro(context));
117777                 }
117778
117779
117780                 function pan(d) {
117781                     return function() {
117782                         if (event.shiftKey) { return; }
117783                         if (context.container().select('.combobox').size()) { return; }
117784                         event.preventDefault();
117785                         context.map().pan(d, 100);
117786                     };
117787                 }
117788
117789             }
117790
117791
117792             var ui = {};
117793
117794             var _loadPromise;
117795             // renders the iD interface into the container node
117796             ui.ensureLoaded = function () {
117797
117798                 if (_loadPromise) { return _loadPromise; }
117799
117800                 return _loadPromise = Promise.all([
117801                         // must have strings and presets before loading the UI
117802                         _mainLocalizer.ensureLoaded(),
117803                         _mainPresetIndex.ensureLoaded()
117804                     ])
117805                     .then(function () {
117806                         if (!context.container().empty()) { render(context.container()); }
117807                     })
117808                     .catch(function (err) { return console.error(err); });  // eslint-disable-line
117809             };
117810
117811
117812             // `ui.restart()` will destroy and rebuild the entire iD interface,
117813             // for example to switch the locale while iD is running.
117814             ui.restart = function() {
117815                 context.keybinding().clear();
117816
117817                 _loadPromise = null;
117818
117819                 context.container().selectAll('*').remove();
117820
117821                 ui.ensureLoaded();
117822             };
117823
117824             ui.lastPointerType = function() {
117825                 return _lastPointerType;
117826             };
117827
117828             ui.flash = uiFlash(context);
117829
117830             ui.sidebar = uiSidebar(context);
117831
117832             ui.photoviewer = uiPhotoviewer(context);
117833
117834             ui.onResize = function(withPan) {
117835                 var map = context.map();
117836
117837                 // Recalc dimensions of map and sidebar.. (`true` = force recalc)
117838                 // This will call `getBoundingClientRect` and trigger reflow,
117839                 //  but the values will be cached for later use.
117840                 var mapDimensions = utilGetDimensions(context.container().select('.main-content'), true);
117841                 utilGetDimensions(context.container().select('.sidebar'), true);
117842
117843                 if (withPan !== undefined) {
117844                     map.redrawEnable(false);
117845                     map.pan(withPan);
117846                     map.redrawEnable(true);
117847                 }
117848                 map.dimensions(mapDimensions);
117849
117850                 ui.photoviewer.onMapResize();
117851
117852                 // check if header or footer have overflowed
117853                 ui.checkOverflow('.top-toolbar');
117854                 ui.checkOverflow('.map-footer-bar');
117855
117856                 // Use outdated code so it works on Explorer
117857                 var resizeWindowEvent = document.createEvent('Event');
117858
117859                 resizeWindowEvent.initEvent('resizeWindow', true, true);
117860
117861                 document.dispatchEvent(resizeWindowEvent);
117862             };
117863
117864
117865             // Call checkOverflow when resizing or whenever the contents change.
117866             ui.checkOverflow = function(selector, reset) {
117867                 if (reset) {
117868                     delete _needWidth[selector];
117869                 }
117870
117871                 var element = select(selector);
117872                 var scrollWidth = element.property('scrollWidth');
117873                 var clientWidth = element.property('clientWidth');
117874                 var needed = _needWidth[selector] || scrollWidth;
117875
117876                 if (scrollWidth > clientWidth) {    // overflow happening
117877                     element.classed('narrow', true);
117878                     if (!_needWidth[selector]) {
117879                         _needWidth[selector] = scrollWidth;
117880                     }
117881
117882                 } else if (scrollWidth >= needed) {
117883                     element.classed('narrow', false);
117884                 }
117885             };
117886
117887             ui.togglePanes = function(showPane) {
117888                 var shownPanes = context.container().selectAll('.map-pane.shown');
117889
117890                 var side = _mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left';
117891
117892                 shownPanes
117893                     .classed('shown', false);
117894
117895                 context.container().selectAll('.map-pane-control button')
117896                     .classed('active', false);
117897
117898                 if (showPane) {
117899                     shownPanes
117900                         .style('display', 'none')
117901                         .style(side, '-500px');
117902
117903                     context.container().selectAll('.' + showPane.attr('pane') + '-control button')
117904                         .classed('active', true);
117905
117906                     showPane
117907                         .classed('shown', true)
117908                         .style('display', 'block');
117909                     if (shownPanes.empty()) {
117910                         showPane
117911                             .style('display', 'block')
117912                             .style(side, '-500px')
117913                             .transition()
117914                             .duration(200)
117915                             .style(side, '0px');
117916                     } else {
117917                         showPane
117918                             .style(side, '0px');
117919                     }
117920                 } else {
117921                     shownPanes
117922                         .style('display', 'block')
117923                         .style(side, '0px')
117924                         .transition()
117925                         .duration(200)
117926                         .style(side, '-500px')
117927                         .on('end', function() {
117928                             select(this).style('display', 'none');
117929                         });
117930                 }
117931             };
117932
117933
117934             var _editMenu = uiEditMenu(context);
117935
117936             ui.editMenu = function() {
117937                 return _editMenu;
117938             };
117939
117940             ui.showEditMenu = function(anchorPoint, triggerType, operations) {
117941
117942                 // remove any displayed menu
117943                 ui.closeEditMenu();
117944
117945                 if (!operations && context.mode().operations) { operations = context.mode().operations(); }
117946                 if (!operations || !operations.length) { return; }
117947
117948                 // disable menu if in wide selection, for example
117949                 if (!context.map().editableDataEnabled()) { return; }
117950
117951                 var surfaceNode = context.surface().node();
117952                 if (surfaceNode.focus) {   // FF doesn't support it
117953                     // focus the surface or else clicking off the menu may not trigger modeBrowse
117954                     surfaceNode.focus();
117955                 }
117956
117957                 operations.forEach(function(operation) {
117958                     if (operation.point) { operation.point(anchorPoint); }
117959                 });
117960
117961                 _editMenu
117962                     .anchorLoc(anchorPoint)
117963                     .triggerType(triggerType)
117964                     .operations(operations);
117965
117966                 // render the menu
117967                 context.map().supersurface.call(_editMenu);
117968             };
117969
117970             ui.closeEditMenu = function() {
117971                 // remove any existing menu no matter how it was added
117972                 context.map().supersurface
117973                     .select('.edit-menu').remove();
117974             };
117975
117976
117977             var _saveLoading = select(null);
117978
117979             context.uploader()
117980                 .on('saveStarted.ui', function() {
117981                     _saveLoading = uiLoading(context)
117982                         .message(_t('save.uploading'))
117983                         .blocking(true);
117984                     context.container().call(_saveLoading);  // block input during upload
117985                 })
117986                 .on('saveEnded.ui', function() {
117987                     _saveLoading.close();
117988                     _saveLoading = select(null);
117989                 });
117990
117991             return ui;
117992         }
117993
117994         function coreContext() {
117995           var this$1 = this;
117996
117997           var dispatch$1 = dispatch('enter', 'exit', 'change');
117998           var context = utilRebind({}, dispatch$1, 'on');
117999           var _deferred = new Set();
118000
118001           context.version = '2.18.4';
118002           context.privacyVersion = '20200407';
118003
118004           // iD will alter the hash so cache the parameters intended to setup the session
118005           context.initialHashParams = window.location.hash ? utilStringQs(window.location.hash) : {};
118006
118007           context.isFirstSession = !corePreferences('sawSplash') && !corePreferences('sawPrivacyVersion');
118008
118009
118010           /* Changeset */
118011           // An osmChangeset object. Not loaded until needed.
118012           context.changeset = null;
118013
118014           var _defaultChangesetComment = context.initialHashParams.comment;
118015           var _defaultChangesetSource = context.initialHashParams.source;
118016           var _defaultChangesetHashtags = context.initialHashParams.hashtags;
118017           context.defaultChangesetComment = function(val) {
118018             if (!arguments.length) { return _defaultChangesetComment; }
118019             _defaultChangesetComment = val;
118020             return context;
118021           };
118022           context.defaultChangesetSource = function(val) {
118023             if (!arguments.length) { return _defaultChangesetSource; }
118024             _defaultChangesetSource = val;
118025             return context;
118026           };
118027           context.defaultChangesetHashtags = function(val) {
118028             if (!arguments.length) { return _defaultChangesetHashtags; }
118029             _defaultChangesetHashtags = val;
118030             return context;
118031           };
118032
118033           /* Document title */
118034           /* (typically shown as the label for the browser window/tab) */
118035
118036           // If true, iD will update the title based on what the user is doing
118037           var _setsDocumentTitle = true;
118038           context.setsDocumentTitle = function(val) {
118039             if (!arguments.length) { return _setsDocumentTitle; }
118040             _setsDocumentTitle = val;
118041             return context;
118042           };
118043           // The part of the title that is always the same
118044           var _documentTitleBase = document.title;
118045           context.documentTitleBase = function(val) {
118046             if (!arguments.length) { return _documentTitleBase; }
118047             _documentTitleBase = val;
118048             return context;
118049           };
118050
118051
118052           /* User interface and keybinding */
118053           var _ui;
118054           context.ui = function () { return _ui; };
118055           context.lastPointerType = function () { return _ui.lastPointerType(); };
118056
118057           var _keybinding = utilKeybinding('context');
118058           context.keybinding = function () { return _keybinding; };
118059           select(document).call(_keybinding);
118060
118061
118062           /* Straight accessors. Avoid using these if you can. */
118063           // Instantiate the connection here because it doesn't require passing in
118064           // `context` and it's needed for pre-init calls like `preauth`
118065           var _connection = services.osm;
118066           var _history;
118067           var _validator;
118068           var _uploader;
118069           context.connection = function () { return _connection; };
118070           context.history = function () { return _history; };
118071           context.validator = function () { return _validator; };
118072           context.uploader = function () { return _uploader; };
118073
118074           /* Connection */
118075           context.preauth = function (options) {
118076             if (_connection) {
118077               _connection.switch(options);
118078             }
118079             return context;
118080           };
118081
118082           /* connection options for source switcher (optional) */
118083           var _apiConnections;
118084           context.apiConnections = function(val) {
118085             if (!arguments.length) { return _apiConnections; }
118086             _apiConnections = val;
118087             return context;
118088           };
118089
118090
118091           // A string or array or locale codes to prefer over the browser's settings
118092           context.locale = function(locale) {
118093             if (!arguments.length) { return _mainLocalizer.localeCode(); }
118094             _mainLocalizer.preferredLocaleCodes(locale);
118095             return context;
118096           };
118097
118098
118099           function afterLoad(cid, callback) {
118100             return function (err, result) {
118101               if (err) {
118102                 // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
118103                 if (err.status === 400 || err.status === 401 || err.status === 403) {
118104                   if (_connection) {
118105                     _connection.logout();
118106                   }
118107                 }
118108                 if (typeof callback === 'function') {
118109                   callback(err);
118110                 }
118111                 return;
118112
118113               } else if (_connection && _connection.getConnectionId() !== cid) {
118114                 if (typeof callback === 'function') {
118115                   callback({ message: 'Connection Switched', status: -1 });
118116                 }
118117                 return;
118118
118119               } else {
118120                 _history.merge(result.data, result.extent);
118121                 if (typeof callback === 'function') {
118122                   callback(err, result);
118123                 }
118124                 return;
118125               }
118126             };
118127           }
118128
118129
118130           context.loadTiles = function (projection, callback) {
118131             var handle = window.requestIdleCallback(function () {
118132               _deferred.delete(handle);
118133               if (_connection && context.editableDataEnabled()) {
118134                 var cid = _connection.getConnectionId();
118135                 _connection.loadTiles(projection, afterLoad(cid, callback));
118136               }
118137             });
118138             _deferred.add(handle);
118139           };
118140
118141           context.loadTileAtLoc = function (loc, callback) {
118142             var handle = window.requestIdleCallback(function () {
118143               _deferred.delete(handle);
118144               if (_connection && context.editableDataEnabled()) {
118145                 var cid = _connection.getConnectionId();
118146                 _connection.loadTileAtLoc(loc, afterLoad(cid, callback));
118147               }
118148             });
118149             _deferred.add(handle);
118150           };
118151
118152           context.loadEntity = function (entityID, callback) {
118153             if (_connection) {
118154               var cid = _connection.getConnectionId();
118155               _connection.loadEntity(entityID, afterLoad(cid, callback));
118156             }
118157           };
118158
118159           context.zoomToEntity = function (entityID, zoomTo) {
118160             if (zoomTo !== false) {
118161               context.loadEntity(entityID, function (err, result) {
118162                 if (err) { return; }
118163                 var entity = result.data.find(function (e) { return e.id === entityID; });
118164                 if (entity) {
118165                   _map.zoomTo(entity);
118166                 }
118167               });
118168             }
118169
118170             _map.on('drawn.zoomToEntity', function () {
118171               if (!context.hasEntity(entityID)) { return; }
118172               _map.on('drawn.zoomToEntity', null);
118173               context.on('enter.zoomToEntity', null);
118174               context.enter(modeSelect(context, [entityID]));
118175             });
118176
118177             context.on('enter.zoomToEntity', function () {
118178               if (_mode.id !== 'browse') {
118179                 _map.on('drawn.zoomToEntity', null);
118180                 context.on('enter.zoomToEntity', null);
118181               }
118182             });
118183           };
118184
118185           var _minEditableZoom = 16;
118186           context.minEditableZoom = function(val) {
118187             if (!arguments.length) { return _minEditableZoom; }
118188             _minEditableZoom = val;
118189             if (_connection) {
118190               _connection.tileZoom(val);
118191             }
118192             return context;
118193           };
118194
118195           // String length limits in Unicode characters, not JavaScript UTF-16 code units
118196           context.maxCharsForTagKey = function () { return 255; };
118197           context.maxCharsForTagValue = function () { return 255; };
118198           context.maxCharsForRelationRole = function () { return 255; };
118199
118200           function cleanOsmString(val, maxChars) {
118201             // be lenient with input
118202             if (val === undefined || val === null) {
118203               val = '';
118204             } else {
118205               val = val.toString();
118206             }
118207
118208             // remove whitespace
118209             val = val.trim();
118210
118211             // use the canonical form of the string
118212             if (val.normalize) { val = val.normalize('NFC'); }
118213
118214             // trim to the number of allowed characters
118215             return utilUnicodeCharsTruncated(val, maxChars);
118216           }
118217           context.cleanTagKey = function (val) { return cleanOsmString(val, context.maxCharsForTagKey()); };
118218           context.cleanTagValue = function (val) { return cleanOsmString(val, context.maxCharsForTagValue()); };
118219           context.cleanRelationRole = function (val) { return cleanOsmString(val, context.maxCharsForRelationRole()); };
118220
118221
118222           /* History */
118223           var _inIntro = false;
118224           context.inIntro = function(val) {
118225             if (!arguments.length) { return _inIntro; }
118226             _inIntro = val;
118227             return context;
118228           };
118229
118230           // Immediately save the user's history to localstorage, if possible
118231           // This is called someteimes, but also on the `window.onbeforeunload` handler
118232           context.save = function () {
118233             // no history save, no message onbeforeunload
118234             if (_inIntro || context.container().select('.modal').size()) { return; }
118235
118236             var canSave;
118237             if (_mode && _mode.id === 'save') {
118238               canSave = false;
118239
118240               // Attempt to prevent user from creating duplicate changes - see #5200
118241               if (services.osm && services.osm.isChangesetInflight()) {
118242                 _history.clearSaved();
118243                 return;
118244               }
118245
118246             } else {
118247               canSave = context.selectedIDs().every(function (id) {
118248                 var entity = context.hasEntity(id);
118249                 return entity && !entity.isDegenerate();
118250               });
118251             }
118252
118253             if (canSave) {
118254               _history.save();
118255             }
118256             if (_history.hasChanges()) {
118257               return _t('save.unsaved_changes');
118258             }
118259           };
118260
118261           // Debounce save, since it's a synchronous localStorage write,
118262           // and history changes can happen frequently (e.g. when dragging).
118263           context.debouncedSave = debounce(context.save, 350);
118264
118265           function withDebouncedSave(fn) {
118266             return function() {
118267               var result = fn.apply(_history, arguments);
118268               context.debouncedSave();
118269               return result;
118270             };
118271           }
118272
118273
118274           /* Graph */
118275           context.hasEntity = function (id) { return _history.graph().hasEntity(id); };
118276           context.entity = function (id) { return _history.graph().entity(id); };
118277
118278
118279           /* Modes */
118280           var _mode;
118281           context.mode = function () { return _mode; };
118282           context.enter = function (newMode) {
118283             if (_mode) {
118284               _mode.exit();
118285               dispatch$1.call('exit', this$1, _mode);
118286             }
118287
118288             _mode = newMode;
118289             _mode.enter();
118290             dispatch$1.call('enter', this$1, _mode);
118291           };
118292
118293           context.selectedIDs = function () { return (_mode && _mode.selectedIDs && _mode.selectedIDs()) || []; };
118294           context.activeID = function () { return _mode && _mode.activeID && _mode.activeID(); };
118295
118296           var _selectedNoteID;
118297           context.selectedNoteID = function(noteID) {
118298             if (!arguments.length) { return _selectedNoteID; }
118299             _selectedNoteID = noteID;
118300             return context;
118301           };
118302
118303           // NOTE: Don't change the name of this until UI v3 is merged
118304           var _selectedErrorID;
118305           context.selectedErrorID = function(errorID) {
118306             if (!arguments.length) { return _selectedErrorID; }
118307             _selectedErrorID = errorID;
118308             return context;
118309           };
118310
118311
118312           /* Behaviors */
118313           context.install = function (behavior) { return context.surface().call(behavior); };
118314           context.uninstall = function (behavior) { return context.surface().call(behavior.off); };
118315
118316
118317           /* Copy/Paste */
118318           var _copyGraph;
118319           context.copyGraph = function () { return _copyGraph; };
118320
118321           var _copyIDs = [];
118322           context.copyIDs = function(val) {
118323             if (!arguments.length) { return _copyIDs; }
118324             _copyIDs = val;
118325             _copyGraph = _history.graph();
118326             return context;
118327           };
118328
118329           var _copyLonLat;
118330           context.copyLonLat = function(val) {
118331             if (!arguments.length) { return _copyLonLat; }
118332             _copyLonLat = val;
118333             return context;
118334           };
118335
118336
118337           /* Background */
118338           var _background;
118339           context.background = function () { return _background; };
118340
118341
118342           /* Features */
118343           var _features;
118344           context.features = function () { return _features; };
118345           context.hasHiddenConnections = function (id) {
118346             var graph = _history.graph();
118347             var entity = graph.entity(id);
118348             return _features.hasHiddenConnections(entity, graph);
118349           };
118350
118351
118352           /* Photos */
118353           var _photos;
118354           context.photos = function () { return _photos; };
118355
118356
118357           /* Map */
118358           var _map;
118359           context.map = function () { return _map; };
118360           context.layers = function () { return _map.layers(); };
118361           context.surface = function () { return _map.surface; };
118362           context.editableDataEnabled = function () { return _map.editableDataEnabled(); };
118363           context.surfaceRect = function () { return _map.surface.node().getBoundingClientRect(); };
118364           context.editable = function () {
118365             // don't allow editing during save
118366             var mode = context.mode();
118367             if (!mode || mode.id === 'save') { return false; }
118368             return _map.editableDataEnabled();
118369           };
118370
118371
118372           /* Debug */
118373           var _debugFlags = {
118374             tile: false,        // tile boundaries
118375             collision: false,   // label collision bounding boxes
118376             imagery: false,     // imagery bounding polygons
118377             target: false,      // touch targets
118378             downloaded: false   // downloaded data from osm
118379           };
118380           context.debugFlags = function () { return _debugFlags; };
118381           context.getDebug = function (flag) { return flag && _debugFlags[flag]; };
118382           context.setDebug = function(flag, val) {
118383             if (arguments.length === 1) { val = true; }
118384             _debugFlags[flag] = val;
118385             dispatch$1.call('change');
118386             return context;
118387           };
118388
118389
118390           /* Container */
118391           var _container = select(null);
118392           context.container = function(val) {
118393             if (!arguments.length) { return _container; }
118394             _container = val;
118395             _container.classed('ideditor', true);
118396             return context;
118397           };
118398           context.containerNode = function(val) {
118399             if (!arguments.length) { return context.container().node(); }
118400             context.container(select(val));
118401             return context;
118402           };
118403
118404           var _embed;
118405           context.embed = function(val) {
118406             if (!arguments.length) { return _embed; }
118407             _embed = val;
118408             return context;
118409           };
118410
118411
118412           /* Assets */
118413           var _assetPath = '';
118414           context.assetPath = function(val) {
118415             if (!arguments.length) { return _assetPath; }
118416             _assetPath = val;
118417             _mainFileFetcher.assetPath(val);
118418             return context;
118419           };
118420
118421           var _assetMap = {};
118422           context.assetMap = function(val) {
118423             if (!arguments.length) { return _assetMap; }
118424             _assetMap = val;
118425             _mainFileFetcher.assetMap(val);
118426             return context;
118427           };
118428
118429           context.asset = function (val) {
118430             if (/^http(s)?:\/\//i.test(val)) { return val; }
118431             var filename = _assetPath + val;
118432             return _assetMap[filename] || filename;
118433           };
118434
118435           context.imagePath = function (val) { return context.asset(("img/" + val)); };
118436
118437
118438           /* reset (aka flush) */
118439           context.reset = context.flush = function () {
118440             context.debouncedSave.cancel();
118441
118442             Array.from(_deferred).forEach(function (handle) {
118443               window.cancelIdleCallback(handle);
118444               _deferred.delete(handle);
118445             });
118446
118447             Object.values(services).forEach(function (service) {
118448               if (service && typeof service.reset === 'function') {
118449                 service.reset(context);
118450               }
118451             });
118452
118453             context.changeset = null;
118454
118455             _validator.reset();
118456             _features.reset();
118457             _history.reset();
118458             _uploader.reset();
118459
118460             // don't leave stale state in the inspector
118461             context.container().select('.inspector-wrap *').remove();
118462
118463             return context;
118464           };
118465
118466
118467           /* Projections */
118468           context.projection = geoRawMercator();
118469           context.curtainProjection = geoRawMercator();
118470
118471
118472           /* Init */
118473           context.init = function () {
118474
118475             instantiateInternal();
118476
118477             initializeDependents();
118478
118479             return context;
118480
118481             // Load variables and properties. No property of `context` should be accessed
118482             // until this is complete since load statuses are indeterminate. The order
118483             // of instantiation shouldn't matter.
118484             function instantiateInternal() {
118485
118486               _history = coreHistory(context);
118487               context.graph = _history.graph;
118488               context.pauseChangeDispatch = _history.pauseChangeDispatch;
118489               context.resumeChangeDispatch = _history.resumeChangeDispatch;
118490               context.perform = withDebouncedSave(_history.perform);
118491               context.replace = withDebouncedSave(_history.replace);
118492               context.pop = withDebouncedSave(_history.pop);
118493               context.overwrite = withDebouncedSave(_history.overwrite);
118494               context.undo = withDebouncedSave(_history.undo);
118495               context.redo = withDebouncedSave(_history.redo);
118496
118497               _validator = coreValidator(context);
118498               _uploader = coreUploader(context);
118499
118500               _background = rendererBackground(context);
118501               _features = rendererFeatures(context);
118502               _map = rendererMap(context);
118503               _photos = rendererPhotos(context);
118504
118505               _ui = uiInit(context);
118506             }
118507
118508             // Set up objects that might need to access properties of `context`. The order
118509             // might matter if dependents make calls to each other. Be wary of async calls.
118510             function initializeDependents() {
118511
118512               if (context.initialHashParams.presets) {
118513                 _mainPresetIndex.addablePresetIDs(new Set(context.initialHashParams.presets.split(',')));
118514               }
118515
118516               if (context.initialHashParams.locale) {
118517                 _mainLocalizer.preferredLocaleCodes(context.initialHashParams.locale);
118518               }
118519
118520               // kick off some async work
118521               _mainLocalizer.ensureLoaded();
118522               _background.ensureLoaded();
118523               _mainPresetIndex.ensureLoaded();
118524
118525               Object.values(services).forEach(function (service) {
118526                 if (service && typeof service.init === 'function') {
118527                   service.init();
118528                 }
118529               });
118530
118531               _map.init();
118532               _validator.init();
118533               _features.init();
118534               _photos.init();
118535
118536               if (services.maprules && context.initialHashParams.maprules) {
118537                 d3_json(context.initialHashParams.maprules)
118538                   .then(function (mapcss) {
118539                     services.maprules.init();
118540                     mapcss.forEach(function (mapcssSelector) { return services.maprules.addRule(mapcssSelector); });
118541                   })
118542                   .catch(function () { /* ignore */ });
118543               }
118544
118545               // if the container isn't available, e.g. when testing, don't load the UI
118546               if (!context.container().empty()) { _ui.ensureLoaded(); }
118547             }
118548           };
118549
118550
118551           return context;
118552         }
118553
118554         // When `debug = true`, we use `Object.freeze` on immutables in iD.
118555         // This is only done in testing because of the performance penalty.
118556         var debug = false;
118557         var d3 = {
118558           customEvent: customEvent,
118559           dispatch:  dispatch,
118560           event:  event,
118561           geoMercator: mercator,
118562           geoProjection: projection,
118563           polygonArea: d3_polygonArea,
118564           polygonCentroid: d3_polygonCentroid,
118565           select: select,
118566           selectAll: selectAll,
118567           timerFlush: timerFlush
118568         };
118569
118570         var iD = /*#__PURE__*/Object.freeze({
118571                 __proto__: null,
118572                 debug: debug,
118573                 d3: d3,
118574                 actionAddEntity: actionAddEntity,
118575                 actionAddMember: actionAddMember,
118576                 actionAddMidpoint: actionAddMidpoint,
118577                 actionAddVertex: actionAddVertex,
118578                 actionChangeMember: actionChangeMember,
118579                 actionChangePreset: actionChangePreset,
118580                 actionChangeTags: actionChangeTags,
118581                 actionCircularize: actionCircularize,
118582                 actionConnect: actionConnect,
118583                 actionCopyEntities: actionCopyEntities,
118584                 actionDeleteMember: actionDeleteMember,
118585                 actionDeleteMultiple: actionDeleteMultiple,
118586                 actionDeleteNode: actionDeleteNode,
118587                 actionDeleteRelation: actionDeleteRelation,
118588                 actionDeleteWay: actionDeleteWay,
118589                 actionDiscardTags: actionDiscardTags,
118590                 actionDisconnect: actionDisconnect,
118591                 actionExtract: actionExtract,
118592                 actionJoin: actionJoin,
118593                 actionMerge: actionMerge,
118594                 actionMergeNodes: actionMergeNodes,
118595                 actionMergePolygon: actionMergePolygon,
118596                 actionMergeRemoteChanges: actionMergeRemoteChanges,
118597                 actionMove: actionMove,
118598                 actionMoveMember: actionMoveMember,
118599                 actionMoveNode: actionMoveNode,
118600                 actionNoop: actionNoop,
118601                 actionOrthogonalize: actionOrthogonalize,
118602                 actionRestrictTurn: actionRestrictTurn,
118603                 actionReverse: actionReverse,
118604                 actionRevert: actionRevert,
118605                 actionRotate: actionRotate,
118606                 actionSplit: actionSplit,
118607                 actionStraightenNodes: actionStraightenNodes,
118608                 actionStraightenWay: actionStraightenWay,
118609                 actionUnrestrictTurn: actionUnrestrictTurn,
118610                 actionReflect: actionReflect,
118611                 actionUpgradeTags: actionUpgradeTags,
118612                 behaviorAddWay: behaviorAddWay,
118613                 behaviorBreathe: behaviorBreathe,
118614                 behaviorDrag: behaviorDrag,
118615                 behaviorDrawWay: behaviorDrawWay,
118616                 behaviorDraw: behaviorDraw,
118617                 behaviorEdit: behaviorEdit,
118618                 behaviorHash: behaviorHash,
118619                 behaviorHover: behaviorHover,
118620                 behaviorLasso: behaviorLasso,
118621                 behaviorOperation: behaviorOperation,
118622                 behaviorPaste: behaviorPaste,
118623                 behaviorSelect: behaviorSelect,
118624                 coreContext: coreContext,
118625                 coreFileFetcher: coreFileFetcher,
118626                 fileFetcher: _mainFileFetcher,
118627                 coreDifference: coreDifference,
118628                 coreGraph: coreGraph,
118629                 coreHistory: coreHistory,
118630                 coreLocalizer: coreLocalizer,
118631                 t: _t,
118632                 localizer: _mainLocalizer,
118633                 prefs: corePreferences,
118634                 coreTree: coreTree,
118635                 coreUploader: coreUploader,
118636                 coreValidator: coreValidator,
118637                 geoExtent: geoExtent,
118638                 geoLatToMeters: geoLatToMeters,
118639                 geoLonToMeters: geoLonToMeters,
118640                 geoMetersToLat: geoMetersToLat,
118641                 geoMetersToLon: geoMetersToLon,
118642                 geoMetersToOffset: geoMetersToOffset,
118643                 geoOffsetToMeters: geoOffsetToMeters,
118644                 geoScaleToZoom: geoScaleToZoom,
118645                 geoSphericalClosestNode: geoSphericalClosestNode,
118646                 geoSphericalDistance: geoSphericalDistance,
118647                 geoZoomToScale: geoZoomToScale,
118648                 geoAngle: geoAngle,
118649                 geoChooseEdge: geoChooseEdge,
118650                 geoEdgeEqual: geoEdgeEqual,
118651                 geoGetSmallestSurroundingRectangle: geoGetSmallestSurroundingRectangle,
118652                 geoHasLineIntersections: geoHasLineIntersections,
118653                 geoHasSelfIntersections: geoHasSelfIntersections,
118654                 geoRotate: geoRotate,
118655                 geoLineIntersection: geoLineIntersection,
118656                 geoPathHasIntersections: geoPathHasIntersections,
118657                 geoPathIntersections: geoPathIntersections,
118658                 geoPathLength: geoPathLength,
118659                 geoPointInPolygon: geoPointInPolygon,
118660                 geoPolygonContainsPolygon: geoPolygonContainsPolygon,
118661                 geoPolygonIntersectsPolygon: geoPolygonIntersectsPolygon,
118662                 geoViewportEdge: geoViewportEdge,
118663                 geoRawMercator: geoRawMercator,
118664                 geoVecAdd: geoVecAdd,
118665                 geoVecAngle: geoVecAngle,
118666                 geoVecCross: geoVecCross,
118667                 geoVecDot: geoVecDot,
118668                 geoVecEqual: geoVecEqual,
118669                 geoVecFloor: geoVecFloor,
118670                 geoVecInterp: geoVecInterp,
118671                 geoVecLength: geoVecLength,
118672                 geoVecLengthSquare: geoVecLengthSquare,
118673                 geoVecNormalize: geoVecNormalize,
118674                 geoVecNormalizedDot: geoVecNormalizedDot,
118675                 geoVecProject: geoVecProject,
118676                 geoVecSubtract: geoVecSubtract,
118677                 geoVecScale: geoVecScale,
118678                 geoOrthoNormalizedDotProduct: geoOrthoNormalizedDotProduct,
118679                 geoOrthoCalcScore: geoOrthoCalcScore,
118680                 geoOrthoMaxOffsetAngle: geoOrthoMaxOffsetAngle,
118681                 geoOrthoCanOrthogonalize: geoOrthoCanOrthogonalize,
118682                 modeAddArea: modeAddArea,
118683                 modeAddLine: modeAddLine,
118684                 modeAddPoint: modeAddPoint,
118685                 modeAddNote: modeAddNote,
118686                 modeBrowse: modeBrowse,
118687                 modeDragNode: modeDragNode,
118688                 modeDragNote: modeDragNote,
118689                 modeDrawArea: modeDrawArea,
118690                 modeDrawLine: modeDrawLine,
118691                 modeMove: modeMove,
118692                 modeRotate: modeRotate,
118693                 modeSave: modeSave,
118694                 modeSelect: modeSelect,
118695                 modeSelectData: modeSelectData,
118696                 modeSelectError: modeSelectError,
118697                 modeSelectNote: modeSelectNote,
118698                 operationCircularize: operationCircularize,
118699                 operationContinue: operationContinue,
118700                 operationCopy: operationCopy,
118701                 operationDelete: operationDelete,
118702                 operationDisconnect: operationDisconnect,
118703                 operationDowngrade: operationDowngrade,
118704                 operationExtract: operationExtract,
118705                 operationMerge: operationMerge,
118706                 operationMove: operationMove,
118707                 operationOrthogonalize: operationOrthogonalize,
118708                 operationPaste: operationPaste,
118709                 operationReflectShort: operationReflectShort,
118710                 operationReflectLong: operationReflectLong,
118711                 operationReverse: operationReverse,
118712                 operationRotate: operationRotate,
118713                 operationSplit: operationSplit,
118714                 operationStraighten: operationStraighten,
118715                 osmChangeset: osmChangeset,
118716                 osmEntity: osmEntity,
118717                 osmNode: osmNode,
118718                 osmNote: osmNote,
118719                 osmRelation: osmRelation,
118720                 osmWay: osmWay,
118721                 QAItem: QAItem,
118722                 osmIntersection: osmIntersection,
118723                 osmTurn: osmTurn,
118724                 osmInferRestriction: osmInferRestriction,
118725                 osmLanes: osmLanes,
118726                 osmOldMultipolygonOuterMemberOfRelation: osmOldMultipolygonOuterMemberOfRelation,
118727                 osmIsOldMultipolygonOuterMember: osmIsOldMultipolygonOuterMember,
118728                 osmOldMultipolygonOuterMember: osmOldMultipolygonOuterMember,
118729                 osmJoinWays: osmJoinWays,
118730                 get osmAreaKeys () { return osmAreaKeys; },
118731                 osmSetAreaKeys: osmSetAreaKeys,
118732                 osmTagSuggestingArea: osmTagSuggestingArea,
118733                 get osmPointTags () { return osmPointTags; },
118734                 osmSetPointTags: osmSetPointTags,
118735                 get osmVertexTags () { return osmVertexTags; },
118736                 osmSetVertexTags: osmSetVertexTags,
118737                 osmNodeGeometriesForTags: osmNodeGeometriesForTags,
118738                 osmOneWayTags: osmOneWayTags,
118739                 osmPavedTags: osmPavedTags,
118740                 osmIsInterestingTag: osmIsInterestingTag,
118741                 osmRoutableHighwayTagValues: osmRoutableHighwayTagValues,
118742                 osmFlowingWaterwayTagValues: osmFlowingWaterwayTagValues,
118743                 osmRailwayTrackTagValues: osmRailwayTrackTagValues,
118744                 presetCategory: presetCategory,
118745                 presetCollection: presetCollection,
118746                 presetField: presetField,
118747                 presetPreset: presetPreset,
118748                 presetManager: _mainPresetIndex,
118749                 presetIndex: presetIndex,
118750                 rendererBackgroundSource: rendererBackgroundSource,
118751                 rendererBackground: rendererBackground,
118752                 rendererFeatures: rendererFeatures,
118753                 rendererMap: rendererMap,
118754                 rendererPhotos: rendererPhotos,
118755                 rendererTileLayer: rendererTileLayer,
118756                 services: services,
118757                 serviceKeepRight: serviceKeepRight,
118758                 serviceImproveOSM: serviceImproveOSM,
118759                 serviceOsmose: serviceOsmose,
118760                 serviceMapillary: serviceMapillary,
118761                 serviceMapRules: serviceMapRules,
118762                 serviceNominatim: serviceNominatim,
118763                 serviceOpenstreetcam: serviceOpenstreetcam,
118764                 serviceOsm: serviceOsm,
118765                 serviceOsmWikibase: serviceOsmWikibase,
118766                 serviceStreetside: serviceStreetside,
118767                 serviceTaginfo: serviceTaginfo,
118768                 serviceVectorTile: serviceVectorTile,
118769                 serviceWikidata: serviceWikidata,
118770                 serviceWikipedia: serviceWikipedia,
118771                 svgAreas: svgAreas,
118772                 svgData: svgData,
118773                 svgDebug: svgDebug,
118774                 svgDefs: svgDefs,
118775                 svgKeepRight: svgKeepRight,
118776                 svgIcon: svgIcon,
118777                 svgGeolocate: svgGeolocate,
118778                 svgLabels: svgLabels,
118779                 svgLayers: svgLayers,
118780                 svgLines: svgLines,
118781                 svgMapillaryImages: svgMapillaryImages,
118782                 svgMapillarySigns: svgMapillarySigns,
118783                 svgMidpoints: svgMidpoints,
118784                 svgNotes: svgNotes,
118785                 svgMarkerSegments: svgMarkerSegments,
118786                 svgOpenstreetcamImages: svgOpenstreetcamImages,
118787                 svgOsm: svgOsm,
118788                 svgPassiveVertex: svgPassiveVertex,
118789                 svgPath: svgPath,
118790                 svgPointTransform: svgPointTransform,
118791                 svgPoints: svgPoints,
118792                 svgRelationMemberTags: svgRelationMemberTags,
118793                 svgSegmentWay: svgSegmentWay,
118794                 svgStreetside: svgStreetside,
118795                 svgTagClasses: svgTagClasses,
118796                 svgTagPattern: svgTagPattern,
118797                 svgTouch: svgTouch,
118798                 svgTurns: svgTurns,
118799                 svgVertices: svgVertices,
118800                 uiFieldDefaultCheck: uiFieldCheck,
118801                 uiFieldOnewayCheck: uiFieldCheck,
118802                 uiFieldCheck: uiFieldCheck,
118803                 uiFieldMultiCombo: uiFieldCombo,
118804                 uiFieldNetworkCombo: uiFieldCombo,
118805                 uiFieldSemiCombo: uiFieldCombo,
118806                 uiFieldTypeCombo: uiFieldCombo,
118807                 uiFieldCombo: uiFieldCombo,
118808                 uiFieldUrl: uiFieldText,
118809                 uiFieldIdentifier: uiFieldText,
118810                 uiFieldNumber: uiFieldText,
118811                 uiFieldTel: uiFieldText,
118812                 uiFieldEmail: uiFieldText,
118813                 uiFieldText: uiFieldText,
118814                 uiFieldAccess: uiFieldAccess,
118815                 uiFieldAddress: uiFieldAddress,
118816                 uiFieldCycleway: uiFieldCycleway,
118817                 uiFieldLanes: uiFieldLanes,
118818                 uiFieldLocalized: uiFieldLocalized,
118819                 uiFieldMaxspeed: uiFieldMaxspeed,
118820                 uiFieldStructureRadio: uiFieldRadio,
118821                 uiFieldRadio: uiFieldRadio,
118822                 uiFieldRestrictions: uiFieldRestrictions,
118823                 uiFieldTextarea: uiFieldTextarea,
118824                 uiFieldWikidata: uiFieldWikidata,
118825                 uiFieldWikipedia: uiFieldWikipedia,
118826                 uiFields: uiFields,
118827                 uiIntro: uiIntro,
118828                 uiPanelBackground: uiPanelBackground,
118829                 uiPanelHistory: uiPanelHistory,
118830                 uiPanelLocation: uiPanelLocation,
118831                 uiPanelMeasurement: uiPanelMeasurement,
118832                 uiInfoPanels: uiInfoPanels,
118833                 uiPaneBackground: uiPaneBackground,
118834                 uiPaneHelp: uiPaneHelp,
118835                 uiPaneIssues: uiPaneIssues,
118836                 uiPaneMapData: uiPaneMapData,
118837                 uiPanePreferences: uiPanePreferences,
118838                 uiSectionBackgroundDisplayOptions: uiSectionBackgroundDisplayOptions,
118839                 uiSectionBackgroundList: uiSectionBackgroundList,
118840                 uiSectionBackgroundOffset: uiSectionBackgroundOffset,
118841                 uiSectionChanges: uiSectionChanges,
118842                 uiSectionDataLayers: uiSectionDataLayers,
118843                 uiSectionEntityIssues: uiSectionEntityIssues,
118844                 uiSectionFeatureType: uiSectionFeatureType,
118845                 uiSectionMapFeatures: uiSectionMapFeatures,
118846                 uiSectionMapStyleOptions: uiSectionMapStyleOptions,
118847                 uiSectionOverlayList: uiSectionOverlayList,
118848                 uiSectionPhotoOverlays: uiSectionPhotoOverlays,
118849                 uiSectionPresetFields: uiSectionPresetFields,
118850                 uiSectionPrivacy: uiSectionPrivacy,
118851                 uiSectionRawMemberEditor: uiSectionRawMemberEditor,
118852                 uiSectionRawMembershipEditor: uiSectionRawMembershipEditor,
118853                 uiSectionRawTagEditor: uiSectionRawTagEditor,
118854                 uiSectionSelectionList: uiSectionSelectionList,
118855                 uiSectionValidationIssues: uiSectionValidationIssues,
118856                 uiSectionValidationOptions: uiSectionValidationOptions,
118857                 uiSectionValidationRules: uiSectionValidationRules,
118858                 uiSectionValidationStatus: uiSectionValidationStatus,
118859                 uiSettingsCustomBackground: uiSettingsCustomBackground,
118860                 uiSettingsCustomData: uiSettingsCustomData,
118861                 uiInit: uiInit,
118862                 uiAccount: uiAccount,
118863                 uiAttribution: uiAttribution,
118864                 uiChangesetEditor: uiChangesetEditor,
118865                 uiCmd: uiCmd,
118866                 uiCombobox: uiCombobox,
118867                 uiCommit: uiCommit,
118868                 uiCommitWarnings: uiCommitWarnings,
118869                 uiConfirm: uiConfirm,
118870                 uiConflicts: uiConflicts,
118871                 uiContributors: uiContributors,
118872                 uiCurtain: uiCurtain,
118873                 uiDataEditor: uiDataEditor,
118874                 uiDataHeader: uiDataHeader,
118875                 uiDisclosure: uiDisclosure,
118876                 uiEditMenu: uiEditMenu,
118877                 uiEntityEditor: uiEntityEditor,
118878                 uiFeatureInfo: uiFeatureInfo,
118879                 uiFeatureList: uiFeatureList,
118880                 uiField: uiField,
118881                 uiFieldHelp: uiFieldHelp,
118882                 uiFlash: uiFlash,
118883                 uiFormFields: uiFormFields,
118884                 uiFullScreen: uiFullScreen,
118885                 uiGeolocate: uiGeolocate,
118886                 uiImproveOsmComments: uiImproveOsmComments,
118887                 uiImproveOsmDetails: uiImproveOsmDetails,
118888                 uiImproveOsmEditor: uiImproveOsmEditor,
118889                 uiImproveOsmHeader: uiImproveOsmHeader,
118890                 uiInfo: uiInfo,
118891                 uiInspector: uiInspector,
118892                 uiIssuesInfo: uiIssuesInfo,
118893                 uiKeepRightDetails: uiKeepRightDetails,
118894                 uiKeepRightEditor: uiKeepRightEditor,
118895                 uiKeepRightHeader: uiKeepRightHeader,
118896                 uiLasso: uiLasso,
118897                 uiLoading: uiLoading,
118898                 uiMapInMap: uiMapInMap,
118899                 uiModal: uiModal,
118900                 uiNotice: uiNotice,
118901                 uiNoteComments: uiNoteComments,
118902                 uiNoteEditor: uiNoteEditor,
118903                 uiNoteHeader: uiNoteHeader,
118904                 uiNoteReport: uiNoteReport,
118905                 uiPopover: uiPopover,
118906                 uiPresetIcon: uiPresetIcon,
118907                 uiPresetList: uiPresetList,
118908                 uiRestore: uiRestore,
118909                 uiScale: uiScale,
118910                 uiSidebar: uiSidebar,
118911                 uiSourceSwitch: uiSourceSwitch,
118912                 uiSpinner: uiSpinner,
118913                 uiSplash: uiSplash,
118914                 uiStatus: uiStatus,
118915                 uiSuccess: uiSuccess,
118916                 uiTagReference: uiTagReference,
118917                 uiToggle: uiToggle,
118918                 uiTooltip: uiTooltip,
118919                 uiVersion: uiVersion,
118920                 uiViewOnOSM: uiViewOnOSM,
118921                 uiViewOnKeepRight: uiViewOnKeepRight,
118922                 uiZoom: uiZoom,
118923                 utilAesEncrypt: utilAesEncrypt,
118924                 utilAesDecrypt: utilAesDecrypt,
118925                 utilArrayChunk: utilArrayChunk,
118926                 utilArrayDifference: utilArrayDifference,
118927                 utilArrayFlatten: utilArrayFlatten,
118928                 utilArrayGroupBy: utilArrayGroupBy,
118929                 utilArrayIdentical: utilArrayIdentical,
118930                 utilArrayIntersection: utilArrayIntersection,
118931                 utilArrayUnion: utilArrayUnion,
118932                 utilArrayUniq: utilArrayUniq,
118933                 utilArrayUniqBy: utilArrayUniqBy,
118934                 utilAsyncMap: utilAsyncMap,
118935                 utilCleanTags: utilCleanTags,
118936                 utilCombinedTags: utilCombinedTags,
118937                 utilDeepMemberSelector: utilDeepMemberSelector,
118938                 utilDetect: utilDetect,
118939                 utilDisplayName: utilDisplayName,
118940                 utilDisplayNameForPath: utilDisplayNameForPath,
118941                 utilDisplayType: utilDisplayType,
118942                 utilDisplayLabel: utilDisplayLabel,
118943                 utilEntityRoot: utilEntityRoot,
118944                 utilEditDistance: utilEditDistance,
118945                 utilEntitySelector: utilEntitySelector,
118946                 utilEntityOrMemberSelector: utilEntityOrMemberSelector,
118947                 utilEntityOrDeepMemberSelector: utilEntityOrDeepMemberSelector,
118948                 utilFastMouse: utilFastMouse,
118949                 utilFunctor: utilFunctor,
118950                 utilGetAllNodes: utilGetAllNodes,
118951                 utilGetSetValue: utilGetSetValue,
118952                 utilHashcode: utilHashcode,
118953                 utilHighlightEntities: utilHighlightEntities,
118954                 utilKeybinding: utilKeybinding,
118955                 utilNoAuto: utilNoAuto,
118956                 utilObjectOmit: utilObjectOmit,
118957                 utilPrefixCSSProperty: utilPrefixCSSProperty,
118958                 utilPrefixDOMProperty: utilPrefixDOMProperty,
118959                 utilQsString: utilQsString,
118960                 utilRebind: utilRebind,
118961                 utilSafeClassName: utilSafeClassName,
118962                 utilSetTransform: utilSetTransform,
118963                 utilSessionMutex: utilSessionMutex,
118964                 utilStringQs: utilStringQs,
118965                 utilTagDiff: utilTagDiff,
118966                 utilTagText: utilTagText,
118967                 utilTiler: utilTiler,
118968                 utilTotalExtent: utilTotalExtent,
118969                 utilTriggerEvent: utilTriggerEvent,
118970                 utilUnicodeCharsCount: utilUnicodeCharsCount,
118971                 utilUnicodeCharsTruncated: utilUnicodeCharsTruncated,
118972                 utilUniqueDomId: utilUniqueDomId,
118973                 utilWrap: utilWrap,
118974                 validationAlmostJunction: validationAlmostJunction,
118975                 validationCloseNodes: validationCloseNodes,
118976                 validationCrossingWays: validationCrossingWays,
118977                 validationDisconnectedWay: validationDisconnectedWay,
118978                 validationFormatting: validationFormatting,
118979                 validationHelpRequest: validationHelpRequest,
118980                 validationImpossibleOneway: validationImpossibleOneway,
118981                 validationIncompatibleSource: validationIncompatibleSource,
118982                 validationMaprules: validationMaprules,
118983                 validationMismatchedGeometry: validationMismatchedGeometry,
118984                 validationMissingRole: validationMissingRole,
118985                 validationMissingTag: validationMissingTag,
118986                 validationOutdatedTags: validationOutdatedTags,
118987                 validationPrivateData: validationPrivateData,
118988                 validationSuspiciousName: validationSuspiciousName,
118989                 validationUnsquareWay: validationUnsquareWay
118990         });
118991
118992         // polyfill requestIdleCallback
118993         window.requestIdleCallback = window.requestIdleCallback ||
118994             function(cb) {
118995                 var start = Date.now();
118996                 return window.requestAnimationFrame(function() {
118997                     cb({
118998                         didTimeout: false,
118999                         timeRemaining: function() {
119000                             return Math.max(0, 50 - (Date.now() - start));
119001                         }
119002                     });
119003                 });
119004             };
119005
119006         window.cancelIdleCallback = window.cancelIdleCallback ||
119007             function(id) {
119008                 window.cancelAnimationFrame(id);
119009             };
119010         window.iD = iD;
119011
119012 }());