]> git.openstreetmap.org Git - rails.git/blobdiff - vendor/assets/iD/iD.js
Update to iD v2.24.1
[rails.git] / vendor / assets / iD / iD.js
index 511fdbe1bed64ab16fff93f4eb23e1f3e7f4399a..d4d7bb70c4863ce14ed0ccfdcf578264051f3d2d 100644 (file)
@@ -9,8 +9,8 @@
     return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
   };
   var __export = (target, all) => {
-    for (var name2 in all)
-      __defProp(target, name2, { get: all[name2], enumerable: true });
+    for (var name in all)
+      __defProp(target, name, { get: all[name], enumerable: true });
   };
   var __copyProps = (to, from, except, desc) => {
     if (from && typeof from === "object" || typeof from === "function") {
     }
     return to;
   };
-  var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
+  var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
+    // If the importer is in node compatibility mode or this is not an ESM
+    // file that has been converted to a CommonJS file using a Babel-
+    // compatible transform (i.e. "__esModule" has not been set), then set
+    // "default" to the CommonJS "module.exports" for node compatibility.
+    isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
+    mod
+  ));
 
   // node_modules/diacritics/index.js
   var require_diacritics = __commonJS({
         [69216, 69247],
         [126064, 126143],
         [126464, 126719]
+        // Mathematical Alphabetic symbols https://www.unicode.org/charts/PDF/U1EE00.pdf
       ];
       function isArabic(char) {
         if (char.length > 1) {
         all: function() {
           return this._all(this.data, []);
         },
-        search: function(bbox) {
+        search: function(bbox2) {
           var node = this.data, result = [], toBBox = this.toBBox;
-          if (!intersects(bbox, node))
+          if (!intersects(bbox2, node))
             return result;
           var nodesToSearch = [], i2, len, child, childBBox;
           while (node) {
             for (i2 = 0, len = node.children.length; i2 < len; i2++) {
               child = node.children[i2];
               childBBox = node.leaf ? toBBox(child) : child;
-              if (intersects(bbox, childBBox)) {
+              if (intersects(bbox2, childBBox)) {
                 if (node.leaf)
                   result.push(child);
-                else if (contains(bbox, childBBox))
+                else if (contains(bbox2, childBBox))
                   this._all(child, result);
                 else
                   nodesToSearch.push(child);
           }
           return result;
         },
-        collides: function(bbox) {
+        collides: function(bbox2) {
           var node = this.data, toBBox = this.toBBox;
-          if (!intersects(bbox, node))
+          if (!intersects(bbox2, node))
             return false;
           var nodesToSearch = [], i2, len, child, childBBox;
           while (node) {
             for (i2 = 0, len = node.children.length; i2 < len; i2++) {
               child = node.children[i2];
               childBBox = node.leaf ? toBBox(child) : child;
-              if (intersects(bbox, childBBox)) {
-                if (node.leaf || contains(bbox, childBBox))
+              if (intersects(bbox2, childBBox)) {
+                if (node.leaf || contains(bbox2, childBBox))
                   return true;
                 nodesToSearch.push(child);
               }
         remove: function(item, equalsFn) {
           if (!item)
             return this;
-          var node = this.data, bbox = this.toBBox(item), path = [], indexes = [], i2, parent, index, goingUp;
+          var node = this.data, bbox2 = this.toBBox(item), path = [], indexes = [], i2, parent, index, goingUp;
           while (node || path.length) {
             if (!node) {
               node = path.pop();
                 return this;
               }
             }
-            if (!goingUp && !node.leaf && contains(node, bbox)) {
+            if (!goingUp && !node.leaf && contains(node, bbox2)) {
               path.push(node);
               indexes.push(i2);
               i2 = 0;
           calcBBox(node, this.toBBox);
           return node;
         },
-        _chooseSubtree: function(bbox, node, level, path) {
+        _chooseSubtree: function(bbox2, node, level, path) {
           var i2, len, child, targetNode, area, enlargement, minArea, minEnlargement;
           while (true) {
             path.push(node);
             for (i2 = 0, len = node.children.length; i2 < len; i2++) {
               child = node.children[i2];
               area = bboxArea(child);
-              enlargement = enlargedArea(bbox, child) - area;
+              enlargement = enlargedArea(bbox2, child) - area;
               if (enlargement < minEnlargement) {
                 minEnlargement = enlargement;
                 minArea = area < minArea ? area : minArea;
           return node;
         },
         _insert: function(item, level, isNode) {
-          var toBBox = this.toBBox, bbox = isNode ? item : toBBox(item), insertPath = [];
-          var node = this._chooseSubtree(bbox, this.data, level, insertPath);
+          var toBBox = this.toBBox, bbox2 = isNode ? item : toBBox(item), insertPath = [];
+          var node = this._chooseSubtree(bbox2, this.data, level, insertPath);
           node.children.push(item);
-          extend2(node, bbox);
+          extend2(node, bbox2);
           while (level >= 0) {
             if (insertPath[level].children.length > this._maxEntries) {
               this._split(insertPath, level);
             } else
               break;
           }
-          this._adjustParentBBoxes(bbox, insertPath, level);
+          this._adjustParentBBoxes(bbox2, insertPath, level);
         },
+        // split overflowed node into two
         _split: function(insertPath, level) {
           var node = insertPath[level], M = node.children.length, m = this._minEntries;
           this._chooseSplitAxis(node, m, M);
           }
           return index;
         },
+        // sorts node children by the best axis for split
         _chooseSplitAxis: function(node, m, M) {
           var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX, compareMinY = node.leaf ? this.compareMinY : compareNodeMinY, xMargin = this._allDistMargin(node, m, M, compareMinX), yMargin = this._allDistMargin(node, m, M, compareMinY);
           if (xMargin < yMargin)
             node.children.sort(compareMinX);
         },
+        // total margin of all possible split distributions where each node is at least m full
         _allDistMargin: function(node, m, M, compare) {
           node.children.sort(compare);
           var toBBox = this.toBBox, leftBBox = distBBox(node, 0, m, toBBox), rightBBox = distBBox(node, M - m, M, toBBox), margin = bboxMargin(leftBBox) + bboxMargin(rightBBox), i2, child;
           }
           return margin;
         },
-        _adjustParentBBoxes: function(bbox, path, level) {
+        _adjustParentBBoxes: function(bbox2, path, level) {
           for (var i2 = level; i2 >= 0; i2--) {
-            extend2(path[i2], bbox);
+            extend2(path[i2], bbox2);
           }
         },
         _condense: function(path) {
           var compareArr = ["return a", " - b", ";"];
           this.compareMinX = new Function("a", "b", compareArr.join(format2[0]));
           this.compareMinY = new Function("a", "b", compareArr.join(format2[1]));
-          this.toBBox = new Function("a", "return {minX: a" + format2[0] + ", minY: a" + format2[1] + ", maxX: a" + format2[2] + ", maxY: a" + format2[3] + "};");
+          this.toBBox = new Function(
+            "a",
+            "return {minX: a" + format2[0] + ", minY: a" + format2[1] + ", maxX: a" + format2[2] + ", maxY: a" + format2[3] + "};"
+          );
         }
       };
       function findItem(item, items, equalsFn) {
       module2.exports = lineclip2;
       lineclip2.polyline = lineclip2;
       lineclip2.polygon = polygonclip2;
-      function lineclip2(points, bbox, result) {
-        var len = points.length, codeA = bitCode2(points[0], bbox), part = [], i2, a, b, codeB, lastCode;
+      function lineclip2(points, bbox2, result) {
+        var len = points.length, codeA = bitCode2(points[0], bbox2), part = [], i2, a, b, codeB, lastCode;
         if (!result)
           result = [];
         for (i2 = 1; i2 < len; i2++) {
           a = points[i2 - 1];
           b = points[i2];
-          codeB = lastCode = bitCode2(b, bbox);
+          codeB = lastCode = bitCode2(b, bbox2);
           while (true) {
             if (!(codeA | codeB)) {
               part.push(a);
             } else if (codeA & codeB) {
               break;
             } else if (codeA) {
-              a = intersect2(a, b, codeA, bbox);
-              codeA = bitCode2(a, bbox);
+              a = intersect2(a, b, codeA, bbox2);
+              codeA = bitCode2(a, bbox2);
             } else {
-              b = intersect2(a, b, codeB, bbox);
-              codeB = bitCode2(b, bbox);
+              b = intersect2(a, b, codeB, bbox2);
+              codeB = bitCode2(b, bbox2);
             }
           }
           codeA = lastCode;
           result.push(part);
         return result;
       }
-      function polygonclip2(points, bbox) {
+      function polygonclip2(points, bbox2) {
         var result, edge, prev, prevInside, i2, p, inside;
         for (edge = 1; edge <= 8; edge *= 2) {
           result = [];
           prev = points[points.length - 1];
-          prevInside = !(bitCode2(prev, bbox) & edge);
+          prevInside = !(bitCode2(prev, bbox2) & edge);
           for (i2 = 0; i2 < points.length; i2++) {
             p = points[i2];
-            inside = !(bitCode2(p, bbox) & edge);
+            inside = !(bitCode2(p, bbox2) & edge);
             if (inside !== prevInside)
-              result.push(intersect2(prev, p, edge, bbox));
+              result.push(intersect2(prev, p, edge, bbox2));
             if (inside)
               result.push(p);
             prev = p;
         }
         return result;
       }
-      function intersect2(a, b, edge, bbox) {
-        return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : null;
-      }
-      function bitCode2(p, bbox) {
+      function intersect2(a, b, edge, bbox2) {
+        return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox2[3] - a[1]) / (b[1] - a[1]), bbox2[3]] : (
+          // top
+          edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox2[1] - a[1]) / (b[1] - a[1]), bbox2[1]] : (
+            // bottom
+            edge & 2 ? [bbox2[2], a[1] + (b[1] - a[1]) * (bbox2[2] - a[0]) / (b[0] - a[0])] : (
+              // right
+              edge & 1 ? [bbox2[0], a[1] + (b[1] - a[1]) * (bbox2[0] - a[0]) / (b[0] - a[0])] : (
+                // left
+                null
+              )
+            )
+          )
+        );
+      }
+      function bitCode2(p, bbox2) {
         var code = 0;
-        if (p[0] < bbox[0])
+        if (p[0] < bbox2[0])
           code |= 1;
-        else if (p[0] > bbox[2])
+        else if (p[0] > bbox2[2])
           code |= 2;
-        if (p[1] < bbox[1])
+        if (p[1] < bbox2[1])
           code |= 4;
-        else if (p[1] > bbox[3])
+        else if (p[1] > bbox2[3])
           code |= 8;
         return code;
       }
           return multi && output.length ? output : null;
         }
         query.tree = tree;
-        query.bbox = function queryBBox(bbox) {
+        query.bbox = function queryBBox(bbox2) {
           var output = [];
           var result = tree.search({
-            minX: bbox[0],
-            minY: bbox[1],
-            maxX: bbox[2],
-            maxY: bbox[3]
+            minX: bbox2[0],
+            minY: bbox2[1],
+            maxX: bbox2[2],
+            maxY: bbox2[3]
           });
           for (var i3 = 0; i3 < result.length; i3++) {
-            if (polygonIntersectsBBox(result[i3].coords, bbox)) {
+            if (polygonIntersectsBBox(result[i3].coords, bbox2)) {
               output.push(result[i3].props);
             }
           }
         };
         return query;
       }
-      function polygonIntersectsBBox(polygon2, bbox) {
+      function polygonIntersectsBBox(polygon2, bbox2) {
         var bboxCenter = [
-          (bbox[0] + bbox[2]) / 2,
-          (bbox[1] + bbox[3]) / 2
+          (bbox2[0] + bbox2[2]) / 2,
+          (bbox2[1] + bbox2[3]) / 2
         ];
         if (insidePolygon(polygon2, bboxCenter))
           return true;
         for (var i2 = 0; i2 < polygon2.length; i2++) {
-          if (lineclip2(polygon2[i2], bbox).length > 0)
+          if (lineclip2(polygon2[i2], bbox2).length > 0)
             return true;
         }
         return false;
         }
         var [lng, lat] = center;
         if (typeof lng !== "number" || typeof lat !== "number") {
-          throw new Error(`ERROR! Longitude and Latitude has to be numbers but where ${typeof lng} and ${typeof lat}`);
+          throw new Error(
+            `ERROR! Longitude and Latitude has to be numbers but where ${typeof lng} and ${typeof lat}`
+          );
         }
         if (lng > 180 || lng < -180) {
           throw new Error(`ERROR! Longitude has to be between -180 and 180 but was ${lng}`);
         var lat1 = toRadians(c1[1]);
         var lon1 = toRadians(c1[0]);
         var dByR = distance / earthRadius2;
-        var lat = Math.asin(Math.sin(lat1) * Math.cos(dByR) + Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing));
-        var lon = lon1 + Math.atan2(Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1), Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat));
+        var lat = Math.asin(
+          Math.sin(lat1) * Math.cos(dByR) + Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing)
+        );
+        var lon = lon1 + Math.atan2(
+          Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1),
+          Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat)
+        );
         return [toDegrees(lon), toDegrees(lat)];
       }
       module2.exports = function circleToPolygon2(center, radius, options2) {
         var start2 = toRadians(bearing);
         var coordinates = [];
         for (var i2 = 0; i2 < n2; ++i2) {
-          coordinates.push(offset(center, radius, earthRadius2, start2 + direction * 2 * Math.PI * -i2 / n2));
+          coordinates.push(
+            offset(
+              center,
+              radius,
+              earthRadius2,
+              start2 + direction * 2 * Math.PI * -i2 / n2
+            )
+          );
         }
         coordinates.push(coordinates[0]);
         return {
             _defineProperties(Constructor, staticProps);
           return Constructor;
         }
-        var Node = function() {
-          function Node2(key, data) {
-            this.next = null;
-            this.key = key;
-            this.data = data;
-            this.left = null;
-            this.right = null;
-          }
-          return Node2;
-        }();
+        var Node = (
+          /** @class */
+          function() {
+            function Node2(key, data) {
+              this.next = null;
+              this.key = key;
+              this.data = data;
+              this.left = null;
+              this.right = null;
+            }
+            return Node2;
+          }()
+        );
         function DEFAULT_COMPARE(a, b) {
           return a > b ? 1 : a < b ? -1 : 0;
         }
               printRow(root3.right, indent2, true, out, printNode);
           }
         }
-        var Tree = function() {
-          function Tree2(comparator) {
-            if (comparator === void 0) {
-              comparator = DEFAULT_COMPARE;
-            }
-            this._root = null;
-            this._size = 0;
-            this._comparator = comparator;
-          }
-          Tree2.prototype.insert = function(key, data) {
-            this._size++;
-            return this._root = insert(key, data, this._root, this._comparator);
-          };
-          Tree2.prototype.add = function(key, data) {
-            var node = new Node(key, data);
-            if (this._root === null) {
-              node.left = node.right = null;
-              this._size++;
-              this._root = node;
-            }
-            var comparator = this._comparator;
-            var t = splay(key, this._root, comparator);
-            var cmp2 = comparator(key, t.key);
-            if (cmp2 === 0)
-              this._root = t;
-            else {
-              if (cmp2 < 0) {
-                node.left = t.left;
-                node.right = t;
-                t.left = null;
-              } else if (cmp2 > 0) {
-                node.right = t.right;
-                node.left = t;
-                t.right = null;
-              }
-              this._size++;
-              this._root = node;
-            }
-            return this._root;
-          };
-          Tree2.prototype.remove = function(key) {
-            this._root = this._remove(key, this._root, this._comparator);
-          };
-          Tree2.prototype._remove = function(i2, t, comparator) {
-            var x;
-            if (t === null)
-              return null;
-            t = splay(i2, t, comparator);
-            var cmp2 = comparator(i2, t.key);
-            if (cmp2 === 0) {
-              if (t.left === null) {
-                x = t.right;
-              } else {
-                x = splay(i2, t.left, comparator);
-                x.right = t.right;
+        var Tree = (
+          /** @class */
+          function() {
+            function Tree2(comparator) {
+              if (comparator === void 0) {
+                comparator = DEFAULT_COMPARE;
               }
-              this._size--;
-              return x;
+              this._root = null;
+              this._size = 0;
+              this._comparator = comparator;
             }
-            return t;
-          };
-          Tree2.prototype.pop = function() {
-            var node = this._root;
-            if (node) {
-              while (node.left) {
-                node = node.left;
+            Tree2.prototype.insert = function(key, data) {
+              this._size++;
+              return this._root = insert(key, data, this._root, this._comparator);
+            };
+            Tree2.prototype.add = function(key, data) {
+              var node = new Node(key, data);
+              if (this._root === null) {
+                node.left = node.right = null;
+                this._size++;
+                this._root = node;
               }
-              this._root = splay(node.key, this._root, this._comparator);
-              this._root = this._remove(node.key, this._root, this._comparator);
-              return {
-                key: node.key,
-                data: node.data
-              };
-            }
-            return null;
-          };
-          Tree2.prototype.findStatic = function(key) {
-            var current = this._root;
-            var compare = this._comparator;
-            while (current) {
-              var cmp2 = compare(key, current.key);
+              var comparator = this._comparator;
+              var t = splay(key, this._root, comparator);
+              var cmp2 = comparator(key, t.key);
               if (cmp2 === 0)
-                return current;
-              else if (cmp2 < 0)
-                current = current.left;
-              else
-                current = current.right;
-            }
-            return null;
-          };
-          Tree2.prototype.find = function(key) {
-            if (this._root) {
-              this._root = splay(key, this._root, this._comparator);
-              if (this._comparator(key, this._root.key) !== 0)
+                this._root = t;
+              else {
+                if (cmp2 < 0) {
+                  node.left = t.left;
+                  node.right = t;
+                  t.left = null;
+                } else if (cmp2 > 0) {
+                  node.right = t.right;
+                  node.left = t;
+                  t.right = null;
+                }
+                this._size++;
+                this._root = node;
+              }
+              return this._root;
+            };
+            Tree2.prototype.remove = function(key) {
+              this._root = this._remove(key, this._root, this._comparator);
+            };
+            Tree2.prototype._remove = function(i2, t, comparator) {
+              var x;
+              if (t === null)
                 return null;
-            }
-            return this._root;
-          };
-          Tree2.prototype.contains = function(key) {
-            var current = this._root;
-            var compare = this._comparator;
-            while (current) {
-              var cmp2 = compare(key, current.key);
-              if (cmp2 === 0)
-                return true;
-              else if (cmp2 < 0)
-                current = current.left;
-              else
-                current = current.right;
-            }
-            return false;
-          };
-          Tree2.prototype.forEach = function(visitor, ctx) {
-            var current = this._root;
-            var Q = [];
-            var done = false;
-            while (!done) {
-              if (current !== null) {
-                Q.push(current);
-                current = current.left;
-              } else {
-                if (Q.length !== 0) {
-                  current = Q.pop();
-                  visitor.call(ctx, current);
-                  current = current.right;
-                } else
-                  done = true;
+              t = splay(i2, t, comparator);
+              var cmp2 = comparator(i2, t.key);
+              if (cmp2 === 0) {
+                if (t.left === null) {
+                  x = t.right;
+                } else {
+                  x = splay(i2, t.left, comparator);
+                  x.right = t.right;
+                }
+                this._size--;
+                return x;
               }
-            }
-            return this;
-          };
-          Tree2.prototype.range = function(low, high, fn, ctx) {
-            var Q = [];
-            var compare = this._comparator;
-            var node = this._root;
-            var cmp2;
-            while (Q.length !== 0 || node) {
+              return t;
+            };
+            Tree2.prototype.pop = function() {
+              var node = this._root;
               if (node) {
-                Q.push(node);
-                node = node.left;
-              } else {
-                node = Q.pop();
-                cmp2 = compare(node.key, high);
-                if (cmp2 > 0) {
-                  break;
-                } else if (compare(node.key, low) >= 0) {
-                  if (fn.call(ctx, node))
-                    return this;
+                while (node.left) {
+                  node = node.left;
                 }
-                node = node.right;
+                this._root = splay(node.key, this._root, this._comparator);
+                this._root = this._remove(node.key, this._root, this._comparator);
+                return {
+                  key: node.key,
+                  data: node.data
+                };
               }
-            }
-            return this;
-          };
-          Tree2.prototype.keys = function() {
-            var keys = [];
-            this.forEach(function(_a) {
-              var key = _a.key;
-              return keys.push(key);
-            });
-            return keys;
-          };
-          Tree2.prototype.values = function() {
-            var values = [];
-            this.forEach(function(_a) {
-              var data = _a.data;
-              return values.push(data);
-            });
-            return values;
-          };
-          Tree2.prototype.min = function() {
-            if (this._root)
-              return this.minNode(this._root).key;
-            return null;
-          };
-          Tree2.prototype.max = function() {
-            if (this._root)
-              return this.maxNode(this._root).key;
-            return null;
-          };
-          Tree2.prototype.minNode = function(t) {
-            if (t === void 0) {
-              t = this._root;
-            }
-            if (t)
-              while (t.left) {
-                t = t.left;
+              return null;
+            };
+            Tree2.prototype.findStatic = function(key) {
+              var current = this._root;
+              var compare = this._comparator;
+              while (current) {
+                var cmp2 = compare(key, current.key);
+                if (cmp2 === 0)
+                  return current;
+                else if (cmp2 < 0)
+                  current = current.left;
+                else
+                  current = current.right;
               }
-            return t;
-          };
-          Tree2.prototype.maxNode = function(t) {
-            if (t === void 0) {
-              t = this._root;
-            }
-            if (t)
-              while (t.right) {
-                t = t.right;
+              return null;
+            };
+            Tree2.prototype.find = function(key) {
+              if (this._root) {
+                this._root = splay(key, this._root, this._comparator);
+                if (this._comparator(key, this._root.key) !== 0)
+                  return null;
               }
-            return t;
-          };
-          Tree2.prototype.at = function(index2) {
-            var current = this._root;
-            var done = false;
-            var i2 = 0;
-            var Q = [];
-            while (!done) {
-              if (current) {
-                Q.push(current);
-                current = current.left;
-              } else {
-                if (Q.length > 0) {
-                  current = Q.pop();
-                  if (i2 === index2)
-                    return current;
-                  i2++;
+              return this._root;
+            };
+            Tree2.prototype.contains = function(key) {
+              var current = this._root;
+              var compare = this._comparator;
+              while (current) {
+                var cmp2 = compare(key, current.key);
+                if (cmp2 === 0)
+                  return true;
+                else if (cmp2 < 0)
+                  current = current.left;
+                else
                   current = current.right;
-                } else
-                  done = true;
               }
-            }
-            return null;
-          };
-          Tree2.prototype.next = function(d) {
-            var root3 = this._root;
-            var successor = null;
-            if (d.right) {
-              successor = d.right;
-              while (successor.left) {
-                successor = successor.left;
+              return false;
+            };
+            Tree2.prototype.forEach = function(visitor, ctx) {
+              var current = this._root;
+              var Q = [];
+              var done = false;
+              while (!done) {
+                if (current !== null) {
+                  Q.push(current);
+                  current = current.left;
+                } else {
+                  if (Q.length !== 0) {
+                    current = Q.pop();
+                    visitor.call(ctx, current);
+                    current = current.right;
+                  } else
+                    done = true;
+                }
+              }
+              return this;
+            };
+            Tree2.prototype.range = function(low, high, fn, ctx) {
+              var Q = [];
+              var compare = this._comparator;
+              var node = this._root;
+              var cmp2;
+              while (Q.length !== 0 || node) {
+                if (node) {
+                  Q.push(node);
+                  node = node.left;
+                } else {
+                  node = Q.pop();
+                  cmp2 = compare(node.key, high);
+                  if (cmp2 > 0) {
+                    break;
+                  } else if (compare(node.key, low) >= 0) {
+                    if (fn.call(ctx, node))
+                      return this;
+                  }
+                  node = node.right;
+                }
+              }
+              return this;
+            };
+            Tree2.prototype.keys = function() {
+              var keys = [];
+              this.forEach(function(_a) {
+                var key = _a.key;
+                return keys.push(key);
+              });
+              return keys;
+            };
+            Tree2.prototype.values = function() {
+              var values = [];
+              this.forEach(function(_a) {
+                var data = _a.data;
+                return values.push(data);
+              });
+              return values;
+            };
+            Tree2.prototype.min = function() {
+              if (this._root)
+                return this.minNode(this._root).key;
+              return null;
+            };
+            Tree2.prototype.max = function() {
+              if (this._root)
+                return this.maxNode(this._root).key;
+              return null;
+            };
+            Tree2.prototype.minNode = function(t) {
+              if (t === void 0) {
+                t = this._root;
+              }
+              if (t)
+                while (t.left) {
+                  t = t.left;
+                }
+              return t;
+            };
+            Tree2.prototype.maxNode = function(t) {
+              if (t === void 0) {
+                t = this._root;
+              }
+              if (t)
+                while (t.right) {
+                  t = t.right;
+                }
+              return t;
+            };
+            Tree2.prototype.at = function(index2) {
+              var current = this._root;
+              var done = false;
+              var i2 = 0;
+              var Q = [];
+              while (!done) {
+                if (current) {
+                  Q.push(current);
+                  current = current.left;
+                } else {
+                  if (Q.length > 0) {
+                    current = Q.pop();
+                    if (i2 === index2)
+                      return current;
+                    i2++;
+                    current = current.right;
+                  } else
+                    done = true;
+                }
+              }
+              return null;
+            };
+            Tree2.prototype.next = function(d) {
+              var root3 = this._root;
+              var successor = null;
+              if (d.right) {
+                successor = d.right;
+                while (successor.left) {
+                  successor = successor.left;
+                }
+                return successor;
+              }
+              var comparator = this._comparator;
+              while (root3) {
+                var cmp2 = comparator(d.key, root3.key);
+                if (cmp2 === 0)
+                  break;
+                else if (cmp2 < 0) {
+                  successor = root3;
+                  root3 = root3.left;
+                } else
+                  root3 = root3.right;
               }
               return successor;
-            }
-            var comparator = this._comparator;
-            while (root3) {
-              var cmp2 = comparator(d.key, root3.key);
-              if (cmp2 === 0)
-                break;
-              else if (cmp2 < 0) {
-                successor = root3;
-                root3 = root3.left;
-              } else
-                root3 = root3.right;
-            }
-            return successor;
-          };
-          Tree2.prototype.prev = function(d) {
-            var root3 = this._root;
-            var predecessor = null;
-            if (d.left !== null) {
-              predecessor = d.left;
-              while (predecessor.right) {
-                predecessor = predecessor.right;
+            };
+            Tree2.prototype.prev = function(d) {
+              var root3 = this._root;
+              var predecessor = null;
+              if (d.left !== null) {
+                predecessor = d.left;
+                while (predecessor.right) {
+                  predecessor = predecessor.right;
+                }
+                return predecessor;
+              }
+              var comparator = this._comparator;
+              while (root3) {
+                var cmp2 = comparator(d.key, root3.key);
+                if (cmp2 === 0)
+                  break;
+                else if (cmp2 < 0)
+                  root3 = root3.left;
+                else {
+                  predecessor = root3;
+                  root3 = root3.right;
+                }
               }
               return predecessor;
-            }
-            var comparator = this._comparator;
-            while (root3) {
-              var cmp2 = comparator(d.key, root3.key);
-              if (cmp2 === 0)
-                break;
-              else if (cmp2 < 0)
-                root3 = root3.left;
-              else {
-                predecessor = root3;
-                root3 = root3.right;
+            };
+            Tree2.prototype.clear = function() {
+              this._root = null;
+              this._size = 0;
+              return this;
+            };
+            Tree2.prototype.toList = function() {
+              return toList(this._root);
+            };
+            Tree2.prototype.load = function(keys, values, presort) {
+              if (values === void 0) {
+                values = [];
               }
-            }
-            return predecessor;
-          };
-          Tree2.prototype.clear = function() {
-            this._root = null;
-            this._size = 0;
-            return this;
-          };
-          Tree2.prototype.toList = function() {
-            return toList(this._root);
-          };
-          Tree2.prototype.load = function(keys, values, presort) {
-            if (values === void 0) {
-              values = [];
-            }
-            if (presort === void 0) {
-              presort = false;
-            }
-            var size = keys.length;
-            var comparator = this._comparator;
-            if (presort)
-              sort(keys, values, 0, size - 1, comparator);
-            if (this._root === null) {
-              this._root = loadRecursive(keys, values, 0, size);
-              this._size = size;
-            } else {
-              var mergedList = mergeLists(this.toList(), createList(keys, values), comparator);
-              size = this._size + size;
-              this._root = sortedListToBST({
-                head: mergedList
-              }, 0, size);
-            }
-            return this;
-          };
-          Tree2.prototype.isEmpty = function() {
-            return this._root === null;
-          };
-          Object.defineProperty(Tree2.prototype, "size", {
-            get: function get3() {
-              return this._size;
-            },
-            enumerable: true,
-            configurable: true
-          });
-          Object.defineProperty(Tree2.prototype, "root", {
-            get: function get3() {
-              return this._root;
-            },
-            enumerable: true,
-            configurable: true
-          });
-          Tree2.prototype.toString = function(printNode) {
-            if (printNode === void 0) {
-              printNode = function printNode2(n2) {
-                return String(n2.key);
-              };
-            }
-            var out = [];
-            printRow(this._root, "", true, function(v) {
-              return out.push(v);
-            }, printNode);
-            return out.join("");
-          };
-          Tree2.prototype.update = function(key, newKey, newData) {
-            var comparator = this._comparator;
-            var _a = split(key, this._root, comparator), left = _a.left, right = _a.right;
-            if (comparator(key, newKey) < 0) {
-              right = insert(newKey, newData, right, comparator);
-            } else {
-              left = insert(newKey, newData, left, comparator);
-            }
-            this._root = merge3(left, right, comparator);
-          };
-          Tree2.prototype.split = function(key) {
-            return split(key, this._root, this._comparator);
-          };
-          return Tree2;
-        }();
+              if (presort === void 0) {
+                presort = false;
+              }
+              var size = keys.length;
+              var comparator = this._comparator;
+              if (presort)
+                sort(keys, values, 0, size - 1, comparator);
+              if (this._root === null) {
+                this._root = loadRecursive(keys, values, 0, size);
+                this._size = size;
+              } else {
+                var mergedList = mergeLists(this.toList(), createList(keys, values), comparator);
+                size = this._size + size;
+                this._root = sortedListToBST({
+                  head: mergedList
+                }, 0, size);
+              }
+              return this;
+            };
+            Tree2.prototype.isEmpty = function() {
+              return this._root === null;
+            };
+            Object.defineProperty(Tree2.prototype, "size", {
+              get: function get4() {
+                return this._size;
+              },
+              enumerable: true,
+              configurable: true
+            });
+            Object.defineProperty(Tree2.prototype, "root", {
+              get: function get4() {
+                return this._root;
+              },
+              enumerable: true,
+              configurable: true
+            });
+            Tree2.prototype.toString = function(printNode) {
+              if (printNode === void 0) {
+                printNode = function printNode2(n2) {
+                  return String(n2.key);
+                };
+              }
+              var out = [];
+              printRow(this._root, "", true, function(v) {
+                return out.push(v);
+              }, printNode);
+              return out.join("");
+            };
+            Tree2.prototype.update = function(key, newKey, newData) {
+              var comparator = this._comparator;
+              var _a = split(key, this._root, comparator), left = _a.left, right = _a.right;
+              if (comparator(key, newKey) < 0) {
+                right = insert(newKey, newData, right, comparator);
+              } else {
+                left = insert(newKey, newData, left, comparator);
+              }
+              this._root = merge3(left, right, comparator);
+            };
+            Tree2.prototype.split = function(key) {
+              return split(key, this._root, this._comparator);
+            };
+            return Tree2;
+          }()
+        );
         function loadRecursive(keys, values, start2, end) {
           var size = end - start2;
           if (size > 0) {
           sort(keys, values, left, j2, compare);
           sort(keys, values, j2 + 1, right, compare);
         }
-        var isInBbox = function isInBbox2(bbox, point) {
-          return bbox.ll.x <= point.x && point.x <= bbox.ur.x && bbox.ll.y <= point.y && point.y <= bbox.ur.y;
+        var isInBbox = function isInBbox2(bbox2, point2) {
+          return bbox2.ll.x <= point2.x && point2.x <= bbox2.ur.x && bbox2.ll.y <= point2.y && point2.y <= bbox2.ur.y;
         };
         var getBboxOverlap = function getBboxOverlap2(b1, b2) {
           if (b2.ur.x < b1.ll.x || b1.ur.x < b2.ll.x || b2.ur.y < b1.ll.y || b1.ur.y < b2.ll.y)
         var SweepEvent = /* @__PURE__ */ function() {
           _createClass(SweepEvent2, null, [{
             key: "compare",
+            // for ordering sweep events in the sweep event queue
             value: function compare(a, b) {
               var ptCmp = SweepEvent2.comparePoints(a.point, b.point);
               if (ptCmp !== 0)
                 return a.isLeft ? 1 : -1;
               return Segment.compare(a.segment, b.segment);
             }
+            // for ordering points in sweep line order
           }, {
             key: "comparePoints",
             value: function comparePoints(aPt, bPt) {
                 return 1;
               return 0;
             }
+            // Warning: 'point' input will be modified and re-used (for performance)
           }]);
-          function SweepEvent2(point, isLeft) {
+          function SweepEvent2(point2, isLeft) {
             _classCallCheck(this, SweepEvent2);
-            if (point.events === void 0)
-              point.events = [this];
+            if (point2.events === void 0)
+              point2.events = [this];
             else
-              point.events.push(this);
-            this.point = point;
+              point2.events.push(this);
+            this.point = point2;
             this.isLeft = isLeft;
           }
           _createClass(SweepEvent2, [{
               }
               this.checkForConsuming();
             }
+            /* Do a pass over our linked events and check to see if any pair
+             * of segments match, and should be consumed. */
           }, {
             key: "checkForConsuming",
             value: function checkForConsuming() {
               }
               return events;
             }
+            /**
+             * Returns a comparator function for sorting linked events that will
+             * favor the event that will give us the smallest left-side angle.
+             * All ring construction starts as low as possible heading to the right,
+             * so by always turning left as sharp as possible we'll get polygons
+             * without uncessary loops & holes.
+             *
+             * The comparator function has a compute cache such that it avoids
+             * re-computing already-computed values.
+             */
           }, {
             key: "getLeftmostComparator",
             value: function getLeftmostComparator(baseEvent) {
         var Segment = /* @__PURE__ */ function() {
           _createClass(Segment2, null, [{
             key: "compare",
+            /* This compare() function is for ordering segments in the sweep
+             * line tree, and does so according to the following criteria:
+             *
+             * Consider the vertical line that lies an infinestimal step to the
+             * right of the right-more of the two left endpoints of the input
+             * segments. Imagine slowly moving a point up from negative infinity
+             * in the increasing y direction. Which of the two segments will that
+             * point intersect first? That segment comes 'before' the other one.
+             *
+             * If neither segment would be intersected by such a line, (if one
+             * or more of the segments are vertical) then the line to be considered
+             * is directly on the right-more of the two left inputs.
+             */
             value: function compare(a, b) {
               var alx = a.leftSE.point.x;
               var blx = b.leftSE.point.x;
                 return 1;
               return 0;
             }
+            /* Warning: a reference to ringWindings input will be stored,
+             *  and possibly will be later modified */
           }]);
           function Segment2(leftSE, rightSE, rings, windings) {
             _classCallCheck(this, Segment2);
           }
           _createClass(Segment2, [{
             key: "replaceRightSE",
+            /* When a segment is split, the rightSE is replaced with a new sweep event */
             value: function replaceRightSE(newRightSE) {
               this.rightSE = newRightSE;
               this.rightSE.segment = this;
             }
           }, {
             key: "bbox",
-            value: function bbox() {
+            value: function bbox2() {
               var y12 = this.leftSE.point.y;
               var y2 = this.rightSE.point.y;
               return {
                 }
               };
             }
+            /* A vector from the left point to the right */
           }, {
             key: "vector",
             value: function vector() {
             value: function isAnEndpoint(pt) {
               return pt.x === this.leftSE.point.x && pt.y === this.leftSE.point.y || pt.x === this.rightSE.point.x && pt.y === this.rightSE.point.y;
             }
+            /* Compare this segment with a point.
+             *
+             * A point P is considered to be colinear to a segment if there
+             * exists a distance D such that if we travel along the segment
+             * from one * endpoint towards the other a distance D, we find
+             * ourselves at point P.
+             *
+             * Return value indicates:
+             *
+             *   1: point lies above the segment (to the left of vertical)
+             *   0: point is colinear to segment
+             *  -1: point lies below the segment (to the right of vertical)
+             */
           }, {
             key: "comparePoint",
-            value: function comparePoint(point) {
-              if (this.isAnEndpoint(point))
+            value: function comparePoint(point2) {
+              if (this.isAnEndpoint(point2))
                 return 0;
               var lPt = this.leftSE.point;
               var rPt = this.rightSE.point;
               var v = this.vector();
               if (lPt.x === rPt.x) {
-                if (point.x === lPt.x)
+                if (point2.x === lPt.x)
                   return 0;
-                return point.x < lPt.x ? 1 : -1;
+                return point2.x < lPt.x ? 1 : -1;
               }
-              var yDist = (point.y - lPt.y) / v.y;
+              var yDist = (point2.y - lPt.y) / v.y;
               var xFromYDist = lPt.x + yDist * v.x;
-              if (point.x === xFromYDist)
+              if (point2.x === xFromYDist)
                 return 0;
-              var xDist = (point.x - lPt.x) / v.x;
+              var xDist = (point2.x - lPt.x) / v.x;
               var yFromXDist = lPt.y + xDist * v.y;
-              if (point.y === yFromXDist)
+              if (point2.y === yFromXDist)
                 return 0;
-              return point.y < yFromXDist ? -1 : 1;
-            }
+              return point2.y < yFromXDist ? -1 : 1;
+            }
+            /**
+             * Given another segment, returns the first non-trivial intersection
+             * between the two segments (in terms of sweep line ordering), if it exists.
+             *
+             * A 'non-trivial' intersection is one that will cause one or both of the
+             * segments to be split(). As such, 'trivial' vs. 'non-trivial' intersection:
+             *
+             *   * endpoint of segA with endpoint of segB --> trivial
+             *   * endpoint of segA with point along segB --> non-trivial
+             *   * endpoint of segB with point along segA --> non-trivial
+             *   * point along segA with point along segB --> non-trivial
+             *
+             * If no non-trivial intersection exists, return null
+             * Else, return null.
+             */
           }, {
             key: "getIntersection",
             value: function getIntersection(other) {
                 return null;
               return rounder.round(pt.x, pt.y);
             }
+            /**
+             * Split the given segment into multiple segments on the given points.
+             *  * Each existing segment will retain its leftSE and a new rightSE will be
+             *    generated for it.
+             *  * A new segment will be generated which will adopt the original segment's
+             *    rightSE, and a new leftSE will be generated for it.
+             *  * If there are more than two points given to split on, new segments
+             *    in the middle will be generated with new leftSE and rightSE's.
+             *  * An array of the newly generated SweepEvents will be returned.
+             *
+             * Warning: input array of points is modified
+             */
           }, {
             key: "split",
-            value: function split2(point) {
+            value: function split2(point2) {
               var newEvents = [];
-              var alreadyLinked = point.events !== void 0;
-              var newLeftSE = new SweepEvent(point, true);
-              var newRightSE = new SweepEvent(point, false);
+              var alreadyLinked = point2.events !== void 0;
+              var newLeftSE = new SweepEvent(point2, true);
+              var newRightSE = new SweepEvent(point2, false);
               var oldRightSE = this.rightSE;
               this.replaceRightSE(newRightSE);
               newEvents.push(newRightSE);
               }
               return newEvents;
             }
+            /* Swap which event is left and right */
           }, {
             key: "swapEvents",
             value: function swapEvents() {
                 this.windings[i2] *= -1;
               }
             }
+            /* Consume another segment. We take their rings under our wing
+             * and mark them as consumed. Use for perfectly overlapping segments */
           }, {
             key: "consume",
             value: function consume(other) {
               consumee.leftSE.consumedBy = consumer.leftSE;
               consumee.rightSE.consumedBy = consumer.rightSE;
             }
+            /* The first segment previous segment chain that is in the result */
           }, {
             key: "prevInResult",
             value: function prevInResult() {
               }
               return this._afterState;
             }
+            /* Is this segment part of the final result? */
           }, {
             key: "isInResult",
             value: function isInResult() {
               if (typeof geomRing[i2][0] !== "number" || typeof geomRing[i2][1] !== "number") {
                 throw new Error("Input geometry is not a valid Polygon or MultiPolygon");
               }
-              var point = rounder.round(geomRing[i2][0], geomRing[i2][1]);
-              if (point.x === prevPoint.x && point.y === prevPoint.y)
+              var point2 = rounder.round(geomRing[i2][0], geomRing[i2][1]);
+              if (point2.x === prevPoint.x && point2.y === prevPoint.y)
                 continue;
-              this.segments.push(Segment.fromRing(prevPoint, point, this));
-              if (point.x < this.bbox.ll.x)
-                this.bbox.ll.x = point.x;
-              if (point.y < this.bbox.ll.y)
-                this.bbox.ll.y = point.y;
-              if (point.x > this.bbox.ur.x)
-                this.bbox.ur.x = point.x;
-              if (point.y > this.bbox.ur.y)
-                this.bbox.ur.y = point.y;
-              prevPoint = point;
+              this.segments.push(Segment.fromRing(prevPoint, point2, this));
+              if (point2.x < this.bbox.ll.x)
+                this.bbox.ll.x = point2.x;
+              if (point2.y < this.bbox.ll.y)
+                this.bbox.ll.y = point2.y;
+              if (point2.x > this.bbox.ur.x)
+                this.bbox.ur.x = point2.x;
+              if (point2.y > this.bbox.ur.y)
+                this.bbox.ur.y = point2.y;
+              prevPoint = point2;
             }
             if (firstPoint.x !== prevPoint.x || firstPoint.y !== prevPoint.y) {
               this.segments.push(Segment.fromRing(prevPoint, firstPoint, this));
         var RingOut = /* @__PURE__ */ function() {
           _createClass(RingOut2, null, [{
             key: "factory",
+            /* Given the segments from the sweep line pass, compute & return a series
+             * of closed rings from all the segments marked to be part of the result */
             value: function factory(allSegments) {
               var ringsOut = [];
               for (var i2 = 0, iMax = allSegments.length; i2 < iMax; i2++) {
               }
               return this._enclosingRing;
             }
+            /* Returns the ring that encloses this one, if any */
           }, {
             key: "_calcEnclosingRing",
             value: function _calcEnclosingRing() {
               }
               return newEvents;
             }
+            /* Safely split a segment that is currently in the datastructures
+             * IE - a segment other than the one that is currently being processed. */
           }, {
             key: "_splitSafely",
             value: function _splitSafely(seg, pt) {
           }
           _createClass(Operation2, [{
             key: "run",
-            value: function run(type3, geom, moreGeoms) {
-              operation.type = type3;
+            value: function run(type2, geom, moreGeoms) {
+              operation.type = type2;
               rounder.reset();
               var multipolys = [new MultiPolyIn(geom, true)];
               for (var i2 = 0, iMax = moreGeoms.length; i2 < iMax; i2++) {
     "node_modules/geojson-precision/index.js"(exports2, module2) {
       (function() {
         function parse(t, coordinatePrecision, extrasPrecision) {
-          function point(p) {
+          function point2(p) {
             return p.map(function(e, index) {
               if (index < 2) {
                 return 1 * e.toFixed(coordinatePrecision);
             });
           }
           function multi(l) {
-            return l.map(point);
+            return l.map(point2);
           }
           function poly(p) {
             return p.map(multi);
             }
             switch (obj.type) {
               case "Point":
-                obj.coordinates = point(obj.coordinates);
+                obj.coordinates = point2(obj.coordinates);
                 return obj;
               case "LineString":
               case "MultiPoint":
   // node_modules/@aitodotai/json-stringify-pretty-compact/index.js
   var require_json_stringify_pretty_compact = __commonJS({
     "node_modules/@aitodotai/json-stringify-pretty-compact/index.js"(exports2, module2) {
-      function isObject2(obj) {
+      function isObject3(obj) {
         return typeof obj === "object" && obj !== null;
       }
       function forEach(obj, cb) {
         if (Array.isArray(obj)) {
           obj.forEach(cb);
-        } else if (isObject2(obj)) {
+        } else if (isObject3(obj)) {
           Object.keys(obj).forEach(function(key) {
             var val = obj[key];
             cb(val, key);
       }
       function getTreeDepth(obj) {
         var depth = 0;
-        if (Array.isArray(obj) || isObject2(obj)) {
+        if (Array.isArray(obj) || isObject3(obj)) {
           forEach(obj, function(val) {
-            if (Array.isArray(val) || isObject2(val)) {
+            if (Array.isArray(val) || isObject3(val)) {
               var tmpDepth = getTreeDepth(val);
               if (tmpDepth > depth) {
                 depth = tmpDepth;
       }
       function stringify3(obj, options2) {
         options2 = options2 || {};
-        var indent2 = JSON.stringify([1], null, get3(options2, "indent", 2)).slice(2, -3);
-        var addMargin = get3(options2, "margins", false);
-        var addArrayMargin = get3(options2, "arrayMargins", false);
-        var addObjectMargin = get3(options2, "objectMargins", false);
-        var maxLength = indent2 === "" ? Infinity : get3(options2, "maxLength", 80);
-        var maxNesting = get3(options2, "maxNesting", Infinity);
+        var indent2 = JSON.stringify([1], null, get4(options2, "indent", 2)).slice(2, -3);
+        var addMargin = get4(options2, "margins", false);
+        var addArrayMargin = get4(options2, "arrayMargins", false);
+        var addObjectMargin = get4(options2, "objectMargins", false);
+        var maxLength = indent2 === "" ? Infinity : get4(options2, "maxLength", 80);
+        var maxNesting = get4(options2, "maxNesting", Infinity);
         return function _stringify(obj2, currentIndent, reserved) {
           if (obj2 && typeof obj2.toJSON === "function") {
             obj2 = obj2.toJSON();
               return prettified;
             }
           }
-          if (isObject2(obj2)) {
+          if (isObject3(obj2)) {
             var nextIndent = currentIndent + indent2;
             var items = [];
             var delimiters;
             };
             if (Array.isArray(obj2)) {
               for (var index = 0; index < obj2.length; index++) {
-                items.push(_stringify(obj2[index], nextIndent, comma(obj2, index)) || "null");
+                items.push(
+                  _stringify(obj2[index], nextIndent, comma(obj2, index)) || "null"
+                );
               }
               delimiters = "[]";
             } else {
               Object.keys(obj2).forEach(function(key, index2, array2) {
                 var keyPart = JSON.stringify(key) + ": ";
-                var value = _stringify(obj2[key], nextIndent, keyPart.length + comma(array2, index2));
+                var value = _stringify(
+                  obj2[key],
+                  nextIndent,
+                  keyPart.length + comma(array2, index2)
+                );
                 if (value !== void 0) {
                   items.push(keyPart + value);
                 }
           return string2 ? match : tokens[match];
         });
       }
-      function get3(options2, name2, defaultValue) {
-        return name2 in options2 ? options2[name2] : defaultValue;
+      function get4(options2, name, defaultValue) {
+        return name in options2 ? options2[name] : defaultValue;
       }
       module2.exports = stringify3;
     }
         function convertToInt32(bytes) {
           var result = [];
           for (var i2 = 0; i2 < bytes.length; i2 += 4) {
-            result.push(bytes[i2] << 24 | bytes[i2 + 1] << 16 | bytes[i2 + 2] << 8 | bytes[i2 + 3]);
+            result.push(
+              bytes[i2] << 24 | bytes[i2 + 1] << 16 | bytes[i2 + 2] << 8 | bytes[i2 + 3]
+            );
           }
           return result;
         }
         cloneableTags[argsTag] = cloneableTags[arrayTag] = cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = cloneableTags[boolTag] = cloneableTags[dateTag] = cloneableTags[float32Tag] = cloneableTags[float64Tag] = cloneableTags[int8Tag] = cloneableTags[int16Tag] = cloneableTags[int32Tag] = cloneableTags[mapTag] = cloneableTags[numberTag] = cloneableTags[objectTag] = cloneableTags[regexpTag] = cloneableTags[setTag] = cloneableTags[stringTag] = cloneableTags[symbolTag2] = cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
         cloneableTags[errorTag] = cloneableTags[funcTag] = cloneableTags[weakMapTag] = false;
         var deburredLetters = {
+          // Latin-1 Supplement block.
           "\xC0": "A",
           "\xC1": "A",
           "\xC2": "A",
           "\xDE": "Th",
           "\xFE": "th",
           "\xDF": "ss",
+          // Latin Extended-A block.
           "\u0100": "A",
           "\u0102": "A",
           "\u0104": "A",
           var nativeObjectToString3 = objectProto3.toString;
           var objectCtorString = funcToString.call(Object2);
           var oldDash = root3._;
-          var reIsNative = RegExp2("^" + funcToString.call(hasOwnProperty2).replace(reRegExpChar, "\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, "$1.*?") + "$");
+          var reIsNative = RegExp2(
+            "^" + funcToString.call(hasOwnProperty2).replace(reRegExpChar, "\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, "$1.*?") + "$"
+          );
           var Buffer2 = moduleExports ? context.Buffer : undefined2, Symbol3 = context.Symbol, Uint8Array2 = context.Uint8Array, allocUnsafe = Buffer2 ? Buffer2.allocUnsafe : undefined2, getPrototype = overArg(Object2.getPrototypeOf, Object2), objectCreate = Object2.create, propertyIsEnumerable = objectProto3.propertyIsEnumerable, splice = arrayProto.splice, spreadableSymbol = Symbol3 ? Symbol3.isConcatSpreadable : undefined2, symIterator = Symbol3 ? Symbol3.iterator : undefined2, symToStringTag3 = Symbol3 ? Symbol3.toStringTag : undefined2;
           var defineProperty = function() {
             try {
             function object() {
             }
             return function(proto) {
-              if (!isObject2(proto)) {
+              if (!isObject3(proto)) {
                 return {};
               }
               if (objectCreate) {
             this.__values__ = undefined2;
           }
           lodash.templateSettings = {
+            /**
+             * Used to detect `data` property values to be HTML-escaped.
+             *
+             * @memberOf _.templateSettings
+             * @type {RegExp}
+             */
             "escape": reEscape,
+            /**
+             * Used to detect code to be evaluated.
+             *
+             * @memberOf _.templateSettings
+             * @type {RegExp}
+             */
             "evaluate": reEvaluate,
+            /**
+             * Used to detect `data` property values to inject.
+             *
+             * @memberOf _.templateSettings
+             * @type {RegExp}
+             */
             "interpolate": reInterpolate,
+            /**
+             * Used to reference the data object in the template text.
+             *
+             * @memberOf _.templateSettings
+             * @type {string}
+             */
             "variable": "",
+            /**
+             * Used to import variables into the compiled template.
+             *
+             * @memberOf _.templateSettings
+             * @type {Object}
+             */
             "imports": {
+              /**
+               * A reference to the `lodash` function.
+               *
+               * @memberOf _.templateSettings.imports
+               * @type {Function}
+               */
               "_": lodash
             }
           };
                 index += dir;
                 var iterIndex = -1, value = array2[index];
                 while (++iterIndex < iterLength) {
-                  var data = iteratees[iterIndex], iteratee2 = data.iteratee, type3 = data.type, computed = iteratee2(value);
-                  if (type3 == LAZY_MAP_FLAG) {
+                  var data = iteratees[iterIndex], iteratee2 = data.iteratee, type2 = data.type, computed = iteratee2(value);
+                  if (type2 == LAZY_MAP_FLAG) {
                     value = computed;
                   } else if (!computed) {
-                    if (type3 == LAZY_FILTER_FLAG) {
+                    if (type2 == LAZY_FILTER_FLAG) {
                       continue outer;
                     } else {
                       break outer;
           function arrayLikeKeys(value, inherited) {
             var isArr = isArray2(value), isArg = !isArr && isArguments(value), isBuff = !isArr && !isArg && isBuffer(value), isType = !isArr && !isArg && !isBuff && isTypedArray(value), skipIndexes = isArr || isArg || isBuff || isType, result2 = skipIndexes ? baseTimes(value.length, String2) : [], length = result2.length;
             for (var key in value) {
-              if ((inherited || hasOwnProperty2.call(value, key)) && !(skipIndexes && (key == "length" || isBuff && (key == "offset" || key == "parent") || isType && (key == "buffer" || key == "byteLength" || key == "byteOffset") || isIndex(key, length)))) {
+              if ((inherited || hasOwnProperty2.call(value, key)) && !(skipIndexes && // Safari 9 has enumerable `arguments.length` in strict mode.
+              (key == "length" || // Node.js 0.10 has enumerable non-index properties on buffers.
+              isBuff && (key == "offset" || key == "parent") || // PhantomJS 2 has enumerable non-index properties on typed arrays.
+              isType && (key == "buffer" || key == "byteLength" || key == "byteOffset") || // Skip index properties.
+              isIndex(key, length)))) {
                 result2.push(key);
               }
             }
           function baseAt(object, paths) {
             var index = -1, length = paths.length, result2 = Array2(length), skip = object == null;
             while (++index < length) {
-              result2[index] = skip ? undefined2 : get3(object, paths[index]);
+              result2[index] = skip ? undefined2 : get4(object, paths[index]);
             }
             return result2;
           }
             if (result2 !== undefined2) {
               return result2;
             }
-            if (!isObject2(value)) {
+            if (!isObject3(value)) {
               return value;
             }
             var isArr = isArray2(value);
             return true;
           }
           function baseIsNative(value) {
-            if (!isObject2(value) || isMasked(value)) {
+            if (!isObject3(value) || isMasked(value)) {
               return false;
             }
             var pattern = isFunction(value) ? reIsNative : reIsHostCtor;
             return result2;
           }
           function baseKeysIn(object) {
-            if (!isObject2(object)) {
+            if (!isObject3(object)) {
               return nativeKeysIn(object);
             }
             var isProto = isPrototype(object), result2 = [];
               return matchesStrictComparable(toKey(path), srcValue);
             }
             return function(object) {
-              var objValue = get3(object, path);
+              var objValue = get4(object, path);
               return objValue === undefined2 && objValue === srcValue ? hasIn(object, path) : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG);
             };
           }
             }
             baseFor(source, function(srcValue, key) {
               stack || (stack = new Stack());
-              if (isObject2(srcValue)) {
+              if (isObject3(srcValue)) {
                 baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack);
               } else {
                 var newValue = customizer ? customizer(safeGet(object, key), srcValue, key + "", object, source, stack) : undefined2;
                 newValue = objValue;
                 if (isArguments(objValue)) {
                   newValue = toPlainObject(objValue);
-                } else if (!isObject2(objValue) || isFunction(objValue)) {
+                } else if (!isObject3(objValue) || isFunction(objValue)) {
                   newValue = initCloneObject(srcValue);
                 }
               } else {
             return shuffleSelf(array2, baseClamp(n2, 0, array2.length));
           }
           function baseSet(object, path, value, customizer) {
-            if (!isObject2(object)) {
+            if (!isObject3(object)) {
               return object;
             }
             path = castPath(path, object);
                 var objValue = nested[key];
                 newValue = customizer ? customizer(objValue, key, nested) : undefined2;
                 if (newValue === undefined2) {
-                  newValue = isObject2(objValue) ? objValue : isIndex(path[index + 1]) ? [] : {};
+                  newValue = isObject3(objValue) ? objValue : isIndex(path[index + 1]) ? [] : {};
                 }
               }
               assignValue(nested, key, newValue);
                   return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
               }
               var thisBinding = baseCreate(Ctor.prototype), result2 = Ctor.apply(thisBinding, args);
-              return isObject2(result2) ? result2 : thisBinding;
+              return isObject3(result2) ? result2 : thisBinding;
             };
           }
           function createCurry(func, bitmask, arity) {
               var holders = length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder ? [] : replaceHolders(args, placeholder);
               length -= holders.length;
               if (length < arity) {
-                return createRecurry(func, bitmask, createHybrid, wrapper.placeholder, undefined2, args, holders, undefined2, undefined2, arity - length);
+                return createRecurry(
+                  func,
+                  bitmask,
+                  createHybrid,
+                  wrapper.placeholder,
+                  undefined2,
+                  args,
+                  holders,
+                  undefined2,
+                  undefined2,
+                  arity - length
+                );
               }
               var fn = this && this !== root3 && this instanceof wrapper ? Ctor : func;
               return apply(fn, this, args);
               length -= holdersCount;
               if (isCurried && length < arity) {
                 var newHolders = replaceHolders(args, placeholder);
-                return createRecurry(func, bitmask, createHybrid, wrapper.placeholder, thisArg, args, newHolders, argPos, ary2, arity - length);
+                return createRecurry(
+                  func,
+                  bitmask,
+                  createHybrid,
+                  wrapper.placeholder,
+                  thisArg,
+                  args,
+                  newHolders,
+                  argPos,
+                  ary2,
+                  arity - length
+                );
               }
               var thisBinding = isBind ? thisArg : this, fn = isBindKey ? thisBinding[func] : func;
               length = args.length;
             return objValue;
           }
           function customDefaultsMerge(objValue, srcValue, key, object, source, stack) {
-            if (isObject2(objValue) && isObject2(srcValue)) {
+            if (isObject3(objValue) && isObject3(srcValue)) {
               stack.set(srcValue, objValue);
               baseMerge(objValue, srcValue, undefined2, customDefaultsMerge, stack);
               stack["delete"](srcValue);
             return isArray2(value) || isArguments(value) || !!(spreadableSymbol && value && value[spreadableSymbol]);
           }
           function isIndex(value, length) {
-            var type3 = typeof value;
+            var type2 = typeof value;
             length = length == null ? MAX_SAFE_INTEGER : length;
-            return !!length && (type3 == "number" || type3 != "symbol" && reIsUint.test(value)) && (value > -1 && value % 1 == 0 && value < length);
+            return !!length && (type2 == "number" || type2 != "symbol" && reIsUint.test(value)) && (value > -1 && value % 1 == 0 && value < length);
           }
           function isIterateeCall(value, index, object) {
-            if (!isObject2(object)) {
+            if (!isObject3(object)) {
               return false;
             }
-            var type3 = typeof index;
-            if (type3 == "number" ? isArrayLike(object) && isIndex(index, object.length) : type3 == "string" && index in object) {
+            var type2 = typeof index;
+            if (type2 == "number" ? isArrayLike(object) && isIndex(index, object.length) : type2 == "string" && index in object) {
               return eq(object[index], value);
             }
             return false;
             if (isArray2(value)) {
               return false;
             }
-            var type3 = typeof value;
-            if (type3 == "number" || type3 == "symbol" || type3 == "boolean" || value == null || isSymbol2(value)) {
+            var type2 = typeof value;
+            if (type2 == "number" || type2 == "symbol" || type2 == "boolean" || value == null || isSymbol2(value)) {
               return true;
             }
             return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || object != null && value in Object2(object);
           }
           function isKeyable(value) {
-            var type3 = typeof value;
-            return type3 == "string" || type3 == "number" || type3 == "symbol" || type3 == "boolean" ? value !== "__proto__" : value === null;
+            var type2 = typeof value;
+            return type2 == "string" || type2 == "number" || type2 == "symbol" || type2 == "boolean" ? value !== "__proto__" : value === null;
           }
           function isLaziable(func) {
             var funcName = getFuncName(func), other = lodash[funcName];
             return value === proto;
           }
           function isStrictComparable(value) {
-            return value === value && !isObject2(value);
+            return value === value && !isObject3(value);
           }
           function matchesStrictComparable(key, srcValue) {
             return function(object) {
               throw new TypeError2(FUNC_ERROR_TEXT3);
             }
             wait = toNumber2(wait) || 0;
-            if (isObject2(options2)) {
+            if (isObject3(options2)) {
               leading = !!options2.leading;
               maxing = "maxWait" in options2;
               maxWait = maxing ? nativeMax2(toNumber2(options2.maxWait) || 0, wait) : maxWait;
             if (typeof func != "function") {
               throw new TypeError2(FUNC_ERROR_TEXT3);
             }
-            if (isObject2(options2)) {
+            if (isObject3(options2)) {
               leading = "leading" in options2 ? !!options2.leading : leading;
               trailing = "trailing" in options2 ? !!options2.trailing : trailing;
             }
           }
           var isBuffer = nativeIsBuffer || stubFalse;
           var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate;
-          function isElement(value) {
+          function isElement2(value) {
             return isObjectLike2(value) && value.nodeType === 1 && !isPlainObject(value);
           }
           function isEmpty(value) {
             return typeof value == "number" && nativeIsFinite(value);
           }
           function isFunction(value) {
-            if (!isObject2(value)) {
+            if (!isObject3(value)) {
               return false;
             }
             var tag = baseGetTag2(value);
           function isLength(value) {
             return typeof value == "number" && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
           }
-          function isObject2(value) {
-            var type3 = typeof value;
-            return value != null && (type3 == "object" || type3 == "function");
+          function isObject3(value) {
+            var type2 = typeof value;
+            return value != null && (type2 == "object" || type2 == "function");
           }
           function isObjectLike2(value) {
             return value != null && typeof value == "object";
             if (isSymbol2(value)) {
               return NAN2;
             }
-            if (isObject2(value)) {
+            if (isObject3(value)) {
               var other = typeof value.valueOf == "function" ? value.valueOf() : value;
-              value = isObject2(other) ? other + "" : other;
+              value = isObject3(other) ? other + "" : other;
             }
             if (typeof value != "string") {
               return value === 0 ? value : +value;
           function functionsIn(object) {
             return object == null ? [] : baseFunctions(object, keysIn(object));
           }
-          function get3(object, path, defaultValue) {
+          function get4(object, path, defaultValue) {
             var result2 = object == null ? undefined2 : baseGet(object, path);
             return result2 === undefined2 ? defaultValue : result2;
           }
               var Ctor = object && object.constructor;
               if (isArrLike) {
                 accumulator = isArr ? new Ctor() : [];
-              } else if (isObject2(object)) {
+              } else if (isObject3(object)) {
                 accumulator = isFunction(Ctor) ? baseCreate(getPrototype(object)) : {};
               } else {
                 accumulator = {};
             options2 = assignInWith({}, options2, settings, customDefaultsAssignIn);
             var imports = assignInWith({}, options2.imports, settings.imports, customDefaultsAssignIn), importsKeys = keys(imports), importsValues = baseValues(imports, importsKeys);
             var isEscaping, isEvaluating, index = 0, interpolate = options2.interpolate || reNoMatch, source = "__p += '";
-            var reDelimiters = RegExp2((options2.escape || reNoMatch).source + "|" + interpolate.source + "|" + (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + "|" + (options2.evaluate || reNoMatch).source + "|$", "g");
+            var reDelimiters = RegExp2(
+              (options2.escape || reNoMatch).source + "|" + interpolate.source + "|" + (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + "|" + (options2.evaluate || reNoMatch).source + "|$",
+              "g"
+            );
             var sourceURL = "//# sourceURL=" + (hasOwnProperty2.call(options2, "sourceURL") ? (options2.sourceURL + "").replace(/\s/g, " ") : "lodash.templateSources[" + ++templateCounter + "]") + "\n";
             string.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
               interpolateValue || (interpolateValue = esTemplateValue);
           }
           function truncate(string, options2) {
             var length = DEFAULT_TRUNC_LENGTH, omission = DEFAULT_TRUNC_OMISSION;
-            if (isObject2(options2)) {
+            if (isObject3(options2)) {
               var separator = "separator" in options2 ? options2.separator : separator;
               length = "length" in options2 ? toInteger(options2.length) : length;
               omission = "omission" in options2 ? baseToString2(options2.omission) : omission;
           });
           function mixin(object, source, options2) {
             var props = keys(source), methodNames = baseFunctions(source, props);
-            if (options2 == null && !(isObject2(source) && (methodNames.length || !props.length))) {
+            if (options2 == null && !(isObject3(source) && (methodNames.length || !props.length))) {
               options2 = source;
               source = object;
               object = this;
               methodNames = baseFunctions(source, keys(source));
             }
-            var chain2 = !(isObject2(options2) && "chain" in options2) || !!options2.chain, isFunc = isFunction(object);
+            var chain2 = !(isObject3(options2) && "chain" in options2) || !!options2.chain, isFunc = isFunction(object);
             arrayEach(methodNames, function(methodName) {
               var func = source[methodName];
               object[methodName] = func;
           lodash.forInRight = forInRight;
           lodash.forOwn = forOwn;
           lodash.forOwnRight = forOwnRight;
-          lodash.get = get3;
+          lodash.get = get4;
           lodash.gt = gt;
           lodash.gte = gte;
           lodash.has = has;
           lodash.isBoolean = isBoolean;
           lodash.isBuffer = isBuffer;
           lodash.isDate = isDate;
-          lodash.isElement = isElement;
+          lodash.isElement = isElement2;
           lodash.isEmpty = isEmpty;
           lodash.isEqual = isEqual;
           lodash.isEqualWith = isEqualWith;
           lodash.isNil = isNil;
           lodash.isNull = isNull;
           lodash.isNumber = isNumber2;
-          lodash.isObject = isObject2;
+          lodash.isObject = isObject3;
           lodash.isObjectLike = isObjectLike2;
           lodash.isPlainObject = isPlainObject;
           lodash.isRegExp = isRegExp;
             };
           });
           arrayEach(["filter", "map", "takeWhile"], function(methodName, index) {
-            var type3 = index + 1, isFilter = type3 == LAZY_FILTER_FLAG || type3 == LAZY_WHILE_FLAG;
+            var type2 = index + 1, isFilter = type2 == LAZY_FILTER_FLAG || type2 == LAZY_WHILE_FLAG;
             LazyWrapper.prototype[methodName] = function(iteratee2) {
               var result2 = this.clone();
               result2.__iteratees__.push({
                 "iteratee": getIteratee(iteratee2, 3),
-                "type": type3
+                "type": type2
               });
               result2.__filtered__ = result2.__filtered__ || isFilter;
               return result2;
   var require_rbush_min = __commonJS({
     "node_modules/rbush/rbush.min.js"(exports2, module2) {
       !function(t, i2) {
-        typeof exports2 == "object" && typeof module2 != "undefined" ? module2.exports = i2() : typeof define == "function" && define.amd ? define(i2) : (t = t || self).RBush = i2();
+        "object" == typeof exports2 && "undefined" != typeof module2 ? module2.exports = i2() : "function" == typeof define && define.amd ? define(i2) : (t = t || self).RBush = i2();
       }(exports2, function() {
         "use strict";
         function t(t2, r2, e3, a2, h2) {
                 for (; h3(n3[x], p2) > 0; )
                   x--;
               }
-              h3(n3[e4], p2) === 0 ? i2(n3, e4, x) : i2(n3, ++x, a3), x <= r3 && (e4 = x + 1), r3 <= x && (a3 = x - 1);
+              0 === h3(n3[e4], p2) ? i2(n3, e4, x) : i2(n3, ++x, a3), x <= r3 && (e4 = x + 1), r3 <= x && (a3 = x - 1);
             }
           }(t2, r2, e3 || 0, a2 || t2.length - 1, h2 || n2);
         }
           return t2 < i3 ? -1 : t2 > i3 ? 1 : 0;
         }
         var r = function(t2) {
-          t2 === void 0 && (t2 = 9), this._maxEntries = Math.max(4, t2), this._minEntries = Math.max(2, Math.ceil(0.4 * this._maxEntries)), this.clear();
+          void 0 === t2 && (t2 = 9), this._maxEntries = Math.max(4, t2), this._minEntries = Math.max(2, Math.ceil(0.4 * this._maxEntries)), this.clear();
         };
         function e(t2, i3, n3) {
           if (!n3)
           for (var n3, r2, a2, h2 = this.data, o2 = this.toBBox(t2), s2 = [], l2 = []; h2 || s2.length; ) {
             if (h2 || (h2 = s2.pop(), r2 = s2[s2.length - 1], n3 = l2.pop(), a2 = true), h2.leaf) {
               var f3 = e(t2, h2.children, i3);
-              if (f3 !== -1)
+              if (-1 !== f3)
                 return h2.children.splice(f3, 1), s2.push(h2), this._condense(s2), this;
             }
             a2 || h2.leaf || !m(h2, o2) ? r2 ? (n3++, h2 = r2.children[n3], a2 = false) : h2 = null : (s2.push(h2), l2.push(n3), n3 = 0, r2 = h2, h2 = h2.children[0]);
             o(i3[r2], t2);
         }, r.prototype._condense = function(t2) {
           for (var i3 = t2.length - 1, n3 = void 0; i3 >= 0; i3--)
-            t2[i3].children.length === 0 ? i3 > 0 ? (n3 = t2[i3 - 1].children).splice(n3.indexOf(t2[i3]), 1) : this.clear() : a(t2[i3], this.toBBox);
+            0 === t2[i3].children.length ? i3 > 0 ? (n3 = t2[i3 - 1].children).splice(n3.indexOf(t2[i3]), 1) : this.clear() : a(t2[i3], this.toBBox);
         }, r;
       });
     }
         destroy: function() {
           this.buf = null;
         },
+        // === READING =================================================================
         readFields: function(readField, result, end) {
           end = end || this.length;
           while (this.pos < end) {
           this.pos += 4;
           return val;
         },
+        // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed)
         readFixed64: function() {
           var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
           this.pos += 8;
           this.pos = end;
           return buffer;
         },
+        // verbose for performance reasons; doesn't affect gzipped size
         readPackedVarint: function(arr, isSigned) {
           if (this.type !== Pbf.Bytes)
             return arr.push(this.readVarint(isSigned));
           return arr;
         },
         skip: function(val) {
-          var type3 = val & 7;
-          if (type3 === Pbf.Varint)
+          var type2 = val & 7;
+          if (type2 === Pbf.Varint)
             while (this.buf[this.pos++] > 127) {
             }
-          else if (type3 === Pbf.Bytes)
+          else if (type2 === Pbf.Bytes)
             this.pos = this.readVarint() + this.pos;
-          else if (type3 === Pbf.Fixed32)
+          else if (type2 === Pbf.Fixed32)
             this.pos += 4;
-          else if (type3 === Pbf.Fixed64)
+          else if (type2 === Pbf.Fixed64)
             this.pos += 8;
           else
-            throw new Error("Unimplemented type: " + type3);
+            throw new Error("Unimplemented type: " + type2);
         },
-        writeTag: function(tag, type3) {
-          this.writeVarint(tag << 3 | type3);
+        // === WRITING =================================================================
+        writeTag: function(tag, type2) {
+          this.writeVarint(tag << 3 | type2);
         },
         realloc: function(min3) {
           var length = this.length || 16;
         this.y = y;
       }
       Point.prototype = {
+        /**
+         * Clone this point, returning a new point that can be modified
+         * without affecting the old one.
+         * @return {Point} the clone
+         */
         clone: function() {
           return new Point(this.x, this.y);
         },
+        /**
+         * Add this point's x & y coordinates to another point,
+         * yielding a new point.
+         * @param {Point} p the other point
+         * @return {Point} output point
+         */
         add: function(p) {
           return this.clone()._add(p);
         },
+        /**
+         * Subtract this point's x & y coordinates to from point,
+         * yielding a new point.
+         * @param {Point} p the other point
+         * @return {Point} output point
+         */
         sub: function(p) {
           return this.clone()._sub(p);
         },
+        /**
+         * Multiply this point's x & y coordinates by point,
+         * yielding a new point.
+         * @param {Point} p the other point
+         * @return {Point} output point
+         */
         multByPoint: function(p) {
           return this.clone()._multByPoint(p);
         },
+        /**
+         * Divide this point's x & y coordinates by point,
+         * yielding a new point.
+         * @param {Point} p the other point
+         * @return {Point} output point
+         */
         divByPoint: function(p) {
           return this.clone()._divByPoint(p);
         },
+        /**
+         * Multiply this point's x & y coordinates by a factor,
+         * yielding a new point.
+         * @param {Point} k factor
+         * @return {Point} output point
+         */
         mult: function(k) {
           return this.clone()._mult(k);
         },
+        /**
+         * Divide this point's x & y coordinates by a factor,
+         * yielding a new point.
+         * @param {Point} k factor
+         * @return {Point} output point
+         */
         div: function(k) {
           return this.clone()._div(k);
         },
+        /**
+         * Rotate this point around the 0, 0 origin by an angle a,
+         * given in radians
+         * @param {Number} a angle to rotate around, in radians
+         * @return {Point} output point
+         */
         rotate: function(a) {
           return this.clone()._rotate(a);
         },
+        /**
+         * Rotate this point around p point by an angle a,
+         * given in radians
+         * @param {Number} a angle to rotate around, in radians
+         * @param {Point} p Point to rotate around
+         * @return {Point} output point
+         */
         rotateAround: function(a, p) {
           return this.clone()._rotateAround(a, p);
         },
+        /**
+         * Multiply this point by a 4x1 transformation matrix
+         * @param {Array<Number>} m transformation matrix
+         * @return {Point} output point
+         */
         matMult: function(m) {
           return this.clone()._matMult(m);
         },
+        /**
+         * Calculate this point but as a unit vector from 0, 0, meaning
+         * that the distance from the resulting point to the 0, 0
+         * coordinate will be equal to 1 and the angle from the resulting
+         * point to the 0, 0 coordinate will be the same as before.
+         * @return {Point} unit vector point
+         */
         unit: function() {
           return this.clone()._unit();
         },
+        /**
+         * Compute a perpendicular point, where the new y coordinate
+         * is the old x coordinate and the new x coordinate is the old y
+         * coordinate multiplied by -1
+         * @return {Point} perpendicular point
+         */
         perp: function() {
           return this.clone()._perp();
         },
+        /**
+         * Return a version of this point with the x & y coordinates
+         * rounded to integers.
+         * @return {Point} rounded point
+         */
         round: function() {
           return this.clone()._round();
         },
+        /**
+         * Return the magitude of this point: this is the Euclidean
+         * distance from the 0, 0 coordinate to this point's x and y
+         * coordinates.
+         * @return {Number} magnitude
+         */
         mag: function() {
           return Math.sqrt(this.x * this.x + this.y * this.y);
         },
+        /**
+         * Judge whether this point is equal to another point, returning
+         * true or false.
+         * @param {Point} other the other point
+         * @return {boolean} whether the points are equal
+         */
         equals: function(other) {
           return this.x === other.x && this.y === other.y;
         },
+        /**
+         * Calculate the distance from this point to another point
+         * @param {Point} p the other point
+         * @return {Number} distance
+         */
         dist: function(p) {
           return Math.sqrt(this.distSqr(p));
         },
+        /**
+         * Calculate the distance from this point to another point,
+         * without the square root step. Useful if you're comparing
+         * relative distances.
+         * @param {Point} p the other point
+         * @return {Number} distance
+         */
         distSqr: function(p) {
           var dx = p.x - this.x, dy = p.y - this.y;
           return dx * dx + dy * dy;
         },
+        /**
+         * Get the angle from the 0, 0 coordinate to this point, in radians
+         * coordinates.
+         * @return {Number} angle
+         */
         angle: function() {
           return Math.atan2(this.y, this.x);
         },
+        /**
+         * Get the angle from this point to another point, in radians
+         * @param {Point} b the other point
+         * @return {Number} angle
+         */
         angleTo: function(b) {
           return Math.atan2(this.y - b.y, this.x - b.x);
         },
+        /**
+         * Get the angle between this point and another point, in radians
+         * @param {Point} b the other point
+         * @return {Number} angle
+         */
         angleWith: function(b) {
           return this.angleWithSep(b.x, b.y);
         },
+        /*
+         * Find the angle of the two vectors, solving the formula for
+         * the cross product a x b = |a||b|sin(θ) for θ.
+         * @param {Number} x the x-coordinate
+         * @param {Number} y the y-coordinate
+         * @return {Number} the angle in radians
+         */
         angleWithSep: function(x, y) {
-          return Math.atan2(this.x * y - this.y * x, this.x * x + this.y * y);
+          return Math.atan2(
+            this.x * y - this.y * x,
+            this.x * x + this.y * y
+          );
         },
         _matMult: function(m) {
           var x = m[0] * this.x + m[1] * this.y, y = m[2] * this.x + m[3] * this.y;
         return [x12, y12, x2, y2];
       };
       VectorTileFeature.prototype.toGeoJSON = function(x, y, z) {
-        var size = this.extent * Math.pow(2, z), x05 = this.extent * x, y05 = this.extent * y, coords = this.loadGeometry(), type3 = VectorTileFeature.types[this.type], i2, j2;
+        var size = this.extent * Math.pow(2, z), x05 = this.extent * x, y05 = this.extent * y, coords = this.loadGeometry(), type2 = VectorTileFeature.types[this.type], i2, j2;
         function project(line) {
           for (var j3 = 0; j3 < line.length; j3++) {
             var p = line[j3], y2 = 180 - (p.y + y05) * 360 / size;
         if (coords.length === 1) {
           coords = coords[0];
         } else {
-          type3 = "Multi" + type3;
+          type2 = "Multi" + type2;
         }
         var result = {
           type: "Feature",
           geometry: {
-            type: type3,
+            type: type2,
             coordinates: coords
           },
           properties: this.properties
         pluck,
         isList,
         isFunction,
-        isObject: isObject2,
+        isObject: isObject3,
         Global
       };
       function make_assign() {
       function isFunction(val) {
         return val && {}.toString.call(val) === "[object Function]";
       }
-      function isObject2(val) {
+      function isObject3(val) {
         return val && {}.toString.call(val) === "[object Object]";
       }
     }
       var create2 = util.create;
       var isList = util.isList;
       var isFunction = util.isFunction;
-      var isObject2 = util.isObject;
+      var isObject3 = util.isObject;
       module2.exports = {
         createStore
       };
       var storeAPI = {
         version: "2.0.12",
         enabled: false,
+        // get returns the value of the given key. If that value
+        // is undefined, it returns optionalDefaultValue instead.
         get: function(key, optionalDefaultValue) {
           var data = this.storage.read(this._namespacePrefix + key);
           return this._deserialize(data, optionalDefaultValue);
         },
+        // set will store the given value at key and returns value.
+        // Calling set with value === undefined is equivalent to calling remove.
         set: function(key, value) {
           if (value === void 0) {
             return this.remove(key);
           this.storage.write(this._namespacePrefix + key, this._serialize(value));
           return value;
         },
+        // remove deletes the key and value stored at the given key.
         remove: function(key) {
           this.storage.remove(this._namespacePrefix + key);
         },
+        // each will call the given callback once for each key-value pair
+        // in this store.
         each: function(callback) {
           var self2 = this;
           this.storage.each(function(val, namespacedKey) {
             callback.call(self2, self2._deserialize(val), (namespacedKey || "").replace(self2._namespaceRegexp, ""));
           });
         },
+        // clearAll will remove all the stored key-value pairs in this store.
         clearAll: function() {
           this.storage.clearAll();
         },
+        // additional functionality that can't live in plugins
+        // ---------------------------------------------------
+        // hasNamespace returns true if this store instance has the given namespace.
         hasNamespace: function(namespace) {
           return this._namespacePrefix == "__storejs_" + namespace + "_";
         },
+        // createStore creates a store.js instance with the first
+        // functioning storage in the list of storage candidates,
+        // and applies the the given mixins to the instance.
         createStore: function() {
           return createStore.apply(this, arguments);
         },
               throw new Error("Plugins must be function values that return objects");
             }
             var pluginProperties = plugin.call(this);
-            if (!isObject2(pluginProperties)) {
+            if (!isObject3(pluginProperties)) {
               throw new Error("Plugins must return an object of function properties");
             }
             each(pluginProperties, function(pluginFnProp, propName) {
               self2._assignPluginFnProp(pluginFnProp, propName);
             });
           },
+          // Put deprecated properties in the private API, so as to not expose it to accidential
+          // discovery through inspection of the store object.
+          // Deprecated: addStorage
           addStorage: function(storage) {
             _warn("store.addStorage(storage) is deprecated. Use createStore([storages])");
             this._addStorage(storage);
   var require_all = __commonJS({
     "node_modules/store/storages/all.js"(exports2, module2) {
       module2.exports = [
+        // Listed in order of usage preference
         require_localStorage(),
         require_oldFF_globalStorage(),
         require_oldIE_userDataStorage(),
         }
         if (typeof JSON.stringify !== "function") {
           meta = {
+            // table of character substitutions
             "\b": "\\b",
             "  ": "\\t",
             "\n": "\\n",
                 return "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
               });
             }
-            if (rx_one.test(text.replace(rx_two, "@").replace(rx_three, "]").replace(rx_four, ""))) {
+            if (rx_one.test(
+              text.replace(rx_two, "@").replace(rx_three, "]").replace(rx_four, "")
+            )) {
               j = eval("(" + text + ")");
               return typeof reviver === "function" ? walk({ "": j }, "") : j;
             }
   }
   var viewClasses;
   var isArrayBufferView;
-  function normalizeName(name2) {
-    if (typeof name2 !== "string") {
-      name2 = String(name2);
+  function normalizeName(name) {
+    if (typeof name !== "string") {
+      name = String(name);
     }
-    if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name2) || name2 === "") {
-      throw new TypeError('Invalid character in header field name: "' + name2 + '"');
+    if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === "") {
+      throw new TypeError('Invalid character in header field name: "' + name + '"');
     }
-    return name2.toLowerCase();
+    return name.toLowerCase();
   }
   function normalizeValue(value) {
     if (typeof value !== "string") {
   function Headers(headers) {
     this.map = {};
     if (headers instanceof Headers) {
-      headers.forEach(function(value, name2) {
-        this.append(name2, value);
+      headers.forEach(function(value, name) {
+        this.append(name, value);
       }, this);
     } else if (Array.isArray(headers)) {
       headers.forEach(function(header) {
         this.append(header[0], header[1]);
       }, this);
     } else if (headers) {
-      Object.getOwnPropertyNames(headers).forEach(function(name2) {
-        this.append(name2, headers[name2]);
+      Object.getOwnPropertyNames(headers).forEach(function(name) {
+        this.append(name, headers[name]);
       }, this);
     }
   }
-  Headers.prototype.append = function(name2, value) {
-    name2 = normalizeName(name2);
+  Headers.prototype.append = function(name, value) {
+    name = normalizeName(name);
     value = normalizeValue(value);
-    var oldValue = this.map[name2];
-    this.map[name2] = oldValue ? oldValue + ", " + value : value;
+    var oldValue = this.map[name];
+    this.map[name] = oldValue ? oldValue + ", " + value : value;
   };
-  Headers.prototype["delete"] = function(name2) {
-    delete this.map[normalizeName(name2)];
+  Headers.prototype["delete"] = function(name) {
+    delete this.map[normalizeName(name)];
   };
-  Headers.prototype.get = function(name2) {
-    name2 = normalizeName(name2);
-    return this.has(name2) ? this.map[name2] : null;
+  Headers.prototype.get = function(name) {
+    name = normalizeName(name);
+    return this.has(name) ? this.map[name] : null;
   };
-  Headers.prototype.has = function(name2) {
-    return this.map.hasOwnProperty(normalizeName(name2));
+  Headers.prototype.has = function(name) {
+    return this.map.hasOwnProperty(normalizeName(name));
   };
-  Headers.prototype.set = function(name2, value) {
-    this.map[normalizeName(name2)] = normalizeValue(value);
+  Headers.prototype.set = function(name, value) {
+    this.map[normalizeName(name)] = normalizeValue(value);
   };
   Headers.prototype.forEach = function(callback, thisArg) {
-    for (var name2 in this.map) {
-      if (this.map.hasOwnProperty(name2)) {
-        callback.call(thisArg, this.map[name2], name2, this);
+    for (var name in this.map) {
+      if (this.map.hasOwnProperty(name)) {
+        callback.call(thisArg, this.map[name], name, this);
       }
     }
   };
   Headers.prototype.keys = function() {
     var items = [];
-    this.forEach(function(value, name2) {
-      items.push(name2);
+    this.forEach(function(value, name) {
+      items.push(name);
     });
     return iteratorFor(items);
   };
   };
   Headers.prototype.entries = function() {
     var items = [];
-    this.forEach(function(value, name2) {
-      items.push([name2, value]);
+    this.forEach(function(value, name) {
+      items.push([name, value]);
     });
     return iteratorFor(items);
   };
             return isConsumed;
           }
           if (ArrayBuffer.isView(this._bodyArrayBuffer)) {
-            return Promise.resolve(this._bodyArrayBuffer.buffer.slice(this._bodyArrayBuffer.byteOffset, this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength));
+            return Promise.resolve(
+              this._bodyArrayBuffer.buffer.slice(
+                this._bodyArrayBuffer.byteOffset,
+                this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength
+              )
+            );
           } else {
             return Promise.resolve(this._bodyArrayBuffer);
           }
     body.trim().split("&").forEach(function(bytes) {
       if (bytes) {
         var split = bytes.split("=");
-        var name2 = split.shift().replace(/\+/g, " ");
+        var name = split.shift().replace(/\+/g, " ");
         var value = split.join("=").replace(/\+/g, " ");
-        form.append(decodeURIComponent(name2), decodeURIComponent(value));
+        form.append(decodeURIComponent(name), decodeURIComponent(value));
       }
     });
     return form;
   try {
     new DOMException2();
   } catch (err) {
-    DOMException2 = function(message, name2) {
+    DOMException2 = function(message, name) {
       this.message = message;
-      this.name = name2;
+      this.name = name;
       var error = Error(message);
       this.stack = error.stack;
     };
         }
       }
       if (init2 && typeof init2.headers === "object" && !(init2.headers instanceof Headers)) {
-        Object.getOwnPropertyNames(init2.headers).forEach(function(name2) {
-          xhr.setRequestHeader(name2, normalizeValue(init2.headers[name2]));
+        Object.getOwnPropertyNames(init2.headers).forEach(function(name) {
+          xhr.setRequestHeader(name, normalizeValue(init2.headers[name]));
         });
       } else {
-        request3.headers.forEach(function(value, name2) {
-          xhr.setRequestHeader(name2, value);
+        request3.headers.forEach(function(value, name) {
+          xhr.setRequestHeader(name, value);
         });
       }
       if (request3.signal) {
       }
       _createClass(Emitter2, [{
         key: "addEventListener",
-        value: function addEventListener(type3, callback, options2) {
-          if (!(type3 in this.listeners)) {
-            this.listeners[type3] = [];
+        value: function addEventListener(type2, callback, options2) {
+          if (!(type2 in this.listeners)) {
+            this.listeners[type2] = [];
           }
-          this.listeners[type3].push({
+          this.listeners[type2].push({
             callback,
             options: options2
           });
         }
       }, {
         key: "removeEventListener",
-        value: function removeEventListener(type3, callback) {
-          if (!(type3 in this.listeners)) {
+        value: function removeEventListener(type2, callback) {
+          if (!(type2 in this.listeners)) {
             return;
           }
-          var stack = this.listeners[type3];
+          var stack = this.listeners[type2];
           for (var i2 = 0, l = stack.length; i2 < l; i2++) {
             if (stack[i2].callback === callback) {
               stack.splice(i2, 1);
       return typeof self2.Request === "function" && !self2.Request.prototype.hasOwnProperty("signal") || !self2.AbortController;
     }
     function abortableFetchDecorator(patchTargets) {
-      if (typeof patchTargets === "function") {
+      if ("function" === typeof patchTargets) {
         patchTargets = {
           fetch: patchTargets
         };
   // modules/index.js
   var modules_exports = {};
   __export(modules_exports, {
+    LocationManager: () => LocationManager,
     QAItem: () => QAItem,
     actionAddEntity: () => actionAddEntity,
     actionAddMember: () => actionAddMember,
     coreGraph: () => coreGraph,
     coreHistory: () => coreHistory,
     coreLocalizer: () => coreLocalizer,
-    coreLocations: () => coreLocations,
     coreTree: () => coreTree,
     coreUploader: () => coreUploader,
     coreValidator: () => coreValidator,
     geoViewportEdge: () => geoViewportEdge,
     geoZoomToScale: () => geoZoomToScale,
     localizer: () => _mainLocalizer,
-    locationManager: () => _mainLocations,
+    locationManager: () => _sharedLocationManager,
     modeAddArea: () => modeAddArea,
     modeAddLine: () => modeAddLine,
     modeAddNote: () => modeAddNote,
     osmIsOldMultipolygonOuterMember: () => osmIsOldMultipolygonOuterMember,
     osmJoinWays: () => osmJoinWays,
     osmLanes: () => osmLanes,
+    osmLifecyclePrefixes: () => osmLifecyclePrefixes,
     osmNode: () => osmNode,
     osmNodeGeometriesForTags: () => osmNodeGeometriesForTags,
     osmNote: () => osmNote,
     osmPointTags: () => osmPointTags,
     osmRailwayTrackTagValues: () => osmRailwayTrackTagValues,
     osmRelation: () => osmRelation,
+    osmRemoveLifecyclePrefix: () => osmRemoveLifecyclePrefix,
     osmRoutableHighwayTagValues: () => osmRoutableHighwayTagValues,
     osmSetAreaKeys: () => osmSetAreaKeys,
     osmSetPointTags: () => osmSetPointTags,
     uiFieldAccess: () => uiFieldAccess,
     uiFieldAddress: () => uiFieldAddress,
     uiFieldCheck: () => uiFieldCheck,
+    uiFieldColour: () => uiFieldText,
     uiFieldCombo: () => uiFieldCombo,
-    uiFieldCycleway: () => uiFieldCycleway,
     uiFieldDefaultCheck: () => uiFieldCheck,
+    uiFieldDirectionalCombo: () => uiFieldDirectionalCombo,
     uiFieldEmail: () => uiFieldText,
     uiFieldHelp: () => uiFieldHelp,
     uiFieldIdentifier: () => uiFieldText,
     uiKeepRightEditor: () => uiKeepRightEditor,
     uiKeepRightHeader: () => uiKeepRightHeader,
     uiLasso: () => uiLasso,
+    uiLengthIndicator: () => uiLengthIndicator,
     uiLoading: () => uiLoading,
     uiMapInMap: () => uiMapInMap,
     uiModal: () => uiModal,
     utilArrayUniq: () => utilArrayUniq,
     utilArrayUniqBy: () => utilArrayUniqBy,
     utilAsyncMap: () => utilAsyncMap,
+    utilCleanOsmString: () => utilCleanOsmString,
     utilCleanTags: () => utilCleanTags,
     utilCombinedTags: () => utilCombinedTags,
     utilCompareIDs: () => utilCompareIDs,
       } else if (options2 && options2.reverseOneway && key === "oneway") {
         return onewayReplacements[value] || value;
       } else if (includeAbsolute && directionKey.test(key)) {
-        if (compassReplacements[value])
-          return compassReplacements[value];
-        var degrees3 = parseFloat(value);
-        if (typeof degrees3 === "number" && !isNaN(degrees3)) {
-          if (degrees3 < 180) {
-            degrees3 += 180;
+        return value.split(";").map((value2) => {
+          if (compassReplacements[value2])
+            return compassReplacements[value2];
+          var degrees3 = Number(value2);
+          if (isFinite(degrees3)) {
+            if (degrees3 < 180) {
+              degrees3 += 180;
+            } else {
+              degrees3 -= 180;
+            }
+            return degrees3.toString();
           } else {
-            degrees3 -= 180;
+            return valueReplacements[value2] || value2;
           }
-          return degrees3.toString();
-        }
+        }).join(";");
       }
       return valueReplacements[value] || value;
     }
 
   // modules/osm/tags.js
   function osmIsInterestingTag(key) {
-    return key !== "attribution" && key !== "created_by" && key !== "source" && key !== "odbl" && key.indexOf("source:") !== 0 && key.indexOf("source_ref") !== 0 && key.indexOf("tiger:") !== 0;
+    return key !== "attribution" && key !== "created_by" && key !== "source" && key !== "odbl" && key.indexOf("source:") !== 0 && key.indexOf("source_ref") !== 0 && // purposely exclude colon
+    key.indexOf("tiger:") !== 0;
+  }
+  var osmLifecyclePrefixes = {
+    // nonexistent, might be built
+    proposed: true,
+    planned: true,
+    // under maintentance or between groundbreaking and opening
+    construction: true,
+    // existent but not functional
+    disused: true,
+    // dilapidated to nonexistent
+    abandoned: true,
+    was: true,
+    // nonexistent, still may appear in imagery
+    dismantled: true,
+    razed: true,
+    demolished: true,
+    destroyed: true,
+    removed: true,
+    obliterated: true,
+    // existent occasionally, e.g. stormwater drainage basin
+    intermittent: true
+  };
+  function osmRemoveLifecyclePrefix(key) {
+    const keySegments = key.split(":");
+    if (keySegments.length === 1)
+      return key;
+    if (keySegments[0] in osmLifecyclePrefixes) {
+      return key.slice(keySegments[0].length + 1);
+    }
+    return key;
   }
   var osmAreaKeys = {};
   function osmSetAreaKeys(value) {
       turntable: true,
       wash: true
     },
+    traffic_calming: {
+      island: true
+    },
     waterway: {
       dam: true
     }
     if (tags.area === "no")
       return null;
     var returnTags = {};
-    for (var key in tags) {
+    for (var realKey in tags) {
+      const key = osmRemoveLifecyclePrefix(realKey);
       if (key in osmAreaKeys && !(tags[key] in osmAreaKeys[key])) {
-        returnTags[key] = tags[key];
+        returnTags[realKey] = tags[realKey];
         return returnTags;
       }
       if (key in osmAreaKeysExceptions && tags[key] in osmAreaKeysExceptions[key]) {
-        returnTags[key] = tags[key];
+        returnTags[realKey] = tags[realKey];
         return returnTags;
       }
     }
     return null;
   }
+  var osmLineTags = {};
+  function osmSetLineTags(value) {
+    osmLineTags = value;
+  }
   var osmPointTags = {};
   function osmSetPointTags(value) {
     osmPointTags = value;
       "paved": true,
       "asphalt": true,
       "concrete": true,
+      "chipseal": true,
       "concrete:lanes": true,
       "concrete:plates": true
     },
       streamGeometry(object.geometry, stream);
     },
     FeatureCollection: function(object, stream) {
-      var features2 = object.features, i2 = -1, n2 = features2.length;
+      var features = object.features, i2 = -1, n2 = features.length;
       while (++i2 < n2)
-        streamGeometry(features2[i2].geometry, stream);
+        streamGeometry(features[i2].geometry, stream);
     }
   };
   var streamGeometryType = {
       if (direction > 0 ? t0 < t1 : t0 > t1)
         t0 += direction * tau;
     }
-    for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) {
-      point = spherical([cosRadius, -sinRadius * cos(t), -sinRadius * sin(t)]);
-      stream.point(point[0], point[1]);
+    for (var point2, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) {
+      point2 = spherical([cosRadius, -sinRadius * cos(t), -sinRadius * sin(t)]);
+      stream.point(point2[0], point2[1]);
     }
   }
-  function circleRadius(cosRadius, point) {
-    point = cartesian(point), point[0] -= cosRadius;
-    cartesianNormalizeInPlace(point);
-    var radius = acos(-point[1]);
-    return ((-point[2] < 0 ? -radius : radius) + tau - epsilon) % tau;
+  function circleRadius(cosRadius, point2) {
+    point2 = cartesian(point2), point2[0] -= cosRadius;
+    cartesianNormalizeInPlace(point2);
+    var radius = acos(-point2[1]);
+    return ((-point2[2] < 0 ? -radius : radius) + tau - epsilon) % tau;
   }
 
   // node_modules/d3-geo/src/clip/buffer.js
   }
 
   // node_modules/d3-geo/src/clip/rejoin.js
-  function Intersection(point, points, other, entry) {
-    this.x = point;
+  function Intersection(point2, points, other, entry) {
+    this.x = point2;
     this.z = points;
     this.o = other;
     this.e = entry;
     for (i2 = 0, n2 = clip.length; i2 < n2; ++i2) {
       clip[i2].e = startInside = !startInside;
     }
-    var start2 = subject[0], points, point;
+    var start2 = subject[0], points, point2;
     while (1) {
       var current = start2, isSubject = true;
       while (current.v)
         if (current.e) {
           if (isSubject) {
             for (i2 = 0, n2 = points.length; i2 < n2; ++i2)
-              stream.point((point = points[i2])[0], point[1]);
+              stream.point((point2 = points[i2])[0], point2[1]);
           } else {
             interpolate(current.x, current.n.x, 1, stream);
           }
           if (isSubject) {
             points = current.p.z;
             for (i2 = points.length - 1; i2 >= 0; --i2)
-              stream.point((point = points[i2])[0], point[1]);
+              stream.point((point2 = points[i2])[0], point2[1]);
           } else {
             interpolate(current.x, current.p.x, -1, stream);
           }
   }
 
   // node_modules/d3-geo/src/polygonContains.js
-  function longitude(point) {
-    return abs(point[0]) <= pi ? point[0] : sign(point[0]) * ((abs(point[0]) + pi) % tau - pi);
+  function longitude(point2) {
+    return abs(point2[0]) <= pi ? point2[0] : sign(point2[0]) * ((abs(point2[0]) + pi) % tau - pi);
   }
-  function polygonContains_default(polygon2, point) {
-    var lambda = longitude(point), phi = point[1], sinPhi = sin(phi), normal = [sin(lambda), -cos(lambda), 0], angle2 = 0, winding = 0;
+  function polygonContains_default(polygon2, point2) {
+    var lambda = longitude(point2), phi = point2[1], sinPhi = sin(phi), normal = [sin(lambda), -cos(lambda), 0], angle2 = 0, winding = 0;
     var sum = new Adder();
     if (sinPhi === 1)
       phi = halfPi + epsilon;
     return function(sink) {
       var line = clipLine(sink), ringBuffer = buffer_default(), ringSink = clipLine(ringBuffer), polygonStarted = false, polygon2, segments, ring;
       var clip = {
-        point,
+        point: point2,
         lineStart,
         lineEnd,
         polygonStart: function() {
           polygon2 = [];
         },
         polygonEnd: function() {
-          clip.point = point;
+          clip.point = point2;
           clip.lineStart = lineStart;
           clip.lineEnd = lineEnd;
           segments = merge(segments);
           sink.polygonEnd();
         }
       };
-      function point(lambda, phi) {
+      function point2(lambda, phi) {
         if (pointVisible(lambda, phi))
           sink.point(lambda, phi);
       }
         line.lineStart();
       }
       function lineEnd() {
-        clip.point = point;
+        clip.point = point2;
         line.lineEnd();
       }
       function pointRing(lambda, phi) {
       function ringEnd() {
         pointRing(ring[0][0], ring[0][1]);
         ringSink.lineEnd();
-        var clean2 = ringSink.clean(), ringSegments = ringBuffer.result(), i2, n2 = ringSegments.length, m, segment, point2;
+        var clean2 = ringSink.clean(), ringSegments = ringBuffer.result(), i2, n2 = ringSegments.length, m, segment, point3;
         ring.pop();
         polygon2.push(ring);
         ring = null;
               sink.polygonStart(), polygonStarted = true;
             sink.lineStart();
             for (i2 = 0; i2 < m; ++i2)
-              sink.point((point2 = segment[i2])[0], point2[1]);
+              sink.point((point3 = segment[i2])[0], point3[1]);
             sink.lineEnd();
           }
           return;
   }
 
   // node_modules/d3-geo/src/clip/antimeridian.js
-  var antimeridian_default = clip_default(function() {
-    return true;
-  }, clipAntimeridianLine, clipAntimeridianInterpolate, [-pi, -halfPi]);
+  var antimeridian_default = clip_default(
+    function() {
+      return true;
+    },
+    clipAntimeridianLine,
+    clipAntimeridianInterpolate,
+    [-pi, -halfPi]
+  );
   function clipAntimeridianLine(stream) {
     var lambda04 = NaN, phi02 = NaN, sign0 = NaN, clean2;
     return {
             stream.lineEnd();
           point0 = null;
         },
+        // Rejoin first and last segments if there were intersections and the first
+        // and last points were visible.
         clean: function() {
           return clean2 | (v00 && v0) << 1;
         }
     return function(stream) {
       var activeStream = stream, bufferStream = buffer_default(), segments, polygon2, ring, x__, y__, v__, x_, y_, v_, first, clean2;
       var clipStream = {
-        point,
+        point: point2,
         lineStart,
         lineEnd,
         polygonStart,
         polygonEnd
       };
-      function point(x, y) {
+      function point2(x, y) {
         if (visible(x, y))
           activeStream.point(x, y);
       }
       function polygonInside() {
         var winding = 0;
         for (var i2 = 0, n2 = polygon2.length; i2 < n2; ++i2) {
-          for (var ring2 = polygon2[i2], j2 = 1, m = ring2.length, point2 = ring2[0], a0, a1, b0 = point2[0], b1 = point2[1]; j2 < m; ++j2) {
-            a0 = b0, a1 = b1, point2 = ring2[j2], b0 = point2[0], b1 = point2[1];
+          for (var ring2 = polygon2[i2], j2 = 1, m = ring2.length, point3 = ring2[0], a0, a1, b0 = point3[0], b1 = point3[1]; j2 < m; ++j2) {
+            a0 = b0, a1 = b1, point3 = ring2[j2], b0 = point3[0], b1 = point3[1];
             if (a1 <= y12) {
               if (b1 > y12 && (b0 - a0) * (y12 - a1) > (b1 - a1) * (x05 - a0))
                 ++winding;
             bufferStream.rejoin();
           segments.push(bufferStream.result());
         }
-        clipStream.point = point;
+        clipStream.point = point2;
         if (v_)
           activeStream.lineEnd();
       }
     return function(stream) {
       var lambda003, x004, y004, a00, b00, c00, lambda04, x05, y05, a0, b0, c0;
       var resampleStream = {
-        point,
+        point: point2,
         lineStart,
         lineEnd,
         polygonStart: function() {
           resampleStream.lineStart = lineStart;
         }
       };
-      function point(x, y) {
+      function point2(x, y) {
         x = project(x, y);
         stream.point(x[0], x[1]);
       }
         stream.point(x05, y05);
       }
       function lineEnd() {
-        resampleStream.point = point;
+        resampleStream.point = point2;
         stream.lineEnd();
       }
       function ringStart() {
   }
   function projectionMutator(projectAt) {
     var project, k = 150, x = 480, y = 250, lambda = 0, phi = 0, deltaLambda = 0, deltaPhi = 0, deltaGamma = 0, rotate, alpha = 0, sx = 1, sy = 1, theta = null, preclip = antimeridian_default, x05 = null, y05, x12, y12, postclip = identity_default, delta2 = 0.5, projectResample, projectTransform, projectRotateTransform, cache, cacheStream;
-    function projection2(point) {
-      return projectRotateTransform(point[0] * radians, point[1] * radians);
+    function projection2(point2) {
+      return projectRotateTransform(point2[0] * radians, point2[1] * radians);
     }
-    function invert(point) {
-      point = projectRotateTransform.invert(point[0], point[1]);
-      return point && [point[0] * degrees, point[1] * degrees];
+    function invert(point2) {
+      point2 = projectRotateTransform.invert(point2[0], point2[1]);
+      return point2 && [point2[0] * degrees, point2[1] * degrees];
     }
     projection2.stream = function(stream) {
       return cache && cacheStream === stream ? cache : cache = transformRadians(transformRotate(rotate)(preclip(projectResample(postclip(cacheStream = stream)))));
     tileSize = tileSize || 256;
     return tileSize * Math.pow(2, z) / TAU;
   }
-  function geoSphericalClosestNode(nodes, point) {
+  function geoSphericalClosestNode(nodes, point2) {
     var minDistance = Infinity, distance;
     var indexOfMin;
     for (var i2 in nodes) {
-      distance = geoSphericalDistance(nodes[i2].loc, point);
+      distance = geoSphericalDistance(nodes[i2].loc, point2);
       if (distance < minDistance) {
         minDistance = distance;
         indexOfMin = i2;
     extend: function(obj) {
       if (!(obj instanceof geoExtent))
         obj = new geoExtent(obj);
-      return geoExtent([Math.min(obj[0][0], this[0][0]), Math.min(obj[0][1], this[0][1])], [Math.max(obj[1][0], this[1][0]), Math.max(obj[1][1], this[1][1])]);
+      return geoExtent(
+        [Math.min(obj[0][0], this[0][0]), Math.min(obj[0][1], this[0][1])],
+        [Math.max(obj[1][0], this[1][0]), Math.max(obj[1][1], this[1][1])]
+      );
     },
     _extend: function(extent) {
       this[0][0] = Math.min(extent[0][0], this[0][0]);
     intersection: function(obj) {
       if (!this.intersects(obj))
         return new geoExtent();
-      return new geoExtent([Math.max(obj[0][0], this[0][0]), Math.max(obj[0][1], this[0][1])], [Math.min(obj[1][0], this[1][0]), Math.min(obj[1][1], this[1][1])]);
+      return new geoExtent(
+        [Math.max(obj[0][0], this[0][0]), Math.max(obj[0][1], this[0][1])],
+        [Math.min(obj[1][0], this[1][0]), Math.min(obj[1][1], this[1][1])]
+      );
     },
     percentContainedIn: function(obj) {
       if (!(obj instanceof geoExtent))
     padByMeters: function(meters) {
       var dLat = geoMetersToLat(meters);
       var dLon = geoMetersToLon(meters, this.center()[1]);
-      return geoExtent([this[0][0] - dLon, this[0][1] - dLat], [this[1][0] + dLon, this[1][1] + dLat]);
+      return geoExtent(
+        [this[0][0] - dLon, this[0][1] - dLat],
+        [this[1][0] + dLon, this[1][1] + dLat]
+      );
     },
     toParam: function() {
       return this.rectangle().join(",");
     return a[0] === b[0] && a[1] === b[1] || a[0] === b[1] && a[1] === b[0];
   }
   function geoRotate(points, angle2, around) {
-    return points.map(function(point) {
-      var radial = geoVecSubtract(point, around);
+    return points.map(function(point2) {
+      var radial = geoVecSubtract(point2, around);
       return [
         radial[0] * Math.cos(angle2) - radial[1] * Math.sin(angle2) + around[0],
         radial[0] * Math.sin(angle2) + radial[1] * Math.cos(angle2) + around[1]
       ];
     });
   }
-  function geoChooseEdge(nodes, point, projection2, activeID) {
+  function geoChooseEdge(nodes, point2, projection2, activeID) {
     var dist = geoVecLength;
     var points = nodes.map(function(n2) {
       return projection2(n2.loc);
         continue;
       var o = points[i2];
       var s = geoVecSubtract(points[i2 + 1], o);
-      var v = geoVecSubtract(point, o);
+      var v = geoVecSubtract(point2, o);
       var proj = geoVecDot(v, s) / geoVecDot(s, s);
       var p;
       if (proj < 0) {
       } else {
         p = [o[0] + proj * s[0], o[1] + proj * s[1]];
       }
-      var d = dist(p, point);
+      var d = dist(p, point2);
       if (d < min3) {
         min3 = d;
         idx = i2 + 1;
     }
     return false;
   }
-  function geoPointInPolygon(point, polygon2) {
-    var x = point[0];
-    var y = point[1];
+  function geoPointInPolygon(point2, polygon2) {
+    var x = point2[0];
+    var y = point2[1];
     var inside = false;
     for (var i2 = 0, j2 = polygon2.length - 1; i2 < polygon2.length; j2 = i2++) {
       var xi = polygon2[i2][0];
     return inside;
   }
   function geoPolygonContainsPolygon(outer, inner) {
-    return inner.every(function(point) {
-      return geoPointInPolygon(point, outer);
+    return inner.every(function(point2) {
+      return geoPointInPolygon(point2, outer);
     });
   }
   function geoPolygonIntersectsPolygon(outer, inner, checkSegments) {
     function testPoints(outer2, inner2) {
-      return inner2.some(function(point) {
-        return geoPointInPolygon(point, outer2);
+      return inner2.some(function(point2) {
+        return geoPointInPolygon(point2, outer2);
       });
     }
     return testPoints(outer, inner) || !!checkSegments && geoPathHasIntersections(outer, inner);
       var c2 = i2 === hull.length - 1 ? hull[0] : hull[i2 + 1];
       var angle2 = Math.atan2(c2[1] - c1[1], c2[0] - c1[0]);
       var poly = geoRotate(hull, -angle2, centroid);
-      var extent = poly.reduce(function(extent2, point) {
-        return extent2.extend(geoExtent(point));
+      var extent = poly.reduce(function(extent2, point2) {
+        return extent2.extend(geoExtent(point2));
       }, geoExtent());
       var area = extent.area();
       if (area < minArea) {
     }
     return length;
   }
-  function geoViewportEdge(point, dimensions) {
+  function geoViewportEdge(point2, dimensions) {
     var pad2 = [80, 20, 50, 20];
     var x = 0;
     var y = 0;
-    if (point[0] > dimensions[0] - pad2[1]) {
+    if (point2[0] > dimensions[0] - pad2[1]) {
       x = -10;
     }
-    if (point[0] < pad2[3]) {
+    if (point2[0] < pad2[3]) {
       x = 10;
     }
-    if (point[1] > dimensions[1] - pad2[2]) {
+    if (point2[1] > dimensions[1] - pad2[2]) {
       y = -10;
     }
-    if (point[1] < pad2[0]) {
+    if (point2[1] < pad2[0]) {
       y = 10;
     }
     if (x || y) {
   }
   function parseTypenames(typenames, types) {
     return typenames.trim().split(/^|\s+/).map(function(t) {
-      var name2 = "", i2 = t.indexOf(".");
+      var name = "", i2 = t.indexOf(".");
       if (i2 >= 0)
-        name2 = t.slice(i2 + 1), t = t.slice(0, i2);
+        name = t.slice(i2 + 1), t = t.slice(0, i2);
       if (t && !types.hasOwnProperty(t))
         throw new Error("unknown type: " + t);
-      return { type: t, name: name2 };
+      return { type: t, name };
     });
   }
   Dispatch.prototype = dispatch.prototype = {
         copy2[t] = _[t].slice();
       return new Dispatch(copy2);
     },
-    call: function(type3, that) {
+    call: function(type2, that) {
       if ((n2 = arguments.length - 2) > 0)
         for (var args = new Array(n2), i2 = 0, n2, t; i2 < n2; ++i2)
           args[i2] = arguments[i2 + 2];
-      if (!this._.hasOwnProperty(type3))
-        throw new Error("unknown type: " + type3);
-      for (t = this._[type3], i2 = 0, n2 = t.length; i2 < n2; ++i2)
+      if (!this._.hasOwnProperty(type2))
+        throw new Error("unknown type: " + type2);
+      for (t = this._[type2], i2 = 0, n2 = t.length; i2 < n2; ++i2)
         t[i2].value.apply(that, args);
     },
-    apply: function(type3, that, args) {
-      if (!this._.hasOwnProperty(type3))
-        throw new Error("unknown type: " + type3);
-      for (var t = this._[type3], i2 = 0, n2 = t.length; i2 < n2; ++i2)
+    apply: function(type2, that, args) {
+      if (!this._.hasOwnProperty(type2))
+        throw new Error("unknown type: " + type2);
+      for (var t = this._[type2], i2 = 0, n2 = t.length; i2 < n2; ++i2)
         t[i2].value.apply(that, args);
     }
   };
-  function get(type3, name2) {
-    for (var i2 = 0, n2 = type3.length, c; i2 < n2; ++i2) {
-      if ((c = type3[i2]).name === name2) {
+  function get(type2, name) {
+    for (var i2 = 0, n2 = type2.length, c; i2 < n2; ++i2) {
+      if ((c = type2[i2]).name === name) {
         return c.value;
       }
     }
   }
-  function set(type3, name2, callback) {
-    for (var i2 = 0, n2 = type3.length; i2 < n2; ++i2) {
-      if (type3[i2].name === name2) {
-        type3[i2] = noop2, type3 = type3.slice(0, i2).concat(type3.slice(i2 + 1));
+  function set(type2, name, callback) {
+    for (var i2 = 0, n2 = type2.length; i2 < n2; ++i2) {
+      if (type2[i2].name === name) {
+        type2[i2] = noop2, type2 = type2.slice(0, i2).concat(type2.slice(i2 + 1));
         break;
       }
     }
     if (callback != null)
-      type3.push({ name: name2, value: callback });
-    return type3;
+      type2.push({ name, value: callback });
+    return type2;
   }
   var dispatch_default = dispatch;
 
   };
 
   // node_modules/d3-selection/src/namespace.js
-  function namespace_default(name2) {
-    var prefix = name2 += "", i2 = prefix.indexOf(":");
-    if (i2 >= 0 && (prefix = name2.slice(0, i2)) !== "xmlns")
-      name2 = name2.slice(i2 + 1);
-    return namespaces_default.hasOwnProperty(prefix) ? { space: namespaces_default[prefix], local: name2 } : name2;
+  function namespace_default(name) {
+    var prefix = name += "", i2 = prefix.indexOf(":");
+    if (i2 >= 0 && (prefix = name.slice(0, i2)) !== "xmlns")
+      name = name.slice(i2 + 1);
+    return namespaces_default.hasOwnProperty(prefix) ? { space: namespaces_default[prefix], local: name } : name;
   }
 
   // node_modules/d3-selection/src/creator.js
-  function creatorInherit(name2) {
+  function creatorInherit(name) {
     return function() {
       var document2 = this.ownerDocument, uri = this.namespaceURI;
-      return uri === xhtml && document2.documentElement.namespaceURI === xhtml ? document2.createElement(name2) : document2.createElementNS(uri, name2);
+      return uri === xhtml && document2.documentElement.namespaceURI === xhtml ? document2.createElement(name) : document2.createElementNS(uri, name);
     };
   }
   function creatorFixed(fullname) {
       return this.ownerDocument.createElementNS(fullname.space, fullname.local);
     };
   }
-  function creator_default(name2) {
-    var fullname = namespace_default(name2);
+  function creator_default(name) {
+    var fullname = namespace_default(name);
     return (fullname.local ? creatorFixed : creatorInherit)(fullname);
   }
 
   }
 
   // node_modules/d3-selection/src/selection/attr.js
-  function attrRemove(name2) {
+  function attrRemove(name) {
     return function() {
-      this.removeAttribute(name2);
+      this.removeAttribute(name);
     };
   }
   function attrRemoveNS(fullname) {
       this.removeAttributeNS(fullname.space, fullname.local);
     };
   }
-  function attrConstant(name2, value) {
+  function attrConstant(name, value) {
     return function() {
-      this.setAttribute(name2, value);
+      this.setAttribute(name, value);
     };
   }
   function attrConstantNS(fullname, value) {
       this.setAttributeNS(fullname.space, fullname.local, value);
     };
   }
-  function attrFunction(name2, value) {
+  function attrFunction(name, value) {
     return function() {
       var v = value.apply(this, arguments);
       if (v == null)
-        this.removeAttribute(name2);
+        this.removeAttribute(name);
       else
-        this.setAttribute(name2, v);
+        this.setAttribute(name, v);
     };
   }
   function attrFunctionNS(fullname, value) {
         this.setAttributeNS(fullname.space, fullname.local, v);
     };
   }
-  function attr_default(name2, value) {
-    var fullname = namespace_default(name2);
+  function attr_default(name, value) {
+    var fullname = namespace_default(name);
     if (arguments.length < 2) {
       var node = this.node();
       return fullname.local ? node.getAttributeNS(fullname.space, fullname.local) : node.getAttribute(fullname);
   }
 
   // node_modules/d3-selection/src/selection/style.js
-  function styleRemove(name2) {
+  function styleRemove(name) {
     return function() {
-      this.style.removeProperty(name2);
+      this.style.removeProperty(name);
     };
   }
-  function styleConstant(name2, value, priority) {
+  function styleConstant(name, value, priority) {
     return function() {
-      this.style.setProperty(name2, value, priority);
+      this.style.setProperty(name, value, priority);
     };
   }
-  function styleFunction(name2, value, priority) {
+  function styleFunction(name, value, priority) {
     return function() {
       var v = value.apply(this, arguments);
       if (v == null)
-        this.style.removeProperty(name2);
+        this.style.removeProperty(name);
       else
-        this.style.setProperty(name2, v, priority);
+        this.style.setProperty(name, v, priority);
     };
   }
-  function style_default(name2, value, priority) {
-    return arguments.length > 1 ? this.each((value == null ? styleRemove : typeof value === "function" ? styleFunction : styleConstant)(name2, value, priority == null ? "" : priority)) : styleValue(this.node(), name2);
+  function style_default(name, value, priority) {
+    return arguments.length > 1 ? this.each((value == null ? styleRemove : typeof value === "function" ? styleFunction : styleConstant)(name, value, priority == null ? "" : priority)) : styleValue(this.node(), name);
   }
-  function styleValue(node, name2) {
-    return node.style.getPropertyValue(name2) || window_default(node).getComputedStyle(node, null).getPropertyValue(name2);
+  function styleValue(node, name) {
+    return node.style.getPropertyValue(name) || window_default(node).getComputedStyle(node, null).getPropertyValue(name);
   }
 
   // node_modules/d3-selection/src/selection/property.js
-  function propertyRemove(name2) {
+  function propertyRemove(name) {
     return function() {
-      delete this[name2];
+      delete this[name];
     };
   }
-  function propertyConstant(name2, value) {
+  function propertyConstant(name, value) {
     return function() {
-      this[name2] = value;
+      this[name] = value;
     };
   }
-  function propertyFunction(name2, value) {
+  function propertyFunction(name, value) {
     return function() {
       var v = value.apply(this, arguments);
       if (v == null)
-        delete this[name2];
+        delete this[name];
       else
-        this[name2] = v;
+        this[name] = v;
     };
   }
-  function property_default(name2, value) {
-    return arguments.length > 1 ? this.each((value == null ? propertyRemove : typeof value === "function" ? propertyFunction : propertyConstant)(name2, value)) : this.node()[name2];
+  function property_default(name, value) {
+    return arguments.length > 1 ? this.each((value == null ? propertyRemove : typeof value === "function" ? propertyFunction : propertyConstant)(name, value)) : this.node()[name];
   }
 
   // node_modules/d3-selection/src/selection/classed.js
     this._names = classArray(node.getAttribute("class") || "");
   }
   ClassList.prototype = {
-    add: function(name2) {
-      var i2 = this._names.indexOf(name2);
+    add: function(name) {
+      var i2 = this._names.indexOf(name);
       if (i2 < 0) {
-        this._names.push(name2);
+        this._names.push(name);
         this._node.setAttribute("class", this._names.join(" "));
       }
     },
-    remove: function(name2) {
-      var i2 = this._names.indexOf(name2);
+    remove: function(name) {
+      var i2 = this._names.indexOf(name);
       if (i2 >= 0) {
         this._names.splice(i2, 1);
         this._node.setAttribute("class", this._names.join(" "));
       }
     },
-    contains: function(name2) {
-      return this._names.indexOf(name2) >= 0;
+    contains: function(name) {
+      return this._names.indexOf(name) >= 0;
     }
   };
   function classedAdd(node, names) {
       (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);
     };
   }
-  function classed_default(name2, value) {
-    var names = classArray(name2 + "");
+  function classed_default(name, value) {
+    var names = classArray(name + "");
     if (arguments.length < 2) {
       var list = classList(this.node()), i2 = -1, n2 = names.length;
       while (++i2 < n2)
   }
 
   // node_modules/d3-selection/src/selection/append.js
-  function append_default(name2) {
-    var create2 = typeof name2 === "function" ? name2 : creator_default(name2);
+  function append_default(name) {
+    var create2 = typeof name === "function" ? name : creator_default(name);
     return this.select(function() {
       return this.appendChild(create2.apply(this, arguments));
     });
   function constantNull() {
     return null;
   }
-  function insert_default(name2, before) {
-    var create2 = typeof name2 === "function" ? name2 : creator_default(name2), select = before == null ? constantNull : typeof before === "function" ? before : selector_default(before);
+  function insert_default(name, before) {
+    var create2 = typeof name === "function" ? name : creator_default(name), select = before == null ? constantNull : typeof before === "function" ? before : selector_default(before);
     return this.select(function() {
       return this.insertBefore(create2.apply(this, arguments), select.apply(this, arguments) || null);
     });
   }
   function parseTypenames2(typenames) {
     return typenames.trim().split(/^|\s+/).map(function(t) {
-      var name2 = "", i2 = t.indexOf(".");
+      var name = "", i2 = t.indexOf(".");
       if (i2 >= 0)
-        name2 = t.slice(i2 + 1), t = t.slice(0, i2);
-      return { type: t, name: name2 };
+        name = t.slice(i2 + 1), t = t.slice(0, i2);
+      return { type: t, name };
     });
   }
   function onRemove(typename) {
   }
 
   // node_modules/d3-selection/src/selection/dispatch.js
-  function dispatchEvent(node, type3, params) {
+  function dispatchEvent(node, type2, params) {
     var window2 = window_default(node), event = window2.CustomEvent;
     if (typeof event === "function") {
-      event = new event(type3, params);
+      event = new event(type2, params);
     } else {
       event = window2.document.createEvent("Event");
       if (params)
-        event.initEvent(type3, params.bubbles, params.cancelable), event.detail = params.detail;
+        event.initEvent(type2, params.bubbles, params.cancelable), event.detail = params.detail;
       else
-        event.initEvent(type3, false, false);
+        event.initEvent(type2, false, false);
     }
     node.dispatchEvent(event);
   }
-  function dispatchConstant(type3, params) {
+  function dispatchConstant(type2, params) {
     return function() {
-      return dispatchEvent(this, type3, params);
+      return dispatchEvent(this, type2, params);
     };
   }
-  function dispatchFunction(type3, params) {
+  function dispatchFunction(type2, params) {
     return function() {
-      return dispatchEvent(this, type3, params.apply(this, arguments));
+      return dispatchEvent(this, type2, params.apply(this, arguments));
     };
   }
-  function dispatch_default2(type3, params) {
-    return this.each((typeof params === "function" ? dispatchFunction : dispatchConstant)(type3, params));
+  function dispatch_default2(type2, params) {
+    return this.each((typeof params === "function" ? dispatchFunction : dispatchConstant)(type2, params));
   }
 
   // node_modules/d3-selection/src/selection/iterator.js
     if (node) {
       var svg2 = node.ownerSVGElement || node;
       if (svg2.createSVGPoint) {
-        var point = svg2.createSVGPoint();
-        point.x = event.clientX, point.y = event.clientY;
-        point = point.matrixTransform(node.getScreenCTM().inverse());
-        return [point.x, point.y];
+        var point2 = svg2.createSVGPoint();
+        point2.x = event.clientX, point2.y = event.clientY;
+        point2 = point2.matrixTransform(node.getScreenCTM().inverse());
+        return [point2.x, point2.y];
       }
       if (node.getBoundingClientRect) {
         var rect = node.getBoundingClientRect();
   var constant_default2 = (x) => () => x;
 
   // node_modules/d3-drag/src/event.js
-  function DragEvent(type3, {
+  function DragEvent(type2, {
     sourceEvent,
     subject,
     target,
     dispatch: dispatch10
   }) {
     Object.defineProperties(this, {
-      type: { value: type3, enumerable: true, configurable: true },
+      type: { value: type2, enumerable: true, configurable: true },
       sourceEvent: { value: sourceEvent, enumerable: true, configurable: true },
       subject: { value: subject, enumerable: true, configurable: true },
       target: { value: target, enumerable: true, configurable: true },
         return;
       dx = s.x - p[0] || 0;
       dy = s.y - p[1] || 0;
-      return function gesture(type3, event2, touch2) {
+      return function gesture(type2, event2, touch2) {
         var p02 = p, n2;
-        switch (type3) {
+        switch (type2) {
           case "start":
             gestures[identifier] = gesture, n2 = active++;
             break;
             p = pointer_default(touch2 || event2, container2), n2 = active;
             break;
         }
-        dispatch10.call(type3, that, new DragEvent(type3, {
-          sourceEvent: event2,
-          subject: s,
-          target: drag,
-          identifier,
-          active: n2,
-          x: p[0] + dx,
-          y: p[1] + dy,
-          dx: p[0] - p02[0],
-          dy: p[1] - p02[1],
-          dispatch: dispatch10
-        }), d);
+        dispatch10.call(
+          type2,
+          that,
+          new DragEvent(type2, {
+            sourceEvent: event2,
+            subject: s,
+            target: drag,
+            identifier,
+            active: n2,
+            x: p[0] + dx,
+            y: p[1] + dy,
+            dx: p[0] - p02[0],
+            dy: p[1] - p02[1],
+            dispatch: dispatch10
+          }),
+          d
+        );
       };
     }
     drag.filter = function(_) {
       return this.rgb().displayable();
     },
     hex: color_formatHex,
+    // Deprecated! Use color.formatHex.
     formatHex: color_formatHex,
     formatHex8: color_formatHex8,
     formatHsl: color_formatHsl,
       return -0.5 <= this.r && this.r < 255.5 && (-0.5 <= this.g && this.g < 255.5) && (-0.5 <= this.b && this.b < 255.5) && (0 <= this.opacity && this.opacity <= 1);
     },
     hex: rgb_formatHex,
+    // Deprecated! Use color.formatHex.
     formatHex: rgb_formatHex,
     formatHex8: rgb_formatHex8,
     formatRgb: rgb_formatRgb,
     },
     rgb() {
       var h = this.h % 360 + (this.h < 0) * 360, s = isNaN(h) || isNaN(this.s) ? 0 : this.s, l = this.l, m2 = l + (l < 0.5 ? l : 1 - l) * s, m1 = 2 * l - m2;
-      return new Rgb(hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2), hsl2rgb(h, m1, m2), hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2), this.opacity);
+      return new Rgb(
+        hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),
+        hsl2rgb(h, m1, m2),
+        hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),
+        this.opacity
+      );
     },
     clamp() {
       return new Hsl(clamph(this.h), clampt(this.s), clampt(this.l), clampa(this.opacity));
   var RUNNING = 4;
   var ENDING = 5;
   var ENDED = 6;
-  function schedule_default(node, name2, id2, index, group, timing) {
+  function schedule_default(node, name, id2, index, group, timing) {
     var schedules = node.__transition;
     if (!schedules)
       node.__transition = {};
     else if (id2 in schedules)
       return;
     create(node, id2, {
-      name: name2,
+      name,
       index,
+      // For context during callback.
       group,
+      // For context during callback.
       on: emptyOn,
       tween: emptyTween,
       time: timing.time,
   }
 
   // node_modules/d3-transition/src/interrupt.js
-  function interrupt_default(node, name2) {
+  function interrupt_default(node, name) {
     var schedules = node.__transition, schedule, active, empty2 = true, i2;
     if (!schedules)
       return;
-    name2 = name2 == null ? null : name2 + "";
+    name = name == null ? null : name + "";
     for (i2 in schedules) {
-      if ((schedule = schedules[i2]).name !== name2) {
+      if ((schedule = schedules[i2]).name !== name) {
         empty2 = false;
         continue;
       }
   }
 
   // node_modules/d3-transition/src/selection/interrupt.js
-  function interrupt_default2(name2) {
+  function interrupt_default2(name) {
     return this.each(function() {
-      interrupt_default(this, name2);
+      interrupt_default(this, name);
     });
   }
 
   // node_modules/d3-transition/src/transition/tween.js
-  function tweenRemove(id2, name2) {
+  function tweenRemove(id2, name) {
     var tween0, tween1;
     return function() {
       var schedule = set2(this, id2), tween = schedule.tween;
       if (tween !== tween0) {
         tween1 = tween0 = tween;
         for (var i2 = 0, n2 = tween1.length; i2 < n2; ++i2) {
-          if (tween1[i2].name === name2) {
+          if (tween1[i2].name === name) {
             tween1 = tween1.slice();
             tween1.splice(i2, 1);
             break;
       schedule.tween = tween1;
     };
   }
-  function tweenFunction(id2, name2, value) {
+  function tweenFunction(id2, name, value) {
     var tween0, tween1;
     if (typeof value !== "function")
       throw new Error();
       var schedule = set2(this, id2), tween = schedule.tween;
       if (tween !== tween0) {
         tween1 = (tween0 = tween).slice();
-        for (var t = { name: name2, value }, i2 = 0, n2 = tween1.length; i2 < n2; ++i2) {
-          if (tween1[i2].name === name2) {
+        for (var t = { name, value }, i2 = 0, n2 = tween1.length; i2 < n2; ++i2) {
+          if (tween1[i2].name === name) {
             tween1[i2] = t;
             break;
           }
       schedule.tween = tween1;
     };
   }
-  function tween_default(name2, value) {
+  function tween_default(name, value) {
     var id2 = this._id;
-    name2 += "";
+    name += "";
     if (arguments.length < 2) {
       var tween = get2(this.node(), id2).tween;
       for (var i2 = 0, n2 = tween.length, t; i2 < n2; ++i2) {
-        if ((t = tween[i2]).name === name2) {
+        if ((t = tween[i2]).name === name) {
           return t.value;
         }
       }
       return null;
     }
-    return this.each((value == null ? tweenRemove : tweenFunction)(id2, name2, value));
+    return this.each((value == null ? tweenRemove : tweenFunction)(id2, name, value));
   }
-  function tweenValue(transition2, name2, value) {
+  function tweenValue(transition2, name, value) {
     var id2 = transition2._id;
     transition2.each(function() {
       var schedule = set2(this, id2);
-      (schedule.value || (schedule.value = {}))[name2] = value.apply(this, arguments);
+      (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments);
     });
     return function(node) {
-      return get2(node, id2).value[name2];
+      return get2(node, id2).value[name];
     };
   }
 
   }
 
   // node_modules/d3-transition/src/transition/attr.js
-  function attrRemove2(name2) {
+  function attrRemove2(name) {
     return function() {
-      this.removeAttribute(name2);
+      this.removeAttribute(name);
     };
   }
   function attrRemoveNS2(fullname) {
       this.removeAttributeNS(fullname.space, fullname.local);
     };
   }
-  function attrConstant2(name2, interpolate, value1) {
+  function attrConstant2(name, interpolate, value1) {
     var string00, string1 = value1 + "", interpolate0;
     return function() {
-      var string0 = this.getAttribute(name2);
+      var string0 = this.getAttribute(name);
       return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
     };
   }
       return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
     };
   }
-  function attrFunction2(name2, interpolate, value) {
+  function attrFunction2(name, interpolate, value) {
     var string00, string10, interpolate0;
     return function() {
       var string0, value1 = value(this), string1;
       if (value1 == null)
-        return void this.removeAttribute(name2);
-      string0 = this.getAttribute(name2);
+        return void this.removeAttribute(name);
+      string0 = this.getAttribute(name);
       string1 = value1 + "";
       return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
     };
       return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
     };
   }
-  function attr_default2(name2, value) {
-    var fullname = namespace_default(name2), i2 = fullname === "transform" ? interpolateTransformSvg : interpolate_default;
-    return this.attrTween(name2, typeof value === "function" ? (fullname.local ? attrFunctionNS2 : attrFunction2)(fullname, i2, tweenValue(this, "attr." + name2, value)) : value == null ? (fullname.local ? attrRemoveNS2 : attrRemove2)(fullname) : (fullname.local ? attrConstantNS2 : attrConstant2)(fullname, i2, value));
+  function attr_default2(name, value) {
+    var fullname = namespace_default(name), i2 = fullname === "transform" ? interpolateTransformSvg : interpolate_default;
+    return this.attrTween(name, typeof value === "function" ? (fullname.local ? attrFunctionNS2 : attrFunction2)(fullname, i2, tweenValue(this, "attr." + name, value)) : value == null ? (fullname.local ? attrRemoveNS2 : attrRemove2)(fullname) : (fullname.local ? attrConstantNS2 : attrConstant2)(fullname, i2, value));
   }
 
   // node_modules/d3-transition/src/transition/attrTween.js
-  function attrInterpolate(name2, i2) {
+  function attrInterpolate(name, i2) {
     return function(t) {
-      this.setAttribute(name2, i2.call(this, t));
+      this.setAttribute(name, i2.call(this, t));
     };
   }
   function attrInterpolateNS(fullname, i2) {
     tween._value = value;
     return tween;
   }
-  function attrTween(name2, value) {
+  function attrTween(name, value) {
     var t0, i0;
     function tween() {
       var i2 = value.apply(this, arguments);
       if (i2 !== i0)
-        t0 = (i0 = i2) && attrInterpolate(name2, i2);
+        t0 = (i0 = i2) && attrInterpolate(name, i2);
       return t0;
     }
     tween._value = value;
     return tween;
   }
-  function attrTween_default(name2, value) {
-    var key = "attr." + name2;
+  function attrTween_default(name, value) {
+    var key = "attr." + name;
     if (arguments.length < 2)
       return (key = this.tween(key)) && key._value;
     if (value == null)
       return this.tween(key, null);
     if (typeof value !== "function")
       throw new Error();
-    var fullname = namespace_default(name2);
+    var fullname = namespace_default(name);
     return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));
   }
 
   }
 
   // node_modules/d3-transition/src/transition/on.js
-  function start(name2) {
-    return (name2 + "").trim().split(/^|\s+/).every(function(t) {
+  function start(name) {
+    return (name + "").trim().split(/^|\s+/).every(function(t) {
       var i2 = t.indexOf(".");
       if (i2 >= 0)
         t = t.slice(0, i2);
       return !t || t === "start";
     });
   }
-  function onFunction(id2, name2, listener) {
-    var on0, on1, sit = start(name2) ? init : set2;
+  function onFunction(id2, name, listener) {
+    var on0, on1, sit = start(name) ? init : set2;
     return function() {
       var schedule = sit(this, id2), on = schedule.on;
       if (on !== on0)
-        (on1 = (on0 = on).copy()).on(name2, listener);
+        (on1 = (on0 = on).copy()).on(name, listener);
       schedule.on = on1;
     };
   }
-  function on_default2(name2, listener) {
+  function on_default2(name, listener) {
     var id2 = this._id;
-    return arguments.length < 2 ? get2(this.node(), id2).on.on(name2) : this.each(onFunction(id2, name2, listener));
+    return arguments.length < 2 ? get2(this.node(), id2).on.on(name) : this.each(onFunction(id2, name, listener));
   }
 
   // node_modules/d3-transition/src/transition/remove.js
 
   // node_modules/d3-transition/src/transition/select.js
   function select_default3(select) {
-    var name2 = this._name, id2 = this._id;
+    var name = this._name, id2 = this._id;
     if (typeof select !== "function")
       select = selector_default(select);
     for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j2 = 0; j2 < m; ++j2) {
           if ("__data__" in node)
             subnode.__data__ = node.__data__;
           subgroup[i2] = subnode;
-          schedule_default(subgroup[i2], name2, id2, i2, subgroup, get2(node, id2));
+          schedule_default(subgroup[i2], name, id2, i2, subgroup, get2(node, id2));
         }
       }
     }
-    return new Transition(subgroups, this._parents, name2, id2);
+    return new Transition(subgroups, this._parents, name, id2);
   }
 
   // node_modules/d3-transition/src/transition/selectAll.js
   function selectAll_default3(select) {
-    var name2 = this._name, id2 = this._id;
+    var name = this._name, id2 = this._id;
     if (typeof select !== "function")
       select = selectorAll_default(select);
     for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j2 = 0; j2 < m; ++j2) {
         if (node = group[i2]) {
           for (var children2 = select.call(node, node.__data__, i2, group), child, inherit2 = get2(node, id2), k = 0, l = children2.length; k < l; ++k) {
             if (child = children2[k]) {
-              schedule_default(child, name2, id2, k, children2, inherit2);
+              schedule_default(child, name, id2, k, children2, inherit2);
             }
           }
           subgroups.push(children2);
         }
       }
     }
-    return new Transition(subgroups, parents, name2, id2);
+    return new Transition(subgroups, parents, name, id2);
   }
 
   // node_modules/d3-transition/src/transition/selection.js
   }
 
   // node_modules/d3-transition/src/transition/style.js
-  function styleNull(name2, interpolate) {
+  function styleNull(name, interpolate) {
     var string00, string10, interpolate0;
     return function() {
-      var string0 = styleValue(this, name2), string1 = (this.style.removeProperty(name2), styleValue(this, name2));
+      var string0 = styleValue(this, name), string1 = (this.style.removeProperty(name), styleValue(this, name));
       return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : interpolate0 = interpolate(string00 = string0, string10 = string1);
     };
   }
-  function styleRemove2(name2) {
+  function styleRemove2(name) {
     return function() {
-      this.style.removeProperty(name2);
+      this.style.removeProperty(name);
     };
   }
-  function styleConstant2(name2, interpolate, value1) {
+  function styleConstant2(name, interpolate, value1) {
     var string00, string1 = value1 + "", interpolate0;
     return function() {
-      var string0 = styleValue(this, name2);
+      var string0 = styleValue(this, name);
       return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
     };
   }
-  function styleFunction2(name2, interpolate, value) {
+  function styleFunction2(name, interpolate, value) {
     var string00, string10, interpolate0;
     return function() {
-      var string0 = styleValue(this, name2), value1 = value(this), string1 = value1 + "";
+      var string0 = styleValue(this, name), value1 = value(this), string1 = value1 + "";
       if (value1 == null)
-        string1 = value1 = (this.style.removeProperty(name2), styleValue(this, name2));
+        string1 = value1 = (this.style.removeProperty(name), styleValue(this, name));
       return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
     };
   }
-  function styleMaybeRemove(id2, name2) {
-    var on0, on1, listener0, key = "style." + name2, event = "end." + key, remove2;
+  function styleMaybeRemove(id2, name) {
+    var on0, on1, listener0, key = "style." + name, event = "end." + key, remove2;
     return function() {
-      var schedule = set2(this, id2), on = schedule.on, listener = schedule.value[key] == null ? remove2 || (remove2 = styleRemove2(name2)) : void 0;
+      var schedule = set2(this, id2), on = schedule.on, listener = schedule.value[key] == null ? remove2 || (remove2 = styleRemove2(name)) : void 0;
       if (on !== on0 || listener0 !== listener)
         (on1 = (on0 = on).copy()).on(event, listener0 = listener);
       schedule.on = on1;
     };
   }
-  function style_default2(name2, value, priority) {
-    var i2 = (name2 += "") === "transform" ? interpolateTransformCss : interpolate_default;
-    return value == null ? this.styleTween(name2, styleNull(name2, i2)).on("end.style." + name2, styleRemove2(name2)) : typeof value === "function" ? this.styleTween(name2, styleFunction2(name2, i2, tweenValue(this, "style." + name2, value))).each(styleMaybeRemove(this._id, name2)) : this.styleTween(name2, styleConstant2(name2, i2, value), priority).on("end.style." + name2, null);
+  function style_default2(name, value, priority) {
+    var i2 = (name += "") === "transform" ? interpolateTransformCss : interpolate_default;
+    return value == null ? this.styleTween(name, styleNull(name, i2)).on("end.style." + name, styleRemove2(name)) : typeof value === "function" ? this.styleTween(name, styleFunction2(name, i2, tweenValue(this, "style." + name, value))).each(styleMaybeRemove(this._id, name)) : this.styleTween(name, styleConstant2(name, i2, value), priority).on("end.style." + name, null);
   }
 
   // node_modules/d3-transition/src/transition/styleTween.js
-  function styleInterpolate(name2, i2, priority) {
+  function styleInterpolate(name, i2, priority) {
     return function(t) {
-      this.style.setProperty(name2, i2.call(this, t), priority);
+      this.style.setProperty(name, i2.call(this, t), priority);
     };
   }
-  function styleTween(name2, value, priority) {
+  function styleTween(name, value, priority) {
     var t, i0;
     function tween() {
       var i2 = value.apply(this, arguments);
       if (i2 !== i0)
-        t = (i0 = i2) && styleInterpolate(name2, i2, priority);
+        t = (i0 = i2) && styleInterpolate(name, i2, priority);
       return t;
     }
     tween._value = value;
     return tween;
   }
-  function styleTween_default(name2, value, priority) {
-    var key = "style." + (name2 += "");
+  function styleTween_default(name, value, priority) {
+    var key = "style." + (name += "");
     if (arguments.length < 2)
       return (key = this.tween(key)) && key._value;
     if (value == null)
       return this.tween(key, null);
     if (typeof value !== "function")
       throw new Error();
-    return this.tween(key, styleTween(name2, value, priority == null ? "" : priority));
+    return this.tween(key, styleTween(name, value, priority == null ? "" : priority));
   }
 
   // node_modules/d3-transition/src/transition/text.js
 
   // node_modules/d3-transition/src/transition/transition.js
   function transition_default() {
-    var name2 = this._name, id0 = this._id, id1 = newId();
+    var name = this._name, id0 = this._id, id1 = newId();
     for (var groups = this._groups, m = groups.length, j2 = 0; j2 < m; ++j2) {
       for (var group = groups[j2], n2 = group.length, node, i2 = 0; i2 < n2; ++i2) {
         if (node = group[i2]) {
           var inherit2 = get2(node, id0);
-          schedule_default(node, name2, id1, i2, group, {
+          schedule_default(node, name, id1, i2, group, {
             time: inherit2.time + inherit2.delay + inherit2.duration,
             delay: 0,
             duration: inherit2.duration,
         }
       }
     }
-    return new Transition(groups, this._parents, name2, id1);
+    return new Transition(groups, this._parents, name, id1);
   }
 
   // node_modules/d3-transition/src/transition/end.js
 
   // node_modules/d3-transition/src/transition/index.js
   var id = 0;
-  function Transition(groups, parents, name2, id2) {
+  function Transition(groups, parents, name, id2) {
     this._groups = groups;
     this._parents = parents;
-    this._name = name2;
+    this._name = name;
     this._id = id2;
   }
-  function transition(name2) {
-    return selection_default().transition(name2);
+  function transition(name) {
+    return selection_default().transition(name);
   }
   function newId() {
     return ++id;
   // node_modules/d3-transition/src/selection/transition.js
   var defaultTiming = {
     time: null,
+    // Set on use.
     delay: 0,
     duration: 250,
     ease: cubicInOut
     }
     return timing;
   }
-  function transition_default2(name2) {
+  function transition_default2(name) {
     var id2, timing;
-    if (name2 instanceof Transition) {
-      id2 = name2._id, name2 = name2._name;
+    if (name instanceof Transition) {
+      id2 = name._id, name = name._name;
     } else {
-      id2 = newId(), (timing = defaultTiming).time = now(), name2 = name2 == null ? null : name2 + "";
+      id2 = newId(), (timing = defaultTiming).time = now(), name = name == null ? null : name + "";
     }
     for (var groups = this._groups, m = groups.length, j2 = 0; j2 < m; ++j2) {
       for (var group = groups[j2], n2 = group.length, node, i2 = 0; i2 < n2; ++i2) {
         if (node = group[i2]) {
-          schedule_default(node, name2, id2, i2, group, timing || inherit(node, id2));
+          schedule_default(node, name, id2, i2, group, timing || inherit(node, id2));
         }
       }
     }
-    return new Transition(groups, this._parents, name2, id2);
+    return new Transition(groups, this._parents, name, id2);
   }
 
   // node_modules/d3-transition/src/selection/index.js
   var constant_default4 = (x) => () => x;
 
   // node_modules/d3-zoom/src/event.js
-  function ZoomEvent(type3, {
+  function ZoomEvent(type2, {
     sourceEvent,
     target,
     transform: transform2,
     dispatch: dispatch10
   }) {
     Object.defineProperties(this, {
-      type: { value: type3, enumerable: true, configurable: true },
+      type: { value: type2, enumerable: true, configurable: true },
       sourceEvent: { value: sourceEvent, enumerable: true, configurable: true },
       target: { value: target, enumerable: true, configurable: true },
       transform: { value: transform2, enumerable: true, configurable: true },
     translate: function(x, y) {
       return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y);
     },
-    apply: function(point) {
-      return [point[0] * this.k + this.x, point[1] * this.k + this.y];
+    apply: function(point2) {
+      return [point2[0] * this.k + this.x, point2[1] * this.k + this.y];
     },
     applyX: function(x) {
       return x * this.k + this.x;
   }
   function defaultConstrain(transform2, extent, translateExtent) {
     var dx0 = transform2.invertX(extent[0][0]) - translateExtent[0][0], dx1 = transform2.invertX(extent[1][0]) - translateExtent[1][0], dy0 = transform2.invertY(extent[0][1]) - translateExtent[0][1], dy1 = transform2.invertY(extent[1][1]) - translateExtent[1][1];
-    return transform2.translate(dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1), dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1));
+    return transform2.translate(
+      dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),
+      dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)
+    );
   }
   function zoom_default2() {
     var filter2 = defaultFilter2, extent = defaultExtent, constrain = defaultConstrain, wheelDelta = defaultWheelDelta, touchable = defaultTouchable2, scaleExtent = [0, Infinity], translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]], duration = 250, interpolate = zoom_default, listeners = dispatch_default("start", "zoom", "end"), touchstarting, touchfirst, touchending, touchDelay = 500, wheelDelay = 150, clickDistance2 = 0, tapDistance = 10;
     function zoom(selection2) {
       selection2.property("__zoom", defaultTransform).on("wheel.zoom", wheeled, { passive: false }).on("mousedown.zoom", mousedowned).on("dblclick.zoom", dblclicked).filter(touchable).on("touchstart.zoom", touchstarted).on("touchmove.zoom", touchmoved).on("touchend.zoom touchcancel.zoom", touchended).style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
     }
-    zoom.transform = function(collection, transform2, point, event) {
+    zoom.transform = function(collection, transform2, point2, event) {
       var selection2 = collection.selection ? collection.selection() : collection;
       selection2.property("__zoom", defaultTransform);
       if (collection !== selection2) {
-        schedule(collection, transform2, point, event);
+        schedule(collection, transform2, point2, event);
       } else {
         selection2.interrupt().each(function() {
           gesture(this, arguments).event(event).start().zoom(null, typeof transform2 === "function" ? transform2.apply(this, arguments) : transform2).end();
     };
     zoom.translateBy = function(selection2, x, y, event) {
       zoom.transform(selection2, function() {
-        return constrain(this.__zoom.translate(typeof x === "function" ? x.apply(this, arguments) : x, typeof y === "function" ? y.apply(this, arguments) : y), extent.apply(this, arguments), translateExtent);
+        return constrain(this.__zoom.translate(
+          typeof x === "function" ? x.apply(this, arguments) : x,
+          typeof y === "function" ? y.apply(this, arguments) : y
+        ), extent.apply(this, arguments), translateExtent);
       }, null, event);
     };
     zoom.translateTo = function(selection2, x, y, p, event) {
       zoom.transform(selection2, function() {
         var e = extent.apply(this, arguments), t = this.__zoom, p02 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p;
-        return constrain(identity2.translate(p02[0], p02[1]).scale(t.k).translate(typeof x === "function" ? -x.apply(this, arguments) : -x, typeof y === "function" ? -y.apply(this, arguments) : -y), e, translateExtent);
+        return constrain(identity2.translate(p02[0], p02[1]).scale(t.k).translate(
+          typeof x === "function" ? -x.apply(this, arguments) : -x,
+          typeof y === "function" ? -y.apply(this, arguments) : -y
+        ), e, translateExtent);
       }, p, event);
     };
     function scale(transform2, k) {
     function centroid(extent2) {
       return [(+extent2[0][0] + +extent2[1][0]) / 2, (+extent2[0][1] + +extent2[1][1]) / 2];
     }
-    function schedule(transition2, transform2, point, event) {
+    function schedule(transition2, transform2, point2, event) {
       transition2.on("start.zoom", function() {
         gesture(this, arguments).event(event).start();
       }).on("interrupt.zoom end.zoom", function() {
         gesture(this, arguments).event(event).end();
       }).tween("zoom", function() {
-        var that = this, args = arguments, g = gesture(that, args).event(event), e = extent.apply(that, args), p = point == null ? centroid(e) : typeof point === "function" ? point.apply(that, args) : point, w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]), a = that.__zoom, b = typeof transform2 === "function" ? transform2.apply(that, args) : transform2, i2 = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
+        var that = this, args = arguments, g = gesture(that, args).event(event), e = extent.apply(that, args), p = point2 == null ? centroid(e) : typeof point2 === "function" ? point2.apply(that, args) : point2, w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]), a = that.__zoom, b = typeof transform2 === "function" ? transform2.apply(that, args) : transform2, i2 = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
         return function(t) {
           if (t === 1)
             t = b;
         }
         return this;
       },
-      emit: function(type3) {
+      emit: function(type2) {
         var d = select_default2(this.that).datum();
-        listeners.call(type3, this.that, new ZoomEvent(type3, {
-          sourceEvent: this.sourceEvent,
-          target: zoom,
-          type: type3,
-          transform: this.that.__zoom,
-          dispatch: listeners
-        }), d);
+        listeners.call(
+          type2,
+          this.that,
+          new ZoomEvent(type2, {
+            sourceEvent: this.sourceEvent,
+            target: zoom,
+            type: type2,
+            transform: this.that.__zoom,
+            dispatch: listeners
+          }),
+          d
+        );
       }
     };
     function wheeled(event, ...args) {
     var x = 0;
     var y = 0;
     var clipExtent = [[0, 0], [0, 0]];
-    function projection2(point) {
-      point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
-      return [point[0] * k + x, y - point[1] * k];
+    function projection2(point2) {
+      point2 = project(point2[0] * Math.PI / 180, point2[1] * Math.PI / 180);
+      return [point2[0] * k + x, y - point2[1] * k];
     }
-    projection2.invert = function(point) {
-      point = project.invert((point[0] - x) / k, (y - point[1]) / k);
-      return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
+    projection2.invert = function(point2) {
+      point2 = project.invert((point2[0] - x) / k, (y - point2[1]) / k);
+      return point2 && [point2[0] * 180 / Math.PI, point2[1] * 180 / Math.PI];
     };
     projection2.scale = function(_) {
       if (!arguments.length)
   // modules/core/file_fetcher.js
   var import_vparse = __toESM(require_vparse());
 
-  // package.json
-  var name = "iD";
-  var version = "2.21.1";
-  var description = "A friendly editor for OpenStreetMap";
-  var main = "dist/iD.min.js";
-  var repository = "github:openstreetmap/iD";
-  var homepage = "https://github.com/openstreetmap/iD";
-  var bugs = "https://github.com/openstreetmap/iD/issues";
-  var keywords = [
-    "editor",
-    "openstreetmap"
-  ];
-  var license = "ISC";
-  var scripts = {
-    all: "run-s clean build dist",
-    build: "run-s build:css build:data build:js",
-    "build:css": "node scripts/build_css.js",
-    "build:data": "shx mkdir -p dist/data && node scripts/build_data.js",
-    "build:stats": "esbuild-visualizer --metadata dist/esbuild.json --exclude *.png --filename docs/statistics.html",
-    "build:js": "node config/esbuild.config.mjs",
-    "build:js:watch": "node config/esbuild.config.mjs --watch",
-    clean: "shx rm -f dist/esbuild.json dist/*.js dist/*.map dist/*.css dist/img/*.svg",
-    dist: "run-p dist:**",
-    "dist:mapillary": "shx mkdir -p dist/mapillary-js && shx cp -R node_modules/mapillary-js/dist/* dist/mapillary-js/",
-    "dist:pannellum": "shx mkdir -p dist/pannellum-streetside && shx cp -R node_modules/pannellum/build/* dist/pannellum-streetside/",
-    "dist:min": "node config/esbuild.config.min.mjs",
-    "dist:svg:iD": 'svg-sprite --symbol --symbol-dest . --shape-id-generator "iD-%s" --symbol-sprite dist/img/iD-sprite.svg "svg/iD-sprite/**/*.svg"',
-    "dist:svg:community": 'svg-sprite --symbol --symbol-dest . --shape-id-generator "community-%s" --symbol-sprite dist/img/community-sprite.svg node_modules/osm-community-index/dist/img/*.svg',
-    "dist:svg:fa": "svg-sprite --symbol --symbol-dest . --symbol-sprite dist/img/fa-sprite.svg svg/fontawesome/*.svg",
-    "dist:svg:maki": 'svg-sprite --symbol --symbol-dest . --shape-id-generator "maki-%s" --symbol-sprite dist/img/maki-sprite.svg node_modules/@mapbox/maki/icons/*.svg',
-    "dist:svg:mapillary:signs": "svg-sprite --symbol --symbol-dest . --symbol-sprite dist/img/mapillary-sprite.svg node_modules/mapillary_sprite_source/package_signs/*.svg",
-    "dist:svg:mapillary:objects": "svg-sprite --symbol --symbol-dest . --symbol-sprite dist/img/mapillary-object-sprite.svg node_modules/mapillary_sprite_source/package_objects/*.svg",
-    "dist:svg:temaki": 'svg-sprite --symbol --symbol-dest . --shape-id-generator "temaki-%s" --symbol-sprite dist/img/temaki-sprite.svg node_modules/@ideditor/temaki/icons/*.svg',
-    imagery: "node scripts/update_imagery.js",
-    lint: "eslint scripts test/spec modules",
-    "lint:fix": "eslint scripts test/spec modules --fix",
-    start: "run-s build:js start:server",
-    "start:watch": "run-p build:js:watch start:server",
-    "start:server": "node scripts/server.js",
-    test: "npm-run-all -s lint build test:spec",
-    "test:spec": "karma start karma.conf.js",
-    translations: "node scripts/update_locales.js"
-  };
-  var dependencies = {
-    "@ideditor/country-coder": "~5.0.3",
-    "@ideditor/location-conflation": "~1.0.2",
-    "@mapbox/geojson-area": "^0.2.2",
-    "@mapbox/sexagesimal": "1.2.0",
-    "@mapbox/vector-tile": "^1.3.1",
-    "@tmcw/togeojson": "^4.5.0",
-    "@turf/bbox-clip": "^6.0.0",
-    "abortcontroller-polyfill": "^1.4.0",
-    "aes-js": "^3.1.2",
-    "alif-toolkit": "^1.2.9",
-    "core-js-bundle": "^3.19.0",
-    diacritics: "1.3.0",
-    "fast-deep-equal": "~3.1.1",
-    "fast-json-stable-stringify": "2.1.0",
-    "lodash-es": "~4.17.15",
-    marked: "~4.0.12",
-    "node-diff3": "~3.1.0",
-    "osm-auth": "~2.0.0",
-    pannellum: "2.5.6",
-    pbf: "^3.2.1",
-    "polygon-clipping": "~0.15.1",
-    rbush: "3.0.1",
-    "whatwg-fetch": "^3.4.1",
-    "which-polygon": "2.2.0"
-  };
-  var devDependencies = {
-    "@fortawesome/fontawesome-svg-core": "~6.1.0",
-    "@fortawesome/free-brands-svg-icons": "~6.1.0",
-    "@fortawesome/free-regular-svg-icons": "~6.1.0",
-    "@fortawesome/free-solid-svg-icons": "~6.1.0",
-    "@ideditor/temaki": "~5.1.0",
-    "@mapbox/maki": "^7.1.0",
-    autoprefixer: "^10.0.1",
-    btoa: "^1.2.1",
-    chai: "^4.3.4",
-    "cldr-core": "37.0.0",
-    "cldr-localenames-full": "37.0.0",
-    chalk: "^4.1.2",
-    "concat-files": "^0.1.1",
-    d3: "~7.4.4",
-    "editor-layer-index": "github:osmlab/editor-layer-index#gh-pages",
-    esbuild: "^0.14.17",
-    "esbuild-visualizer": "^0.3.1",
-    eslint: "^8.8.0",
-    "fetch-mock": "^9.11.0",
-    gaze: "^1.1.3",
-    glob: "^8.0.3",
-    happen: "^0.3.2",
-    "js-yaml": "^4.0.0",
-    "json-stringify-pretty-compact": "^3.0.0",
-    karma: "^6.3.5",
-    "karma-chrome-launcher": "^3.1.0",
-    "karma-coverage": "2.1.1",
-    "karma-mocha": "^2.0.1",
-    "karma-remap-istanbul": "^0.6.0",
-    "mapillary-js": "4.1.0",
-    mapillary_sprite_source: "^1.8.0",
-    minimist: "^1.2.3",
-    mocha: "^9.2.0",
-    "name-suggestion-index": "~6.0",
-    "node-fetch": "^2.6.1",
-    "npm-run-all": "^4.0.0",
-    "osm-community-index": "~5.1.0",
-    postcss: "^8.1.1",
-    "postcss-selector-prepend": "^0.5.0",
-    shelljs: "^0.8.0",
-    shx: "^0.3.0",
-    sinon: "^11.1.2",
-    "sinon-chai": "^3.7.0",
-    smash: "0.0",
-    "static-server": "^2.2.1",
-    "svg-sprite": "1.5.4",
-    vparse: "~1.1.0"
-  };
-  var engines = {
-    node: ">=12"
-  };
-  var browserslist = [
-    "> 0.2%, last 6 major versions, Firefox ESR, maintained node versions"
+  // config/id.js
+  var presetsCdnUrl = "https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@{presets_version}/";
+  var ociCdnUrl = "https://cdn.jsdelivr.net/npm/osm-community-index@{version}/";
+  var wmfSitematrixCdnUrl = "https://cdn.jsdelivr.net/npm/wmf-sitematrix@{version}/";
+  var nsiCdnUrl = "https://cdn.jsdelivr.net/npm/name-suggestion-index@{version}/";
+  var osmApiConnections = [
+    {
+      // "live" db
+      url: "https://www.openstreetmap.org",
+      client_id: "0tmNTmd0Jo1dQp4AUmMBLtGiD9YpMuXzHefitcuVStc",
+      client_secret: "BTlNrNxIPitHdL4sP2clHw5KLoee9aKkA7dQbc0Bj7Q"
+    },
+    {
+      // "dev" db
+      url: "https://api06.dev.openstreetmap.org",
+      client_id: "Ee1wWJ6UlpERbF6BfTNOpwn0R8k_06mvMXdDUkeHMgw",
+      client_secret: "OnfWFC-JkZNHyYdr_viNn_h_RTZXRslKcUxllOXqf5g"
+    }
   ];
+  var taginfoApiUrl = "https://taginfo.openstreetmap.org/api/4/";
+  var nominatimApiUrl = "https://nominatim.openstreetmap.org/";
+
+  // package.json
   var package_default = {
-    name,
-    version,
-    description,
-    main,
-    repository,
-    homepage,
-    bugs,
-    keywords,
-    license,
-    scripts,
-    dependencies,
-    devDependencies,
-    engines,
-    browserslist
+    name: "iD",
+    version: "2.24.1",
+    description: "A friendly editor for OpenStreetMap",
+    main: "dist/iD.min.js",
+    repository: "github:openstreetmap/iD",
+    homepage: "https://github.com/openstreetmap/iD",
+    bugs: "https://github.com/openstreetmap/iD/issues",
+    keywords: [
+      "editor",
+      "openstreetmap"
+    ],
+    license: "ISC",
+    scripts: {
+      all: "run-s clean build dist",
+      build: "run-s build:css build:data build:js",
+      "build:css": "node scripts/build_css.js",
+      "build:data": "shx mkdir -p dist/data && node scripts/build_data.js",
+      "build:stats": "node config/esbuild.config.mjs --stats && esbuild-visualizer --metadata dist/esbuild.json --exclude *.png --filename docs/statistics.html && shx rm dist/esbuild.json",
+      "build:js": "node config/esbuild.config.mjs",
+      "build:js:watch": "node config/esbuild.config.mjs --watch",
+      clean: "shx rm -f dist/esbuild.json dist/*.js dist/*.map dist/*.css dist/img/*.svg",
+      dist: "run-p dist:**",
+      "dist:mapillary": "shx mkdir -p dist/mapillary-js && shx cp -R node_modules/mapillary-js/dist/* dist/mapillary-js/",
+      "dist:pannellum": "shx mkdir -p dist/pannellum-streetside && shx cp -R node_modules/pannellum/build/* dist/pannellum-streetside/",
+      "dist:min": "node config/esbuild.config.min.mjs",
+      "dist:svg:iD": 'svg-sprite --symbol --symbol-dest . --shape-id-generator "iD-%s" --symbol-sprite dist/img/iD-sprite.svg "svg/iD-sprite/**/*.svg"',
+      "dist:svg:community": 'svg-sprite --symbol --symbol-dest . --shape-id-generator "community-%s" --symbol-sprite dist/img/community-sprite.svg node_modules/osm-community-index/dist/img/*.svg',
+      "dist:svg:fa": "svg-sprite --symbol --symbol-dest . --symbol-sprite dist/img/fa-sprite.svg svg/fontawesome/*.svg",
+      "dist:svg:maki": 'svg-sprite --symbol --symbol-dest . --shape-id-generator "maki-%s" --symbol-sprite dist/img/maki-sprite.svg node_modules/@mapbox/maki/icons/*.svg',
+      "dist:svg:mapillary:signs": "svg-sprite --symbol --symbol-dest . --symbol-sprite dist/img/mapillary-sprite.svg node_modules/mapillary_sprite_source/package_signs/*.svg",
+      "dist:svg:mapillary:objects": "svg-sprite --symbol --symbol-dest . --symbol-sprite dist/img/mapillary-object-sprite.svg node_modules/mapillary_sprite_source/package_objects/*.svg",
+      "dist:svg:temaki": 'svg-sprite --symbol --symbol-dest . --shape-id-generator "temaki-%s" --symbol-sprite dist/img/temaki-sprite.svg node_modules/@ideditor/temaki/icons/*.svg',
+      imagery: "node scripts/update_imagery.js",
+      lint: "eslint scripts test/spec modules",
+      "lint:fix": "eslint scripts test/spec modules --fix",
+      start: "run-s build:js start:server",
+      "start:watch": "run-p build:js:watch start:server",
+      "start:server": "node scripts/server.js",
+      test: "npm-run-all -s lint build test:spec",
+      "test:spec": "karma start config/karma.conf.js",
+      translations: "node scripts/update_locales.js"
+    },
+    dependencies: {
+      "@ideditor/country-coder": "~5.1.0",
+      "@ideditor/location-conflation": "~1.1.0",
+      "@mapbox/geojson-area": "^0.2.2",
+      "@mapbox/sexagesimal": "1.2.0",
+      "@mapbox/vector-tile": "^1.3.1",
+      "@tmcw/togeojson": "^5.2.1",
+      "@turf/bbox": "^6.0.0",
+      "@turf/bbox-clip": "^6.0.0",
+      "abortcontroller-polyfill": "^1.4.0",
+      "aes-js": "^3.1.2",
+      "alif-toolkit": "^1.2.9",
+      "core-js-bundle": "^3.19.0",
+      diacritics: "1.3.0",
+      "fast-deep-equal": "~3.1.1",
+      "fast-json-stable-stringify": "2.1.0",
+      "lodash-es": "~4.17.15",
+      marked: "~4.2.2",
+      "node-diff3": "~3.1.0",
+      "osm-auth": "~2.0.0",
+      pannellum: "2.5.6",
+      pbf: "^3.2.1",
+      "polygon-clipping": "~0.15.1",
+      rbush: "3.0.1",
+      "whatwg-fetch": "^3.4.1",
+      "which-polygon": "2.2.0"
+    },
+    devDependencies: {
+      "@fortawesome/fontawesome-svg-core": "~6.2.0",
+      "@fortawesome/free-brands-svg-icons": "~6.2.0",
+      "@fortawesome/free-regular-svg-icons": "~6.2.0",
+      "@fortawesome/free-solid-svg-icons": "~6.2.0",
+      "@ideditor/temaki": "~5.2.0",
+      "@mapbox/maki": "^8.0.0",
+      "@openstreetmap/id-tagging-schema": "^5.0.1",
+      "@transifex/api": "^5.0.1",
+      autoprefixer: "^10.0.1",
+      chai: "^4.3.4",
+      chalk: "^4.1.2",
+      "cldr-core": "^41.0.0",
+      "cldr-localenames-full": "^41.0.0",
+      "concat-files": "^0.1.1",
+      d3: "~7.8.1",
+      "editor-layer-index": "github:osmlab/editor-layer-index#gh-pages",
+      esbuild: "^0.17.3",
+      "esbuild-visualizer": "^0.4.0",
+      eslint: "^8.8.0",
+      "fetch-mock": "^9.11.0",
+      gaze: "^1.1.3",
+      glob: "^8.0.3",
+      happen: "^0.3.2",
+      "js-yaml": "^4.0.0",
+      "json-stringify-pretty-compact": "^3.0.0",
+      karma: "^6.3.5",
+      "karma-chrome-launcher": "^3.1.0",
+      "karma-coverage": "2.1.1",
+      "karma-mocha": "^2.0.1",
+      "karma-remap-istanbul": "^0.6.0",
+      mapillary_sprite_source: "^1.8.0",
+      "mapillary-js": "4.1.1",
+      minimist: "^1.2.3",
+      mocha: "^10.0.0",
+      "name-suggestion-index": "~6.0",
+      "node-fetch": "^2.6.1",
+      "npm-run-all": "^4.0.0",
+      "osm-community-index": "~5.5.0",
+      postcss: "^8.1.1",
+      "postcss-selector-prepend": "^0.5.0",
+      shelljs: "^0.8.0",
+      shx: "^0.3.0",
+      sinon: "^11.1.2",
+      "sinon-chai": "^3.7.0",
+      smash: "0.0",
+      "static-server": "^2.2.1",
+      "svg-sprite": "2.0.2",
+      vparse: "~1.1.0"
+    },
+    engines: {
+      node: ">=16.14"
+    },
+    browserslist: [
+      "> 0.2%, last 6 major versions, Firefox ESR, maintained node versions"
+    ]
   };
 
   // modules/core/file_fetcher.js
   function coreFileFetcher() {
     const ociVersion = package_default.dependencies["osm-community-index"] || package_default.devDependencies["osm-community-index"];
     const v = (0, import_vparse.default)(ociVersion);
-    const vMinor = `${v.major}.${v.minor}`;
+    const ociVersionMinor = `${v.major}.${v.minor}`;
+    const presetsVersion = package_default.devDependencies["@openstreetmap/id-tagging-schema"];
     let _this = {};
     let _inflight4 = {};
     let _fileMap = {
       "address_formats": "data/address_formats.min.json",
-      "deprecated": "https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/deprecated.min.json",
-      "discarded": "https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/discarded.min.json",
       "imagery": "data/imagery.min.json",
       "intro_graph": "data/intro_graph.min.json",
       "keepRight": "data/keepRight.min.json",
       "languages": "data/languages.min.json",
       "locales": "locales/index.min.json",
-      "oci_defaults": `https://cdn.jsdelivr.net/npm/osm-community-index@${vMinor}/dist/defaults.min.json`,
-      "oci_features": `https://cdn.jsdelivr.net/npm/osm-community-index@${vMinor}/dist/featureCollection.min.json`,
-      "oci_resources": `https://cdn.jsdelivr.net/npm/osm-community-index@${vMinor}/dist/resources.min.json`,
-      "preset_categories": "https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/preset_categories.min.json",
-      "preset_defaults": "https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/preset_defaults.min.json",
-      "preset_fields": "https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/fields.min.json",
-      "preset_presets": "https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/presets.min.json",
       "phone_formats": "data/phone_formats.min.json",
       "qa_data": "data/qa_data.min.json",
       "shortcuts": "data/shortcuts.min.json",
       "territory_languages": "data/territory_languages.min.json",
-      "wmf_sitematrix": "https://cdn.jsdelivr.net/npm/wmf-sitematrix@0.1/wikipedia.min.json"
+      "oci_defaults": ociCdnUrl.replace("{version}", ociVersionMinor) + "dist/defaults.min.json",
+      "oci_features": ociCdnUrl.replace("{version}", ociVersionMinor) + "dist/featureCollection.min.json",
+      "oci_resources": ociCdnUrl.replace("{version}", ociVersionMinor) + "dist/resources.min.json",
+      "presets_package": presetsCdnUrl.replace("{presets_version}", presetsVersion) + "package.json",
+      "deprecated": presetsCdnUrl + "dist/deprecated.min.json",
+      "discarded": presetsCdnUrl + "dist/discarded.min.json",
+      "preset_categories": presetsCdnUrl + "dist/preset_categories.min.json",
+      "preset_defaults": presetsCdnUrl + "dist/preset_defaults.min.json",
+      "preset_fields": presetsCdnUrl + "dist/fields.min.json",
+      "preset_presets": presetsCdnUrl + "dist/presets.min.json",
+      "wmf_sitematrix": wmfSitematrixCdnUrl.replace("{version}", "0.1") + "wikipedia.min.json"
     };
     let _cachedData = {};
     _this.cache = () => _cachedData;
       if (!url) {
         return Promise.reject(`Unknown data file for "${which}"`);
       }
+      if (url.includes("{presets_version}")) {
+        return _this.get("presets_package").then((result) => {
+          const presetsVersion2 = result.version;
+          return getUrl(url.replace("{presets_version}", presetsVersion2), which);
+        });
+      } else {
+        return getUrl(url);
+      }
+    };
+    function getUrl(url, which) {
       let prom = _inflight4[url];
       if (!prom) {
         _inflight4[url] = prom = fetch(url).then((response) => {
         });
       }
       return prom;
-    };
+    }
     _this.fileMap = function(val) {
       if (!arguments.length)
         return _fileMap;
 
   // node_modules/@ideditor/country-coder/dist/country-coder.mjs
   var import_which_polygon = __toESM(require_which_polygon(), 1);
-  var type = "FeatureCollection";
-  var features = [
+  var borders_default = { type: "FeatureCollection", features: [
     { type: "Feature", properties: { wikidata: "Q21", nameEn: "England", aliases: ["GB-ENG"], country: "GB", groups: ["Q23666", "Q3336843", "154", "150", "UN"], driveSide: "left", roadSpeedUnit: "mph", roadHeightUnit: "ft", callingCodes: ["44"] }, geometry: { type: "MultiPolygon", coordinates: [[[[-6.03913, 51.13217], [-7.74976, 48.64773], [1.17405, 50.74239], [2.18458, 51.52087], [2.56575, 51.85301], [0.792, 57.56437], [-2.30613, 55.62698], [-2.17058, 55.45916], [-2.6095, 55.28488], [-2.63532, 55.19452], [-3.02906, 55.04606], [-3.09361, 54.94924], [-3.38407, 54.94278], [-4.1819, 54.57861], [-3.5082, 53.54318], [-3.08228, 53.25526], [-3.03675, 53.25092], [-2.92329, 53.19383], [-2.92022, 53.17685], [-2.98598, 53.15589], [-2.90649, 53.10964], [-2.87469, 53.12337], [-2.89131, 53.09374], [-2.83133, 52.99184], [-2.7251, 52.98389], [-2.72221, 52.92969], [-2.80549, 52.89428], [-2.85897, 52.94487], [-2.92401, 52.93836], [-2.97243, 52.9651], [-3.13576, 52.895], [-3.15744, 52.84947], [-3.16105, 52.79599], [-3.08734, 52.77504], [-3.01001, 52.76636], [-2.95581, 52.71794], [-3.01724, 52.72083], [-3.04398, 52.65435], [-3.13648, 52.58208], [-3.12926, 52.5286], [-3.09746, 52.53077], [-3.08662, 52.54811], [-3.00929, 52.57774], [-2.99701, 52.551], [-3.03603, 52.49969], [-3.13359, 52.49174], [-3.22971, 52.45344], [-3.22754, 52.42526], [-3.04687, 52.34504], [-2.95364, 52.3501], [-2.99701, 52.323], [-3.00785, 52.2753], [-3.09289, 52.20546], [-3.12638, 52.08114], [-2.97111, 51.90456], [-2.8818, 51.93196], [-2.78742, 51.88833], [-2.74277, 51.84367], [-2.66234, 51.83555], [-2.66336, 51.59504], [-3.20563, 51.31615], [-6.03913, 51.13217]]]] } },
     { type: "Feature", properties: { wikidata: "Q22", nameEn: "Scotland", aliases: ["GB-SCT"], country: "GB", groups: ["Q23666", "Q3336843", "154", "150", "UN"], driveSide: "left", roadSpeedUnit: "mph", roadHeightUnit: "ft", callingCodes: ["44"] }, geometry: { type: "MultiPolygon", coordinates: [[[[0.792, 57.56437], [-0.3751, 61.32236], [-14.78497, 57.60709], [-6.82333, 55.83103], [-4.69044, 54.3629], [-3.38407, 54.94278], [-3.09361, 54.94924], [-3.02906, 55.04606], [-2.63532, 55.19452], [-2.6095, 55.28488], [-2.17058, 55.45916], [-2.30613, 55.62698], [0.792, 57.56437]]]] } },
     { type: "Feature", properties: { wikidata: "Q25", nameEn: "Wales", aliases: ["GB-WLS"], country: "GB", groups: ["Q23666", "Q3336843", "154", "150", "UN"], driveSide: "left", roadSpeedUnit: "mph", roadHeightUnit: "ft", callingCodes: ["44"] }, geometry: { type: "MultiPolygon", coordinates: [[[[-3.5082, 53.54318], [-5.37267, 53.63269], [-6.03913, 51.13217], [-3.20563, 51.31615], [-2.66336, 51.59504], [-2.66234, 51.83555], [-2.74277, 51.84367], [-2.78742, 51.88833], [-2.8818, 51.93196], [-2.97111, 51.90456], [-3.12638, 52.08114], [-3.09289, 52.20546], [-3.00785, 52.2753], [-2.99701, 52.323], [-2.95364, 52.3501], [-3.04687, 52.34504], [-3.22754, 52.42526], [-3.22971, 52.45344], [-3.13359, 52.49174], [-3.03603, 52.49969], [-2.99701, 52.551], [-3.00929, 52.57774], [-3.08662, 52.54811], [-3.09746, 52.53077], [-3.12926, 52.5286], [-3.13648, 52.58208], [-3.04398, 52.65435], [-3.01724, 52.72083], [-2.95581, 52.71794], [-3.01001, 52.76636], [-3.08734, 52.77504], [-3.16105, 52.79599], [-3.15744, 52.84947], [-3.13576, 52.895], [-2.97243, 52.9651], [-2.92401, 52.93836], [-2.85897, 52.94487], [-2.80549, 52.89428], [-2.72221, 52.92969], [-2.7251, 52.98389], [-2.83133, 52.99184], [-2.89131, 53.09374], [-2.87469, 53.12337], [-2.90649, 53.10964], [-2.98598, 53.15589], [-2.92022, 53.17685], [-2.92329, 53.19383], [-3.03675, 53.25092], [-3.08228, 53.25526], [-3.5082, 53.54318]]]] } },
     { type: "Feature", properties: { iso1A2: "CD", iso1A3: "COD", iso1N3: "180", wikidata: "Q974", nameEn: "Democratic Republic of the Congo", aliases: ["ZR"], groups: ["017", "202", "002", "UN"], 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", "UN"], 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", "UN"], 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", "UN"], 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", 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.35512, 47.57014], [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]]]] }, properties: { iso1A2: "CH", iso1A3: "CHE", iso1N3: "756", wikidata: "Q39", nameEn: "Switzerland", groups: ["155", "150", "UN"], callingCodes: ["41"] } },
     { type: "Feature", properties: { iso1A2: "CI", iso1A3: "CIV", iso1N3: "384", wikidata: "Q1008", nameEn: "C\xF4te d'Ivoire", groups: ["011", "202", "002", "UN"], 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", "UN"], driveSide: "left", callingCodes: ["682"] }, geometry: { type: "MultiPolygon", coordinates: [[[[-168.15106, -10.26955], [-156.45576, -31.75456], [-156.48634, -15.52824], [-156.50903, -7.4975], [-168.15106, -10.26955]]]] } },
     { type: "Feature", properties: { iso1A2: "CL", iso1A3: "CHL", iso1N3: "152", wikidata: "Q298", nameEn: "Chile", groups: ["005", "419", "019", "UN"], 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: "HU", iso1A3: "HUN", iso1N3: "348", wikidata: "Q28", nameEn: "Hungary", groups: ["EU", "151", "150", "UN"], 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: ["Q3320166", "Q105472", "EU", "039", "150", "UN"], isoStatus: "excRes", callingCodes: ["34"] }, geometry: { type: "MultiPolygon", coordinates: [[[[-12.00985, 30.24121], [-25.3475, 27.87574], [-14.43883, 27.02969], [-12.00985, 30.24121]]]] } },
     { type: "Feature", properties: { iso1A2: "ID", iso1A3: "IDN", iso1N3: "360", wikidata: "Q252", nameEn: "Indonesia", aliases: ["RI"] }, geometry: null },
-    { type: "Feature", properties: { iso1A2: "IE", iso1A3: "IRL", iso1N3: "372", wikidata: "Q27", nameEn: "Republic of Ireland", groups: ["EU", "Q22890", "154", "150", "UN"], 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.34755, 55.49206], [-7.75229, 55.93854], [-22.01468, 48.19557], [-6.03913, 51.13217], [-5.37267, 53.63269], [-6.26218, 54.09785]]]] } },
+    { type: "Feature", 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.34755, 55.49206], [-7.75229, 55.93854], [-11.75, 54], [-11, 51], [-6.03913, 51.13217], [-5.37267, 53.63269], [-6.26218, 54.09785]]]] }, properties: { iso1A2: "IE", iso1A3: "IRL", iso1N3: "372", wikidata: "Q27", nameEn: "Republic of Ireland", groups: ["EU", "Q22890", "154", "150", "UN"], driveSide: "left", callingCodes: ["353"] } },
     { type: "Feature", properties: { iso1A2: "IL", iso1A3: "ISR", iso1N3: "376", wikidata: "Q801", nameEn: "Israel", groups: ["145", "142", "UN"], 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: ["Q185086", "154", "150", "UN"], driveSide: "left", roadSpeedUnit: "mph", roadHeightUnit: "ft", callingCodes: ["44 01624", "44 07624", "44 07524", "44 07924"] }, geometry: { type: "MultiPolygon", coordinates: [[[[-3.98763, 54.07351], [-4.1819, 54.57861], [-5.6384, 53.81157], [-3.98763, 54.07351]]]] } },
     { type: "Feature", properties: { iso1A2: "IN", iso1A3: "IND", iso1N3: "356", wikidata: "Q668", nameEn: "India" }, geometry: null },
     { type: "Feature", properties: { iso1A2: "ZA", iso1A3: "ZAF", iso1N3: "710", wikidata: "Q258", nameEn: "South Africa", groups: ["018", "202", "002", "UN"], 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", "UN"], 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", "UN"], 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]]]] } }
-  ];
-  var borders_default = { type, features };
+  ] };
   var borders = borders_default;
   var whichPolygonGetter = {};
   var featuresByCode = {};
           sharedGroups = sharedGroups.filter((groupID) => memberGroups.indexOf(groupID) !== -1);
         }
       });
-      props.groups = props.groups.concat(sharedGroups.filter((groupID) => props.groups.indexOf(groupID) === -1));
+      props.groups = props.groups.concat(
+        sharedGroups.filter((groupID) => props.groups.indexOf(groupID) === -1)
+      );
       sharedGroups.forEach((groupID) => {
         const groupFeature = featuresByCode[groupID];
         if (groupFeature.properties.members.indexOf(props.id) === -1) {
         if (!props.roadSpeedUnit)
           props.roadSpeedUnit = "km/h";
       } else if (props.members) {
-        const vals = Array.from(new Set(props.members.map((id2) => {
-          const member = featuresByCode[id2];
-          if (member.geometry)
-            return member.properties.roadSpeedUnit || "km/h";
-        }).filter(Boolean)));
+        const vals = Array.from(
+          new Set(
+            props.members.map((id2) => {
+              const member = featuresByCode[id2];
+              if (member.geometry)
+                return member.properties.roadSpeedUnit || "km/h";
+            }).filter(Boolean)
+          )
+        );
         if (vals.length === 1)
           props.roadSpeedUnit = vals[0];
       }
         if (!props.roadHeightUnit)
           props.roadHeightUnit = "m";
       } else if (props.members) {
-        const vals = Array.from(new Set(props.members.map((id2) => {
-          const member = featuresByCode[id2];
-          if (member.geometry)
-            return member.properties.roadHeightUnit || "m";
-        }).filter(Boolean)));
+        const vals = Array.from(
+          new Set(
+            props.members.map((id2) => {
+              const member = featuresByCode[id2];
+              if (member.geometry)
+                return member.properties.roadHeightUnit || "m";
+            }).filter(Boolean)
+          )
+        );
         if (vals.length === 1)
           props.roadHeightUnit = vals[0];
       }
         if (!props.driveSide)
           props.driveSide = "right";
       } else if (props.members) {
-        const vals = Array.from(new Set(props.members.map((id2) => {
-          const member = featuresByCode[id2];
-          if (member.geometry)
-            return member.properties.driveSide || "right";
-        }).filter(Boolean)));
+        const vals = Array.from(
+          new Set(
+            props.members.map((id2) => {
+              const member = featuresByCode[id2];
+              if (member.geometry)
+                return member.properties.driveSide || "right";
+            }).filter(Boolean)
+          )
+        );
         if (vals.length === 1)
           props.driveSide = vals[0];
       }
     function loadCallingCodes(feature22) {
       const props = feature22.properties;
       if (!feature22.geometry && props.members) {
-        props.callingCodes = Array.from(new Set(props.members.reduce((array2, id2) => {
-          const member = featuresByCode[id2];
-          if (member.geometry && member.properties.callingCodes) {
-            return array2.concat(member.properties.callingCodes);
-          }
-          return array2;
-        }, [])));
+        props.callingCodes = Array.from(
+          new Set(
+            props.members.reduce((array2, id2) => {
+              const member = featuresByCode[id2];
+              if (member.geometry && member.properties.callingCodes) {
+                return array2.concat(member.properties.callingCodes);
+              }
+              return array2;
+            }, [])
+          )
+        );
       }
     }
     function loadFlag(feature22) {
         }
       }
     }
-    const features2 = featuresContaining(loc);
-    const match = features2.find((feature22) => {
+    const features = featuresContaining(loc);
+    const match = features.find((feature22) => {
       let levelIndex = levels.indexOf(feature22.properties.level);
       if (feature22.properties.level === targetLevel || levelIndex > targetLevelIndex && levelIndex <= maxLevelIndex) {
         if (!withProp || feature22.properties[withProp]) {
     }
     return featuresByCode[stringID] || null;
   }
-  function smallestFeaturesForBbox(bbox) {
-    return whichPolygonGetter.bbox(bbox).map((props) => featuresByCode[props.id]);
+  function smallestFeaturesForBbox(bbox2) {
+    return whichPolygonGetter.bbox(bbox2).map((props) => featuresByCode[props.id]);
   }
   function smallestOrMatchingFeature(query) {
     if (typeof query === "object") {
     const feature22 = featureForID(id2);
     if (!feature22)
       return [];
-    let features2 = [];
+    let features = [];
     if (!strict) {
-      features2.push(feature22);
+      features.push(feature22);
     }
     const properties = feature22.properties;
     (properties.members || []).forEach((memberID) => {
-      features2.push(featuresByCode[memberID]);
+      features.push(featuresByCode[memberID]);
     });
-    return features2;
+    return features;
   }
   function aggregateFeature(id2) {
-    const features2 = featuresIn(id2, false);
-    if (features2.length === 0)
+    const features = featuresIn(id2, false);
+    if (features.length === 0)
       return null;
     let aggregateCoordinates = [];
-    features2.forEach((feature22) => {
+    features.forEach((feature22) => {
       if (feature22.geometry && feature22.geometry.type === "MultiPolygon" && feature22.geometry.coordinates) {
         aggregateCoordinates = aggregateCoordinates.concat(feature22.geometry.coordinates);
       }
     });
     return {
       type: "Feature",
-      properties: features2[0].properties,
+      properties: features[0].properties,
       geometry: {
         type: "MultiPolygon",
         coordinates: aggregateCoordinates
   var import_geojson_precision = __toESM(require_geojson_precision(), 1);
   var import_json_stringify_pretty_compact = __toESM(require_json_stringify_pretty_compact(), 1);
   var location_conflation_default = class {
+    // constructor
+    //
+    // `fc`  Optional FeatureCollection of known features
+    //
+    // Optionally pass a GeoJSON FeatureCollection of known features which we can refer to later.
+    // Each feature must have a filename-like `id`, for example: `something.geojson`
+    //
+    // {
+    //   "type": "FeatureCollection"
+    //   "features": [
+    //     {
+    //       "type": "Feature",
+    //       "id": "philly_metro.geojson",
+    //       "properties": { … },
+    //       "geometry": { … }
+    //     }
+    //   ]
+    // }
     constructor(fc) {
       this._cache = {};
       this._strict = true;
       world.properties.area = import_geojson_area.default.geometry(world.geometry) / 1e6;
       this._cache.Q2 = world;
     }
+    // validateLocation
+    // `location`  The location to validate
+    //
+    // Pass a `location` value to validate
+    //
+    // Returns a result like:
+    //   {
+    //     type:     'point', 'geojson', or 'countrycoder'
+    //     location:  the queried location
+    //     id:        the stable identifier for the feature
+    //   }
+    // or `null` if the location is invalid
+    //
     validateLocation(location) {
       if (Array.isArray(location) && (location.length === 2 || location.length === 3)) {
         const lon = location[0];
         return null;
       }
     }
+    // resolveLocation
+    // `location`  The location to resolve
+    //
+    // Pass a `location` value to resolve
+    //
+    // Returns a result like:
+    //   {
+    //     type:      'point', 'geojson', or 'countrycoder'
+    //     location:  the queried location
+    //     id:        a stable identifier for the feature
+    //     feature:   the resolved GeoJSON feature
+    //   }
+    //  or `null` if the location is invalid
+    //
     resolveLocation(location) {
       const valid = this.validateLocation(location);
       if (!valid)
           id: id2,
           properties: { id: id2, area: Number(area.toFixed(2)) },
           geometry: (0, import_circle_to_polygon.default)([lon, lat], radius * 1e3, EDGES)
+          // km to m
         }, PRECISION);
         return Object.assign(valid, { feature: feature3 });
       } else if (valid.type === "geojson") {
         return null;
       }
     }
+    // validateLocationSet
+    // `locationSet`  the locationSet to validate
+    //
+    // Pass a locationSet Object to validate like:
+    //   {
+    //     include: [ Array of locations ],
+    //     exclude: [ Array of locations ]
+    //   }
+    //
+    // Returns a result like:
+    //   {
+    //     type:         'locationset'
+    //     locationSet:  the queried locationSet
+    //     id:           the stable identifier for the feature
+    //   }
+    // or `null` if the locationSet is invalid
+    //
     validateLocationSet(locationSet) {
       locationSet = locationSet || {};
       const validator = this.validateLocation.bind(this);
       }
       return { type: "locationset", locationSet, id: id2 };
     }
+    // resolveLocationSet
+    // `locationSet`  the locationSet to resolve
+    //
+    // Pass a locationSet Object to validate like:
+    //   {
+    //     include: [ Array of locations ],
+    //     exclude: [ Array of locations ]
+    //   }
+    //
+    // Returns a result like:
+    //   {
+    //     type:         'locationset'
+    //     locationSet:  the queried locationSet
+    //     id:           the stable identifier for the feature
+    //     feature:      the resolved GeoJSON feature
+    //   }
+    // or `null` if the locationSet is invalid
+    //
     resolveLocationSet(locationSet) {
       locationSet = locationSet || {};
       const valid = this.validateLocationSet(locationSet);
       this._cache[id2] = resultGeoJSON;
       return Object.assign(valid, { feature: resultGeoJSON });
     }
+    // strict
+    //
     strict(val) {
       if (val === void 0) {
         return this._strict;
         return this;
       }
     }
+    // cache
+    // convenience method to access the internal cache
     cache() {
       return this._cache;
     }
+    // stringify
+    // convenience method to prettyStringify the given object
     stringify(obj, options2) {
       return (0, import_json_stringify_pretty_compact.default)(obj, options2);
     }
   };
-  function _clip(features2, which) {
-    if (!Array.isArray(features2) || !features2.length)
+  function _clip(features, which) {
+    if (!Array.isArray(features) || !features.length)
       return null;
     const fn = { UNION: import_polygon_clipping.default.union, DIFFERENCE: import_polygon_clipping.default.difference }[which];
-    const args = features2.map((feature3) => feature3.geometry.coordinates);
+    const args = features.map((feature3) => feature3.geometry.coordinates);
     const coords = fn.apply(null, args);
     return {
       type: "Feature",
     return aRank > bRank ? 1 : aRank < bRank ? -1 : a.id.localeCompare(b.id);
   }
 
-  // modules/core/locations.js
+  // modules/core/LocationManager.js
   var import_which_polygon2 = __toESM(require_which_polygon());
   var import_geojson_area2 = __toESM(require_geojson_area());
+  var _loco = new location_conflation_default();
+  var LocationManager = class {
+    /**
+     * @constructor
+     */
+    constructor() {
+      this._wp = null;
+      this._resolved = /* @__PURE__ */ new Map();
+      this._knownLocationSets = /* @__PURE__ */ new Map();
+      this._locationIncludedIn = /* @__PURE__ */ new Map();
+      this._locationExcludedIn = /* @__PURE__ */ new Map();
+      const world = { locationSet: { include: ["Q2"] } };
+      this._resolveLocationSet(world);
+      this._rebuildIndex();
+    }
+    /**
+     * _validateLocationSet
+     * Pass an Object with a `locationSet` property.
+     * Validates the `locationSet` and sets a `locationSetID` property on the object.
+     * To avoid so much computation we only resolve the include and exclude regions, but not the locationSet itself.
+     *
+     * Use `_resolveLocationSet()` instead if you need to resolve geojson of locationSet, for example to render it.
+     * Note: You need to call `_rebuildIndex()` after you're all finished validating the locationSets.
+     *
+     * @param  `obj`  Object to check, it should have `locationSet` property
+     */
+    _validateLocationSet(obj) {
+      if (obj.locationSetID)
+        return;
+      try {
+        let locationSet = obj.locationSet;
+        if (!locationSet) {
+          throw new Error("object missing locationSet property");
+        }
+        if (!locationSet.include) {
+          locationSet.include = ["Q2"];
+        }
+        const locationSetID = _loco.validateLocationSet(locationSet).id;
+        obj.locationSetID = locationSetID;
+        if (this._knownLocationSets.has(locationSetID))
+          return;
+        let area = 0;
+        (locationSet.include || []).forEach((location) => {
+          const locationID = _loco.validateLocation(location).id;
+          let geojson = this._resolved.get(locationID);
+          if (!geojson) {
+            geojson = _loco.resolveLocation(location).feature;
+            this._resolved.set(locationID, geojson);
+          }
+          area += geojson.properties.area;
+          let s = this._locationIncludedIn.get(locationID);
+          if (!s) {
+            s = /* @__PURE__ */ new Set();
+            this._locationIncludedIn.set(locationID, s);
+          }
+          s.add(locationSetID);
+        });
+        (locationSet.exclude || []).forEach((location) => {
+          const locationID = _loco.validateLocation(location).id;
+          let geojson = this._resolved.get(locationID);
+          if (!geojson) {
+            geojson = _loco.resolveLocation(location).feature;
+            this._resolved.set(locationID, geojson);
+          }
+          area -= geojson.properties.area;
+          let s = this._locationExcludedIn.get(locationID);
+          if (!s) {
+            s = /* @__PURE__ */ new Set();
+            this._locationExcludedIn.set(locationID, s);
+          }
+          s.add(locationSetID);
+        });
+        this._knownLocationSets.set(locationSetID, area);
+      } catch (err) {
+        obj.locationSet = { include: ["Q2"] };
+        obj.locationSetID = "+[Q2]";
+      }
+    }
+    /**
+     * _resolveLocationSet
+     * Does everything that `_validateLocationSet()` does, but then "resolves" the locationSet into GeoJSON.
+     * This step is a bit more computationally expensive, so really only needed if you intend to render the shape.
+     *
+     * Note: You need to call `_rebuildIndex()` after you're all finished validating the locationSets.
+     *
+     * @param  `obj`  Object to check, it should have `locationSet` property
+     */
+    _resolveLocationSet(obj) {
+      this._validateLocationSet(obj);
+      if (this._resolved.has(obj.locationSetID))
+        return;
+      try {
+        const result = _loco.resolveLocationSet(obj.locationSet);
+        const locationSetID = result.id;
+        obj.locationSetID = locationSetID;
+        if (!result.feature.geometry.coordinates.length || !result.feature.properties.area) {
+          throw new Error(`locationSet ${locationSetID} resolves to an empty feature.`);
+        }
+        let geojson = JSON.parse(JSON.stringify(result.feature));
+        geojson.id = locationSetID;
+        geojson.properties.id = locationSetID;
+        this._resolved.set(locationSetID, geojson);
+      } catch (err) {
+        obj.locationSet = { include: ["Q2"] };
+        obj.locationSetID = "+[Q2]";
+      }
+    }
+    /**
+     * _rebuildIndex
+     * Rebuilds the whichPolygon index with whatever features have been resolved into GeoJSON.
+     */
+    _rebuildIndex() {
+      this._wp = (0, import_which_polygon2.default)({ features: [...this._resolved.values()] });
+    }
+    /**
+     * mergeCustomGeoJSON
+     * Accepts a FeatureCollection-like object containing custom locations
+     * Each feature must have a filename-like `id`, for example: `something.geojson`
+     * {
+     *   "type": "FeatureCollection"
+     *   "features": [
+     *     {
+     *       "type": "Feature",
+     *       "id": "philly_metro.geojson",
+     *       "properties": { … },
+     *       "geometry": { … }
+     *     }
+     *   ]
+     * }
+     *
+     * @param  `fc`  FeatureCollection-like Object containing custom locations
+     */
+    mergeCustomGeoJSON(fc) {
+      if (!fc || fc.type !== "FeatureCollection" || !Array.isArray(fc.features))
+        return;
+      fc.features.forEach((feature3) => {
+        feature3.properties = feature3.properties || {};
+        let props = feature3.properties;
+        let id2 = feature3.id || props.id;
+        if (!id2 || !/^\S+\.geojson$/i.test(id2))
+          return;
+        id2 = id2.toLowerCase();
+        feature3.id = id2;
+        props.id = id2;
+        if (!props.area) {
+          const area = import_geojson_area2.default.geometry(feature3.geometry) / 1e6;
+          props.area = Number(area.toFixed(2));
+        }
+        _loco._cache[id2] = feature3;
+      });
+    }
+    /**
+     * mergeLocationSets
+     * Accepts an Array of Objects containing `locationSet` properties:
+     * [
+     *  { id: 'preset1', locationSet: {…} },
+     *  { id: 'preset2', locationSet: {…} },
+     *  …
+     * ]
+     * After validating, the Objects will be decorated with a `locationSetID` property:
+     * [
+     *  { id: 'preset1', locationSet: {…}, locationSetID: '+[Q2]' },
+     *  { id: 'preset2', locationSet: {…}, locationSetID: '+[Q30]' },
+     *  …
+     * ]
+     *
+     * @param  `objects`  Objects to check - they should have `locationSet` property
+     * @return  Promise resolved true (this function used to be slow/async, now it's faster and sync)
+     */
+    mergeLocationSets(objects) {
+      if (!Array.isArray(objects))
+        return Promise.reject("nothing to do");
+      objects.forEach((obj) => this._validateLocationSet(obj));
+      this._rebuildIndex();
+      return Promise.resolve(objects);
+    }
+    /**
+     * locationSetID
+     * Returns a locationSetID for a given locationSet (fallback to `+[Q2]`, world)
+     * (The locationSet doesn't necessarily need to be resolved to compute its `id`)
+     *
+     * @param  `locationSet`  A locationSet Object, e.g. `{ include: ['us'] }`
+     * @return  String locationSetID, e.g. `+[Q30]`
+     */
+    locationSetID(locationSet) {
+      let locationSetID;
+      try {
+        locationSetID = _loco.validateLocationSet(locationSet).id;
+      } catch (err) {
+        locationSetID = "+[Q2]";
+      }
+      return locationSetID;
+    }
+    /**
+     * feature
+     * Returns the resolved GeoJSON feature for a given locationSetID (fallback to 'world')
+     * A GeoJSON feature:
+     * {
+     *   type: 'Feature',
+     *   id: '+[Q30]',
+     *   properties: { id: '+[Q30]', area: 21817019.17, … },
+     *   geometry: { … }
+     * }
+     *
+     * @param  `locationSetID`  String identifier, e.g. `+[Q30]`
+     * @return  GeoJSON object (fallback to world)
+     */
+    feature(locationSetID = "+[Q2]") {
+      const feature3 = this._resolved.get(locationSetID);
+      return feature3 || this._resolved.get("+[Q2]");
+    }
+    /**
+     * locationSetsAt
+     * Find all the locationSets valid at the given location.
+     * Results include the area (in km²) to facilitate sorting.
+     *
+     * Object of locationSetIDs to areas (in km²)
+     * {
+     *   "+[Q2]": 511207893.3958111,
+     *   "+[Q30]": 21817019.17,
+     *   "+[new_jersey.geojson]": 22390.77,
+     *   …
+     * }
+     *
+     * @param  `loc`  `[lon,lat]` location to query, e.g. `[-74.4813, 40.7967]`
+     * @return  Object of locationSetIDs valid at given location
+     */
+    locationSetsAt(loc) {
+      let result = {};
+      const hits = this._wp(loc, true) || [];
+      const thiz = this;
+      hits.forEach((prop) => {
+        if (prop.id[0] !== "+")
+          return;
+        const locationSetID = prop.id;
+        const area = thiz._knownLocationSets.get(locationSetID);
+        if (area) {
+          result[locationSetID] = area;
+        }
+      });
+      hits.forEach((prop) => {
+        if (prop.id[0] === "+")
+          return;
+        const locationID = prop.id;
+        const included = thiz._locationIncludedIn.get(locationID);
+        (included || []).forEach((locationSetID) => {
+          const area = thiz._knownLocationSets.get(locationSetID);
+          if (area) {
+            result[locationSetID] = area;
+          }
+        });
+      });
+      hits.forEach((prop) => {
+        if (prop.id[0] === "+")
+          return;
+        const locationID = prop.id;
+        const excluded = thiz._locationExcludedIn.get(locationID);
+        (excluded || []).forEach((locationSetID) => {
+          delete result[locationSetID];
+        });
+      });
+      return result;
+    }
+    // Direct access to the location-conflation resolver
+    loco() {
+      return _loco;
+    }
+  };
+  var _sharedLocationManager = new LocationManager();
 
-  // modules/util/aes.js
-  var import_aes_js = __toESM(require_aes_js());
-  var DEFAULT_128 = [250, 157, 60, 79, 142, 134, 229, 129, 138, 126, 210, 129, 29, 71, 160, 208];
-  function utilAesEncrypt(text2, key) {
-    key = key || DEFAULT_128;
-    const textBytes = import_aes_js.default.utils.utf8.toBytes(text2);
-    const aesCtr = new import_aes_js.default.ModeOfOperation.ctr(key);
-    const encryptedBytes = aesCtr.encrypt(textBytes);
-    const encryptedHex = import_aes_js.default.utils.hex.fromBytes(encryptedBytes);
-    return encryptedHex;
+  // node_modules/lodash-es/_freeGlobal.js
+  var freeGlobal = typeof global == "object" && global && global.Object === Object && global;
+  var freeGlobal_default = freeGlobal;
+
+  // node_modules/lodash-es/_root.js
+  var freeSelf = typeof self == "object" && self && self.Object === Object && self;
+  var root2 = freeGlobal_default || freeSelf || Function("return this")();
+  var root_default = root2;
+
+  // node_modules/lodash-es/_Symbol.js
+  var Symbol2 = root_default.Symbol;
+  var Symbol_default = Symbol2;
+
+  // node_modules/lodash-es/_getRawTag.js
+  var objectProto = Object.prototype;
+  var hasOwnProperty = objectProto.hasOwnProperty;
+  var nativeObjectToString = objectProto.toString;
+  var symToStringTag = Symbol_default ? Symbol_default.toStringTag : void 0;
+  function getRawTag(value) {
+    var isOwn = hasOwnProperty.call(value, symToStringTag), tag = value[symToStringTag];
+    try {
+      value[symToStringTag] = void 0;
+      var unmasked = true;
+    } catch (e) {
+    }
+    var result = nativeObjectToString.call(value);
+    if (unmasked) {
+      if (isOwn) {
+        value[symToStringTag] = tag;
+      } else {
+        delete value[symToStringTag];
+      }
+    }
+    return result;
   }
-  function utilAesDecrypt(encryptedHex, key) {
-    key = key || DEFAULT_128;
-    const encryptedBytes = import_aes_js.default.utils.hex.toBytes(encryptedHex);
-    const aesCtr = new import_aes_js.default.ModeOfOperation.ctr(key);
-    const decryptedBytes = aesCtr.decrypt(encryptedBytes);
-    const text2 = import_aes_js.default.utils.utf8.fromBytes(decryptedBytes);
-    return text2;
+  var getRawTag_default = getRawTag;
+
+  // node_modules/lodash-es/_objectToString.js
+  var objectProto2 = Object.prototype;
+  var nativeObjectToString2 = objectProto2.toString;
+  function objectToString(value) {
+    return nativeObjectToString2.call(value);
   }
+  var objectToString_default = objectToString;
 
-  // modules/util/clean_tags.js
-  function utilCleanTags(tags) {
-    var out = {};
-    for (var k in tags) {
-      if (!k)
-        continue;
-      var v = tags[k];
-      if (v !== void 0) {
-        out[k] = cleanValue(k, v);
+  // node_modules/lodash-es/_baseGetTag.js
+  var nullTag = "[object Null]";
+  var undefinedTag = "[object Undefined]";
+  var symToStringTag2 = Symbol_default ? Symbol_default.toStringTag : void 0;
+  function baseGetTag(value) {
+    if (value == null) {
+      return value === void 0 ? undefinedTag : nullTag;
+    }
+    return symToStringTag2 && symToStringTag2 in Object(value) ? getRawTag_default(value) : objectToString_default(value);
+  }
+  var baseGetTag_default = baseGetTag;
+
+  // node_modules/lodash-es/isObjectLike.js
+  function isObjectLike(value) {
+    return value != null && typeof value == "object";
+  }
+  var isObjectLike_default = isObjectLike;
+
+  // node_modules/lodash-es/isSymbol.js
+  var symbolTag = "[object Symbol]";
+  function isSymbol(value) {
+    return typeof value == "symbol" || isObjectLike_default(value) && baseGetTag_default(value) == symbolTag;
+  }
+  var isSymbol_default = isSymbol;
+
+  // node_modules/lodash-es/_arrayMap.js
+  function arrayMap(array2, iteratee) {
+    var index = -1, length = array2 == null ? 0 : array2.length, result = Array(length);
+    while (++index < length) {
+      result[index] = iteratee(array2[index], index, array2);
+    }
+    return result;
+  }
+  var arrayMap_default = arrayMap;
+
+  // node_modules/lodash-es/isArray.js
+  var isArray = Array.isArray;
+  var isArray_default = isArray;
+
+  // node_modules/lodash-es/_baseToString.js
+  var INFINITY = 1 / 0;
+  var symbolProto = Symbol_default ? Symbol_default.prototype : void 0;
+  var symbolToString = symbolProto ? symbolProto.toString : void 0;
+  function baseToString(value) {
+    if (typeof value == "string") {
+      return value;
+    }
+    if (isArray_default(value)) {
+      return arrayMap_default(value, baseToString) + "";
+    }
+    if (isSymbol_default(value)) {
+      return symbolToString ? symbolToString.call(value) : "";
+    }
+    var result = value + "";
+    return result == "0" && 1 / value == -INFINITY ? "-0" : result;
+  }
+  var baseToString_default = baseToString;
+
+  // node_modules/lodash-es/_trimmedEndIndex.js
+  var reWhitespace = /\s/;
+  function trimmedEndIndex(string) {
+    var index = string.length;
+    while (index-- && reWhitespace.test(string.charAt(index))) {
+    }
+    return index;
+  }
+  var trimmedEndIndex_default = trimmedEndIndex;
+
+  // node_modules/lodash-es/_baseTrim.js
+  var reTrimStart = /^\s+/;
+  function baseTrim(string) {
+    return string ? string.slice(0, trimmedEndIndex_default(string) + 1).replace(reTrimStart, "") : string;
+  }
+  var baseTrim_default = baseTrim;
+
+  // node_modules/lodash-es/isObject.js
+  function isObject(value) {
+    var type2 = typeof value;
+    return value != null && (type2 == "object" || type2 == "function");
+  }
+  var isObject_default = isObject;
+
+  // node_modules/lodash-es/toNumber.js
+  var NAN = 0 / 0;
+  var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
+  var reIsBinary = /^0b[01]+$/i;
+  var reIsOctal = /^0o[0-7]+$/i;
+  var freeParseInt = parseInt;
+  function toNumber(value) {
+    if (typeof value == "number") {
+      return value;
+    }
+    if (isSymbol_default(value)) {
+      return NAN;
+    }
+    if (isObject_default(value)) {
+      var other = typeof value.valueOf == "function" ? value.valueOf() : value;
+      value = isObject_default(other) ? other + "" : other;
+    }
+    if (typeof value != "string") {
+      return value === 0 ? value : +value;
+    }
+    value = baseTrim_default(value);
+    var isBinary = reIsBinary.test(value);
+    return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value;
+  }
+  var toNumber_default = toNumber;
+
+  // node_modules/lodash-es/toString.js
+  function toString(value) {
+    return value == null ? "" : baseToString_default(value);
+  }
+  var toString_default = toString;
+
+  // node_modules/lodash-es/_basePropertyOf.js
+  function basePropertyOf(object) {
+    return function(key) {
+      return object == null ? void 0 : object[key];
+    };
+  }
+  var basePropertyOf_default = basePropertyOf;
+
+  // node_modules/lodash-es/now.js
+  var now2 = function() {
+    return root_default.Date.now();
+  };
+  var now_default = now2;
+
+  // node_modules/lodash-es/debounce.js
+  var FUNC_ERROR_TEXT = "Expected a function";
+  var nativeMax = Math.max;
+  var nativeMin = Math.min;
+  function debounce(func, wait, options2) {
+    var lastArgs, lastThis, maxWait, result, timerId, lastCallTime, lastInvokeTime = 0, leading = false, maxing = false, trailing = true;
+    if (typeof func != "function") {
+      throw new TypeError(FUNC_ERROR_TEXT);
+    }
+    wait = toNumber_default(wait) || 0;
+    if (isObject_default(options2)) {
+      leading = !!options2.leading;
+      maxing = "maxWait" in options2;
+      maxWait = maxing ? nativeMax(toNumber_default(options2.maxWait) || 0, wait) : maxWait;
+      trailing = "trailing" in options2 ? !!options2.trailing : trailing;
+    }
+    function invokeFunc(time) {
+      var args = lastArgs, thisArg = lastThis;
+      lastArgs = lastThis = void 0;
+      lastInvokeTime = time;
+      result = func.apply(thisArg, args);
+      return result;
+    }
+    function leadingEdge(time) {
+      lastInvokeTime = time;
+      timerId = setTimeout(timerExpired, wait);
+      return leading ? invokeFunc(time) : result;
+    }
+    function remainingWait(time) {
+      var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime, timeWaiting = wait - timeSinceLastCall;
+      return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting;
+    }
+    function shouldInvoke(time) {
+      var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime;
+      return lastCallTime === void 0 || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait;
+    }
+    function timerExpired() {
+      var time = now_default();
+      if (shouldInvoke(time)) {
+        return trailingEdge(time);
       }
+      timerId = setTimeout(timerExpired, remainingWait(time));
     }
-    return out;
-    function cleanValue(k2, v2) {
-      function keepSpaces(k3) {
-        return /_hours|_times|:conditional$/.test(k3);
+    function trailingEdge(time) {
+      timerId = void 0;
+      if (trailing && lastArgs) {
+        return invokeFunc(time);
       }
-      function skip(k3) {
-        return /^(description|note|fixme)$/.test(k3);
+      lastArgs = lastThis = void 0;
+      return result;
+    }
+    function cancel() {
+      if (timerId !== void 0) {
+        clearTimeout(timerId);
       }
-      if (skip(k2))
-        return v2;
-      var cleaned = v2.split(";").map(function(s) {
-        return s.trim();
-      }).join(keepSpaces(k2) ? "; " : ";");
-      if (k2.indexOf("website") !== -1 || k2.indexOf("email") !== -1 || cleaned.indexOf("http") === 0) {
-        cleaned = cleaned.replace(/[\u200B-\u200F\uFEFF]/g, "");
+      lastInvokeTime = 0;
+      lastArgs = lastCallTime = lastThis = timerId = void 0;
+    }
+    function flush() {
+      return timerId === void 0 ? result : trailingEdge(now_default());
+    }
+    function debounced() {
+      var time = now_default(), isInvoking = shouldInvoke(time);
+      lastArgs = arguments;
+      lastThis = this;
+      lastCallTime = time;
+      if (isInvoking) {
+        if (timerId === void 0) {
+          return leadingEdge(lastCallTime);
+        }
+        if (maxing) {
+          clearTimeout(timerId);
+          timerId = setTimeout(timerExpired, wait);
+          return invokeFunc(lastCallTime);
+        }
       }
-      return cleaned;
+      if (timerId === void 0) {
+        timerId = setTimeout(timerExpired, wait);
+      }
+      return result;
     }
+    debounced.cancel = cancel;
+    debounced.flush = flush;
+    return debounced;
   }
+  var debounce_default = debounce;
+
+  // node_modules/lodash-es/_escapeHtmlChar.js
+  var htmlEscapes = {
+    "&": "&amp;",
+    "<": "&lt;",
+    ">": "&gt;",
+    '"': "&quot;",
+    "'": "&#39;"
+  };
+  var escapeHtmlChar = basePropertyOf_default(htmlEscapes);
+  var escapeHtmlChar_default = escapeHtmlChar;
+
+  // node_modules/lodash-es/escape.js
+  var reUnescapedHtml = /[&<>"']/g;
+  var reHasUnescapedHtml = RegExp(reUnescapedHtml.source);
+  function escape2(string) {
+    string = toString_default(string);
+    return string && reHasUnescapedHtml.test(string) ? string.replace(reUnescapedHtml, escapeHtmlChar_default) : string;
+  }
+  var escape_default = escape2;
+
+  // node_modules/lodash-es/throttle.js
+  var FUNC_ERROR_TEXT2 = "Expected a function";
+  function throttle(func, wait, options2) {
+    var leading = true, trailing = true;
+    if (typeof func != "function") {
+      throw new TypeError(FUNC_ERROR_TEXT2);
+    }
+    if (isObject_default(options2)) {
+      leading = "leading" in options2 ? !!options2.leading : leading;
+      trailing = "trailing" in options2 ? !!options2.trailing : trailing;
+    }
+    return debounce_default(func, wait, {
+      "leading": leading,
+      "maxWait": wait,
+      "trailing": trailing
+    });
+  }
+  var throttle_default = throttle;
+
+  // node_modules/lodash-es/_unescapeHtmlChar.js
+  var htmlUnescapes = {
+    "&amp;": "&",
+    "&lt;": "<",
+    "&gt;": ">",
+    "&quot;": '"',
+    "&#39;": "'"
+  };
+  var unescapeHtmlChar = basePropertyOf_default(htmlUnescapes);
+  var unescapeHtmlChar_default = unescapeHtmlChar;
+
+  // node_modules/lodash-es/unescape.js
+  var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g;
+  var reHasEscapedHtml = RegExp(reEscapedHtml.source);
+  function unescape2(string) {
+    string = toString_default(string);
+    return string && reHasEscapedHtml.test(string) ? string.replace(reEscapedHtml, unescapeHtmlChar_default) : string;
+  }
+  var unescape_default = unescape2;
 
   // modules/util/detect.js
   var _detected;
       _detected.version = navigator.appVersion;
     }
     _detected.version = _detected.version.split(/\W/).slice(0, 2).join(".");
-    _detected.opera = _detected.browser.toLowerCase() === "opera" && parseFloat(_detected.version) < 15;
+    _detected.opera = _detected.browser.toLowerCase() === "opera" && Number(_detected.version) < 15;
     if (_detected.browser.toLowerCase() === "msie") {
       _detected.ie = true;
       _detected.browser = "Internet Explorer";
       _detected.os = "win";
       _detected.platform = "Unknown";
     }
-    _detected.isMobileWebKit = (/\b(iPad|iPhone|iPod)\b/.test(ua) || navigator.platform === "MacIntel" && "maxTouchPoints" in navigator && navigator.maxTouchPoints > 1) && /WebKit/.test(ua) && !/Edge/.test(ua) && !window.MSStream;
-    _detected.browserLocales = Array.from(new Set([navigator.language].concat(navigator.languages || []).concat([
-      navigator.userLanguage
-    ]).filter(Boolean)));
+    _detected.isMobileWebKit = (/\b(iPad|iPhone|iPod)\b/.test(ua) || // HACK: iPadOS 13+ requests desktop sites by default by using a Mac user agent,
+    // so assume any "mac" with multitouch is actually iOS
+    navigator.platform === "MacIntel" && "maxTouchPoints" in navigator && navigator.maxTouchPoints > 1) && /WebKit/.test(ua) && !/Edge/.test(ua) && !window.MSStream;
+    _detected.browserLocales = Array.from(new Set(
+      // remove duplicates
+      [navigator.language].concat(navigator.languages || []).concat([
+        // old property for backwards compatibility
+        navigator.userLanguage
+      ]).filter(Boolean)
+    ));
     const loc = window.top.location;
     let origin = loc.origin;
     if (!origin) {
     return _detected;
   }
 
+  // modules/util/aes.js
+  var import_aes_js = __toESM(require_aes_js());
+  var DEFAULT_128 = [250, 157, 60, 79, 142, 134, 229, 129, 138, 126, 210, 129, 29, 71, 160, 208];
+  function utilAesEncrypt(text2, key) {
+    key = key || DEFAULT_128;
+    const textBytes = import_aes_js.default.utils.utf8.toBytes(text2);
+    const aesCtr = new import_aes_js.default.ModeOfOperation.ctr(key);
+    const encryptedBytes = aesCtr.encrypt(textBytes);
+    const encryptedHex = import_aes_js.default.utils.hex.fromBytes(encryptedBytes);
+    return encryptedHex;
+  }
+  function utilAesDecrypt(encryptedHex, key) {
+    key = key || DEFAULT_128;
+    const encryptedBytes = import_aes_js.default.utils.hex.toBytes(encryptedHex);
+    const aesCtr = new import_aes_js.default.ModeOfOperation.ctr(key);
+    const decryptedBytes = aesCtr.decrypt(encryptedBytes);
+    const text2 = import_aes_js.default.utils.utf8.fromBytes(decryptedBytes);
+    return text2;
+  }
+
+  // modules/util/clean_tags.js
+  function utilCleanTags(tags) {
+    var out = {};
+    for (var k in tags) {
+      if (!k)
+        continue;
+      var v = tags[k];
+      if (v !== void 0) {
+        out[k] = cleanValue(k, v);
+      }
+    }
+    return out;
+    function cleanValue(k2, v2) {
+      function keepSpaces(k3) {
+        return /_hours|_times|:conditional$/.test(k3);
+      }
+      function skip(k3) {
+        return /^(description|note|fixme)$/.test(k3);
+      }
+      if (skip(k2))
+        return v2;
+      var cleaned = v2.split(";").map(function(s) {
+        return s.trim();
+      }).join(keepSpaces(k2) ? "; " : ";");
+      if (k2.indexOf("website") !== -1 || k2.indexOf("email") !== -1 || cleaned.indexOf("http") === 0) {
+        cleaned = cleaned.replace(/[\u200B-\u200F\uFEFF]/g, "");
+      }
+      return cleaned;
+    }
+  }
+
   // modules/util/get_set_value.js
   function utilGetSetValue(selection2, value) {
     function d3_selection_value(value2) {
           callback,
           event: {
             key: void 0,
+            // preferred
             keyCode: 0,
+            // fallback
             modifiers: {
               shiftKey: false,
               ctrlKey: false,
     return keybinding;
   }
   utilKeybinding.modifierCodes = {
+    // Shift key, ⇧
     "\u21E7": 16,
     shift: 16,
+    // CTRL key, on Mac: ⌃
     "\u2303": 17,
     ctrl: 17,
+    // ALT key, on Mac: ⌥ (Alt)
     "\u2325": 18,
     alt: 18,
     option: 18,
+    // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
     "\u2318": 91,
     meta: 91,
     cmd: 91,
   utilKeybinding.plusKeys = ["plus", "ffplus", "=", "ffequals", "\u2260", "\xB1"];
   utilKeybinding.minusKeys = ["_", "-", "ffminus", "dash", "\u2013", "\u2014"];
   utilKeybinding.keys = {
+    // Backspace key, on Mac: ⌫ (Backspace)
     "\u232B": "Backspace",
     backspace: "Backspace",
+    // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
     "\u21E5": "Tab",
     "\u21C6": "Tab",
     tab: "Tab",
+    // Return key, ↩
     "\u21A9": "Enter",
     "\u21B5": "Enter",
     "\u23CE": "Enter",
     "return": "Enter",
     enter: "Enter",
     "\u2305": "Enter",
+    // Pause/Break key
     "pause": "Pause",
     "pause-break": "Pause",
+    // Caps Lock key, ⇪
     "\u21EA": "CapsLock",
     caps: "CapsLock",
     "caps-lock": "CapsLock",
+    // Escape key, on Mac: ⎋, on Windows: Esc
     "\u238B": ["Escape", "Esc"],
     escape: ["Escape", "Esc"],
     esc: ["Escape", "Esc"],
+    // Space key
     space: [" ", "Spacebar"],
+    // Page-Up key, or pgup, on Mac: ↖
     "\u2196": "PageUp",
     pgup: "PageUp",
     "page-up": "PageUp",
+    // Page-Down key, or pgdown, on Mac: ↘
     "\u2198": "PageDown",
     pgdown: "PageDown",
     "page-down": "PageDown",
+    // END key, on Mac: ⇟
     "\u21DF": "End",
     end: "End",
+    // HOME key, on Mac: ⇞
     "\u21DE": "Home",
     home: "Home",
+    // Insert key, or ins
     ins: "Insert",
     insert: "Insert",
+    // Delete key, on Mac: ⌦ (Delete)
     "\u2326": ["Delete", "Del"],
     del: ["Delete", "Del"],
     "delete": ["Delete", "Del"],
+    // Left Arrow Key, or ←
     "\u2190": ["ArrowLeft", "Left"],
     left: ["ArrowLeft", "Left"],
     "arrow-left": ["ArrowLeft", "Left"],
+    // Up Arrow Key, or ↑
     "\u2191": ["ArrowUp", "Up"],
     up: ["ArrowUp", "Up"],
     "arrow-up": ["ArrowUp", "Up"],
+    // Right Arrow Key, or →
     "\u2192": ["ArrowRight", "Right"],
     right: ["ArrowRight", "Right"],
     "arrow-right": ["ArrowRight", "Right"],
+    // Up Arrow Key, or ↓
     "\u2193": ["ArrowDown", "Down"],
     down: ["ArrowDown", "Down"],
     "arrow-down": ["ArrowDown", "Down"],
+    // odities, stuff for backward compatibility (browsers and code):
+    // Num-Multiply, or *
     "*": ["*", "Multiply"],
     star: ["*", "Multiply"],
     asterisk: ["*", "Multiply"],
     multiply: ["*", "Multiply"],
+    // Num-Plus or +
     "+": ["+", "Add"],
     "plus": ["+", "Add"],
+    // Num-Subtract, or -
     "-": ["-", "Subtract"],
     subtract: ["-", "Subtract"],
     "dash": ["-", "Subtract"],
+    // Semicolon
     semicolon: ";",
+    // = or equals
     equals: "=",
+    // Comma, or ,
     comma: ",",
+    // Period, or ., or full-stop
     period: ".",
     "full-stop": ".",
+    // Slash, or /, or forward-slash
     slash: "/",
     "forward-slash": "/",
+    // Tick, or `, or back-quote
     tick: "`",
     "back-quote": "`",
+    // Open bracket, or [
     "open-bracket": "[",
+    // Back slash, or \
     "back-slash": "\\",
+    // Close backet, or ]
     "close-bracket": "]",
+    // Apostrophe, or Quote, or '
     quote: "'",
     apostrophe: "'",
+    // NUMPAD 0-9
     "num-0": "0",
     "num-1": "1",
     "num-2": "2",
     "num-7": "7",
     "num-8": "8",
     "num-9": "9",
+    // F1-F25
     f1: "F1",
     f2: "F2",
     f3: "F3",
     f25: "F25"
   };
   utilKeybinding.keyCodes = {
+    // Backspace key, on Mac: ⌫ (Backspace)
     "\u232B": 8,
     backspace: 8,
+    // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
     "\u21E5": 9,
     "\u21C6": 9,
     tab: 9,
+    // Return key, ↩
     "\u21A9": 13,
     "\u21B5": 13,
     "\u23CE": 13,
     "return": 13,
     enter: 13,
     "\u2305": 13,
+    // Pause/Break key
     "pause": 19,
     "pause-break": 19,
+    // Caps Lock key, ⇪
     "\u21EA": 20,
     caps: 20,
     "caps-lock": 20,
+    // Escape key, on Mac: ⎋, on Windows: Esc
     "\u238B": 27,
     escape: 27,
     esc: 27,
+    // Space key
     space: 32,
+    // Page-Up key, or pgup, on Mac: ↖
     "\u2196": 33,
     pgup: 33,
     "page-up": 33,
+    // Page-Down key, or pgdown, on Mac: ↘
     "\u2198": 34,
     pgdown: 34,
     "page-down": 34,
+    // END key, on Mac: ⇟
     "\u21DF": 35,
     end: 35,
+    // HOME key, on Mac: ⇞
     "\u21DE": 36,
     home: 36,
+    // Insert key, or ins
     ins: 45,
     insert: 45,
+    // Delete key, on Mac: ⌦ (Delete)
     "\u2326": 46,
     del: 46,
     "delete": 46,
+    // Left Arrow Key, or ←
     "\u2190": 37,
     left: 37,
     "arrow-left": 37,
+    // Up Arrow Key, or ↑
     "\u2191": 38,
     up: 38,
     "arrow-up": 38,
+    // Right Arrow Key, or →
     "\u2192": 39,
     right: 39,
     "arrow-right": 39,
+    // Up Arrow Key, or ↓
     "\u2193": 40,
     down: 40,
     "arrow-down": 40,
+    // odities, printing characters that come out wrong:
+    // Firefox Equals
     "ffequals": 61,
+    // Num-Multiply, or *
     "*": 106,
     star: 106,
     asterisk: 106,
     multiply: 106,
+    // Num-Plus or +
     "+": 107,
     "plus": 107,
+    // Num-Subtract, or -
     "-": 109,
     subtract: 109,
+    // Vertical Bar / Pipe
     "|": 124,
+    // Firefox Plus
     "ffplus": 171,
+    // Firefox Minus
     "ffminus": 173,
+    // Semicolon
     ";": 186,
     semicolon: 186,
+    // = or equals
     "=": 187,
     "equals": 187,
+    // Comma, or ,
     ",": 188,
     comma: 188,
+    // Dash / Underscore key
     "dash": 189,
+    // Period, or ., or full-stop
     ".": 190,
     period: 190,
     "full-stop": 190,
+    // Slash, or /, or forward-slash
     "/": 191,
     slash: 191,
     "forward-slash": 191,
+    // Tick, or `, or back-quote
     "`": 192,
     tick: 192,
     "back-quote": 192,
+    // Open bracket, or [
     "[": 219,
     "open-bracket": 219,
+    // Back slash, or \
     "\\": 220,
     "back-slash": 220,
+    // Close backet, or ]
     "]": 221,
     "close-bracket": 221,
+    // Apostrophe, or Quote, or '
     "'": 222,
     quote: 222,
     apostrophe: 222
   }
 
   // modules/util/session_mutex.js
-  function utilSessionMutex(name2) {
+  function utilSessionMutex(name) {
     var mutex = {};
     var intervalID;
     function renew() {
       var expires = new Date();
       expires.setSeconds(expires.getSeconds() + 5);
-      document.cookie = name2 + "=1; expires=" + expires.toUTCString() + "; sameSite=strict";
+      document.cookie = name + "=1; expires=" + expires.toUTCString() + "; sameSite=strict";
     }
     mutex.lock = function() {
       if (intervalID)
         return true;
-      var cookie = document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + name2 + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1");
+      var cookie = document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + name + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1");
       if (cookie)
         return false;
       renew();
     mutex.unlock = function() {
       if (!intervalID)
         return;
-      document.cookie = name2 + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; sameSite=strict";
+      document.cookie = name + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; sameSite=strict";
       clearInterval(intervalID);
       intervalID = null;
     };
         (_translate[0] - _scale / 2) / k,
         (_translate[1] - _scale / 2) / k
       ];
-      var cols = range(clamp3(Math.floor(-origin[0]) - _margin, tileMin, tileMax + 1), clamp3(Math.ceil(_size[0] / k - origin[0]) + _margin, tileMin, tileMax + 1));
-      var rows = range(clamp3(Math.floor(-origin[1]) - _margin, tileMin, tileMax + 1), clamp3(Math.ceil(_size[1] / k - origin[1]) + _margin, tileMin, tileMax + 1));
+      var cols = range(
+        clamp3(Math.floor(-origin[0]) - _margin, tileMin, tileMax + 1),
+        clamp3(Math.ceil(_size[0] / k - origin[0]) + _margin, tileMin, tileMax + 1)
+      );
+      var rows = range(
+        clamp3(Math.floor(-origin[1]) - _margin, tileMin, tileMax + 1),
+        clamp3(Math.ceil(_size[1] / k - origin[1]) + _margin, tileMin, tileMax + 1)
+      );
       var tiles = [];
       for (var i2 = 0; i2 < rows.length; i2++) {
         var y = rows[i2];
         return {
           id: tile.toString(),
           xyz: tile,
-          extent: geoExtent(projection2.invert([x, y + ts]), projection2.invert([x + ts, y]))
+          extent: geoExtent(
+            projection2.invert([x, y + ts]),
+            projection2.invert([x + ts, y])
+          )
         };
       }).filter(Boolean);
     };
     tiler8.getGeoJSON = function(projection2) {
-      var features2 = tiler8.getTiles(projection2).map(function(tile) {
+      var features = tiler8.getTiles(projection2).map(function(tile) {
         return {
           type: "Feature",
           properties: {
       });
       return {
         type: "FeatureCollection",
-        features: features2
+        features
       };
     };
     tiler8.tileSize = function(val) {
   }
 
   // modules/util/trigger_event.js
-  function utilTriggerEvent(target, type3) {
+  function utilTriggerEvent(target, type2) {
     target.each(function() {
       var evt = document.createEvent("HTMLEvents");
-      evt.initEvent(type3, true, true);
+      evt.initEvent(type2, true, true);
       this.dispatchEvent(evt);
     });
   }
 
-  // modules/core/locations.js
-  var _mainLocations = coreLocations();
-  function coreLocations() {
-    let _this = {};
-    let _resolvedFeatures = {};
-    let _loco = new location_conflation_default();
-    let _wp;
-    const world = { locationSet: { include: ["Q2"] } };
-    resolveLocationSet(world);
-    rebuildIndex();
-    let _queue = [];
-    let _deferred2 = /* @__PURE__ */ new Set();
-    let _inProcess;
-    function processQueue() {
-      if (!_queue.length)
-        return Promise.resolve();
-      const chunk = _queue.pop();
-      return new Promise((resolvePromise) => {
-        const handle = window.requestIdleCallback(() => {
-          _deferred2.delete(handle);
-          chunk.forEach(resolveLocationSet);
-          resolvePromise();
-        });
-        _deferred2.add(handle);
-      }).then(() => processQueue());
-    }
-    function resolveLocationSet(obj) {
-      if (obj.locationSetID)
-        return;
-      try {
-        let locationSet = obj.locationSet;
-        if (!locationSet) {
-          throw new Error("object missing locationSet property");
-        }
-        if (!locationSet.include) {
-          locationSet.include = ["Q2"];
-        }
-        const resolved = _loco.resolveLocationSet(locationSet);
-        const locationSetID = resolved.id;
-        obj.locationSetID = locationSetID;
-        if (!resolved.feature.geometry.coordinates.length || !resolved.feature.properties.area) {
-          throw new Error(`locationSet ${locationSetID} resolves to an empty feature.`);
-        }
-        if (!_resolvedFeatures[locationSetID]) {
-          let feature3 = JSON.parse(JSON.stringify(resolved.feature));
-          feature3.id = locationSetID;
-          feature3.properties.id = locationSetID;
-          _resolvedFeatures[locationSetID] = feature3;
-        }
-      } catch (err) {
-        obj.locationSet = { include: ["Q2"] };
-        obj.locationSetID = "+[Q2]";
-      }
-    }
-    function rebuildIndex() {
-      _wp = (0, import_which_polygon2.default)({ features: Object.values(_resolvedFeatures) });
-    }
-    _this.mergeCustomGeoJSON = (fc) => {
-      if (fc && fc.type === "FeatureCollection" && Array.isArray(fc.features)) {
-        fc.features.forEach((feature3) => {
-          feature3.properties = feature3.properties || {};
-          let props = feature3.properties;
-          let id2 = feature3.id || props.id;
-          if (!id2 || !/^\S+\.geojson$/i.test(id2))
-            return;
-          id2 = id2.toLowerCase();
-          feature3.id = id2;
-          props.id = id2;
-          if (!props.area) {
-            const area = import_geojson_area2.default.geometry(feature3.geometry) / 1e6;
-            props.area = Number(area.toFixed(2));
-          }
-          _loco._cache[id2] = feature3;
-        });
-      }
-    };
-    _this.mergeLocationSets = (objects) => {
-      if (!Array.isArray(objects))
-        return Promise.reject("nothing to do");
-      _queue = _queue.concat(utilArrayChunk(objects, 200));
-      if (!_inProcess) {
-        _inProcess = processQueue().then(() => {
-          rebuildIndex();
-          _inProcess = null;
-          return objects;
-        });
-      }
-      return _inProcess;
-    };
-    _this.locationSetID = (locationSet) => {
-      let locationSetID;
-      try {
-        locationSetID = _loco.validateLocationSet(locationSet).id;
-      } catch (err) {
-        locationSetID = "+[Q2]";
-      }
-      return locationSetID;
-    };
-    _this.feature = (locationSetID) => _resolvedFeatures[locationSetID] || _resolvedFeatures["+[Q2]"];
-    _this.locationsAt = (loc) => {
-      let result = {};
-      (_wp(loc, true) || []).forEach((prop) => result[prop.id] = prop.area);
-      return result;
-    };
-    _this.query = (loc, multi) => _wp(loc, multi);
-    _this.loco = () => _loco;
-    _this.wp = () => _wp;
-    return _this;
-  }
-
-  // node_modules/lodash-es/_freeGlobal.js
-  var freeGlobal = typeof global == "object" && global && global.Object === Object && global;
-  var freeGlobal_default = freeGlobal;
-
-  // node_modules/lodash-es/_root.js
-  var freeSelf = typeof self == "object" && self && self.Object === Object && self;
-  var root2 = freeGlobal_default || freeSelf || Function("return this")();
-  var root_default = root2;
-
-  // node_modules/lodash-es/_Symbol.js
-  var Symbol2 = root_default.Symbol;
-  var Symbol_default = Symbol2;
-
-  // node_modules/lodash-es/_getRawTag.js
-  var objectProto = Object.prototype;
-  var hasOwnProperty = objectProto.hasOwnProperty;
-  var nativeObjectToString = objectProto.toString;
-  var symToStringTag = Symbol_default ? Symbol_default.toStringTag : void 0;
-  function getRawTag(value) {
-    var isOwn = hasOwnProperty.call(value, symToStringTag), tag = value[symToStringTag];
-    try {
-      value[symToStringTag] = void 0;
-      var unmasked = true;
-    } catch (e) {
-    }
-    var result = nativeObjectToString.call(value);
-    if (unmasked) {
-      if (isOwn) {
-        value[symToStringTag] = tag;
-      } else {
-        delete value[symToStringTag];
-      }
-    }
-    return result;
-  }
-  var getRawTag_default = getRawTag;
-
-  // node_modules/lodash-es/_objectToString.js
-  var objectProto2 = Object.prototype;
-  var nativeObjectToString2 = objectProto2.toString;
-  function objectToString(value) {
-    return nativeObjectToString2.call(value);
-  }
-  var objectToString_default = objectToString;
-
-  // node_modules/lodash-es/_baseGetTag.js
-  var nullTag = "[object Null]";
-  var undefinedTag = "[object Undefined]";
-  var symToStringTag2 = Symbol_default ? Symbol_default.toStringTag : void 0;
-  function baseGetTag(value) {
-    if (value == null) {
-      return value === void 0 ? undefinedTag : nullTag;
-    }
-    return symToStringTag2 && symToStringTag2 in Object(value) ? getRawTag_default(value) : objectToString_default(value);
-  }
-  var baseGetTag_default = baseGetTag;
-
-  // node_modules/lodash-es/isObjectLike.js
-  function isObjectLike(value) {
-    return value != null && typeof value == "object";
-  }
-  var isObjectLike_default = isObjectLike;
-
-  // node_modules/lodash-es/isSymbol.js
-  var symbolTag = "[object Symbol]";
-  function isSymbol(value) {
-    return typeof value == "symbol" || isObjectLike_default(value) && baseGetTag_default(value) == symbolTag;
-  }
-  var isSymbol_default = isSymbol;
-
-  // node_modules/lodash-es/_arrayMap.js
-  function arrayMap(array2, iteratee) {
-    var index = -1, length = array2 == null ? 0 : array2.length, result = Array(length);
-    while (++index < length) {
-      result[index] = iteratee(array2[index], index, array2);
-    }
-    return result;
-  }
-  var arrayMap_default = arrayMap;
-
-  // node_modules/lodash-es/isArray.js
-  var isArray = Array.isArray;
-  var isArray_default = isArray;
-
-  // node_modules/lodash-es/_baseToString.js
-  var INFINITY = 1 / 0;
-  var symbolProto = Symbol_default ? Symbol_default.prototype : void 0;
-  var symbolToString = symbolProto ? symbolProto.toString : void 0;
-  function baseToString(value) {
-    if (typeof value == "string") {
-      return value;
-    }
-    if (isArray_default(value)) {
-      return arrayMap_default(value, baseToString) + "";
-    }
-    if (isSymbol_default(value)) {
-      return symbolToString ? symbolToString.call(value) : "";
-    }
-    var result = value + "";
-    return result == "0" && 1 / value == -INFINITY ? "-0" : result;
-  }
-  var baseToString_default = baseToString;
-
-  // node_modules/lodash-es/_trimmedEndIndex.js
-  var reWhitespace = /\s/;
-  function trimmedEndIndex(string) {
-    var index = string.length;
-    while (index-- && reWhitespace.test(string.charAt(index))) {
-    }
-    return index;
-  }
-  var trimmedEndIndex_default = trimmedEndIndex;
-
-  // node_modules/lodash-es/_baseTrim.js
-  var reTrimStart = /^\s+/;
-  function baseTrim(string) {
-    return string ? string.slice(0, trimmedEndIndex_default(string) + 1).replace(reTrimStart, "") : string;
-  }
-  var baseTrim_default = baseTrim;
-
-  // node_modules/lodash-es/isObject.js
-  function isObject(value) {
-    var type3 = typeof value;
-    return value != null && (type3 == "object" || type3 == "function");
-  }
-  var isObject_default = isObject;
-
-  // node_modules/lodash-es/toNumber.js
-  var NAN = 0 / 0;
-  var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
-  var reIsBinary = /^0b[01]+$/i;
-  var reIsOctal = /^0o[0-7]+$/i;
-  var freeParseInt = parseInt;
-  function toNumber(value) {
-    if (typeof value == "number") {
-      return value;
-    }
-    if (isSymbol_default(value)) {
-      return NAN;
-    }
-    if (isObject_default(value)) {
-      var other = typeof value.valueOf == "function" ? value.valueOf() : value;
-      value = isObject_default(other) ? other + "" : other;
-    }
-    if (typeof value != "string") {
-      return value === 0 ? value : +value;
-    }
-    value = baseTrim_default(value);
-    var isBinary = reIsBinary.test(value);
-    return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value;
-  }
-  var toNumber_default = toNumber;
-
-  // node_modules/lodash-es/toString.js
-  function toString(value) {
-    return value == null ? "" : baseToString_default(value);
-  }
-  var toString_default = toString;
-
-  // node_modules/lodash-es/_basePropertyOf.js
-  function basePropertyOf(object) {
-    return function(key) {
-      return object == null ? void 0 : object[key];
-    };
-  }
-  var basePropertyOf_default = basePropertyOf;
-
-  // node_modules/lodash-es/now.js
-  var now2 = function() {
-    return root_default.Date.now();
-  };
-  var now_default = now2;
-
-  // node_modules/lodash-es/debounce.js
-  var FUNC_ERROR_TEXT = "Expected a function";
-  var nativeMax = Math.max;
-  var nativeMin = Math.min;
-  function debounce(func, wait, options2) {
-    var lastArgs, lastThis, maxWait, result, timerId, lastCallTime, lastInvokeTime = 0, leading = false, maxing = false, trailing = true;
-    if (typeof func != "function") {
-      throw new TypeError(FUNC_ERROR_TEXT);
-    }
-    wait = toNumber_default(wait) || 0;
-    if (isObject_default(options2)) {
-      leading = !!options2.leading;
-      maxing = "maxWait" in options2;
-      maxWait = maxing ? nativeMax(toNumber_default(options2.maxWait) || 0, wait) : maxWait;
-      trailing = "trailing" in options2 ? !!options2.trailing : trailing;
-    }
-    function invokeFunc(time) {
-      var args = lastArgs, thisArg = lastThis;
-      lastArgs = lastThis = void 0;
-      lastInvokeTime = time;
-      result = func.apply(thisArg, args);
-      return result;
-    }
-    function leadingEdge(time) {
-      lastInvokeTime = time;
-      timerId = setTimeout(timerExpired, wait);
-      return leading ? invokeFunc(time) : result;
-    }
-    function remainingWait(time) {
-      var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime, timeWaiting = wait - timeSinceLastCall;
-      return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting;
-    }
-    function shouldInvoke(time) {
-      var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime;
-      return lastCallTime === void 0 || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait;
-    }
-    function timerExpired() {
-      var time = now_default();
-      if (shouldInvoke(time)) {
-        return trailingEdge(time);
-      }
-      timerId = setTimeout(timerExpired, remainingWait(time));
-    }
-    function trailingEdge(time) {
-      timerId = void 0;
-      if (trailing && lastArgs) {
-        return invokeFunc(time);
-      }
-      lastArgs = lastThis = void 0;
-      return result;
-    }
-    function cancel() {
-      if (timerId !== void 0) {
-        clearTimeout(timerId);
-      }
-      lastInvokeTime = 0;
-      lastArgs = lastCallTime = lastThis = timerId = void 0;
-    }
-    function flush() {
-      return timerId === void 0 ? result : trailingEdge(now_default());
-    }
-    function debounced() {
-      var time = now_default(), isInvoking = shouldInvoke(time);
-      lastArgs = arguments;
-      lastThis = this;
-      lastCallTime = time;
-      if (isInvoking) {
-        if (timerId === void 0) {
-          return leadingEdge(lastCallTime);
-        }
-        if (maxing) {
-          clearTimeout(timerId);
-          timerId = setTimeout(timerExpired, wait);
-          return invokeFunc(lastCallTime);
-        }
-      }
-      if (timerId === void 0) {
-        timerId = setTimeout(timerExpired, wait);
-      }
-      return result;
-    }
-    debounced.cancel = cancel;
-    debounced.flush = flush;
-    return debounced;
-  }
-  var debounce_default = debounce;
-
-  // node_modules/lodash-es/_escapeHtmlChar.js
-  var htmlEscapes = {
-    "&": "&amp;",
-    "<": "&lt;",
-    ">": "&gt;",
-    '"': "&quot;",
-    "'": "&#39;"
-  };
-  var escapeHtmlChar = basePropertyOf_default(htmlEscapes);
-  var escapeHtmlChar_default = escapeHtmlChar;
-
-  // node_modules/lodash-es/escape.js
-  var reUnescapedHtml = /[&<>"']/g;
-  var reHasUnescapedHtml = RegExp(reUnescapedHtml.source);
-  function escape2(string) {
-    string = toString_default(string);
-    return string && reHasUnescapedHtml.test(string) ? string.replace(reUnescapedHtml, escapeHtmlChar_default) : string;
-  }
-  var escape_default = escape2;
-
-  // node_modules/lodash-es/throttle.js
-  var FUNC_ERROR_TEXT2 = "Expected a function";
-  function throttle(func, wait, options2) {
-    var leading = true, trailing = true;
-    if (typeof func != "function") {
-      throw new TypeError(FUNC_ERROR_TEXT2);
-    }
-    if (isObject_default(options2)) {
-      leading = "leading" in options2 ? !!options2.leading : leading;
-      trailing = "trailing" in options2 ? !!options2.trailing : trailing;
-    }
-    return debounce_default(func, wait, {
-      "leading": leading,
-      "maxWait": wait,
-      "trailing": trailing
-    });
-  }
-  var throttle_default = throttle;
-
-  // node_modules/lodash-es/_unescapeHtmlChar.js
-  var htmlUnescapes = {
-    "&amp;": "&",
-    "&lt;": "<",
-    "&gt;": ">",
-    "&quot;": '"',
-    "&#39;": "'"
-  };
-  var unescapeHtmlChar = basePropertyOf_default(htmlUnescapes);
-  var unescapeHtmlChar_default = unescapeHtmlChar;
-
-  // node_modules/lodash-es/unescape.js
-  var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g;
-  var reHasEscapedHtml = RegExp(reEscapedHtml.source);
-  function unescape2(string) {
-    string = toString_default(string);
-    return string && reHasEscapedHtml.test(string) ? string.replace(reEscapedHtml, unescapeHtmlChar_default) : string;
-  }
-  var unescape_default = unescape2;
-
   // modules/core/localizer.js
   var _mainLocalizer = coreLocalizer();
   var _t = _mainLocalizer.t;
         return _loadPromise;
       let filesToFetch = [
         "languages",
+        // load the list of languages
         "locales"
+        // load the list of supported locales
       ];
       const localeDirs = {
         general: "locales",
-        tagging: "https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/translations"
+        tagging: presetsCdnUrl + "dist/translations"
       };
       let fileMap = _mainFileFetcher.fileMap();
       for (let scopeId in localeDirs) {
       }
     };
     localizer.t.append = function(stringId, replacements, locale2) {
-      return function(selection2) {
+      const ret = function(selection2) {
         const info = localizer.tInfo(stringId, replacements, locale2);
         return selection2.append("span").attr("class", "localized-text").attr("lang", info.locale || "und").text((replacements && replacements.prefix || "") + info.text + (replacements && replacements.suffix || ""));
       };
+      ret.stringId = stringId;
+      return ret;
     };
     localizer.languageName = (code, options2) => {
       if (_languageNames[code]) {
     };
     _this.index = (id2) => _this.collection.findIndex((d) => d.id === id2);
     _this.matchGeometry = (geometry) => {
-      return presetCollection(_this.collection.filter((d) => d.matchGeometry(geometry)));
+      return presetCollection(
+        _this.collection.filter((d) => d.matchGeometry(geometry))
+      );
     };
     _this.matchAllGeometry = (geometries) => {
-      return presetCollection(_this.collection.filter((d) => d && d.matchAllGeometry(geometries)));
+      return presetCollection(
+        _this.collection.filter((d) => d && d.matchAllGeometry(geometries))
+      );
     };
     _this.matchAnyGeometry = (geometries) => {
-      return presetCollection(_this.collection.filter((d) => geometries.some((geom) => d.matchGeometry(geom))));
+      return presetCollection(
+        _this.collection.filter((d) => geometries.some((geom) => d.matchGeometry(geom)))
+      );
     };
     _this.fallback = (geometry) => {
       let id2 = geometry;
               if (strings.some((s) => s === value)) {
                 return strings.find((s) => s === value);
               } else {
-                return strings.find((s) => s.includes(value));
+                return strings.filter((s) => s.includes(value)).sort((a2, b2) => a2.length - b2.length)[0];
               }
             };
             aCompare = findMatchingAlias([aCompare].concat(a[aliasesProp]()));
       }
       let pool = _this.collection;
       if (Array.isArray(loc)) {
-        const validLocations = _mainLocations.locationsAt(loc);
-        pool = pool.filter((a) => !a.locationSetID || validLocations[a.locationSetID]);
+        const validHere = _sharedLocationManager.locationSetsAt(loc);
+        pool = pool.filter((a) => !a.locationSetID || validHere[a.locationSetID]);
       }
       const searchable = pool.filter((a) => a.searchable !== false && a.suggestion !== true);
       const suggestions = pool.filter((a) => a.suggestion === true);
       if (value.includes("=")) {
         leadingTagKeyValues = searchable.filter((a) => a.tags && Object.keys(a.tags).some((key) => key + "=" + a.tags[key] === value)).concat(searchable.filter((a) => a.tags && Object.keys(a.tags).some((key) => leading(key + "=" + a.tags[key]))));
       }
-      let results = leadingNames.concat(leadingSuggestions, leadingNamesStripped, leadingSuggestionsStripped, leadingTerms, leadingSuggestionTerms, leadingTagValues, similarName, similarSuggestions, similarTerms, leadingTagKeyValues).slice(0, MAXRESULTS - 1);
+      let results = leadingNames.concat(
+        leadingSuggestions,
+        leadingNamesStripped,
+        leadingSuggestionsStripped,
+        leadingTerms,
+        leadingSuggestionTerms,
+        leadingTagValues,
+        similarName,
+        similarSuggestions,
+        similarTerms,
+        leadingTagKeyValues
+      ).slice(0, MAXRESULTS - 1);
       if (geometry) {
         if (typeof geometry === "string") {
           results.push(_this.fallback(geometry));
     let _searchName;
     let _searchNameStripped;
     _this.id = categoryID;
-    _this.members = presetCollection((category.members || []).map((presetID) => allPresets[presetID]).filter(Boolean));
+    _this.members = presetCollection(
+      (category.members || []).map((presetID) => allPresets[presetID]).filter(Boolean)
+    );
     _this.geometry = _this.members.collection.reduce((acc, preset) => {
       for (let i2 in preset.geometry) {
         const geometry = preset.geometry[i2];
     _this.matchAllGeometry = (geometries) => _this.members.collection.some((preset) => preset.matchAllGeometry(geometries));
     _this.matchScore = () => -1;
     _this.name = () => _t(`_tagging.presets.categories.${categoryID}.name`, { "default": categoryID });
-    _this.nameLabel = () => _t.html(`_tagging.presets.categories.${categoryID}.name`, { "default": categoryID });
+    _this.nameLabel = () => _t.append(`_tagging.presets.categories.${categoryID}.name`, { "default": categoryID });
     _this.terms = () => [];
     _this.searchName = () => {
       if (!_searchName) {
   }
 
   // modules/presets/field.js
-  function presetField(fieldID, field) {
+  function presetField(fieldID, field, allFields) {
+    allFields = allFields || {};
     let _this = Object.assign({}, field);
     _this.id = fieldID;
     _this.safeid = utilSafeClassName(fieldID);
     };
     _this.t = (scope, options2) => _t(`_tagging.presets.fields.${fieldID}.${scope}`, options2);
     _this.t.html = (scope, options2) => _t.html(`_tagging.presets.fields.${fieldID}.${scope}`, options2);
+    _this.t.append = (scope, options2) => _t.append(`_tagging.presets.fields.${fieldID}.${scope}`, options2);
     _this.hasTextForStringId = (scope) => _mainLocalizer.hasTextForStringId(`_tagging.presets.fields.${fieldID}.${scope}`);
-    _this.title = () => _this.overrideLabel || _this.t("label", { "default": fieldID });
-    _this.label = () => _this.overrideLabel || _this.t.html("label", { "default": fieldID });
-    const _placeholder = _this.placeholder;
-    _this.placeholder = () => _this.t("placeholder", { "default": _placeholder });
+    _this.resolveReference = (which) => {
+      const referenceRegex = /^\{(.*)\}$/;
+      const match = (field[which] || "").match(referenceRegex);
+      if (match) {
+        const field2 = allFields[match[1]];
+        if (field2) {
+          return field2;
+        }
+        console.error(`Unable to resolve referenced field: ${match[1]}`);
+      }
+      return _this;
+    };
+    _this.title = () => _this.overrideLabel || _this.resolveReference("label").t("label", { "default": fieldID });
+    _this.label = () => _this.overrideLabel ? (selection2) => selection2.text(_this.overrideLabel) : _this.resolveReference("label").t.append("label", { "default": fieldID });
+    _this.placeholder = () => _this.resolveReference("placeholder").t("placeholder", { "default": "" });
     _this.originalTerms = (_this.terms || []).join();
-    _this.terms = () => _this.t("terms", { "default": _this.originalTerms }).toLowerCase().trim().split(/\s*,+\s*/);
+    _this.terms = () => _this.resolveReference("label").t("terms", { "default": _this.originalTerms }).toLowerCase().trim().split(/\s*,+\s*/);
     _this.increment = _this.type === "number" ? _this.increment || 1 : void 0;
     return _this;
   }
     let _searchNameStripped;
     let _searchAliases;
     let _searchAliasesStripped;
+    const referenceRegex = /^\{(.*)\}$/;
     _this.id = presetID;
     _this.safeid = utilSafeClassName(presetID);
     _this.originalTerms = (_this.terms || []).join();
     _this.originalReference = _this.reference || {};
     _this.originalFields = _this.fields || [];
     _this.originalMoreFields = _this.moreFields || [];
-    _this.fields = () => _resolvedFields || (_resolvedFields = resolve("fields"));
-    _this.moreFields = () => _resolvedMoreFields || (_resolvedMoreFields = resolve("moreFields"));
+    _this.fields = () => _resolvedFields || (_resolvedFields = resolveFields("fields"));
+    _this.moreFields = () => _resolvedMoreFields || (_resolvedMoreFields = resolveFields("moreFields"));
     _this.resetFields = () => _resolvedFields = _resolvedMoreFields = null;
     _this.tags = _this.tags || {};
     _this.addTags = _this.addTags || _this.tags;
       const textID = `_tagging.presets.presets.${presetID}.${scope}`;
       return _t(textID, options2);
     };
-    _this.t.html = (scope, options2) => {
+    _this.t.append = (scope, options2) => {
       const textID = `_tagging.presets.presets.${presetID}.${scope}`;
-      return _t.html(textID, options2);
+      return _t.append(textID, options2);
     };
+    function resolveReference(which) {
+      const match = (_this[which] || "").match(referenceRegex);
+      if (match) {
+        const preset2 = allPresets[match[1]];
+        if (preset2) {
+          return preset2;
+        }
+        console.error(`Unable to resolve referenced preset: ${match[1]}`);
+      }
+      return _this;
+    }
     _this.name = () => {
-      return _this.t("name", { "default": _this.originalName });
+      return resolveReference("originalName").t("name", { "default": _this.originalName || presetID });
     };
     _this.nameLabel = () => {
-      return _this.t.html("name", { "default": _this.originalName });
+      return resolveReference("originalName").t.append("name", { "default": _this.originalName || presetID });
     };
     _this.subtitle = () => {
       if (_this.suggestion) {
       if (_this.suggestion) {
         let path = presetID.split("/");
         path.pop();
-        return _t.html("_tagging.presets.presets." + path.join("/") + ".name");
+        return _t.append("_tagging.presets.presets." + path.join("/") + ".name");
       }
       return null;
     };
     _this.aliases = () => {
-      return _this.t("aliases", { "default": _this.originalAliases }).trim().split(/\s*[\r\n]+\s*/);
+      return resolveReference("originalName").t("aliases", { "default": _this.originalAliases }).trim().split(/\s*[\r\n]+\s*/);
+    };
+    _this.terms = () => {
+      return resolveReference("originalName").t("terms", { "default": _this.originalTerms }).toLowerCase().trim().split(/\s*,+\s*/);
     };
-    _this.terms = () => _this.t("terms", { "default": _this.originalTerms }).toLowerCase().trim().split(/\s*,+\s*/);
     _this.searchName = () => {
       if (!_searchName) {
         _searchName = (_this.suggestion ? _this.originalName : _this.name()).toLowerCase();
       tags = utilObjectOmit(tags, Object.keys(removeTags));
       if (geometry && !skipFieldDefaults) {
         _this.fields().forEach((field) => {
-          if (field.matchGeometry(geometry) && field.key && field.default === tags[field.key]) {
+          if (field.matchGeometry(geometry) && field.key && field.default === tags[field.key] && (!ignoringKeys || ignoringKeys.indexOf(field.key) === -1)) {
             delete tags[field.key];
           }
         });
       }
       return tags;
     };
-    function resolve(which) {
+    function resolveFields(which) {
       const fieldIDs = which === "fields" ? _this.originalFields : _this.originalMoreFields;
       let resolved = [];
       fieldIDs.forEach((fieldID) => {
-        const match = fieldID.match(/\{(.*)\}/);
+        const match = fieldID.match(referenceRegex);
         if (match !== null) {
           resolved = resolved.concat(inheritFields(match[1], which));
         } else if (allFields[fieldID]) {
         }
       }
       function shouldInherit(f2) {
-        if (f2.key && _this.tags[f2.key] !== void 0 && f2.type !== "multiCombo" && f2.type !== "semiCombo" && f2.type !== "manyCombo" && f2.type !== "check")
+        if (f2.key && _this.tags[f2.key] !== void 0 && // inherit anyway if multiple values are allowed or just a checkbox
+        f2.type !== "multiCombo" && f2.type !== "semiCombo" && f2.type !== "manyCombo" && f2.type !== "check")
           return false;
         return true;
       }
           fields: vals[3]
         });
         osmSetAreaKeys(_this.areaKeys());
+        osmSetLineTags(_this.lineTags());
         osmSetPointTags(_this.pointTags());
         osmSetVertexTags(_this.vertexTags());
       });
         Object.keys(d.fields).forEach((fieldID) => {
           let f2 = d.fields[fieldID];
           if (f2) {
-            f2 = presetField(fieldID, f2);
+            f2 = presetField(fieldID, f2, _fields);
             if (f2.locationSet)
               newLocationSets.push(f2);
             _fields[fieldID] = f2;
         Object.keys(d.defaults).forEach((geometry) => {
           const def = d.defaults[geometry];
           if (Array.isArray(def)) {
-            _defaults[geometry] = presetCollection(def.map((id2) => _presets[id2] || _categories[id2]).filter(Boolean));
+            _defaults[geometry] = presetCollection(
+              def.map((id2) => _presets[id2] || _categories[id2]).filter(Boolean)
+            );
           } else {
             delete _defaults[geometry];
           }
         });
       });
       if (d.featureCollection && Array.isArray(d.featureCollection.features)) {
-        _mainLocations.mergeCustomGeoJSON(d.featureCollection);
+        _sharedLocationManager.mergeCustomGeoJSON(d.featureCollection);
       }
       if (newLocationSets.length) {
-        _mainLocations.mergeLocationSets(newLocationSets);
+        _sharedLocationManager.mergeLocationSets(newLocationSets);
       }
       return _this;
     };
         }
       }
       if (bestMatch && bestMatch.locationSetID && bestMatch.locationSetID !== "+[Q2]" && Array.isArray(loc)) {
-        let validLocations = _mainLocations.locationsAt(loc);
-        if (!validLocations[bestMatch.locationSetID]) {
+        const validHere = _sharedLocationManager.locationSetsAt(loc);
+        if (!validHere[bestMatch.locationSetID]) {
           matchCandidates.sort((a, b) => a.score < b.score ? 1 : -1);
           for (let i2 = 0; i2 < matchCandidates.length; i2++) {
             const candidateScore = matchCandidates[i2];
-            if (!candidateScore.candidate.locationSetID || validLocations[candidateScore.candidate.locationSetID]) {
+            if (!candidateScore.candidate.locationSetID || validHere[candidateScore.candidate.locationSetID]) {
               bestMatch = candidateScore.candidate;
               bestScore = candidateScore.score;
               break;
         footway: true,
         railway: true,
         junction: true,
+        traffic_calming: true,
         type: true
       };
       let areaKeys = {};
         let key;
         for (key in p.addTags) {
           const value = p.addTags[key];
-          if (key in areaKeys && p.geometry.indexOf("line") !== -1 && value !== "*") {
+          if (key in areaKeys && // probably an area...
+          p.geometry.indexOf("line") !== -1 && // but sometimes a line
+          value !== "*") {
             areaKeys[key][value] = true;
           }
         }
       });
       return areaKeys;
     };
+    _this.lineTags = () => {
+      return _this.collection.filter((lineTags, d) => {
+        if (d.suggestion || d.replacement || d.searchable === false)
+          return lineTags;
+        const keys = d.tags && Object.keys(d.tags);
+        const key = keys && keys.length && keys[0];
+        if (!key)
+          return lineTags;
+        if (d.geometry.indexOf("line") !== -1) {
+          lineTags[key] = lineTags[key] || [];
+          lineTags[key].push(d.tags);
+        }
+        return lineTags;
+      }, {});
+    };
     _this.pointTags = () => {
       return _this.collection.reduce((pointTags, d) => {
         if (d.suggestion || d.replacement || d.searchable === false)
     };
     _this.field = (id2) => _fields[id2];
     _this.universal = () => _universal;
-    _this.defaults = (geometry, n2, startWithRecents, loc) => {
+    _this.defaults = (geometry, n2, startWithRecents, loc, extraPresets) => {
       let recents = [];
       if (startWithRecents) {
         recents = _this.recent().matchGeometry(geometry).collection.slice(0, 4);
       } else {
         defaults2 = _defaults[geometry].collection.concat(_this.fallback(geometry));
       }
-      let result = presetCollection(utilArrayUniq(recents.concat(defaults2)).slice(0, n2 - 1));
+      let result = presetCollection(
+        utilArrayUniq(recents.concat(defaults2).concat(extraPresets || [])).slice(0, n2 - 1)
+      );
       if (Array.isArray(loc)) {
-        const validLocations = _mainLocations.locationsAt(loc);
-        result.collection = result.collection.filter((a) => !a.locationSetID || validLocations[a.locationSetID]);
+        const validHere = _sharedLocationManager.locationSetsAt(loc);
+        result.collection = result.collection.filter((a) => !a.locationSetID || validHere[a.locationSetID]);
       }
       return result;
     };
       return _this;
     };
     _this.recent = () => {
-      return presetCollection(utilArrayUniq(_this.getRecents().map((d) => d.preset)));
+      return presetCollection(
+        utilArrayUniq(_this.getRecents().map((d) => d.preset).filter((d) => d.searchable !== false))
+      );
     };
     function RibbonItem(preset, source) {
       let item = {};
   }
   function utilDisplayName(entity) {
     var localizedNameKey = "name:" + _mainLocalizer.languageCode().toLowerCase();
-    var name2 = entity.tags[localizedNameKey] || entity.tags.name || "";
-    if (name2)
-      return name2;
+    var name = entity.tags[localizedNameKey] || entity.tags.name || "";
+    if (name)
+      return name;
     var tags = {
       direction: entity.tags.direction,
       from: entity.tags.from,
       }
     }
     if (keyComponents.length) {
-      name2 = _t("inspector.display_name." + keyComponents.join("_"), tags);
+      name = _t("inspector.display_name." + keyComponents.join("_"), tags);
     }
-    return name2;
+    return name;
   }
   function utilDisplayNameForPath(entity) {
-    var name2 = utilDisplayName(entity);
+    var name = utilDisplayName(entity);
     var isFirefox = utilDetect().browser.toLowerCase().indexOf("firefox") > -1;
     var isNewChromium = Number(utilDetect().version.split(".")[0]) >= 96;
-    if (!isFirefox && !isNewChromium && name2 && rtlRegex.test(name2)) {
-      name2 = fixRTLTextForSvg(name2);
+    if (!isFirefox && !isNewChromium && name && rtlRegex.test(name)) {
+      name = fixRTLTextForSvg(name);
     }
-    return name2;
+    return name;
   }
   function utilDisplayType(id2) {
     return {
     for (var key in tags) {
       if (!Array.isArray(tags[key]))
         continue;
-      tags[key] = tags[key].sort(function(val1, val2) {
+      tags[key] = tags[key].sort(function(val12, val2) {
         var key2 = key2;
         var count2 = tagCounts[key2 + "=" + val2];
-        var count1 = tagCounts[key2 + "=" + val1];
+        var count1 = tagCounts[key2 + "=" + val12];
         if (count2 !== count1) {
           return count2 - count1;
         }
-        if (val2 && val1) {
-          return val1.localeCompare(val2);
+        if (val2 && val12) {
+          return val12.localeCompare(val2);
         }
-        return val1 ? 1 : -1;
+        return val12 ? 1 : -1;
       });
     }
     return tags;
     return str2.split("&").reduce(function(obj, pair2) {
       var parts = pair2.split("=");
       if (parts.length === 2) {
-        obj[parts[0]] = parts[1] === null ? "" : decodeURIComponent(parts[1]);
+        obj[parts[0]] = null === parts[1] ? "" : decodeURIComponent(parts[1]);
       }
       return obj;
     }, {});
     var s = document.body;
     if (property in s)
       return property;
-    property = property.substr(0, 1).toUpperCase() + property.substr(1);
+    property = property.slice(0, 1).toUpperCase() + property.slice(1);
     while (++i2 < n2) {
       if (prefixes2[i2] + property in s) {
         return prefixes2[i2] + property;
         if (b.charAt(i2 - 1) === a.charAt(j2 - 1)) {
           matrix[i2][j2] = matrix[i2 - 1][j2 - 1];
         } else {
-          matrix[i2][j2] = Math.min(matrix[i2 - 1][j2 - 1] + 1, Math.min(matrix[i2][j2 - 1] + 1, matrix[i2 - 1][j2] + 1));
+          matrix[i2][j2] = Math.min(
+            matrix[i2 - 1][j2 - 1] + 1,
+            // substitution
+            Math.min(
+              matrix[i2][j2 - 1] + 1,
+              // insertion
+              matrix[i2 - 1][j2] + 1
+            )
+          );
         }
       }
     }
     }
     return ids[oldestIDIndex];
   }
+  function utilCleanOsmString(val, maxChars) {
+    if (val === void 0 || val === null) {
+      val = "";
+    } else {
+      val = val.toString();
+    }
+    val = val.trim();
+    if (val.normalize)
+      val = val.normalize("NFC");
+    return utilUnicodeCharsTruncated(val, maxChars);
+  }
 
   // modules/osm/entity.js
   function osmEntity(attrs) {
     }
     return new osmEntity().initialize(arguments);
   }
-  osmEntity.id = function(type3) {
-    return osmEntity.id.fromOSM(type3, osmEntity.id.next[type3]--);
+  osmEntity.id = function(type2) {
+    return osmEntity.id.fromOSM(type2, osmEntity.id.next[type2]--);
   };
   osmEntity.id.next = {
     changeset: -1,
     way: -1,
     relation: -1
   };
-  osmEntity.id.fromOSM = function(type3, id2) {
-    return type3[0] + id2;
+  osmEntity.id.fromOSM = function(type2, id2) {
+    return type2[0] + id2;
   };
   osmEntity.id.toOSM = function(id2) {
     var match = id2.match(/^[cnwr](-?\d+)$/);
           merged[k] = t2;
         } else if (t1 !== t2) {
           changed = true;
-          merged[k] = utilUnicodeCharsTruncated(utilArrayUnion(t1.split(/;\s*/), t2.split(/;\s*/)).join(";"), 255);
+          merged[k] = utilUnicodeCharsTruncated(
+            utilArrayUnion(t1.split(/;\s*/), t2.split(/;\s*/)).join(";"),
+            255
+            // avoid exceeding character limit; see also context.maxCharsForTagValue()
+          );
         }
       }
       return changed ? this.update({ tags: merged }) : this;
         return -10;
       return 0;
     },
+    // the approximate width of the line based on its tags except its `width` tag
     impliedLineWidthMeters: function() {
       var averageWidths = {
         highway: {
+          // width is for single lane
           motorway: 5,
           motorway_link: 5,
           trunk: 4.5,
           footway: 1.5
         },
         railway: {
+          // width includes ties and rail bed, not just track gauge
           rail: 2.5,
           light_rail: 2.5,
           tram: 2.5,
       }
       return false;
     },
+    // Some identifier for tag that implies that this way is "sided",
+    // i.e. the right side is the 'inside' (e.g. the right side of a
+    // natural=cliff is lower).
     sidednessIdentifier: function() {
       for (var key in this.tags) {
         var value = this.tags[key];
       }
       return true;
     },
+    // returns an object with the tag that implies this is an area, if any
     tagSuggestingArea: function() {
       return osmTagSuggestingArea(this.tags);
     },
         return this.isArea() ? "area" : "line";
       });
     },
+    // returns an array of objects representing the segments between the nodes in this way
     segments: function(graph) {
       function segmentExtent(graph2) {
         var n1 = graph2.hasEntity(this.nodes[0]);
         return segments;
       });
     },
+    // If this way is not closed, append the beginning node to the end of the nodelist to close it.
     close: function() {
       if (this.isClosed() || !this.nodes.length)
         return this;
       nodes.push(nodes[0]);
       return this.update({ nodes });
     },
+    // If this way is closed, remove any connector nodes from the end of the nodelist to unclose it.
     unclose: function() {
       if (!this.isClosed())
         return this;
       nodes = nodes.filter(noRepeatNodes);
       return this.update({ nodes });
     },
+    // Adds a node (id) in front of the node which is currently at position index.
+    // If index is undefined, the node will be added to the end of the way for linear ways,
+    //   or just before the final connecting node for circular ways.
+    // Consecutive duplicates are eliminated including existing ones.
+    // Circularity is always preserved when adding a node.
     addNode: function(id2, index) {
       var nodes = this.nodes.slice();
       var isClosed = this.isClosed();
       }
       return this.update({ nodes });
     },
+    // Replaces the node which is currently at position index with the given node (id).
+    // Consecutive duplicates are eliminated including existing ones.
+    // Circularity is preserved when updating a node.
     updateNode: function(id2, index) {
       var nodes = this.nodes.slice();
       var isClosed = this.isClosed();
       }
       return this.update({ nodes });
     },
+    // Replaces each occurrence of node id needle with replacement.
+    // Consecutive duplicates are eliminated including existing ones.
+    // Circularity is preserved.
     replaceNode: function(needleID, replacementID) {
       var nodes = this.nodes.slice();
       var isClosed = this.isClosed();
       }
       return this.update({ nodes });
     },
+    // Removes each occurrence of node id.
+    // Consecutive duplicates are eliminated including existing ones.
+    // Circularity is preserved.
     removeNode: function(id2) {
       var nodes = this.nodes.slice();
       var isClosed = this.isClosed();
   function actionAddMidpoint(midpoint, node) {
     return function(graph) {
       graph = graph.replace(node.move(midpoint.loc));
-      var parents = utilArrayIntersection(graph.parentWays(graph.entity(midpoint.edge[0])), graph.parentWays(graph.entity(midpoint.edge[1])));
+      var parents = utilArrayIntersection(
+        graph.parentWays(graph.entity(midpoint.edge[0])),
+        graph.parentWays(graph.entity(midpoint.edge[1]))
+      );
       parents.forEach(function(way) {
         for (var i2 = 0; i2 < way.nodes.length - 1; i2++) {
           if (geoEdgeEqual([way.nodes[i2], way.nodes[i2 + 1]], midpoint.edge)) {
       var entity = graph.entity(entityID);
       var geometry = entity.geometry(graph);
       var tags = entity.tags;
+      var preserveKeys;
+      if (newPreset) {
+        preserveKeys = [];
+        if (newPreset.addTags) {
+          preserveKeys = preserveKeys.concat(Object.keys(newPreset.addTags));
+        }
+        if (oldPreset && !oldPreset.id.startsWith(newPreset.id)) {
+          newPreset.fields().concat(newPreset.moreFields()).filter((f2) => f2.matchGeometry(geometry)).map((f2) => f2.key).filter(Boolean).forEach((key) => preserveKeys.push(key));
+        }
+      }
       if (oldPreset)
-        tags = oldPreset.unsetTags(tags, geometry, newPreset && newPreset.addTags ? Object.keys(newPreset.addTags) : null);
+        tags = oldPreset.unsetTags(tags, geometry, preserveKeys);
       if (newPreset)
         tags = newPreset.setTags(tags, geometry, skipFieldDefaults);
       return graph.replace(entity.update({ tags }));
   }
 
   // modules/osm/node.js
+  var cardinal = {
+    north: 0,
+    n: 0,
+    northnortheast: 22,
+    nne: 22,
+    northeast: 45,
+    ne: 45,
+    eastnortheast: 67,
+    ene: 67,
+    east: 90,
+    e: 90,
+    eastsoutheast: 112,
+    ese: 112,
+    southeast: 135,
+    se: 135,
+    southsoutheast: 157,
+    sse: 157,
+    south: 180,
+    s: 180,
+    southsouthwest: 202,
+    ssw: 202,
+    southwest: 225,
+    sw: 225,
+    westsouthwest: 247,
+    wsw: 247,
+    west: 270,
+    w: 270,
+    westnorthwest: 292,
+    wnw: 292,
+    northwest: 315,
+    nw: 315,
+    northnorthwest: 337,
+    nnw: 337
+  };
   function osmNode() {
     if (!(this instanceof osmNode)) {
       return new osmNode().initialize(arguments);
     isDegenerate: function() {
       return !(Array.isArray(this.loc) && this.loc.length === 2 && this.loc[0] >= -180 && this.loc[0] <= 180 && this.loc[1] >= -90 && this.loc[1] <= 90);
     },
+    // Inspect tags and geometry to determine which direction(s) this node/vertex points
     directions: function(resolver, projection2) {
       var val;
       var i2;
       }
       if (val === "")
         return [];
-      var cardinal = {
-        north: 0,
-        n: 0,
-        northnortheast: 22,
-        nne: 22,
-        northeast: 45,
-        ne: 45,
-        eastnortheast: 67,
-        ene: 67,
-        east: 90,
-        e: 90,
-        eastsoutheast: 112,
-        ese: 112,
-        southeast: 135,
-        se: 135,
-        southsoutheast: 157,
-        sse: 157,
-        south: 180,
-        s: 180,
-        southsouthwest: 202,
-        ssw: 202,
-        southwest: 225,
-        sw: 225,
-        westsouthwest: 247,
-        wsw: 247,
-        west: 270,
-        w: 270,
-        westnorthwest: 292,
-        wnw: 292,
-        northwest: 315,
-        nw: 315,
-        northnorthwest: 337,
-        nnw: 337
-      };
       var values = val.split(";");
       var results = [];
       values.forEach(function(v) {
           }
         }, this);
         Object.keys(nodeIds).forEach(function(nodeId) {
-          results.push(geoAngle(this, resolver.entity(nodeId), projection2) * (180 / Math.PI) + 90);
+          results.push(
+            geoAngle(this, resolver.entity(nodeId), projection2) * (180 / Math.PI) + 90
+          );
         }, this);
       }, this);
       return utilArrayUniq(results);
           indexRange += nodes.length;
         }
         for (j2 = 1; j2 < indexRange; j2++) {
-          var point = geoVecInterp(hull[i2], hull[i2 + 1], j2 / indexRange);
-          var node = nodes[(j2 + startIndex) % nodes.length].move(projection2.invert(point));
+          var point2 = geoVecInterp(hull[i2], hull[i2 + 1], j2 / indexRange);
+          var node = nodes[(j2 + startIndex) % nodes.length].move(projection2.invert(point2));
           graph = graph.replace(node);
         }
       }
       var entities = ids.map(function(id2) {
         return graph.entity(id2);
       });
-      return Object.assign({ line: [] }, utilArrayGroupBy(entities, function(entity) {
-        return entity.geometry(graph);
-      }));
+      return Object.assign(
+        { line: [] },
+        utilArrayGroupBy(entities, function(entity) {
+          return entity.geometry(graph);
+        })
+      );
     }
     var action = function(graph) {
       var ways = ids.map(graph.entity, graph);
           return;
         var multipolygon = multipolygons[0];
         for (var key in survivor.tags) {
-          if (multipolygon.tags[key] && multipolygon.tags[key] !== survivor.tags[key])
+          if (multipolygon.tags[key] && // don't collapse if tags cannot be cleanly merged
+          multipolygon.tags[key] !== survivor.tags[key])
             return;
         }
         survivor = survivor.mergeTags(multipolygon.tags);
         graph = graph.replace(survivor);
-        graph = actionDeleteRelation(multipolygon.id, true)(graph);
+        graph = actionDeleteRelation(
+          multipolygon.id,
+          true
+          /* allow untagged members */
+        )(graph);
         var tags = Object.assign({}, survivor.tags);
         if (survivor.geometry(graph) !== "area") {
           tags.area = "yes";
             return e.loc;
           });
           var intersections = geoPathIntersections(path1, path2);
-          var common = utilArrayIntersection(joined[0].nodes.map(function(n2) {
-            return n2.loc.toString();
-          }), intersections.map(function(n2) {
-            return n2.toString();
-          }));
+          var common = utilArrayIntersection(
+            joined[0].nodes.map(function(n2) {
+              return n2.loc.toString();
+            }),
+            intersections.map(function(n2) {
+              return n2.toString();
+            })
+          );
           if (common.length !== intersections.length) {
             return "paths_intersect";
           }
       var entities = ids.map(function(id2) {
         return graph.entity(id2);
       });
-      return Object.assign({ point: [], area: [], line: [], relation: [] }, utilArrayGroupBy(entities, function(entity) {
-        return entity.geometry(graph);
-      }));
+      return Object.assign(
+        { point: [], area: [], line: [], relation: [] },
+        utilArrayGroupBy(entities, function(entity) {
+          return entity.geometry(graph);
+        })
+      );
     }
     var action = function(graph) {
       var geometries = groupEntitiesByGeometry(graph);
       var target = geometries.area[0] || geometries.line[0];
       var points = geometries.point;
-      points.forEach(function(point) {
-        target = target.mergeTags(point.tags);
+      points.forEach(function(point2) {
+        target = target.mergeTags(point2.tags);
         graph = graph.replace(target);
-        graph.parentRelations(point).forEach(function(parent) {
-          graph = graph.replace(parent.replaceMember(point, target));
+        graph.parentRelations(point2).forEach(function(parent) {
+          graph = graph.replace(parent.replaceMember(point2, target));
         });
         var nodes = utilArrayUniq(graph.childNodes(target));
-        var removeNode = point;
-        if (!point.isNew()) {
+        var removeNode = point2;
+        if (!point2.isNew()) {
           var inserted = false;
           var canBeReplaced = function(node2) {
             return !(graph.parentWays(node2).length > 1 || graph.parentRelations(node2).length);
           };
           var replaceNode = function(node2) {
-            graph = graph.replace(point.update({ tags: node2.tags, loc: node2.loc }));
-            target = target.replaceNode(node2.id, point.id);
+            graph = graph.replace(point2.update({ tags: node2.tags, loc: node2.loc }));
+            target = target.replaceNode(node2.id, point2.id);
             graph = graph.replace(target);
             removeNode = node2;
             inserted = true;
               break;
             }
           }
-          if (!inserted && point.hasInterestingTags()) {
+          if (!inserted && point2.hasInterestingTags()) {
             for (i2 = 0; i2 < nodes.length; i2++) {
               node = nodes[i2];
               if (canBeReplaced(node) && !node.hasInterestingTags()) {
             if (!inserted) {
               for (i2 = 0; i2 < nodes.length; i2++) {
                 node = nodes[i2];
-                if (canBeReplaced(node) && utilCompareIDs(point.id, node.id) < 0) {
+                if (canBeReplaced(node) && utilCompareIDs(point2.id, node.id) < 0) {
                   replaceNode(node);
                   break;
                 }
         }
       };
     },
+    // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
+    // XML. Returns a string.
     osmChangeJXON: function(changes) {
       var changeset_id = this.id;
       function nest(x, order) {
     isDegenerate: function() {
       return this.members.length === 0;
     },
+    // Return an array of members, each extended with an 'index' property whose value
+    // is the member index.
     indexedMembers: function() {
       var result = new Array(this.members.length);
       for (var i2 = 0; i2 < this.members.length; i2++) {
       }
       return result;
     },
+    // Return the first member with the given role. A copy of the member object
+    // is returned, extended with an 'index' property whose value is the member index.
     memberByRole: function(role) {
       for (var i2 = 0; i2 < this.members.length; i2++) {
         if (this.members[i2].role === role) {
         }
       }
     },
+    // Same as memberByRole, but returns all members with the given role
     membersByRole: function(role) {
       var result = [];
       for (var i2 = 0; i2 < this.members.length; i2++) {
       }
       return result;
     },
+    // Return the first member with the given id. A copy of the member object
+    // is returned, extended with an 'index' property whose value is the member index.
     memberById: function(id2) {
       for (var i2 = 0; i2 < this.members.length; i2++) {
         if (this.members[i2].id === id2) {
         }
       }
     },
+    // Return the first member with the given id and role. A copy of the member object
+    // is returned, extended with an 'index' property whose value is the member index.
     memberByIdAndRole: function(id2, role) {
       for (var i2 = 0; i2 < this.members.length; i2++) {
         if (this.members[i2].id === id2 && this.members[i2].role === role) {
       members.splice(toIndex, 0, members.splice(fromIndex, 1)[0]);
       return this.update({ members });
     },
+    // Wherever a member appears with id `needle.id`, replace it with a member
+    // with id `replacement.id`, type `replacement.type`, and the original role,
+    // By default, adding a duplicate member (by id and role) is prevented.
+    // Return an updated relation.
     replaceMember: function(needle, replacement, keepDuplicates) {
       if (!this.memberById(needle.id))
         return this;
     isConnectivity: function() {
       return !!(this.tags.type && this.tags.type.match(/^connectivity:?/));
     },
+    // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
+    // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
+    //
+    // This corresponds to the structure needed for rendering a multipolygon path using a
+    // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
+    //
+    // In the case of invalid geometries, this function will still return a result which
+    // includes the nodes of all way members, but some Nds may be unclosed and some inner
+    // rings not matched with the intended outer ring.
+    //
     multipolygon: function(resolver) {
       var outers = this.members.filter(function(m) {
-        return (m.role || "outer") === "outer";
+        return "outer" === (m.role || "outer");
       });
       var inners = this.members.filter(function(m) {
-        return m.role === "inner";
+        return "inner" === m.role;
       });
       outers = osmJoinWays(outers, resolver);
       inners = osmJoinWays(inners, resolver);
       this.id = id2;
       return this;
     }
+    // Generic handling for newly created QAItems
     static id() {
       return this.nextId--;
     }
         wayB = wayB.update({ nodes: nodesB });
       }
       if (wayA.tags.step_count) {
-        var stepCount = parseFloat(wayA.tags.step_count);
-        if (stepCount && isFinite(stepCount) && stepCount > 0 && Math.round(stepCount) === stepCount) {
+        var stepCount = Number(wayA.tags.step_count);
+        if (stepCount && // ensure a number
+        isFinite(stepCount) && // ensure positive
+        stepCount > 0 && // ensure integer
+        Math.round(stepCount) === stepCount) {
           var tagsA = Object.assign({}, wayA.tags);
           var tagsB = Object.assign({}, wayB.tags);
           var ratioA = lengthA / (lengthA + lengthB);
         "parentRels": Object.getPrototypeOf(this._parentRels)
       };
     },
+    // Unlike other graph methods, rebase mutates in place. This is because it
+    // is used only during the history operation that merges newly downloaded
+    // data into each state. To external consumers, it should appear as if the
+    // graph always contained the newly downloaded data.
     rebase: function(entities, stack, force) {
       var base = this.base();
       var i2, j2, k, id2;
       }, this);
       this.transients = {};
     },
+    // Updates calculated properties (parentWays, parentRels) for the specified change
     _updateCalculated: function(oldentity, entity, parentWays, parentRels) {
       parentWays = parentWays || this._parentWays;
       parentRels = parentRels || this._parentRels;
-      var type3 = entity && entity.type || oldentity && oldentity.type;
+      var type2 = entity && entity.type || oldentity && oldentity.type;
       var removed, added, i2;
-      if (type3 === "way") {
+      if (type2 === "way") {
         if (oldentity && entity) {
           removed = utilArrayDifference(oldentity.nodes, entity.nodes);
           added = utilArrayDifference(entity.nodes, oldentity.nodes);
           parentWays[added[i2]] = new Set(parentWays[added[i2]]);
           parentWays[added[i2]].add(entity.id);
         }
-      } else if (type3 === "relation") {
+      } else if (type2 === "relation") {
         var oldentityMemberIDs = oldentity ? oldentity.members.map(function(m) {
           return m.id;
         }) : [];
         graph.frozen = true;
       return graph;
     },
+    // Obliterates any existing entities
     load: function(entities) {
       var base = this.base();
       this.entities = Object.create(base.entities);
           return;
         currPath.push(entity.id);
         currRestrictions = (currRestrictions || []).slice();
-        var i3, j3;
         if (entity.type === "node") {
-          var parents2 = vgraph2.parentWays(entity);
-          var nextWays = [];
-          for (i3 = 0; i3 < parents2.length; i3++) {
-            var way2 = parents2[i3];
-            if (way2.__oneWay && way2.nodes[0] !== entity.id)
-              continue;
-            if (currPath.indexOf(way2.id) !== -1 && currPath.length >= 3)
+          stepNode(entity, currPath, currRestrictions);
+        } else {
+          stepWay(entity, currPath, currRestrictions, matchedRestriction);
+        }
+      }
+      function stepNode(entity, currPath, currRestrictions) {
+        var i3, j3;
+        var parents2 = vgraph2.parentWays(entity);
+        var nextWays = [];
+        for (i3 = 0; i3 < parents2.length; i3++) {
+          var way2 = parents2[i3];
+          if (way2.__oneWay && way2.nodes[0] !== entity.id)
+            continue;
+          if (currPath.indexOf(way2.id) !== -1 && currPath.length >= 3)
+            continue;
+          var restrict = null;
+          for (j3 = 0; j3 < currRestrictions.length; j3++) {
+            var restriction = currRestrictions[j3];
+            var f2 = restriction.memberByRole("from");
+            var v = restriction.membersByRole("via");
+            var t = restriction.memberByRole("to");
+            var isNo = /^no_/.test(restriction.tags.restriction);
+            var isOnly = /^only_/.test(restriction.tags.restriction);
+            if (!(isNo || isOnly)) {
               continue;
-            var restrict = null;
-            for (j3 = 0; j3 < currRestrictions.length; j3++) {
-              var restriction = currRestrictions[j3];
-              var f2 = restriction.memberByRole("from");
-              var v = restriction.membersByRole("via");
-              var t = restriction.memberByRole("to");
-              var isOnly = /^only_/.test(restriction.tags.restriction);
-              var matchesFrom = f2.id === fromWayId;
-              var matchesViaTo = false;
-              var isAlongOnlyPath = false;
-              if (t.id === way2.id) {
-                if (v.length === 1 && v[0].type === "node") {
-                  matchesViaTo = v[0].id === entity.id && (matchesFrom && currPath.length === 2 || !matchesFrom && currPath.length > 2);
-                } else {
-                  var pathVias = [];
-                  for (k = 2; k < currPath.length; k += 2) {
-                    pathVias.push(currPath[k]);
-                  }
-                  var restrictionVias = [];
-                  for (k = 0; k < v.length; k++) {
-                    if (v[k].type === "way") {
-                      restrictionVias.push(v[k].id);
-                    }
-                  }
-                  var diff = utilArrayDifference(pathVias, restrictionVias);
-                  matchesViaTo = !diff.length;
+            }
+            var matchesFrom = f2.id === fromWayId;
+            var matchesViaTo = false;
+            var isAlongOnlyPath = false;
+            if (t.id === way2.id) {
+              if (v.length === 1 && v[0].type === "node") {
+                matchesViaTo = v[0].id === entity.id && (matchesFrom && currPath.length === 2 || !matchesFrom && currPath.length > 2);
+              } else {
+                var pathVias = [];
+                for (k = 2; k < currPath.length; k += 2) {
+                  pathVias.push(currPath[k]);
                 }
-              } else if (isOnly) {
+                var restrictionVias = [];
                 for (k = 0; k < v.length; k++) {
-                  if (v[k].type === "way" && v[k].id === way2.id) {
-                    isAlongOnlyPath = true;
-                    break;
+                  if (v[k].type === "way") {
+                    restrictionVias.push(v[k].id);
                   }
                 }
+                var diff = utilArrayDifference(pathVias, restrictionVias);
+                matchesViaTo = !diff.length;
               }
-              if (matchesViaTo) {
-                if (isOnly) {
-                  restrict = { id: restriction.id, direct: matchesFrom, from: f2.id, only: true, end: true };
-                } else {
-                  restrict = { id: restriction.id, direct: matchesFrom, from: f2.id, no: true, end: true };
+            } else if (isOnly) {
+              for (k = 0; k < v.length; k++) {
+                if (v[k].type === "way" && v[k].id === way2.id) {
+                  isAlongOnlyPath = true;
+                  break;
                 }
+              }
+            }
+            if (matchesViaTo) {
+              if (isOnly) {
+                restrict = { id: restriction.id, direct: matchesFrom, from: f2.id, only: true, end: true };
               } else {
-                if (isAlongOnlyPath) {
-                  restrict = { id: restriction.id, direct: false, from: f2.id, only: true, end: false };
-                } else if (isOnly) {
-                  restrict = { id: restriction.id, direct: false, from: f2.id, no: true, end: true };
-                }
+                restrict = { id: restriction.id, direct: matchesFrom, from: f2.id, no: true, end: true };
+              }
+            } else {
+              if (isAlongOnlyPath) {
+                restrict = { id: restriction.id, direct: false, from: f2.id, only: true, end: false };
+              } else if (isOnly) {
+                restrict = { id: restriction.id, direct: false, from: f2.id, no: true, end: true };
               }
-              if (restrict && restrict.direct)
-                break;
             }
-            nextWays.push({ way: way2, restrict });
+            if (restrict && restrict.direct)
+              break;
           }
-          nextWays.forEach(function(nextWay) {
-            step(nextWay.way, currPath, currRestrictions, nextWay.restrict);
-          });
-        } else {
-          if (currPath.length >= 3) {
-            var turnPath = currPath.slice();
-            if (matchedRestriction && matchedRestriction.direct === false) {
-              for (i3 = 0; i3 < turnPath.length; i3++) {
-                if (turnPath[i3] === matchedRestriction.from) {
-                  turnPath = turnPath.slice(i3);
-                  break;
-                }
+          nextWays.push({ way: way2, restrict });
+        }
+        nextWays.forEach(function(nextWay) {
+          step(nextWay.way, currPath, currRestrictions, nextWay.restrict);
+        });
+      }
+      function stepWay(entity, currPath, currRestrictions, matchedRestriction) {
+        var i3;
+        if (currPath.length >= 3) {
+          var turnPath = currPath.slice();
+          if (matchedRestriction && matchedRestriction.direct === false) {
+            for (i3 = 0; i3 < turnPath.length; i3++) {
+              if (turnPath[i3] === matchedRestriction.from) {
+                turnPath = turnPath.slice(i3);
+                break;
               }
             }
-            var turn = pathToTurn(turnPath);
-            if (turn) {
-              if (matchedRestriction) {
-                turn.restrictionID = matchedRestriction.id;
-                turn.no = matchedRestriction.no;
-                turn.only = matchedRestriction.only;
-                turn.direct = matchedRestriction.direct;
-              }
-              turns.push(osmTurn(turn));
+          }
+          var turn = pathToTurn(turnPath);
+          if (turn) {
+            if (matchedRestriction) {
+              turn.restrictionID = matchedRestriction.id;
+              turn.no = matchedRestriction.no;
+              turn.only = matchedRestriction.only;
+              turn.direct = matchedRestriction.direct;
             }
-            if (currPath[0] === currPath[2])
-              return;
+            turns.push(osmTurn(turn));
           }
-          if (matchedRestriction && matchedRestriction.end)
+          if (currPath[0] === currPath[2])
             return;
-          var n1 = vgraph2.entity(entity.first());
-          var n2 = vgraph2.entity(entity.last());
-          var dist = geoSphericalDistance(n1.loc, n2.loc);
-          var nextNodes = [];
-          if (currPath.length > 1) {
-            if (dist > maxDistance)
-              return;
-            if (!entity.__via)
-              return;
-          }
-          if (!entity.__oneWay && keyVertexIds.indexOf(n1.id) !== -1 && currPath.indexOf(n1.id) === -1) {
-            nextNodes.push(n1);
-          }
-          if (keyVertexIds.indexOf(n2.id) !== -1 && currPath.indexOf(n2.id) === -1) {
-            nextNodes.push(n2);
-          }
-          nextNodes.forEach(function(nextNode) {
-            var fromRestrictions = vgraph2.parentRelations(entity).filter(function(r) {
-              if (!r.isRestriction())
-                return false;
-              var f3 = r.memberByRole("from");
-              if (!f3 || f3.id !== entity.id)
-                return false;
-              var isOnly2 = /^only_/.test(r.tags.restriction);
-              if (!isOnly2)
-                return true;
-              var isOnlyVia = false;
-              var v2 = r.membersByRole("via");
-              if (v2.length === 1 && v2[0].type === "node") {
-                isOnlyVia = v2[0].id === nextNode.id;
-              } else {
-                for (var i4 = 0; i4 < v2.length; i4++) {
-                  if (v2[i4].type !== "way")
-                    continue;
-                  var viaWay = vgraph2.entity(v2[i4].id);
-                  if (viaWay.first() === nextNode.id || viaWay.last() === nextNode.id) {
-                    isOnlyVia = true;
-                    break;
-                  }
+        }
+        if (matchedRestriction && matchedRestriction.end)
+          return;
+        var n1 = vgraph2.entity(entity.first());
+        var n2 = vgraph2.entity(entity.last());
+        var dist = geoSphericalDistance(n1.loc, n2.loc);
+        var nextNodes = [];
+        if (currPath.length > 1) {
+          if (dist > maxDistance)
+            return;
+          if (!entity.__via)
+            return;
+        }
+        if (!entity.__oneWay && // bidirectional..
+        keyVertexIds.indexOf(n1.id) !== -1 && // key vertex..
+        currPath.indexOf(n1.id) === -1) {
+          nextNodes.push(n1);
+        }
+        if (keyVertexIds.indexOf(n2.id) !== -1 && // key vertex..
+        currPath.indexOf(n2.id) === -1) {
+          nextNodes.push(n2);
+        }
+        nextNodes.forEach(function(nextNode) {
+          var fromRestrictions = vgraph2.parentRelations(entity).filter(function(r) {
+            if (!r.isRestriction())
+              return false;
+            var f2 = r.memberByRole("from");
+            if (!f2 || f2.id !== entity.id)
+              return false;
+            var isOnly = /^only_/.test(r.tags.restriction);
+            if (!isOnly)
+              return true;
+            var isOnlyVia = false;
+            var v = r.membersByRole("via");
+            if (v.length === 1 && v[0].type === "node") {
+              isOnlyVia = v[0].id === nextNode.id;
+            } else {
+              for (var i4 = 0; i4 < v.length; i4++) {
+                if (v[i4].type !== "way")
+                  continue;
+                var viaWay = vgraph2.entity(v[i4].id);
+                if (viaWay.first() === nextNode.id || viaWay.last() === nextNode.id) {
+                  isOnlyVia = true;
+                  break;
                 }
               }
-              return isOnlyVia;
-            });
-            step(nextNode, currPath, currRestrictions.concat(fromRestrictions), false);
+            }
+            return isOnlyVia;
           });
-        }
+          step(nextNode, currPath, currRestrictions.concat(fromRestrictions), false);
+        });
       }
       function pathToTurn(path) {
         if (path.length < 3)
           return "other";
         }
       });
-      return Object.assign({ closedWay: [], multipolygon: [], other: [] }, geometryGroups);
+      return Object.assign(
+        { closedWay: [], multipolygon: [], other: [] },
+        geometryGroups
+      );
     }
     var action = function(graph) {
       var entities = groupEntities(graph);
         return polygons.map(function(d, n2) {
           if (i2 === n2)
             return null;
-          return geoPolygonContainsPolygon(d.nodes.map(function(n3) {
-            return n3.loc;
-          }), w.nodes.map(function(n3) {
-            return n3.loc;
-          }));
+          return geoPolygonContainsPolygon(
+            d.nodes.map(function(n3) {
+              return n3.loc;
+            }),
+            w.nodes.map(function(n3) {
+              return n3.loc;
+            })
+          );
         });
       });
       var members = [];
         ab,
         oStart: h.buffer1[0],
         oLength: h.buffer1[1],
+        // length of o to remove
         abStart: h.buffer2[0],
         abLength: h.buffer2[1]
+        // length of a/b to insert
+        // abContent: (ab === 'a' ? a : b).slice(h.buffer2[0], h.buffer2[0] + h.buffer2[1])
       });
     }
     diffIndices(o, a).forEach((item) => addHunk(item, "a"));
         var k = keys[i2];
         if (o[k] !== b[k] && a[k] !== b[k]) {
           if (o[k] !== a[k]) {
-            _conflicts.push(_t.html("merge_remote_changes.conflict.tags", { tag: k, local: a[k], remote: b[k], user: { html: user(remote.user) } }));
+            _conflicts.push(_t.html(
+              "merge_remote_changes.conflict.tags",
+              { tag: k, local: a[k], remote: b[k], user: { html: user(remote.user) } }
+            ));
           } else {
             if (b.hasOwnProperty(k)) {
               tags[k] = b[k];
         t = 1;
       t = Math.min(Math.max(+t, 0), 1);
       var node = graph.entity(nodeID);
-      return graph.replace(node.move(geoVecInterp(node.loc, toLoc, t)));
+      return graph.replace(
+        node.move(geoVecInterp(node.loc, toLoc, t))
+      );
     };
     action.transitionable = true;
     return action;
       var nodeCount = {};
       var points = [];
       var corner = { i: 0, dotp: 1 };
-      var node, point, loc, score, motions, i2, j2;
+      var node, point2, loc, score, motions, i2, j2;
       for (i2 = 0; i2 < nodes.length; i2++) {
         node = nodes[i2];
         nodeCount[node.id] = (nodeCount[node.id] || 0) + 1;
         var straights = [];
         var simplified = [];
         for (i2 = 0; i2 < points.length; i2++) {
-          point = points[i2];
+          point2 = points[i2];
           var dotp = 0;
           if (isClosed || i2 > 0 && i2 < points.length - 1) {
             var a = points[(i2 - 1 + points.length) % points.length];
             var b = points[(i2 + 1) % points.length];
-            dotp = Math.abs(geoOrthoNormalizedDotProduct(a.coord, b.coord, point.coord));
+            dotp = Math.abs(geoOrthoNormalizedDotProduct(a.coord, b.coord, point2.coord));
           }
           if (dotp > upperThreshold) {
-            straights.push(point);
+            straights.push(point2);
           } else {
-            simplified.push(point);
+            simplified.push(point2);
           }
         }
         var bestPoints = clonePoints(simplified);
         if (isClosed)
           bestCoords.push(bestCoords[0]);
         for (i2 = 0; i2 < bestPoints.length; i2++) {
-          point = bestPoints[i2];
-          if (!geoVecEqual(originalPoints[i2].coord, point.coord)) {
-            node = graph.entity(point.id);
-            loc = projection2.invert(point.coord);
+          point2 = bestPoints[i2];
+          if (!geoVecEqual(originalPoints[i2].coord, point2.coord)) {
+            node = graph.entity(point2.id);
+            loc = projection2.invert(point2.coord);
             graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
           }
         }
         for (i2 = 0; i2 < straights.length; i2++) {
-          point = straights[i2];
-          if (nodeCount[point.id] > 1)
+          point2 = straights[i2];
+          if (nodeCount[point2.id] > 1)
             continue;
-          node = graph.entity(point.id);
+          node = graph.entity(point2.id);
           if (t === 1 && graph.parentWays(node).length === 1 && graph.parentRelations(node).length === 0 && !node.hasInterestingTags()) {
             graph = actionDeleteNode(node.id)(graph);
           } else {
-            var choice = geoVecProject(point.coord, bestCoords);
+            var choice = geoVecProject(point2.coord, bestCoords);
             if (choice) {
               loc = projection2.invert(choice.target);
               graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
           return { id: p.id, coord: [p.coord[0], p.coord[1]] };
         });
       }
-      function calcMotion(point2, i3, array2) {
+      function calcMotion(point3, i3, array2) {
         if (!isClosed && (i3 === 0 || i3 === array2.length - 1))
           return [0, 0];
         if (nodeCount[array2[i3].id] > 1)
           return [0, 0];
         var a2 = array2[(i3 - 1 + array2.length) % array2.length].coord;
-        var origin = point2.coord;
+        var origin = point3.coord;
         var b2 = array2[(i3 + 1) % array2.length].coord;
         var p = geoVecSubtract(a2, origin);
         var q = geoVecSubtract(b2, origin);
     var action = function(graph) {
       return graph.update(function(graph2) {
         utilGetAllNodes(rotateIds, graph2).forEach(function(node) {
-          var point = geoRotate([projection2(node.loc)], angle2, pivot)[0];
-          graph2 = graph2.replace(node.move(projection2.invert(point)));
+          var point2 = geoRotate([projection2(node.loc)], angle2, pivot)[0];
+          graph2 = graph2.replace(node.move(projection2.invert(point2)));
         });
       });
     };
   function actionScale(ids, pivotLoc, scaleFactor, projection2) {
     return function(graph) {
       return graph.update(function(graph2) {
-        let point, radial;
+        let point2, radial;
         utilGetAllNodes(ids, graph2).forEach(function(node) {
-          point = projection2(node.loc);
+          point2 = projection2(node.loc);
           radial = [
-            point[0] - pivotLoc[0],
-            point[1] - pivotLoc[1]
+            point2[0] - pivotLoc[0],
+            point2[1] - pivotLoc[1]
           ];
-          point = [
+          point2 = [
             pivotLoc[0] + scaleFactor * radial[0],
             pivotLoc[1] + scaleFactor * radial[1]
           ];
-          graph2 = graph2.replace(node.move(projection2.invert(point)));
+          graph2 = graph2.replace(node.move(projection2.invert(point2)));
         });
       });
     };
       var endPoint = endpoints[1];
       for (var i2 = 0; i2 < points.length; i2++) {
         var node = nodes[i2];
-        var point = points[i2];
-        var u = positionAlongWay(point, startPoint, endPoint);
-        var point2 = geoVecInterp(startPoint, endPoint, u);
-        var loc2 = projection2.invert(point2);
+        var point2 = points[i2];
+        var u = positionAlongWay(point2, startPoint, endPoint);
+        var point22 = geoVecInterp(startPoint, endPoint, u);
+        var loc2 = projection2.invert(point22);
         graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
       }
       return graph;
       var endPoint = endpoints[1];
       var maxDistance = 0;
       for (var i2 = 0; i2 < points.length; i2++) {
-        var point = points[i2];
-        var u = positionAlongWay(point, startPoint, endPoint);
+        var point2 = points[i2];
+        var u = positionAlongWay(point2, startPoint, endPoint);
         var p = geoVecInterp(startPoint, endPoint, u);
-        var dist = geoVecLength(p, point);
+        var dist = geoVecLength(p, point2);
         if (!isNaN(dist) && dist > maxDistance) {
           maxDistance = dist;
         }
       var i2;
       for (i2 = 1; i2 < points.length - 1; i2++) {
         var node = nodes[i2];
-        var point = points[i2];
+        var point2 = points[i2];
         if (t < 1 || shouldKeepNode(node, graph)) {
-          var u = positionAlongWay(point, startPoint, endPoint);
+          var u = positionAlongWay(point2, startPoint, endPoint);
           var p = geoVecInterp(startPoint, endPoint, u);
           var loc2 = projection2.invert(p);
           graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
       }
       var maxDistance = 0;
       for (i2 = 1; i2 < points.length - 1; i2++) {
-        var point = points[i2];
-        var u = positionAlongWay(point, startPoint, endPoint);
+        var point2 = points[i2];
+        var u = positionAlongWay(point2, startPoint, endPoint);
         var p = geoVecInterp(startPoint, endPoint, u);
-        var dist = geoVecLength(p, point);
+        var dist = geoVecLength(p, point2);
         if (isNaN(dist) || dist > threshold) {
           return "too_bendy";
         } else if (dist > maxDistance) {
       var keepingAllNodes = nodes.every(function(node, i3) {
         return i3 === 0 || i3 === nodes.length - 1 || shouldKeepNode(node, graph);
       });
-      if (maxDistance < 1e-4 && keepingAllNodes) {
+      if (maxDistance < 1e-4 && // Allow straightening even if already straight in order to remove extraneous nodes
+      keepingAllNodes) {
         return "straight_enough";
       }
     };
   var _disableSpace = false;
   var _lastSpace = null;
   function behaviorDraw(context) {
-    var dispatch10 = dispatch_default("move", "down", "downcancel", "click", "clickWay", "clickNode", "undo", "cancel", "finish");
+    var dispatch10 = dispatch_default(
+      "move",
+      "down",
+      "downcancel",
+      "click",
+      "clickWay",
+      "clickNode",
+      "undo",
+      "cancel",
+      "finish"
+    );
     var keybinding = utilKeybinding("draw");
     var _hover = behaviorHover(context).altDisables(true).ignoreVertex(true).on("hover", context.ui().sidebar.hover);
     var _edit = behaviorEdit(context);
         dispatch10.call("clickNode", this, target, d);
         return;
       } else if (target && target.type === "way" && (mode.id !== "add-point" || mode.preset.matchGeometry("vertex"))) {
-        var choice = geoChooseEdge(context.graph().childNodes(target), loc, context.projection, context.activeID());
+        var choice = geoChooseEdge(
+          context.graph().childNodes(target),
+          loc,
+          context.projection,
+          context.activeID()
+        );
         if (choice) {
           var edge = [target.nodes[choice.index - 1], target.nodes[choice.index]];
           dispatch10.call("clickWay", this, choice.loc, edge, d);
         _disableSpace = false;
         select_default2(window).on("keyup.space-block", null);
       });
-      var loc = context.map().mouse() || context.projection(context.map().center());
+      var loc = context.map().mouse() || // or the map center if the mouse has never entered the map
+      context.projection(context.map().center());
       click(d3_event, loc);
     }
     function backspace(d3_event) {
     var group = locale2.grouping === void 0 || locale2.thousands === void 0 ? identity_default3 : formatGroup_default(map.call(locale2.grouping, Number), locale2.thousands + ""), currencyPrefix = locale2.currency === void 0 ? "" : locale2.currency[0] + "", currencySuffix = locale2.currency === void 0 ? "" : locale2.currency[1] + "", decimal = locale2.decimal === void 0 ? "." : locale2.decimal + "", numerals = locale2.numerals === void 0 ? identity_default3 : formatNumerals_default(map.call(locale2.numerals, String)), percent = locale2.percent === void 0 ? "%" : locale2.percent + "", minus = locale2.minus === void 0 ? "\u2212" : locale2.minus + "", nan = locale2.nan === void 0 ? "NaN" : locale2.nan + "";
     function newFormat(specifier) {
       specifier = formatSpecifier(specifier);
-      var fill = specifier.fill, align = specifier.align, sign2 = specifier.sign, symbol = specifier.symbol, zero3 = specifier.zero, width = specifier.width, comma = specifier.comma, precision2 = specifier.precision, trim = specifier.trim, type3 = specifier.type;
-      if (type3 === "n")
-        comma = true, type3 = "g";
-      else if (!formatTypes_default[type3])
-        precision2 === void 0 && (precision2 = 12), trim = true, type3 = "g";
+      var fill = specifier.fill, align = specifier.align, sign2 = specifier.sign, symbol = specifier.symbol, zero3 = specifier.zero, width = specifier.width, comma = specifier.comma, precision2 = specifier.precision, trim = specifier.trim, type2 = specifier.type;
+      if (type2 === "n")
+        comma = true, type2 = "g";
+      else if (!formatTypes_default[type2])
+        precision2 === void 0 && (precision2 = 12), trim = true, type2 = "g";
       if (zero3 || fill === "0" && align === "=")
         zero3 = true, fill = "0", align = "=";
-      var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type3) ? "0" + type3.toLowerCase() : "", suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type3) ? percent : "";
-      var formatType = formatTypes_default[type3], maybeSuffix = /[defgprs%]/.test(type3);
-      precision2 = precision2 === void 0 ? 6 : /[gprs]/.test(type3) ? Math.max(1, Math.min(21, precision2)) : Math.max(0, Math.min(20, precision2));
+      var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type2) ? "0" + type2.toLowerCase() : "", suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type2) ? percent : "";
+      var formatType = formatTypes_default[type2], maybeSuffix = /[defgprs%]/.test(type2);
+      precision2 = precision2 === void 0 ? 6 : /[gprs]/.test(type2) ? Math.max(1, Math.min(21, precision2)) : Math.max(0, Math.min(20, precision2));
       function format2(value) {
         var valuePrefix = prefix, valueSuffix = suffix, i2, n2, c;
-        if (type3 === "c") {
+        if (type2 === "c") {
           valueSuffix = formatType(value) + valueSuffix;
           value = "";
         } else {
           if (valueNegative && +value === 0 && sign2 !== "+")
             valueNegative = false;
           valuePrefix = (valueNegative ? sign2 === "(" ? sign2 : minus : sign2 === "-" || sign2 === "(" ? "" : sign2) + valuePrefix;
-          valueSuffix = (type3 === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign2 === "(" ? ")" : "");
+          valueSuffix = (type2 === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign2 === "(" ? ")" : "");
           if (maybeSuffix) {
             i2 = -1, n2 = value.length;
             while (++i2 < n2) {
     var _done = false;
     var _timer;
     function ratchetyInterpolator(a, b, steps2, units) {
-      a = parseFloat(a);
-      b = parseFloat(b);
+      a = Number(a);
+      b = Number(b);
       var sample = quantize().domain([0, 1]).range(quantize_default(number_default(a, b), steps2));
       return function(t) {
         return String(sample(t)) + (units || "");
     function setAnimationParams(transition2, fromTo) {
       var toFrom = fromTo === "from" ? "to" : "from";
       transition2.styleTween("stroke-opacity", function(d) {
-        return ratchetyInterpolator(_params[d.id][toFrom].opacity, _params[d.id][fromTo].opacity, steps);
+        return ratchetyInterpolator(
+          _params[d.id][toFrom].opacity,
+          _params[d.id][fromTo].opacity,
+          steps
+        );
       }).styleTween("stroke-width", function(d) {
-        return ratchetyInterpolator(_params[d.id][toFrom].width, _params[d.id][fromTo].width, steps, "px");
+        return ratchetyInterpolator(
+          _params[d.id][toFrom].width,
+          _params[d.id][fromTo].width,
+          steps,
+          "px"
+        );
       }).styleTween("fill-opacity", function(d) {
-        return ratchetyInterpolator(_params[d.id][toFrom].opacity, _params[d.id][fromTo].opacity, steps);
+        return ratchetyInterpolator(
+          _params[d.id][toFrom].opacity,
+          _params[d.id][fromTo].opacity,
+          steps
+        );
       }).styleTween("r", function(d) {
-        return ratchetyInterpolator(_params[d.id][toFrom].width, _params[d.id][fromTo].width, steps, "px");
+        return ratchetyInterpolator(
+          _params[d.id][toFrom].width,
+          _params[d.id][fromTo].width,
+          steps,
+          "px"
+        );
       });
     }
     function calcAnimationParams(selection2) {
         var opacity;
         var width;
         if (tag === "circle") {
-          opacity = parseFloat(s.style("fill-opacity") || 0.5);
-          width = parseFloat(s.style("r") || 15.5);
+          opacity = Number(s.style("fill-opacity") || 0.5);
+          width = Number(s.style("r") || 15.5);
         } else {
-          opacity = parseFloat(s.style("stroke-opacity") || 0.7);
-          width = parseFloat(s.style("stroke-width") || 10);
+          opacity = Number(s.style("stroke-opacity") || 0.7);
+          width = Number(s.style("stroke-width") || 10);
         }
         p.tag = tag;
         p.from.opacity = opacity * 0.6;
       d3_event.preventDefault();
       var disabled = _operation.disabled();
       if (disabled) {
-        context.ui().flash.duration(4e3).iconName("#iD-operation-" + _operation.id).iconClass("operation disabled").label(_operation.tooltip)();
+        context.ui().flash.duration(4e3).iconName("#iD-operation-" + _operation.id).iconClass("operation disabled").label(_operation.tooltip())();
       } else {
         context.ui().flash.duration(2e3).iconName("#iD-operation-" + _operation.id).iconClass("operation").label(_operation.annotation() || _operation.title)();
         if (_operation.point)
     };
     operation.tooltip = function() {
       var disable = operation.disabled();
-      return disable ? _t("operations.circularize." + disable + "." + _amount) : _t("operations.circularize.description." + _amount);
+      return disable ? _t.append("operations.circularize." + disable + "." + _amount) : _t.append("operations.circularize.description." + _amount);
     };
     operation.annotation = function() {
       return _t("operations.circularize.annotation.feature", { n: _actions.length });
     };
     operation.id = "circularize";
     operation.keys = [_t("operations.circularize.key")];
-    operation.title = _t("operations.circularize.title");
+    operation.title = _t.append("operations.circularize.title");
     operation.behavior = behaviorOperation(context).which(operation);
     return operation;
   }
         var parents = context.graph().parentRelations(entity);
         for (var i2 = 0; i2 < parents.length; i2++) {
           var parent = parents[i2];
-          var type3 = parent.tags.type;
+          var type2 = parent.tags.type;
           var role = parent.memberById(id2).role || "outer";
-          if (type3 === "route" || type3 === "boundary" || type3 === "multipolygon" && role === "outer") {
+          if (type2 === "route" || type2 === "boundary" || type2 === "multipolygon" && role === "outer") {
             return true;
           }
         }
     };
     operation.tooltip = function() {
       var disable = operation.disabled();
-      return disable ? _t("operations.delete." + disable + "." + multi) : _t("operations.delete.description." + multi);
+      return disable ? _t.append("operations.delete." + disable + "." + multi) : _t.append("operations.delete.description." + multi);
     };
     operation.annotation = function() {
       return selectedIDs.length === 1 ? _t("operations.delete.annotation." + context.graph().geometry(selectedIDs[0])) : _t("operations.delete.annotation.feature", { n: selectedIDs.length });
     };
     operation.id = "delete";
     operation.keys = [uiCmd("\u2318\u232B"), uiCmd("\u2318\u2326"), uiCmd("\u2326")];
-    operation.title = _t("operations.delete.title");
+    operation.title = _t.append("operations.delete.title");
     operation.behavior = behaviorOperation(context).which(operation);
     return operation;
   }
     };
     operation.tooltip = function() {
       var disable = operation.disabled();
-      return disable ? _t("operations.orthogonalize." + disable + "." + _amount) : _t("operations.orthogonalize.description." + _type + "." + _amount);
+      return disable ? _t.append("operations.orthogonalize." + disable + "." + _amount) : _t.append("operations.orthogonalize.description." + _type + "." + _amount);
     };
     operation.annotation = function() {
       return _t("operations.orthogonalize.annotation." + _type, { n: _actions.length });
     };
     operation.id = "orthogonalize";
     operation.keys = [_t("operations.orthogonalize.key")];
-    operation.title = _t("operations.orthogonalize.title");
+    operation.title = _t.append("operations.orthogonalize.title");
     operation.behavior = behaviorOperation(context).which(operation);
     return operation;
   }
     };
     operation.tooltip = function() {
       var disable = operation.disabled();
-      return disable ? _t("operations.reflect." + disable + "." + multi) : _t("operations.reflect.description." + axis + "." + multi);
+      return disable ? _t.append("operations.reflect." + disable + "." + multi) : _t.append("operations.reflect.description." + axis + "." + multi);
     };
     operation.annotation = function() {
       return _t("operations.reflect.annotation." + axis + ".feature", { n: selectedIDs.length });
     };
     operation.id = "reflect-" + axis;
     operation.keys = [_t("operations.reflect.key." + axis)];
-    operation.title = _t("operations.reflect.title." + axis);
+    operation.title = _t.append("operations.reflect.title." + axis);
     operation.behavior = behaviorOperation(context).which(operation);
     return operation;
   }
     };
     operation.tooltip = function() {
       var disable = operation.disabled();
-      return disable ? _t("operations.move." + disable + "." + multi) : _t("operations.move.description." + multi);
+      return disable ? _t.append("operations.move." + disable + "." + multi) : _t.append("operations.move.description." + multi);
     };
     operation.annotation = function() {
       return selectedIDs.length === 1 ? _t("operations.move.annotation." + context.graph().geometry(selectedIDs[0])) : _t("operations.move.annotation.feature", { n: selectedIDs.length });
     };
     operation.id = "move";
     operation.keys = [_t("operations.move.key")];
-    operation.title = _t("operations.move.title");
+    operation.title = _t.append("operations.move.title");
     operation.behavior = behaviorOperation(context).which(operation);
     operation.mouseOnly = true;
     return operation;
     };
     operation.tooltip = function() {
       var disable = operation.disabled();
-      return disable ? _t("operations.rotate." + disable + "." + multi) : _t("operations.rotate.description." + multi);
+      return disable ? _t.append("operations.rotate." + disable + "." + multi) : _t.append("operations.rotate.description." + multi);
     };
     operation.annotation = function() {
       return selectedIDs.length === 1 ? _t("operations.rotate.annotation." + context.graph().geometry(selectedIDs[0])) : _t("operations.rotate.annotation.feature", { n: selectedIDs.length });
     };
     operation.id = "rotate";
     operation.keys = [_t("operations.rotate.key")];
-    operation.title = _t("operations.rotate.title");
+    operation.title = _t.append("operations.rotate.title");
     operation.behavior = behaviorOperation(context).which(operation);
     operation.mouseOnly = true;
     return operation;
       _isCancelled = !context.editable() || d3_event.shiftKey || hasHidden;
       if (_isCancelled) {
         if (hasHidden) {
-          context.ui().flash.duration(4e3).iconName("#iD-icon-no").label(_t("modes.drag_node.connected_to_hidden"))();
+          context.ui().flash.duration(4e3).iconName("#iD-icon-no").label(_t.append("modes.drag_node.connected_to_hidden"))();
         }
         return drag.cancel();
       }
           }
         }
       }
-      context.replace(actionMoveNode(entity.id, loc));
+      context.replace(
+        actionMoveNode(entity.id, loc)
+      );
       var isInvalid = false;
       if (target) {
         isInvalid = hasRelationConflict(entity, target, edge, context.graph());
       var nope = context.surface().classed("nope");
       if (isInvalid === "relation" || isInvalid === "restriction") {
         if (!nope) {
-          context.ui().flash.duration(4e3).iconName("#iD-icon-no").label(_t.html("operations.connect." + isInvalid, { relation: _mainPresetIndex.item("type/restriction").name() }))();
+          context.ui().flash.duration(4e3).iconName("#iD-icon-no").label(_t.append(
+            "operations.connect." + isInvalid,
+            { relation: _mainPresetIndex.item("type/restriction").name() }
+          ))();
         }
       } else if (isInvalid) {
         var errorID = isInvalid === "line" ? "lines" : "areas";
-        context.ui().flash.duration(3e3).iconName("#iD-icon-no").label(_t.html("self_intersection.error." + errorID))();
+        context.ui().flash.duration(3e3).iconName("#iD-icon-no").label(_t.append("self_intersection.error." + errorID))();
       } else {
         if (nope) {
           context.ui().flash.duration(1).label("")();
       }
       return false;
     }
-    function move(d3_event, entity, point) {
+    function move(d3_event, entity, point2) {
       if (_isCancelled)
         return;
       d3_event.stopPropagation();
       context.surface().classed("nope-disabled", d3_event.altKey);
-      _lastLoc = context.projection.invert(point);
+      _lastLoc = context.projection.invert(point2);
       doMove(d3_event, entity);
-      var nudge = geoViewportEdge(point, context.map().dimensions());
+      var nudge = geoViewportEdge(point2, context.map().dimensions());
       if (nudge) {
         startNudge(d3_event, entity, nudge);
       } else {
       var nope = d && d.properties && d.properties.nope || context.surface().classed("nope");
       var target = d && d.properties && d.properties.entity;
       if (nope) {
-        context.perform(_actionBounceBack(entity.id, _startLoc));
+        context.perform(
+          _actionBounceBack(entity.id, _startLoc)
+        );
       } else if (target && target.type === "way") {
         var choice = geoChooseEdge(context.graph().childNodes(target), context.map().mouse(), context.projection, entity.id);
-        context.replace(actionAddMidpoint({
-          loc: choice.loc,
-          edge: [target.nodes[choice.index - 1], target.nodes[choice.index]]
-        }, entity), connectAnnotation(entity, target));
+        context.replace(
+          actionAddMidpoint({
+            loc: choice.loc,
+            edge: [target.nodes[choice.index - 1], target.nodes[choice.index]]
+          }, entity),
+          connectAnnotation(entity, target)
+        );
       } else if (target && target.type === "node" && shouldSnapToNode(target)) {
-        context.replace(actionConnect([target.id, entity.id]), connectAnnotation(entity, target));
+        context.replace(
+          actionConnect([target.id, entity.id]),
+          connectAnnotation(entity, target)
+        );
       } else if (_wasMidpoint) {
-        context.replace(actionNoop(), _t("operations.add.annotation.vertex"));
+        context.replace(
+          actionNoop(),
+          _t("operations.add.annotation.vertex")
+        );
       } else {
-        context.replace(actionNoop(), moveAnnotation(entity));
+        context.replace(
+          actionNoop(),
+          moveAnnotation(entity)
+        );
       }
       if (wasPoint) {
         context.enter(modeSelect(context, [entity.id]));
   }
 
   // node_modules/d3-fetch/src/xml.js
-  function parser(type3) {
-    return (input, init2) => text_default3(input, init2).then((text2) => new DOMParser().parseFromString(text2, type3));
+  function parser(type2) {
+    return (input, init2) => text_default3(input, init2).then((text2) => new DOMParser().parseFromString(text2, type2));
   }
   var xml_default = parser("application/xml");
   var html = parser("text/html");
   var _krData = { errorTypes: {}, localizeStrings: {} };
   var _cache;
   var _krRuleset = [
+    // no 20 - multiple node on same spot - these are mostly boundaries overlapping roads
     30,
     40,
     50,
       items.forEach((item) => {
         const match = item.match(/\#(\d+)\((.+)\)?/);
         if (match !== null && match.length > 2) {
-          newList.push(linkEntity2("w" + match[1]) + " " + _t("QA.keepRight.errorTypes.231.layer", { layer: match[2] }));
+          newList.push(
+            linkEntity2("w" + match[1]) + " " + _t("QA.keepRight.errorTypes.231.layer", { layer: match[2] })
+          );
         }
       });
       return newList.join(", ");
         rtree: new import_rbush.default()
       };
     },
+    // KeepRight API:  http://osm.mueschelsoft.de/keepright/interfacing.php
     loadIssues(projection2) {
       const options2 = {
         format: "geojson",
             } = feature3;
             let {
               geometry: { coordinates: loc },
-              properties: { description: description2 = "" }
+              properties: { description = "" }
             } = feature3;
             const issueTemplate = _krData.errorTypes[itemType];
             const parentIssueType = (Math.floor(itemType / 10) * 10).toString();
             const whichTemplate = _krData.errorTypes[whichType];
             switch (whichType) {
               case "170":
-                description2 = `This feature has a FIXME tag: ${description2}`;
+                description = `This feature has a FIXME tag: ${description}`;
                 break;
               case "292":
               case "293":
-                description2 = description2.replace("A turn-", "This turn-");
+                description = description.replace("A turn-", "This turn-");
                 break;
               case "294":
               case "295":
               case "296":
               case "297":
               case "298":
-                description2 = `This turn-restriction~${description2}`;
+                description = `This turn-restriction~${description}`;
                 break;
               case "300":
-                description2 = "This highway is missing a maxspeed tag";
+                description = "This highway is missing a maxspeed tag";
                 break;
               case "411":
               case "412":
               case "413":
-                description2 = `This feature~${description2}`;
+                description = `This feature~${description}`;
                 break;
             }
             let coincident = false;
             do {
               let delta = coincident ? [1e-5, 0] : [0, 1e-5];
               loc = geoVecAdd(loc, delta);
-              let bbox = geoExtent(loc).bbox();
-              coincident = _cache.rtree.search(bbox).length;
+              let bbox2 = geoExtent(loc).bbox();
+              coincident = _cache.rtree.search(bbox2).length;
             } while (coincident);
             let d = new QAItem(loc, this, itemType, id2, {
               comment,
-              description: description2,
+              description,
               whichType,
               parentIssueType,
               severity: whichTemplate.severity || "error",
           callback(null, d);
       });
     },
+    // Get all cached QAItems covering the viewport
     getItems(projection2) {
       const viewport = projection2.clipExtent();
       const min3 = [viewport[0][0], viewport[1][1]];
       const max3 = [viewport[1][0], viewport[0][1]];
-      const bbox = geoExtent(projection2.invert(min3), projection2.invert(max3)).bbox();
-      return _cache.rtree.search(bbox).map((d) => d.data);
+      const bbox2 = geoExtent(projection2.invert(min3), projection2.invert(max3)).bbox();
+      return _cache.rtree.search(bbox2).map((d) => d.data);
     },
+    // Get a QAItem from cache
+    // NOTE: Don't change method name until UI v3 is merged
     getError(id2) {
       return _cache.data[id2];
     },
+    // Replace a single QAItem in the cache
     replaceItem(item) {
       if (!(item instanceof QAItem) || !item.id)
         return;
       updateRtree(encodeIssueRtree(item), true);
       return item;
     },
+    // Remove a single QAItem from the cache
     removeItem(item) {
       if (!(item instanceof QAItem) || !item.id)
         return;
     issueURL(item) {
       return `${_krUrlRoot}/report_map.php?schema=${item.schema}&error=${item.id}`;
     },
+    // Get an array of issues closed during this session.
+    // Used to populate `closed:keepright` changeset tag
     getClosedIDs() {
       return Object.keys(_cache.closed).sort();
     }
   }
   function pointAverage(points) {
     if (points.length) {
-      const sum = points.reduce((acc, point) => geoVecAdd(acc, [point.lon, point.lat]), [0, 0]);
+      const sum = points.reduce(
+        (acc, point2) => geoVecAdd(acc, [point2.lon, point2.lat]),
+        [0, 0]
+      );
       return geoVecScale(sum, 1 / points.length);
     } else {
       return [0, 0];
     do {
       let delta = coincident ? [1e-5, 0] : bumpUp ? [0, 1e-5] : [0, 0];
       loc = geoVecAdd(loc, delta);
-      let bbox = geoExtent(loc).bbox();
-      coincident = _cache2.rtree.search(bbox).length;
+      let bbox2 = geoExtent(loc).bbox();
+      coincident = _cache2.rtree.search(bbox2).length;
     } while (coincident);
     return loc;
   }
         client: "iD",
         status: "OPEN",
         zoom: "19"
+        // Use a high zoom so that clusters aren't returned
       };
       const tiles = tiler2.zoomExtent([_tileZoom2, _tileZoom2]).getTiles(projection2);
       abortUnwantedRequests2(_cache2, tiles);
         const params = Object.assign({}, options2, { east, south, west, north });
         const requests = {};
         Object.keys(_impOsmUrls).forEach((k) => {
-          const kParams = Object.assign({}, params, k === "mr" ? { type: "PARKING,ROAD,BOTH,PATH" } : { confidenceLevel: "C1" });
+          const kParams = Object.assign(
+            {},
+            params,
+            k === "mr" ? { type: "PARKING,ROAD,BOTH,PATH" } : { confidenceLevel: "C1" }
+          );
           const url = `${_impOsmUrls[k]}/search?` + utilQsString(kParams);
           const controller = new AbortController();
           requests[k] = controller;
                 loc = preventCoincident(loc, false);
                 let d = new QAItem(loc, this, k, itemId, {
                   issueKey: k,
+                  // used as a category
                   identifier: {
+                    // used to post changes
                     wayId,
                     fromNodeId,
                     toNodeId
             }
             if (data.tiles) {
               data.tiles.forEach((feature3) => {
-                const { type: type3, x, y, numberOfTrips } = feature3;
-                const geoType = type3.toLowerCase();
+                const { type: type2, x, y, numberOfTrips } = feature3;
+                const geoType = type2.toLowerCase();
                 const itemId = `${geoType}${x}${y}${numberOfTrips}`;
                 let loc = pointAverage(feature3.points);
                 loc = preventCoincident(loc, false);
             }
             if (data.entities) {
               data.entities.forEach((feature3) => {
-                const { point, id: id2, segments, numberOfPasses, turnType } = feature3;
+                const { point: point2, id: id2, segments, numberOfPasses, turnType } = feature3;
                 const itemId = `${id2.replace(/[,:+#]/g, "_")}`;
-                const loc = preventCoincident([point.lon, point.lat], true);
+                const loc = preventCoincident([point2.lon, point2.lat], true);
                 const ids = id2.split(",");
                 const from_way = ids[0];
                 const via_node = ids[3];
         });
       }
     },
+    // Get all cached QAItems covering the viewport
     getItems(projection2) {
       const viewport = projection2.clipExtent();
       const min3 = [viewport[0][0], viewport[1][1]];
       const max3 = [viewport[1][0], viewport[0][1]];
-      const bbox = geoExtent(projection2.invert(min3), projection2.invert(max3)).bbox();
-      return _cache2.rtree.search(bbox).map((d) => d.data);
+      const bbox2 = geoExtent(projection2.invert(min3), projection2.invert(max3)).bbox();
+      return _cache2.rtree.search(bbox2).map((d) => d.data);
     },
+    // Get a QAItem from cache
+    // NOTE: Don't change method name until UI v3 is merged
     getError(id2) {
       return _cache2.data[id2];
     },
+    // get the name of the icon to display for this item
     getIcon(itemType) {
       return _impOsmData.icons[itemType];
     },
+    // Replace a single QAItem in the cache
     replaceItem(issue) {
       if (!(issue instanceof QAItem) || !issue.id)
         return;
       updateRtree2(encodeIssueRtree2(issue), true);
       return issue;
     },
+    // Remove a single QAItem from the cache
     removeItem(issue) {
       if (!(issue instanceof QAItem) || !issue.id)
         return;
       delete _cache2.data[issue.id];
       updateRtree2(encodeIssueRtree2(issue), false);
     },
+    // Used to populate `closed:improveosm:*` changeset tags
     getClosedCounts() {
       return _cache2.closed;
     }
   // node_modules/marked/lib/marked.esm.js
   function getDefaults() {
     return {
+      async: false,
       baseUrl: null,
       breaks: false,
       extensions: null,
       sanitize: false,
       sanitizer: null,
       silent: false,
-      smartLists: false,
       smartypants: false,
       tokenizer: null,
       walkTokens: null,
     regex = typeof regex === "string" ? regex : regex.source;
     opt = opt || "";
     const obj = {
-      replace: (name2, val) => {
+      replace: (name, val) => {
         val = val.source || val;
         val = val.replace(caret, "$1");
-        regex = regex.replace(name2, val);
+        regex = regex.replace(name, val);
         return obj;
       },
       getRegex: () => {
         href,
         title,
         text: text2,
-        tokens: lexer2.inlineTokens(text2, [])
+        tokens: lexer2.inlineTokens(text2)
       };
       lexer2.state.inLink = false;
       return token;
         return {
           type: "code",
           raw,
-          lang: cap[2] ? cap[2].trim() : cap[2],
+          lang: cap[2] ? cap[2].trim().replace(this.rules.inline._escapes, "$1") : cap[2],
           text: text2
         };
       }
             text2 = trimmed.trim();
           }
         }
-        const token = {
+        return {
           type: "heading",
           raw: cap[0],
           depth: cap[1].length,
           text: text2,
-          tokens: []
+          tokens: this.lexer.inline(text2)
         };
-        this.lexer.inline(token.text, token.tokens);
-        return token;
       }
     }
     hr(src) {
           if (!endEarly) {
             const nextBulletRegex = new RegExp(`^ {0,${Math.min(3, indent2 - 1)}}(?:[*+-]|\\d{1,9}[.)])((?: [^\\n]*)?(?:\\n|$))`);
             const hrRegex = new RegExp(`^ {0,${Math.min(3, indent2 - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`);
-            const fencesBeginRegex = new RegExp(`^( {0,${Math.min(3, indent2 - 1)}})(\`\`\`|~~~)`);
+            const fencesBeginRegex = new RegExp(`^ {0,${Math.min(3, indent2 - 1)}}(?:\`\`\`|~~~)`);
+            const headingBeginRegex = new RegExp(`^ {0,${Math.min(3, indent2 - 1)}}#`);
             while (src) {
               rawLine = src.split("\n", 1)[0];
               line = rawLine;
               if (fencesBeginRegex.test(line)) {
                 break;
               }
-              if (this.rules.block.heading.test(line)) {
+              if (headingBeginRegex.test(line)) {
                 break;
               }
               if (nextBulletRegex.test(line)) {
           text: cap[0]
         };
         if (this.options.sanitize) {
+          const text2 = this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape4(cap[0]);
           token.type = "paragraph";
-          token.text = this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape4(cap[0]);
-          token.tokens = [];
-          this.lexer.inline(token.text, token.tokens);
+          token.text = text2;
+          token.tokens = this.lexer.inline(text2);
         }
         return token;
       }
           type: "def",
           tag,
           raw: cap[0],
-          href: cap[2],
-          title: cap[3]
+          href: cap[2] ? cap[2].replace(this.rules.inline._escapes, "$1") : cap[2],
+          title: cap[3] ? cap[3].replace(this.rules.inline._escapes, "$1") : cap[3]
         };
       }
     }
           }
           l = item.header.length;
           for (j2 = 0; j2 < l; j2++) {
-            item.header[j2].tokens = [];
-            this.lexer.inline(item.header[j2].text, item.header[j2].tokens);
+            item.header[j2].tokens = this.lexer.inline(item.header[j2].text);
           }
           l = item.rows.length;
           for (j2 = 0; j2 < l; j2++) {
             row = item.rows[j2];
             for (k = 0; k < row.length; k++) {
-              row[k].tokens = [];
-              this.lexer.inline(row[k].text, row[k].tokens);
+              row[k].tokens = this.lexer.inline(row[k].text);
             }
           }
           return item;
     lheading(src) {
       const cap = this.rules.block.lheading.exec(src);
       if (cap) {
-        const token = {
+        return {
           type: "heading",
           raw: cap[0],
           depth: cap[2].charAt(0) === "=" ? 1 : 2,
           text: cap[1],
-          tokens: []
+          tokens: this.lexer.inline(cap[1])
         };
-        this.lexer.inline(token.text, token.tokens);
-        return token;
       }
     }
     paragraph(src) {
       const cap = this.rules.block.paragraph.exec(src);
       if (cap) {
-        const token = {
+        const text2 = cap[1].charAt(cap[1].length - 1) === "\n" ? cap[1].slice(0, -1) : cap[1];
+        return {
           type: "paragraph",
           raw: cap[0],
-          text: cap[1].charAt(cap[1].length - 1) === "\n" ? cap[1].slice(0, -1) : cap[1],
-          tokens: []
+          text: text2,
+          tokens: this.lexer.inline(text2)
         };
-        this.lexer.inline(token.text, token.tokens);
-        return token;
       }
     }
     text(src) {
       const cap = this.rules.block.text.exec(src);
       if (cap) {
-        const token = {
+        return {
           type: "text",
           raw: cap[0],
           text: cap[0],
-          tokens: []
+          tokens: this.lexer.inline(cap[0])
         };
-        this.lexer.inline(token.text, token.tokens);
-        return token;
       }
     }
     escape(src) {
           if (delimTotal > 0)
             continue;
           rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal);
+          const raw = src.slice(0, lLength + match.index + (match[0].length - rDelim.length) + rLength);
           if (Math.min(lLength, rLength) % 2) {
-            const text3 = src.slice(1, lLength + match.index + rLength);
+            const text3 = raw.slice(1, -1);
             return {
               type: "em",
-              raw: src.slice(0, lLength + match.index + rLength + 1),
+              raw,
               text: text3,
-              tokens: this.lexer.inlineTokens(text3, [])
+              tokens: this.lexer.inlineTokens(text3)
             };
           }
-          const text2 = src.slice(2, lLength + match.index + rLength - 1);
+          const text2 = raw.slice(2, -2);
           return {
             type: "strong",
-            raw: src.slice(0, lLength + match.index + rLength + 1),
+            raw,
             text: text2,
-            tokens: this.lexer.inlineTokens(text2, [])
+            tokens: this.lexer.inlineTokens(text2)
           };
         }
       }
           type: "del",
           raw: cap[0],
           text: cap[2],
-          tokens: this.lexer.inlineTokens(cap[2], [])
+          tokens: this.lexer.inlineTokens(cap[2])
         };
       }
     }
     def: /^ {0,3}\[(label)\]: *(?:\n *)?<?([^\s>]+)>?(?:(?: +(?:\n *)?| *\n *)(title))? *(?:\n+|$)/,
     table: noopTest,
     lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,
+    // regex template, placeholders will be replaced according to different paragraph
+    // interruption rules of commonmark and the original markdown spec:
     _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,
     text: /^[^\n]+/
   };
   block.normal = merge2({}, block);
   block.gfm = merge2({}, block.normal, {
     table: "^ *([^\\n ].*\\|.*)\\n {0,3}(?:\\| *)?(:?-+:? *(?:\\| *:?-+:? *)*)(?:\\| *)?(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)"
+    // Cells
   });
   block.gfm.table = edit(block.gfm.table).replace("hr", block.hr).replace("heading", " {0,3}#{1,6} ").replace("blockquote", " {0,3}>").replace("code", " {4}[^\\n]").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list", " {0,3}(?:[*+-]|1[.)]) ").replace("html", "</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag", block._tag).getRegex();
   block.gfm.paragraph = edit(block._paragraph).replace("hr", block.hr).replace("heading", " {0,3}#{1,6} ").replace("|lheading", "").replace("table", block.gfm.table).replace("blockquote", " {0,3}>").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list", " {0,3}(?:[*+-]|1[.)]) ").replace("html", "</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag", block._tag).getRegex();
   block.pedantic = merge2({}, block.normal, {
-    html: edit(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)|<tag(?:"[^"]*"|'[^']*'|\\s[^'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment", block._comment).replace(/tag/g, "(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),
+    html: edit(
+      `^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)|<tag(?:"[^"]*"|'[^']*'|\\s[^'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`
+    ).replace("comment", block._comment).replace(/tag/g, "(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),
     def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
     heading: /^(#{1,6})(.*)(?:\n+|$)/,
     fences: noopTest,
+    // fences not supported
     paragraph: edit(block.normal._paragraph).replace("hr", block.hr).replace("heading", " *#{1,6} *[^\n]").replace("lheading", block.lheading).replace("blockquote", " {0,3}>").replace("|fences", "").replace("|list", "").replace("|html", "").getRegex()
   });
   var inline = {
     autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
     url: noopTest,
     tag: "^comment|^</[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^<![a-zA-Z]+\\s[\\s\\S]*?>|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>",
+    // CDATA section
     link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,
     reflink: /^!?\[(label)\]\[(ref)\]/,
     nolink: /^!?\[(ref)\](?:\[\])?/,
     reflinkSearch: "reflink|nolink(?!\\()",
     emStrong: {
       lDelim: /^(?:\*+(?:([punct_])|[^\s*]))|^_+(?:([punct*])|([^\s_]))/,
-      rDelimAst: /^[^_*]*?\_\_[^_*]*?\*[^_*]*?(?=\_\_)|[^*]+(?=[^*])|[punct_](\*+)(?=[\s]|$)|[^punct*_\s](\*+)(?=[punct_\s]|$)|[punct_\s](\*+)(?=[^punct*_\s])|[\s](\*+)(?=[punct_])|[punct_](\*+)(?=[punct_])|[^punct*_\s](\*+)(?=[^punct*_\s])/,
-      rDelimUnd: /^[^_*]*?\*\*[^_*]*?\_[^_*]*?(?=\*\*)|[^_]+(?=[^_])|[punct*](\_+)(?=[\s]|$)|[^punct*_\s](\_+)(?=[punct*\s]|$)|[punct*\s](\_+)(?=[^punct*_\s])|[\s](\_+)(?=[punct*])|[punct*](\_+)(?=[punct*])/
+      //        (1) and (2) can only be a Right Delimiter. (3) and (4) can only be Left.  (5) and (6) can be either Left or Right.
+      //          () Skip orphan inside strong                                      () Consume to delim     (1) #***                (2) a***#, a***                             (3) #***a, ***a                 (4) ***#              (5) #***#                 (6) a***a
+      rDelimAst: /^(?:[^_*\\]|\\.)*?\_\_(?:[^_*\\]|\\.)*?\*(?:[^_*\\]|\\.)*?(?=\_\_)|(?:[^*\\]|\\.)+(?=[^*])|[punct_](\*+)(?=[\s]|$)|(?:[^punct*_\s\\]|\\.)(\*+)(?=[punct_\s]|$)|[punct_\s](\*+)(?=[^punct*_\s])|[\s](\*+)(?=[punct_])|[punct_](\*+)(?=[punct_])|(?:[^punct*_\s\\]|\\.)(\*+)(?=[^punct*_\s])/,
+      rDelimUnd: /^(?:[^_*\\]|\\.)*?\*\*(?:[^_*\\]|\\.)*?\_(?:[^_*\\]|\\.)*?(?=\*\*)|(?:[^_\\]|\\.)+(?=[^_])|[punct*](\_+)(?=[\s]|$)|(?:[^punct*_\s\\]|\\.)(\_+)(?=[punct*\s]|$)|[punct*\s](\_+)(?=[^punct*_\s])|[\s](\_+)(?=[punct*])|[punct*](\_+)(?=[punct*])/
+      // ^- Not allowed for _
     },
     code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
     br: /^( {2,}|\\)\n(?!\s*$)/,
   inline._punctuation = "!\"#$%&'()+\\-.,/:;<=>?@\\[\\]`^{|}~";
   inline.punctuation = edit(inline.punctuation).replace(/punctuation/g, inline._punctuation).getRegex();
   inline.blockSkip = /\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g;
-  inline.escapedEmSt = /\\\*|\\_/g;
+  inline.escapedEmSt = /(?:^|[^\\])(?:\\\\)*\\[*_]/g;
   inline._comment = edit(block._comment).replace("(?:-->|$)", "-->").getRegex();
   inline.emStrong.lDelim = edit(inline.emStrong.lDelim).replace(/punct/g, inline._punctuation).getRegex();
   inline.emStrong.rDelimAst = edit(inline.emStrong.rDelimAst, "g").replace(/punct/g, inline._punctuation).getRegex();
       }
       this.tokenizer.rules = rules;
     }
+    /**
+     * Expose Rules
+     */
     static get rules() {
       return {
         block,
         inline
       };
     }
+    /**
+     * Static Lex Method
+     */
     static lex(src, options2) {
       const lexer2 = new Lexer(options2);
       return lexer2.lex(src);
     }
+    /**
+     * Static Lex Inline Method
+     */
     static lexInline(src, options2) {
       const lexer2 = new Lexer(options2);
       return lexer2.inlineTokens(src);
     }
+    /**
+     * Preprocessing
+     */
     lex(src) {
       src = src.replace(/\r\n|\r/g, "\n");
       this.blockTokens(src, this.tokens);
       }
       return this.tokens;
     }
+    /**
+     * Lexing
+     */
     blockTokens(src, tokens = []) {
       if (this.options.pedantic) {
         src = src.replace(/\t/g, "    ").replace(/^ +$/gm, "");
       this.state.top = true;
       return tokens;
     }
-    inline(src, tokens) {
+    inline(src, tokens = []) {
       this.inlineQueue.push({ src, tokens });
+      return tokens;
     }
+    /**
+     * Lexing/Compiling
+     */
     inlineTokens(src, tokens = []) {
       let token, lastToken, cutSrc;
       let maskedSrc = src;
         maskedSrc = maskedSrc.slice(0, match.index) + "[" + repeatString("a", match[0].length - 2) + "]" + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);
       }
       while ((match = this.tokenizer.rules.inline.escapedEmSt.exec(maskedSrc)) != null) {
-        maskedSrc = maskedSrc.slice(0, match.index) + "++" + maskedSrc.slice(this.tokenizer.rules.inline.escapedEmSt.lastIndex);
+        maskedSrc = maskedSrc.slice(0, match.index + match[0].length - 2) + "++" + maskedSrc.slice(this.tokenizer.rules.inline.escapedEmSt.lastIndex);
+        this.tokenizer.rules.inline.escapedEmSt.lastIndex--;
       }
       while (src) {
         if (!keepPrevChar) {
       }
       return '<pre><code class="' + this.options.langPrefix + escape4(lang, true) + '">' + (escaped ? code : escape4(code, true)) + "</code></pre>\n";
     }
+    /**
+     * @param {string} quote
+     */
     blockquote(quote2) {
       return `<blockquote>
 ${quote2}</blockquote>
@@ -35981,6 +37134,12 @@ ${quote2}</blockquote>
     html(html2) {
       return html2;
     }
+    /**
+     * @param {string} text
+     * @param {string} level
+     * @param {string} raw
+     * @param {any} slugger
+     */
     heading(text2, level, raw, slugger) {
       if (this.options.headerIds) {
         const id2 = this.options.headerPrefix + slugger.slug(raw);
@@ -35994,9 +37153,12 @@ ${quote2}</blockquote>
       return this.options.xhtml ? "<hr/>\n" : "<hr>\n";
     }
     list(body, ordered, start2) {
-      const type3 = ordered ? "ol" : "ul", startatt = ordered && start2 !== 1 ? ' start="' + start2 + '"' : "";
-      return "<" + type3 + startatt + ">\n" + body + "</" + type3 + ">\n";
+      const type2 = ordered ? "ol" : "ul", startatt = ordered && start2 !== 1 ? ' start="' + start2 + '"' : "";
+      return "<" + type2 + startatt + ">\n" + body + "</" + type2 + ">\n";
     }
+    /**
+     * @param {string} text
+     */
     listitem(text2) {
       return `<li>${text2}</li>
 `;
@@ -36004,41 +37166,69 @@ ${quote2}</blockquote>
     checkbox(checked) {
       return "<input " + (checked ? 'checked="" ' : "") + 'disabled="" type="checkbox"' + (this.options.xhtml ? " /" : "") + "> ";
     }
+    /**
+     * @param {string} text
+     */
     paragraph(text2) {
       return `<p>${text2}</p>
 `;
     }
+    /**
+     * @param {string} header
+     * @param {string} body
+     */
     table(header, body) {
       if (body)
         body = `<tbody>${body}</tbody>`;
       return "<table>\n<thead>\n" + header + "</thead>\n" + body + "</table>\n";
     }
+    /**
+     * @param {string} content
+     */
     tablerow(content) {
       return `<tr>
 ${content}</tr>
 `;
     }
     tablecell(content, flags) {
-      const type3 = flags.header ? "th" : "td";
-      const tag = flags.align ? `<${type3} align="${flags.align}">` : `<${type3}>`;
-      return tag + content + `</${type3}>
+      const type2 = flags.header ? "th" : "td";
+      const tag = flags.align ? `<${type2} align="${flags.align}">` : `<${type2}>`;
+      return tag + content + `</${type2}>
 `;
     }
+    /**
+     * span level renderer
+     * @param {string} text
+     */
     strong(text2) {
       return `<strong>${text2}</strong>`;
     }
+    /**
+     * @param {string} text
+     */
     em(text2) {
       return `<em>${text2}</em>`;
     }
+    /**
+     * @param {string} text
+     */
     codespan(text2) {
       return `<code>${text2}</code>`;
     }
     br() {
       return this.options.xhtml ? "<br/>" : "<br>";
     }
+    /**
+     * @param {string} text
+     */
     del(text2) {
       return `<del>${text2}</del>`;
     }
+    /**
+     * @param {string} href
+     * @param {string} title
+     * @param {string} text
+     */
     link(href, title, text2) {
       href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
       if (href === null) {
@@ -36051,6 +37241,11 @@ ${content}</tr>
       out += ">" + text2 + "</a>";
       return out;
     }
+    /**
+     * @param {string} href
+     * @param {string} title
+     * @param {string} text
+     */
     image(href, title, text2) {
       href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
       if (href === null) {
@@ -36068,6 +37263,7 @@ ${content}</tr>
     }
   };
   var TextRenderer = class {
+    // no need for block level renderers
     strong(text2) {
       return text2;
     }
@@ -36100,9 +37296,17 @@ ${content}</tr>
     constructor() {
       this.seen = {};
     }
+    /**
+     * @param {string} value
+     */
     serialize(value) {
       return value.toLowerCase().trim().replace(/<[!\/a-z].*?>/ig, "").replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, "").replace(/\s/g, "-");
     }
+    /**
+     * Finds the next safe (unique) slug to use
+     * @param {string} originalSlug
+     * @param {boolean} isDryRun
+     */
     getNextSafeSlug(originalSlug, isDryRun) {
       let slug = originalSlug;
       let occurenceAccumulator = 0;
@@ -36119,6 +37323,12 @@ ${content}</tr>
       }
       return slug;
     }
+    /**
+     * Convert string to unique id
+     * @param {object} [options]
+     * @param {boolean} [options.dryrun] Generates the next unique slug without
+     * updating the internal accumulator.
+     */
     slug(value, options2 = {}) {
       const slug = this.serialize(value);
       return this.getNextSafeSlug(slug, options2.dryrun);
@@ -36133,14 +37343,23 @@ ${content}</tr>
       this.textRenderer = new TextRenderer();
       this.slugger = new Slugger();
     }
+    /**
+     * Static Parse Method
+     */
     static parse(tokens, options2) {
       const parser3 = new Parser(options2);
       return parser3.parse(tokens);
     }
+    /**
+     * Static Parse Inline Method
+     */
     static parseInline(tokens, options2) {
       const parser3 = new Parser(options2);
       return parser3.parseInline(tokens);
     }
+    /**
+     * Parse Loop
+     */
     parse(tokens, top = true) {
       let out = "", i2, j2, k, l2, l3, row, cell, header, body, token, ordered, start2, loose, itemBody, item, checked, task, checkbox, ret;
       const l = tokens.length;
@@ -36162,11 +37381,20 @@ ${content}</tr>
             continue;
           }
           case "heading": {
-            out += this.renderer.heading(this.parseInline(token.tokens), token.depth, unescape3(this.parseInline(token.tokens, this.textRenderer)), this.slugger);
+            out += this.renderer.heading(
+              this.parseInline(token.tokens),
+              token.depth,
+              unescape3(this.parseInline(token.tokens, this.textRenderer)),
+              this.slugger
+            );
             continue;
           }
           case "code": {
-            out += this.renderer.code(token.text, token.lang, token.escaped);
+            out += this.renderer.code(
+              token.text,
+              token.lang,
+              token.escaped
+            );
             continue;
           }
           case "table": {
@@ -36174,7 +37402,10 @@ ${content}</tr>
             cell = "";
             l2 = token.header.length;
             for (j2 = 0; j2 < l2; j2++) {
-              cell += this.renderer.tablecell(this.parseInline(token.header[j2].tokens), { header: true, align: token.align[j2] });
+              cell += this.renderer.tablecell(
+                this.parseInline(token.header[j2].tokens),
+                { header: true, align: token.align[j2] }
+              );
             }
             header += this.renderer.tablerow(cell);
             body = "";
@@ -36184,7 +37415,10 @@ ${content}</tr>
               cell = "";
               l3 = row.length;
               for (k = 0; k < l3; k++) {
-                cell += this.renderer.tablecell(this.parseInline(row[k].tokens), { header: false, align: token.align[k] });
+                cell += this.renderer.tablecell(
+                  this.parseInline(row[k].tokens),
+                  { header: false, align: token.align[k] }
+                );
               }
               body += this.renderer.tablerow(cell);
             }
@@ -36261,6 +37495,9 @@ ${content}</tr>
       }
       return out;
     }
+    /**
+     * Parse Inline Tokens
+     */
     parseInline(tokens, renderer) {
       renderer = renderer || this.renderer;
       let out = "", i2, token, ret;
@@ -36397,18 +37634,26 @@ ${content}</tr>
       }
       return;
     }
+    function onError(e) {
+      e.message += "\nPlease report this to https://github.com/markedjs/marked.";
+      if (opt.silent) {
+        return "<p>An error occurred:</p><pre>" + escape4(e.message + "", true) + "</pre>";
+      }
+      throw e;
+    }
     try {
       const tokens = Lexer.lex(src, opt);
       if (opt.walkTokens) {
+        if (opt.async) {
+          return Promise.all(marked.walkTokens(tokens, opt.walkTokens)).then(() => {
+            return Parser.parse(tokens, opt);
+          }).catch(onError);
+        }
         marked.walkTokens(tokens, opt.walkTokens);
       }
       return Parser.parse(tokens, opt);
     } catch (e) {
-      e.message += "\nPlease report this to https://github.com/markedjs/marked.";
-      if (opt.silent) {
-        return "<p>An error occurred:</p><pre>" + escape4(e.message + "", true) + "</pre>";
-      }
-      throw e;
+      onError(e);
     }
   }
   marked.options = marked.setOptions = function(opt) {
@@ -36504,10 +37749,12 @@ ${content}</tr>
       if (pack.walkTokens) {
         const walkTokens2 = marked.defaults.walkTokens;
         opts.walkTokens = function(token) {
-          pack.walkTokens.call(this, token);
+          let values = [];
+          values.push(pack.walkTokens.call(this, token));
           if (walkTokens2) {
-            walkTokens2.call(this, token);
+            values = values.concat(walkTokens2.call(this, token));
           }
+          return values;
         };
       }
       if (hasExtensions) {
@@ -36517,35 +37764,37 @@ ${content}</tr>
     });
   };
   marked.walkTokens = function(tokens, callback) {
+    let values = [];
     for (const token of tokens) {
-      callback.call(marked, token);
+      values = values.concat(callback.call(marked, token));
       switch (token.type) {
         case "table": {
           for (const cell of token.header) {
-            marked.walkTokens(cell.tokens, callback);
+            values = values.concat(marked.walkTokens(cell.tokens, callback));
           }
           for (const row of token.rows) {
             for (const cell of row) {
-              marked.walkTokens(cell.tokens, callback);
+              values = values.concat(marked.walkTokens(cell.tokens, callback));
             }
           }
           break;
         }
         case "list": {
-          marked.walkTokens(token.items, callback);
+          values = values.concat(marked.walkTokens(token.items, callback));
           break;
         }
         default: {
           if (marked.defaults.extensions && marked.defaults.extensions.childTokens && marked.defaults.extensions.childTokens[token.type]) {
             marked.defaults.extensions.childTokens[token.type].forEach(function(childTokens) {
-              marked.walkTokens(token[childTokens], callback);
+              values = values.concat(marked.walkTokens(token[childTokens], callback));
             });
           } else if (token.tokens) {
-            marked.walkTokens(token.tokens, callback);
+            values = values.concat(marked.walkTokens(token.tokens, callback));
           }
         }
       }
     }
+    return values;
   };
   marked.parseInline = function(src, opt) {
     if (typeof src === "undefined" || src === null) {
@@ -36622,8 +37871,8 @@ ${content}</tr>
     do {
       let delta = coincident ? [1e-5, 0] : [0, 1e-5];
       loc = geoVecAdd(loc, delta);
-      let bbox = geoExtent(loc).bbox();
-      coincident = _cache3.rtree.search(bbox).length;
+      let bbox2 = geoExtent(loc).bbox();
+      coincident = _cache3.rtree.search(bbox2).length;
     } while (coincident);
     return loc;
   }
@@ -36660,6 +37909,8 @@ ${content}</tr>
     },
     loadIssues(projection2) {
       let params = {
+        // Tiles return a maximum # of issues
+        // So we want to filter our request for only types iD supports
         item: _osmoseData.items
       };
       let tiles = tiler3.zoomExtent([_tileZoom3, _tileZoom3]).getTiles(projection2);
@@ -36668,7 +37919,7 @@ ${content}</tr>
         if (_cache3.loadedTile[tile.id] || _cache3.inflightTile[tile.id])
           return;
         let [x, y, z] = tile.xyz;
-        let url = `${_osmoseUrlRoot}/issues/${z}/${x}/${y}.json?` + utilQsString(params);
+        let url = `${_osmoseUrlRoot}/issues/${z}/${x}/${y}.geojson?` + utilQsString(params);
         let controller = new AbortController();
         _cache3.inflightTile[tile.id] = controller;
         json_default(url, { signal: controller.signal }).then((data) => {
@@ -36781,19 +38032,24 @@ ${content}</tr>
           callback(err.message);
       });
     },
+    // Get all cached QAItems covering the viewport
     getItems(projection2) {
       const viewport = projection2.clipExtent();
       const min3 = [viewport[0][0], viewport[1][1]];
       const max3 = [viewport[1][0], viewport[0][1]];
-      const bbox = geoExtent(projection2.invert(min3), projection2.invert(max3)).bbox();
-      return _cache3.rtree.search(bbox).map((d) => d.data);
+      const bbox2 = geoExtent(projection2.invert(min3), projection2.invert(max3)).bbox();
+      return _cache3.rtree.search(bbox2).map((d) => d.data);
     },
+    // Get a QAItem from cache
+    // NOTE: Don't change method name until UI v3 is merged
     getError(id2) {
       return _cache3.data[id2];
     },
+    // get the name of the icon to display for this item
     getIcon(itemType) {
       return _osmoseData.icons[itemType];
     },
+    // Replace a single QAItem in the cache
     replaceItem(item) {
       if (!(item instanceof QAItem) || !item.id)
         return;
@@ -36801,12 +38057,14 @@ ${content}</tr>
       updateRtree3(encodeIssueRtree3(item), true);
       return item;
     },
+    // Remove a single QAItem from the cache
     removeItem(item) {
       if (!(item instanceof QAItem) || !item.id)
         return;
       delete _cache3.data[item.id];
       updateRtree3(encodeIssueRtree3(item), false);
     },
+    // Used to populate `closed:osmose:*` changeset tags
     getClosedCounts() {
       return _cache3.closed;
     },
@@ -36879,9 +38137,9 @@ ${content}</tr>
   }
   function loadTileDataToCache(data, tile, which) {
     const vectorTile = new import_vector_tile.VectorTile(new import_pbf.default(data));
-    let features2, cache, layer, i2, feature3, loc, d;
+    let features, cache, layer, i2, feature3, loc, d;
     if (vectorTile.layers.hasOwnProperty("image")) {
-      features2 = [];
+      features = [];
       cache = _mlyCache.images;
       layer = vectorTile.layers.image;
       for (i2 = 0; i2 < layer.length; i2++) {
@@ -36896,7 +38154,7 @@ ${content}</tr>
           sequence_id: feature3.properties.sequence_id
         };
         cache.forImageId[d.id] = d;
-        features2.push({
+        features.push({
           minX: loc[0],
           minY: loc[1],
           maxX: loc[0],
@@ -36905,11 +38163,11 @@ ${content}</tr>
         });
       }
       if (cache.rtree) {
-        cache.rtree.load(features2);
+        cache.rtree.load(features);
       }
     }
     if (vectorTile.layers.hasOwnProperty("sequence")) {
-      features2 = [];
+      features = [];
       cache = _mlyCache.sequences;
       layer = vectorTile.layers.sequence;
       for (i2 = 0; i2 < layer.length; i2++) {
@@ -36922,7 +38180,7 @@ ${content}</tr>
       }
     }
     if (vectorTile.layers.hasOwnProperty("point")) {
-      features2 = [];
+      features = [];
       cache = _mlyCache[which];
       layer = vectorTile.layers.point;
       for (i2 = 0; i2 < layer.length; i2++) {
@@ -36935,7 +38193,7 @@ ${content}</tr>
           last_seen_at: feature3.properties.last_seen_at,
           value: feature3.properties.value
         };
-        features2.push({
+        features.push({
           minX: loc[0],
           minY: loc[1],
           maxX: loc[0],
@@ -36944,11 +38202,11 @@ ${content}</tr>
         });
       }
       if (cache.rtree) {
-        cache.rtree.load(features2);
+        cache.rtree.load(features);
       }
     }
     if (vectorTile.layers.hasOwnProperty("traffic_sign")) {
-      features2 = [];
+      features = [];
       cache = _mlyCache[which];
       layer = vectorTile.layers.traffic_sign;
       for (i2 = 0; i2 < layer.length; i2++) {
@@ -36961,7 +38219,7 @@ ${content}</tr>
           last_seen_at: feature3.properties.last_seen_at,
           value: feature3.properties.value
         };
-        features2.push({
+        features.push({
           minX: loc[0],
           minY: loc[1],
           maxX: loc[0],
@@ -36970,7 +38228,7 @@ ${content}</tr>
         });
       }
       if (cache.rtree) {
-        cache.rtree.load(features2);
+        cache.rtree.load(features);
       }
     }
   }
@@ -37005,12 +38263,14 @@ ${content}</tr>
     }, []);
   }
   var mapillary_default = {
+    // Initialize Mapillary
     init: function() {
       if (!_mlyCache) {
         this.reset();
       }
       this.event = utilRebind(this, dispatch5, "on");
     },
+    // Reset cache and state
     reset: function() {
       if (_mlyCache) {
         Object.values(_mlyCache.requests.inflight).forEach(function(request3) {
@@ -37027,29 +38287,34 @@ ${content}</tr>
       };
       _mlyActiveImage = null;
     },
+    // Get visible images
     images: function(projection2) {
       const limit = 5;
       return searchLimited(limit, projection2, _mlyCache.images.rtree);
     },
+    // Get visible traffic signs
     signs: function(projection2) {
       const limit = 5;
       return searchLimited(limit, projection2, _mlyCache.signs.rtree);
     },
+    // Get visible map (point) features
     mapFeatures: function(projection2) {
       const limit = 5;
       return searchLimited(limit, projection2, _mlyCache.points.rtree);
     },
+    // Get cached image by id
     cachedImage: function(imageId) {
       return _mlyCache.images.forImageId[imageId];
     },
+    // Get visible sequences
     sequences: function(projection2) {
       const viewport = projection2.clipExtent();
       const min3 = [viewport[0][0], viewport[1][1]];
       const max3 = [viewport[1][0], viewport[0][1]];
-      const bbox = geoExtent(projection2.invert(min3), projection2.invert(max3)).bbox();
+      const bbox2 = geoExtent(projection2.invert(min3), projection2.invert(max3)).bbox();
       const sequenceIds = {};
       let lineStrings = [];
-      _mlyCache.images.rtree.search(bbox).forEach(function(d) {
+      _mlyCache.images.rtree.search(bbox2).forEach(function(d) {
         if (d.data.sequence_id) {
           sequenceIds[d.data.sequence_id] = true;
         }
@@ -37061,15 +38326,19 @@ ${content}</tr>
       });
       return lineStrings;
     },
+    // Load images in the visible area
     loadImages: function(projection2) {
       loadTiles("images", tileUrl, 14, projection2);
     },
+    // Load traffic signs in the visible area
     loadSigns: function(projection2) {
       loadTiles("signs", trafficSignTileUrl, 14, projection2);
     },
+    // Load map (point) features in the visible area
     loadMapFeatures: function(projection2) {
       loadTiles("points", mapFeatureTileUrl, 14, projection2);
     },
+    // Return a promise that resolves when the image viewer (Mapillary JS) library has finished loading
     ensureViewerLoaded: function(context) {
       if (_loadViewerPromise)
         return _loadViewerPromise;
@@ -37097,31 +38366,45 @@ ${content}</tr>
       });
       return _loadViewerPromise;
     },
+    // Load traffic sign image sprites
     loadSignResources: function(context) {
-      context.ui().svgDefs.addSprites(["mapillary-sprite"], false);
+      context.ui().svgDefs.addSprites(
+        ["mapillary-sprite"],
+        false
+        /* don't override colors */
+      );
       return this;
     },
+    // Load map (point) feature image sprites
     loadObjectResources: function(context) {
-      context.ui().svgDefs.addSprites(["mapillary-object-sprite"], false);
+      context.ui().svgDefs.addSprites(
+        ["mapillary-object-sprite"],
+        false
+        /* don't override colors */
+      );
       return this;
     },
+    // Remove previous detections in image viewer
     resetTags: function() {
       if (_mlyViewer && !_mlyFallback) {
         _mlyViewer.getComponent("tag").removeAll();
       }
     },
+    // Show map feature detections in image viewer
     showFeatureDetections: function(value) {
       _mlyShowFeatureDetections = value;
       if (!_mlyShowFeatureDetections && !_mlyShowSignDetections) {
         this.resetTags();
       }
     },
+    // Show traffic sign detections in image viewer
     showSignDetections: function(value) {
       _mlyShowSignDetections = value;
       if (!_mlyShowFeatureDetections && !_mlyShowSignDetections) {
         this.resetTags();
       }
     },
+    // Apply filter to image viewer
     filterViewer: function(context) {
       const showsPano = context.photos().showsPanoramic();
       const showsFlat = context.photos().showsFlat();
@@ -37144,6 +38427,7 @@ ${content}</tr>
       _mlyViewerFilter = filter2;
       return filter2;
     },
+    // Make the image viewer visible
     showViewer: function(context) {
       const wrap2 = context.container().select(".photoviewer").classed("hide", false);
       const isHidden = wrap2.selectAll(".photo-wrapper.mly-wrapper.hide").size();
@@ -37154,6 +38438,7 @@ ${content}</tr>
       }
       return this;
     },
+    // Hide the image viewer and resets map markers
     hideViewer: function(context) {
       _mlyActiveImage = null;
       if (!_mlyFallback && _mlyViewer) {
@@ -37169,6 +38454,7 @@ ${content}</tr>
       dispatch5.call("loadedSigns");
       return this.setStyles(context, null);
     },
+    // Update the URL with current image id
     updateUrlImage: function(imageId) {
       if (!window.mocha) {
         const hash = utilStringQs(window.location.hash);
@@ -37180,12 +38466,14 @@ ${content}</tr>
         window.location.replace("#" + utilQsString(hash, true));
       }
     },
+    // Highlight the detection in the viewer that is related to the clicked map feature
     highlightDetection: function(detection) {
       if (detection) {
         _mlyHighlightedDetection = detection.id;
       }
       return this;
     },
+    // Initialize image viewer (Mapillar JS)
     initViewer: function(context) {
       const that = this;
       if (!window.mapillary)
@@ -37210,7 +38498,9 @@ ${content}</tr>
           sequence: false,
           tag: false,
           image: true,
+          // fallback
           navigation: true
+          // fallback
         };
       }
       _mlyViewer = new mapillary.Viewer(opts);
@@ -37240,6 +38530,7 @@ ${content}</tr>
         dispatch5.call("bearingChanged", void 0, e);
       }
     },
+    // Move to an image
     selectImage: function(context, imageId) {
       if (_mlyViewer && imageId) {
         _mlyViewer.moveTo(imageId).catch(function(e) {
@@ -37248,12 +38539,15 @@ ${content}</tr>
       }
       return this;
     },
+    // Return the currently displayed image
     getActiveImage: function() {
       return _mlyActiveImage;
     },
+    // Return a list of detection objects for the given id
     getDetections: function(id2) {
       return loadData(`${apiUrl}/${id2}/detections?access_token=${accessToken}&fields=id,value,image`);
     },
+    // Set the currently visible image
     setActiveImage: function(image) {
       if (image) {
         _mlyActiveImage = {
@@ -37267,6 +38561,7 @@ ${content}</tr>
         _mlyActiveImage = null;
       }
     },
+    // Update the currently highlighted sequence and selected bubble.
     setStyles: function(context, hovered) {
       const hoveredImageId = hovered && hovered.id;
       const hoveredSequenceId = hovered && hovered.sequence_id;
@@ -37283,6 +38578,7 @@ ${content}</tr>
       });
       return this;
     },
+    // Get detections for the current image and shows them in the image viewer
     updateDetections: function(imageId, url) {
       if (!_mlyViewer || _mlyFallback)
         return;
@@ -37341,18 +38637,23 @@ ${content}</tr>
         const tile = new import_vector_tile.VectorTile(new import_pbf.default(uintArray.buffer));
         const layer = tile.layers["mpy-or"];
         const geometries = layer.feature(0).loadGeometry();
-        const polygon2 = geometries.map((ring) => ring.map((point) => [point.x / layer.extent, point.y / layer.extent]));
-        tag = new mapillary.OutlineTag(data.id, new mapillary.PolygonGeometry(polygon2[0]), {
-          text: text2,
-          textColor: color2,
-          lineColor: color2,
-          lineWidth: 2,
-          fillColor: color2,
-          fillOpacity: 0.3
-        });
+        const polygon2 = geometries.map((ring) => ring.map((point2) => [point2.x / layer.extent, point2.y / layer.extent]));
+        tag = new mapillary.OutlineTag(
+          data.id,
+          new mapillary.PolygonGeometry(polygon2[0]),
+          {
+            text: text2,
+            textColor: color2,
+            lineColor: color2,
+            lineWidth: 2,
+            fillColor: color2,
+            fillOpacity: 0.3
+          }
+        );
         return tag;
       }
     },
+    // Return the current cache
     cache: function() {
       return _mlyCache;
     }
@@ -37406,7 +38707,7 @@ ${content}</tr>
       var issue = this;
       if (issue.severity === "warning") {
         fixes.push(new validationIssueFix({
-          title: _t.html("issues.fix.ignore_issue.title"),
+          title: _t.append("issues.fix.ignore_issue.title"),
           icon: "iD-icon-close",
           onClick: function() {
             context.validator().ignoreIssue(this.issue.id);
@@ -37414,7 +38715,7 @@ ${content}</tr>
         }));
       }
       fixes.forEach(function(fix) {
-        fix.id = fix.title;
+        fix.id = fix.title.stringId;
         fix.issue = issue;
         if (fix.autoArgs) {
           issue.autoFix = fix;
@@ -37528,6 +38829,7 @@ ${content}</tr>
       this._areaKeys = osmAreaKeys;
       this._lineKeys = buildLineKeys();
     },
+    // list of rules only relevant to tag checks...
     filterRuleChecks: function(selector) {
       var _ruleChecks = this._ruleChecks;
       return Object.keys(selector).reduce(function(rules, key) {
@@ -37537,6 +38839,7 @@ ${content}</tr>
         return rules;
       }, []);
     },
+    // builds tagMap from mapcss-parse selector object...
     buildTagMap: function(selector) {
       var getRegexValues = function(regexes) {
         return regexes.map(function(regex) {
@@ -37567,6 +38870,7 @@ ${content}</tr>
       }, {});
       return tagMap;
     },
+    // inspired by osmWay#isArea()
     inferGeometry: function(tagMap) {
       var _lineKeys = this._lineKeys;
       var _areaKeys = this._areaKeys;
@@ -37594,14 +38898,18 @@ ${content}</tr>
       }
       return "line";
     },
+    // adds from mapcss-parse selector check...
     addRule: function(selector) {
       var rule = {
+        // checks relevant to mapcss-selector
         checks: this.filterRuleChecks(selector),
+        // true if all conditions for a tag error are true..
         matches: function(entity) {
           return this.checks.every(function(check) {
             return check(entity.tags);
           });
         },
+        // borrowed from Way#isArea()
         inferredGeometry: this.inferGeometry(this.buildTagMap(selector), this._areaKeys),
         geometryMatches: function(entity, graph) {
           if (entity.type === "node" || entity.type === "relation") {
@@ -37610,6 +38918,7 @@ ${content}</tr>
             return this.inferredGeometry === entity.geometry(graph);
           }
         },
+        // when geometries match and tag matches are present, return a warning...
         findIssues: function(entity, graph, issues) {
           if (this.geometryMatches(entity, graph) && this.matches(entity)) {
             var severity = Object.keys(selector).indexOf("error") > -1 ? "error" : "warning";
@@ -37630,9 +38939,11 @@ ${content}</tr>
     clearRules: function() {
       this._validationRules = [];
     },
+    // returns validationRules...
     validationRules: function() {
       return this._validationRules;
     },
+    // returns ruleChecks
     ruleChecks: function() {
       return this._ruleChecks;
     }
@@ -37640,7 +38951,7 @@ ${content}</tr>
 
   // modules/services/nominatim.js
   var import_rbush5 = __toESM(require_rbush_min());
-  var apibase = "https://nominatim.openstreetmap.org/";
+  var apibase = nominatimApiUrl;
   var _inflight = {};
   var _nominatimCache;
   var nominatim_default = {
@@ -37667,7 +38978,9 @@ ${content}</tr>
       });
     },
     reverse: function(loc, callback) {
-      var cached = _nominatimCache.search({ minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1] });
+      var cached = _nominatimCache.search(
+        { minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1] }
+      );
       if (cached.length > 0) {
         if (callback)
           callback(null, cached[0].data);
@@ -37728,357 +39041,359 @@ ${content}</tr>
   function simplify(str2) {
     if (typeof str2 !== "string")
       return "";
-    return import_diacritics2.default.remove(str2.replace(/&/g, "and").replace(/İ/ig, "i").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\u2000-\u206f\u2cf9-\u2cfc\u2cfe\u2cff\u2d70\u2e00-\u2e7f\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\ufeff\uff01-\uff03\uff05-\uff07\uff0a\uff0c\uff0e\uff0f\uff1a\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65]+/g, "").toLowerCase());
+    return import_diacritics2.default.remove(
+      str2.replace(/&/g, "and").replace(/İ/ig, "i").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\u2000-\u206f\u2cf9-\u2cfc\u2cfe\u2cff\u2d70\u2e00-\u2e7f\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\ufeff\uff01-\uff03\uff05-\uff07\uff0a\uff0c\uff0e\uff0f\uff1a\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65]+/g, "").toLowerCase()
+    );
   }
 
   // node_modules/name-suggestion-index/config/matchGroups.json
-  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/beer",
-      "shop/beverages",
-      "shop/kiosk",
-      "shop/wine"
-    ],
-    camping: [
-      "tourism/camp_site",
-      "tourism/caravan_site"
-    ],
-    car_parts: [
-      "shop/car_parts",
-      "shop/car_repair",
-      "shop/tires",
-      "shop/tyres"
-    ],
-    clinic: [
-      "amenity/clinic",
-      "amenity/doctors",
-      "healthcare/clinic",
-      "healthcare/laboratory",
-      "healthcare/physiotherapist",
-      "healthcare/sample_collection",
-      "healthcare/dialysis"
-    ],
-    convenience: [
-      "shop/beauty",
-      "shop/chemist",
-      "shop/convenience",
-      "shop/cosmetics",
-      "shop/grocery",
-      "shop/kiosk",
-      "shop/newsagent",
-      "shop/perfumery"
-    ],
-    coworking: [
-      "amenity/coworking_space",
-      "office/coworking",
-      "office/coworking_space"
-    ],
-    dentist: [
-      "amenity/dentist",
-      "amenity/doctors",
-      "healthcare/dentist"
-    ],
-    electronics: [
-      "office/telecommunication",
-      "shop/computer",
-      "shop/electronics",
-      "shop/hifi",
-      "shop/kiosk",
-      "shop/mobile",
-      "shop/mobile_phone",
-      "shop/telecommunication"
-    ],
-    fabric: [
-      "shop/fabric",
-      "shop/haberdashery",
-      "shop/sewing"
-    ],
-    fashion: [
-      "shop/accessories",
-      "shop/bag",
-      "shop/boutique",
-      "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/bar",
-      "amenity/cafe",
-      "amenity/fast_food",
-      "amenity/ice_cream",
-      "amenity/pub",
-      "amenity/restaurant",
-      "shop/bakery",
-      "shop/candy",
-      "shop/chocolate",
-      "shop/coffee",
-      "shop/confectionary",
-      "shop/confectionery",
-      "shop/food",
-      "shop/kiosk",
-      "shop/ice_cream",
-      "shop/pastry",
-      "shop/tea"
-    ],
-    fuel: [
-      "amenity/fuel",
-      "shop/gas",
-      "shop/convenience;gas",
-      "shop/gas;convenience"
-    ],
-    gift: [
-      "shop/gift",
-      "shop/card",
-      "shop/cards",
-      "shop/kiosk",
-      "shop/stationery"
-    ],
-    hardware: [
-      "shop/bathroom_furnishing",
-      "shop/carpet",
-      "shop/diy",
-      "shop/doityourself",
-      "shop/doors",
-      "shop/electrical",
-      "shop/flooring",
-      "shop/hardware",
-      "shop/hardware_store",
-      "shop/power_tools",
-      "shop/tool_hire",
-      "shop/tools",
-      "shop/trade"
-    ],
-    health_food: [
-      "shop/health",
-      "shop/health_food",
-      "shop/herbalist",
-      "shop/nutrition_supplements"
-    ],
-    hobby: [
-      "shop/electronics",
-      "shop/hobby",
-      "shop/books",
-      "shop/games",
-      "shop/collector",
-      "shop/toys",
-      "shop/model",
-      "shop/video_games",
-      "shop/anime"
-    ],
-    hospital: [
-      "amenity/doctors",
-      "amenity/hospital",
-      "healthcare/hospital"
-    ],
-    houseware: [
-      "shop/houseware",
-      "shop/interior_decoration"
-    ],
-    lifeboat_station: [
-      "amenity/lifeboat_station",
-      "emergency/lifeboat_station",
-      "emergency/marine_rescue"
-    ],
-    lodging: [
-      "tourism/hotel",
-      "tourism/motel"
-    ],
-    money_transfer: [
-      "amenity/money_transfer",
-      "shop/money_transfer"
-    ],
-    office_supplies: [
-      "shop/office_supplies",
-      "shop/stationary",
-      "shop/stationery"
-    ],
-    outdoor: [
-      "shop/clothes",
-      "shop/outdoor",
-      "shop/sports"
-    ],
-    parcel_locker: [
-      "amenity/parcel_locker",
-      "amenity/vending_machine"
-    ],
-    pharmacy: [
-      "amenity/doctors",
-      "amenity/pharmacy",
-      "healthcare/pharmacy"
-    ],
-    playground: [
-      "amenity/theme_park",
-      "leisure/amusement_arcade",
-      "leisure/playground"
-    ],
-    rental: [
-      "amenity/bicycle_rental",
-      "amenity/boat_rental",
-      "amenity/car_rental",
-      "amenity/truck_rental",
-      "amenity/vehicle_rental",
-      "shop/kiosk",
-      "shop/rental"
-    ],
-    school: [
-      "amenity/childcare",
-      "amenity/college",
-      "amenity/kindergarten",
-      "amenity/language_school",
-      "amenity/prep_school",
-      "amenity/school",
-      "amenity/university"
-    ],
-    storage: [
-      "shop/storage_units",
-      "shop/storage_rental"
-    ],
-    substation: [
-      "power/station",
-      "power/substation",
-      "power/sub_station"
-    ],
-    supermarket: [
-      "shop/food",
-      "shop/frozen_food",
-      "shop/greengrocer",
-      "shop/grocery",
-      "shop/supermarket",
-      "shop/wholesale"
-    ],
-    variety_store: [
-      "shop/variety_store",
-      "shop/discount",
-      "shop/convenience"
-    ],
-    vending: [
-      "amenity/vending_machine",
-      "shop/kiosk",
-      "shop/vending_machine"
-    ],
-    weight_loss: [
-      "amenity/clinic",
-      "amenity/doctors",
-      "amenity/weight_clinic",
-      "healthcare/counselling",
-      "leisure/fitness_centre",
-      "office/therapist",
-      "shop/beauty",
-      "shop/diet",
-      "shop/food",
-      "shop/health_food",
-      "shop/herbalist",
-      "shop/nutrition",
-      "shop/nutrition_supplements",
-      "shop/weight_loss"
-    ],
-    wholesale: [
-      "shop/wholesale",
-      "shop/supermarket",
-      "shop/department_store"
-    ]
-  };
   var matchGroups_default = {
-    matchGroups
+    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/beer",
+        "shop/beverages",
+        "shop/kiosk",
+        "shop/wine"
+      ],
+      camping: [
+        "tourism/camp_site",
+        "tourism/caravan_site"
+      ],
+      car_parts: [
+        "shop/car_parts",
+        "shop/car_repair",
+        "shop/tires",
+        "shop/tyres"
+      ],
+      clinic: [
+        "amenity/clinic",
+        "amenity/doctors",
+        "healthcare/clinic",
+        "healthcare/laboratory",
+        "healthcare/physiotherapist",
+        "healthcare/sample_collection",
+        "healthcare/dialysis"
+      ],
+      convenience: [
+        "shop/beauty",
+        "shop/chemist",
+        "shop/convenience",
+        "shop/cosmetics",
+        "shop/grocery",
+        "shop/kiosk",
+        "shop/newsagent",
+        "shop/perfumery"
+      ],
+      coworking: [
+        "amenity/coworking_space",
+        "office/coworking",
+        "office/coworking_space"
+      ],
+      dentist: [
+        "amenity/dentist",
+        "amenity/doctors",
+        "healthcare/dentist"
+      ],
+      electronics: [
+        "office/telecommunication",
+        "shop/computer",
+        "shop/electronics",
+        "shop/hifi",
+        "shop/kiosk",
+        "shop/mobile",
+        "shop/mobile_phone",
+        "shop/telecommunication"
+      ],
+      fabric: [
+        "shop/fabric",
+        "shop/haberdashery",
+        "shop/sewing"
+      ],
+      fashion: [
+        "shop/accessories",
+        "shop/bag",
+        "shop/boutique",
+        "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/bar",
+        "amenity/cafe",
+        "amenity/fast_food",
+        "amenity/ice_cream",
+        "amenity/pub",
+        "amenity/restaurant",
+        "shop/bakery",
+        "shop/candy",
+        "shop/chocolate",
+        "shop/coffee",
+        "shop/confectionary",
+        "shop/confectionery",
+        "shop/food",
+        "shop/kiosk",
+        "shop/ice_cream",
+        "shop/pastry",
+        "shop/tea"
+      ],
+      fuel: [
+        "amenity/fuel",
+        "shop/gas",
+        "shop/convenience;gas",
+        "shop/gas;convenience"
+      ],
+      gift: [
+        "shop/gift",
+        "shop/card",
+        "shop/cards",
+        "shop/kiosk",
+        "shop/stationery"
+      ],
+      hardware: [
+        "shop/bathroom_furnishing",
+        "shop/carpet",
+        "shop/diy",
+        "shop/doityourself",
+        "shop/doors",
+        "shop/electrical",
+        "shop/flooring",
+        "shop/hardware",
+        "shop/hardware_store",
+        "shop/power_tools",
+        "shop/tool_hire",
+        "shop/tools",
+        "shop/trade"
+      ],
+      health_food: [
+        "shop/health",
+        "shop/health_food",
+        "shop/herbalist",
+        "shop/nutrition_supplements"
+      ],
+      hobby: [
+        "shop/electronics",
+        "shop/hobby",
+        "shop/books",
+        "shop/games",
+        "shop/collector",
+        "shop/toys",
+        "shop/model",
+        "shop/video_games",
+        "shop/anime"
+      ],
+      hospital: [
+        "amenity/doctors",
+        "amenity/hospital",
+        "healthcare/hospital"
+      ],
+      houseware: [
+        "shop/houseware",
+        "shop/interior_decoration"
+      ],
+      lifeboat_station: [
+        "amenity/lifeboat_station",
+        "emergency/lifeboat_station",
+        "emergency/marine_rescue"
+      ],
+      lodging: [
+        "tourism/hotel",
+        "tourism/motel"
+      ],
+      money_transfer: [
+        "amenity/money_transfer",
+        "shop/money_transfer"
+      ],
+      office_supplies: [
+        "shop/office_supplies",
+        "shop/stationary",
+        "shop/stationery"
+      ],
+      outdoor: [
+        "shop/clothes",
+        "shop/outdoor",
+        "shop/sports"
+      ],
+      parcel_locker: [
+        "amenity/parcel_locker",
+        "amenity/vending_machine"
+      ],
+      pharmacy: [
+        "amenity/doctors",
+        "amenity/pharmacy",
+        "healthcare/pharmacy"
+      ],
+      playground: [
+        "amenity/theme_park",
+        "leisure/amusement_arcade",
+        "leisure/playground"
+      ],
+      rental: [
+        "amenity/bicycle_rental",
+        "amenity/boat_rental",
+        "amenity/car_rental",
+        "amenity/truck_rental",
+        "amenity/vehicle_rental",
+        "shop/kiosk",
+        "shop/rental"
+      ],
+      school: [
+        "amenity/childcare",
+        "amenity/college",
+        "amenity/kindergarten",
+        "amenity/language_school",
+        "amenity/prep_school",
+        "amenity/school",
+        "amenity/university"
+      ],
+      storage: [
+        "shop/storage_units",
+        "shop/storage_rental"
+      ],
+      substation: [
+        "power/station",
+        "power/substation",
+        "power/sub_station"
+      ],
+      supermarket: [
+        "shop/food",
+        "shop/frozen_food",
+        "shop/greengrocer",
+        "shop/grocery",
+        "shop/supermarket",
+        "shop/wholesale"
+      ],
+      variety_store: [
+        "shop/variety_store",
+        "shop/discount",
+        "shop/convenience"
+      ],
+      vending: [
+        "amenity/vending_machine",
+        "shop/kiosk",
+        "shop/vending_machine"
+      ],
+      weight_loss: [
+        "amenity/clinic",
+        "amenity/doctors",
+        "amenity/weight_clinic",
+        "healthcare/counselling",
+        "leisure/fitness_centre",
+        "office/therapist",
+        "shop/beauty",
+        "shop/diet",
+        "shop/food",
+        "shop/health_food",
+        "shop/herbalist",
+        "shop/nutrition",
+        "shop/nutrition_supplements",
+        "shop/weight_loss"
+      ],
+      wholesale: [
+        "shop/wholesale",
+        "shop/supermarket",
+        "shop/department_store"
+      ]
+    }
   };
 
   // node_modules/name-suggestion-index/config/genericWords.json
-  var genericWords = [
-    "^(barn|bazaa?r|bench|bou?tique|building|casa|church)$",
-    "^(baseball|basketball|football|soccer|softball|tennis(halle)?)\\s?(field|court)?$",
-    "^(club|green|out|ware)\\s?house$",
-    "^(driveway|el \xE1rbol|fountain|generic|golf|government|graveyard)$",
-    "^(fixme|n\\s?\\/?\\s?a|name|no\\s?name|none|null|temporary|test|unknown)$",
-    "^(hofladen|librairie|magazine?|maison)$",
-    "^(mobile home|skate)?\\s?park$",
-    "^(obuwie|pond|pool|sale|shops?|sklep|stores?)$",
-    "^\\?+$",
-    "^private$",
-    "^tattoo( studio)?$",
-    "^windmill$",
-    "^\u0446\u0435\u0440\u043A\u043E\u0432\u043D\u0430\u044F( \u043B\u0430\u0432\u043A\u0430)?$"
-  ];
   var genericWords_default = {
-    genericWords
+    genericWords: [
+      "^(barn|bazaa?r|bench|bou?tique|building|casa|church)$",
+      "^(baseball|basketball|football|soccer|softball|tennis(halle)?)\\s?(field|court)?$",
+      "^(club|green|out|ware)\\s?house$",
+      "^(driveway|el \xE1rbol|fountain|generic|golf|government|graveyard)$",
+      "^(fixme|n\\s?\\/?\\s?a|name|no\\s?name|none|null|temporary|test|unknown)$",
+      "^(hofladen|librairie|magazine?|maison)$",
+      "^(mobile home|skate)?\\s?park$",
+      "^(obuwie|pond|pool|sale|shops?|sklep|stores?)$",
+      "^\\?+$",
+      "^private$",
+      "^tattoo( studio)?$",
+      "^windmill$",
+      "^\u0446\u0435\u0440\u043A\u043E\u0432\u043D\u0430\u044F( \u043B\u0430\u0432\u043A\u0430)?$"
+    ]
   };
 
   // node_modules/name-suggestion-index/config/trees.json
-  var trees = {
-    brands: {
-      emoji: "\u{1F354}",
-      mainTag: "brand:wikidata",
-      sourceTags: ["brand", "name"],
-      nameTags: {
-        primary: "^(name|name:\\w+)$",
-        alternate: "^(brand|brand:\\w+|operator|operator:\\w+|\\w+_name|\\w+_name:\\w+)$"
-      }
-    },
-    flags: {
-      emoji: "\u{1F6A9}",
-      mainTag: "flag:wikidata",
-      nameTags: {
-        primary: "^(flag:name|flag:name:\\w+)$",
-        alternate: "^(country|country:\\w+|flag|flag:\\w+|subject|subject:\\w+)$"
-      }
-    },
-    operators: {
-      emoji: "\u{1F4BC}",
-      mainTag: "operator:wikidata",
-      sourceTags: ["operator"],
-      nameTags: {
-        primary: "^(name|name:\\w+|operator|operator:\\w+)$",
-        alternate: "^(brand|brand:\\w+|\\w+_name|\\w+_name:\\w+)$"
-      }
-    },
-    transit: {
-      emoji: "\u{1F687}",
-      mainTag: "network:wikidata",
-      sourceTags: ["network"],
-      nameTags: {
-        primary: "^network$",
-        alternate: "^(operator|operator:\\w+|network:\\w+|\\w+_name|\\w+_name:\\w+)$"
+  var trees_default = {
+    trees: {
+      brands: {
+        emoji: "\u{1F354}",
+        mainTag: "brand:wikidata",
+        sourceTags: ["brand", "name"],
+        nameTags: {
+          primary: "^(name|name:\\w+)$",
+          alternate: "^(brand|brand:\\w+|operator|operator:\\w+|\\w+_name|\\w+_name:\\w+)$"
+        }
+      },
+      flags: {
+        emoji: "\u{1F6A9}",
+        mainTag: "flag:wikidata",
+        nameTags: {
+          primary: "^(flag:name|flag:name:\\w+)$",
+          alternate: "^(country|country:\\w+|flag|flag:\\w+|subject|subject:\\w+)$"
+        }
+      },
+      operators: {
+        emoji: "\u{1F4BC}",
+        mainTag: "operator:wikidata",
+        sourceTags: ["operator"],
+        nameTags: {
+          primary: "^(name|name:\\w+|operator|operator:\\w+)$",
+          alternate: "^(brand|brand:\\w+|\\w+_name|\\w+_name:\\w+)$"
+        }
+      },
+      transit: {
+        emoji: "\u{1F687}",
+        mainTag: "network:wikidata",
+        sourceTags: ["network"],
+        nameTags: {
+          primary: "^network$",
+          alternate: "^(operator|operator:\\w+|network:\\w+|\\w+_name|\\w+_name:\\w+)$"
+        }
       }
     }
   };
-  var trees_default = {
-    trees
-  };
 
   // node_modules/name-suggestion-index/lib/matcher.js
-  var matchGroups2 = matchGroups_default.matchGroups;
-  var trees2 = trees_default.trees;
+  var matchGroups = matchGroups_default.matchGroups;
+  var trees = trees_default.trees;
   var Matcher = class {
+    //
+    // `constructor`
+    // initialize the genericWords regexes
     constructor() {
       this.matchIndex = void 0;
       this.genericWords = /* @__PURE__ */ new Map();
@@ -38088,6 +39403,18 @@ ${content}</tr>
       this.locationIndex = void 0;
       this.warnings = [];
     }
+    //
+    // `buildMatchIndex()`
+    // Call this to prepare the matcher for use
+    //
+    // `data` needs to be an Object indexed on a 'tree/key/value' path.
+    // (e.g. cache filled by `fileTree.read` or data found in `dist/nsi.json`)
+    // {
+    //    'brands/amenity/bank': { properties: {}, items: [ {}, {}, … ] },
+    //    'brands/amenity/bar':  { properties: {}, items: [ {}, {}, … ] },
+    //    …
+    // }
+    //
     buildMatchIndex(data) {
       const that = this;
       if (that.matchIndex)
@@ -38101,7 +39428,7 @@ ${content}</tr>
         const k = parts[1];
         const v = parts[2];
         const thiskv = `${k}/${v}`;
-        const tree = trees2[t];
+        const tree = trees[t];
         let branch = that.matchIndex.get(thiskv);
         if (!branch) {
           branch = {
@@ -38126,7 +39453,7 @@ ${content}</tr>
         const skipGenericKV = skipGenericKVMatches(t, k, v);
         const genericKV = /* @__PURE__ */ new Set([`${k}/yes`, `building/yes`]);
         const matchGroupKV = /* @__PURE__ */ new Set();
-        Object.values(matchGroups2).forEach((matchGroup) => {
+        Object.values(matchGroups).forEach((matchGroup) => {
           const inGroup = matchGroup.some((otherkv) => otherkv === thiskv);
           if (!inGroup)
             return;
@@ -38220,6 +39547,20 @@ ${content}</tr>
         return t === "flags" || t === "transit" || k === "landuse" || v === "atm" || v === "bicycle_parking" || v === "car_sharing" || v === "caravan_site" || v === "charging_station" || v === "dog_park" || v === "parking" || v === "phone" || v === "playground" || v === "post_box" || v === "public_bookcase" || v === "recycling" || v === "vending_machine";
       }
     }
+    //
+    // `buildLocationIndex()`
+    // Call this to prepare a which-polygon location index.
+    // This *resolves* all the locationSets into GeoJSON, which takes some time.
+    // You can skip this step if you don't care about matching within a location.
+    //
+    // `data` needs to be an Object indexed on a 'tree/key/value' path.
+    // (e.g. cache filled by `fileTree.read` or data found in `dist/nsi.json`)
+    // {
+    //    'brands/amenity/bank': { properties: {}, items: [ {}, {}, … ] },
+    //    'brands/amenity/bar':  { properties: {}, items: [ {}, {}, … ] },
+    //    …
+    // }
+    //
     buildLocationIndex(data, loco) {
       const that = this;
       if (that.locationIndex)
@@ -38260,6 +39601,54 @@ ${content}</tr>
         return JSON.parse(JSON.stringify(obj));
       }
     }
+    //
+    // `match()`
+    // Pass parts and return an Array of matches.
+    // `k` - key
+    // `v` - value
+    // `n` - namelike
+    // `loc` - optional - [lon,lat] location to search
+    //
+    // 1. If the [k,v,n] tuple matches a canonical item…
+    // Return an Array of match results.
+    // Each result will include the area in km² that the item is valid.
+    //
+    // Order of results:
+    // Primary ordering will be on the "match" column:
+    //   "primary" - where the query matches the `name` tag, followed by
+    //   "alternate" - where the query matches an alternate name tag (e.g. short_name, brand, operator, etc)
+    // Secondary ordering will be on the "area" column:
+    //   "area descending" if no location was provided, (worldwide before local)
+    //   "area ascending" if location was provided (local before worldwide)
+    //
+    // [
+    //   { match: 'primary',   itemID: String,  area: Number,  kv: String,  nsimple: String },
+    //   { match: 'primary',   itemID: String,  area: Number,  kv: String,  nsimple: String },
+    //   { match: 'alternate', itemID: String,  area: Number,  kv: String,  nsimple: String },
+    //   { match: 'alternate', itemID: String,  area: Number,  kv: String,  nsimple: String },
+    //   …
+    // ]
+    //
+    // -or-
+    //
+    // 2. If the [k,v,n] tuple matches an exclude pattern…
+    // Return an Array with a single exclude result, either
+    //
+    // [ { match: 'excludeGeneric', pattern: String,  kv: String } ]  // "generic" e.g. "Food Court"
+    //   or
+    // [ { match: 'excludeNamed', pattern: String,  kv: String } ]    // "named", e.g. "Kebabai"
+    //
+    // About results
+    //   "generic" - a generic word that is probably not really a name.
+    //     For these, iD should warn the user "Hey don't put 'food court' in the name tag".
+    //   "named" - a real name like "Kebabai" that is just common, but not a brand.
+    //     For these, iD should just let it be. We don't include these in NSI, but we don't want to nag users about it either.
+    //
+    // -or-
+    //
+    // 3. If the [k,v,n] tuple matches nothing of any kind, return `null`
+    //
+    //
     match(k, v, n2, loc) {
       const that = this;
       if (!that.matchIndex) {
@@ -38283,8 +39672,8 @@ ${content}</tr>
         let didMatch = tryMatch(which, kv);
         if (didMatch)
           return;
-        for (let mg in matchGroups2) {
-          const matchGroup = matchGroups2[mg];
+        for (let mg in matchGroups) {
+          const matchGroup = matchGroups[mg];
           const inGroup = matchGroup.some((otherkv) => otherkv === kv);
           if (!inGroup)
             continue;
@@ -38360,6 +39749,11 @@ ${content}</tr>
         }
       }
     }
+    //
+    // `getWarnings()`
+    // Return any warnings discovered when buiding the index.
+    // (currently this does nothing)
+    //
     getWarnings() {
       return this.warnings;
     }
@@ -38590,19 +39984,19 @@ ${content}</tr>
     var _segmentsByWayId = {};
     var tree = {};
     function entityBBox(entity) {
-      var bbox = entity.extent(head).bbox();
-      bbox.id = entity.id;
-      _bboxes[entity.id] = bbox;
-      return bbox;
+      var bbox2 = entity.extent(head).bbox();
+      bbox2.id = entity.id;
+      _bboxes[entity.id] = bbox2;
+      return bbox2;
     }
     function segmentBBox(segment) {
       var extent = segment.extent(head);
       if (!extent)
         return null;
-      var bbox = extent.bbox();
-      bbox.segment = segment;
-      _segmentsBBoxes[segment.id] = bbox;
-      return bbox;
+      var bbox2 = extent.bbox();
+      bbox2.segment = segment;
+      _segmentsBBoxes[segment.id] = bbox2;
+      return bbox2;
     }
     function removeEntity(entity) {
       _rtree.remove(_bboxes[entity.id]);
@@ -38696,23 +40090,23 @@ ${content}</tr>
     }
     tree.intersects = function(extent, graph) {
       updateToGraph(graph);
-      return _rtree.search(extent.bbox()).map(function(bbox) {
-        return graph.entity(bbox.id);
+      return _rtree.search(extent.bbox()).map(function(bbox2) {
+        return graph.entity(bbox2.id);
       });
     };
     tree.waySegments = function(extent, graph) {
       updateToGraph(graph);
-      return _segmentsRTree.search(extent.bbox()).map(function(bbox) {
-        return bbox.segment;
+      return _segmentsRTree.search(extent.bbox()).map(function(bbox2) {
+        return bbox2.segment;
       });
     };
     return tree;
   }
 
   // modules/svg/icon.js
-  function svgIcon(name2, svgklass, useklass) {
+  function svgIcon(name, svgklass, useklass) {
     return function drawIcon(selection2) {
-      selection2.selectAll("svg.icon" + (svgklass ? "." + svgklass.split(" ")[0] : "")).data([0]).enter().append("svg").attr("class", "icon " + (svgklass || "")).append("use").attr("xlink:href", name2).attr("class", useklass);
+      selection2.selectAll("svg.icon" + (svgklass ? "." + svgklass.split(" ")[0] : "")).data([0]).enter().append("svg").attr("class", "icon " + (svgklass || "")).append("use").attr("xlink:href", name).attr("class", useklass);
     };
   }
 
@@ -38913,6 +40307,7 @@ ${content}</tr>
         select_default2(document).interrupt("history.perform");
         return _replace(arguments, 1);
       },
+      // Same as calling pop and then perform
       overwrite: function() {
         select_default2(document).interrupt("history.perform");
         return _overwrite(arguments, 1);
@@ -38929,6 +40324,7 @@ ${content}</tr>
         }
         return change(previous);
       },
+      // Back to the previous annotated state or _index = 0.
       undo: function() {
         select_default2(document).interrupt("history.perform");
         var previousStack = _stack[_index];
@@ -38941,6 +40337,7 @@ ${content}</tr>
         dispatch10.call("undone", this, _stack[_index], previousStack);
         return change(previous);
       },
+      // Forward to the next annotated state.
       redo: function() {
         select_default2(document).interrupt("history.perform");
         var previousStack = _stack[_index];
@@ -38984,6 +40381,8 @@ ${content}</tr>
           i2++;
         }
       },
+      // Returns the entities from the active graph with bounding boxes
+      // overlapping the given `extent`.
       intersects: function(extent) {
         return _tree.intersects(extent, _stack[_index].graph);
       },
@@ -39040,6 +40439,7 @@ ${content}</tr>
           return Array.from(s);
         }
       },
+      // save the current history state
       checkpoint: function(key) {
         _checkpoints[key] = {
           stack: _stack,
@@ -39047,6 +40447,7 @@ ${content}</tr>
         };
         return history;
       },
+      // restore history state to a given checkpoint or reset completely
       reset: function(key) {
         if (key !== void 0 && _checkpoints.hasOwnProperty(key)) {
           _stack = _checkpoints[key].stack;
@@ -39061,6 +40462,16 @@ ${content}</tr>
         dispatch10.call("change");
         return history;
       },
+      // `toIntroGraph()` is used to export the intro graph used by the walkthrough.
+      //
+      // To use it:
+      //  1. Start the walkthrough.
+      //  2. Get to a "free editing" tutorial step
+      //  3. Make your edits to the walkthrough map
+      //  4. In your browser dev console run:
+      //        `id.history().toIntroGraph()`
+      //  5. This outputs stringified JSON to the browser console
+      //  6. Copy it to `data/intro_graph.json` and prettify it in your code editor
       toIntroGraph: function() {
         var nextID = { n: 0, r: 0, w: 0 };
         var permIDs = {};
@@ -39175,6 +40586,7 @@ ${content}</tr>
           stack: s,
           nextIDs: osmEntity.id.next,
           index: _index,
+          // note the time the changes were saved
           timestamp: new Date().getTime()
         });
       },
@@ -39294,13 +40706,15 @@ ${content}</tr>
         lock.unlock();
       },
       save: function() {
-        if (lock.locked() && !_hasUnresolvedRestorableChanges) {
+        if (lock.locked() && // don't overwrite existing, unresolved changes
+        !_hasUnresolvedRestorableChanges) {
           const success = corePreferences(getKey("saved_history"), history.toJSON() || null);
           if (!success)
             dispatch10.call("storage_error");
         }
         return history;
       },
+      // delete the history version saved in localStorage
       clearSaved: function() {
         context.debouncedSave.cancel();
         if (lock.locked()) {
@@ -39318,6 +40732,7 @@ ${content}</tr>
       hasRestorableChanges: function() {
         return _hasUnresolvedRestorableChanges;
       },
+      // load history from a version stored in localStorage
       restore: function() {
         if (lock.locked()) {
           _hasUnresolvedRestorableChanges = false;
@@ -39355,7 +40770,7 @@ ${content}</tr>
 
   // modules/validations/almost_junction.js
   function validationAlmostJunction(context) {
-    const type3 = "almost_junction";
+    const type2 = "almost_junction";
     const EXTEND_TH_METERS = 5;
     const WELD_TH_METERS = 0.75;
     const CLOSE_NODE_TH = EXTEND_TH_METERS - WELD_TH_METERS;
@@ -39376,18 +40791,18 @@ ${content}</tr>
       let issues = [];
       extendableNodeInfos.forEach((extendableNodeInfo) => {
         issues.push(new validationIssue({
-          type: type3,
+          type: type2,
           subtype: "highway-highway",
           severity: "warning",
           message: function(context2) {
             const entity1 = context2.hasEntity(this.entityIds[0]);
             if (this.entityIds[0] === this.entityIds[2]) {
-              return entity1 ? _t.html("issues.almost_junction.self.message", {
+              return entity1 ? _t.append("issues.almost_junction.self.message", {
                 feature: utilDisplayLabel(entity1, context2.graph())
               }) : "";
             } else {
               const entity2 = context2.hasEntity(this.entityIds[2]);
-              return entity1 && entity2 ? _t.html("issues.almost_junction.message", {
+              return entity1 && entity2 ? _t.append("issues.almost_junction.message", {
                 feature: utilDisplayLabel(entity1, context2.graph()),
                 feature2: utilDisplayLabel(entity2, context2.graph())
               }) : "";
@@ -39413,7 +40828,7 @@ ${content}</tr>
       function makeFixes(context2) {
         let fixes = [new validationIssueFix({
           icon: "iD-icon-abutment",
-          title: _t.html("issues.fix.connect_features.title"),
+          title: _t.append("issues.fix.connect_features.title"),
           onClick: function(context3) {
             const annotation = _t("issues.fix.connect_almost_junction.annotation");
             const [, endNodeId, crossWayId] = this.issue.entityIds;
@@ -39424,7 +40839,10 @@ ${content}</tr>
             if (nearEndNodes.length > 0) {
               const collinear = findSmallJoinAngle(midNode, endNode, nearEndNodes);
               if (collinear) {
-                context3.perform(actionMergeNodes([collinear.id, endNode.id], collinear.loc), annotation);
+                context3.perform(
+                  actionMergeNodes([collinear.id, endNode.id], collinear.loc),
+                  annotation
+                );
                 return;
               }
             }
@@ -39433,9 +40851,15 @@ ${content}</tr>
             const edgeNodes = [context3.entity(targetEdge[0]), context3.entity(targetEdge[1])];
             const closestNodeInfo = geoSphericalClosestNode(edgeNodes, crossLoc);
             if (closestNodeInfo.distance < WELD_TH_METERS) {
-              context3.perform(actionMergeNodes([closestNodeInfo.node.id, endNode.id], closestNodeInfo.node.loc), annotation);
+              context3.perform(
+                actionMergeNodes([closestNodeInfo.node.id, endNode.id], closestNodeInfo.node.loc),
+                annotation
+              );
             } else {
-              context3.perform(actionAddMidpoint({ loc: crossLoc, edge: targetEdge }, endNode), annotation);
+              context3.perform(
+                actionAddMidpoint({ loc: crossLoc, edge: targetEdge }, endNode),
+                annotation
+              );
             }
           }
         })];
@@ -39443,12 +40867,15 @@ ${content}</tr>
         if (node && !node.hasInterestingTags()) {
           fixes.push(new validationIssueFix({
             icon: "maki-barrier",
-            title: _t.html("issues.fix.tag_as_disconnected.title"),
+            title: _t.append("issues.fix.tag_as_disconnected.title"),
             onClick: function(context3) {
               const nodeID = this.issue.entityIds[1];
               const tags = Object.assign({}, context3.entity(nodeID).tags);
               tags.noexit = "yes";
-              context3.perform(actionChangeTags(nodeID, tags), _t("issues.fix.tag_as_disconnected.annotation"));
+              context3.perform(
+                actionChangeTags(nodeID, tags),
+                _t("issues.fix.tag_as_disconnected.annotation")
+              );
             }
           }));
         }
@@ -39582,13 +41009,13 @@ ${content}</tr>
         return null;
       }
     };
-    validation.type = type3;
+    validation.type = type2;
     return validation;
   }
 
   // modules/validations/close_nodes.js
   function validationCloseNodes(context) {
-    var type3 = "close_nodes";
+    var type2 = "close_nodes";
     var pointThresholdMeters = 0.2;
     var validation = function(entity, graph) {
       if (entity.type === "node") {
@@ -39631,8 +41058,8 @@ ${content}</tr>
       function shouldCheckWay(way) {
         if (way.nodes.length <= 2 || way.isClosed() && way.nodes.length <= 4)
           return false;
-        var bbox = way.extent(graph).bbox();
-        var hypotenuseMeters = geoSphericalDistance([bbox.minX, bbox.minY], [bbox.maxX, bbox.maxY]);
+        var bbox2 = way.extent(graph).bbox();
+        var hypotenuseMeters = geoSphericalDistance([bbox2.minX, bbox2.minY], [bbox2.maxX, bbox2.maxY]);
         if (hypotenuseMeters < 1.5)
           return false;
         return true;
@@ -39709,6 +41136,10 @@ ${content}</tr>
           if (nearby.type !== "node" || nearby.geometry(graph) !== "point")
             continue;
           if (nearby.loc === node.loc || geoSphericalDistance(node.loc, nearby.loc) < pointThresholdMeters) {
+            if ("memorial:type" in node.tags && "memorial:type" in nearby.tags && node.tags["memorial:type"] === "stolperstein" && nearby.tags["memorial:type"] === "stolperstein")
+              continue;
+            if ("memorial" in node.tags && "memorial" in nearby.tags && node.tags.memorial === "stolperstein" && nearby.tags.memorial === "stolperstein")
+              continue;
             var zAxisKeys = { layer: true, level: true, "addr:housenumber": true, "addr:unit": true };
             var zAxisDifferentiates = false;
             for (var key in zAxisKeys) {
@@ -39722,12 +41153,12 @@ ${content}</tr>
             if (zAxisDifferentiates)
               continue;
             issues.push(new validationIssue({
-              type: type3,
+              type: type2,
               subtype: "detached",
               severity: "warning",
               message: function(context2) {
                 var entity2 = context2.hasEntity(this.entityIds[0]), entity22 = context2.hasEntity(this.entityIds[1]);
-                return entity2 && entity22 ? _t.html("issues.close_nodes.detached.message", {
+                return entity2 && entity22 ? _t.append("issues.close_nodes.detached.message", {
                   feature: utilDisplayLabel(entity2, context2.graph()),
                   feature2: utilDisplayLabel(entity22, context2.graph())
                 }) : "";
@@ -39738,11 +41169,11 @@ ${content}</tr>
                 return [
                   new validationIssueFix({
                     icon: "iD-operation-disconnect",
-                    title: _t.html("issues.fix.move_points_apart.title")
+                    title: _t.append("issues.fix.move_points_apart.title")
                   }),
                   new validationIssueFix({
                     icon: "iD-icon-layers",
-                    title: _t.html("issues.fix.use_different_layers_or_levels.title")
+                    title: _t.append("issues.fix.use_different_layers_or_levels.title")
                   })
                 ];
               }
@@ -39774,12 +41205,12 @@ ${content}</tr>
             return null;
         }
         return new validationIssue({
-          type: type3,
+          type: type2,
           subtype: "vertices",
           severity: "warning",
           message: function(context2) {
             var entity2 = context2.hasEntity(this.entityIds[0]);
-            return entity2 ? _t.html("issues.close_nodes.message", { way: utilDisplayLabel(entity2, context2.graph()) }) : "";
+            return entity2 ? _t.append("issues.close_nodes.message", { way: utilDisplayLabel(entity2, context2.graph()) }) : "";
           },
           reference: showReference,
           entityIds: [way.id, node1.id, node2.id],
@@ -39788,7 +41219,7 @@ ${content}</tr>
             return [
               new validationIssueFix({
                 icon: "iD-icon-plus",
-                title: _t.html("issues.fix.merge_points.title"),
+                title: _t.append("issues.fix.merge_points.title"),
                 onClick: function(context2) {
                   var entityIds = this.issue.entityIds;
                   var action = actionMergeNodes([entityIds[1], entityIds[2]]);
@@ -39797,7 +41228,7 @@ ${content}</tr>
               }),
               new validationIssueFix({
                 icon: "iD-operation-disconnect",
-                title: _t.html("issues.fix.move_points_apart.title")
+                title: _t.append("issues.fix.move_points_apart.title")
               })
             ];
           }
@@ -39808,13 +41239,13 @@ ${content}</tr>
         }
       }
     };
-    validation.type = type3;
+    validation.type = type2;
     return validation;
   }
 
   // modules/validations/crossing_ways.js
   function validationCrossingWays(context) {
-    var type3 = "crossing_ways";
+    var type2 = "crossing_ways";
     function getFeatureWithFeatureTypeTagsForWay(way, graph) {
       if (getFeatureType(way, graph) === null) {
         var parentRels = graph.parentRelations(way);
@@ -39929,7 +41360,7 @@ ${content}</tr>
               return {};
             }
             var pathFeature = entity1IsPath ? entity1 : entity2;
-            if (["marked", "unmarked"].indexOf(pathFeature.tags.crossing) !== -1) {
+            if (["marked", "unmarked", "traffic_signals", "uncontrolled"].indexOf(pathFeature.tags.crossing) !== -1) {
               return bothLines ? { highway: "crossing", crossing: pathFeature.tags.crossing } : {};
             }
             return bothLines ? { highway: "crossing" } : {};
@@ -40031,8 +41462,8 @@ ${content}</tr>
             continue;
           segment1 = [n1.loc, n2.loc];
           segment2 = [nA.loc, nB.loc];
-          var point = geoLineIntersection(segment1, segment2);
-          if (point) {
+          var point2 = geoLineIntersection(segment1, segment2);
+          if (point2) {
             edgeCrossInfos.push({
               wayInfos: [
                 {
@@ -40046,7 +41477,7 @@ ${content}</tr>
                   edge: [nA.id, nB.id]
                 }
               ],
-              crossPoint: point
+              crossPoint: point2
             });
             if (oneOnly) {
               checkedSingleCrossingWays[way2.id] = true;
@@ -40065,7 +41496,8 @@ ${content}</tr>
         return [entity];
       } else if (entity.type === "relation") {
         return entity.members.reduce(function(array2, member) {
-          if (member.type === "way" && (!member.role || member.role === "outer" || member.role === "inner")) {
+          if (member.type === "way" && // only look at geometry ways
+          (!member.role || member.role === "outer" || member.role === "inner")) {
             var entity2 = graph.hasEntity(member.id);
             if (entity2 && array2.indexOf(entity2) === -1) {
               array2.push(entity2);
@@ -40127,13 +41559,13 @@ ${content}</tr>
       }
       var uniqueID = crossing.crossPoint[0].toFixed(4) + "," + crossing.crossPoint[1].toFixed(4);
       return new validationIssue({
-        type: type3,
+        type: type2,
         subtype,
         severity: "warning",
         message: function(context2) {
           var graph2 = context2.graph();
           var entity1 = graph2.hasEntity(this.entityIds[0]), entity2 = graph2.hasEntity(this.entityIds[1]);
-          return entity1 && entity2 ? _t.html("issues.crossing_ways.message", {
+          return entity1 && entity2 ? _t.append("issues.crossing_ways.message", {
             feature: utilDisplayLabel(entity1, graph2),
             feature2: utilDisplayLabel(entity2, graph2)
           }) : "";
@@ -40163,7 +41595,7 @@ ${content}</tr>
           if (isCrossingIndoors) {
             fixes.push(new validationIssueFix({
               icon: "iD-icon-layers",
-              title: _t.html("issues.fix.use_different_levels.title")
+              title: _t.append("issues.fix.use_different_levels.title")
             }));
           } else if (isCrossingTunnels || isCrossingBridges || featureType1 === "building" || featureType2 === "building") {
             fixes.push(makeChangeLayerFix("higher"));
@@ -40179,7 +41611,7 @@ ${content}</tr>
           }
           fixes.push(new validationIssueFix({
             icon: "iD-operation-move",
-            title: _t.html("issues.fix.reposition_features.title")
+            title: _t.append("issues.fix.reposition_features.title")
           }));
           return fixes;
         }
@@ -40191,7 +41623,7 @@ ${content}</tr>
     function makeAddBridgeOrTunnelFix(fixTitleID, iconName, bridgeOrTunnel) {
       return new validationIssueFix({
         icon: iconName,
-        title: _t.html("issues.fix." + fixTitleID + ".title"),
+        title: _t.append("issues.fix." + fixTitleID + ".title"),
         onClick: function(context2) {
           var mode = context2.mode();
           if (!mode || mode.id !== "select")
@@ -40218,7 +41650,7 @@ ${content}</tr>
           var action = function actionAddStructure(graph) {
             var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
             var crossedWay = graph.hasEntity(crossedWayID);
-            var structLengthMeters = crossedWay && crossedWay.tags.width && parseFloat(crossedWay.tags.width);
+            var structLengthMeters = crossedWay && isFinite(crossedWay.tags.width) && Number(crossedWay.tags.width);
             if (!structLengthMeters) {
               structLengthMeters = crossedWay && crossedWay.impliedLineWidthMeters();
             }
@@ -40342,37 +41774,40 @@ ${content}</tr>
       }
       return new validationIssueFix({
         icon: "iD-icon-crossing",
-        title: _t.html("issues.fix." + fixTitleID + ".title"),
+        title: _t.append("issues.fix." + fixTitleID + ".title"),
         onClick: function(context2) {
           var loc = this.issue.loc;
           var connectionTags2 = this.issue.data.connectionTags;
           var edges = this.issue.data.edges;
-          context2.perform(function actionConnectCrossingWays(graph) {
-            var node = osmNode({ loc, tags: connectionTags2 });
-            graph = graph.replace(node);
-            var nodesToMerge = [node.id];
-            var mergeThresholdInMeters = 0.75;
-            edges.forEach(function(edge) {
-              var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
-              var nearby = geoSphericalClosestNode(edgeNodes, loc);
-              if ((!nearby.node.hasInterestingTags() || nearby.node.isCrossing()) && nearby.distance < mergeThresholdInMeters) {
-                nodesToMerge.push(nearby.node.id);
-              } else {
-                graph = actionAddMidpoint({ loc, edge }, node)(graph);
+          context2.perform(
+            function actionConnectCrossingWays(graph) {
+              var node = osmNode({ loc, tags: connectionTags2 });
+              graph = graph.replace(node);
+              var nodesToMerge = [node.id];
+              var mergeThresholdInMeters = 0.75;
+              edges.forEach(function(edge) {
+                var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
+                var nearby = geoSphericalClosestNode(edgeNodes, loc);
+                if ((!nearby.node.hasInterestingTags() || nearby.node.isCrossing()) && nearby.distance < mergeThresholdInMeters) {
+                  nodesToMerge.push(nearby.node.id);
+                } else {
+                  graph = actionAddMidpoint({ loc, edge }, node)(graph);
+                }
+              });
+              if (nodesToMerge.length > 1) {
+                graph = actionMergeNodes(nodesToMerge, loc)(graph);
               }
-            });
-            if (nodesToMerge.length > 1) {
-              graph = actionMergeNodes(nodesToMerge, loc)(graph);
-            }
-            return graph;
-          }, _t("issues.fix.connect_crossing_features.annotation"));
+              return graph;
+            },
+            _t("issues.fix.connect_crossing_features.annotation")
+          );
         }
       });
     }
     function makeChangeLayerFix(higherOrLower) {
       return new validationIssueFix({
         icon: "iD-icon-" + (higherOrLower === "higher" ? "up" : "down"),
-        title: _t.html("issues.fix.tag_this_as_" + higherOrLower + ".title"),
+        title: _t.append("issues.fix.tag_this_as_" + higherOrLower + ".title"),
         onClick: function(context2) {
           var mode = context2.mode();
           if (!mode || mode.id !== "select")
@@ -40404,11 +41839,14 @@ ${content}</tr>
             }
           }
           tags.layer = layer.toString();
-          context2.perform(actionChangeTags(entity.id, tags), _t("operations.change_tags.annotation"));
+          context2.perform(
+            actionChangeTags(entity.id, tags),
+            _t("operations.change_tags.annotation")
+          );
         }
       });
     }
-    validation.type = type3;
+    validation.type = type2;
     return validation;
   }
 
@@ -40437,10 +41875,13 @@ ${content}</tr>
     }
     function removeDrawNode() {
       context.pauseChangeDispatch();
-      context.replace(function actionDeleteDrawNode(graph) {
-        var way = graph.entity(wayID);
-        return graph.replace(way.removeNode(_drawNode.id)).remove(_drawNode);
-      }, _annotation);
+      context.replace(
+        function actionDeleteDrawNode(graph) {
+          var way = graph.entity(wayID);
+          return graph.replace(way.removeNode(_drawNode.id)).remove(_drawNode);
+        },
+        _annotation
+      );
       _drawNode = void 0;
       context.resumeChangeDispatch();
     }
@@ -40480,7 +41921,10 @@ ${content}</tr>
       }
       context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
       _drawNode = context.entity(_drawNode.id);
-      checkGeometry(true);
+      checkGeometry(
+        true
+        /* includeDrawNode */
+      );
     }
     function checkGeometry(includeDrawNode) {
       var nopeDisabled = context.surface().classed("nope-disabled");
@@ -40549,7 +41993,9 @@ ${content}</tr>
         _headNodeID = _origWay.nodes[_origWay.nodes.length - 1];
       }
       _wayGeometry = _origWay.geometry(context.graph());
-      _annotation = _t((_origWay.nodes.length === (_origWay.isClosed() ? 2 : 1) ? "operations.start.annotation." : "operations.continue.annotation.") + _wayGeometry);
+      _annotation = _t(
+        (_origWay.nodes.length === (_origWay.isClosed() ? 2 : 1) ? "operations.start.annotation." : "operations.continue.annotation.") + _wayGeometry
+      );
       _pointerHasMoved = false;
       context.pauseChangeDispatch();
       context.perform(actionNoop(), _annotation);
@@ -40591,7 +42037,10 @@ ${content}</tr>
       } else {
         createDrawNode(loc);
       }
-      checkGeometry(true);
+      checkGeometry(
+        true
+        /* includeDrawNode */
+      );
       if (d && d.properties && d.properties.nope || context.surface().classed("nope")) {
         if (!_pointerHasMoved) {
           removeDrawNode();
@@ -40611,19 +42060,26 @@ ${content}</tr>
     };
     drawWay.addWay = function(loc, edge, d) {
       attemptAdd(d, loc, function() {
-        context.replace(actionAddMidpoint({ loc, edge }, _drawNode), _annotation);
+        context.replace(
+          actionAddMidpoint({ loc, edge }, _drawNode),
+          _annotation
+        );
       });
     };
     drawWay.addNode = function(node, d) {
-      if (node.id === _headNodeID || _origWay.isClosed() && node.id === _origWay.first()) {
+      if (node.id === _headNodeID || // or the first node when drawing an area
+      _origWay.isClosed() && node.id === _origWay.first()) {
         drawWay.finish();
         return;
       }
       attemptAdd(d, node.loc, function() {
-        context.replace(function actionReplaceDrawNode(graph) {
-          graph = graph.replace(graph.entity(wayID).removeNode(_drawNode.id)).remove(_drawNode);
-          return graph.replace(graph.entity(wayID).addNode(node.id, _nodeIndex));
-        }, _annotation);
+        context.replace(
+          function actionReplaceDrawNode(graph) {
+            graph = graph.replace(graph.entity(wayID).removeNode(_drawNode.id)).remove(_drawNode);
+            return graph.replace(graph.entity(wayID).addNode(node.id, _nodeIndex));
+          },
+          _annotation
+        );
       });
     };
     function getFeatureType(ways) {
@@ -40641,18 +42097,18 @@ ${content}</tr>
         const [secondLastNodeId, lastNodeId] = _origWay.nodes.slice(isDrawingArea ? -3 : -2);
         const historyGraph = context.history().graph();
         if (!lastNodeId || !secondLastNodeId || !historyGraph.hasEntity(lastNodeId) || !historyGraph.hasEntity(secondLastNodeId)) {
-          context.ui().flash.duration(4e3).iconName("#iD-icon-no").label(_t.html("operations.follow.error.needs_more_initial_nodes"))();
+          context.ui().flash.duration(4e3).iconName("#iD-icon-no").label(_t.append("operations.follow.error.needs_more_initial_nodes"))();
           return;
         }
         const lastNodesParents = historyGraph.parentWays(historyGraph.entity(lastNodeId)).filter((w) => w.id !== wayID);
         const secondLastNodesParents = historyGraph.parentWays(historyGraph.entity(secondLastNodeId)).filter((w) => w.id !== wayID);
         const featureType = getFeatureType(lastNodesParents);
         if (lastNodesParents.length !== 1 || secondLastNodesParents.length === 0) {
-          context.ui().flash.duration(4e3).iconName("#iD-icon-no").label(_t.html(`operations.follow.error.intersection_of_multiple_ways.${featureType}`))();
+          context.ui().flash.duration(4e3).iconName("#iD-icon-no").label(_t.append(`operations.follow.error.intersection_of_multiple_ways.${featureType}`))();
           return;
         }
         if (!secondLastNodesParents.some((n2) => n2.id === lastNodesParents[0].id)) {
-          context.ui().flash.duration(4e3).iconName("#iD-icon-no").label(_t.html(`operations.follow.error.intersection_of_different_ways.${featureType}`))();
+          context.ui().flash.duration(4e3).iconName("#iD-icon-no").label(_t.append(`operations.follow.error.intersection_of_different_ways.${featureType}`))();
           return;
         }
         const way = lastNodesParents[0];
@@ -40669,13 +42125,16 @@ ${content}</tr>
           properties: { target: true, entity: nextNode }
         });
       } catch (ex) {
-        context.ui().flash.duration(4e3).iconName("#iD-icon-no").label(_t.html("operations.follow.error.unknown"))();
+        context.ui().flash.duration(4e3).iconName("#iD-icon-no").label(_t.append("operations.follow.error.unknown"))();
       }
     }
     keybinding.on(_t("operations.follow.key"), followMode);
     select_default2(document).call(keybinding);
     drawWay.finish = function() {
-      checkGeometry(false);
+      checkGeometry(
+        false
+        /* includeDrawNode */
+      );
       if (context.surface().classed("nope")) {
         dispatch10.call("rejectedSelfIntersection", this);
         return;
@@ -40726,7 +42185,7 @@ ${content}</tr>
       id: "draw-line"
     };
     var behavior = behaviorDrawWay(context, wayID, mode, startGraph).on("rejectedSelfIntersection.modeDrawLine", function() {
-      context.ui().flash.iconName("#iD-icon-no").label(_t.html("self_intersection.error.lines"))();
+      context.ui().flash.iconName("#iD-icon-no").label(_t.append("self_intersection.error.lines"))();
     });
     mode.wayID = wayID;
     mode.isContinuing = continuing;
@@ -40748,7 +42207,7 @@ ${content}</tr>
 
   // modules/validations/disconnected_way.js
   function validationDisconnectedWay() {
-    var type3 = "disconnected_way";
+    var type2 = "disconnected_way";
     function isTaggedAsHighway(entity) {
       return osmRoutableHighwayTagValues[entity.tags.highway];
     }
@@ -40757,13 +42216,13 @@ ${content}</tr>
       if (!routingIslandWays)
         return [];
       return [new validationIssue({
-        type: type3,
+        type: type2,
         subtype: "highway",
         severity: "warning",
         message: function(context) {
           var entity2 = this.entityIds.length && context.hasEntity(this.entityIds[0]);
           var label = entity2 && utilDisplayLabel(entity2, context.graph());
-          return _t.html("issues.disconnected_way.routable.message", { count: this.entityIds.length, highway: label });
+          return _t.append("issues.disconnected_way.routable.message", { count: this.entityIds.length, highway: label });
         },
         reference: showReference,
         entityIds: Array.from(routingIslandWays).map(function(way) {
@@ -40786,12 +42245,12 @@ ${content}</tr>
           }
           if (!fixes.length) {
             fixes.push(new validationIssueFix({
-              title: _t.html("issues.fix.connect_feature.title")
+              title: _t.append("issues.fix.connect_feature.title")
             }));
           }
           fixes.push(new validationIssueFix({
             icon: "iD-operation-delete",
-            title: _t.html("issues.fix.delete_feature.title"),
+            title: _t.append("issues.fix.delete_feature.title"),
             entityIds: [singleEntity.id],
             onClick: function(context2) {
               var id2 = this.issue.entityIds[0];
@@ -40803,7 +42262,7 @@ ${content}</tr>
           }));
         } else {
           fixes.push(new validationIssueFix({
-            title: _t.html("issues.fix.connect_features.title")
+            title: _t.append("issues.fix.connect_features.title")
           }));
         }
         return fixes;
@@ -40816,7 +42275,8 @@ ${content}</tr>
         var waysToCheck = [];
         function queueParentWays(node) {
           graph.parentWays(node).forEach(function(parentWay) {
-            if (!routingIsland.has(parentWay) && isRoutableWay(parentWay, false)) {
+            if (!routingIsland.has(parentWay) && // only check each feature once
+            isRoutableWay(parentWay, false)) {
               routingIsland.add(parentWay);
               waysToCheck.push(parentWay);
             }
@@ -40880,7 +42340,7 @@ ${content}</tr>
         var useLeftContinue = whichEnd === "start" && textDirection === "ltr" || whichEnd === "end" && textDirection === "rtl";
         return new validationIssueFix({
           icon: "iD-operation-continue" + (useLeftContinue ? "-left" : ""),
-          title: _t.html("issues.fix.continue_from_" + whichEnd + ".title"),
+          title: _t.append("issues.fix.continue_from_" + whichEnd + ".title"),
           entityIds: [vertexID],
           onClick: function(context) {
             var wayId = this.issue.entityIds[0];
@@ -40893,18 +42353,20 @@ ${content}</tr>
             if (!context.editable() || !map2.trimmedExtent().contains(vertex2.loc)) {
               map2.zoomToEase(vertex2);
             }
-            context.enter(modeDrawLine(context, wayId, context.graph(), "line", way.affix(vertexId), true));
+            context.enter(
+              modeDrawLine(context, wayId, context.graph(), "line", way.affix(vertexId), true)
+            );
           }
         });
       }
     };
-    validation.type = type3;
+    validation.type = type2;
     return validation;
   }
 
   // modules/validations/invalid_format.js
   function validationFormatting() {
-    var type3 = "invalid_format";
+    var type2 = "invalid_format";
     var validation = function(entity) {
       var issues = [];
       function isValidEmail(email) {
@@ -40922,12 +42384,15 @@ ${content}</tr>
         });
         if (emails.length) {
           issues.push(new validationIssue({
-            type: type3,
+            type: type2,
             subtype: "email",
             severity: "warning",
             message: function(context) {
               var entity2 = context.hasEntity(this.entityIds[0]);
-              return entity2 ? _t.html("issues.invalid_format.email.message" + this.data, { feature: utilDisplayLabel(entity2, context.graph()), email: emails.join(", ") }) : "";
+              return entity2 ? _t.append(
+                "issues.invalid_format.email.message" + this.data,
+                { feature: utilDisplayLabel(entity2, context.graph()), email: emails.join(", ") }
+              ) : "";
             },
             reference: showReferenceEmail,
             entityIds: [entity.id],
@@ -40938,13 +42403,13 @@ ${content}</tr>
       }
       return issues;
     };
-    validation.type = type3;
+    validation.type = type2;
     return validation;
   }
 
   // modules/validations/help_request.js
   function validationHelpRequest(context) {
-    var type3 = "help_request";
+    var type2 = "help_request";
     var validation = function checkFixmeTag(entity) {
       if (!entity.tags.fixme)
         return [];
@@ -40956,19 +42421,24 @@ ${content}</tr>
           return [];
       }
       return [new validationIssue({
-        type: type3,
+        type: type2,
         subtype: "fixme_tag",
         severity: "warning",
         message: function(context2) {
           var entity2 = context2.hasEntity(this.entityIds[0]);
-          return entity2 ? _t.html("issues.fixme_tag.message", {
-            feature: utilDisplayLabel(entity2, context2.graph(), true)
+          return entity2 ? _t.append("issues.fixme_tag.message", {
+            feature: utilDisplayLabel(
+              entity2,
+              context2.graph(),
+              true
+              /* verbose */
+            )
           }) : "";
         },
         dynamicFixes: function() {
           return [
             new validationIssueFix({
-              title: _t.html("issues.fix.address_the_concern.title")
+              title: _t.append("issues.fix.address_the_concern.title")
             })
           ];
         },
@@ -40979,13 +42449,13 @@ ${content}</tr>
         selection2.selectAll(".issue-reference").data([0]).enter().append("div").attr("class", "issue-reference").call(_t.append("issues.fixme_tag.reference"));
       }
     };
-    validation.type = type3;
+    validation.type = type2;
     return validation;
   }
 
   // modules/validations/impossible_oneway.js
   function validationImpossibleOneway() {
-    var type3 = "impossible_oneway";
+    var type2 = "impossible_oneway";
     var validation = function checkImpossibleOneway(entity, graph) {
       if (entity.type !== "way" || entity.geometry(graph) !== "line")
         return [];
@@ -41111,12 +42581,12 @@ ${content}</tr>
           referenceID += placement;
         }
         return [new validationIssue({
-          type: type3,
+          type: type2,
           subtype: wayType,
           severity: "warning",
           message: function(context) {
             var entity2 = context.hasEntity(this.entityIds[0]);
-            return entity2 ? _t.html("issues.impossible_oneway." + messageID + ".message", {
+            return entity2 ? _t.append("issues.impossible_oneway." + messageID + ".message", {
               feature: utilDisplayLabel(entity2, context.graph())
             }) : "";
           },
@@ -41127,7 +42597,7 @@ ${content}</tr>
             if (attachedOneways.length) {
               fixes.push(new validationIssueFix({
                 icon: "iD-operation-reverse",
-                title: _t.html("issues.fix.reverse_feature.title"),
+                title: _t.append("issues.fix.reverse_feature.title"),
                 entityIds: [way.id],
                 onClick: function(context) {
                   var id2 = this.issue.entityIds[0];
@@ -41140,7 +42610,7 @@ ${content}</tr>
               var useLeftContinue = isFirst && textDirection === "ltr" || !isFirst && textDirection === "rtl";
               fixes.push(new validationIssueFix({
                 icon: "iD-operation-continue" + (useLeftContinue ? "-left" : ""),
-                title: _t.html("issues.fix.continue_from_" + (isFirst ? "start" : "end") + ".title"),
+                title: _t.append("issues.fix.continue_from_" + (isFirst ? "start" : "end") + ".title"),
                 onClick: function(context) {
                   var entityID = this.issue.entityIds[0];
                   var vertexID = this.issue.entityIds[1];
@@ -41166,15 +42636,17 @@ ${content}</tr>
       if (!context.editable() || !map2.trimmedExtent().contains(vertex.loc)) {
         map2.zoomToEase(vertex);
       }
-      context.enter(modeDrawLine(context, way.id, context.graph(), "line", way.affix(vertex.id), true));
+      context.enter(
+        modeDrawLine(context, way.id, context.graph(), "line", way.affix(vertex.id), true)
+      );
     }
-    validation.type = type3;
+    validation.type = type2;
     return validation;
   }
 
   // modules/validations/incompatible_source.js
   function validationIncompatibleSource() {
-    const type3 = "incompatible_source";
+    const type2 = "incompatible_source";
     const incompatibleRules = [
       {
         id: "amap",
@@ -41206,12 +42678,17 @@ ${content}</tr>
         if (!matchRule)
           return null;
         return new validationIssue({
-          type: type3,
+          type: type2,
           severity: "warning",
           message: (context) => {
             const entity2 = context.hasEntity(entityID);
-            return entity2 ? _t.html("issues.incompatible_source.feature.message", {
-              feature: utilDisplayLabel(entity2, context.graph(), true),
+            return entity2 ? _t.append("issues.incompatible_source.feature.message", {
+              feature: utilDisplayLabel(
+                entity2,
+                context.graph(),
+                true
+                /* verbose */
+              ),
               value: source
             }) : "";
           },
@@ -41220,7 +42697,7 @@ ${content}</tr>
           hash: source,
           dynamicFixes: () => {
             return [
-              new validationIssueFix({ title: _t.html("issues.fix.remove_proprietary_data.title") })
+              new validationIssueFix({ title: _t.append("issues.fix.remove_proprietary_data.title") })
             ];
           }
         });
@@ -41231,13 +42708,13 @@ ${content}</tr>
         };
       }
     };
-    validation.type = type3;
+    validation.type = type2;
     return validation;
   }
 
   // modules/validations/maprules.js
   function validationMaprules() {
-    var type3 = "maprules";
+    var type2 = "maprules";
     var validation = function checkMaprules(entity, graph) {
       if (!services.maprules)
         return [];
@@ -41249,14 +42726,14 @@ ${content}</tr>
       }
       return issues;
     };
-    validation.type = type3;
+    validation.type = type2;
     return validation;
   }
 
   // modules/validations/mismatched_geometry.js
   var import_fast_deep_equal4 = __toESM(require_fast_deep_equal());
   function validationMismatchedGeometry() {
-    var type3 = "mismatched_geometry";
+    var type2 = "mismatched_geometry";
     function tagSuggestingLineIsArea(entity) {
       if (entity.type !== "way" || entity.isClosed())
         return null;
@@ -41283,7 +42760,10 @@ ${content}</tr>
         if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
           return function(context) {
             var way2 = context.entity(this.issue.entityIds[0]);
-            context.perform(actionMergeNodes([way2.nodes[0], way2.nodes[way2.nodes.length - 1]], nodes[0].loc), _t("issues.fix.connect_endpoints.annotation"));
+            context.perform(
+              actionMergeNodes([way2.nodes[0], way2.nodes[way2.nodes.length - 1]], nodes[0].loc),
+              _t("issues.fix.connect_endpoints.annotation")
+            );
           };
         }
       }
@@ -41295,7 +42775,10 @@ ${content}</tr>
           var way2 = context.entity(wayId);
           var nodeId = way2.nodes[0];
           var index = way2.nodes.length;
-          context.perform(actionAddVertex(wayId, nodeId, index), _t("issues.fix.connect_endpoints.annotation"));
+          context.perform(
+            actionAddVertex(wayId, nodeId, index),
+            _t("issues.fix.connect_endpoints.annotation")
+          );
         };
       }
     }
@@ -41303,14 +42786,31 @@ ${content}</tr>
       var tagSuggestingArea = tagSuggestingLineIsArea(entity);
       if (!tagSuggestingArea)
         return null;
+      var validAsLine = false;
+      var presetAsLine = _mainPresetIndex.matchTags(entity.tags, "line");
+      if (presetAsLine) {
+        validAsLine = true;
+        var key = Object.keys(tagSuggestingArea)[0];
+        if (presetAsLine.tags[key] && presetAsLine.tags[key] === "*") {
+          validAsLine = false;
+        }
+        if (Object.keys(presetAsLine.tags).length === 0) {
+          validAsLine = false;
+        }
+      }
       return new validationIssue({
-        type: type3,
+        type: type2,
         subtype: "area_as_line",
         severity: "warning",
         message: function(context) {
           var entity2 = context.hasEntity(this.entityIds[0]);
-          return entity2 ? _t.html("issues.tag_suggests_area.message", {
-            feature: utilDisplayLabel(entity2, "area", true),
+          return entity2 ? _t.append("issues.tag_suggests_area.message", {
+            feature: utilDisplayLabel(
+              entity2,
+              "area",
+              true
+              /* verbose */
+            ),
             tag: utilTagText({ tags: tagSuggestingArea })
           }) : "";
         },
@@ -41321,21 +42821,26 @@ ${content}</tr>
           var fixes = [];
           var entity2 = context.entity(this.entityIds[0]);
           var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity2, context.graph());
-          fixes.push(new validationIssueFix({
-            title: _t.html("issues.fix.connect_endpoints.title"),
-            onClick: connectEndsOnClick
-          }));
+          if (!validAsLine) {
+            fixes.push(new validationIssueFix({
+              title: _t.append("issues.fix.connect_endpoints.title"),
+              onClick: connectEndsOnClick
+            }));
+          }
           fixes.push(new validationIssueFix({
             icon: "iD-operation-delete",
-            title: _t.html("issues.fix.remove_tag.title"),
+            title: _t.append("issues.fix.remove_tag.title"),
             onClick: function(context2) {
               var entityId = this.issue.entityIds[0];
               var entity3 = context2.entity(entityId);
               var tags = Object.assign({}, entity3.tags);
-              for (var key in tagSuggestingArea) {
-                delete tags[key];
+              for (var key2 in tagSuggestingArea) {
+                delete tags[key2];
               }
-              context2.perform(actionChangeTags(entityId, tags), _t("issues.fix.remove_tag.annotation"));
+              context2.perform(
+                actionChangeTags(entityId, tags),
+                _t("issues.fix.remove_tag.annotation")
+              );
             }
           }));
           return fixes;
@@ -41356,13 +42861,18 @@ ${content}</tr>
       var allowedGeometries = osmNodeGeometriesForTags(entity.tags);
       if (geometry === "point" && !allowedGeometries.point && allowedGeometries.vertex) {
         return new validationIssue({
-          type: type3,
+          type: type2,
           subtype: "vertex_as_point",
           severity: "warning",
           message: function(context) {
             var entity2 = context.hasEntity(this.entityIds[0]);
-            return entity2 ? _t.html("issues.vertex_as_point.message", {
-              feature: utilDisplayLabel(entity2, "vertex", true)
+            return entity2 ? _t.append("issues.vertex_as_point.message", {
+              feature: utilDisplayLabel(
+                entity2,
+                "vertex",
+                true
+                /* verbose */
+              )
             }) : "";
           },
           reference: function showReference(selection2) {
@@ -41372,13 +42882,18 @@ ${content}</tr>
         });
       } else if (geometry === "vertex" && !allowedGeometries.vertex && allowedGeometries.point) {
         return new validationIssue({
-          type: type3,
+          type: type2,
           subtype: "point_as_vertex",
           severity: "warning",
           message: function(context) {
             var entity2 = context.hasEntity(this.entityIds[0]);
-            return entity2 ? _t.html("issues.point_as_vertex.message", {
-              feature: utilDisplayLabel(entity2, "point", true)
+            return entity2 ? _t.append("issues.point_as_vertex.message", {
+              feature: utilDisplayLabel(
+                entity2,
+                "point",
+                true
+                /* verbose */
+              )
             }) : "";
           },
           reference: function showReference(selection2) {
@@ -41404,7 +42919,8 @@ ${content}</tr>
       var asSource = _mainPresetIndex.match(entity, graph);
       var targetGeom = targetGeoms.find((nodeGeom) => {
         var asTarget = _mainPresetIndex.matchTags(entity.tags, nodeGeom);
-        if (!asSource || !asTarget || asSource === asTarget || (0, import_fast_deep_equal4.default)(asSource.tags, asTarget.tags))
+        if (!asSource || !asTarget || asSource === asTarget || // sometimes there are two presets with the same tags for different geometries
+        (0, import_fast_deep_equal4.default)(asSource.tags, asTarget.tags))
           return false;
         if (asTarget.isFallback())
           return false;
@@ -41430,13 +42946,18 @@ ${content}</tr>
         dynamicFixes = lineToAreaDynamicFixes;
       }
       return new validationIssue({
-        type: type3,
+        type: type2,
         subtype,
         severity: "warning",
         message: function(context) {
           var entity2 = context.hasEntity(this.entityIds[0]);
-          return entity2 ? _t.html("issues." + referenceId + ".message", {
-            feature: utilDisplayLabel(entity2, targetGeom, true)
+          return entity2 ? _t.append("issues." + referenceId + ".message", {
+            feature: utilDisplayLabel(
+              entity2,
+              targetGeom,
+              true
+              /* verbose */
+            )
           }) : "";
         },
         reference: function showReference(selection2) {
@@ -41460,13 +42981,16 @@ ${content}</tr>
           if (tags2.area) {
             delete tags2.area;
           }
-          context2.perform(actionChangeTags(entityId2, tags2), _t("issues.fix.convert_to_line.annotation"));
+          context2.perform(
+            actionChangeTags(entityId2, tags2),
+            _t("issues.fix.convert_to_line.annotation")
+          );
         };
       }
       return [
         new validationIssueFix({
           icon: "iD-icon-line",
-          title: _t.html("issues.fix.convert_to_line.title"),
+          title: _t.append("issues.fix.convert_to_line.title"),
           onClick: convertOnClick
         })
       ];
@@ -41478,20 +43002,24 @@ ${content}</tr>
         extractOnClick = function(context2) {
           var entityId2 = this.issue.entityIds[0];
           var action = actionExtract(entityId2, context2.projection);
-          context2.perform(action, _t("operations.extract.annotation", { n: 1 }));
+          context2.perform(
+            action,
+            _t("operations.extract.annotation", { n: 1 })
+          );
           context2.enter(modeSelect(context2, [action.getExtractedNodeID()]));
         };
       }
       return [
         new validationIssueFix({
           icon: "iD-operation-extract",
-          title: _t.html("issues.fix.extract_point.title"),
+          title: _t.append("issues.fix.extract_point.title"),
           onClick: extractOnClick
         })
       ];
     }
     function unclosedMultipolygonPartIssues(entity, graph) {
-      if (entity.type !== "relation" || !entity.isMultipolygon() || entity.isDegenerate() || !entity.isComplete(graph))
+      if (entity.type !== "relation" || !entity.isMultipolygon() || entity.isDegenerate() || // cannot determine issues for incompletely-downloaded relations
+      !entity.isComplete(graph))
         return [];
       var sequences = osmJoinWays(entity.members, graph);
       var issues = [];
@@ -41504,13 +43032,18 @@ ${content}</tr>
         if (firstNode === lastNode)
           continue;
         var issue = new validationIssue({
-          type: type3,
+          type: type2,
           subtype: "unclosed_multipolygon_part",
           severity: "warning",
           message: function(context) {
             var entity2 = context.hasEntity(this.entityIds[0]);
-            return entity2 ? _t.html("issues.unclosed_multipolygon_part.message", {
-              feature: utilDisplayLabel(entity2, context.graph(), true)
+            return entity2 ? _t.append("issues.unclosed_multipolygon_part.message", {
+              feature: utilDisplayLabel(
+                entity2,
+                context.graph(),
+                true
+                /* verbose */
+              )
             }) : "";
           },
           reference: showReference,
@@ -41539,13 +43072,13 @@ ${content}</tr>
         return [mismatch];
       return unclosedMultipolygonPartIssues(entity, graph);
     };
-    validation.type = type3;
+    validation.type = type2;
     return validation;
   }
 
   // modules/validations/missing_role.js
   function validationMissingRole() {
-    var type3 = "missing_role";
+    var type2 = "missing_role";
     var validation = function checkMissingRole(entity, graph) {
       var issues = [];
       if (entity.type === "way") {
@@ -41572,11 +43105,11 @@ ${content}</tr>
     }
     function makeIssue(way, relation, member) {
       return new validationIssue({
-        type: type3,
+        type: type2,
         severity: "warning",
         message: function(context) {
           var member2 = context.hasEntity(this.entityIds[1]), relation2 = context.hasEntity(this.entityIds[0]);
-          return member2 && relation2 ? _t.html("issues.missing_role.message", {
+          return member2 && relation2 ? _t.append("issues.missing_role.message", {
             member: utilDisplayLabel(member2, context.graph()),
             relation: utilDisplayLabel(relation2, context.graph())
           }) : "";
@@ -41593,11 +43126,14 @@ ${content}</tr>
             makeAddRoleFix("outer"),
             new validationIssueFix({
               icon: "iD-operation-delete",
-              title: _t.html("issues.fix.remove_from_relation.title"),
+              title: _t.append("issues.fix.remove_from_relation.title"),
               onClick: function(context) {
-                context.perform(actionDeleteMember(this.issue.entityIds[0], this.issue.data.member.index), _t("operations.delete_member.annotation", {
-                  n: 1
-                }));
+                context.perform(
+                  actionDeleteMember(this.issue.entityIds[0], this.issue.data.member.index),
+                  _t("operations.delete_member.annotation", {
+                    n: 1
+                  })
+                );
               }
             })
           ];
@@ -41609,23 +43145,26 @@ ${content}</tr>
     }
     function makeAddRoleFix(role) {
       return new validationIssueFix({
-        title: _t.html("issues.fix.set_as_" + role + ".title"),
+        title: _t.append("issues.fix.set_as_" + role + ".title"),
         onClick: function(context) {
           var oldMember = this.issue.data.member;
           var member = { id: this.issue.entityIds[1], type: oldMember.type, role };
-          context.perform(actionChangeMember(this.issue.entityIds[0], member, oldMember.index), _t("operations.change_role.annotation", {
-            n: 1
-          }));
+          context.perform(
+            actionChangeMember(this.issue.entityIds[0], member, oldMember.index),
+            _t("operations.change_role.annotation", {
+              n: 1
+            })
+          );
         }
       });
     }
-    validation.type = type3;
+    validation.type = type2;
     return validation;
   }
 
   // modules/validations/missing_tag.js
   function validationMissingTag(context) {
-    var type3 = "missing_tag";
+    var type2 = "missing_tag";
     function hasDescriptiveTags(entity, graph) {
       var onlyAttributeKeys = ["description", "name", "note", "start_date"];
       var entityDescriptiveKeys = Object.keys(entity.tags).filter(function(k) {
@@ -41650,7 +43189,9 @@ ${content}</tr>
       var subtype;
       var osm = context.connection();
       var isUnloadedNode = entity.type === "node" && osm && !osm.isDataLoaded(entity.loc);
-      if (!isUnloadedNode && entity.geometry(graph) !== "vertex" && !entity.hasParentRelations(graph)) {
+      if (!isUnloadedNode && // allow untagged nodes that are part of ways
+      entity.geometry(graph) !== "vertex" && // allow untagged entities that are part of relations
+      !entity.hasParentRelations(graph)) {
         if (Object.keys(entity.tags).length === 0) {
           subtype = "any";
         } else if (!hasDescriptiveTags(entity, graph)) {
@@ -41669,12 +43210,12 @@ ${content}</tr>
       var canDelete = entity.version === void 0 || entity.v !== void 0;
       var severity = canDelete && subtype !== "highway_classification" ? "error" : "warning";
       return [new validationIssue({
-        type: type3,
+        type: type2,
         subtype,
         severity,
         message: function(context2) {
           var entity2 = context2.hasEntity(this.entityIds[0]);
-          return entity2 ? _t.html("issues." + messageID + ".message", {
+          return entity2 ? _t.append("issues." + messageID + ".message", {
             feature: utilDisplayLabel(entity2, context2.graph())
           }) : "";
         },
@@ -41685,7 +43226,7 @@ ${content}</tr>
           var selectFixType = subtype === "highway_classification" ? "select_road_type" : "select_preset";
           fixes.push(new validationIssueFix({
             icon: "iD-icon-search",
-            title: _t.html("issues.fix." + selectFixType + ".title"),
+            title: _t.append("issues.fix." + selectFixType + ".title"),
             onClick: function(context3) {
               context3.ui().sidebar.showPresetList();
             }
@@ -41703,12 +43244,14 @@ ${content}</tr>
               }
             };
           }
-          fixes.push(new validationIssueFix({
-            icon: "iD-operation-delete",
-            title: _t.html("issues.fix.delete_feature.title"),
-            disabledReason: disabledReasonID ? _t("operations.delete." + disabledReasonID + ".single") : void 0,
-            onClick: deleteOnClick
-          }));
+          fixes.push(
+            new validationIssueFix({
+              icon: "iD-operation-delete",
+              title: _t.append("issues.fix.delete_feature.title"),
+              disabledReason: disabledReasonID ? _t("operations.delete." + disabledReasonID + ".single") : void 0,
+              onClick: deleteOnClick
+            })
+          );
           return fixes;
         }
       })];
@@ -41716,28 +43259,34 @@ ${content}</tr>
         selection2.selectAll(".issue-reference").data([0]).enter().append("div").attr("class", "issue-reference").call(_t.append("issues." + referenceID + ".reference"));
       }
     };
-    validation.type = type3;
+    validation.type = type2;
     return validation;
   }
 
   // modules/validations/outdated_tags.js
   function validationOutdatedTags() {
-    const type3 = "outdated_tags";
+    const type2 = "outdated_tags";
     let _waitingForDeprecated = true;
     let _dataDeprecated;
     _mainFileFetcher.get("deprecated").then((d) => _dataDeprecated = d).catch(() => {
     }).finally(() => _waitingForDeprecated = false);
     function oldTagIssues(entity, graph) {
-      const oldTags = Object.assign({}, entity.tags);
+      if (!entity.hasInterestingTags())
+        return [];
       let preset = _mainPresetIndex.match(entity, graph);
-      let subtype = "deprecated_tags";
       if (!preset)
         return [];
-      if (!entity.hasInterestingTags())
-        return [];
+      const oldTags = Object.assign({}, entity.tags);
+      let subtype = "deprecated_tags";
       if (preset.replacement) {
         const newPreset = _mainPresetIndex.item(preset.replacement);
-        graph = actionChangePreset(entity.id, preset, newPreset, true)(graph);
+        graph = actionChangePreset(
+          entity.id,
+          preset,
+          newPreset,
+          true
+          /* skip field defaults */
+        )(graph);
         entity = graph.entity(entity.id);
         preset = newPreset;
       }
@@ -41791,7 +43340,7 @@ ${content}</tr>
       }
       let autoArgs = subtype !== "noncanonical_brand" ? [doUpgrade, _t("issues.fix.upgrade_tags.annotation")] : null;
       issues.push(new validationIssue({
-        type: type3,
+        type: type2,
         subtype,
         severity: "warning",
         message: showMessage,
@@ -41802,7 +43351,7 @@ ${content}</tr>
           let fixes = [
             new validationIssueFix({
               autoArgs,
-              title: _t.html("issues.fix.upgrade_tags.title"),
+              title: _t.append("issues.fix.upgrade_tags.title"),
               onClick: (context) => {
                 context.perform(doUpgrade, _t("issues.fix.upgrade_tags.annotation"));
               }
@@ -41810,12 +43359,14 @@ ${content}</tr>
           ];
           const item = nsiResult && nsiResult.matched;
           if (item) {
-            fixes.push(new validationIssueFix({
-              title: _t.html("issues.fix.tag_as_not.title", { name: item.displayName }),
-              onClick: (context) => {
-                context.perform(addNotTag, _t("issues.fix.tag_as_not.annotation"));
-              }
-            }));
+            fixes.push(
+              new validationIssueFix({
+                title: _t.append("issues.fix.tag_as_not.title", { name: item.displayName }),
+                onClick: (context) => {
+                  context.perform(addNotTag, _t("issues.fix.tag_as_not.annotation"));
+                }
+              })
+            );
           }
           return fixes;
         }
@@ -41862,8 +43413,13 @@ ${content}</tr>
         if (subtype === "noncanonical_brand" && isOnlyAddingTags) {
           messageID += "_incomplete";
         }
-        return _t.html(messageID, {
-          feature: utilDisplayLabel(currEntity, context.graph(), true)
+        return _t.append(messageID, {
+          feature: utilDisplayLabel(
+            currEntity,
+            context.graph(),
+            true
+            /* verbose */
+          )
         });
       }
       function showReference(selection2) {
@@ -41890,7 +43446,7 @@ ${content}</tr>
       if (!multipolygon || !outerWay)
         return [];
       return [new validationIssue({
-        type: type3,
+        type: type2,
         subtype: "old_multipolygon",
         severity: "warning",
         message: showMessage,
@@ -41900,7 +43456,7 @@ ${content}</tr>
           return [
             new validationIssueFix({
               autoArgs: [doUpgrade, _t("issues.fix.move_tags.annotation")],
-              title: _t.html("issues.fix.move_tags.title"),
+              title: _t.append("issues.fix.move_tags.title"),
               onClick: (context) => {
                 context.perform(doUpgrade, _t("issues.fix.move_tags.annotation"));
               }
@@ -41921,7 +43477,15 @@ ${content}</tr>
         let currMultipolygon = context.hasEntity(multipolygon.id);
         if (!currMultipolygon)
           return "";
-        return _t.html("issues.old_multipolygon.message", { multipolygon: utilDisplayLabel(currMultipolygon, context.graph(), true) });
+        return _t.append(
+          "issues.old_multipolygon.message",
+          { multipolygon: utilDisplayLabel(
+            currMultipolygon,
+            context.graph(),
+            true
+            /* verbose */
+          ) }
+        );
       }
       function showReference(selection2) {
         selection2.selectAll(".issue-reference").data([0]).enter().append("div").attr("class", "issue-reference").call(_t.append("issues.old_multipolygon.reference"));
@@ -41933,13 +43497,13 @@ ${content}</tr>
         issues = oldTagIssues(entity, graph);
       return issues;
     };
-    validation.type = type3;
+    validation.type = type2;
     return validation;
   }
 
   // modules/validations/private_data.js
   function validationPrivateData() {
-    var type3 = "private_data";
+    var type2 = "private_data";
     var privateBuildingValues = {
       detached: true,
       farm: true,
@@ -41983,7 +43547,7 @@ ${content}</tr>
         return [];
       var fixID = tagDiff.length === 1 ? "remove_tag" : "remove_tags";
       return [new validationIssue({
-        type: type3,
+        type: type2,
         severity: "warning",
         message: showMessage,
         reference: showReference,
@@ -41992,9 +43556,9 @@ ${content}</tr>
           return [
             new validationIssueFix({
               icon: "iD-operation-delete",
-              title: _t.html("issues.fix." + fixID + ".title"),
+              title: _t.append("issues.fix." + fixID + ".title"),
               onClick: function(context) {
-                context.perform(doUpgrade, _t("issues.fix.upgrade_tags.annotation"));
+                context.perform(doUpgrade, _t("issues.fix.remove_tag.annotation"));
               }
             })
           ];
@@ -42018,7 +43582,10 @@ ${content}</tr>
         var currEntity = context.hasEntity(this.entityIds[0]);
         if (!currEntity)
           return "";
-        return _t.html("issues.private_data.contact.message", { feature: utilDisplayLabel(currEntity, context.graph()) });
+        return _t.append(
+          "issues.private_data.contact.message",
+          { feature: utilDisplayLabel(currEntity, context.graph()) }
+        );
       }
       function showReference(selection2) {
         var enter = selection2.selectAll(".issue-reference").data([0]).enter();
@@ -42032,13 +43599,13 @@ ${content}</tr>
         });
       }
     };
-    validation.type = type3;
+    validation.type = type2;
     return validation;
   }
 
   // modules/validations/suspicious_name.js
   function validationSuspiciousName() {
-    const type3 = "suspicious_name";
+    const type2 = "suspicious_name";
     const keysToTestForGenericValues = [
       "aerialway",
       "aeroway",
@@ -42078,13 +43645,13 @@ ${content}</tr>
       }
       return false;
     }
-    function isGenericName(name2, tags) {
-      name2 = name2.toLowerCase();
-      return nameMatchesRawTag(name2, tags) || isGenericMatchInNsi(tags);
+    function isGenericName(name, tags) {
+      name = name.toLowerCase();
+      return nameMatchesRawTag(name, tags) || isGenericMatchInNsi(tags);
     }
     function makeGenericNameIssue(entityId, nameKey, genericName, langCode) {
       return new validationIssue({
-        type: type3,
+        type: type2,
         subtype: "generic_name",
         severity: "warning",
         message: function(context) {
@@ -42093,7 +43660,10 @@ ${content}</tr>
             return "";
           let preset = _mainPresetIndex.match(entity, context.graph());
           let langName = langCode && _mainLocalizer.languageName(langCode);
-          return _t.html("issues.generic_name.message" + (langName ? "_language" : ""), { feature: preset.name(), name: genericName, language: langName });
+          return _t.append(
+            "issues.generic_name.message" + (langName ? "_language" : ""),
+            { feature: preset.name(), name: genericName, language: langName }
+          );
         },
         reference: showReference,
         entityIds: [entityId],
@@ -42102,13 +43672,16 @@ ${content}</tr>
           return [
             new validationIssueFix({
               icon: "iD-operation-delete",
-              title: _t.html("issues.fix.remove_the_name.title"),
+              title: _t.append("issues.fix.remove_the_name.title"),
               onClick: function(context) {
                 let entityId2 = this.issue.entityIds[0];
                 let entity = context.entity(entityId2);
                 let tags = Object.assign({}, entity.tags);
                 delete tags[nameKey];
-                context.perform(actionChangeTags(entityId2, tags), _t("issues.fix.remove_generic_name.annotation"));
+                context.perform(
+                  actionChangeTags(entityId2, tags),
+                  _t("issues.fix.remove_generic_name.annotation")
+                );
               }
             })
           ];
@@ -42120,7 +43693,7 @@ ${content}</tr>
     }
     function makeIncorrectNameIssue(entityId, nameKey, incorrectName, langCode) {
       return new validationIssue({
-        type: type3,
+        type: type2,
         subtype: "not_name",
         severity: "warning",
         message: function(context) {
@@ -42129,7 +43702,10 @@ ${content}</tr>
             return "";
           const preset = _mainPresetIndex.match(entity, context.graph());
           const langName = langCode && _mainLocalizer.languageName(langCode);
-          return _t.html("issues.incorrect_name.message" + (langName ? "_language" : ""), { feature: preset.name(), name: incorrectName, language: langName });
+          return _t.append(
+            "issues.incorrect_name.message" + (langName ? "_language" : ""),
+            { feature: preset.name(), name: incorrectName, language: langName }
+          );
         },
         reference: showReference,
         entityIds: [entityId],
@@ -42138,13 +43714,16 @@ ${content}</tr>
           return [
             new validationIssueFix({
               icon: "iD-operation-delete",
-              title: _t.html("issues.fix.remove_the_name.title"),
+              title: _t.append("issues.fix.remove_the_name.title"),
               onClick: function(context) {
                 const entityId2 = this.issue.entityIds[0];
                 const entity = context.entity(entityId2);
                 let tags = Object.assign({}, entity.tags);
                 delete tags[nameKey];
-                context.perform(actionChangeTags(entityId2, tags), _t("issues.fix.remove_mistaken_name.annotation"));
+                context.perform(
+                  actionChangeTags(entityId2, tags),
+                  _t("issues.fix.remove_mistaken_name.annotation")
+                );
               }
             })
           ];
@@ -42183,13 +43762,13 @@ ${content}</tr>
       }
       return issues;
     };
-    validation.type = type3;
+    validation.type = type2;
     return validation;
   }
 
   // modules/validations/unsquare_way.js
   function validationUnsquareWay(context) {
-    var type3 = "unsquare_way";
+    var type2 = "unsquare_way";
     var DEFAULT_DEG_THRESHOLD = 5;
     var epsilon3 = 0.05;
     var nodeThreshold = 10;
@@ -42228,7 +43807,7 @@ ${content}</tr>
       if (hasConnectedSquarableWays)
         return [];
       var storedDegreeThreshold = corePreferences("validate-square-degrees");
-      var degreeThreshold = isNaN(storedDegreeThreshold) ? DEFAULT_DEG_THRESHOLD : parseFloat(storedDegreeThreshold);
+      var degreeThreshold = isFinite(storedDegreeThreshold) ? Number(storedDegreeThreshold) : DEFAULT_DEG_THRESHOLD;
       var points = nodes.map(function(node) {
         return context.projection(node.loc);
       });
@@ -42241,12 +43820,12 @@ ${content}</tr>
         autoArgs = [autoAction, _t("operations.orthogonalize.annotation.feature", { n: 1 })];
       }
       return [new validationIssue({
-        type: type3,
+        type: type2,
         subtype: "building",
         severity: "warning",
         message: function(context2) {
           var entity2 = context2.hasEntity(this.entityIds[0]);
-          return entity2 ? _t.html("issues.unsquare_way.message", {
+          return entity2 ? _t.append("issues.unsquare_way.message", {
             feature: utilDisplayLabel(entity2, context2.graph())
           }) : "";
         },
@@ -42257,16 +43836,34 @@ ${content}</tr>
           return [
             new validationIssueFix({
               icon: "iD-operation-orthogonalize",
-              title: _t.html("issues.fix.square_feature.title"),
+              title: _t.append("issues.fix.square_feature.title"),
               autoArgs,
               onClick: function(context2, completionHandler) {
                 var entityId = this.issue.entityIds[0];
-                context2.perform(actionOrthogonalize(entityId, context2.projection, void 0, degreeThreshold), _t("operations.orthogonalize.annotation.feature", { n: 1 }));
+                context2.perform(
+                  actionOrthogonalize(entityId, context2.projection, void 0, degreeThreshold),
+                  _t("operations.orthogonalize.annotation.feature", { n: 1 })
+                );
                 window.setTimeout(function() {
                   completionHandler();
                 }, 175);
               }
             })
+            /*
+            new validationIssueFix({
+                title: t.append('issues.fix.tag_as_unsquare.title'),
+                onClick: function(context) {
+                    var entityId = this.issue.entityIds[0];
+                    var entity = context.entity(entityId);
+                    var tags = Object.assign({}, entity.tags);  // shallow copy
+                    tags.nonsquare = 'yes';
+                    context.perform(
+                        actionChangeTags(entityId, tags),
+                        t('issues.fix.tag_as_unsquare.annotation')
+                    );
+                }
+            })
+            */
           ];
         }
       })];
@@ -42274,7 +43871,7 @@ ${content}</tr>
         selection2.selectAll(".issue-reference").data([0]).enter().append("div").attr("class", "issue-reference").call(_t.append("issues.unsquare_way.buildings.reference"));
       }
     };
-    validation.type = type3;
+    validation.type = type2;
     return validation;
   }
 
@@ -42303,11 +43900,11 @@ ${content}</tr>
       rules.forEach((rule) => {
         rule = rule.trim();
         const parts = rule.split("/", 2);
-        const type3 = parts[0];
+        const type2 = parts[0];
         const subtype = parts[1] || "*";
-        if (!type3 || !subtype)
+        if (!type2 || !subtype)
           return;
-        result.push({ type: makeRegExp(type3), subtype: makeRegExp(subtype) });
+        result.push({ type: makeRegExp(type2), subtype: makeRegExp(subtype) });
       });
       return result;
       function makeRegExp(str2) {
@@ -42467,14 +44064,19 @@ ${content}</tr>
     };
     validator.getSharedEntityIssues = (entityIDs, options2) => {
       const orderedIssueTypes = [
+        // Show some issue types in a particular order:
         "missing_tag",
         "missing_role",
+        // - missing data first
         "outdated_tags",
         "mismatched_geometry",
+        // - identity issues
         "crossing_ways",
         "almost_junction",
+        // - geometry issues where fixing them might solve connectivity issues
         "disconnected_way",
         "impossible_oneway"
+        // - finally connectivity issues
       ];
       const allIssues = validator.getIssues(options2);
       const forEntityIDs = new Set(entityIDs);
@@ -42589,23 +44191,23 @@ ${content}</tr>
         detected = detected.filter(applySeverityOverrides);
         result.issues = result.issues.concat(detected);
         function applySeverityOverrides(issue) {
-          const type3 = issue.type;
+          const type2 = issue.type;
           const subtype = issue.subtype || "";
           let i2;
           for (i2 = 0; i2 < _errorOverrides.length; i2++) {
-            if (_errorOverrides[i2].type.test(type3) && _errorOverrides[i2].subtype.test(subtype)) {
+            if (_errorOverrides[i2].type.test(type2) && _errorOverrides[i2].subtype.test(subtype)) {
               issue.severity = "error";
               return true;
             }
           }
           for (i2 = 0; i2 < _warningOverrides.length; i2++) {
-            if (_warningOverrides[i2].type.test(type3) && _warningOverrides[i2].subtype.test(subtype)) {
+            if (_warningOverrides[i2].type.test(type2) && _warningOverrides[i2].subtype.test(subtype)) {
               issue.severity = "warning";
               return true;
             }
           }
           for (i2 = 0; i2 < _disableOverrides.length; i2++) {
-            if (_disableOverrides[i2].type.test(type3) && _disableOverrides[i2].subtype.test(subtype)) {
+            if (_disableOverrides[i2].type.test(type2) && _disableOverrides[i2].subtype.test(subtype)) {
               return false;
             }
           }
@@ -42695,7 +44297,9 @@ ${content}</tr>
       queuedEntityIDs: /* @__PURE__ */ new Set(),
       provisionalEntityIDs: /* @__PURE__ */ new Set(),
       issuesByIssueID: {},
+      // issue.id -> issue
       issuesByEntityID: {}
+      // entity.id -> Set(issue.id)
     };
     cache.cacheIssue = (issue) => {
       (issue.entityIds || []).forEach((entityID) => {
@@ -42720,8 +44324,8 @@ ${content}</tr>
     cache.uncacheIssues = (issues) => {
       issues.forEach(cache.uncacheIssue);
     };
-    cache.uncacheIssuesOfType = (type3) => {
-      const issuesOfType = Object.values(cache.issuesByIssueID).filter((issue) => issue.type === type3);
+    cache.uncacheIssuesOfType = (type2) => {
+      const issuesOfType = Object.values(cache.issuesByIssueID).filter((issue) => issue.type === type2);
       cache.uncacheIssues(issuesOfType);
     };
     cache.uncacheEntityID = (entityID) => {
@@ -42762,7 +44366,25 @@ ${content}</tr>
 
   // modules/core/uploader.js
   function coreUploader(context) {
-    var dispatch10 = dispatch_default("saveStarted", "saveEnded", "willAttemptUpload", "progressChanged", "resultNoChanges", "resultErrors", "resultConflicts", "resultSuccess");
+    var dispatch10 = dispatch_default(
+      // Start and end events are dispatched exactly once each per legitimate outside call to `save`
+      "saveStarted",
+      // dispatched as soon as a call to `save` has been deemed legitimate
+      "saveEnded",
+      // dispatched after the result event has been dispatched
+      "willAttemptUpload",
+      // dispatched before the actual upload call occurs, if it will
+      "progressChanged",
+      // Each save results in one of these outcomes:
+      "resultNoChanges",
+      // upload wasn't attempted since there were no edits
+      "resultErrors",
+      // upload failed due to errors
+      "resultConflicts",
+      // upload failed due to data conflicts
+      "resultSuccess"
+      // upload completed without errors
+    );
     var _isSaving = false;
     var _conflicts = [];
     var _errors = [];
@@ -43052,6 +44674,12 @@ ${content}</tr>
 
   // modules/util/IntervalTasksQueue.js
   var IntervalTasksQueue = class {
+    /**
+     * Interval in milliseconds inside which only 1 task can execute.
+     * e.g. if interval is 200ms, and 5 async tasks are unqueued,
+     * they will complete in ~1s if not cleared
+     * @param {number} intervalInMs
+     */
     constructor(intervalInMs) {
       this.intervalInMs = intervalInMs;
       this.pendingHandles = [];
@@ -43129,11 +44757,16 @@ ${content}</tr>
     };
     source.label = function() {
       var id_safe = source.id.replace(/\./g, "<TX_DOT>");
-      return _t.html("imagery." + id_safe + ".name", { default: (0, import_lodash2.escape)(_name) });
+      return _t.append("imagery." + id_safe + ".name", { default: (0, import_lodash2.escape)(_name) });
+    };
+    source.hasDescription = function() {
+      var id_safe = source.id.replace(/\./g, "<TX_DOT>");
+      var descriptionText = _mainLocalizer.tInfo("imagery." + id_safe + ".description", { default: (0, import_lodash2.escape)(_description) }).text;
+      return descriptionText !== "";
     };
     source.description = function() {
       var id_safe = source.id.replace(/\./g, "<TX_DOT>");
-      return _t.html("imagery." + id_safe + ".description", { default: (0, import_lodash2.escape)(_description) });
+      return _t.append("imagery." + id_safe + ".description", { default: (0, import_lodash2.escape)(_description) });
     };
     source.best = function() {
       return _best;
@@ -43202,7 +44835,8 @@ ${content}</tr>
             case "wkid":
               return projection2.replace(/^EPSG:/, "");
             case "bbox":
-              if (projection2 === "EPSG:4326" && /VERSION=1.3|CRS={proj}/.test(source.template().toUpperCase())) {
+              if (projection2 === "EPSG:4326" && // The CRS parameter implies version 1.3 (prior versions use SRS)
+              /VERSION=1.3|CRS={proj}/.test(source.template().toUpperCase())) {
                 return maxXminY.y + "," + minXmaxY.x + "," + minXmaxY.y + "," + maxXminY.x;
               } else {
                 return minXmaxY.x + "," + maxXminY.y + "," + maxXminY.x + "," + minXmaxY.y;
@@ -43427,8 +45061,8 @@ ${content}</tr>
           vintage,
           source: clean2(result.NICE_NAME),
           description: clean2(result.NICE_DESC),
-          resolution: clean2(+parseFloat(result.SRC_RES).toFixed(4)),
-          accuracy: clean2(+parseFloat(result.SRC_ACC).toFixed(4))
+          resolution: clean2(+Number(result.SRC_RES).toFixed(4)),
+          accuracy: clean2(+Number(result.SRC_ACC).toFixed(4))
         };
         if (isFinite(metadata.resolution)) {
           metadata.resolution += " m";
@@ -43456,7 +45090,7 @@ ${content}</tr>
       return _t("background.none");
     };
     source.label = function() {
-      return _t.html("background.none");
+      return _t.append("background.none");
     };
     source.imageryUsed = function() {
       return null;
@@ -43472,7 +45106,7 @@ ${content}</tr>
       return _t("background.custom");
     };
     source.label = function() {
-      return _t.html("background.custom");
+      return _t.append("background.custom");
     };
     source.imageryUsed = function() {
       var cleaned = source.template();
@@ -43495,6 +45129,373 @@ ${content}</tr>
     return source;
   };
 
+  // node_modules/@turf/helpers/dist/es/index.js
+  var earthRadius = 63710088e-1;
+  var factors = {
+    centimeters: earthRadius * 100,
+    centimetres: earthRadius * 100,
+    degrees: earthRadius / 111325,
+    feet: earthRadius * 3.28084,
+    inches: earthRadius * 39.37,
+    kilometers: earthRadius / 1e3,
+    kilometres: earthRadius / 1e3,
+    meters: earthRadius,
+    metres: earthRadius,
+    miles: earthRadius / 1609.344,
+    millimeters: earthRadius * 1e3,
+    millimetres: earthRadius * 1e3,
+    nauticalmiles: earthRadius / 1852,
+    radians: 1,
+    yards: earthRadius * 1.0936
+  };
+  var unitsFactors = {
+    centimeters: 100,
+    centimetres: 100,
+    degrees: 1 / 111325,
+    feet: 3.28084,
+    inches: 39.37,
+    kilometers: 1 / 1e3,
+    kilometres: 1 / 1e3,
+    meters: 1,
+    metres: 1,
+    miles: 1 / 1609.344,
+    millimeters: 1e3,
+    millimetres: 1e3,
+    nauticalmiles: 1 / 1852,
+    radians: 1 / earthRadius,
+    yards: 1.0936133
+  };
+  function feature2(geom, properties, options2) {
+    if (options2 === void 0) {
+      options2 = {};
+    }
+    var feat = { type: "Feature" };
+    if (options2.id === 0 || options2.id) {
+      feat.id = options2.id;
+    }
+    if (options2.bbox) {
+      feat.bbox = options2.bbox;
+    }
+    feat.properties = properties || {};
+    feat.geometry = geom;
+    return feat;
+  }
+  function polygon(coordinates, properties, options2) {
+    if (options2 === void 0) {
+      options2 = {};
+    }
+    for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) {
+      var ring = coordinates_1[_i];
+      if (ring.length < 4) {
+        throw new Error("Each LinearRing of a Polygon must have 4 or more Positions.");
+      }
+      for (var j2 = 0; j2 < ring[ring.length - 1].length; j2++) {
+        if (ring[ring.length - 1][j2] !== ring[0][j2]) {
+          throw new Error("First and last Position are not equivalent.");
+        }
+      }
+    }
+    var geom = {
+      type: "Polygon",
+      coordinates
+    };
+    return feature2(geom, properties, options2);
+  }
+  function lineString(coordinates, properties, options2) {
+    if (options2 === void 0) {
+      options2 = {};
+    }
+    if (coordinates.length < 2) {
+      throw new Error("coordinates must be an array of two or more positions");
+    }
+    var geom = {
+      type: "LineString",
+      coordinates
+    };
+    return feature2(geom, properties, options2);
+  }
+  function multiLineString(coordinates, properties, options2) {
+    if (options2 === void 0) {
+      options2 = {};
+    }
+    var geom = {
+      type: "MultiLineString",
+      coordinates
+    };
+    return feature2(geom, properties, options2);
+  }
+  function multiPolygon(coordinates, properties, options2) {
+    if (options2 === void 0) {
+      options2 = {};
+    }
+    var geom = {
+      type: "MultiPolygon",
+      coordinates
+    };
+    return feature2(geom, properties, options2);
+  }
+
+  // node_modules/@turf/invariant/dist/es/index.js
+  function getGeom(geojson) {
+    if (geojson.type === "Feature") {
+      return geojson.geometry;
+    }
+    return geojson;
+  }
+
+  // node_modules/@turf/bbox-clip/dist/es/lib/lineclip.js
+  function lineclip(points, bbox2, result) {
+    var len = points.length, codeA = bitCode(points[0], bbox2), part = [], i2, codeB, lastCode;
+    var a;
+    var b;
+    if (!result)
+      result = [];
+    for (i2 = 1; i2 < len; i2++) {
+      a = points[i2 - 1];
+      b = points[i2];
+      codeB = lastCode = bitCode(b, bbox2);
+      while (true) {
+        if (!(codeA | codeB)) {
+          part.push(a);
+          if (codeB !== lastCode) {
+            part.push(b);
+            if (i2 < len - 1) {
+              result.push(part);
+              part = [];
+            }
+          } else if (i2 === len - 1) {
+            part.push(b);
+          }
+          break;
+        } else if (codeA & codeB) {
+          break;
+        } else if (codeA) {
+          a = intersect(a, b, codeA, bbox2);
+          codeA = bitCode(a, bbox2);
+        } else {
+          b = intersect(a, b, codeB, bbox2);
+          codeB = bitCode(b, bbox2);
+        }
+      }
+      codeA = lastCode;
+    }
+    if (part.length)
+      result.push(part);
+    return result;
+  }
+  function polygonclip(points, bbox2) {
+    var result, edge, prev, prevInside, i2, p, inside;
+    for (edge = 1; edge <= 8; edge *= 2) {
+      result = [];
+      prev = points[points.length - 1];
+      prevInside = !(bitCode(prev, bbox2) & edge);
+      for (i2 = 0; i2 < points.length; i2++) {
+        p = points[i2];
+        inside = !(bitCode(p, bbox2) & edge);
+        if (inside !== prevInside)
+          result.push(intersect(prev, p, edge, bbox2));
+        if (inside)
+          result.push(p);
+        prev = p;
+        prevInside = inside;
+      }
+      points = result;
+      if (!points.length)
+        break;
+    }
+    return result;
+  }
+  function intersect(a, b, edge, bbox2) {
+    return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox2[3] - a[1]) / (b[1] - a[1]), bbox2[3]] : edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox2[1] - a[1]) / (b[1] - a[1]), bbox2[1]] : edge & 2 ? [bbox2[2], a[1] + (b[1] - a[1]) * (bbox2[2] - a[0]) / (b[0] - a[0])] : edge & 1 ? [bbox2[0], a[1] + (b[1] - a[1]) * (bbox2[0] - a[0]) / (b[0] - a[0])] : null;
+  }
+  function bitCode(p, bbox2) {
+    var code = 0;
+    if (p[0] < bbox2[0])
+      code |= 1;
+    else if (p[0] > bbox2[2])
+      code |= 2;
+    if (p[1] < bbox2[1])
+      code |= 4;
+    else if (p[1] > bbox2[3])
+      code |= 8;
+    return code;
+  }
+
+  // node_modules/@turf/bbox-clip/dist/es/index.js
+  function bboxClip(feature3, bbox2) {
+    var geom = getGeom(feature3);
+    var type2 = geom.type;
+    var properties = feature3.type === "Feature" ? feature3.properties : {};
+    var coords = geom.coordinates;
+    switch (type2) {
+      case "LineString":
+      case "MultiLineString": {
+        var lines_1 = [];
+        if (type2 === "LineString") {
+          coords = [coords];
+        }
+        coords.forEach(function(line) {
+          lineclip(line, bbox2, lines_1);
+        });
+        if (lines_1.length === 1) {
+          return lineString(lines_1[0], properties);
+        }
+        return multiLineString(lines_1, properties);
+      }
+      case "Polygon":
+        return polygon(clipPolygon(coords, bbox2), properties);
+      case "MultiPolygon":
+        return multiPolygon(coords.map(function(poly) {
+          return clipPolygon(poly, bbox2);
+        }), properties);
+      default:
+        throw new Error("geometry " + type2 + " not supported");
+    }
+  }
+  function clipPolygon(rings, bbox2) {
+    var outRings = [];
+    for (var _i = 0, rings_1 = rings; _i < rings_1.length; _i++) {
+      var ring = rings_1[_i];
+      var clipped = polygonclip(ring, bbox2);
+      if (clipped.length > 0) {
+        if (clipped[0][0] !== clipped[clipped.length - 1][0] || clipped[0][1] !== clipped[clipped.length - 1][1]) {
+          clipped.push(clipped[0]);
+        }
+        if (clipped.length >= 4) {
+          outRings.push(clipped);
+        }
+      }
+    }
+    return outRings;
+  }
+
+  // node_modules/@turf/meta/dist/es/index.js
+  function coordEach(geojson, callback, excludeWrapCoord) {
+    if (geojson === null)
+      return;
+    var j2, k, l, geometry, stopG, coords, geometryMaybeCollection, wrapShrink = 0, coordIndex = 0, isGeometryCollection, type2 = geojson.type, isFeatureCollection = type2 === "FeatureCollection", isFeature = type2 === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
+    for (var featureIndex = 0; featureIndex < stop; featureIndex++) {
+      geometryMaybeCollection = isFeatureCollection ? geojson.features[featureIndex].geometry : isFeature ? geojson.geometry : geojson;
+      isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
+      stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
+      for (var geomIndex = 0; geomIndex < stopG; geomIndex++) {
+        var multiFeatureIndex = 0;
+        var geometryIndex = 0;
+        geometry = isGeometryCollection ? geometryMaybeCollection.geometries[geomIndex] : geometryMaybeCollection;
+        if (geometry === null)
+          continue;
+        coords = geometry.coordinates;
+        var geomType = geometry.type;
+        wrapShrink = excludeWrapCoord && (geomType === "Polygon" || geomType === "MultiPolygon") ? 1 : 0;
+        switch (geomType) {
+          case null:
+            break;
+          case "Point":
+            if (callback(
+              coords,
+              coordIndex,
+              featureIndex,
+              multiFeatureIndex,
+              geometryIndex
+            ) === false)
+              return false;
+            coordIndex++;
+            multiFeatureIndex++;
+            break;
+          case "LineString":
+          case "MultiPoint":
+            for (j2 = 0; j2 < coords.length; j2++) {
+              if (callback(
+                coords[j2],
+                coordIndex,
+                featureIndex,
+                multiFeatureIndex,
+                geometryIndex
+              ) === false)
+                return false;
+              coordIndex++;
+              if (geomType === "MultiPoint")
+                multiFeatureIndex++;
+            }
+            if (geomType === "LineString")
+              multiFeatureIndex++;
+            break;
+          case "Polygon":
+          case "MultiLineString":
+            for (j2 = 0; j2 < coords.length; j2++) {
+              for (k = 0; k < coords[j2].length - wrapShrink; k++) {
+                if (callback(
+                  coords[j2][k],
+                  coordIndex,
+                  featureIndex,
+                  multiFeatureIndex,
+                  geometryIndex
+                ) === false)
+                  return false;
+                coordIndex++;
+              }
+              if (geomType === "MultiLineString")
+                multiFeatureIndex++;
+              if (geomType === "Polygon")
+                geometryIndex++;
+            }
+            if (geomType === "Polygon")
+              multiFeatureIndex++;
+            break;
+          case "MultiPolygon":
+            for (j2 = 0; j2 < coords.length; j2++) {
+              geometryIndex = 0;
+              for (k = 0; k < coords[j2].length; k++) {
+                for (l = 0; l < coords[j2][k].length - wrapShrink; l++) {
+                  if (callback(
+                    coords[j2][k][l],
+                    coordIndex,
+                    featureIndex,
+                    multiFeatureIndex,
+                    geometryIndex
+                  ) === false)
+                    return false;
+                  coordIndex++;
+                }
+                geometryIndex++;
+              }
+              multiFeatureIndex++;
+            }
+            break;
+          case "GeometryCollection":
+            for (j2 = 0; j2 < geometry.geometries.length; j2++)
+              if (coordEach(geometry.geometries[j2], callback, excludeWrapCoord) === false)
+                return false;
+            break;
+          default:
+            throw new Error("Unknown Geometry Type");
+        }
+      }
+    }
+  }
+
+  // node_modules/@turf/bbox/dist/es/index.js
+  function bbox(geojson) {
+    var result = [Infinity, Infinity, -Infinity, -Infinity];
+    coordEach(geojson, function(coord2) {
+      if (result[0] > coord2[0]) {
+        result[0] = coord2[0];
+      }
+      if (result[1] > coord2[1]) {
+        result[1] = coord2[1];
+      }
+      if (result[2] < coord2[0]) {
+        result[2] = coord2[0];
+      }
+      if (result[3] < coord2[1]) {
+        result[3] = coord2[1];
+      }
+    });
+    return result;
+  }
+  bbox["default"] = bbox;
+  var es_default = bbox;
+
   // modules/renderer/background.js
   var import_which_polygon4 = __toESM(require_which_polygon());
 
@@ -43661,7 +45662,10 @@ ${content}</tr>
             if (result && result.vintage && result.vintage.range) {
               span.text(result.vintage.range);
             } else {
-              span.html(_t.html("info_panels.background.vintage") + ": " + _t.html("info_panels.background.unknown"));
+              span.text("");
+              span.call(_t.append("info_panels.background.vintage"));
+              span.append("span").text(": ");
+              span.call(_t.append("info_panels.background.unknown"));
             }
           });
         });
@@ -43711,7 +45715,7 @@ ${content}</tr>
           imagery: sources,
           features: {}
         };
-        const features2 = sources.map((source) => {
+        const features = sources.map((source) => {
           if (!source.polygon)
             return null;
           const rings = source.polygon.map((ring) => [ring]);
@@ -43723,7 +45727,7 @@ ${content}</tr>
           _imageryIndex.features[source.id] = feature3;
           return feature3;
         }).filter(Boolean);
-        _imageryIndex.query = (0, import_which_polygon4.default)({ type: "FeatureCollection", features: features2 });
+        _imageryIndex.query = (0, import_which_polygon4.default)({ type: "FeatureCollection", features });
         _imageryIndex.backgrounds = sources.map((source) => {
           if (source.type === "bing") {
             return rendererBackgroundSource.Bing(source, dispatch10);
@@ -43938,7 +45942,9 @@ ${content}</tr>
           return;
         }
       }
-      layer = rendererTileLayer(context).source(d).projection(context.projection).dimensions(baseLayer.dimensions());
+      layer = rendererTileLayer(context).source(d).projection(context.projection).dimensions(
+        baseLayer.dimensions()
+      );
       _overlayLayers.push(layer);
       dispatch10.call("change");
       background.updateImagery();
@@ -44000,30 +46006,41 @@ ${content}</tr>
     background.ensureLoaded = () => {
       if (_loadPromise)
         return _loadPromise;
-      function parseMapParams(qmap) {
-        if (!qmap)
-          return false;
-        const params = qmap.split("/").map(Number);
-        if (params.length < 3 || params.some(isNaN))
-          return false;
-        return geoExtent([params[2], params[1]]);
-      }
+      return _loadPromise = ensureImageryIndex();
+    };
+    background.init = () => {
+      const loadPromise = background.ensureLoaded();
       const hash = utilStringQs(window.location.hash);
-      const requested = hash.background || hash.layer;
-      let extent = parseMapParams(hash.map);
-      return _loadPromise = ensureImageryIndex().then((imageryIndex) => {
-        const first = imageryIndex.backgrounds.length && imageryIndex.backgrounds[0];
+      const requestedBackground = hash.background || hash.layer;
+      const lastUsedBackground = corePreferences("background-last-used");
+      return loadPromise.then((imageryIndex) => {
+        const extent = context.map().extent();
+        const validBackgrounds = background.sources(extent).filter((d) => d.id !== "none" && d.id !== "custom");
+        const first = validBackgrounds.length && validBackgrounds[0];
+        const isLastUsedValid = !!validBackgrounds.find((d) => d.id && d.id === lastUsedBackground);
         let best;
-        if (!requested && extent) {
-          best = background.sources(extent).find((s) => s.best());
+        if (!requestedBackground && extent) {
+          const viewArea = extent.area();
+          best = validBackgrounds.find((s) => {
+            if (!s.best() || s.overlay)
+              return false;
+            let bbox2 = es_default(bboxClip(
+              { type: "MultiPolygon", coordinates: [s.polygon || [extent.polygon()]] },
+              extent.rectangle()
+            ));
+            let area = geoExtent(bbox2.slice(0, 2), bbox2.slice(2, 4)).area();
+            return area / viewArea > 0.5;
+          });
         }
-        if (requested && requested.indexOf("custom:") === 0) {
-          const template = requested.replace(/^custom:/, "");
+        if (requestedBackground && requestedBackground.indexOf("custom:") === 0) {
+          const template = requestedBackground.replace(/^custom:/, "");
           const custom = background.findSource("custom");
           background.baseLayerSource(custom.template(template));
           corePreferences("background-custom-template", template);
         } else {
-          background.baseLayerSource(background.findSource(requested) || best || background.findSource(corePreferences("background-last-used")) || background.findSource("Bing") || first || background.findSource("none"));
+          background.baseLayerSource(
+            background.findSource(requestedBackground) || best || isLastUsedValid && background.findSource(lastUsedBackground) || background.findSource("Bing") || first || background.findSource("none")
+          );
         }
         const locator = imageryIndex.backgrounds.find((d) => d.overlay && d.default);
         if (locator) {
@@ -44048,7 +46065,8 @@ ${content}</tr>
             background.offset(geoMetersToOffset(offset));
           }
         }
-      }).catch(() => {
+      }).catch((err) => {
+        console.error(err);
       });
     };
     return utilRebind(background, dispatch10, "on");
@@ -44057,7 +46075,7 @@ ${content}</tr>
   // modules/renderer/features.js
   function rendererFeatures(context) {
     var dispatch10 = dispatch_default("change", "redraw");
-    var features2 = utilRebind({}, dispatch10, "on");
+    var features = utilRebind({}, dispatch10, "on");
     var _deferred2 = /* @__PURE__ */ new Set();
     var traffic_roads = {
       "motorway": true,
@@ -44072,7 +46090,8 @@ ${content}</tr>
       "tertiary_link": true,
       "residential": true,
       "unclassified": true,
-      "living_street": true
+      "living_street": true,
+      "busway": true
     };
     var service_roads = {
       "service": true,
@@ -44087,16 +46106,6 @@ ${content}</tr>
       "steps": true,
       "pedestrian": true
     };
-    var past_futures = {
-      "proposed": true,
-      "construction": true,
-      "abandoned": true,
-      "dismantled": true,
-      "disused": true,
-      "razed": true,
-      "demolished": true,
-      "obliterated": true
-    };
     var _cullFactor = 1;
     var _cache4 = {};
     var _rules = {};
@@ -44107,7 +46116,7 @@ ${content}</tr>
     function update() {
       if (!window.mocha) {
         var hash = utilStringQs(window.location.hash);
-        var disabled = features2.disabled();
+        var disabled = features.disabled();
         if (disabled.length) {
           hash.disable_features = disabled.join(",");
         } else {
@@ -44116,7 +46125,7 @@ ${content}</tr>
         window.location.replace("#" + utilQsString(hash, true));
         corePreferences("disabled-features", disabled.join(","));
       }
-      _hidden = features2.hidden();
+      _hidden = features.hidden();
       dispatch10.call("change");
       dispatch10.call("redraw");
     }
@@ -44126,6 +46135,7 @@ ${content}</tr>
       _rules[k] = {
         filter: filter2,
         enabled: isEnabled,
+        // whether the user wants it enabled..
         count: 0,
         currentMax: max3 || Infinity,
         defaultMax: max3 || Infinity,
@@ -44169,8 +46179,8 @@ ${content}</tr>
     defineRule("landuse", function isLanduse(tags, geometry) {
       return geometry === "area" && !_rules.buildings.filter(tags) && !_rules.building_parts.filter(tags) && !_rules.indoor.filter(tags) && !_rules.water.filter(tags) && !_rules.pistes.filter(tags);
     });
-    defineRule("boundaries", function isBoundary(tags) {
-      return !!tags.boundary && !(traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway] || tags.waterway || tags.railway || tags.landuse || tags.natural || tags.building || tags.power);
+    defineRule("boundaries", function isBoundary(tags, geometry) {
+      return (geometry === "line" && !!tags.boundary || geometry === "relation" && tags.type === "boundary") && !(traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway] || tags.waterway || tags.railway || tags.landuse || tags.natural || tags.building || tags.power);
     });
     defineRule("water", function isWater(tags) {
       return !!tags.waterway || tags.natural === "water" || tags.natural === "coastline" || tags.natural === "bay" || tags.landuse === "pond" || tags.landuse === "basin" || tags.landuse === "reservoir" || tags.landuse === "salt_pond";
@@ -44194,22 +46204,21 @@ ${content}</tr>
       var strings = Object.keys(tags);
       for (var i2 = 0; i2 < strings.length; i2++) {
         var s = strings[i2];
-        if (past_futures[s] || past_futures[tags[s]]) {
+        if (osmLifecyclePrefixes[s] || osmLifecyclePrefixes[tags[s]])
           return true;
-        }
       }
       return false;
     });
     defineRule("others", function isOther(tags, geometry) {
       return geometry === "line" || geometry === "area";
     });
-    features2.features = function() {
+    features.features = function() {
       return _rules;
     };
-    features2.keys = function() {
+    features.keys = function() {
       return _keys;
     };
-    features2.enabled = function(k) {
+    features.enabled = function(k) {
       if (!arguments.length) {
         return _keys.filter(function(k2) {
           return _rules[k2].enabled;
@@ -44217,7 +46226,7 @@ ${content}</tr>
       }
       return _rules[k] && _rules[k].enabled;
     };
-    features2.disabled = function(k) {
+    features.disabled = function(k) {
       if (!arguments.length) {
         return _keys.filter(function(k2) {
           return !_rules[k2].enabled;
@@ -44225,7 +46234,7 @@ ${content}</tr>
       }
       return _rules[k] && !_rules[k].enabled;
     };
-    features2.hidden = function(k) {
+    features.hidden = function(k) {
       if (!arguments.length) {
         return _keys.filter(function(k2) {
           return _rules[k2].hidden();
@@ -44233,7 +46242,7 @@ ${content}</tr>
       }
       return _rules[k] && _rules[k].hidden();
     };
-    features2.autoHidden = function(k) {
+    features.autoHidden = function(k) {
       if (!arguments.length) {
         return _keys.filter(function(k2) {
           return _rules[k2].autoHidden();
@@ -44241,13 +46250,13 @@ ${content}</tr>
       }
       return _rules[k] && _rules[k].autoHidden();
     };
-    features2.enable = function(k) {
+    features.enable = function(k) {
       if (_rules[k] && !_rules[k].enabled) {
         _rules[k].enable();
         update();
       }
     };
-    features2.enableAll = function() {
+    features.enableAll = function() {
       var didEnable = false;
       for (var k in _rules) {
         if (!_rules[k].enabled) {
@@ -44258,13 +46267,13 @@ ${content}</tr>
       if (didEnable)
         update();
     };
-    features2.disable = function(k) {
+    features.disable = function(k) {
       if (_rules[k] && _rules[k].enabled) {
         _rules[k].disable();
         update();
       }
     };
-    features2.disableAll = function() {
+    features.disableAll = function() {
       var didDisable = false;
       for (var k in _rules) {
         if (_rules[k].enabled) {
@@ -44275,7 +46284,7 @@ ${content}</tr>
       if (didDisable)
         update();
     };
-    features2.toggle = function(k) {
+    features.toggle = function(k) {
       if (_rules[k]) {
         (function(f2) {
           return f2.enabled ? f2.disable() : f2.enable();
@@ -44283,13 +46292,13 @@ ${content}</tr>
         update();
       }
     };
-    features2.resetStats = function() {
+    features.resetStats = function() {
       for (var i2 = 0; i2 < _keys.length; i2++) {
         _rules[_keys[i2]].count = 0;
       }
       dispatch10.call("change");
     };
-    features2.gatherStats = function(d, resolver, dimensions) {
+    features.gatherStats = function(d, resolver, dimensions) {
       var needsRedraw = false;
       var types = utilArrayGroupBy(d, "type");
       var entities = [].concat(types.relation || [], types.way || [], types.node || []);
@@ -44300,12 +46309,12 @@ ${content}</tr>
       _cullFactor = dimensions[0] * dimensions[1] / 1e6;
       for (i2 = 0; i2 < entities.length; i2++) {
         geometry = entities[i2].geometry(resolver);
-        matches = Object.keys(features2.getMatches(entities[i2], resolver, geometry));
+        matches = Object.keys(features.getMatches(entities[i2], resolver, geometry));
         for (j2 = 0; j2 < matches.length; j2++) {
           _rules[matches[j2]].count++;
         }
       }
-      currHidden = features2.hidden();
+      currHidden = features.hidden();
       if (currHidden !== _hidden) {
         _hidden = currHidden;
         needsRedraw = true;
@@ -44313,21 +46322,21 @@ ${content}</tr>
       }
       return needsRedraw;
     };
-    features2.stats = function() {
+    features.stats = function() {
       for (var i2 = 0; i2 < _keys.length; i2++) {
         _stats[_keys[i2]] = _rules[_keys[i2]].count;
       }
       return _stats;
     };
-    features2.clear = function(d) {
+    features.clear = function(d) {
       for (var i2 = 0; i2 < d.length; i2++) {
-        features2.clearEntity(d[i2]);
+        features.clearEntity(d[i2]);
       }
     };
-    features2.clearEntity = function(entity) {
+    features.clearEntity = function(entity) {
       delete _cache4[osmEntity.key(entity)];
     };
-    features2.reset = function() {
+    features.reset = function() {
       Array.from(_deferred2).forEach(function(handle) {
         window.cancelIdleCallback(handle);
         _deferred2.delete(handle);
@@ -44337,7 +46346,7 @@ ${content}</tr>
     function relationShouldBeChecked(relation) {
       return relation.tags.type === "boundary";
     }
-    features2.getMatches = function(entity, resolver, geometry) {
+    features.getMatches = function(entity, resolver, geometry) {
       if (geometry === "vertex" || geometry === "relation" && !relationShouldBeChecked(entity))
         return {};
       var ent = osmEntity.key(entity);
@@ -44352,8 +46361,9 @@ ${content}</tr>
             if (hasMatch)
               continue;
             if (entity.type === "way") {
-              var parents = features2.getParents(entity, resolver, geometry);
-              if (parents.length === 1 && parents[0].isMultipolygon() || parents.length > 0 && parents.every(function(parent) {
+              var parents = features.getParents(entity, resolver, geometry);
+              if (parents.length === 1 && parents[0].isMultipolygon() || // 2b. or belongs only to boundary relations
+              parents.length > 0 && parents.every(function(parent) {
                 return parent.tags.type === "boundary";
               })) {
                 var pkey = osmEntity.key(parents[0]);
@@ -44372,7 +46382,7 @@ ${content}</tr>
       }
       return _cache4[ent].matches;
     };
-    features2.getParents = function(entity, resolver, geometry) {
+    features.getParents = function(entity, resolver, geometry) {
       if (geometry === "point")
         return [];
       var ent = osmEntity.key(entity);
@@ -44390,7 +46400,7 @@ ${content}</tr>
       }
       return _cache4[ent].parents;
     };
-    features2.isHiddenPreset = function(preset, geometry) {
+    features.isHiddenPreset = function(preset, geometry) {
       if (!_hidden.length)
         return false;
       if (!preset.tags)
@@ -44406,36 +46416,36 @@ ${content}</tr>
       }
       return false;
     };
-    features2.isHiddenFeature = function(entity, resolver, geometry) {
+    features.isHiddenFeature = function(entity, resolver, geometry) {
       if (!_hidden.length)
         return false;
       if (!entity.version)
         return false;
       if (_forceVisible[entity.id])
         return false;
-      var matches = Object.keys(features2.getMatches(entity, resolver, geometry));
+      var matches = Object.keys(features.getMatches(entity, resolver, geometry));
       return matches.length && matches.every(function(k) {
-        return features2.hidden(k);
+        return features.hidden(k);
       });
     };
-    features2.isHiddenChild = function(entity, resolver, geometry) {
+    features.isHiddenChild = function(entity, resolver, geometry) {
       if (!_hidden.length)
         return false;
       if (!entity.version || geometry === "point")
         return false;
       if (_forceVisible[entity.id])
         return false;
-      var parents = features2.getParents(entity, resolver, geometry);
+      var parents = features.getParents(entity, resolver, geometry);
       if (!parents.length)
         return false;
       for (var i2 = 0; i2 < parents.length; i2++) {
-        if (!features2.isHidden(parents[i2], resolver, parents[i2].geometry(resolver))) {
+        if (!features.isHidden(parents[i2], resolver, parents[i2].geometry(resolver))) {
           return false;
         }
       }
       return true;
     };
-    features2.hasHiddenConnections = function(entity, resolver) {
+    features.hasHiddenConnections = function(entity, resolver) {
       if (!_hidden.length)
         return false;
       var childNodes, connections;
@@ -44444,36 +46454,36 @@ ${content}</tr>
         connections = [];
       } else {
         childNodes = entity.nodes ? resolver.childNodes(entity) : [];
-        connections = features2.getParents(entity, resolver, entity.geometry(resolver));
+        connections = features.getParents(entity, resolver, entity.geometry(resolver));
       }
       connections = childNodes.reduce(function(result, e) {
         return resolver.isShared(e) ? utilArrayUnion(result, resolver.parentWays(e)) : result;
       }, connections);
       return connections.some(function(e) {
-        return features2.isHidden(e, resolver, e.geometry(resolver));
+        return features.isHidden(e, resolver, e.geometry(resolver));
       });
     };
-    features2.isHidden = function(entity, resolver, geometry) {
+    features.isHidden = function(entity, resolver, geometry) {
       if (!_hidden.length)
         return false;
       if (!entity.version)
         return false;
-      var fn = geometry === "vertex" ? features2.isHiddenChild : features2.isHiddenFeature;
+      var fn = geometry === "vertex" ? features.isHiddenChild : features.isHiddenFeature;
       return fn(entity, resolver, geometry);
     };
-    features2.filter = function(d, resolver) {
+    features.filter = function(d, resolver) {
       if (!_hidden.length)
         return d;
       var result = [];
       for (var i2 = 0; i2 < d.length; i2++) {
         var entity = d[i2];
-        if (!features2.isHidden(entity, resolver, entity.geometry(resolver))) {
+        if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
           result.push(entity);
         }
       }
       return result;
     };
-    features2.forceVisible = function(entityIDs) {
+    features.forceVisible = function(entityIDs) {
       if (!arguments.length)
         return Object.keys(_forceVisible);
       _forceVisible = {};
@@ -44486,18 +46496,18 @@ ${content}</tr>
           }
         }
       }
-      return features2;
+      return features;
     };
-    features2.init = function() {
+    features.init = function() {
       var storage = corePreferences("disabled-features");
       if (storage) {
         var storageDisabled = storage.replace(/;/g, ",").split(",");
-        storageDisabled.forEach(features2.disable);
+        storageDisabled.forEach(features.disable);
       }
       var hash = utilStringQs(window.location.hash);
       if (hash.disable_features) {
         var hashDisabled = hash.disable_features.replace(/;/g, ",").split(",");
-        hashDisabled.forEach(features2.disable);
+        hashDisabled.forEach(features.disable);
       }
     };
     context.history().on("merge.features", function(newEntities) {
@@ -44509,12 +46519,12 @@ ${content}</tr>
         var entities = [].concat(types.relation || [], types.way || [], types.node || []);
         for (var i2 = 0; i2 < entities.length; i2++) {
           var geometry = entities[i2].geometry(graph);
-          features2.getMatches(entities[i2], graph, geometry);
+          features.getMatches(entities[i2], graph, geometry);
         }
       });
       _deferred2.add(handle);
     });
-    return features2;
+    return features;
   }
 
   // modules/svg/areas.js
@@ -44673,8 +46683,8 @@ ${content}</tr>
       var tags = entity.tags;
       var shouldCopyMultipolygonTags = !entity.hasInterestingTags();
       graph.parentRelations(entity).forEach(function(relation) {
-        var type3 = relation.tags.type;
-        if (type3 === "multipolygon" && shouldCopyMultipolygonTags || type3 === "boundary") {
+        var type2 = relation.tags.type;
+        if (type2 === "multipolygon" && shouldCopyMultipolygonTags || type2 === "boundary") {
           tags = Object.assign({}, relation.tags, tags);
         }
       });
@@ -44689,14 +46699,14 @@ ${content}</tr>
     }
     function getWaySegments() {
       var isActiveWay = way.nodes.indexOf(activeID) !== -1;
-      var features2 = { passive: [], active: [] };
+      var features = { passive: [], active: [] };
       var start2 = {};
       var end = {};
-      var node, type3;
+      var node, type2;
       for (var i2 = 0; i2 < way.nodes.length; i2++) {
         node = graph.entity(way.nodes[i2]);
-        type3 = svgPassiveVertex(node, graph, activeID);
-        end = { node, type: type3 };
+        type2 = svgPassiveVertex(node, graph, activeID);
+        end = { node, type: type2 };
         if (start2.type !== void 0) {
           if (start2.node.id === activeID || end.node.id === activeID) {
           } else if (isActiveWay && (start2.type === 2 || end.type === 2)) {
@@ -44709,9 +46719,9 @@ ${content}</tr>
         }
         start2 = end;
       }
-      return features2;
+      return features;
       function pushActive(start3, end2, index) {
-        features2.active.push({
+        features.active.push({
           type: "Feature",
           id: way.id + "-" + index + "-nope",
           properties: {
@@ -44728,7 +46738,7 @@ ${content}</tr>
         });
       }
       function pushPassive(start3, end2, index) {
-        features2.passive.push({
+        features.passive.push({
           type: "Feature",
           id: way.id + "-" + index,
           properties: {
@@ -44770,18 +46780,7 @@ ${content}</tr>
       "building:part",
       "indoor"
     ];
-    var statuses = [
-      "proposed",
-      "planned",
-      "construction",
-      "disused",
-      "abandoned",
-      "dismantled",
-      "razed",
-      "demolished",
-      "obliterated",
-      "intermittent"
-    ];
+    var statuses = Object.keys(osmLifecyclePrefixes);
     var secondaries = [
       "oneway",
       "bridge",
@@ -44929,6 +46928,12 @@ ${content}</tr>
 
   // modules/svg/tag_pattern.js
   var patterns = {
+    // tag - pattern name
+    // -or-
+    // tag - value - pattern name
+    // -or-
+    // tag - value - rules (optional tag-values, pattern name)
+    // (matches earlier rules first, so fallback should be last entry)
     amenity: {
       grave_yard: "cemetery",
       fountain: "water_standing"
@@ -44949,6 +46954,7 @@ ${content}</tr>
         { leaf_type: "needleleaved", pattern: "forest_needleleaved" },
         { leaf_type: "leafless", pattern: "forest_leafless" },
         { pattern: "forest" }
+        // same as 'leaf_type:mixed'
       ],
       grave_yard: "cemetery",
       grass: "grass",
@@ -44984,6 +46990,7 @@ ${content}</tr>
         { leaf_type: "needleleaved", pattern: "forest_needleleaved" },
         { leaf_type: "leafless", pattern: "forest_leafless" },
         { pattern: "forest" }
+        // same as 'leaf_type:mixed'
       ]
     },
     golf: {
@@ -45055,9 +47062,9 @@ ${content}</tr>
       var base = context.history().base();
       var data = { targets: [], nopes: [] };
       entities.forEach(function(way) {
-        var features2 = svgSegmentWay(way, graph, activeID);
-        data.targets.push.apply(data.targets, features2.passive);
-        data.nopes.push.apply(data.nopes, features2.active);
+        var features = svgSegmentWay(way, graph, activeID);
+        data.targets.push.apply(data.targets, features.passive);
+        data.nopes.push.apply(data.nopes, features.active);
       });
       var targetData = data.targets.filter(getPath);
       var targets = selection2.selectAll(".area.target-allowed").filter(function(d) {
@@ -45177,64 +47184,128 @@ ${content}</tr>
   // modules/svg/data.js
   var import_fast_json_stable_stringify = __toESM(require_fast_json_stable_stringify());
 
-  // node_modules/@tmcw/togeojson/dist/togeojson.es.js
-  function nodeVal(x) {
-    if (x && x.normalize) {
-      x.normalize();
-    }
-    return x && x.textContent || "";
+  // node_modules/@tmcw/togeojson/dist/togeojson.es.mjs
+  function $(element, tagName) {
+    return Array.from(element.getElementsByTagName(tagName));
   }
-  function get1(x, y) {
-    const n2 = x.getElementsByTagName(y);
-    return n2.length ? n2[0] : null;
+  function normalizeId(id2) {
+    return id2[0] === "#" ? id2 : `#${id2}`;
   }
-  function getLineStyle(extensions) {
-    const style = {};
-    if (extensions) {
-      const lineStyle = get1(extensions, "line");
-      if (lineStyle) {
-        const color2 = nodeVal(get1(lineStyle, "color")), opacity = parseFloat(nodeVal(get1(lineStyle, "opacity"))), width = parseFloat(nodeVal(get1(lineStyle, "width")));
-        if (color2)
-          style.stroke = color2;
-        if (!isNaN(opacity))
-          style["stroke-opacity"] = opacity;
-        if (!isNaN(width))
-          style["stroke-width"] = width * 96 / 25.4;
-      }
+  function $ns(element, tagName, ns) {
+    return Array.from(element.getElementsByTagNameNS(ns, tagName));
+  }
+  function nodeVal(node) {
+    node?.normalize();
+    return node && node.textContent || "";
+  }
+  function get1(node, tagName, callback) {
+    const n2 = node.getElementsByTagName(tagName);
+    const result = n2.length ? n2[0] : null;
+    if (result && callback)
+      callback(result);
+    return result;
+  }
+  function get3(node, tagName, callback) {
+    const properties = {};
+    if (!node)
+      return properties;
+    const n2 = node.getElementsByTagName(tagName);
+    const result = n2.length ? n2[0] : null;
+    if (result && callback) {
+      return callback(result, properties);
+    }
+    return properties;
+  }
+  function val1(node, tagName, callback) {
+    const val = nodeVal(get1(node, tagName));
+    if (val && callback)
+      return callback(val) || {};
+    return {};
+  }
+  function $num(node, tagName, callback) {
+    const val = parseFloat(nodeVal(get1(node, tagName)));
+    if (isNaN(val))
+      return void 0;
+    if (val && callback)
+      return callback(val) || {};
+    return {};
+  }
+  function num1(node, tagName, callback) {
+    const val = parseFloat(nodeVal(get1(node, tagName)));
+    if (isNaN(val))
+      return void 0;
+    if (val && callback)
+      callback(val);
+    return val;
+  }
+  function getMulti(node, propertyNames) {
+    const properties = {};
+    for (const property of propertyNames) {
+      val1(node, property, (val) => {
+        properties[property] = val;
+      });
     }
-    return style;
+    return properties;
+  }
+  function isElement(node) {
+    return node?.nodeType === 1;
+  }
+  function getLineStyle(node) {
+    return get3(node, "line", (lineStyle) => {
+      const val = Object.assign({}, val1(lineStyle, "color", (color2) => {
+        return { stroke: `#${color2}` };
+      }), $num(lineStyle, "opacity", (opacity) => {
+        return { "stroke-opacity": opacity };
+      }), $num(lineStyle, "width", (width) => {
+        return { "stroke-width": width * 96 / 25.4 };
+      }));
+      return val;
+    });
   }
   function getExtensions(node) {
     let values = [];
-    if (node !== null) {
-      for (let i2 = 0; i2 < node.childNodes.length; i2++) {
-        const child = node.childNodes[i2];
-        if (child.nodeType !== 1)
-          continue;
-        const name2 = ["heart", "gpxtpx:hr", "hr"].includes(child.nodeName) ? "heart" : child.nodeName;
-        if (name2 === "gpxtpx:TrackPointExtension") {
-          values = values.concat(getExtensions(child));
-        } else {
-          const val = nodeVal(child);
-          values.push([name2, isNaN(val) ? val : parseFloat(val)]);
-        }
+    if (node === null)
+      return values;
+    for (const child of Array.from(node.childNodes)) {
+      if (!isElement(child))
+        continue;
+      const name = abbreviateName(child.nodeName);
+      if (name === "gpxtpx:TrackPointExtension") {
+        values = values.concat(getExtensions(child));
+      } else {
+        const val = nodeVal(child);
+        values.push([name, parseNumeric(val)]);
       }
     }
     return values;
   }
-  function getMulti(x, ys) {
-    const o = {};
-    let n2;
-    let k;
-    for (k = 0; k < ys.length; k++) {
-      n2 = get1(x, ys[k]);
-      if (n2)
-        o[ys[k]] = nodeVal(n2);
+  function abbreviateName(name) {
+    return ["heart", "gpxtpx:hr", "hr"].includes(name) ? "heart" : name;
+  }
+  function parseNumeric(val) {
+    const num = parseFloat(val);
+    return isNaN(num) ? val : num;
+  }
+  function coordPair$1(node) {
+    const ll = [
+      parseFloat(node.getAttribute("lon") || ""),
+      parseFloat(node.getAttribute("lat") || "")
+    ];
+    if (isNaN(ll[0]) || isNaN(ll[1])) {
+      return null;
     }
-    return o;
+    num1(node, "ele", (val) => {
+      ll.push(val);
+    });
+    const time = get1(node, "time");
+    return {
+      coordinates: ll,
+      time: time ? nodeVal(time) : null,
+      extendedValues: getExtensions(get1(node, "extensions"))
+    };
   }
-  function getProperties$1(node) {
-    const prop = getMulti(node, [
+  function extractProperties(node) {
+    const properties = getMulti(node, [
       "name",
       "cmt",
       "desc",
@@ -45242,87 +47313,67 @@ ${content}</tr>
       "time",
       "keywords"
     ]);
-    const extensions = node.getElementsByTagNameNS("http://www.garmin.com/xmlschemas/GpxExtensions/v3", "*");
-    for (let i2 = 0; i2 < extensions.length; i2++) {
-      const extension = extensions[i2];
-      if (extension.parentNode.parentNode === node) {
-        prop[extension.tagName.replace(":", "_")] = nodeVal(extension);
+    const extensions = Array.from(node.getElementsByTagNameNS("http://www.garmin.com/xmlschemas/GpxExtensions/v3", "*"));
+    for (const child of extensions) {
+      if (child.parentNode?.parentNode === node) {
+        properties[child.tagName.replace(":", "_")] = nodeVal(child);
       }
     }
-    const links = node.getElementsByTagName("link");
-    if (links.length)
-      prop.links = [];
-    for (let i2 = 0; i2 < links.length; i2++) {
-      prop.links.push(Object.assign({ href: links[i2].getAttribute("href") }, getMulti(links[i2], ["text", "type"])));
-    }
-    return prop;
-  }
-  function coordPair$1(x) {
-    const ll = [
-      parseFloat(x.getAttribute("lon")),
-      parseFloat(x.getAttribute("lat"))
-    ];
-    const ele = get1(x, "ele");
-    const time = get1(x, "time");
-    if (ele) {
-      const e = parseFloat(nodeVal(ele));
-      if (!isNaN(e)) {
-        ll.push(e);
-      }
+    const links = $(node, "link");
+    if (links.length) {
+      properties.links = links.map((link2) => Object.assign({ href: link2.getAttribute("href") }, getMulti(link2, ["text", "type"])));
     }
-    return {
-      coordinates: ll,
-      time: time ? nodeVal(time) : null,
-      extendedValues: getExtensions(get1(x, "extensions"))
-    };
-  }
-  function getRoute(node) {
-    const line = getPoints$1(node, "rtept");
-    if (!line)
-      return;
-    return {
-      type: "Feature",
-      properties: Object.assign(getProperties$1(node), getLineStyle(get1(node, "extensions")), { _gpxType: "rte" }),
-      geometry: {
-        type: "LineString",
-        coordinates: line.line
-      }
-    };
+    return properties;
   }
   function getPoints$1(node, pointname) {
-    const pts = node.getElementsByTagName(pointname);
-    if (pts.length < 2)
-      return;
+    const pts = $(node, pointname);
     const line = [];
     const times = [];
     const extendedValues = {};
     for (let i2 = 0; i2 < pts.length; i2++) {
       const c = coordPair$1(pts[i2]);
+      if (!c) {
+        continue;
+      }
       line.push(c.coordinates);
       if (c.time)
         times.push(c.time);
-      for (let j2 = 0; j2 < c.extendedValues.length; j2++) {
-        const [name2, val] = c.extendedValues[j2];
-        const plural = name2 === "heart" ? name2 : name2.replace("gpxtpx:", "") + "s";
+      for (const [name, val] of c.extendedValues) {
+        const plural = name === "heart" ? name : name.replace("gpxtpx:", "") + "s";
         if (!extendedValues[plural]) {
           extendedValues[plural] = Array(pts.length).fill(null);
         }
         extendedValues[plural][i2] = val;
       }
     }
+    if (line.length < 2)
+      return;
     return {
       line,
       times,
       extendedValues
     };
   }
+  function getRoute(node) {
+    const line = getPoints$1(node, "rtept");
+    if (!line)
+      return;
+    return {
+      type: "Feature",
+      properties: Object.assign({ _gpxType: "rte" }, extractProperties(node), getLineStyle(get1(node, "extensions"))),
+      geometry: {
+        type: "LineString",
+        coordinates: line.line
+      }
+    };
+  }
   function getTrack(node) {
-    const segments = node.getElementsByTagName("trkseg");
+    const segments = $(node, "trkseg");
     const track = [];
     const times = [];
     const extractedLines = [];
-    for (let i2 = 0; i2 < segments.length; i2++) {
-      const line = getPoints$1(segments[i2], "trkpt");
+    for (const segment of segments) {
+      const line = getPoints$1(segment, "trkpt");
       if (line) {
         extractedLines.push(line);
         if (line.times && line.times.length)
@@ -45330,27 +47381,29 @@ ${content}</tr>
       }
     }
     if (extractedLines.length === 0)
-      return;
+      return null;
     const multi = extractedLines.length > 1;
-    const properties = Object.assign(getProperties$1(node), getLineStyle(get1(node, "extensions")), { _gpxType: "trk" }, times.length ? {
+    const properties = Object.assign({ _gpxType: "trk" }, extractProperties(node), getLineStyle(get1(node, "extensions")), times.length ? {
       coordinateProperties: {
         times: multi ? times : times[0]
       }
     } : {});
-    for (let i2 = 0; i2 < extractedLines.length; i2++) {
-      const line = extractedLines[i2];
+    for (const line of extractedLines) {
       track.push(line.line);
-      for (const [name2, val] of Object.entries(line.extendedValues)) {
-        if (!properties.coordinateProperties) {
-          properties.coordinateProperties = {};
-        }
-        const props = properties.coordinateProperties;
+      if (!properties.coordinateProperties) {
+        properties.coordinateProperties = {};
+      }
+      const props = properties.coordinateProperties;
+      const entries = Object.entries(line.extendedValues);
+      for (let i2 = 0; i2 < entries.length; i2++) {
+        const [name, val] = entries[i2];
         if (multi) {
-          if (!props[name2])
-            props[name2] = extractedLines.map((line2) => new Array(line2.line.length).fill(null));
-          props[name2][i2] = val;
+          if (!props[name]) {
+            props[name] = extractedLines.map((line2) => new Array(line2.line.length).fill(null));
+          }
+          props[name][i2] = val;
         } else {
-          props[name2] = val;
+          props[name] = val;
         }
       }
     }
@@ -45367,326 +47420,355 @@ ${content}</tr>
     };
   }
   function getPoint(node) {
+    const properties = Object.assign(extractProperties(node), getMulti(node, ["sym"]));
+    const pair2 = coordPair$1(node);
+    if (!pair2)
+      return null;
     return {
       type: "Feature",
-      properties: Object.assign(getProperties$1(node), getMulti(node, ["sym"])),
+      properties,
       geometry: {
         type: "Point",
-        coordinates: coordPair$1(node).coordinates
+        coordinates: pair2.coordinates
       }
     };
   }
-  function* gpxGen(doc) {
-    const tracks = doc.getElementsByTagName("trk");
-    const routes = doc.getElementsByTagName("rte");
-    const waypoints = doc.getElementsByTagName("wpt");
-    for (let i2 = 0; i2 < tracks.length; i2++) {
-      const feature3 = getTrack(tracks[i2]);
+  function* gpxGen(node) {
+    for (const track of $(node, "trk")) {
+      const feature3 = getTrack(track);
       if (feature3)
         yield feature3;
     }
-    for (let i2 = 0; i2 < routes.length; i2++) {
-      const feature3 = getRoute(routes[i2]);
+    for (const route of $(node, "rte")) {
+      const feature3 = getRoute(route);
       if (feature3)
         yield feature3;
     }
-    for (let i2 = 0; i2 < waypoints.length; i2++) {
-      yield getPoint(waypoints[i2]);
+    for (const waypoint of $(node, "wpt")) {
+      const point2 = getPoint(waypoint);
+      if (point2)
+        yield point2;
     }
   }
-  function gpx(doc) {
+  function gpx(node) {
     return {
       type: "FeatureCollection",
-      features: Array.from(gpxGen(doc))
+      features: Array.from(gpxGen(node))
     };
   }
+  function fixColor(v, prefix) {
+    const properties = {};
+    const colorProp = prefix == "stroke" || prefix === "fill" ? prefix : prefix + "-color";
+    if (v[0] === "#") {
+      v = v.substring(1);
+    }
+    if (v.length === 6 || v.length === 3) {
+      properties[colorProp] = "#" + v;
+    } else if (v.length === 8) {
+      properties[prefix + "-opacity"] = parseInt(v.substring(0, 2), 16) / 255;
+      properties[colorProp] = "#" + v.substring(6, 8) + v.substring(4, 6) + v.substring(2, 4);
+    }
+    return properties;
+  }
+  function numericProperty(node, source, target) {
+    const properties = {};
+    num1(node, source, (val) => {
+      properties[target] = val;
+    });
+    return properties;
+  }
+  function getColor(node, output) {
+    return get3(node, "color", (elem) => fixColor(nodeVal(elem), output));
+  }
+  function extractIcon(node) {
+    return get3(node, "IconStyle", (iconStyle) => {
+      return Object.assign(getColor(iconStyle, "icon"), numericProperty(iconStyle, "scale", "icon-scale"), numericProperty(iconStyle, "heading", "icon-heading"), get3(iconStyle, "hotSpot", (hotspot) => {
+        const left = parseFloat(hotspot.getAttribute("x") || "");
+        const top = parseFloat(hotspot.getAttribute("y") || "");
+        const xunits = hotspot.getAttribute("xunits") || "";
+        const yunits = hotspot.getAttribute("yunits") || "";
+        if (!isNaN(left) && !isNaN(top))
+          return {
+            "icon-offset": [left, top],
+            "icon-offset-units": [xunits, yunits]
+          };
+        return {};
+      }), get3(iconStyle, "Icon", (icon2, properties) => {
+        val1(icon2, "href", (href) => {
+          properties.icon = href;
+        });
+        return properties;
+      }));
+    });
+  }
+  function extractLabel(node) {
+    return get3(node, "LabelStyle", (labelStyle) => {
+      return Object.assign(getColor(labelStyle, "label"), numericProperty(labelStyle, "scale", "label-scale"));
+    });
+  }
+  function extractLine(node) {
+    return get3(node, "LineStyle", (lineStyle) => {
+      return Object.assign(getColor(lineStyle, "stroke"), numericProperty(lineStyle, "width", "stroke-width"));
+    });
+  }
+  function extractPoly(node) {
+    return get3(node, "PolyStyle", (polyStyle, properties) => {
+      return Object.assign(properties, get3(polyStyle, "color", (elem) => fixColor(nodeVal(elem), "fill")), val1(polyStyle, "fill", (fill) => {
+        if (fill === "0")
+          return { "fill-opacity": 0 };
+      }), val1(polyStyle, "outline", (outline) => {
+        if (outline === "0")
+          return { "stroke-opacity": 0 };
+      }));
+    });
+  }
+  function extractStyle(node) {
+    return Object.assign({}, extractPoly(node), extractLine(node), extractLabel(node), extractIcon(node));
+  }
   var removeSpace = /\s*/g;
   var trimSpace = /^\s*|\s*$/g;
   var splitSpace = /\s+/;
-  function okhash(x) {
-    if (!x || !x.length)
-      return 0;
-    let h = 0;
-    for (let i2 = 0; i2 < x.length; i2++) {
-      h = (h << 5) - h + x.charCodeAt(i2) | 0;
-    }
-    return h;
-  }
-  function coord1(v) {
-    return v.replace(removeSpace, "").split(",").map(parseFloat);
+  function coord1(value) {
+    return value.replace(removeSpace, "").split(",").map(parseFloat).filter((num) => !isNaN(num)).slice(0, 3);
   }
-  function coord(v) {
-    return v.replace(trimSpace, "").split(splitSpace).map(coord1);
+  function coord(value) {
+    return value.replace(trimSpace, "").split(splitSpace).map(coord1).filter((coord2) => {
+      return coord2.length >= 2;
+    });
   }
-  function xml2str(node) {
-    if (node.xml !== void 0)
-      return node.xml;
-    if (node.tagName) {
-      let output = node.tagName;
-      for (let i2 = 0; i2 < node.attributes.length; i2++) {
-        output += node.attributes[i2].name + node.attributes[i2].value;
-      }
-      for (let i2 = 0; i2 < node.childNodes.length; i2++) {
-        output += xml2str(node.childNodes[i2]);
-      }
-      return output;
+  function gxCoords(node) {
+    let elems = $(node, "coord");
+    if (elems.length === 0) {
+      elems = $ns(node, "coord", "*");
     }
-    if (node.nodeName === "#text") {
-      return (node.nodeValue || node.value || "").trim();
-    }
-    if (node.nodeName === "#cdata-section") {
-      return node.nodeValue;
+    const coordinates = elems.map((elem) => {
+      return nodeVal(elem).split(" ").map(parseFloat);
+    });
+    if (coordinates.length === 0) {
+      return null;
     }
-    return "";
-  }
-  var geotypes = ["Polygon", "LineString", "Point", "Track", "gx:Track"];
-  function kmlColor(properties, elem, prefix) {
-    let v = nodeVal(get1(elem, "color")) || "";
-    const colorProp = prefix == "stroke" || prefix === "fill" ? prefix : prefix + "-color";
-    if (v.substr(0, 1) === "#") {
-      v = v.substr(1);
+    return {
+      geometry: coordinates.length > 2 ? {
+        type: "LineString",
+        coordinates
+      } : {
+        type: "Point",
+        coordinates: coordinates[0]
+      },
+      times: $(node, "when").map((elem) => nodeVal(elem))
+    };
+  }
+  function fixRing(ring) {
+    if (ring.length === 0)
+      return ring;
+    const first = ring[0];
+    const last = ring[ring.length - 1];
+    let equal = true;
+    for (let i2 = 0; i2 < Math.max(first.length, last.length); i2++) {
+      if (first[i2] !== last[i2]) {
+        equal = false;
+        break;
+      }
     }
-    if (v.length === 6 || v.length === 3) {
-      properties[colorProp] = v;
-    } else if (v.length === 8) {
-      properties[prefix + "-opacity"] = parseInt(v.substr(0, 2), 16) / 255;
-      properties[colorProp] = "#" + v.substr(6, 2) + v.substr(4, 2) + v.substr(2, 2);
+    if (!equal) {
+      return ring.concat([ring[0]]);
     }
+    return ring;
   }
-  function numericProperty(properties, elem, source, target) {
-    const val = parseFloat(nodeVal(get1(elem, source)));
-    if (!isNaN(val))
-      properties[target] = val;
-  }
-  function gxCoords(root3) {
-    let elems = root3.getElementsByTagName("coord");
-    const coords = [];
-    const times = [];
-    if (elems.length === 0)
-      elems = root3.getElementsByTagName("gx:coord");
-    for (let i2 = 0; i2 < elems.length; i2++) {
-      coords.push(nodeVal(elems[i2]).split(" ").map(parseFloat));
-    }
-    const timeElems = root3.getElementsByTagName("when");
-    for (let j2 = 0; j2 < timeElems.length; j2++)
-      times.push(nodeVal(timeElems[j2]));
-    return {
-      coords,
-      times
-    };
+  var GEO_TYPES = [
+    "Polygon",
+    "LineString",
+    "Point",
+    "Track",
+    "gx:Track"
+  ];
+  function getCoordinates(node) {
+    return nodeVal(get1(node, "coordinates"));
   }
-  function getGeometry(root3) {
-    let geomNode;
-    let geomNodes;
-    let i2;
-    let j2;
-    let k;
-    const geoms = [];
+  function getGeometry(node) {
+    const geometries = [];
     const coordTimes = [];
-    if (get1(root3, "MultiGeometry")) {
-      return getGeometry(get1(root3, "MultiGeometry"));
-    }
-    if (get1(root3, "MultiTrack")) {
-      return getGeometry(get1(root3, "MultiTrack"));
-    }
-    if (get1(root3, "gx:MultiTrack")) {
-      return getGeometry(get1(root3, "gx:MultiTrack"));
-    }
-    for (i2 = 0; i2 < geotypes.length; i2++) {
-      geomNodes = root3.getElementsByTagName(geotypes[i2]);
-      if (geomNodes) {
-        for (j2 = 0; j2 < geomNodes.length; j2++) {
-          geomNode = geomNodes[j2];
-          if (geotypes[i2] === "Point") {
-            geoms.push({
-              type: "Point",
-              coordinates: coord1(nodeVal(get1(geomNode, "coordinates")))
-            });
-          } else if (geotypes[i2] === "LineString") {
-            geoms.push({
-              type: "LineString",
-              coordinates: coord(nodeVal(get1(geomNode, "coordinates")))
-            });
-          } else if (geotypes[i2] === "Polygon") {
-            const rings = geomNode.getElementsByTagName("LinearRing"), coords = [];
-            for (k = 0; k < rings.length; k++) {
-              coords.push(coord(nodeVal(get1(rings[k], "coordinates"))));
+    for (const t of ["MultiGeometry", "MultiTrack", "gx:MultiTrack"]) {
+      const elem = get1(node, t);
+      if (elem) {
+        return getGeometry(elem);
+      }
+    }
+    for (const geoType of GEO_TYPES) {
+      for (const geomNode of $(node, geoType)) {
+        switch (geoType) {
+          case "Point": {
+            const coordinates = coord1(getCoordinates(geomNode));
+            if (coordinates.length >= 2) {
+              geometries.push({
+                type: "Point",
+                coordinates
+              });
             }
-            geoms.push({
-              type: "Polygon",
-              coordinates: coords
-            });
-          } else if (geotypes[i2] === "Track" || geotypes[i2] === "gx:Track") {
-            const track = gxCoords(geomNode);
-            geoms.push({
-              type: "LineString",
-              coordinates: track.coords
-            });
-            if (track.times.length)
-              coordTimes.push(track.times);
+            break;
+          }
+          case "LineString": {
+            const coordinates = coord(getCoordinates(geomNode));
+            if (coordinates.length >= 2) {
+              geometries.push({
+                type: "LineString",
+                coordinates
+              });
+            }
+            break;
+          }
+          case "Polygon": {
+            const coords = [];
+            for (const linearRing of $(geomNode, "LinearRing")) {
+              const ring = fixRing(coord(getCoordinates(linearRing)));
+              if (ring.length >= 4) {
+                coords.push(ring);
+              }
+            }
+            if (coords.length) {
+              geometries.push({
+                type: "Polygon",
+                coordinates: coords
+              });
+            }
+            break;
+          }
+          case "Track":
+          case "gx:Track": {
+            const gx = gxCoords(geomNode);
+            if (!gx)
+              break;
+            const { times, geometry } = gx;
+            geometries.push(geometry);
+            if (times.length)
+              coordTimes.push(times);
+            break;
           }
         }
       }
     }
     return {
-      geoms,
+      geometries,
       coordTimes
     };
   }
-  function getPlacemark(root3, styleIndex, styleMapIndex, styleByHash) {
-    const geomsAndTimes = getGeometry(root3);
-    let i2;
-    const properties = {};
-    const name2 = nodeVal(get1(root3, "name"));
-    const address = nodeVal(get1(root3, "address"));
-    let styleUrl = nodeVal(get1(root3, "styleUrl"));
-    const description2 = nodeVal(get1(root3, "description"));
-    const timeSpan = get1(root3, "TimeSpan");
-    const timeStamp = get1(root3, "TimeStamp");
-    const extendedData = get1(root3, "ExtendedData");
-    let iconStyle = get1(root3, "IconStyle");
-    let labelStyle = get1(root3, "LabelStyle");
-    let lineStyle = get1(root3, "LineStyle");
-    let polyStyle = get1(root3, "PolyStyle");
-    const visibility = get1(root3, "visibility");
-    if (name2)
-      properties.name = name2;
-    if (address)
-      properties.address = address;
-    if (styleUrl) {
-      if (styleUrl[0] !== "#") {
-        styleUrl = "#" + styleUrl;
-      }
-      properties.styleUrl = styleUrl;
-      if (styleIndex[styleUrl]) {
-        properties.styleHash = styleIndex[styleUrl];
-      }
-      if (styleMapIndex[styleUrl]) {
-        properties.styleMapHash = styleMapIndex[styleUrl];
-        properties.styleHash = styleIndex[styleMapIndex[styleUrl].normal];
-      }
-      const style = styleByHash[properties.styleHash];
-      if (style) {
-        if (!iconStyle)
-          iconStyle = get1(style, "IconStyle");
-        if (!labelStyle)
-          labelStyle = get1(style, "LabelStyle");
-        if (!lineStyle)
-          lineStyle = get1(style, "LineStyle");
-        if (!polyStyle)
-          polyStyle = get1(style, "PolyStyle");
-      }
-    }
-    if (description2)
-      properties.description = description2;
-    if (timeSpan) {
-      const begin = nodeVal(get1(timeSpan, "begin"));
-      const end = nodeVal(get1(timeSpan, "end"));
-      properties.timespan = { begin, end };
-    }
-    if (timeStamp) {
-      properties.timestamp = nodeVal(get1(timeStamp, "when"));
-    }
-    if (iconStyle) {
-      kmlColor(properties, iconStyle, "icon");
-      numericProperty(properties, iconStyle, "scale", "icon-scale");
-      numericProperty(properties, iconStyle, "heading", "icon-heading");
-      const hotspot = get1(iconStyle, "hotSpot");
-      if (hotspot) {
-        const left = parseFloat(hotspot.getAttribute("x"));
-        const top = parseFloat(hotspot.getAttribute("y"));
-        if (!isNaN(left) && !isNaN(top))
-          properties["icon-offset"] = [left, top];
+  function extractExtendedData(node) {
+    return get3(node, "ExtendedData", (extendedData, properties) => {
+      for (const data of $(extendedData, "Data")) {
+        properties[data.getAttribute("name") || ""] = nodeVal(get1(data, "value"));
       }
-      const icon2 = get1(iconStyle, "Icon");
-      if (icon2) {
-        const href = nodeVal(get1(icon2, "href"));
-        if (href)
-          properties.icon = href;
+      for (const simpleData of $(extendedData, "SimpleData")) {
+        properties[simpleData.getAttribute("name") || ""] = nodeVal(simpleData);
       }
-    }
-    if (labelStyle) {
-      kmlColor(properties, labelStyle, "label");
-      numericProperty(properties, labelStyle, "scale", "label-scale");
-    }
-    if (lineStyle) {
-      kmlColor(properties, lineStyle, "stroke");
-      numericProperty(properties, lineStyle, "width", "stroke-width");
-    }
-    if (polyStyle) {
-      kmlColor(properties, polyStyle, "fill");
-      const fill = nodeVal(get1(polyStyle, "fill"));
-      const outline = nodeVal(get1(polyStyle, "outline"));
-      if (fill)
-        properties["fill-opacity"] = fill === "1" ? properties["fill-opacity"] || 1 : 0;
-      if (outline)
-        properties["stroke-opacity"] = outline === "1" ? properties["stroke-opacity"] || 1 : 0;
-    }
-    if (extendedData) {
-      const datas = extendedData.getElementsByTagName("Data"), simpleDatas = extendedData.getElementsByTagName("SimpleData");
-      for (i2 = 0; i2 < datas.length; i2++) {
-        properties[datas[i2].getAttribute("name")] = nodeVal(get1(datas[i2], "value"));
+      return properties;
+    });
+  }
+  function geometryListToGeometry(geometries) {
+    return geometries.length === 0 ? null : geometries.length === 1 ? geometries[0] : {
+      type: "GeometryCollection",
+      geometries
+    };
+  }
+  function extractTimeSpan(node) {
+    return get3(node, "TimeSpan", (timeSpan) => {
+      return {
+        timespan: {
+          begin: nodeVal(get1(timeSpan, "begin")),
+          end: nodeVal(get1(timeSpan, "end"))
+        }
+      };
+    });
+  }
+  function extractTimeStamp(node) {
+    return get3(node, "TimeStamp", (timeStamp) => {
+      return { timestamp: nodeVal(get1(timeStamp, "when")) };
+    });
+  }
+  function extractCascadedStyle(node, styleMap) {
+    return val1(node, "styleUrl", (styleUrl) => {
+      styleUrl = normalizeId(styleUrl);
+      if (styleMap[styleUrl]) {
+        return Object.assign({ styleUrl }, styleMap[styleUrl]);
       }
-      for (i2 = 0; i2 < simpleDatas.length; i2++) {
-        properties[simpleDatas[i2].getAttribute("name")] = nodeVal(simpleDatas[i2]);
+      return { styleUrl };
+    });
+  }
+  function getMaybeHTMLDescription(node) {
+    const descriptionNode = get1(node, "description");
+    for (const c of Array.from(descriptionNode?.childNodes || [])) {
+      if (c.nodeType === 4) {
+        return {
+          description: {
+            "@type": "html",
+            value: nodeVal(c)
+          }
+        };
       }
     }
-    if (visibility) {
-      properties.visibility = nodeVal(visibility);
-    }
-    if (geomsAndTimes.coordTimes.length) {
-      properties.coordinateProperties = {
-        times: geomsAndTimes.coordTimes.length === 1 ? geomsAndTimes.coordTimes[0] : geomsAndTimes.coordTimes
-      };
-    }
+    return {};
+  }
+  function getPlacemark(node, styleMap) {
+    const { coordTimes, geometries } = getGeometry(node);
     const feature3 = {
       type: "Feature",
-      geometry: geomsAndTimes.geoms.length === 0 ? null : geomsAndTimes.geoms.length === 1 ? geomsAndTimes.geoms[0] : {
-        type: "GeometryCollection",
-        geometries: geomsAndTimes.geoms
-      },
-      properties
-    };
-    if (root3.getAttribute("id"))
-      feature3.id = root3.getAttribute("id");
+      geometry: geometryListToGeometry(geometries),
+      properties: Object.assign(getMulti(node, [
+        "name",
+        "address",
+        "visibility",
+        "open",
+        "phoneNumber",
+        "description"
+      ]), getMaybeHTMLDescription(node), extractCascadedStyle(node, styleMap), extractStyle(node), extractExtendedData(node), extractTimeSpan(node), extractTimeStamp(node), coordTimes.length ? {
+        coordinateProperties: {
+          times: coordTimes.length === 1 ? coordTimes[0] : coordTimes
+        }
+      } : {})
+    };
+    if (feature3.properties?.visibility !== void 0) {
+      feature3.properties.visibility = feature3.properties.visibility !== "0";
+    }
+    const id2 = node.getAttribute("id");
+    if (id2 !== null && id2 !== "")
+      feature3.id = id2;
     return feature3;
   }
-  function* kmlGen(doc) {
-    const styleIndex = {};
-    const styleByHash = {};
-    const styleMapIndex = {};
-    const placemarks = doc.getElementsByTagName("Placemark");
-    const styles = doc.getElementsByTagName("Style");
-    const styleMaps = doc.getElementsByTagName("StyleMap");
-    for (let k = 0; k < styles.length; k++) {
-      const style = styles[k];
-      const hash = okhash(xml2str(style)).toString(16);
-      let id2 = style.getAttribute("id");
-      if (!id2 && style.parentNode.tagName.replace("gx:", "") === "CascadingStyle") {
-        id2 = style.parentNode.getAttribute("kml:id") || style.parentNode.getAttribute("id");
-      }
-      styleIndex["#" + id2] = hash;
-      styleByHash[hash] = style;
-    }
-    for (let l = 0; l < styleMaps.length; l++) {
-      styleIndex["#" + styleMaps[l].getAttribute("id")] = okhash(xml2str(styleMaps[l])).toString(16);
-      const pairs = styleMaps[l].getElementsByTagName("Pair");
-      const pairsMap = {};
-      for (let m = 0; m < pairs.length; m++) {
-        pairsMap[nodeVal(get1(pairs[m], "key"))] = nodeVal(get1(pairs[m], "styleUrl"));
-      }
-      styleMapIndex["#" + styleMaps[l].getAttribute("id")] = pairsMap;
-    }
-    for (let j2 = 0; j2 < placemarks.length; j2++) {
-      const feature3 = getPlacemark(placemarks[j2], styleIndex, styleMapIndex, styleByHash);
+  function getStyleId(style) {
+    let id2 = style.getAttribute("id");
+    const parentNode = style.parentNode;
+    if (!id2 && isElement(parentNode) && parentNode.localName === "CascadingStyle") {
+      id2 = parentNode.getAttribute("kml:id") || parentNode.getAttribute("id");
+    }
+    return normalizeId(id2 || "");
+  }
+  function buildStyleMap(node) {
+    const styleMap = {};
+    for (const style of $(node, "Style")) {
+      styleMap[getStyleId(style)] = extractStyle(style);
+    }
+    for (const map2 of $(node, "StyleMap")) {
+      const id2 = normalizeId(map2.getAttribute("id") || "");
+      val1(map2, "styleUrl", (styleUrl) => {
+        styleUrl = normalizeId(styleUrl);
+        if (styleMap[styleUrl]) {
+          styleMap[id2] = styleMap[styleUrl];
+        }
+      });
+    }
+    return styleMap;
+  }
+  function* kmlGen(node) {
+    const styleMap = buildStyleMap(node);
+    for (const placemark of $(node, "Placemark")) {
+      const feature3 = getPlacemark(placemark, styleMap);
       if (feature3)
         yield feature3;
     }
   }
-  function kml(doc) {
+  function kml(node) {
     return {
       type: "FeatureCollection",
-      features: Array.from(kmlGen(doc))
+      features: Array.from(kmlGen(node))
     };
   }
 
@@ -46026,12 +48108,12 @@ ${content}</tr>
       return _src || "";
     };
     drawData.fitZoom = function() {
-      var features2 = getFeatures(_geojson);
-      if (!features2.length)
+      var features = getFeatures(_geojson);
+      if (!features.length)
         return;
       var map2 = context.map();
       var viewport = map2.trimmedExtent().polygon();
-      var coords = features2.reduce(function(coords2, feature3) {
+      var coords = features.reduce(function(coords2, feature3) {
         var geom = feature3.geometry;
         if (!geom)
           return coords2;
@@ -46097,8 +48179,8 @@ ${content}</tr>
       const extent = context.map().extent();
       _mainFileFetcher.get("imagery").then((d) => {
         const hits = showImagery && d.query.bbox(extent.rectangle(), true) || [];
-        const features2 = hits.map((d2) => d2.features[d2.id]);
-        let imagery = layer.selectAll("path.debug-imagery").data(features2);
+        const features = hits.map((d2) => d2.features[d2.id]);
+        let imagery = layer.selectAll("path.debug-imagery").data(features);
         imagery.exit().remove();
         imagery.enter().append("path").attr("class", "debug-imagery debug orange");
       }).catch(() => {
@@ -46107,18 +48189,18 @@ ${content}</tr>
       let dataDownloaded = [];
       if (osm && showDownloaded) {
         const rtree = osm.caches("get").tile.rtree;
-        dataDownloaded = rtree.all().map((bbox) => {
+        dataDownloaded = rtree.all().map((bbox2) => {
           return {
             type: "Feature",
-            properties: { id: bbox.id },
+            properties: { id: bbox2.id },
             geometry: {
               type: "Polygon",
               coordinates: [[
-                [bbox.minX, bbox.minY],
-                [bbox.minX, bbox.maxY],
-                [bbox.maxX, bbox.maxY],
-                [bbox.maxX, bbox.minY],
-                [bbox.minX, bbox.minY]
+                [bbox2.minX, bbox2.minY],
+                [bbox2.minX, bbox2.maxY],
+                [bbox2.maxX, bbox2.maxY],
+                [bbox2.maxX, bbox2.minY],
+                [bbox2.minX, bbox2.minY]
               ]]
             }
           };
@@ -46152,8 +48234,8 @@ ${content}</tr>
     function drawDefs(selection2) {
       _defsSelection = selection2.append("defs");
       _defsSelection.append("marker").attr("id", "ideditor-oneway-marker").attr("viewBox", "0 0 10 5").attr("refX", 2.5).attr("refY", 2.5).attr("markerWidth", 2).attr("markerHeight", 2).attr("markerUnits", "strokeWidth").attr("orient", "auto").append("path").attr("class", "oneway-marker-path").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").attr("stroke", "none").attr("fill", "#000").attr("opacity", "0.75");
-      function addSidedMarker(name2, color2, offset) {
-        _defsSelection.append("marker").attr("id", "ideditor-sided-marker-" + name2).attr("viewBox", "0 0 2 2").attr("refX", 1).attr("refY", -offset).attr("markerWidth", 1.5).attr("markerHeight", 1.5).attr("markerUnits", "strokeWidth").attr("orient", "auto").append("path").attr("class", "sided-marker-path sided-marker-" + name2 + "-path").attr("d", "M 0,0 L 1,1 L 2,0 z").attr("stroke", "none").attr("fill", color2);
+      function addSidedMarker(name, color2, offset) {
+        _defsSelection.append("marker").attr("id", "ideditor-sided-marker-" + name).attr("viewBox", "0 0 2 2").attr("refX", 1).attr("refY", -offset).attr("markerWidth", 1.5).attr("markerHeight", 1.5).attr("markerUnits", "strokeWidth").attr("orient", "auto").append("path").attr("class", "sided-marker-path sided-marker-" + name + "-path").attr("d", "M 0,0 L 1,1 L 2,0 z").attr("stroke", "none").attr("fill", color2);
       }
       addSidedMarker("natural", "rgb(170, 170, 170)", 0);
       addSidedMarker("coastline", "#77dede", 1);
@@ -46163,6 +48245,7 @@ ${content}</tr>
       _defsSelection.append("marker").attr("id", "ideditor-viewfield-marker").attr("viewBox", "0 0 16 16").attr("refX", 8).attr("refY", 16).attr("markerWidth", 4).attr("markerHeight", 4).attr("markerUnits", "strokeWidth").attr("orient", "auto").append("path").attr("class", "viewfield-marker-path").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").attr("fill", "#333").attr("fill-opacity", "0.75").attr("stroke", "#fff").attr("stroke-width", "0.5px").attr("stroke-opacity", "0.75");
       _defsSelection.append("marker").attr("id", "ideditor-viewfield-marker-wireframe").attr("viewBox", "0 0 16 16").attr("refX", 8).attr("refY", 16).attr("markerWidth", 4).attr("markerHeight", 4).attr("markerUnits", "strokeWidth").attr("orient", "auto").append("path").attr("class", "viewfield-marker-path").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").attr("fill", "none").attr("stroke", "#fff").attr("stroke-width", "0.5px").attr("stroke-opacity", "0.75");
       var patterns2 = _defsSelection.selectAll("pattern").data([
+        // pattern name, pattern image name
         ["beach", "dots"],
         ["construction", "construction"],
         ["cemetery", "cemetery"],
@@ -46219,7 +48302,9 @@ ${content}</tr>
         var url = context.imagePath(d + ".svg");
         var node = select_default2(this).node();
         svg(url).then(function(svg2) {
-          node.appendChild(select_default2(svg2.documentElement).attr("id", "ideditor-" + d).node());
+          node.appendChild(
+            select_default2(svg2.documentElement).attr("id", "ideditor-" + d).node()
+          );
           if (overrideColors && d !== "iD-sprite") {
             select_default2(node).selectAll("path").attr("fill", "currentColor");
           }
@@ -46451,6 +48536,9 @@ ${content}</tr>
       ["point", "shop", "*", 10],
       ["point", "tourism", "*", 10],
       ["point", "camp_site", "*", 10],
+      ["line", "ref", "*", 12],
+      ["area", "ref", "*", 12],
+      ["point", "ref", "*", 10],
       ["line", "name", "*", 12],
       ["area", "name", "*", 12],
       ["point", "name", "*", 10]
@@ -46461,7 +48549,7 @@ ${content}</tr>
         return preset.id.indexOf(s) >= 0;
       });
     }
-    function get3(array2, prop) {
+    function get4(array2, prop) {
       return function(d, i2) {
         return array2[i2][prop];
       };
@@ -46487,9 +48575,9 @@ ${content}</tr>
     function drawLinePaths(selection2, entities, filter2, classes, labels) {
       var paths = selection2.selectAll("path").filter(filter2).data(entities, osmEntity.key);
       paths.exit().remove();
-      paths.enter().append("path").style("stroke-width", get3(labels, "font-size")).attr("id", function(d) {
+      paths.enter().append("path").style("stroke-width", get4(labels, "font-size")).attr("id", function(d) {
         return "ideditor-labelpath-" + d.id;
-      }).attr("class", classes).merge(paths).attr("d", get3(labels, "lineString"));
+      }).attr("class", classes).merge(paths).attr("d", get4(labels, "lineString"));
     }
     function drawLineLabels(selection2, entities, filter2, classes, labels) {
       var texts = selection2.selectAll("text." + classes).filter(filter2).data(entities, osmEntity.key);
@@ -46506,7 +48594,7 @@ ${content}</tr>
       texts.exit().remove();
       texts.enter().append("text").attr("class", function(d, i2) {
         return classes + " " + labels[i2].classes + " " + d.id;
-      }).merge(texts).attr("x", get3(labels, "x")).attr("y", get3(labels, "y")).style("text-anchor", get3(labels, "textAnchor")).text(utilDisplayName).each(function(d, i2) {
+      }).merge(texts).attr("x", get4(labels, "x")).attr("y", get4(labels, "y")).style("text-anchor", get4(labels, "textAnchor")).text(utilDisplayName).each(function(d, i2) {
         textWidth(utilDisplayName(d), labels[i2].height, this);
       });
     }
@@ -46521,7 +48609,7 @@ ${content}</tr>
     function drawAreaIcons(selection2, entities, filter2, classes, labels) {
       var icons = selection2.selectAll("use." + classes).filter(filter2).data(entities, osmEntity.key);
       icons.exit().remove();
-      icons.enter().append("use").attr("class", "icon " + classes).attr("width", "17px").attr("height", "17px").merge(icons).attr("transform", get3(labels, "transform")).attr("xlink:href", function(d) {
+      icons.enter().append("use").attr("class", "icon " + classes).attr("width", "17px").attr("height", "17px").merge(icons).attr("transform", get4(labels, "transform")).attr("xlink:href", function(d) {
         var preset = _mainPresetIndex.match(d, context.graph());
         var picon = preset && preset.icon;
         return picon ? "#" + picon : "";
@@ -46583,13 +48671,13 @@ ${content}</tr>
           }
           var coord2 = projection2(entity.loc);
           var nodePadding = 10;
-          var bbox = {
+          var bbox2 = {
             minX: coord2[0] - nodePadding,
             minY: coord2[1] - nodePadding - markerPadding,
             maxX: coord2[0] + nodePadding,
             maxY: coord2[1] + nodePadding
           };
-          doInsert(bbox, entity.id + "P");
+          doInsert(bbox2, entity.id + "P");
         }
         if (geometry === "vertex") {
           geometry = "point";
@@ -46625,8 +48713,8 @@ ${content}</tr>
           entity = labelable[k][i2];
           geometry = entity.geometry(graph);
           var getName = geometry === "line" ? utilDisplayNameForPath : utilDisplayName;
-          var name2 = getName(entity);
-          var width = name2 && textWidth(name2, fontSize);
+          var name = getName(entity);
+          var width = name && textWidth(name, fontSize);
           var p = null;
           if (geometry === "point" || geometry === "vertex") {
             if (wireframe)
@@ -46673,23 +48761,23 @@ ${content}</tr>
           y: coord3[1] + offset[1],
           textAnchor: offset[2]
         };
-        var bbox2;
+        var bbox3;
         if (textDirection === "rtl") {
-          bbox2 = {
+          bbox3 = {
             minX: p2.x - width2 - textPadding,
             minY: p2.y - height / 2 - textPadding,
             maxX: p2.x + textPadding,
             maxY: p2.y + height / 2 + textPadding
           };
         } else {
-          bbox2 = {
+          bbox3 = {
             minX: p2.x - textPadding,
             minY: p2.y - height / 2 - textPadding,
             maxX: p2.x + width2 + textPadding,
             maxY: p2.y + height / 2 + textPadding
           };
         }
-        if (tryInsert([bbox2], entity2.id, true)) {
+        if (tryInsert([bbox3], entity2.id, true)) {
           return p2;
         }
       }
@@ -46828,13 +48916,13 @@ ${content}</tr>
         function addIcon() {
           var iconX = centroid[0] - iconSize / 2;
           var iconY = centroid[1] - iconSize / 2;
-          var bbox2 = {
+          var bbox3 = {
             minX: iconX,
             minY: iconY,
             maxX: iconX + iconSize,
             maxY: iconY + iconSize
           };
-          if (tryInsert([bbox2], entity2.id + "I", true)) {
+          if (tryInsert([bbox3], entity2.id + "I", true)) {
             p2.transform = "translate(" + iconX + "," + iconY + ")";
             return true;
           }
@@ -46844,13 +48932,13 @@ ${content}</tr>
           if (width2 && areaWidth >= width2 + 20) {
             var labelX = centroid[0];
             var labelY = centroid[1] + yOffset;
-            var bbox2 = {
+            var bbox3 = {
               minX: labelX - width2 / 2 - padding,
               minY: labelY - height / 2 - padding,
               maxX: labelX + width2 / 2 + padding,
               maxY: labelY + height / 2 + padding
             };
-            if (tryInsert([bbox2], entity2.id, true)) {
+            if (tryInsert([bbox3], entity2.id, true)) {
               p2.x = labelX;
               p2.y = labelY;
               p2.textAnchor = "middle";
@@ -46861,25 +48949,25 @@ ${content}</tr>
           return false;
         }
       }
-      function doInsert(bbox2, id2) {
-        bbox2.id = id2;
+      function doInsert(bbox3, id2) {
+        bbox3.id = id2;
         var oldbox = _entitybboxes[id2];
         if (oldbox) {
           _rdrawn.remove(oldbox);
         }
-        _entitybboxes[id2] = bbox2;
-        _rdrawn.insert(bbox2);
+        _entitybboxes[id2] = bbox3;
+        _rdrawn.insert(bbox3);
       }
       function tryInsert(bboxes, id2, saveSkipped) {
         var skipped = false;
         for (var i3 = 0; i3 < bboxes.length; i3++) {
-          var bbox2 = bboxes[i3];
-          bbox2.id = id2;
-          if (bbox2.minX < 0 || bbox2.minY < 0 || bbox2.maxX > dimensions[0] || bbox2.maxY > dimensions[1]) {
+          var bbox3 = bboxes[i3];
+          bbox3.id = id2;
+          if (bbox3.minX < 0 || bbox3.minY < 0 || bbox3.maxX > dimensions[0] || bbox3.maxY > dimensions[1]) {
             skipped = true;
             break;
           }
-          if (_rdrawn.collides(bbox2)) {
+          if (_rdrawn.collides(bbox3)) {
             skipped = true;
             break;
           }
@@ -46922,11 +49010,11 @@ ${content}</tr>
       var graph = context.graph();
       var selectedIDs = context.selectedIDs();
       var ids = [];
-      var pad2, bbox;
+      var pad2, bbox2;
       if (mouse) {
         pad2 = 20;
-        bbox = { minX: mouse[0] - pad2, minY: mouse[1] - pad2, maxX: mouse[0] + pad2, maxY: mouse[1] + pad2 };
-        var nearMouse = _rdrawn.search(bbox).map(function(entity2) {
+        bbox2 = { minX: mouse[0] - pad2, minY: mouse[1] - pad2, maxX: mouse[0] + pad2, maxY: mouse[1] + pad2 };
+        var nearMouse = _rdrawn.search(bbox2).map(function(entity2) {
           return entity2.id;
         });
         ids.push.apply(ids, nearMouse);
@@ -46941,14 +49029,14 @@ ${content}</tr>
       var debug2 = selection2.selectAll(".labels-group.debug");
       var gj = [];
       if (context.getDebug("collision")) {
-        gj = bbox ? [{
+        gj = bbox2 ? [{
           type: "Polygon",
           coordinates: [[
-            [bbox.minX, bbox.minY],
-            [bbox.maxX, bbox.minY],
-            [bbox.maxX, bbox.maxY],
-            [bbox.minX, bbox.maxY],
-            [bbox.minX, bbox.minY]
+            [bbox2.minX, bbox2.minY],
+            [bbox2.maxX, bbox2.minY],
+            [bbox2.maxX, bbox2.maxY],
+            [bbox2.minX, bbox2.maxY],
+            [bbox2.minX, bbox2.minY]
           ]]
         }] : [];
       }
@@ -48491,9 +50579,9 @@ ${content}</tr>
       var base = context.history().base();
       var data = { targets: [], nopes: [] };
       entities.forEach(function(way) {
-        var features2 = svgSegmentWay(way, graph, activeID);
-        data.targets.push.apply(data.targets, features2.passive);
-        data.nopes.push.apply(data.nopes, features2.active);
+        var features = svgSegmentWay(way, graph, activeID);
+        data.targets.push.apply(data.targets, features.passive);
+        data.nopes.push.apply(data.nopes, features.active);
       });
       var targetData = data.targets.filter(getPath);
       var targets = selection2.selectAll(".line.target-allowed").filter(function(d) {
@@ -48552,7 +50640,8 @@ ${content}</tr>
             var parentMultipolygons = parentRelations.filter(function(relation) {
               return relation.isMultipolygon();
             });
-            if (parentMultipolygons.length > 0 && parentRelations.length === parentMultipolygons.length) {
+            if (parentMultipolygons.length > 0 && // and only multipolygon relations
+            parentRelations.length === parentMultipolygons.length) {
               prefix = "relation area";
             }
           }
@@ -48583,11 +50672,14 @@ ${content}</tr>
       function addMarkers(layergroup, pathclass, groupclass, groupdata, marker) {
         var markergroup = layergroup.selectAll("g." + groupclass).data([pathclass]);
         markergroup = markergroup.enter().append("g").attr("class", groupclass).merge(markergroup);
-        var markers = markergroup.selectAll("path").filter(filter2).data(function data() {
-          return groupdata[this.parentNode.__data__] || [];
-        }, function key(d) {
-          return [d.id, d.index];
-        });
+        var markers = markergroup.selectAll("path").filter(filter2).data(
+          function data() {
+            return groupdata[this.parentNode.__data__] || [];
+          },
+          function key(d) {
+            return [d.id, d.index];
+          }
+        );
         markers.exit().remove();
         markers = markers.enter().append("path").attr("class", pathclass).merge(markers).attr("marker-mid", marker).attr("d", function(d) {
           return d.d;
@@ -48609,7 +50701,7 @@ ${content}</tr>
         if (outer) {
           ways.push(entity.mergeTags(outer.tags));
           oldMultiPolygonOuters[outer.id] = true;
-        } else if (entity.geometry(graph) === "line") {
+        } else if (entity.geometry(graph) === "line" || entity.geometry(graph) === "area" && entity.sidednessIdentifier && entity.sidednessIdentifier() === "coastline") {
           ways.push(entity);
         }
       }
@@ -48622,20 +50714,32 @@ ${content}</tr>
         var onewayArr = v.filter(function(d) {
           return d.isOneWay();
         });
-        var onewaySegments = svgMarkerSegments(projection2, graph, 35, function shouldReverse(entity2) {
-          return entity2.tags.oneway === "-1";
-        }, function bothDirections(entity2) {
-          return entity2.tags.oneway === "reversible" || entity2.tags.oneway === "alternating";
-        });
+        var onewaySegments = svgMarkerSegments(
+          projection2,
+          graph,
+          35,
+          function shouldReverse(entity2) {
+            return entity2.tags.oneway === "-1";
+          },
+          function bothDirections(entity2) {
+            return entity2.tags.oneway === "reversible" || entity2.tags.oneway === "alternating";
+          }
+        );
         onewaydata[k] = utilArrayFlatten(onewayArr.map(onewaySegments));
         var sidedArr = v.filter(function(d) {
           return d.isSided();
         });
-        var sidedSegments = svgMarkerSegments(projection2, graph, 30, function shouldReverse() {
-          return false;
-        }, function bothDirections() {
-          return false;
-        });
+        var sidedSegments = svgMarkerSegments(
+          projection2,
+          graph,
+          30,
+          function shouldReverse() {
+            return false;
+          },
+          function bothDirections() {
+            return false;
+          }
+        );
         sideddata[k] = utilArrayFlatten(sidedArr.map(sidedSegments));
       });
       var covered = selection2.selectAll(".layer-osm.covered");
@@ -48657,10 +50761,16 @@ ${content}</tr>
         layergroup.selectAll("g.line-casing-highlighted").call(drawLineGroup, "casing", true);
         layergroup.selectAll("g.line-stroke-highlighted").call(drawLineGroup, "stroke", true);
         addMarkers(layergroup, "oneway", "onewaygroup", onewaydata, "url(#ideditor-oneway-marker)");
-        addMarkers(layergroup, "sided", "sidedgroup", sideddata, function marker(d) {
-          var category = graph.entity(d.id).sidednessIdentifier();
-          return "url(#ideditor-sided-marker-" + category + ")";
-        });
+        addMarkers(
+          layergroup,
+          "sided",
+          "sidedgroup",
+          sideddata,
+          function marker(d) {
+            var category = graph.entity(d.id).sidednessIdentifier();
+            return "url(#ideditor-sided-marker-" + category + ")";
+          }
+        );
       });
       touchLayer.call(drawTargets, graph, ways, filter2);
     }
@@ -48724,15 +50834,15 @@ ${content}</tr>
           if (midpoints[id2]) {
             midpoints[id2].parents.push(entity);
           } else if (geoVecLength(projection2(a.loc), projection2(b.loc)) > 40) {
-            var point = geoVecInterp(a.loc, b.loc, 0.5);
+            var point2 = geoVecInterp(a.loc, b.loc, 0.5);
             var loc = null;
-            if (extent.intersects(point)) {
-              loc = point;
+            if (extent.intersects(point2)) {
+              loc = point2;
             } else {
               for (var k = 0; k < 4; k++) {
-                point = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]);
-                if (point && geoVecLength(projection2(a.loc), projection2(point)) > 20 && geoVecLength(projection2(b.loc), projection2(point)) > 20) {
-                  loc = point;
+                point2 = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]);
+                if (point2 && geoVecLength(projection2(a.loc), projection2(point2)) > 20 && geoVecLength(projection2(b.loc), projection2(point2)) > 20) {
+                  loc = point2;
                   break;
                 }
               }
@@ -48772,9 +50882,11 @@ ${content}</tr>
         var b2 = graph.entity(d.edge[1]);
         var angle2 = geoAngle(a2, b2, projection2) * (180 / Math.PI);
         return translate(d) + " rotate(" + angle2 + ")";
-      }).call(svgTagClasses().tags(function(d) {
-        return d.parents[0].tags;
-      }));
+      }).call(svgTagClasses().tags(
+        function(d) {
+          return d.parents[0].tags;
+        }
+      ));
       groups.select("polygon.shadow");
       groups.select("polygon.fill");
       touchLayer.call(drawTargets, graph, Object.values(midpoints), midpointFilter);
@@ -48942,6 +51054,7 @@ ${content}</tr>
   var import_fast_deep_equal8 = __toESM(require_fast_deep_equal());
   function svgVertices(projection2, context) {
     var radiuses = {
+      //       z16-, z17,   z18+,  w/icon
       shadow: [6, 7.5, 7.5, 12],
       stroke: [2.5, 3.5, 3.5, 8],
       fill: [1, 1.5, 1.5, 1.5]
@@ -49186,8 +51299,11 @@ ${content}</tr>
       }
       var sets2 = {
         persistent: _currPersistent,
+        // persistent = important vertices (render always)
         selected: _currSelected,
+        // selected + siblings of selected (render always)
         hovered: _currHover
+        // hovered + siblings of hovered (render only in draw modes)
       };
       var all = Object.assign({}, isMoving ? _currHover : {}, _currSelected, _currPersistent);
       var filterRendered = function(d) {
@@ -49250,8 +51366,8 @@ ${content}</tr>
   }
 
   // modules/util/bind_once.js
-  function utilBindOnce(target, type3, listener, capture) {
-    var typeOnce = type3 + ".once";
+  function utilBindOnce(target, type2, listener, capture) {
+    var typeOnce = type2 + ".once";
     function one2() {
       target.on(typeOnce, null);
       listener.apply(this, arguments);
@@ -49281,7 +51397,10 @@ ${content}</tr>
   }
   function defaultConstrain2(transform2, extent, translateExtent) {
     var dx0 = transform2.invertX(extent[0][0]) - translateExtent[0][0], dx1 = transform2.invertX(extent[1][0]) - translateExtent[1][0], dy0 = transform2.invertY(extent[0][1]) - translateExtent[0][1], dy1 = transform2.invertY(extent[1][1]) - translateExtent[1][1];
-    return transform2.translate(dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1), dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1));
+    return transform2.translate(
+      dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),
+      dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)
+    );
   }
   function utilZoomPan() {
     var filter2 = defaultFilter3, extent = defaultExtent2, constrain = defaultConstrain2, wheelDelta = defaultWheelDelta2, scaleExtent = [0, Infinity], translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]], interpolate = zoom_default, dispatch10 = dispatch_default("start", "zoom", "end"), _wheelDelay = 150, _transform = identity2, _activeGesture;
@@ -49289,10 +51408,10 @@ ${content}</tr>
       selection2.on("pointerdown.zoom", pointerdown).on("wheel.zoom", wheeled).style("touch-action", "none").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
       select_default2(window).on("pointermove.zoompan", pointermove).on("pointerup.zoompan pointercancel.zoompan", pointerup);
     }
-    zoom.transform = function(collection, transform2, point) {
+    zoom.transform = function(collection, transform2, point2) {
       var selection2 = collection.selection ? collection.selection() : collection;
       if (collection !== selection2) {
-        schedule(collection, transform2, point);
+        schedule(collection, transform2, point2);
       } else {
         selection2.interrupt().each(function() {
           gesture(this, arguments).start(null).zoom(null, null, typeof transform2 === "function" ? transform2.apply(this, arguments) : transform2).end(null);
@@ -49313,13 +51432,19 @@ ${content}</tr>
     };
     zoom.translateBy = function(selection2, x, y) {
       zoom.transform(selection2, function() {
-        return constrain(_transform.translate(typeof x === "function" ? x.apply(this, arguments) : x, typeof y === "function" ? y.apply(this, arguments) : y), extent.apply(this, arguments), translateExtent);
+        return constrain(_transform.translate(
+          typeof x === "function" ? x.apply(this, arguments) : x,
+          typeof y === "function" ? y.apply(this, arguments) : y
+        ), extent.apply(this, arguments), translateExtent);
       });
     };
     zoom.translateTo = function(selection2, x, y, p) {
       zoom.transform(selection2, function() {
         var e = extent.apply(this, arguments), t = _transform, p02 = !p ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p;
-        return constrain(identity2.translate(p02[0], p02[1]).scale(t.k).translate(typeof x === "function" ? -x.apply(this, arguments) : -x, typeof y === "function" ? -y.apply(this, arguments) : -y), e, translateExtent);
+        return constrain(identity2.translate(p02[0], p02[1]).scale(t.k).translate(
+          typeof x === "function" ? -x.apply(this, arguments) : -x,
+          typeof y === "function" ? -y.apply(this, arguments) : -y
+        ), e, translateExtent);
       }, p);
     };
     function scale(transform2, k) {
@@ -49333,13 +51458,13 @@ ${content}</tr>
     function centroid(extent2) {
       return [(+extent2[0][0] + +extent2[1][0]) / 2, (+extent2[0][1] + +extent2[1][1]) / 2];
     }
-    function schedule(transition2, transform2, point) {
+    function schedule(transition2, transform2, point2) {
       transition2.on("start.zoom", function() {
         gesture(this, arguments).start(null);
       }).on("interrupt.zoom end.zoom", function() {
         gesture(this, arguments).end(null);
       }).tween("zoom", function() {
-        var that = this, args = arguments, g = gesture(that, args), e = extent.apply(that, args), p = !point ? centroid(e) : typeof point === "function" ? point.apply(that, args) : point, w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]), a = _transform, b = typeof transform2 === "function" ? transform2.apply(that, args) : transform2, i2 = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
+        var that = this, args = arguments, g = gesture(that, args), e = extent.apply(that, args), p = !point2 ? centroid(e) : typeof point2 === "function" ? point2.apply(that, args) : point2, w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]), a = _transform, b = typeof transform2 === "function" ? transform2.apply(that, args) : transform2, i2 = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
         return function(t) {
           if (t === 1) {
             t = b;
@@ -49528,7 +51653,8 @@ ${content}</tr>
     var _maxDistance = 20;
     var _pointer;
     function pointerIsValidFor(loc) {
-      return new Date().getTime() - _pointer.startTime <= _maxTimespan && geoVecLength(_pointer.startLoc, loc) <= _maxDistance;
+      return new Date().getTime() - _pointer.startTime <= _maxTimespan && // all pointer events must occur within a small distance of the first pointerdown
+      geoVecLength(_pointer.startLoc, loc) <= _maxDistance;
     }
     function pointerdown(d3_event) {
       if (d3_event.ctrlKey || d3_event.button === 2)
@@ -49588,7 +51714,14 @@ ${content}</tr>
     return Math.max(min3, Math.min(num, max3));
   }
   function rendererMap(context) {
-    var dispatch10 = dispatch_default("move", "drawn", "crossEditableZoom", "hitMinZoom", "changeHighlighting", "changeAreaFill");
+    var dispatch10 = dispatch_default(
+      "move",
+      "drawn",
+      "crossEditableZoom",
+      "hitMinZoom",
+      "changeHighlighting",
+      "changeAreaFill"
+    );
     var projection2 = context.projection;
     var curtainProjection = context.curtainProjection;
     var drawLayers;
@@ -49686,7 +51819,9 @@ ${content}</tr>
         }
       });
       var detected = utilDetect();
-      if ("GestureEvent" in window && !detected.isMobileWebKit) {
+      if ("GestureEvent" in window && // Listening for gesture events on iOS 13.4+ breaks double-tapping,
+      // but we only need to do this on desktop Safari anyway. – #7694
+      !detected.isMobileWebKit) {
         surface.on("gesturestart.surface", function(d3_event) {
           d3_event.preventDefault();
           _gestureTransformStart = projection2.transform();
@@ -49696,7 +51831,8 @@ ${content}</tr>
       _doubleUpHandler.on("doubleUp.map", function(d3_event, p02) {
         if (!_dblClickZoomEnabled)
           return;
-        if (typeof d3_event.target.__data__ === "object" && !select_default2(d3_event.target).classed("fill"))
+        if (typeof d3_event.target.__data__ === "object" && // or area fills
+        !select_default2(d3_event.target).classed("fill"))
           return;
         var zoomOut2 = d3_event.shiftKey;
         var t = projection2.transform();
@@ -49707,7 +51843,10 @@ ${content}</tr>
         map2.transformEase(t);
       });
       context.on("enter.map", function() {
-        if (!map2.editableDataEnabled(true))
+        if (!map2.editableDataEnabled(
+          true
+          /* skip zoom check */
+        ))
           return;
         if (_isTransformed)
           return;
@@ -49766,7 +51905,7 @@ ${content}</tr>
     function drawEditable(difference, extent) {
       var mode = context.mode();
       var graph = context.graph();
-      var features2 = context.features();
+      var features = context.features();
       var all = context.history().intersects(map2.extent());
       var fullRedraw = false;
       var data;
@@ -49790,9 +51929,9 @@ ${content}</tr>
         filter2 = function(d) {
           return set3.has(d.id);
         };
-        features2.clear(data);
+        features.clear(data);
       } else {
-        if (features2.gatherStats(all, graph, _dimensions)) {
+        if (features.gatherStats(all, graph, _dimensions)) {
           extent = void 0;
         }
         if (extent) {
@@ -49810,7 +51949,7 @@ ${content}</tr>
         }
       }
       if (applyFeatureLayerFilters) {
-        data = features2.filter(data, graph);
+        data = features.filter(data, graph);
       } else {
         context.features().resetStats();
       }
@@ -49851,7 +51990,9 @@ ${content}</tr>
       e.preventDefault();
       var props = {
         deltaMode: 0,
+        // dummy values to ignore in zoomPan
         deltaY: 1,
+        // dummy values to ignore in zoomPan
         clientX: e.clientX,
         clientY: e.clientY,
         screenX: e.screenX,
@@ -49883,7 +52024,13 @@ ${content}</tr>
         if (source.deltaMode === 1) {
           var lines = Math.abs(source.deltaY);
           var sign2 = source.deltaY > 0 ? 1 : -1;
-          dY = sign2 * clamp(Math.exp((lines - 1) * 0.75) * 4.000244140625, 4.000244140625, 350.000244140625);
+          dY = sign2 * clamp(
+            Math.exp((lines - 1) * 0.75) * 4.000244140625,
+            4.000244140625,
+            // min
+            350.000244140625
+            // max
+          );
           if (detected.os !== "mac") {
             dY *= 5;
           }
@@ -50078,10 +52225,10 @@ ${content}</tr>
       var k2 = clamp(geoZoomToScale(z2, TILESIZE), kMin, kMax);
       proj.scale(k2);
       var t = proj.translate();
-      var point = proj(loc2);
+      var point2 = proj(loc2);
       var center = pxCenter();
-      t[0] += center[0] - point[0];
-      t[1] += center[1] - point[1];
+      t[0] += center[0] - point2[0];
+      t[1] += center[1] - point2[1];
       return setTransform(identity2.translate(t[0], t[1]).scale(k2), duration, force);
     }
     map2.pan = function(delta, duration) {
@@ -50209,7 +52356,12 @@ ${content}</tr>
     };
     map2.transformEase = function(t2, duration) {
       duration = duration || 250;
-      setTransform(t2, duration, false);
+      setTransform(
+        t2,
+        duration,
+        false
+        /* don't force */
+      );
       return map2;
     };
     map2.zoomToEase = function(obj, duration) {
@@ -50243,7 +52395,10 @@ ${content}</tr>
     };
     map2.extent = function(val) {
       if (!arguments.length) {
-        return new geoExtent(projection2.invert([0, _dimensions[1]]), projection2.invert([_dimensions[0], 0]));
+        return new geoExtent(
+          projection2.invert([0, _dimensions[1]]),
+          projection2.invert([_dimensions[0], 0])
+        );
       } else {
         var extent = geoExtent(val);
         map2.centerZoom(extent.center(), map2.extentZoom(extent));
@@ -50254,7 +52409,10 @@ ${content}</tr>
         var headerY = 71;
         var footerY = 30;
         var pad2 = 10;
-        return new geoExtent(projection2.invert([pad2, _dimensions[1] - footerY - pad2]), projection2.invert([_dimensions[0] - pad2, headerY + pad2]));
+        return new geoExtent(
+          projection2.invert([pad2, _dimensions[1] - footerY - pad2]),
+          projection2.invert([_dimensions[0] - pad2, headerY + pad2])
+        );
       } else {
         var extent = geoExtent(val);
         map2.centerZoom(extent.center(), map2.trimmedExtentZoom(extent));
@@ -50383,20 +52541,20 @@ ${content}</tr>
     photos.dateFilterValue = function(val) {
       return val === _dateFilters[0] ? _fromDate : _toDate;
     };
-    photos.setDateFilter = function(type3, val, updateUrl) {
+    photos.setDateFilter = function(type2, val, updateUrl) {
       var date = val && new Date(val);
       if (date && !isNaN(date)) {
-        val = date.toISOString().substr(0, 10);
+        val = date.toISOString().slice(0, 10);
       } else {
         val = null;
       }
-      if (type3 === _dateFilters[0]) {
+      if (type2 === _dateFilters[0]) {
         _fromDate = val;
         if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) {
           _toDate = _fromDate;
         }
       }
-      if (type3 === _dateFilters[1]) {
+      if (type2 === _dateFilters[1]) {
         _toDate = val;
         if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) {
           _fromDate = _toDate;
@@ -50622,7 +52780,10 @@ ${content}</tr>
           attribution = attribution.append("a").attr("href", d.terms_url).attr("target", "_blank");
         }
         const sourceID = d.id.replace(/\./g, "<TX_DOT>");
-        const terms_text = _t(`imagery.${sourceID}.attribution.text`, { default: d.terms_text || d.id || d.name() });
+        const terms_text = _t(
+          `imagery.${sourceID}.attribution.text`,
+          { default: d.terms_text || d.id || d.name() }
+        );
         if (d.icon && !d.overlay) {
           attribution.append("img").attr("class", "source-image").attr("src", d.icon);
         }
@@ -50995,19 +53156,21 @@ ${content}</tr>
       var heading = _heading.apply(this, arguments);
       var text2 = _title.apply(this, arguments);
       var keys = _keys.apply(this, arguments);
+      var headingCallback = typeof heading === "function" ? heading : (s) => s.text(heading);
+      var textCallback = typeof text2 === "function" ? text2 : (s) => s.text(text2);
       return function(selection2) {
         var headingSelect = selection2.selectAll(".tooltip-heading").data(heading ? [heading] : []);
         headingSelect.exit().remove();
-        headingSelect.enter().append("div").attr("class", "tooltip-heading").merge(headingSelect).html(heading);
+        headingSelect.enter().append("div").attr("class", "tooltip-heading").merge(headingSelect).text("").call(headingCallback);
         var textSelect = selection2.selectAll(".tooltip-text").data(text2 ? [text2] : []);
         textSelect.exit().remove();
-        textSelect.enter().append("div").attr("class", "tooltip-text").merge(textSelect).html(text2);
+        textSelect.enter().append("div").attr("class", "tooltip-text").merge(textSelect).text("").call(textCallback);
         var keyhintWrap = selection2.selectAll(".keyhint-wrap").data(keys && keys.length ? [0] : []);
         keyhintWrap.exit().remove();
         var keyhintWrapEnter = keyhintWrap.enter().append("div").attr("class", "keyhint-wrap");
         keyhintWrapEnter.append("span").call(_t.append("tooltip_keyhint"));
         keyhintWrap = keyhintWrapEnter.merge(keyhintWrap);
-        keyhintWrap.selectAll("kbd.shortcut").data(keys && keys.length ? keys : []).enter().append("kbd").attr("class", "shortcut").html(function(d) {
+        keyhintWrap.selectAll("kbd.shortcut").data(keys && keys.length ? keys : []).enter().append("kbd").attr("class", "shortcut").text(function(d) {
           return d;
         });
       };
@@ -51068,7 +53231,7 @@ ${content}</tr>
         utilHighlightEntities(d.relatedEntityIds(), false, context);
       });
       buttonsEnter.each(function(d) {
-        var tooltip = uiTooltip().heading(d.title).title(d.tooltip()).keys([d.keys[0]]);
+        var tooltip = uiTooltip().heading(() => d.title).title(d.tooltip).keys([d.keys[0]]);
         _tooltips.push(tooltip);
         select_default2(this).call(tooltip).append("div").attr("class", "icon-wrap").call(svgIcon(d.icon && d.icon() || "#iD-operation-" + d.id, "operation"));
       });
@@ -51101,7 +53264,7 @@ ${content}</tr>
         }
         if (operation.disabled()) {
           if (lastPointerUpType === "touch" || lastPointerUpType === "pen") {
-            context.ui().flash.duration(4e3).iconName("#iD-operation-" + operation.id).iconClass("operation disabled").label(operation.tooltip)();
+            context.ui().flash.duration(4e3).iconName("#iD-operation-" + operation.id).iconClass("operation disabled").label(operation.tooltip())();
           }
         } else {
           if (lastPointerUpType === "touch" || lastPointerUpType === "pen") {
@@ -51209,20 +53372,27 @@ ${content}</tr>
   // modules/ui/feature_info.js
   function uiFeatureInfo(context) {
     function update(selection2) {
-      var features2 = context.features();
-      var stats = features2.stats();
+      var features = context.features();
+      var stats = features.stats();
       var count = 0;
-      var hiddenList = features2.hidden().map(function(k) {
+      var hiddenList = features.hidden().map(function(k) {
         if (stats[k]) {
           count += stats[k];
-          return _t.html("inspector.title_count", { title: { html: _t.html("feature." + k + ".description") }, count: stats[k] });
+          return _t.append("inspector.title_count", {
+            title: _t("feature." + k + ".description"),
+            count: stats[k]
+          });
         }
         return null;
       }).filter(Boolean);
-      selection2.html("");
+      selection2.text("");
       if (hiddenList.length) {
         var tooltipBehavior = uiTooltip().placement("top").title(function() {
-          return hiddenList.join("<br/>");
+          return (selection3) => {
+            hiddenList.forEach((hiddenFeature) => {
+              selection3.append("div").call(hiddenFeature);
+            });
+          };
         });
         selection2.append("a").attr("class", "chip").attr("href", "#").call(_t.append("feature_info.hidden_warning", { count })).call(tooltipBehavior).on("click", function(d3_event) {
           tooltipBehavior.hide();
@@ -51246,7 +53416,7 @@ ${content}</tr>
     var _duration = 2e3;
     var _iconName = "#iD-icon-no";
     var _iconClass = "disabled";
-    var _label = "";
+    var _label = (s) => s.text("");
     function flash() {
       if (_flashTimer) {
         _flashTimer.stop();
@@ -51262,7 +53432,7 @@ ${content}</tr>
       content = content.merge(contentEnter);
       content.selectAll(".flash-icon").attr("class", "icon flash-icon " + (_iconClass || ""));
       content.selectAll(".flash-icon use").attr("xlink:href", _iconName);
-      content.selectAll(".flash-text").attr("class", "flash-text").html(_label);
+      content.selectAll(".flash-text").attr("class", "flash-text").call(_label);
       _flashTimer = timeout_default(function() {
         _flashTimer = null;
         context.container().select(".main-footer-wrap").classed("footer-hide", false).classed("footer-show", true);
@@ -51279,7 +53449,11 @@ ${content}</tr>
     flash.label = function(_) {
       if (!arguments.length)
         return _label;
-      _label = _;
+      if (typeof _ !== "function") {
+        _label = (selection2) => selection2.text(_);
+      } else {
+        _label = (selection2) => selection2.text("").call(_);
+      }
       return flash;
     };
     flash.iconName = function(_) {
@@ -51348,8 +53522,11 @@ ${content}</tr>
   // modules/ui/geolocate.js
   function uiGeolocate(context) {
     var _geolocationOptions = {
+      // prioritize speed and power usage over precision
       enableHighAccuracy: false,
+      // don't hang indefinitely getting the location
       timeout: 6e3
+      // 6sec
     };
     var _locating = uiLoading(context).message(_t.html("geolocate.locating")).blocking(true);
     var _layer = context.layers().layer("geolocate");
@@ -51361,7 +53538,11 @@ ${content}</tr>
       if (context.inIntro())
         return;
       if (!_layer.enabled() && !_locating.isShown()) {
-        _timeoutID = setTimeout(error, 1e4);
+        _timeoutID = setTimeout(
+          error,
+          1e4
+          /* 10sec */
+        );
         context.container().call(_locating);
         navigator.geolocation.getCurrentPosition(success, error, _geolocationOptions);
       } else {
@@ -51388,7 +53569,7 @@ ${content}</tr>
       if (_position) {
         zoomTo();
       } else {
-        context.ui().flash.label(_t.html("geolocate.location_unavailable")).iconName("#iD-icon-geolocate")();
+        context.ui().flash.label(_t.append("geolocate.location_unavailable")).iconName("#iD-icon-geolocate")();
       }
       finish();
     }
@@ -51406,7 +53587,9 @@ ${content}</tr>
     return function(selection2) {
       if (!navigator.geolocation || !navigator.geolocation.getCurrentPosition)
         return;
-      _button = selection2.append("button").on("click", click).attr("aria-pressed", false).call(svgIcon("#iD-icon-geolocate", "light")).call(uiTooltip().placement(_mainLocalizer.textDirection() === "rtl" ? "right" : "left").title(_t.html("geolocate.title")).keys([_t("geolocate.key")]));
+      _button = selection2.append("button").on("click", click).attr("aria-pressed", false).call(svgIcon("#iD-icon-geolocate", "light")).call(
+        uiTooltip().placement(_mainLocalizer.textDirection() === "rtl" ? "right" : "left").title(() => _t.append("geolocate.title")).keys([_t("geolocate.key")])
+      );
       context.keybinding().on(_t("geolocate.key"), click);
     };
   }
@@ -51435,9 +53618,9 @@ ${content}</tr>
         _currSourceName = sourceLabel;
         _metadata = {};
       }
-      selection2.html("");
+      selection2.text("");
       var list = selection2.append("ul").attr("class", "background-info");
-      list.append("li").html(_currSourceName);
+      list.append("li").call(_currSourceName);
       _metadataKeys.forEach(function(k) {
         if (isDG && k === "vintage")
           return;
@@ -51511,7 +53694,7 @@ ${content}</tr>
       context.map().on("drawn.info-background", null).on("move.info-background", null);
     };
     panel.id = "background";
-    panel.label = _t.html("info_panels.background.title");
+    panel.label = _t.append("info_panels.background.title");
     panel.key = _t("info_panels.background.key");
     return panel;
   }
@@ -51635,7 +53818,7 @@ ${content}</tr>
       context.on("enter.info-history", null);
     };
     panel.id = "history";
-    panel.label = _t.html("info_panels.history.title");
+    panel.label = _t.append("info_panels.history.title");
     panel.key = _t("info_panels.history.key");
     return panel;
   }
@@ -51803,7 +53986,7 @@ ${content}</tr>
       context.surface().on(".info-location", null);
     };
     panel.id = "location";
-    panel.label = _t.html("info_panels.location.title");
+    panel.label = _t.append("info_panels.location.title");
     panel.key = _t("info_panels.location.key");
     return panel;
   }
@@ -51895,7 +54078,9 @@ ${content}</tr>
       var list = selection2.append("ul");
       var coordItem;
       if (geometry) {
-        list.append("li").call(_t.append("info_panels.measurement.geometry", { suffix: ":" })).append("span").html(closed ? _t.html("info_panels.measurement.closed_" + geometry) : _t.html("geometry." + geometry));
+        list.append("li").call(_t.append("info_panels.measurement.geometry", { suffix: ":" })).append("span").html(
+          closed ? _t.html("info_panels.measurement.closed_" + geometry) : _t.html("geometry." + geometry)
+        );
       }
       if (totalNodeCount) {
         list.append("li").call(_t.append("info_panels.measurement.node_count", { suffix: ":" })).append("span").text(totalNodeCount.toLocaleString(localeCode));
@@ -51947,7 +54132,7 @@ ${content}</tr>
       context.on("enter.info-measurement", null);
     };
     panel.id = "measurement";
-    panel.label = _t.html("info_panels.measurement.title");
+    panel.label = _t.append("info_panels.measurement.title");
     panel.key = _t("info_panels.measurement.key");
     return panel;
   }
@@ -51988,8 +54173,8 @@ ${content}</tr>
         });
         enter.style("opacity", 0).transition().duration(200).style("opacity", 1);
         var title = enter.append("div").attr("class", "panel-title fillD2");
-        title.append("h3").html(function(d) {
-          return panels[d].label;
+        title.append("h3").each(function(d) {
+          return panels[d].label(select_default2(this));
         });
         title.append("button").attr("class", "close").attr("title", _t("icons.close")).on("click", function(d3_event, d) {
           d3_event.stopImmediatePropagation();
@@ -52052,10 +54237,10 @@ ${content}</tr>
   // modules/ui/intro/helper.js
   function pointBox(loc, context) {
     var rect = context.surfaceRect();
-    var point = context.curtainProjection(loc);
+    var point2 = context.curtainProjection(loc);
     return {
-      left: point[0] + rect.left - 40,
-      top: point[1] + rect.top - 60,
+      left: point2[0] + rect.left - 40,
+      top: point2[1] + rect.top - 60,
       width: 80,
       height: 90
     };
@@ -52064,10 +54249,10 @@ ${content}</tr>
     var box;
     if (locOrBox instanceof Array) {
       var rect = context.surfaceRect();
-      var point = context.curtainProjection(locOrBox);
+      var point2 = context.curtainProjection(locOrBox);
       box = {
-        left: point[0] + rect.left,
-        top: point[1] + rect.top
+        left: point2[0] + rect.left,
+        top: point2[1] + rect.top
       };
     } else {
       box = locOrBox;
@@ -52079,13 +54264,14 @@ ${content}</tr>
       height: (box.width || 0) + 2 * padding
     };
   }
-  function icon(name2, svgklass, useklass) {
-    return '<svg class="icon ' + (svgklass || "") + '"><use xlink:href="' + name2 + '"' + (useklass ? ' class="' + useklass + '"' : "") + "></use></svg>";
+  function icon(name, svgklass, useklass) {
+    return '<svg class="icon ' + (svgklass || "") + '"><use xlink:href="' + name + '"' + (useklass ? ' class="' + useklass + '"' : "") + "></use></svg>";
   }
   var helpStringReplacements;
   function helpHtml(id2, replacements) {
     if (!helpStringReplacements) {
       helpStringReplacements = {
+        // insert icons corresponding to various UI elements
         point_icon: icon("#iD-icon-point", "inline"),
         line_icon: icon("#iD-icon-line", "inline"),
         area_icon: icon("#iD-icon-area", "inline"),
@@ -52099,6 +54285,7 @@ ${content}</tr>
         undo_icon: icon(_mainLocalizer.textDirection() === "rtl" ? "#iD-icon-redo" : "#iD-icon-undo", "inline"),
         redo_icon: icon(_mainLocalizer.textDirection() === "rtl" ? "#iD-icon-undo" : "#iD-icon-redo", "inline"),
         save_icon: icon("#iD-icon-save", "inline"),
+        // operation icons
         circularize_icon: icon("#iD-operation-circularize", "inline operation"),
         continue_icon: icon("#iD-operation-continue", "inline operation"),
         copy_icon: icon("#iD-operation-copy", "inline operation"),
@@ -52116,6 +54303,7 @@ ${content}</tr>
         rotate_icon: icon("#iD-operation-rotate", "inline operation"),
         split_icon: icon("#iD-operation-split", "inline operation"),
         straighten_icon: icon("#iD-operation-straighten", "inline operation"),
+        // interaction icons
         leftclick: icon("#iD-walkthrough-mouse-left", "inline operation"),
         rightclick: icon("#iD-walkthrough-mouse-right", "inline operation"),
         mousewheel_icon: icon("#iD-walkthrough-mousewheel", "inline operation"),
@@ -52124,6 +54312,7 @@ ${content}</tr>
         longpress_icon: icon("#iD-walkthrough-longpress", "inline operation"),
         touchdrag_icon: icon("#iD-walkthrough-touchdrag", "inline operation"),
         pinch_icon: icon("#iD-walkthrough-pinch-apart", "inline operation"),
+        // insert keys; may be localized and platform-dependent
         shift: uiCmd.display("\u21E7"),
         alt: uiCmd.display("\u2325"),
         return: uiCmd.display("\u21B5"),
@@ -52132,6 +54321,7 @@ ${content}</tr>
         add_note_key: _t.html("modes.add_note.key"),
         help_key: _t.html("help.key"),
         shortcuts_key: _t.html("shortcuts.toggle.key"),
+        // reference localized UI labels directly so that they'll always match
         save: _t.html("save.title"),
         undo: _t.html("undo.title"),
         redo: _t.html("redo.title"),
@@ -52198,11 +54388,11 @@ ${content}</tr>
   }
   function localize(obj) {
     var key;
-    var name2 = obj.tags && obj.tags.name;
-    if (name2) {
-      key = "intro.graph.name." + slugify(name2);
-      obj.tags.name = _t(key, { default: name2 });
-      checkKey(key, name2);
+    var name = obj.tags && obj.tags.name;
+    if (name) {
+      key = "intro.graph.name." + slugify(name);
+      obj.tags.name = _t(key, { default: name });
+      checkKey(key, name);
     }
     var street = obj.tags && obj.tags["addr:street"];
     if (street) {
@@ -52454,17 +54644,32 @@ ${content}</tr>
     };
     function welcome() {
       context.map().centerZoom([-85.63591, 41.94285], 19);
-      reveal(".intro-nav-wrap .chapter-welcome", helpHtml("intro.welcome.welcome"), { buttonText: _t.html("intro.ok"), buttonCallback: practice });
+      reveal(
+        ".intro-nav-wrap .chapter-welcome",
+        helpHtml("intro.welcome.welcome"),
+        { buttonText: _t.html("intro.ok"), buttonCallback: practice }
+      );
     }
     function practice() {
-      reveal(".intro-nav-wrap .chapter-welcome", helpHtml("intro.welcome.practice"), { buttonText: _t.html("intro.ok"), buttonCallback: words });
+      reveal(
+        ".intro-nav-wrap .chapter-welcome",
+        helpHtml("intro.welcome.practice"),
+        { buttonText: _t.html("intro.ok"), buttonCallback: words }
+      );
     }
     function words() {
-      reveal(".intro-nav-wrap .chapter-welcome", helpHtml("intro.welcome.words"), { buttonText: _t.html("intro.ok"), buttonCallback: chapters });
+      reveal(
+        ".intro-nav-wrap .chapter-welcome",
+        helpHtml("intro.welcome.words"),
+        { buttonText: _t.html("intro.ok"), buttonCallback: chapters }
+      );
     }
     function chapters() {
       dispatch10.call("done");
-      reveal(".intro-nav-wrap .chapter-navigation", helpHtml("intro.welcome.chapters", { next: _t("intro.navigation.title") }));
+      reveal(
+        ".intro-nav-wrap .chapter-navigation",
+        helpHtml("intro.welcome.chapters", { next: _t("intro.navigation.title") })
+      );
     }
     chapter.enter = function() {
       welcome();
@@ -52547,7 +54752,7 @@ ${content}</tr>
         if (context.map().zoom() !== zoomStart) {
           context.map().on("move.intro", null);
           timeout2(function() {
-            continueTo(features2);
+            continueTo(features);
           }, 3e3);
         }
       });
@@ -52556,13 +54761,21 @@ ${content}</tr>
         nextStep();
       }
     }
-    function features2() {
+    function features() {
       var onClick = function() {
         continueTo(pointsLinesAreas);
       };
-      reveal(".surface", helpHtml("intro.navigation.features"), { buttonText: _t.html("intro.ok"), buttonCallback: onClick });
+      reveal(
+        ".surface",
+        helpHtml("intro.navigation.features"),
+        { buttonText: _t.html("intro.ok"), buttonCallback: onClick }
+      );
       context.map().on("drawn.intro", function() {
-        reveal(".surface", helpHtml("intro.navigation.features"), { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: onClick });
+        reveal(
+          ".surface",
+          helpHtml("intro.navigation.features"),
+          { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: onClick }
+        );
       });
       function continueTo(nextStep) {
         context.map().on("drawn.intro", null);
@@ -52573,9 +54786,17 @@ ${content}</tr>
       var onClick = function() {
         continueTo(nodesWays);
       };
-      reveal(".surface", helpHtml("intro.navigation.points_lines_areas"), { buttonText: _t.html("intro.ok"), buttonCallback: onClick });
+      reveal(
+        ".surface",
+        helpHtml("intro.navigation.points_lines_areas"),
+        { buttonText: _t.html("intro.ok"), buttonCallback: onClick }
+      );
       context.map().on("drawn.intro", function() {
-        reveal(".surface", helpHtml("intro.navigation.points_lines_areas"), { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: onClick });
+        reveal(
+          ".surface",
+          helpHtml("intro.navigation.points_lines_areas"),
+          { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: onClick }
+        );
       });
       function continueTo(nextStep) {
         context.map().on("drawn.intro", null);
@@ -52586,9 +54807,17 @@ ${content}</tr>
       var onClick = function() {
         continueTo(clickTownHall);
       };
-      reveal(".surface", helpHtml("intro.navigation.nodes_ways"), { buttonText: _t.html("intro.ok"), buttonCallback: onClick });
+      reveal(
+        ".surface",
+        helpHtml("intro.navigation.nodes_ways"),
+        { buttonText: _t.html("intro.ok"), buttonCallback: onClick }
+      );
       context.map().on("drawn.intro", function() {
-        reveal(".surface", helpHtml("intro.navigation.nodes_ways"), { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: onClick });
+        reveal(
+          ".surface",
+          helpHtml("intro.navigation.nodes_ways"),
+          { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: onClick }
+        );
       });
       function continueTo(nextStep) {
         context.map().on("drawn.intro", null);
@@ -52644,13 +54873,21 @@ ${content}</tr>
       var onClick = function() {
         continueTo(editorTownHall);
       };
-      reveal(box, helpHtml("intro.navigation.selected_townhall"), { buttonText: _t.html("intro.ok"), buttonCallback: onClick });
+      reveal(
+        box,
+        helpHtml("intro.navigation.selected_townhall"),
+        { buttonText: _t.html("intro.ok"), buttonCallback: onClick }
+      );
       context.map().on("move.intro drawn.intro", function() {
         var entity2 = context.hasEntity(hallId);
         if (!entity2)
           return;
         var box2 = pointBox(entity2.loc, context);
-        reveal(box2, helpHtml("intro.navigation.selected_townhall"), { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: onClick });
+        reveal(
+          box2,
+          helpHtml("intro.navigation.selected_townhall"),
+          { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: onClick }
+        );
       });
       context.history().on("change.intro", function() {
         if (!context.hasEntity(hallId)) {
@@ -52670,7 +54907,11 @@ ${content}</tr>
       var onClick = function() {
         continueTo(presetTownHall);
       };
-      reveal(".entity-editor-pane", helpHtml("intro.navigation.editor_townhall"), { buttonText: _t.html("intro.ok"), buttonCallback: onClick });
+      reveal(
+        ".entity-editor-pane",
+        helpHtml("intro.navigation.editor_townhall"),
+        { buttonText: _t.html("intro.ok"), buttonCallback: onClick }
+      );
       context.on("exit.intro", function() {
         continueTo(clickTownHall);
       });
@@ -52696,7 +54937,11 @@ ${content}</tr>
       var onClick = function() {
         continueTo(fieldsTownHall);
       };
-      reveal(".entity-editor-pane .section-feature-type", helpHtml("intro.navigation.preset_townhall", { preset: preset.name() }), { buttonText: _t.html("intro.ok"), buttonCallback: onClick });
+      reveal(
+        ".entity-editor-pane .section-feature-type",
+        helpHtml("intro.navigation.preset_townhall", { preset: preset.name() }),
+        { buttonText: _t.html("intro.ok"), buttonCallback: onClick }
+      );
       context.on("exit.intro", function() {
         continueTo(clickTownHall);
       });
@@ -52720,7 +54965,11 @@ ${content}</tr>
       var onClick = function() {
         continueTo(closeTownHall);
       };
-      reveal(".entity-editor-pane .section-preset-fields", helpHtml("intro.navigation.fields_townhall"), { buttonText: _t.html("intro.ok"), buttonCallback: onClick });
+      reveal(
+        ".entity-editor-pane .section-preset-fields",
+        helpHtml("intro.navigation.fields_townhall"),
+        { buttonText: _t.html("intro.ok"), buttonCallback: onClick }
+      );
       context.on("exit.intro", function() {
         continueTo(clickTownHall);
       });
@@ -52741,14 +54990,21 @@ ${content}</tr>
         return clickTownHall();
       var selector = ".entity-editor-pane button.close svg use";
       var href = select_default2(selector).attr("href") || "#iD-icon-close";
-      reveal(".entity-editor-pane", helpHtml("intro.navigation.close_townhall", { button: { html: icon(href, "inline") } }));
+      reveal(
+        ".entity-editor-pane",
+        helpHtml("intro.navigation.close_townhall", { button: { html: icon(href, "inline") } })
+      );
       context.on("exit.intro", function() {
         continueTo(searchStreet);
       });
       context.history().on("change.intro", function() {
         var selector2 = ".entity-editor-pane button.close svg use";
         var href2 = select_default2(selector2).attr("href") || "#iD-icon-close";
-        reveal(".entity-editor-pane", helpHtml("intro.navigation.close_townhall", { button: { html: icon(href2, "inline") } }), { duration: 0 });
+        reveal(
+          ".entity-editor-pane",
+          helpHtml("intro.navigation.close_townhall", { button: { html: icon(href2, "inline") } }),
+          { duration: 0 }
+        );
       });
       function continueTo(nextStep) {
         context.on("exit.intro", null);
@@ -52765,16 +55021,23 @@ ${content}</tr>
       }
       context.map().centerZoomEase(springStreet, 19, msec);
       timeout2(function() {
-        reveal(".search-header input", helpHtml("intro.navigation.search_street", { name: _t("intro.graph.name.spring-street") }));
+        reveal(
+          ".search-header input",
+          helpHtml("intro.navigation.search_street", { name: _t("intro.graph.name.spring-street") })
+        );
         context.container().select(".search-header input").on("keyup.intro", checkSearchResult);
       }, msec + 100);
     }
     function checkSearchResult() {
       var first = context.container().select(".feature-list-item:nth-child(0n+2)");
       var firstName = first.select(".entity-name");
-      var name2 = _t("intro.graph.name.spring-street");
-      if (!firstName.empty() && firstName.html() === name2) {
-        reveal(first.node(), helpHtml("intro.navigation.choose_street", { name: name2 }), { duration: 300 });
+      var name = _t("intro.graph.name.spring-street");
+      if (!firstName.empty() && firstName.html() === name) {
+        reveal(
+          first.node(),
+          helpHtml("intro.navigation.choose_street", { name }),
+          { duration: 300 }
+        );
         context.on("exit.intro", function() {
           continueTo(selectedStreet);
         });
@@ -52796,7 +55059,11 @@ ${content}</tr>
       var entity = context.entity(springStreetEndId);
       var box = pointBox(entity.loc, context);
       box.height = 500;
-      reveal(box, helpHtml("intro.navigation.selected_street", { name: _t("intro.graph.name.spring-street") }), { duration: 600, buttonText: _t.html("intro.ok"), buttonCallback: onClick });
+      reveal(
+        box,
+        helpHtml("intro.navigation.selected_street", { name: _t("intro.graph.name.spring-street") }),
+        { duration: 600, buttonText: _t.html("intro.ok"), buttonCallback: onClick }
+      );
       timeout2(function() {
         context.map().on("move.intro drawn.intro", function() {
           var entity2 = context.hasEntity(springStreetEndId);
@@ -52804,7 +55071,11 @@ ${content}</tr>
             return;
           var box2 = pointBox(entity2.loc, context);
           box2.height = 500;
-          reveal(box2, helpHtml("intro.navigation.selected_street", { name: _t("intro.graph.name.spring-street") }), { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: onClick });
+          reveal(
+            box2,
+            helpHtml("intro.navigation.selected_street", { name: _t("intro.graph.name.spring-street") }),
+            { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: onClick }
+          );
         });
       }, 600);
       context.on("enter.intro", function(mode) {
@@ -52835,8 +55106,8 @@ ${content}</tr>
       var href = select_default2(selector).attr("href") || "#iD-icon-close";
       reveal(".entity-editor-pane", helpHtml("intro.navigation.street_different_fields") + "{br}" + helpHtml("intro.navigation.editor_street", {
         button: { html: icon(href, "inline") },
-        field1: { html: onewayField.label() },
-        field2: { html: maxspeedField.label() }
+        field1: onewayField.title(),
+        field2: maxspeedField.title()
       }));
       context.on("exit.intro", function() {
         continueTo(play);
@@ -52844,11 +55115,15 @@ ${content}</tr>
       context.history().on("change.intro", function() {
         var selector2 = ".entity-editor-pane button.close svg use";
         var href2 = select_default2(selector2).attr("href") || "#iD-icon-close";
-        reveal(".entity-editor-pane", helpHtml("intro.navigation.street_different_fields") + "{br}" + helpHtml("intro.navigation.editor_street", {
-          button: { html: icon(href2, "inline") },
-          field1: { html: onewayField.label() },
-          field2: { html: maxspeedField.label() }
-        }), { duration: 0 });
+        reveal(
+          ".entity-editor-pane",
+          helpHtml("intro.navigation.street_different_fields") + "{br}" + helpHtml("intro.navigation.editor_street", {
+            button: { html: icon(href2, "inline") },
+            field1: onewayField.title(),
+            field2: maxspeedField.title()
+          }),
+          { duration: 0 }
+        );
       });
       function continueTo(nextStep) {
         context.on("exit.intro", null);
@@ -52858,13 +55133,17 @@ ${content}</tr>
     }
     function play() {
       dispatch10.call("done");
-      reveal(".ideditor", helpHtml("intro.navigation.play", { next: _t("intro.points.title") }), {
-        tooltipBox: ".intro-nav-wrap .chapter-point",
-        buttonText: _t.html("intro.ok"),
-        buttonCallback: function() {
-          reveal(".ideditor");
+      reveal(
+        ".ideditor",
+        helpHtml("intro.navigation.play", { next: _t("intro.points.title") }),
+        {
+          tooltipBox: ".intro-nav-wrap .chapter-point",
+          buttonText: _t.html("intro.ok"),
+          buttonCallback: function() {
+            reveal(".ideditor");
+          }
         }
-      });
+      );
     }
     chapter.enter = function() {
       dragMap();
@@ -52911,7 +55190,10 @@ ${content}</tr>
       }
       context.map().centerZoomEase(intersection, 19, msec);
       timeout2(function() {
-        var tooltip = reveal("button.add-point", helpHtml("intro.points.points_info") + "{br}" + helpHtml("intro.points.add_point"));
+        var tooltip = reveal(
+          "button.add-point",
+          helpHtml("intro.points.points_info") + "{br}" + helpHtml("intro.points.add_point")
+        );
         _pointID = null;
         tooltip.selectAll(".popover-inner").insert("svg", "span").attr("class", "tooltip-illustration").append("use").attr("xlink:href", "#iD-graphic-points");
         context.on("enter.intro", function(mode) {
@@ -52954,7 +55236,10 @@ ${content}</tr>
       }
       context.container().select(".inspector-wrap").on("wheel.intro", eventCancel);
       context.container().select(".preset-search-input").on("keydown.intro", null).on("keyup.intro", checkPresetSearch);
-      reveal(".preset-search-input", helpHtml("intro.points.search_cafe", { preset: cafePreset.name() }));
+      reveal(
+        ".preset-search-input",
+        helpHtml("intro.points.search_cafe", { preset: cafePreset.name() })
+      );
       context.on("enter.intro", function(mode) {
         if (!_pointID || !context.hasEntity(_pointID)) {
           return continueTo(addPoint);
@@ -52964,7 +55249,10 @@ ${content}</tr>
           context.enter(modeSelect(context, [_pointID]));
           context.container().select(".inspector-wrap").on("wheel.intro", eventCancel);
           context.container().select(".preset-search-input").on("keydown.intro", null).on("keyup.intro", checkPresetSearch);
-          reveal(".preset-search-input", helpHtml("intro.points.search_cafe", { preset: cafePreset.name() }));
+          reveal(
+            ".preset-search-input",
+            helpHtml("intro.points.search_cafe", { preset: cafePreset.name() })
+          );
           context.history().on("change.intro", null);
         }
       });
@@ -52972,7 +55260,11 @@ ${content}</tr>
         var first = context.container().select(".preset-list-item:first-child");
         if (first.classed("preset-amenity-cafe")) {
           context.container().select(".preset-search-input").on("keydown.intro", eventCancel, true).on("keyup.intro", null);
-          reveal(first.select(".preset-list-button").node(), helpHtml("intro.points.choose_cafe", { preset: cafePreset.name() }), { duration: 300 });
+          reveal(
+            first.select(".preset-list-button").node(),
+            helpHtml("intro.points.choose_cafe", { preset: cafePreset.name() }),
+            { duration: 300 }
+          );
           context.history().on("change.intro", function() {
             continueTo(aboutFeatureEditor);
           });
@@ -53025,7 +55317,11 @@ ${content}</tr>
           });
           tooltip.select(".instruction").style("display", "none");
         } else {
-          reveal(".entity-editor-pane", addNameString, { tooltipClass: "intro-points-describe" });
+          reveal(
+            ".entity-editor-pane",
+            addNameString,
+            { tooltipClass: "intro-points-describe" }
+          );
         }
       }, 400);
       context.history().on("change.intro", function() {
@@ -53047,7 +55343,10 @@ ${content}</tr>
       context.on("exit.intro", function() {
         continueTo(reselectPoint);
       });
-      reveal(".entity-editor-pane", helpHtml("intro.points.add_close", { button: { html: icon(href, "inline") } }));
+      reveal(
+        ".entity-editor-pane",
+        helpHtml("intro.points.add_close", { button: { html: icon(href, "inline") } })
+      );
       function continueTo(nextStep) {
         context.on("exit.intro", null);
         nextStep();
@@ -53103,7 +55402,11 @@ ${content}</tr>
         continueTo(updateCloseEditor);
       });
       timeout2(function() {
-        reveal(".entity-editor-pane", helpHtml("intro.points.update"), { tooltipClass: "intro-points-describe" });
+        reveal(
+          ".entity-editor-pane",
+          helpHtml("intro.points.update"),
+          { tooltipClass: "intro-points-describe" }
+        );
       }, 400);
       function continueTo(nextStep) {
         context.on("exit.intro", null);
@@ -53120,7 +55423,10 @@ ${content}</tr>
         continueTo(rightClickPoint);
       });
       timeout2(function() {
-        reveal(".entity-editor-pane", helpHtml("intro.points.update_close", { button: { html: icon("#iD-icon-close", "inline") } }));
+        reveal(
+          ".entity-editor-pane",
+          helpHtml("intro.points.update_close", { button: { html: icon("#iD-icon-close", "inline") } })
+        );
       }, 500);
       function continueTo(nextStep) {
         context.on("exit.intro", null);
@@ -53175,10 +55481,18 @@ ${content}</tr>
       if (!node) {
         return continueTo(rightClickPoint);
       }
-      reveal(".edit-menu", helpHtml("intro.points.delete"), { padding: 50 });
+      reveal(
+        ".edit-menu",
+        helpHtml("intro.points.delete"),
+        { padding: 50 }
+      );
       timeout2(function() {
         context.map().on("move.intro", function() {
-          reveal(".edit-menu", helpHtml("intro.points.delete"), { duration: 0, padding: 50 });
+          reveal(
+            ".edit-menu",
+            helpHtml("intro.points.delete"),
+            { duration: 0, padding: 50 }
+          );
         });
       }, 300);
       context.on("exit.intro", function() {
@@ -53204,7 +55518,10 @@ ${content}</tr>
       context.history().on("change.intro", function() {
         continueTo(play);
       });
-      reveal(".top-toolbar button.undo-button", helpHtml("intro.points.undo"));
+      reveal(
+        ".top-toolbar button.undo-button",
+        helpHtml("intro.points.undo")
+      );
       function continueTo(nextStep) {
         context.history().on("change.intro", null);
         nextStep();
@@ -53212,13 +55529,17 @@ ${content}</tr>
     }
     function play() {
       dispatch10.call("done");
-      reveal(".ideditor", helpHtml("intro.points.play", { next: _t("intro.areas.title") }), {
-        tooltipBox: ".intro-nav-wrap .chapter-area",
-        buttonText: _t.html("intro.ok"),
-        buttonCallback: function() {
-          reveal(".ideditor");
+      reveal(
+        ".ideditor",
+        helpHtml("intro.points.play", { next: _t("intro.areas.title") }),
+        {
+          tooltipBox: ".intro-nav-wrap .chapter-area",
+          buttonText: _t.html("intro.ok"),
+          buttonCallback: function() {
+            reveal(".ideditor");
+          }
         }
-      });
+      );
     }
     chapter.enter = function() {
       addPoint();
@@ -53272,7 +55593,10 @@ ${content}</tr>
       }
       context.map().centerZoomEase(playground, 19, msec);
       timeout2(function() {
-        var tooltip = reveal("button.add-area", helpHtml("intro.areas.add_playground"));
+        var tooltip = reveal(
+          "button.add-area",
+          helpHtml("intro.areas.add_playground")
+        );
         tooltip.selectAll(".popover-inner").insert("svg", "span").attr("class", "tooltip-illustration").append("use").attr("xlink:href", "#iD-graphic-areas");
         context.on("enter.intro", function(mode) {
           if (mode.id !== "add-area")
@@ -53294,10 +55618,18 @@ ${content}</tr>
       timeout2(function() {
         var textId = context.lastPointerType() === "mouse" ? "starting_node_click" : "starting_node_tap";
         var startDrawString = helpHtml("intro.areas.start_playground") + helpHtml("intro.areas." + textId);
-        revealPlayground(playground, startDrawString, { duration: 250 });
+        revealPlayground(
+          playground,
+          startDrawString,
+          { duration: 250 }
+        );
         timeout2(function() {
           context.map().on("move.intro drawn.intro", function() {
-            revealPlayground(playground, startDrawString, { duration: 0 });
+            revealPlayground(
+              playground,
+              startDrawString,
+              { duration: 0 }
+            );
           });
           context.on("enter.intro", function(mode) {
             if (mode.id !== "draw-area")
@@ -53317,10 +55649,18 @@ ${content}</tr>
         return chapter.restart();
       }
       _areaID = null;
-      revealPlayground(playground, helpHtml("intro.areas.continue_playground"), { duration: 250 });
+      revealPlayground(
+        playground,
+        helpHtml("intro.areas.continue_playground"),
+        { duration: 250 }
+      );
       timeout2(function() {
         context.map().on("move.intro drawn.intro", function() {
-          revealPlayground(playground, helpHtml("intro.areas.continue_playground"), { duration: 0 });
+          revealPlayground(
+            playground,
+            helpHtml("intro.areas.continue_playground"),
+            { duration: 0 }
+          );
         });
       }, 250);
       context.on("enter.intro", function(mode) {
@@ -53350,10 +55690,18 @@ ${content}</tr>
       }
       _areaID = null;
       var finishString = helpHtml("intro.areas.finish_area_" + (context.lastPointerType() === "mouse" ? "click" : "tap")) + helpHtml("intro.areas.finish_playground");
-      revealPlayground(playground, finishString, { duration: 250 });
+      revealPlayground(
+        playground,
+        finishString,
+        { duration: 250 }
+      );
       timeout2(function() {
         context.map().on("move.intro drawn.intro", function() {
-          revealPlayground(playground, finishString, { duration: 0 });
+          revealPlayground(
+            playground,
+            finishString,
+            { duration: 0 }
+          );
         });
       }, 250);
       context.on("enter.intro", function(mode) {
@@ -53384,7 +55732,10 @@ ${content}</tr>
       timeout2(function() {
         context.container().select(".inspector-wrap .panewrap").style("right", "-100%");
         context.container().select(".preset-search-input").on("keydown.intro", null).on("keyup.intro", checkPresetSearch);
-        reveal(".preset-search-input", helpHtml("intro.areas.search_playground", { preset: playgroundPreset.name() }));
+        reveal(
+          ".preset-search-input",
+          helpHtml("intro.areas.search_playground", { preset: playgroundPreset.name() })
+        );
       }, 400);
       context.on("enter.intro", function(mode) {
         if (!_areaID || !context.hasEntity(_areaID)) {
@@ -53396,14 +55747,21 @@ ${content}</tr>
           context.container().select(".inspector-wrap .panewrap").style("right", "-100%");
           context.container().select(".inspector-wrap").on("wheel.intro", eventCancel);
           context.container().select(".preset-search-input").on("keydown.intro", null).on("keyup.intro", checkPresetSearch);
-          reveal(".preset-search-input", helpHtml("intro.areas.search_playground", { preset: playgroundPreset.name() }));
+          reveal(
+            ".preset-search-input",
+            helpHtml("intro.areas.search_playground", { preset: playgroundPreset.name() })
+          );
           context.history().on("change.intro", null);
         }
       });
       function checkPresetSearch() {
         var first = context.container().select(".preset-list-item:first-child");
         if (first.classed("preset-leisure-playground")) {
-          reveal(first.select(".preset-list-button").node(), helpHtml("intro.areas.choose_playground", { preset: playgroundPreset.name() }), { duration: 300 });
+          reveal(
+            first.select(".preset-list-button").node(),
+            helpHtml("intro.areas.choose_playground", { preset: playgroundPreset.name() }),
+            { duration: 300 }
+          );
           context.container().select(".preset-search-input").on("keydown.intro", eventCancel, true).on("keyup.intro", null);
           context.history().on("change.intro", function() {
             continueTo(clickAddField);
@@ -53450,10 +55808,14 @@ ${content}</tr>
           });
         }
         timeout2(function() {
-          reveal(".more-fields .combobox-input", helpHtml("intro.areas.add_field", {
-            name: { html: nameField.label() },
-            description: { html: descriptionField.label() }
-          }), { duration: 300 });
+          reveal(
+            ".more-fields .combobox-input",
+            helpHtml("intro.areas.add_field", {
+              name: nameField.title(),
+              description: descriptionField.title()
+            }),
+            { duration: 300 }
+          );
           context.container().select(".more-fields .combobox-input").on("click.intro", function() {
             var watcher;
             watcher = window.setInterval(function() {
@@ -53502,7 +55864,11 @@ ${content}</tr>
           }, 300);
         }
       }, 300);
-      reveal("div.combobox", helpHtml("intro.areas.choose_field", { field: { html: descriptionField.label() } }), { duration: 300 });
+      reveal(
+        "div.combobox",
+        helpHtml("intro.areas.choose_field", { field: descriptionField.title() }),
+        { duration: 300 }
+      );
       context.on("exit.intro", function() {
         return continueTo(searchPresets);
       });
@@ -53528,7 +55894,11 @@ ${content}</tr>
       context.on("exit.intro", function() {
         continueTo(play);
       });
-      reveal(".entity-editor-pane", helpHtml("intro.areas.describe_playground", { button: { html: icon("#iD-icon-close", "inline") } }), { duration: 300 });
+      reveal(
+        ".entity-editor-pane",
+        helpHtml("intro.areas.describe_playground", { button: { html: icon("#iD-icon-close", "inline") } }),
+        { duration: 300 }
+      );
       function continueTo(nextStep) {
         context.on("exit.intro", null);
         nextStep();
@@ -53543,12 +55913,16 @@ ${content}</tr>
         return searchPresets();
       }
       context.container().select(".inspector-wrap .panewrap").style("right", "0%");
-      reveal(".entity-editor-pane", helpHtml("intro.areas.retry_add_field", { field: { html: descriptionField.label() } }), {
-        buttonText: _t.html("intro.ok"),
-        buttonCallback: function() {
-          continueTo(clickAddField);
+      reveal(
+        ".entity-editor-pane",
+        helpHtml("intro.areas.retry_add_field", { field: descriptionField.title() }),
+        {
+          buttonText: _t.html("intro.ok"),
+          buttonCallback: function() {
+            continueTo(clickAddField);
+          }
         }
-      });
+      );
       context.on("exit.intro", function() {
         return continueTo(searchPresets);
       });
@@ -53559,13 +55933,17 @@ ${content}</tr>
     }
     function play() {
       dispatch10.call("done");
-      reveal(".ideditor", helpHtml("intro.areas.play", { next: _t("intro.lines.title") }), {
-        tooltipBox: ".intro-nav-wrap .chapter-line",
-        buttonText: _t.html("intro.ok"),
-        buttonCallback: function() {
-          reveal(".ideditor");
+      reveal(
+        ".ideditor",
+        helpHtml("intro.areas.play", { next: _t("intro.lines.title") }),
+        {
+          tooltipBox: ".intro-nav-wrap .chapter-line",
+          buttonText: _t.html("intro.ok"),
+          buttonCallback: function() {
+            reveal(".ideditor");
+          }
         }
-      });
+      );
     }
     chapter.enter = function() {
       addArea();
@@ -53630,7 +56008,10 @@ ${content}</tr>
       }
       context.map().centerZoomEase(tulipRoadStart, 18.5, msec);
       timeout2(function() {
-        var tooltip = reveal("button.add-line", helpHtml("intro.lines.add_line"));
+        var tooltip = reveal(
+          "button.add-line",
+          helpHtml("intro.lines.add_line")
+        );
         tooltip.selectAll(".popover-inner").insert("svg", "span").attr("class", "tooltip-illustration").append("use").attr("xlink:href", "#iD-graphic-lines");
         context.on("enter.intro", function(mode) {
           if (mode.id !== "add-line")
@@ -53679,12 +56060,19 @@ ${content}</tr>
         var padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
         var box = pad(tulipRoadMidpoint, padding, context);
         box.height = box.height * 2;
-        reveal(box, helpHtml("intro.lines.intersect", { name: _t("intro.graph.name.flower-street") }));
+        reveal(
+          box,
+          helpHtml("intro.lines.intersect", { name: _t("intro.graph.name.flower-street") })
+        );
         context.map().on("move.intro drawn.intro", function() {
           padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
           box = pad(tulipRoadMidpoint, padding, context);
           box.height = box.height * 2;
-          reveal(box, helpHtml("intro.lines.intersect", { name: _t("intro.graph.name.flower-street") }), { duration: 0 });
+          reveal(
+            box,
+            helpHtml("intro.lines.intersect", { name: _t("intro.graph.name.flower-street") }),
+            { duration: 0 }
+          );
         });
       }, 550);
       context.history().on("change.intro", function() {
@@ -53723,7 +56111,10 @@ ${content}</tr>
     function retryIntersect() {
       select_default2(window).on("pointerdown.intro mousedown.intro", eventCancel, true);
       var box = pad(tulipRoadIntersection, 80, context);
-      reveal(box, helpHtml("intro.lines.retry_intersect", { name: _t("intro.graph.name.flower-street") }));
+      reveal(
+        box,
+        helpHtml("intro.lines.retry_intersect", { name: _t("intro.graph.name.flower-street") })
+      );
       timeout2(chapter.restart, 3e3);
     }
     function continueLine() {
@@ -53761,7 +56152,10 @@ ${content}</tr>
       context.container().select(".inspector-wrap").on("wheel.intro", eventCancel);
       timeout2(function() {
         context.container().select(".inspector-wrap .panewrap").style("right", "-100%");
-        reveal(button.node(), helpHtml("intro.lines.choose_category_road", { category: roadCategory.name() }));
+        reveal(
+          button.node(),
+          helpHtml("intro.lines.choose_category_road", { category: roadCategory.name() })
+        );
         button.on("click.intro", function() {
           continueTo(choosePresetResidential);
         });
@@ -53789,7 +56183,11 @@ ${content}</tr>
         continueTo(nameRoad);
       });
       timeout2(function() {
-        reveal(subgrid.node(), helpHtml("intro.lines.choose_preset_residential", { preset: residentialPreset.name() }), { tooltipBox: ".preset-highway-residential .preset-list-button", duration: 300 });
+        reveal(
+          subgrid.node(),
+          helpHtml("intro.lines.choose_preset_residential", { preset: residentialPreset.name() }),
+          { tooltipBox: ".preset-highway-residential .preset-list-button", duration: 300 }
+        );
       }, 300);
       function continueTo(nextStep) {
         context.container().select(".preset-list-button").on("click.intro", null);
@@ -53806,7 +56204,10 @@ ${content}</tr>
       context.container().select(".inspector-wrap").on("wheel.intro", eventCancel);
       timeout2(function() {
         var button = context.container().select(".entity-editor-pane .preset-list-button");
-        reveal(button.node(), helpHtml("intro.lines.retry_preset_residential", { preset: residentialPreset.name() }));
+        reveal(
+          button.node(),
+          helpHtml("intro.lines.retry_preset_residential", { preset: residentialPreset.name() })
+        );
         button.on("click.intro", function() {
           continueTo(chooseCategoryRoad);
         });
@@ -53823,7 +56224,11 @@ ${content}</tr>
         continueTo(didNameRoad);
       });
       timeout2(function() {
-        reveal(".entity-editor-pane", helpHtml("intro.lines.name_road", { button: { html: icon("#iD-icon-close", "inline") } }), { tooltipClass: "intro-lines-name_road" });
+        reveal(
+          ".entity-editor-pane",
+          helpHtml("intro.lines.name_road", { button: { html: icon("#iD-icon-close", "inline") } }),
+          { tooltipClass: "intro-lines-name_road" }
+        );
       }, 500);
       function continueTo(nextStep) {
         context.on("exit.intro", null);
@@ -53860,11 +56265,19 @@ ${content}</tr>
         var advance = function() {
           continueTo(addNode);
         };
-        reveal(box, helpHtml("intro.lines.update_line"), { buttonText: _t.html("intro.ok"), buttonCallback: advance });
+        reveal(
+          box,
+          helpHtml("intro.lines.update_line"),
+          { buttonText: _t.html("intro.ok"), buttonCallback: advance }
+        );
         context.map().on("move.intro drawn.intro", function() {
           var padding2 = 250 * Math.pow(2, context.map().zoom() - 19);
           var box2 = pad(woodRoadDragMidpoint, padding2, context);
-          reveal(box2, helpHtml("intro.lines.update_line"), { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: advance });
+          reveal(
+            box2,
+            helpHtml("intro.lines.update_line"),
+            { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: advance }
+          );
         });
       }, msec + 100);
       function continueTo(nextStep) {
@@ -54008,7 +56421,11 @@ ${content}</tr>
         context.history().checkpoint("doneUpdateLine");
         continueTo(deleteLines);
       };
-      reveal(box, helpHtml("intro.lines.continue_drag_midpoint"), { buttonText: _t.html("intro.ok"), buttonCallback: advance });
+      reveal(
+        box,
+        helpHtml("intro.lines.continue_drag_midpoint"),
+        { buttonText: _t.html("intro.ok"), buttonCallback: advance }
+      );
       context.map().on("move.intro drawn.intro", function() {
         if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
           return continueTo(updateLine);
@@ -54016,7 +56433,11 @@ ${content}</tr>
         var padding2 = 100 * Math.pow(2, context.map().zoom() - 19);
         var box2 = pad(woodRoadDragEndpoint, padding2, context);
         box2.height += 400;
-        reveal(box2, helpHtml("intro.lines.continue_drag_midpoint"), { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: advance });
+        reveal(
+          box2,
+          helpHtml("intro.lines.continue_drag_midpoint"),
+          { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: advance }
+        );
       });
       function continueTo(nextStep) {
         context.map().on("move.intro drawn.intro", null);
@@ -54042,13 +56463,21 @@ ${content}</tr>
         var advance = function() {
           continueTo(rightClickIntersection);
         };
-        reveal(box, helpHtml("intro.lines.delete_lines", { street: _t("intro.graph.name.12th-avenue") }), { buttonText: _t.html("intro.ok"), buttonCallback: advance });
+        reveal(
+          box,
+          helpHtml("intro.lines.delete_lines", { street: _t("intro.graph.name.12th-avenue") }),
+          { buttonText: _t.html("intro.ok"), buttonCallback: advance }
+        );
         context.map().on("move.intro drawn.intro", function() {
           var padding2 = 200 * Math.pow(2, context.map().zoom() - 18);
           var box2 = pad(deleteLinesLoc, padding2, context);
           box2.top -= 200;
           box2.height += 400;
-          reveal(box2, helpHtml("intro.lines.delete_lines", { street: _t("intro.graph.name.12th-avenue") }), { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: advance });
+          reveal(
+            box2,
+            helpHtml("intro.lines.delete_lines", { street: _t("intro.graph.name.12th-avenue") }),
+            { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: advance }
+          );
         });
         context.history().on("change.intro", function() {
           timeout2(function() {
@@ -54077,7 +56506,11 @@ ${content}</tr>
         context.map().on("move.intro drawn.intro", function() {
           var padding2 = 60 * Math.pow(2, context.map().zoom() - 18);
           var box2 = pad(eleventhAvenueEnd, padding2, context);
-          reveal(box2, rightClickString, { duration: 0 });
+          reveal(
+            box2,
+            rightClickString,
+            { duration: 0 }
+          );
         });
         context.on("enter.intro", function(mode) {
           if (mode.id !== "select")
@@ -54115,13 +56548,27 @@ ${content}</tr>
       }
       var wasChanged = false;
       _washingtonSegmentID = null;
-      reveal(".edit-menu", helpHtml("intro.lines.split_intersection", { street: _t("intro.graph.name.washington-street") }), { padding: 50 });
+      reveal(
+        ".edit-menu",
+        helpHtml(
+          "intro.lines.split_intersection",
+          { street: _t("intro.graph.name.washington-street") }
+        ),
+        { padding: 50 }
+      );
       context.map().on("move.intro drawn.intro", function() {
         var node2 = selectMenuItem(context, "split").node();
         if (!wasChanged && !node2) {
           return continueTo(rightClickIntersection);
         }
-        reveal(".edit-menu", helpHtml("intro.lines.split_intersection", { street: _t("intro.graph.name.washington-street") }), { duration: 0, padding: 50 });
+        reveal(
+          ".edit-menu",
+          helpHtml(
+            "intro.lines.split_intersection",
+            { street: _t("intro.graph.name.washington-street") }
+          ),
+          { duration: 0, padding: 50 }
+        );
       });
       context.history().on("change.intro", function(changed) {
         wasChanged = true;
@@ -54149,11 +56596,19 @@ ${content}</tr>
       };
       var padding = 60 * Math.pow(2, context.map().zoom() - 18);
       var box = pad(eleventhAvenueEnd, padding, context);
-      reveal(box, helpHtml("intro.lines.retry_split"), { buttonText: _t.html("intro.ok"), buttonCallback: advance });
+      reveal(
+        box,
+        helpHtml("intro.lines.retry_split"),
+        { buttonText: _t.html("intro.ok"), buttonCallback: advance }
+      );
       context.map().on("move.intro drawn.intro", function() {
         var padding2 = 60 * Math.pow(2, context.map().zoom() - 18);
         var box2 = pad(eleventhAvenueEnd, padding2, context);
-        reveal(box2, helpHtml("intro.lines.retry_split"), { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: advance });
+        reveal(
+          box2,
+          helpHtml("intro.lines.retry_split"),
+          { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: advance }
+        );
       });
       function continueTo(nextStep) {
         context.map().on("move.intro drawn.intro", null);
@@ -54170,14 +56625,22 @@ ${content}</tr>
       var padding = 200 * Math.pow(2, context.map().zoom() - 18);
       var box = pad(twelfthAvenue, padding, context);
       box.width = box.width / 2;
-      reveal(box, helpHtml(string, { street1: street, street2: street }), { duration: 500 });
+      reveal(
+        box,
+        helpHtml(string, { street1: street, street2: street }),
+        { duration: 500 }
+      );
       timeout2(function() {
         context.map().centerZoomEase(twelfthAvenue, 18, 500);
         context.map().on("move.intro drawn.intro", function() {
           var padding2 = 200 * Math.pow(2, context.map().zoom() - 18);
           var box2 = pad(twelfthAvenue, padding2, context);
           box2.width = box2.width / 2;
-          reveal(box2, helpHtml(string, { street1: street, street2: street }), { duration: 0 });
+          reveal(
+            box2,
+            helpHtml(string, { street1: street, street2: street }),
+            { duration: 0 }
+          );
         });
       }, 600);
       context.on("enter.intro", function() {
@@ -54226,7 +56689,16 @@ ${content}</tr>
           box = pad(twelfthAvenue, padding, context);
           box.width /= 2;
         }
-        reveal(box, helpHtml("intro.lines.multi_select", { selected, other1: other }) + " " + helpHtml("intro.lines.add_to_selection_" + (context.lastPointerType() === "mouse" ? "click" : "touch"), { selected, other2: other }));
+        reveal(
+          box,
+          helpHtml(
+            "intro.lines.multi_select",
+            { selected, other1: other }
+          ) + " " + helpHtml(
+            "intro.lines.add_to_selection_" + (context.lastPointerType() === "mouse" ? "click" : "touch"),
+            { selected, other2: other }
+          )
+        );
         context.map().on("move.intro drawn.intro", function() {
           if (hasWashington) {
             selected = _t("intro.graph.name.washington-street");
@@ -54241,7 +56713,17 @@ ${content}</tr>
             box = pad(twelfthAvenue, padding, context);
             box.width /= 2;
           }
-          reveal(box, helpHtml("intro.lines.multi_select", { selected, other1: other }) + " " + helpHtml("intro.lines.add_to_selection_" + (context.lastPointerType() === "mouse" ? "click" : "touch"), { selected, other2: other }), { duration: 0 });
+          reveal(
+            box,
+            helpHtml(
+              "intro.lines.multi_select",
+              { selected, other1: other }
+            ) + " " + helpHtml(
+              "intro.lines.add_to_selection_" + (context.lastPointerType() === "mouse" ? "click" : "touch"),
+              { selected, other2: other }
+            ),
+            { duration: 0 }
+          );
         });
         context.on("enter.intro", function() {
           continueTo(multiSelect);
@@ -54308,9 +56790,17 @@ ${content}</tr>
       var node = selectMenuItem(context, "delete").node();
       if (!node)
         return continueTo(multiRightClick);
-      reveal(".edit-menu", helpHtml("intro.lines.multi_delete"), { padding: 50 });
+      reveal(
+        ".edit-menu",
+        helpHtml("intro.lines.multi_delete"),
+        { padding: 50 }
+      );
       context.map().on("move.intro drawn.intro", function() {
-        reveal(".edit-menu", helpHtml("intro.lines.multi_delete"), { duration: 0, padding: 50 });
+        reveal(
+          ".edit-menu",
+          helpHtml("intro.lines.multi_delete"),
+          { duration: 0, padding: 50 }
+        );
       });
       context.on("exit.intro", function() {
         if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
@@ -54347,13 +56837,17 @@ ${content}</tr>
     }
     function play() {
       dispatch10.call("done");
-      reveal(".ideditor", helpHtml("intro.lines.play", { next: _t("intro.buildings.title") }), {
-        tooltipBox: ".intro-nav-wrap .chapter-building",
-        buttonText: _t.html("intro.ok"),
-        buttonCallback: function() {
-          reveal(".ideditor");
+      reveal(
+        ".ideditor",
+        helpHtml("intro.lines.play", { next: _t("intro.buildings.title") }),
+        {
+          tooltipBox: ".intro-nav-wrap .chapter-building",
+          buttonText: _t.html("intro.ok"),
+          buttonCallback: function() {
+            reveal(".ideditor");
+          }
         }
-      });
+      );
     }
     chapter.enter = function() {
       addLine();
@@ -54415,7 +56909,10 @@ ${content}</tr>
       }
       context.map().centerZoomEase(house, 19, msec);
       timeout2(function() {
-        var tooltip = reveal("button.add-area", helpHtml("intro.buildings.add_building"));
+        var tooltip = reveal(
+          "button.add-area",
+          helpHtml("intro.buildings.add_building")
+        );
         tooltip.selectAll(".popover-inner").insert("svg", "span").attr("class", "tooltip-illustration").append("use").attr("xlink:href", "#iD-graphic-buildings");
         context.on("enter.intro", function(mode) {
           if (mode.id !== "add-area")
@@ -54492,9 +56989,17 @@ ${content}</tr>
       var onClick = function() {
         continueTo(addHouse);
       };
-      revealHouse(house, helpHtml("intro.buildings.retry_building"), { buttonText: _t.html("intro.ok"), buttonCallback: onClick });
+      revealHouse(
+        house,
+        helpHtml("intro.buildings.retry_building"),
+        { buttonText: _t.html("intro.ok"), buttonCallback: onClick }
+      );
       context.map().on("move.intro drawn.intro", function() {
-        revealHouse(house, helpHtml("intro.buildings.retry_building"), { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: onClick });
+        revealHouse(
+          house,
+          helpHtml("intro.buildings.retry_building"),
+          { duration: 0, buttonText: _t.html("intro.ok"), buttonCallback: onClick }
+        );
       });
       function continueTo(nextStep) {
         context.map().on("move.intro drawn.intro", null);
@@ -54513,7 +57018,10 @@ ${content}</tr>
       timeout2(function() {
         context.container().select(".inspector-wrap .panewrap").style("right", "-100%");
         var button = context.container().select(".preset-category-building .preset-list-button");
-        reveal(button.node(), helpHtml("intro.buildings.choose_category_building", { category: buildingCatetory.name() }));
+        reveal(
+          button.node(),
+          helpHtml("intro.buildings.choose_category_building", { category: buildingCatetory.name() })
+        );
         button.on("click.intro", function() {
           button.on("click.intro", null);
           continueTo(choosePresetHouse);
@@ -54547,7 +57055,11 @@ ${content}</tr>
       timeout2(function() {
         context.container().select(".inspector-wrap .panewrap").style("right", "-100%");
         var button = context.container().select(".preset-building-house .preset-list-button");
-        reveal(button.node(), helpHtml("intro.buildings.choose_preset_house", { preset: housePreset.name() }), { duration: 300 });
+        reveal(
+          button.node(),
+          helpHtml("intro.buildings.choose_preset_house", { preset: housePreset.name() }),
+          { duration: 300 }
+        );
         button.on("click.intro", function() {
           button.on("click.intro", null);
           continueTo(closeEditorHouse);
@@ -54582,7 +57094,10 @@ ${content}</tr>
         continueTo(rightClickHouse);
       });
       timeout2(function() {
-        reveal(".entity-editor-pane", helpHtml("intro.buildings.close", { button: { html: icon("#iD-icon-close", "inline") } }));
+        reveal(
+          ".entity-editor-pane",
+          helpHtml("intro.buildings.close", { button: { html: icon("#iD-icon-close", "inline") } })
+        );
       }, 500);
       function continueTo(nextStep) {
         context.on("exit.intro", null);
@@ -54637,7 +57152,11 @@ ${content}</tr>
         return continueTo(rightClickHouse);
       }
       var wasChanged = false;
-      reveal(".edit-menu", helpHtml("intro.buildings.square_building"), { padding: 50 });
+      reveal(
+        ".edit-menu",
+        helpHtml("intro.buildings.square_building"),
+        { padding: 50 }
+      );
       context.on("enter.intro", function(mode) {
         if (mode.id === "browse") {
           continueTo(rightClickHouse);
@@ -54650,7 +57169,11 @@ ${content}</tr>
         if (!wasChanged && !node2) {
           return continueTo(rightClickHouse);
         }
-        reveal(".edit-menu", helpHtml("intro.buildings.square_building"), { duration: 0, padding: 50 });
+        reveal(
+          ".edit-menu",
+          helpHtml("intro.buildings.square_building"),
+          { duration: 0, padding: 50 }
+        );
       });
       context.history().on("change.intro", function() {
         wasChanged = true;
@@ -54704,7 +57227,10 @@ ${content}</tr>
       }
       context.map().centerZoomEase(tank, 19.5, msec);
       timeout2(function() {
-        reveal("button.add-area", helpHtml("intro.buildings.add_tank"));
+        reveal(
+          "button.add-area",
+          helpHtml("intro.buildings.add_tank")
+        );
         context.on("enter.intro", function(mode) {
           if (mode.id !== "add-area")
             return;
@@ -54777,7 +57303,10 @@ ${content}</tr>
       timeout2(function() {
         context.container().select(".inspector-wrap .panewrap").style("right", "-100%");
         context.container().select(".preset-search-input").on("keydown.intro", null).on("keyup.intro", checkPresetSearch);
-        reveal(".preset-search-input", helpHtml("intro.buildings.search_tank", { preset: tankPreset.name() }));
+        reveal(
+          ".preset-search-input",
+          helpHtml("intro.buildings.search_tank", { preset: tankPreset.name() })
+        );
       }, 400);
       context.on("enter.intro", function(mode) {
         if (!_tankID || !context.hasEntity(_tankID)) {
@@ -54789,14 +57318,21 @@ ${content}</tr>
           context.container().select(".inspector-wrap .panewrap").style("right", "-100%");
           context.container().select(".inspector-wrap").on("wheel.intro", eventCancel);
           context.container().select(".preset-search-input").on("keydown.intro", null).on("keyup.intro", checkPresetSearch);
-          reveal(".preset-search-input", helpHtml("intro.buildings.search_tank", { preset: tankPreset.name() }));
+          reveal(
+            ".preset-search-input",
+            helpHtml("intro.buildings.search_tank", { preset: tankPreset.name() })
+          );
           context.history().on("change.intro", null);
         }
       });
       function checkPresetSearch() {
         var first = context.container().select(".preset-list-item:first-child");
         if (first.classed("preset-man_made-storage_tank")) {
-          reveal(first.select(".preset-list-button").node(), helpHtml("intro.buildings.choose_tank", { preset: tankPreset.name() }), { duration: 300 });
+          reveal(
+            first.select(".preset-list-button").node(),
+            helpHtml("intro.buildings.choose_tank", { preset: tankPreset.name() }),
+            { duration: 300 }
+          );
           context.container().select(".preset-search-input").on("keydown.intro", eventCancel, true).on("keyup.intro", null);
           context.history().on("change.intro", function() {
             continueTo(closeEditorTank);
@@ -54824,7 +57360,10 @@ ${content}</tr>
         continueTo(rightClickTank);
       });
       timeout2(function() {
-        reveal(".entity-editor-pane", helpHtml("intro.buildings.close", { button: { html: icon("#iD-icon-close", "inline") } }));
+        reveal(
+          ".entity-editor-pane",
+          helpHtml("intro.buildings.close", { button: { html: icon("#iD-icon-close", "inline") } })
+        );
       }, 500);
       function continueTo(nextStep) {
         context.on("exit.intro", null);
@@ -54878,7 +57417,11 @@ ${content}</tr>
         return continueTo(rightClickTank);
       }
       var wasChanged = false;
-      reveal(".edit-menu", helpHtml("intro.buildings.circle_tank"), { padding: 50 });
+      reveal(
+        ".edit-menu",
+        helpHtml("intro.buildings.circle_tank"),
+        { padding: 50 }
+      );
       context.on("enter.intro", function(mode) {
         if (mode.id === "browse") {
           continueTo(rightClickTank);
@@ -54891,7 +57434,11 @@ ${content}</tr>
         if (!wasChanged && !node2) {
           return continueTo(rightClickTank);
         }
-        reveal(".edit-menu", helpHtml("intro.buildings.circle_tank"), { duration: 0, padding: 50 });
+        reveal(
+          ".edit-menu",
+          helpHtml("intro.buildings.circle_tank"),
+          { duration: 0, padding: 50 }
+        );
       });
       context.history().on("change.intro", function() {
         wasChanged = true;
@@ -54925,13 +57472,17 @@ ${content}</tr>
     }
     function play() {
       dispatch10.call("done");
-      reveal(".ideditor", helpHtml("intro.buildings.play", { next: _t("intro.startediting.title") }), {
-        tooltipBox: ".intro-nav-wrap .chapter-startEditing",
-        buttonText: _t.html("intro.ok"),
-        buttonCallback: function() {
-          reveal(".ideditor");
+      reveal(
+        ".ideditor",
+        helpHtml("intro.buildings.play", { next: _t("intro.startediting.title") }),
+        {
+          tooltipBox: ".intro-nav-wrap .chapter-startEditing",
+          buttonText: _t.html("intro.ok"),
+          buttonCallback: function() {
+            reveal(".ideditor");
+          }
         }
-      });
+      );
     }
     chapter.enter = function() {
       addHouse();
@@ -54960,29 +57511,41 @@ ${content}</tr>
       title: "intro.startediting.title"
     };
     function showHelp() {
-      reveal(".map-control.help-control", helpHtml("intro.startediting.help"), {
-        buttonText: _t.html("intro.ok"),
-        buttonCallback: function() {
-          shortcuts();
+      reveal(
+        ".map-control.help-control",
+        helpHtml("intro.startediting.help"),
+        {
+          buttonText: _t.html("intro.ok"),
+          buttonCallback: function() {
+            shortcuts();
+          }
         }
-      });
+      );
     }
     function shortcuts() {
-      reveal(".map-control.help-control", helpHtml("intro.startediting.shortcuts"), {
-        buttonText: _t.html("intro.ok"),
-        buttonCallback: function() {
-          showSave();
+      reveal(
+        ".map-control.help-control",
+        helpHtml("intro.startediting.shortcuts"),
+        {
+          buttonText: _t.html("intro.ok"),
+          buttonCallback: function() {
+            showSave();
+          }
         }
-      });
+      );
     }
     function showSave() {
       context.container().selectAll(".shaded").remove();
-      reveal(".top-toolbar button.save", helpHtml("intro.startediting.save"), {
-        buttonText: _t.html("intro.ok"),
-        buttonCallback: function() {
-          showStart();
+      reveal(
+        ".top-toolbar button.save",
+        helpHtml("intro.startediting.save"),
+        {
+          buttonText: _t.html("intro.ok"),
+          buttonCallback: function() {
+            showStart();
+          }
         }
-      });
+      );
     }
     function showStart() {
       context.container().selectAll(".shaded").remove();
@@ -55175,7 +57738,7 @@ ${content}</tr>
         return "chip " + d.id + "-count";
       }).attr("href", "#").each(function(d) {
         var chipSelection = select_default2(this);
-        var tooltipBehavior = uiTooltip().placement("top").title(_t.html(d.descriptionID));
+        var tooltipBehavior = uiTooltip().placement("top").title(() => _t.append(d.descriptionID));
         chipSelection.call(tooltipBehavior).on("click", function(d3_event) {
           d3_event.preventDefault();
           tooltipBehavior.hide(select_default2(this));
@@ -55275,10 +57838,10 @@ ${content}</tr>
         var zMini = Math.max(zMain - _zDiff, 0.5);
         var kMini = geoZoomToScale(zMini);
         projection2.translate([tMain.x, tMain.y]).scale(kMini);
-        var point = projection2(loc);
+        var point2 = projection2(loc);
         var mouse = _gesture === "pan" ? geoVecSubtract([_tCurr.x, _tCurr.y], [_tStart.x, _tStart.y]) : [0, 0];
-        var xMini = _cMini[0] - point[0] + tMain.x + mouse[0];
-        var yMini = _cMini[1] - point[1] + tMain.y + mouse[1];
+        var xMini = _cMini[0] - point2[0] + tMain.x + mouse[0];
+        var yMini = _cMini[1] - point2[1] + tMain.y + mouse[1];
         projection2.translate([xMini, yMini]).clipExtent([[0, 0], _dMini]);
         _tCurr = projection2.transform();
         if (_isTransformed) {
@@ -55325,10 +57888,10 @@ ${content}</tr>
         dataLayers = dataLayers.enter().append("svg").attr("class", "map-in-map-data").merge(dataLayers).call(dataLayer).call(debugLayer);
         if (_gesture !== "pan") {
           var getPath = path_default(projection2);
-          var bbox = { type: "Polygon", coordinates: [context.map().extent().polygon()] };
+          var bbox2 = { type: "Polygon", coordinates: [context.map().extent().polygon()] };
           viewport = wrap2.selectAll(".map-in-map-viewport").data([0]);
           viewport = viewport.enter().append("svg").attr("class", "map-in-map-viewport").merge(viewport);
-          var path = viewport.selectAll(".map-in-map-bbox").data([bbox]);
+          var path = viewport.selectAll(".map-in-map-bbox").data([bbox2]);
           path.enter().append("path").attr("class", "map-in-map-bbox").merge(path).attr("d", getPath).classed("thick", function(d) {
             return getPath.area(d) < 30;
           });
@@ -55410,9 +57973,18 @@ ${content}</tr>
       function preventDefault(d3_event) {
         d3_event.preventDefault();
       }
-      selection2.append("button").attr("class", "resize-handle-xy").on("touchstart touchdown touchend", preventDefault).on(_pointerPrefix + "down", buildResizeListener(selection2, "resize", dispatch10, { resizeOnX: true, resizeOnY: true }));
-      selection2.append("button").attr("class", "resize-handle-x").on("touchstart touchdown touchend", preventDefault).on(_pointerPrefix + "down", buildResizeListener(selection2, "resize", dispatch10, { resizeOnX: true }));
-      selection2.append("button").attr("class", "resize-handle-y").on("touchstart touchdown touchend", preventDefault).on(_pointerPrefix + "down", buildResizeListener(selection2, "resize", dispatch10, { resizeOnY: true }));
+      selection2.append("button").attr("class", "resize-handle-xy").on("touchstart touchdown touchend", preventDefault).on(
+        _pointerPrefix + "down",
+        buildResizeListener(selection2, "resize", dispatch10, { resizeOnX: true, resizeOnY: true })
+      );
+      selection2.append("button").attr("class", "resize-handle-x").on("touchstart touchdown touchend", preventDefault).on(
+        _pointerPrefix + "down",
+        buildResizeListener(selection2, "resize", dispatch10, { resizeOnX: true })
+      );
+      selection2.append("button").attr("class", "resize-handle-y").on("touchstart touchdown touchend", preventDefault).on(
+        _pointerPrefix + "down",
+        buildResizeListener(selection2, "resize", dispatch10, { resizeOnY: true })
+      );
       function buildResizeListener(target, eventName, dispatch11, options2) {
         var resizeOnX = !!options2.resizeOnX;
         var resizeOnY = !!options2.resizeOnY;
@@ -55710,9 +58282,12 @@ ${content}</tr>
   function uiDataHeader() {
     var _datum;
     function dataHeader(selection2) {
-      var header = selection2.selectAll(".data-header").data(_datum ? [_datum] : [], function(d) {
-        return d.__featurehash__;
-      });
+      var header = selection2.selectAll(".data-header").data(
+        _datum ? [_datum] : [],
+        function(d) {
+          return d.__featurehash__;
+        }
+      );
       header.exit().remove();
       var headerEnter = header.enter().append("div").attr("class", "data-header");
       var iconEnter = headerEnter.append("div").attr("class", "data-header-icon");
@@ -55731,7 +58306,7 @@ ${content}</tr>
   // modules/ui/combobox.js
   var _comboHideTimerID;
   function uiCombobox(context, klass) {
-    var dispatch10 = dispatch_default("accept", "cancel");
+    var dispatch10 = dispatch_default("accept", "cancel", "update");
     var container = context.container();
     var _suggestions = [];
     var _data = [];
@@ -55747,6 +58322,9 @@ ${content}</tr>
       cb(_data.filter(function(d) {
         var terms = d.terms || [];
         terms.push(d.value);
+        if (d.key) {
+          terms.push(d.key);
+        }
         return terms.some(function(term) {
           return term.toString().toLowerCase().indexOf(val.toLowerCase()) !== -1;
         });
@@ -55847,6 +58425,7 @@ ${content}</tr>
               var start2 = input.property("selectionStart");
               input.node().setSelectionRange(start2, start2);
               input.on("input.combo-input", change);
+              change(false);
             });
             break;
           case 9:
@@ -55855,6 +58434,7 @@ ${content}</tr>
           case 13:
             d3_event.preventDefault();
             d3_event.stopPropagation();
+            accept(d3_event);
             break;
           case 38:
             if (tagName === "textarea" && !shown)
@@ -55881,17 +58461,16 @@ ${content}</tr>
           case 27:
             cancel();
             break;
-          case 13:
-            accept(d3_event);
-            break;
         }
       }
-      function change() {
+      function change(doAutoComplete) {
+        if (doAutoComplete === void 0)
+          doAutoComplete = true;
         fetchComboData(value(), function() {
           _selected = null;
           var val = input.property("value");
           if (_suggestions.length) {
-            if (input.property("selectionEnd") === val.length) {
+            if (doAutoComplete && input.property("selectionEnd") === val.length) {
               _selected = tryAutocomplete();
             }
             if (!_selected) {
@@ -55920,7 +58499,8 @@ ${content}</tr>
           }
           index = Math.max(Math.min(index + dir, _suggestions.length - 1), 0);
           _selected = _suggestions[index].value;
-          input.property("value", _selected);
+          utilGetSetValue(input, _selected);
+          dispatch10.call("update");
         }
         render();
         ensureVisible();
@@ -55970,11 +58550,18 @@ ${content}</tr>
         var val = _caseSensitive ? value() : value().toLowerCase();
         if (!val)
           return;
-        if (!isNaN(parseFloat(val)) && isFinite(val))
+        if (isFinite(val))
           return;
+        const suggestionValues = [];
+        _suggestions.forEach((s) => {
+          suggestionValues.push(s.value);
+          if (s.key && s.key !== s.value) {
+            suggestionValues.push(s.key);
+          }
+        });
         var bestIndex = -1;
-        for (var i2 = 0; i2 < _suggestions.length; i2++) {
-          var suggestion = _suggestions[i2].value;
+        for (var i2 = 0; i2 < suggestionValues.length; i2++) {
+          var suggestion = suggestionValues[i2];
           var compare = _caseSensitive ? suggestion : suggestion.toLowerCase();
           if (compare === val) {
             bestIndex = i2;
@@ -55984,9 +58571,10 @@ ${content}</tr>
           }
         }
         if (bestIndex !== -1) {
-          var bestVal = _suggestions[bestIndex].value;
+          var bestVal = suggestionValues[bestIndex];
           input.property("value", bestVal);
           input.node().setSelectionRange(val.length, bestVal.length);
+          dispatch10.call("update");
           return bestVal;
         }
       }
@@ -56007,10 +58595,14 @@ ${content}</tr>
           return "combobox-option " + (d.klass || "");
         }).attr("title", function(d) {
           return d.title;
-        }).html(function(d) {
-          return d.display || d.value;
+        }).each(function(d) {
+          if (d.display) {
+            d.display(select_default2(this));
+          } else {
+            select_default2(this).text(d.value);
+          }
         }).on("mouseenter", _mouseEnterHandler).on("mouseleave", _mouseLeaveHandler).merge(options2).classed("selected", function(d) {
-          return d.value === _selected;
+          return d.value === _selected || d.key === _selected;
         }).on("click.combo-option", accept).order();
         var node = attachTo ? attachTo.node() : input.node();
         var containerRect = container.node().getBoundingClientRect();
@@ -56110,8 +58702,17 @@ ${content}</tr>
       hideToggleEnter.append("span").attr("class", "hide-toggle-text");
       hideToggle = hideToggleEnter.merge(hideToggle);
       hideToggle.on("click", toggle).attr("title", _t(`icons.${_expanded ? "collapse" : "expand"}`)).attr("aria-expanded", _expanded).classed("expanded", _expanded);
-      hideToggle.selectAll(".hide-toggle-text").html(_label());
-      hideToggle.selectAll(".hide-toggle-icon").attr("xlink:href", _expanded ? "#iD-icon-down" : _mainLocalizer.textDirection() === "rtl" ? "#iD-icon-backward" : "#iD-icon-forward");
+      const label = _label();
+      const labelSelection = hideToggle.selectAll(".hide-toggle-text");
+      if (typeof label !== "function") {
+        labelSelection.text(_label());
+      } else {
+        labelSelection.text("").call(label);
+      }
+      hideToggle.selectAll(".hide-toggle-icon").attr(
+        "xlink:href",
+        _expanded ? "#iD-icon-down" : _mainLocalizer.textDirection() === "rtl" ? "#iD-icon-backward" : "#iD-icon-forward"
+      );
       var wrap2 = selection2.selectAll(".disclosure-wrap").data([0]);
       wrap2 = wrap2.enter().append("div").attr("class", "disclosure-wrap disclosure-wrap-" + key).merge(wrap2).classed("hide", !_expanded);
       if (_expanded) {
@@ -56124,7 +58725,10 @@ ${content}</tr>
           corePreferences("disclosure." + key + ".expanded", _expanded);
         }
         hideToggle.classed("expanded", _expanded).attr("aria-expanded", _expanded).attr("title", _t(`icons.${_expanded ? "collapse" : "expand"}`));
-        hideToggle.selectAll(".hide-toggle-icon").attr("xlink:href", _expanded ? "#iD-icon-down" : _mainLocalizer.textDirection() === "rtl" ? "#iD-icon-backward" : "#iD-icon-forward");
+        hideToggle.selectAll(".hide-toggle-icon").attr(
+          "xlink:href",
+          _expanded ? "#iD-icon-down" : _mainLocalizer.textDirection() === "rtl" ? "#iD-icon-backward" : "#iD-icon-forward"
+        );
         wrap2.call(uiToggle(_expanded));
         if (_expanded) {
           wrap2.call(_content);
@@ -56521,11 +59125,15 @@ ${content}</tr>
     var _impliedYes;
     var _entityIDs = [];
     var _value;
+    var stringsField = field.resolveReference("stringsCrossReference");
+    if (!options2 && stringsField.options) {
+      options2 = stringsField.options;
+    }
     if (options2) {
       for (var i2 in options2) {
         var v = options2[i2];
         values.push(v === "undefined" ? void 0 : v);
-        texts.push(field.t.html("options." + v, { "default": v }));
+        texts.push(stringsField.t.html("options." + v, { "default": v }));
       }
     } else {
       values = [void 0, "yes"];
@@ -56598,12 +59206,15 @@ ${content}</tr>
         reverser.call(reverserSetText).on("click", function(d3_event) {
           d3_event.preventDefault();
           d3_event.stopPropagation();
-          context.perform(function(graph) {
-            for (var i3 in _entityIDs) {
-              graph = actionReverse(_entityIDs[i3])(graph);
-            }
-            return graph;
-          }, _t("operations.reverse.annotation.line", { n: 1 }));
+          context.perform(
+            function(graph) {
+              for (var i3 in _entityIDs) {
+                graph = actionReverse(_entityIDs[i3])(graph);
+              }
+              return graph;
+            },
+            _t("operations.reverse.annotation.line", { n: 1 })
+          );
           context.validator().validate();
           select_default2(this).call(reverserSetText);
         });
@@ -56645,20 +59256,71 @@ ${content}</tr>
     return utilRebind(check, dispatch10, "on");
   }
 
+  // modules/ui/length_indicator.js
+  function uiLengthIndicator(maxChars) {
+    var _wrap = select_default2(null);
+    var _tooltip = uiPopover("tooltip max-length-warning").placement("bottom").hasArrow(true).content(() => (selection2) => {
+      selection2.text("");
+      selection2.call(svgIcon("#iD-icon-alert", "inline"));
+      selection2.call(_t.append("inspector.max_length_reached", { maxChars }));
+    });
+    var _silent = false;
+    var lengthIndicator = function(selection2) {
+      _wrap = selection2.selectAll("span.length-indicator-wrap").data([0]);
+      _wrap = _wrap.enter().append("span").merge(_wrap).classed("length-indicator-wrap", true);
+      selection2.call(_tooltip);
+    };
+    lengthIndicator.update = function(val) {
+      const strLen = utilUnicodeCharsCount(utilCleanOsmString(val, Number.POSITIVE_INFINITY));
+      let indicator = _wrap.selectAll("span.length-indicator").data([strLen]);
+      indicator.enter().append("span").merge(indicator).classed("length-indicator", true).classed("limit-reached", (d) => d > maxChars).style("border-right-width", (d) => `${Math.abs(maxChars - d) * 2}px`).style("margin-right", (d) => d > maxChars ? `${(maxChars - d) * 2}px` : 0).style("opacity", (d) => d > maxChars * 0.8 ? Math.min(1, (d / maxChars - 0.8) / (1 - 0.8)) : 0).style("pointer-events", (d) => d > maxChars * 0.8 ? null : "none");
+      if (_silent)
+        return;
+      if (strLen > maxChars) {
+        _tooltip.show();
+      } else {
+        _tooltip.hide();
+      }
+    };
+    lengthIndicator.silent = function(val) {
+      if (!arguments.length)
+        return _silent;
+      _silent = val;
+      return lengthIndicator;
+    };
+    return lengthIndicator;
+  }
+
   // modules/ui/fields/combo.js
+  var valueIcons = {
+    "crossing:markings": [
+      "dashes",
+      "dots",
+      "ladder:paired",
+      "ladder:skewed",
+      "ladder",
+      "lines:paired",
+      "lines",
+      "surface",
+      "zebra:bicolour",
+      "zebra:double",
+      "zebra:paired",
+      "zebra"
+    ]
+  };
   function uiFieldCombo(field, context) {
     var dispatch10 = dispatch_default("change");
     var _isMulti = field.type === "multiCombo" || field.type === "manyCombo";
     var _isNetwork = field.type === "networkCombo";
     var _isSemi = field.type === "semiCombo";
-    var _optarray = field.options;
     var _showTagInfoSuggestions = field.type !== "manyCombo" && field.autoSuggestions !== false;
     var _allowCustomValues = field.type !== "manyCombo" && field.customValues !== false;
     var _snake_case = field.snake_case || field.snake_case === void 0;
-    var _combobox = uiCombobox(context, "combo-" + field.safeid).caseSensitive(field.caseSensitive).minItems(_isMulti || _isSemi ? 1 : 2);
+    var _combobox = uiCombobox(context, "combo-" + field.safeid).caseSensitive(field.caseSensitive).minItems(1);
     var _container = select_default2(null);
     var _inputWrap = select_default2(null);
     var _input = select_default2(null);
+    var _lengthIndicator = uiLengthIndicator(context.maxCharsForTagValue());
     var _comboData = [];
     var _multiData = [];
     var _entityIDs = [];
@@ -56674,7 +59336,7 @@ ${content}</tr>
       field.key += ":";
     }
     function snake(s) {
-      return s.replace(/\s+/g, "_").toLowerCase();
+      return s.replace(/\s+/g, "_");
     }
     function clean2(s) {
       return s.split(";").map(function(s2) {
@@ -56683,7 +59345,7 @@ ${content}</tr>
     }
     function tagValue(dval) {
       dval = clean2(dval || "");
-      var found = _comboData.find(function(o) {
+      var found = getOptions().find(function(o) {
         return o.key && clean2(o.value) === dval;
       });
       if (found)
@@ -56691,18 +59353,44 @@ ${content}</tr>
       if (field.type === "typeCombo" && !dval) {
         return "yes";
       }
-      return (_snake_case ? snake(dval) : dval) || void 0;
+      return restrictTagValueSpelling(dval) || void 0;
+    }
+    function restrictTagValueSpelling(dval) {
+      if (_snake_case) {
+        dval = snake(dval);
+      }
+      if (!field.caseSensitive) {
+        dval = dval.toLowerCase();
+      }
+      return dval;
+    }
+    function getLabelId(field2, v) {
+      return field2.hasTextForStringId(`options.${v}.title`) ? `options.${v}.title` : `options.${v}`;
     }
     function displayValue(tval) {
       tval = tval || "";
-      if (field.hasTextForStringId("options." + tval)) {
-        return field.t("options." + tval, { default: tval });
+      var stringsField = field.resolveReference("stringsCrossReference");
+      const labelId = getLabelId(stringsField, tval);
+      if (stringsField.hasTextForStringId(labelId)) {
+        return stringsField.t(labelId, { default: tval });
       }
       if (field.type === "typeCombo" && tval.toLowerCase() === "yes") {
         return "";
       }
       return tval;
     }
+    function renderValue(tval) {
+      tval = tval || "";
+      var stringsField = field.resolveReference("stringsCrossReference");
+      const labelId = getLabelId(stringsField, tval);
+      if (stringsField.hasTextForStringId(labelId)) {
+        return stringsField.t.append(labelId, { default: tval });
+      }
+      if (field.type === "typeCombo" && tval.toLowerCase() === "yes") {
+        tval = "";
+      }
+      return (selection2) => selection2.text(tval);
+    }
     function objectDifference(a, b) {
       return a.filter(function(d1) {
         return !b.some(function(d2) {
@@ -56722,23 +59410,35 @@ ${content}</tr>
         setStaticValues(setPlaceholder);
       }
     }
-    function setStaticValues(callback) {
-      if (!_optarray)
-        return;
-      _comboData = _optarray.map(function(v) {
+    function getOptions() {
+      var stringsField = field.resolveReference("stringsCrossReference");
+      if (!(field.options || stringsField.options))
+        return [];
+      return (field.options || stringsField.options).map(function(v) {
+        const labelId = getLabelId(stringsField, v);
         return {
           key: v,
-          value: field.t("options." + v, { default: v }),
-          title: v,
-          display: field.t.html("options." + v, { default: v }),
-          klass: field.hasTextForStringId("options." + v) ? "" : "raw-option"
+          value: stringsField.t(labelId, { default: v }),
+          title: stringsField.t(`options.${v}.description`, { default: v }),
+          display: addComboboxIcons(stringsField.t.append(labelId, { default: v }), v),
+          klass: stringsField.hasTextForStringId(labelId) ? "" : "raw-option"
         };
       });
-      _combobox.data(objectDifference(_comboData, _multiData));
+    }
+    function setStaticValues(callback, filter2) {
+      _comboData = getOptions();
+      if (filter2 !== void 0) {
+        _comboData = _comboData.filter(filter2);
+      }
+      _comboData = objectDifference(_comboData, _multiData);
+      _combobox.data(_comboData);
       if (callback)
         callback(_comboData);
     }
     function setTaginfoValues(q, callback) {
+      var queryFilter = (d) => d.value.toLowerCase().includes(q.toLowerCase()) || d.key.toLowerCase().includes(q.toLowerCase());
+      setStaticValues(callback, queryFilter);
+      var stringsField = field.resolveReference("stringsCrossReference");
       var fn = _isMulti ? "multikeys" : "values";
       var query = (_isMulti ? field.key : "") + q;
       var hasCountryPrefix = _isNetwork && _countryCode && _countryCode.indexOf(q.toLowerCase()) === 0;
@@ -56756,42 +59456,56 @@ ${content}</tr>
       services.taginfo[fn](params, function(err, data) {
         if (err)
           return;
-        data = data.filter(function(d) {
-          if (field.type === "typeCombo" && d.value === "yes") {
-            return false;
+        data = data.filter((d) => field.type !== "typeCombo" || d.value !== "yes");
+        data = data.filter((d) => {
+          var value = d.value;
+          if (_isMulti) {
+            value = value.slice(field.key.length);
           }
-          return !d.count || d.count > 10;
+          return value === restrictTagValueSpelling(value);
         });
         var deprecatedValues = osmEntity.deprecatedTagValuesByKey(_dataDeprecated)[field.key];
         if (deprecatedValues) {
-          data = data.filter(function(d) {
-            return deprecatedValues.indexOf(d.value) === -1;
-          });
+          data = data.filter((d) => !deprecatedValues.includes(d.value));
         }
         if (hasCountryPrefix) {
-          data = data.filter(function(d) {
-            return d.value.toLowerCase().indexOf(_countryCode + ":") === 0;
-          });
+          data = data.filter((d) => d.value.toLowerCase().indexOf(_countryCode + ":") === 0);
         }
+        const additionalOptions = (field.options || stringsField.options || []).filter((v) => !data.some((dv) => dv.value === (_isMulti ? field.key + v : v))).map((v) => ({ value: v }));
         _container.classed("empty-combobox", data.length === 0);
-        _comboData = data.map(function(d) {
-          var k = d.value;
+        _comboData = data.concat(additionalOptions).map(function(d) {
+          var v = d.value;
           if (_isMulti)
-            k = k.replace(field.key, "");
-          var label = field.t("options." + k, { default: k });
+            v = v.replace(field.key, "");
+          const labelId = getLabelId(stringsField, v);
+          var isLocalizable = stringsField.hasTextForStringId(labelId);
+          var label = stringsField.t(labelId, { default: v });
           return {
-            key: k,
+            key: v,
             value: label,
-            display: field.t.html("options." + k, { default: k }),
-            title: d.title || label,
-            klass: field.hasTextForStringId("options." + k) ? "" : "raw-option"
+            title: stringsField.t(`options.${v}.description`, { default: isLocalizable ? v : d.title !== label ? d.title : "" }),
+            display: addComboboxIcons(stringsField.t.append(labelId, { default: v }), v),
+            klass: isLocalizable ? "" : "raw-option"
           };
         });
+        _comboData = _comboData.filter(queryFilter);
         _comboData = objectDifference(_comboData, _multiData);
         if (callback)
           callback(_comboData);
       });
     }
+    function addComboboxIcons(disp, value) {
+      if (valueIcons[field.key]) {
+        return function(selection2) {
+          var span = selection2.insert("span", ":first-child").attr("class", "tag-value-icon");
+          if (valueIcons[field.key].indexOf(value) !== -1) {
+            span.call(svgIcon("#iD-" + field.key.replace(/:/g, "_") + "-" + value.replace(/:/g, "_")));
+          }
+          disp.call(this, selection2);
+        };
+      }
+      return disp;
+    }
     function setPlaceholder(values) {
       if (_isMulti || _isSemi) {
         _staticPlaceholder = field.placeholder() || _t("inspector.add");
@@ -56821,12 +59535,19 @@ ${content}</tr>
       var t = {};
       var val;
       if (_isMulti || _isSemi) {
-        val = tagValue(utilGetSetValue(_input).replace(/,/g, ";")) || "";
-        _container.classed("active", false);
-        utilGetSetValue(_input, "");
-        var vals = val.split(";").filter(Boolean);
+        var vals;
+        if (_isMulti) {
+          vals = [tagValue(utilGetSetValue(_input))];
+        } else if (_isSemi) {
+          val = tagValue(utilGetSetValue(_input)) || "";
+          val = val.replace(/,/g, ";");
+          vals = val.split(";");
+        }
+        vals = vals.filter(Boolean);
         if (!vals.length)
           return;
+        _container.classed("active", false);
+        utilGetSetValue(_input, "");
         if (_isMulti) {
           utilArrayUniq(vals).forEach(function(v) {
             var key = (field.key || "") + v;
@@ -56870,13 +59591,14 @@ ${content}</tr>
         }).filter(Boolean);
         arr = utilArrayUniq(arr);
         t[field.key] = arr.length ? arr.join(";") : void 0;
+        _lengthIndicator.update(t[field.key]);
       }
       dispatch10.call("change", this, t);
     }
     function combo(selection2) {
       _container = selection2.selectAll(".form-field-input-wrap").data([0]);
-      var type3 = _isMulti || _isSemi ? "multicombo" : "combo";
-      _container = _container.enter().append("div").attr("class", "form-field-input-wrap form-field-input-" + type3).merge(_container);
+      var type2 = _isMulti || _isSemi ? "multicombo" : "combo";
+      _container = _container.enter().append("div").attr("class", "form-field-input-wrap form-field-input-" + type2).merge(_container);
       if (_isMulti || _isSemi) {
         _container = _container.selectAll(".chiplist").data([0]);
         var listClass = "chiplist";
@@ -56895,12 +59617,24 @@ ${content}</tr>
         _input = _container.selectAll("input").data([0]);
       }
       _input = _input.enter().append("input").attr("type", "text").attr("id", field.domId).call(utilNoAuto).call(initCombo, selection2).merge(_input);
+      if (_isSemi) {
+        _inputWrap.call(_lengthIndicator);
+      } else if (!_isMulti) {
+        _container.call(_lengthIndicator);
+      }
       if (_isNetwork) {
         var extent = combinedEntityExtent();
         var countryCode = extent && iso1A2Code(extent.center());
         _countryCode = countryCode && countryCode.toLowerCase();
       }
-      _input.on("change", change).on("blur", change);
+      _input.on("change", change).on("blur", change).on("input", function() {
+        let val = utilGetSetValue(_input);
+        updateIcon(val);
+        if (_isSemi && _tags[field.key]) {
+          val += ";" + _tags[field.key];
+        }
+        _lengthIndicator.update(val);
+      });
       _input.on("keydown.field", function(d3_event) {
         switch (d3_event.keyCode) {
           case 13:
@@ -56918,9 +59652,24 @@ ${content}</tr>
           _container.classed("active", true);
         });
       }
+      _combobox.on("cancel", function() {
+        _input.node().blur();
+      }).on("update", function() {
+        updateIcon(utilGetSetValue(_input));
+      });
+    }
+    function updateIcon(value) {
+      value = tagValue(value);
+      if (valueIcons[field.key]) {
+        _container.selectAll(".tag-value-icon").remove();
+        if (valueIcons[field.key].indexOf(value) !== -1) {
+          _container.selectAll(".tag-value-icon").data([value]).enter().insert("div", "input").attr("class", "tag-value-icon").call(svgIcon("#iD-" + field.key.replace(/:/g, "_") + "-" + value.replace(/:/g, "_")));
+        }
+      }
     }
     combo.tags = function(tags) {
       _tags = tags;
+      var stringsField = field.resolveReference("stringsCrossReference");
       if (_isMulti || _isSemi) {
         _multiData = [];
         var maxLength;
@@ -56933,10 +59682,11 @@ ${content}</tr>
             var v = tags[k];
             if (!v || typeof v === "string" && v.toLowerCase() === "no")
               continue;
-            var suffix = field.key ? k.substr(field.key.length) : k;
+            var suffix = field.key ? k.slice(field.key.length) : k;
             _multiData.push({
               key: k,
               value: displayValue(suffix),
+              display: renderValue(suffix),
               isMixed: Array.isArray(v)
             });
           }
@@ -56970,6 +59720,7 @@ ${content}</tr>
             return {
               key: v2,
               value: displayValue(v2),
+              display: renderValue(v2),
               isMixed: !commonValues.includes(v2)
             };
           });
@@ -56994,7 +59745,7 @@ ${content}</tr>
           var k2 = d.key;
           if (_isMulti)
             k2 = k2.replace(field.key, "");
-          return !field.hasTextForStringId("options." + k2);
+          return !stringsField.hasTextForStringId("options." + k2);
         }).classed("draggable", allowDragAndDrop).classed("mixed", function(d) {
           return d.isMixed;
         }).attr("title", function(d) {
@@ -57003,8 +59754,14 @@ ${content}</tr>
         if (allowDragAndDrop) {
           registerDragAndDrop(chips);
         }
-        chips.select("span").text(function(d) {
-          return d.value;
+        chips.select("span").each(function(d) {
+          const selection2 = select_default2(this);
+          if (d.display) {
+            selection2.text("");
+            d.display(selection2);
+          } else {
+            selection2.text(d.value);
+          }
         });
         chips.select("a").attr("href", "#").on("click", removeMultikey).attr("class", "remove").text("\xD7");
       } else {
@@ -57013,7 +59770,7 @@ ${content}</tr>
           return displayValue(val);
         }).filter(Boolean);
         var showsValue = !isMixed && tags[field.key] && !(field.type === "typeCombo" && tags[field.key] === "yes");
-        var isRawValue = showsValue && !field.hasTextForStringId("options." + tags[field.key]);
+        var isRawValue = showsValue && !stringsField.hasTextForStringId(`options.${tags[field.key]}`) && !stringsField.hasTextForStringId(`options.${tags[field.key]}.title`);
         var isKnownValue = showsValue && !isRawValue;
         var isReadOnly = !_allowCustomValues || isKnownValue;
         utilGetSetValue(_input, !isMixed ? displayValue(tags[field.key]) : "").classed("raw-value", isRawValue).classed("known-value", isKnownValue).attr("readonly", isReadOnly ? "readonly" : void 0).attr("title", isMixed ? mixedValues.join("\n") : void 0).attr("placeholder", isMixed ? _t("inspector.multiple_values") : _staticPlaceholder || "").classed("mixed", isMixed).on("keydown.deleteCapture", function(d3_event) {
@@ -57025,89 +59782,98 @@ ${content}</tr>
             dispatch10.call("change", this, t);
           }
         });
+        if (!Array.isArray(tags[field.key])) {
+          updateIcon(tags[field.key]);
+        }
+        if (!isMixed) {
+          _lengthIndicator.update(tags[field.key]);
+        }
       }
     };
     function registerDragAndDrop(selection2) {
       var dragOrigin, targetIndex;
-      selection2.call(drag_default().on("start", function(d3_event) {
-        dragOrigin = {
-          x: d3_event.x,
-          y: d3_event.y
-        };
-        targetIndex = null;
-      }).on("drag", function(d3_event) {
-        var x = d3_event.x - dragOrigin.x, y = d3_event.y - dragOrigin.y;
-        if (!select_default2(this).classed("dragging") && Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5)
-          return;
-        var index = selection2.nodes().indexOf(this);
-        select_default2(this).classed("dragging", true);
-        targetIndex = null;
-        var targetIndexOffsetTop = null;
-        var draggedTagWidth = select_default2(this).node().offsetWidth;
-        if (field.key === "destination" || field.key === "via") {
-          _container.selectAll(".chip").style("transform", function(d2, index2) {
-            var node = select_default2(this).node();
-            if (index === index2) {
-              return "translate(" + x + "px, " + y + "px)";
-            } else if (index2 > index && d3_event.y > node.offsetTop) {
-              if (targetIndex === null || index2 > targetIndex) {
-                targetIndex = index2;
+      selection2.call(
+        drag_default().on("start", function(d3_event) {
+          dragOrigin = {
+            x: d3_event.x,
+            y: d3_event.y
+          };
+          targetIndex = null;
+        }).on("drag", function(d3_event) {
+          var x = d3_event.x - dragOrigin.x, y = d3_event.y - dragOrigin.y;
+          if (!select_default2(this).classed("dragging") && // don't display drag until dragging beyond a distance threshold
+          Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5)
+            return;
+          var index = selection2.nodes().indexOf(this);
+          select_default2(this).classed("dragging", true);
+          targetIndex = null;
+          var targetIndexOffsetTop = null;
+          var draggedTagWidth = select_default2(this).node().offsetWidth;
+          if (field.key === "destination" || field.key === "via") {
+            _container.selectAll(".chip").style("transform", function(d2, index2) {
+              var node = select_default2(this).node();
+              if (index === index2) {
+                return "translate(" + x + "px, " + y + "px)";
+              } else if (index2 > index && d3_event.y > node.offsetTop) {
+                if (targetIndex === null || index2 > targetIndex) {
+                  targetIndex = index2;
+                }
+                return "translateY(-100%)";
+              } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) {
+                if (targetIndex === null || index2 < targetIndex) {
+                  targetIndex = index2;
+                }
+                return "translateY(100%)";
               }
-              return "translateY(-100%)";
-            } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) {
-              if (targetIndex === null || index2 < targetIndex) {
+              return null;
+            });
+          } else {
+            _container.selectAll(".chip").each(function(d2, index2) {
+              var node = select_default2(this).node();
+              if (index !== index2 && d3_event.x < node.offsetLeft + node.offsetWidth + 5 && d3_event.x > node.offsetLeft && d3_event.y < node.offsetTop + node.offsetHeight && d3_event.y > node.offsetTop) {
                 targetIndex = index2;
+                targetIndexOffsetTop = node.offsetTop;
               }
-              return "translateY(100%)";
-            }
-            return null;
-          });
-        } else {
-          _container.selectAll(".chip").each(function(d2, index2) {
-            var node = select_default2(this).node();
-            if (index !== index2 && d3_event.x < node.offsetLeft + node.offsetWidth + 5 && d3_event.x > node.offsetLeft && d3_event.y < node.offsetTop + node.offsetHeight && d3_event.y > node.offsetTop) {
-              targetIndex = index2;
-              targetIndexOffsetTop = node.offsetTop;
-            }
-          }).style("transform", function(d2, index2) {
-            var node = select_default2(this).node();
-            if (index === index2) {
-              return "translate(" + x + "px, " + y + "px)";
-            }
-            if (node.offsetTop === targetIndexOffsetTop) {
-              if (index2 < index && index2 >= targetIndex) {
-                return "translateX(" + draggedTagWidth + "px)";
-              } else if (index2 > index && index2 <= targetIndex) {
-                return "translateX(-" + draggedTagWidth + "px)";
+            }).style("transform", function(d2, index2) {
+              var node = select_default2(this).node();
+              if (index === index2) {
+                return "translate(" + x + "px, " + y + "px)";
               }
+              if (node.offsetTop === targetIndexOffsetTop) {
+                if (index2 < index && index2 >= targetIndex) {
+                  return "translateX(" + draggedTagWidth + "px)";
+                } else if (index2 > index && index2 <= targetIndex) {
+                  return "translateX(-" + draggedTagWidth + "px)";
+                }
+              }
+              return null;
+            });
+          }
+        }).on("end", function() {
+          if (!select_default2(this).classed("dragging")) {
+            return;
+          }
+          var index = selection2.nodes().indexOf(this);
+          select_default2(this).classed("dragging", false);
+          _container.selectAll(".chip").style("transform", null);
+          if (typeof targetIndex === "number") {
+            var element = _multiData[index];
+            _multiData.splice(index, 1);
+            _multiData.splice(targetIndex, 0, element);
+            var t = {};
+            if (_multiData.length) {
+              t[field.key] = _multiData.map(function(element2) {
+                return element2.key;
+              }).join(";");
+            } else {
+              t[field.key] = void 0;
             }
-            return null;
-          });
-        }
-      }).on("end", function() {
-        if (!select_default2(this).classed("dragging")) {
-          return;
-        }
-        var index = selection2.nodes().indexOf(this);
-        select_default2(this).classed("dragging", false);
-        _container.selectAll(".chip").style("transform", null);
-        if (typeof targetIndex === "number") {
-          var element = _multiData[index];
-          _multiData.splice(index, 1);
-          _multiData.splice(targetIndex, 0, element);
-          var t = {};
-          if (_multiData.length) {
-            t[field.key] = _multiData.map(function(element2) {
-              return element2.key;
-            }).join(";");
-          } else {
-            t[field.key] = void 0;
+            dispatch10.call("change", this, t);
           }
-          dispatch10.call("change", this, t);
-        }
-        dragOrigin = void 0;
-        targetIndex = void 0;
-      }));
+          dragOrigin = void 0;
+          targetIndex = void 0;
+        })
+      );
     }
     combo.focus = function() {
       _input.node().focus();
@@ -57130,9 +59896,11 @@ ${content}</tr>
     var input = select_default2(null);
     var outlinkButton = select_default2(null);
     var wrap2 = select_default2(null);
+    var _lengthIndicator = uiLengthIndicator(context.maxCharsForTagValue());
     var _entityIDs = [];
     var _tags;
     var _phoneFormats = {};
+    const isDirectionField = field.key.split(":").some((keyPart) => keyPart === "direction");
     if (field.type === "tel") {
       _mainFileFetcher.get("phone_formats").then(function(d) {
         _phoneFormats = d;
@@ -57162,6 +59930,7 @@ ${content}</tr>
       input = wrap2.selectAll("input").data([0]);
       input = input.enter().append("input").attr("type", field.type === "identifier" ? "text" : field.type).attr("id", field.domId).classed(field.type, true).call(utilNoAuto).merge(input);
       input.classed("disabled", !!isLocked).attr("readonly", isLocked || null).on("input", change(true)).on("blur", change()).on("change", change());
+      wrap2.call(_lengthIndicator);
       if (field.type === "tel") {
         updatePhonePlaceholder();
       } else if (field.type === "number") {
@@ -57177,11 +59946,28 @@ ${content}</tr>
           return _t(`inspector.${which}`);
         }).merge(buttons).on("click", function(d3_event, d) {
           d3_event.preventDefault();
+          var isMixed = Array.isArray(_tags[field.key]);
+          if (isMixed)
+            return;
           var raw_vals = input.node().value || "0";
           var vals = raw_vals.split(";");
           vals = vals.map(function(v) {
-            var num = parseFloat(v.trim(), 10);
-            return isFinite(num) ? clamped(num + d) : v.trim();
+            var num = Number(v);
+            if (isDirectionField) {
+              const compassDir = cardinal[v.trim().toLowerCase()];
+              if (compassDir !== void 0) {
+                num = compassDir;
+              }
+            }
+            if (!isFinite(num)) {
+              return v.trim();
+            }
+            num += d;
+            if (isDirectionField) {
+              num = (num % 360 + 360) % 360;
+            }
+            const numDecimals = v.includes(".") ? v.split(".")[1].length : 0;
+            return clamped(num).toFixed(numDecimals);
           });
           input.node().value = vals.join(";");
           change()();
@@ -57213,7 +59999,7 @@ ${content}</tr>
           if (value)
             window.open(value, "_blank");
         }).merge(outlinkButton);
-      } else if (field.key.split(":").includes("colour")) {
+      } else if (field.type === "colour") {
         input.attr("type", "text");
         updateColourPreview();
       }
@@ -57292,7 +60078,7 @@ ${content}</tr>
           if (field.type === "number" && val) {
             var vals = val.split(";");
             vals = vals.map(function(v) {
-              var num = parseFloat(v.trim(), 10);
+              var num = Number(v);
               return isFinite(num) ? clamped(num) : v.trim();
             });
             val = vals.join(";");
@@ -57313,12 +60099,27 @@ ${content}</tr>
       _tags = tags;
       var isMixed = Array.isArray(tags[field.key]);
       utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : "").attr("title", isMixed ? tags[field.key].filter(Boolean).join("\n") : void 0).attr("placeholder", isMixed ? _t("inspector.multiple_values") : field.placeholder() || _t("inspector.unknown")).classed("mixed", isMixed);
+      if (field.type === "number") {
+        const buttons = wrap2.selectAll(".increment, .decrement");
+        if (isMixed) {
+          buttons.attr("disabled", "disabled").classed("disabled", true);
+        } else {
+          var raw_vals = tags[field.key] || "0";
+          const canIncDec = raw_vals.split(";").some((val) => isFinite(Number(val)) || isDirectionField && cardinal[val.trim().toLowerCase()]);
+          buttons.attr("disabled", canIncDec ? null : "disabled").classed("disabled", !canIncDec);
+        }
+      }
+      if (field.type === "tel")
+        updatePhonePlaceholder();
       if (field.key.split(":").includes("colour"))
         updateColourPreview();
       if (outlinkButton && !outlinkButton.empty()) {
         var disabled = !validIdentifierValueForLink();
         outlinkButton.classed("disabled", disabled);
       }
+      if (!isMixed) {
+        _lengthIndicator.update(tags[field.key]);
+      }
     };
     i2.focus = function() {
       var node = input.node();
@@ -57353,7 +60154,9 @@ ${content}</tr>
       enter.append("div").attr("class", "preset-input-access-wrap").append("input").attr("type", "text").attr("class", function(d) {
         return "preset-input-access preset-input-access-" + d;
       }).call(utilNoAuto).each(function(d) {
-        select_default2(this).call(uiCombobox(context, "access-" + d).data(access.options(d)));
+        select_default2(this).call(
+          uiCombobox(context, "access-" + d).data(access.options(d))
+        );
       });
       items = items.merge(enter);
       wrap2.selectAll(".preset-input-access").on("change", change).on("blur", change);
@@ -57366,7 +60169,7 @@ ${content}</tr>
       tag[d] = value || void 0;
       dispatch10.call("change", this, tag);
     }
-    access.options = function(type3) {
+    access.options = function(type2) {
       var options2 = [
         "yes",
         "no",
@@ -57378,122 +60181,168 @@ ${content}</tr>
         "permit",
         "unknown"
       ];
-      if (type3 === "access") {
+      if (type2 === "access") {
         options2 = options2.filter((v) => v !== "yes" && v !== "designated");
       }
-      if (type3 === "bicycle") {
+      if (type2 === "bicycle") {
         options2.splice(options2.length - 4, 0, "dismount");
       }
+      var stringsField = field.resolveReference("stringsCrossReference");
       return options2.map(function(option) {
         return {
-          title: field.t("options." + option + ".description"),
+          title: stringsField.t("options." + option + ".description"),
           value: option
         };
       });
     };
-    var placeholdersByHighway = {
-      footway: {
-        foot: "designated",
-        motor_vehicle: "no"
-      },
-      steps: {
-        foot: "yes",
-        motor_vehicle: "no",
-        bicycle: "no",
-        horse: "no"
-      },
-      pedestrian: {
-        foot: "yes",
-        motor_vehicle: "no"
-      },
-      cycleway: {
-        motor_vehicle: "no",
-        bicycle: "designated"
-      },
-      bridleway: {
-        motor_vehicle: "no",
-        horse: "designated"
-      },
-      path: {
-        foot: "yes",
-        motor_vehicle: "no",
-        bicycle: "yes",
-        horse: "yes"
-      },
-      motorway: {
-        foot: "no",
-        motor_vehicle: "yes",
-        bicycle: "no",
-        horse: "no"
-      },
-      trunk: {
-        motor_vehicle: "yes"
-      },
-      primary: {
-        foot: "yes",
-        motor_vehicle: "yes",
-        bicycle: "yes",
-        horse: "yes"
-      },
-      secondary: {
-        foot: "yes",
-        motor_vehicle: "yes",
-        bicycle: "yes",
-        horse: "yes"
-      },
-      tertiary: {
-        foot: "yes",
-        motor_vehicle: "yes",
-        bicycle: "yes",
-        horse: "yes"
-      },
-      residential: {
-        foot: "yes",
-        motor_vehicle: "yes",
-        bicycle: "yes",
-        horse: "yes"
-      },
-      unclassified: {
-        foot: "yes",
-        motor_vehicle: "yes",
-        bicycle: "yes",
-        horse: "yes"
-      },
-      service: {
-        foot: "yes",
-        motor_vehicle: "yes",
-        bicycle: "yes",
-        horse: "yes"
-      },
-      motorway_link: {
-        foot: "no",
-        motor_vehicle: "yes",
-        bicycle: "no",
-        horse: "no"
-      },
-      trunk_link: {
-        motor_vehicle: "yes"
-      },
-      primary_link: {
-        foot: "yes",
-        motor_vehicle: "yes",
-        bicycle: "yes",
-        horse: "yes"
-      },
-      secondary_link: {
-        foot: "yes",
-        motor_vehicle: "yes",
-        bicycle: "yes",
-        horse: "yes"
-      },
-      tertiary_link: {
-        foot: "yes",
-        motor_vehicle: "yes",
-        bicycle: "yes",
-        horse: "yes"
+    const placeholdersByTag = {
+      highway: {
+        footway: {
+          foot: "designated",
+          motor_vehicle: "no"
+        },
+        steps: {
+          foot: "yes",
+          motor_vehicle: "no",
+          bicycle: "no",
+          horse: "no"
+        },
+        pedestrian: {
+          foot: "yes",
+          motor_vehicle: "no"
+        },
+        cycleway: {
+          motor_vehicle: "no",
+          bicycle: "designated"
+        },
+        bridleway: {
+          motor_vehicle: "no",
+          horse: "designated"
+        },
+        path: {
+          foot: "yes",
+          motor_vehicle: "no",
+          bicycle: "yes",
+          horse: "yes"
+        },
+        motorway: {
+          foot: "no",
+          motor_vehicle: "yes",
+          bicycle: "no",
+          horse: "no"
+        },
+        trunk: {
+          motor_vehicle: "yes"
+        },
+        primary: {
+          foot: "yes",
+          motor_vehicle: "yes",
+          bicycle: "yes",
+          horse: "yes"
+        },
+        secondary: {
+          foot: "yes",
+          motor_vehicle: "yes",
+          bicycle: "yes",
+          horse: "yes"
+        },
+        tertiary: {
+          foot: "yes",
+          motor_vehicle: "yes",
+          bicycle: "yes",
+          horse: "yes"
+        },
+        residential: {
+          foot: "yes",
+          motor_vehicle: "yes",
+          bicycle: "yes",
+          horse: "yes"
+        },
+        unclassified: {
+          foot: "yes",
+          motor_vehicle: "yes",
+          bicycle: "yes",
+          horse: "yes"
+        },
+        service: {
+          foot: "yes",
+          motor_vehicle: "yes",
+          bicycle: "yes",
+          horse: "yes"
+        },
+        motorway_link: {
+          foot: "no",
+          motor_vehicle: "yes",
+          bicycle: "no",
+          horse: "no"
+        },
+        trunk_link: {
+          motor_vehicle: "yes"
+        },
+        primary_link: {
+          foot: "yes",
+          motor_vehicle: "yes",
+          bicycle: "yes",
+          horse: "yes"
+        },
+        secondary_link: {
+          foot: "yes",
+          motor_vehicle: "yes",
+          bicycle: "yes",
+          horse: "yes"
+        },
+        tertiary_link: {
+          foot: "yes",
+          motor_vehicle: "yes",
+          bicycle: "yes",
+          horse: "yes"
+        },
+        construction: {
+          access: "no"
+        }
       },
-      construction: {
-        access: "no"
+      barrier: {
+        bollard: {
+          access: "no",
+          bicycle: "yes",
+          foot: "yes"
+        },
+        bus_trap: {
+          motor_vehicle: "no",
+          psv: "yes",
+          foot: "yes",
+          bicycle: "yes"
+        },
+        city_wall: {
+          access: "no"
+        },
+        coupure: {
+          access: "yes"
+        },
+        cycle_barrier: {
+          motor_vehicle: "no"
+        },
+        ditch: {
+          access: "no"
+        },
+        entrance: {
+          access: "yes"
+        },
+        fence: {
+          access: "no"
+        },
+        hedge: {
+          access: "no"
+        },
+        jersey_barrier: {
+          access: "no"
+        },
+        motorcycle_barrier: {
+          motor_vehicle: "no"
+        },
+        rail_guard: {
+          access: "no"
+        }
       }
     };
     access.tags = function(tags) {
@@ -57516,21 +60365,29 @@ ${content}</tr>
         if (tags.access && typeof tags.access === "string") {
           return tags.access;
         }
-        if (tags.highway) {
-          if (typeof tags.highway === "string") {
-            if (placeholdersByHighway[tags.highway] && placeholdersByHighway[tags.highway][d]) {
-              return placeholdersByHighway[tags.highway][d];
+        function getPlaceholdersByTag(key, placeholdersByKey) {
+          if (typeof tags[key] === "string") {
+            if (placeholdersByKey[tags[key]] && placeholdersByKey[tags[key]][d]) {
+              return placeholdersByKey[tags[key]][d];
             }
           } else {
-            var impliedAccesses = tags.highway.filter(Boolean).map(function(highwayVal) {
-              return placeholdersByHighway[highwayVal] && placeholdersByHighway[highwayVal][d];
+            var impliedAccesses = tags[key].filter(Boolean).map(function(val) {
+              return placeholdersByKey[val] && placeholdersByKey[val][d];
             }).filter(Boolean);
-            if (impliedAccesses.length === tags.highway.length && new Set(impliedAccesses).size === 1) {
+            if (impliedAccesses.length === tags[key].length && new Set(impliedAccesses).size === 1) {
               return impliedAccesses[0];
             }
           }
         }
-        if (d === "access") {
+        for (const key in placeholdersByTag) {
+          if (tags[key]) {
+            const impliedAccess = getPlaceholdersByTag(key, placeholdersByTag[key]);
+            if (impliedAccess) {
+              return impliedAccess;
+            }
+          }
+        }
+        if (d === "access" && !tags.barrier) {
           return "yes";
         }
         return field.placeholder();
@@ -57692,9 +60549,11 @@ ${content}</tr>
         if (dropdowns.indexOf(d.id) === -1)
           return;
         var nearValues = d.id === "street" ? getNearStreets : d.id === "city" ? getNearCities : getNearValues;
-        select_default2(this).call(uiCombobox(context, "address-" + d.id).minItems(1).caseSensitive(true).fetcher(function(value, callback) {
-          callback(nearValues("addr:" + d.id));
-        }));
+        select_default2(this).call(
+          uiCombobox(context, "address-" + d.id).minItems(1).caseSensitive(true).fetcher(function(value, callback) {
+            callback(nearValues("addr:" + d.id));
+          })
+        );
       }
       _wrap.selectAll("input").on("blur", change()).on("change", change());
       _wrap.selectAll("input:not(.combobox-input)").on("input", change(true));
@@ -57779,13 +60638,14 @@ ${content}</tr>
     return utilRebind(address, dispatch10, "on");
   }
 
-  // modules/ui/fields/cycleway.js
-  function uiFieldCycleway(field, context) {
+  // modules/ui/fields/directional_combo.js
+  function uiFieldDirectionalCombo(field, context) {
     var dispatch10 = dispatch_default("change");
     var items = select_default2(null);
     var wrap2 = select_default2(null);
     var _tags;
-    function cycleway(selection2) {
+    var _combos = {};
+    function directionalCombo(selection2) {
       function stripcolon(s) {
         return s.replace(":", "");
       }
@@ -57793,97 +60653,61 @@ ${content}</tr>
       wrap2 = wrap2.enter().append("div").attr("class", "form-field-input-wrap form-field-input-" + field.type).merge(wrap2);
       var div = wrap2.selectAll("ul").data([0]);
       div = div.enter().append("ul").attr("class", "rows").merge(div);
-      var keys = ["cycleway:left", "cycleway:right"];
+      var keys = field.keys.slice(1);
       items = div.selectAll("li").data(keys);
       var enter = items.enter().append("li").attr("class", function(d) {
-        return "labeled-input preset-cycleway-" + stripcolon(d);
+        return "labeled-input preset-directionalcombo-" + stripcolon(d);
       });
-      enter.append("span").attr("class", "label preset-label-cycleway").attr("for", function(d) {
-        return "preset-input-cycleway-" + stripcolon(d);
+      enter.append("span").attr("class", "label preset-label-directionalcombo").attr("for", function(d) {
+        return "preset-input-directionalcombo-" + stripcolon(d);
       }).html(function(d) {
         return field.t.html("types." + d);
       });
-      enter.append("div").attr("class", "preset-input-cycleway-wrap").append("input").attr("type", "text").attr("class", function(d) {
-        return "preset-input-cycleway preset-input-" + stripcolon(d);
-      }).call(utilNoAuto).each(function(d) {
-        select_default2(this).call(uiCombobox(context, "cycleway-" + stripcolon(d)).data(cycleway.options(d)));
-      });
-      items = items.merge(enter);
-      wrap2.selectAll(".preset-input-cycleway").on("change", change).on("blur", change);
-    }
-    function change(d3_event, key) {
-      var newValue = context.cleanTagValue(utilGetSetValue(select_default2(this)));
-      if (!newValue && (Array.isArray(_tags.cycleway) || Array.isArray(_tags[key])))
-        return;
-      if (newValue === "none" || newValue === "") {
-        newValue = void 0;
-      }
-      var otherKey = key === "cycleway:left" ? "cycleway:right" : "cycleway:left";
-      var otherValue = typeof _tags.cycleway === "string" ? _tags.cycleway : _tags[otherKey];
-      if (otherValue && Array.isArray(otherValue)) {
-        otherValue = otherValue[0];
-      }
-      if (otherValue === "none" || otherValue === "") {
-        otherValue = void 0;
-      }
-      var tag = {};
-      if (newValue === otherValue) {
-        tag = {
-          cycleway: newValue,
-          "cycleway:left": void 0,
-          "cycleway:right": void 0
-        };
-      } else {
-        tag = {
-          cycleway: void 0
-        };
-        tag[key] = newValue;
-        tag[otherKey] = otherValue;
-      }
-      dispatch10.call("change", this, tag);
-    }
-    cycleway.options = function() {
-      return field.options.map(function(option) {
-        return {
-          title: field.t("options." + option + ".description"),
-          value: option
+      enter.append("div").attr("class", "preset-input-directionalcombo-wrap form-field-input-wrap").each(function(key) {
+        const subField = {
+          ...field,
+          type: "combo",
+          key
         };
+        const combo = uiFieldCombo(subField, context);
+        combo.on("change", (t) => change(key, t[key]));
+        _combos[key] = combo;
+        select_default2(this).call(combo);
       });
-    };
-    cycleway.tags = function(tags) {
-      _tags = tags;
-      var commonValue = typeof tags.cycleway === "string" && tags.cycleway;
-      utilGetSetValue(items.selectAll(".preset-input-cycleway"), function(d) {
-        if (commonValue)
-          return commonValue;
-        return !tags.cycleway && typeof tags[d] === "string" ? tags[d] : "";
-      }).attr("title", function(d) {
-        if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
-          var vals = [];
-          if (Array.isArray(tags.cycleway)) {
-            vals = vals.concat(tags.cycleway);
-          }
-          if (Array.isArray(tags[d])) {
-            vals = vals.concat(tags[d]);
-          }
-          return vals.filter(Boolean).join("\n");
-        }
-        return null;
-      }).attr("placeholder", function(d) {
-        if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
-          return _t("inspector.multiple_values");
+      items = items.merge(enter);
+      wrap2.selectAll(".preset-input-directionalcombo").on("change", change).on("blur", change);
+    }
+    function change(key, newValue) {
+      const commonKey = field.keys[0];
+      const otherKey = key === field.keys[1] ? field.keys[2] : field.keys[1];
+      dispatch10.call("change", this, (tags) => {
+        const otherValue = tags[otherKey] || tags[commonKey];
+        if (newValue === otherValue) {
+          tags[commonKey] = newValue;
+          delete tags[key];
+          delete tags[otherKey];
+        } else {
+          tags[key] = newValue;
+          delete tags[commonKey];
+          tags[otherKey] = otherValue;
         }
-        return field.placeholder();
-      }).classed("mixed", function(d) {
-        return Array.isArray(tags.cycleway) || Array.isArray(tags[d]);
+        return tags;
       });
+    }
+    directionalCombo.tags = function(tags) {
+      _tags = tags;
+      const commonKey = field.keys[0];
+      for (let key in _combos) {
+        const uniqueValues = [...new Set([].concat(_tags[commonKey]).concat(_tags[key]).filter(Boolean))];
+        _combos[key].tags({ [key]: uniqueValues.length > 1 ? uniqueValues : uniqueValues[0] });
+      }
     };
-    cycleway.focus = function() {
+    directionalCombo.focus = function() {
       var node = wrap2.selectAll("input").node();
       if (node)
         node.focus();
     };
-    return utilRebind(cycleway, dispatch10, "on");
+    return utilRebind(directionalCombo, dispatch10, "on");
   }
 
   // modules/ui/fields/lanes.js
@@ -57950,6 +60774,7 @@ ${content}</tr>
     var wikipedia = services.wikipedia;
     var input = select_default2(null);
     var localizedInputs = select_default2(null);
+    var _lengthIndicator = uiLengthIndicator(context.maxCharsForTagValue());
     var _countryCode;
     var _tags;
     _mainFileFetcher.get("languages").then(loadLanguagesArray).catch(function() {
@@ -57962,7 +60787,7 @@ ${content}</tr>
     var langCombo = uiCombobox(context, "localized-lang").fetcher(fetchLanguages).minItems(0);
     var _selection = select_default2(null);
     var _multilingual = [];
-    var _buttonTip = uiTooltip().title(_t.html("translate.translate")).placement("left");
+    var _buttonTip = uiTooltip().title(() => _t.append("translate.translate")).placement("left");
     var _wikiTitles;
     var _entityIDs = [];
     function loadLanguagesArray(dataLanguages) {
@@ -57970,7 +60795,9 @@ ${content}</tr>
         return;
       var replacements = {
         sr: "sr-Cyrl",
+        // in OSM, `sr` implies Cyrillic
         "sr-Cyrl": false
+        // `sr-Cyrl` isn't used in OSM
       };
       for (var code in dataLanguages) {
         if (replacements[code] === false)
@@ -58046,6 +60873,7 @@ ${content}</tr>
       input = wrap2.selectAll(".localized-main").data([0]);
       input = input.enter().append("input").attr("type", "text").attr("id", field.domId).attr("class", "localized-main").call(utilNoAuto).merge(input);
       input.classed("disabled", !!isLocked).attr("readonly", isLocked || null).on("input", change(true)).on("blur", change()).on("change", change());
+      wrap2.call(_lengthIndicator);
       var translateButton = wrap2.selectAll(".localized-add").data([0]);
       translateButton = translateButton.enter().append("button").attr("class", "localized-add form-field-button").attr("aria-label", _t("icons.plus")).call(svgIcon("#iD-icon-plus")).merge(translateButton);
       translateButton.classed("disabled", !!isLocked).call(isLocked ? _buttonTip.destroy : _buttonTip).on("click", addNew);
@@ -58221,6 +61049,9 @@ ${content}</tr>
       utilGetSetValue(input, typeof tags[field.key] === "string" ? tags[field.key] : "").attr("title", isMixed ? tags[field.key].filter(Boolean).join("\n") : void 0).attr("placeholder", isMixed ? _t("inspector.multiple_values") : field.placeholder()).classed("mixed", isMixed);
       calcMultilingual(tags);
       _selection.call(localized);
+      if (!isMixed) {
+        _lengthIndicator.update(tags[field.key]);
+      }
     };
     localized.focus = function() {
       input.node().focus();
@@ -58282,7 +61113,12 @@ ${content}</tr>
       secondaryUnitInput = wrap2.selectAll("input.roadheight-secondary-unit").data([0]);
       secondaryUnitInput = secondaryUnitInput.enter().append("input").attr("type", "text").call(utilNoAuto).classed("disabled", true).classed("roadheight-secondary-unit", true).attr("readonly", "readonly").merge(secondaryUnitInput);
       function changeUnits() {
-        _isImperial = utilGetSetValue(primaryUnitInput) === "ft";
+        var primaryUnit = utilGetSetValue(primaryUnitInput);
+        if (primaryUnit === "m") {
+          _isImperial = false;
+        } else if (primaryUnit === "ft") {
+          _isImperial = true;
+        }
         utilGetSetValue(primaryUnitInput, _isImperial ? "ft" : "m");
         setUnitSuggestions();
         change();
@@ -58373,7 +61209,12 @@ ${content}</tr>
       unitInput = unitInput.enter().append("input").attr("type", "text").attr("class", "roadspeed-unit").attr("aria-label", _t("inspector.speed_unit")).call(unitCombo).merge(unitInput);
       unitInput.on("blur", changeUnits).on("change", changeUnits);
       function changeUnits() {
-        _isImperial = utilGetSetValue(unitInput) === "mph";
+        var unit2 = utilGetSetValue(unitInput);
+        if (unit2 === "km/h") {
+          _isImperial = false;
+        } else if (unit2 === "mph") {
+          _isImperial = true;
+        }
         utilGetSetValue(unitInput, _isImperial ? "mph" : "km/h");
         setUnitSuggestions();
         change();
@@ -58455,18 +61296,19 @@ ${content}</tr>
       placeholder = wrap2.selectAll(".placeholder");
       labels = wrap2.selectAll("label").data(radioData);
       enter = labels.enter().append("label");
+      var stringsField = field.resolveReference("stringsCrossReference");
       enter.append("input").attr("type", "radio").attr("name", field.id).attr("value", function(d) {
-        return field.t("options." + d, { "default": d });
+        return stringsField.t("options." + d, { "default": d });
       }).attr("checked", false);
-      enter.append("span").html(function(d) {
-        return field.t.html("options." + d, { "default": d });
+      enter.append("span").each(function(d) {
+        stringsField.t.append("options." + d, { "default": d })(select_default2(this));
       });
       labels = labels.merge(enter);
       radios = labels.selectAll("input").on("change", changeRadio);
     }
     function structureExtras(selection2, tags) {
       var selected = selectedKey() || tags.layer !== void 0;
-      var type3 = _mainPresetIndex.field(selected);
+      var type2 = _mainPresetIndex.field(selected);
       var layer = _mainPresetIndex.field("layer");
       var showLayer = selected === "bridge" || selected === "tunnel" || tags.layer !== void 0;
       var extrasWrap = selection2.selectAll(".structure-extras-wrap").data(selected ? [0] : []);
@@ -58474,9 +61316,9 @@ ${content}</tr>
       extrasWrap = extrasWrap.enter().append("div").attr("class", "structure-extras-wrap").merge(extrasWrap);
       var list = extrasWrap.selectAll("ul").data([0]);
       list = list.enter().append("ul").attr("class", "rows").merge(list);
-      if (type3) {
+      if (type2) {
         if (!typeField || typeField.id !== selected) {
-          typeField = uiField(context, type3, _entityIDs, { wrap: false }).on("change", changeType);
+          typeField = uiField(context, type2, _entityIDs, { wrap: false }).on("change", changeType);
         }
         typeField.tags(tags);
       } else {
@@ -58654,9 +61496,11 @@ ${content}</tr>
         _graph = context.graph();
         _intersection = osmIntersection(_graph, _vertexID, _maxDistance);
       }
-      var isOK = _intersection && _intersection.vertices.length && _intersection.vertices.filter(function(vertex) {
+      var isOK = _intersection && _intersection.vertices.length && // has vertices
+      _intersection.vertices.filter(function(vertex) {
         return vertex.id === _vertexID;
-      }).length && _intersection.ways.length > 2 && _intersection.ways.filter(function(way) {
+      }).length && _intersection.ways.length > 2 && // has more than 2 ways
+      _intersection.ways.filter(function(way) {
         return way.__to;
       }).length > 1;
       select_default2(selection2.node().parentNode).classed("hide", !isOK);
@@ -58958,6 +61802,7 @@ ${content}</tr>
         var opts;
         if (isImperial) {
           var distToFeet = {
+            // imprecise conversion for prettier display
             20: 70,
             25: 85,
             30: 100,
@@ -58981,10 +61826,10 @@ ${content}</tr>
     }
     function displayName(entityID, graph) {
       var entity = graph.entity(entityID);
-      var name2 = utilDisplayName(entity) || "";
+      var name = utilDisplayName(entity) || "";
       var matched = _mainPresetIndex.match(entity, graph);
-      var type3 = matched && matched.name() || utilDisplayType(entity.id);
-      return name2 || type3;
+      var type2 = matched && matched.name() || utilDisplayType(entity.id);
+      return name || type2;
     }
     restrictions.entityIDs = function(val) {
       _intersection = null;
@@ -59010,29 +61855,34 @@ ${content}</tr>
   function uiFieldTextarea(field, context) {
     var dispatch10 = dispatch_default("change");
     var input = select_default2(null);
+    var _lengthIndicator = uiLengthIndicator(context.maxCharsForTagValue()).silent(field.usage === "changeset" && field.key === "comment");
     var _tags;
     function textarea(selection2) {
       var wrap2 = selection2.selectAll(".form-field-input-wrap").data([0]);
-      wrap2 = wrap2.enter().append("div").attr("class", "form-field-input-wrap form-field-input-" + field.type).merge(wrap2);
+      wrap2 = wrap2.enter().append("div").attr("class", "form-field-input-wrap form-field-input-" + field.type).style("position", "relative").merge(wrap2);
       input = wrap2.selectAll("textarea").data([0]);
       input = input.enter().append("textarea").attr("id", field.domId).call(utilNoAuto).on("input", change(true)).on("blur", change()).on("change", change()).merge(input);
-    }
-    function change(onInput) {
-      return function() {
-        var val = utilGetSetValue(input);
-        if (!onInput)
-          val = context.cleanTagValue(val);
-        if (!val && Array.isArray(_tags[field.key]))
-          return;
-        var t = {};
-        t[field.key] = val || void 0;
-        dispatch10.call("change", this, t, onInput);
-      };
+      wrap2.call(_lengthIndicator);
+      function change(onInput) {
+        return function() {
+          var val = utilGetSetValue(input);
+          if (!onInput)
+            val = context.cleanTagValue(val);
+          if (!val && Array.isArray(_tags[field.key]))
+            return;
+          var t = {};
+          t[field.key] = val || void 0;
+          dispatch10.call("change", this, t, onInput);
+        };
+      }
     }
     textarea.tags = function(tags) {
       _tags = tags;
       var isMixed = Array.isArray(tags[field.key]);
       utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : "").attr("title", isMixed ? tags[field.key].filter(Boolean).join("\n") : void 0).attr("placeholder", isMixed ? _t("inspector.multiple_values") : field.placeholder() || _t("inspector.unknown")).classed("mixed", isMixed);
+      if (!isMixed) {
+        _lengthIndicator.update(tags[field.key]);
+      }
     };
     textarea.focus = function() {
       input.node().focus();
@@ -59052,7 +61902,8 @@ ${content}</tr>
     var _entityIDs = [];
     var _wikipediaKey = field.keys && field.keys.find(function(key) {
       return key.includes("wikipedia");
-    }), _hintKey = field.key === "wikidata" ? "name" : field.key.split(":")[0];
+    });
+    var _hintKey = field.key === "wikidata" ? "name" : field.key.split(":")[0];
     var combobox = uiCombobox(context, "combo-" + field.safeid).caseSensitive(true).minItems(1);
     function wiki(selection2) {
       _selection = selection2;
@@ -59111,12 +61962,17 @@ ${content}</tr>
       wikidata.itemsForSearchQuery(q, function(err, data) {
         if (err)
           return;
-        for (var i3 in data) {
-          data[i3].value = data[i3].label + " (" + data[i3].id + ")";
-          data[i3].title = data[i3].description;
-        }
+        var result = data.map(function(item) {
+          return {
+            id: item.id,
+            value: item.display.label.value + " (" + item.id + ")",
+            display: (selection2) => selection2.append("span").attr("class", "localized-text").attr("lang", item.display.label.language).text(item.display.label.value),
+            title: item.display.description && item.display.description.value,
+            terms: item.aliases
+          };
+        });
         if (callback)
-          callback(data);
+          callback(result);
       });
     }
     function change() {
@@ -59190,12 +62046,15 @@ ${content}</tr>
         }).filter(Boolean);
         if (!actions.length)
           return;
-        context.overwrite(function actionUpdateWikipediaTags(graph) {
-          actions.forEach(function(action) {
-            graph = action(graph);
-          });
-          return graph;
-        }, context.history().undoAnnotation());
+        context.overwrite(
+          function actionUpdateWikipediaTags(graph) {
+            actions.forEach(function(action) {
+              graph = action(graph);
+            });
+            return graph;
+          },
+          context.history().undoAnnotation()
+        );
       });
     }
     function setLabelForEntity() {
@@ -59224,11 +62083,11 @@ ${content}</tr>
         }
         _wikidataEntity = entity;
         setLabelForEntity();
-        var description2 = entityPropertyForDisplay(entity, "descriptions");
+        var description = entityPropertyForDisplay(entity, "descriptions");
         _selection.select("button.wiki-link").classed("disabled", false);
         _selection.select(".preset-wikidata-description").style("display", function() {
-          return description2.length > 0 ? "flex" : "none";
-        }).select("input").attr("value", description2);
+          return description.length > 0 ? "flex" : "none";
+        }).select("input").attr("value", description);
         _selection.select(".preset-wikidata-identifier").style("display", function() {
           return entity.id ? "flex" : "none";
         }).select("input").attr("value", entity.id);
@@ -59293,9 +62152,11 @@ ${content}</tr>
     });
     const langCombo = uiCombobox(context, "wikipedia-lang").fetcher((value, callback) => {
       const v = value.toLowerCase();
-      callback(_dataWikipedia.filter((d) => {
-        return d[0].toLowerCase().indexOf(v) >= 0 || d[1].toLowerCase().indexOf(v) >= 0 || d[2].toLowerCase().indexOf(v) >= 0;
-      }).map((d) => ({ value: d[1] })));
+      callback(
+        _dataWikipedia.filter((d) => {
+          return d[0].toLowerCase().indexOf(v) >= 0 || d[1].toLowerCase().indexOf(v) >= 0 || d[2].toLowerCase().indexOf(v) >= 0;
+        }).map((d) => ({ value: d[1] }))
+      );
     });
     const titleCombo = uiCombobox(context, "wikipedia-title").fetcher((value, callback) => {
       if (!value) {
@@ -59405,12 +62266,15 @@ ${content}</tr>
         }).filter(Boolean);
         if (!actions.length)
           return;
-        context.overwrite(function actionUpdateWikidataTags(graph) {
-          actions.forEach(function(action) {
-            graph = action(graph);
-          });
-          return graph;
-        }, context.history().undoAnnotation());
+        context.overwrite(
+          function actionUpdateWikidataTags(graph) {
+            actions.forEach(function(action) {
+              graph = action(graph);
+            });
+            return graph;
+          },
+          context.history().undoAnnotation()
+        );
       });
     }
     wiki.tags = (tags) => {
@@ -59443,7 +62307,10 @@ ${content}</tr>
           const defaultLangInfo = defaultLanguageInfo();
           _wikiURL = `https://${defaultLangInfo[2]}.wikipedia.org/w/index.php?fulltext=1&search=${value}`;
         } else {
-          const shownOrDefaultLangInfo = language(true);
+          const shownOrDefaultLangInfo = language(
+            true
+            /* skipEnglishFallback */
+          );
           utilGetSetValue(_langInput, shownOrDefaultLangInfo[1]);
           _wikiURL = "";
         }
@@ -59467,9 +62334,11 @@ ${content}</tr>
     access: uiFieldAccess,
     address: uiFieldAddress,
     check: uiFieldCheck,
+    colour: uiFieldText,
     combo: uiFieldCombo,
-    cycleway: uiFieldCycleway,
+    cycleway: uiFieldDirectionalCombo,
     defaultCheck: uiFieldCheck,
+    directionalCombo: uiFieldDirectionalCombo,
     email: uiFieldText,
     identifier: uiFieldText,
     lanes: uiFieldLanes,
@@ -59517,7 +62386,7 @@ ${content}</tr>
       }, geoExtent());
     }
     var _locked = false;
-    var _lockedTip = uiTooltip().title(_t.html("inspector.lock.suggestion", { label: field.label })).placement("bottom");
+    var _lockedTip = uiTooltip().title(() => _t.append("inspector.lock.suggestion", { label: field.title })).placement("bottom");
     field.keys = field.keys || [field.key];
     if (_show && !field.impl) {
       createField();
@@ -59585,8 +62454,8 @@ ${content}</tr>
           return d.domId;
         });
         var textEnter = labelEnter.append("span").attr("class", "label-text");
-        textEnter.append("span").attr("class", "label-textvalue").html(function(d) {
-          return d.label();
+        textEnter.append("span").attr("class", "label-textvalue").each(function(d) {
+          d.label()(select_default2(this));
         });
         textEnter.append("span").attr("class", "label-textannotation");
         if (options2.remove) {
@@ -59680,12 +62549,13 @@ ${content}</tr>
       }))
         return false;
       if (entityIDs && _entityExtent && field.locationSetID) {
-        var validLocations = _mainLocations.locationsAt(_entityExtent.center());
-        if (!validLocations[field.locationSetID])
+        var validHere = _sharedLocationManager.locationSetsAt(_entityExtent.center());
+        if (!validHere[field.locationSetID])
           return false;
       }
       var prerequisiteTag = field.prerequisiteTag;
-      if (entityIDs && !tagsContainFieldKey() && prerequisiteTag) {
+      if (entityIDs && !tagsContainFieldKey() && // ignore tagging prerequisites if a value is already present
+      prerequisiteTag) {
         if (!entityIDs.every(function(entityID) {
           var entity = context.graph().entity(entityID);
           if (prerequisiteTag.key) {
@@ -59772,14 +62642,16 @@ ${content}</tr>
       var input = more.selectAll(".value").data([0]);
       input.exit().remove();
       input = input.enter().append("input").attr("class", "value").attr("type", "text").attr("placeholder", placeholder).call(utilNoAuto).merge(input);
-      input.call(utilGetSetValue, "").call(moreCombo.data(moreFields).on("accept", function(d) {
-        if (!d)
-          return;
-        var field = d.field;
-        field.show();
-        selection2.call(formFields);
-        field.focus();
-      }));
+      input.call(utilGetSetValue, "").call(
+        moreCombo.data(moreFields).on("accept", function(d) {
+          if (!d)
+            return;
+          var field = d.field;
+          field.show();
+          selection2.call(formFields);
+          field.focus();
+        })
+      );
       if (_lastPlaceholder !== placeholder) {
         input.attr("placeholder", placeholder);
         _lastPlaceholder = placeholder;
@@ -59854,16 +62726,40 @@ ${content}</tr>
               var comment = changeset.tags.comment;
               return comment ? { title: comment, value: comment } : null;
             }).filter(Boolean);
-            commentField.call(commentCombo.data(utilArrayUniqBy(comments, "title")));
+            commentField.call(
+              commentCombo.data(utilArrayUniqBy(comments, "title"))
+            );
           });
         }
       }
-      var hasGoogle = _tags.comment.match(/google/i);
-      var commentWarning = selection2.select(".form-field-comment").selectAll(".comment-warning").data(hasGoogle ? [0] : []);
+      const warnings = [];
+      if (_tags.comment.match(/google/i)) {
+        warnings.push({
+          id: 'contains "google"',
+          msg: _t.append("commit.google_warning"),
+          link: _t("commit.google_warning_link")
+        });
+      }
+      const maxChars = context.maxCharsForTagValue();
+      const strLen = utilUnicodeCharsCount(utilCleanOsmString(_tags.comment, Number.POSITIVE_INFINITY));
+      if (strLen > maxChars || false) {
+        warnings.push({
+          id: "message too long",
+          msg: _t.append("commit.changeset_comment_length_warning", { maxChars })
+        });
+      }
+      var commentWarning = selection2.select(".form-field-comment").selectAll(".comment-warning").data(warnings, (d) => d.id);
       commentWarning.exit().transition().duration(200).style("opacity", 0).remove();
-      var commentEnter = commentWarning.enter().insert("div", ".tag-reference-body").attr("class", "field-warning comment-warning").style("opacity", 0);
-      commentEnter.append("a").attr("target", "_blank").call(svgIcon("#iD-icon-alert", "inline")).attr("href", _t("commit.google_warning_link")).append("span").call(_t.append("commit.google_warning"));
+      var commentEnter = commentWarning.enter().insert("div", ".comment-warning").attr("class", "comment-warning field-warning").style("opacity", 0);
+      commentEnter.call(svgIcon("#iD-icon-alert", "inline")).append("span");
       commentEnter.transition().duration(200).style("opacity", 1);
+      commentWarning.merge(commentEnter).selectAll("div > span").text("").each(function(d) {
+        let selection3 = select_default2(this);
+        if (d.link) {
+          selection3 = selection3.append("a").attr("target", "_blank").attr("href", d.link);
+        }
+        selection3.call(d.msg);
+      });
     }
     changesetEditor.tags = function(_) {
       if (!arguments.length)
@@ -59917,7 +62813,10 @@ ${content}</tr>
     }
     function createObjTree(oParentNode, nVerb, bFreeze, bNesteAttr) {
       var nLevelStart = aCache.length, bChildren = oParentNode.hasChildNodes(), bAttributes = oParentNode.hasAttributes(), bHighVerb = Boolean(nVerb & 2);
-      var sProp, vContent, nLength = 0, sCollectedTxt = "", vResult = bHighVerb ? {} : true;
+      var sProp, vContent, nLength = 0, sCollectedTxt = "", vResult = bHighVerb ? {} : (
+        /* put here the default value for empty nodes: */
+        true
+      );
       if (bChildren) {
         for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
           oNode = oParentNode.childNodes.item(nItem);
@@ -60012,7 +62911,10 @@ ${content}</tr>
       }
     }
     this.build = function(oXMLParent, nVerbosity, bFreeze, bNesteAttributes) {
-      var _nVerb = arguments.length > 1 && typeof nVerbosity === "number" ? nVerbosity & 3 : 1;
+      var _nVerb = arguments.length > 1 && typeof nVerbosity === "number" ? nVerbosity & 3 : (
+        /* put here the default verbosity level: */
+        1
+      );
       return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);
     };
     this.unbuild = function(oObjTree) {
@@ -60035,7 +62937,7 @@ ${content}</tr>
     var section = uiSection("changes-list", context).label(function() {
       var history = context.history();
       var summary = history.difference().summary();
-      return _t.html("inspector.title_count", { title: { html: _t.html("commit.changes") }, count: summary.length });
+      return _t.append("inspector.title_count", { title: _t("commit.changes"), count: summary.length });
     }).disclosureContent(renderDisclosureContent);
     function renderDisclosureContent(selection2) {
       var history = context.history();
@@ -60058,11 +62960,11 @@ ${content}</tr>
         return matched && matched.name() || utilDisplayType(d.entity.id);
       });
       buttons.append("span").attr("class", "entity-name").text(function(d) {
-        var name2 = utilDisplayName(d.entity) || "", string = "";
-        if (name2 !== "") {
+        var name = utilDisplayName(d.entity) || "", string = "";
+        if (name !== "") {
           string += ":";
         }
-        return string += " " + name2;
+        return string += " " + name;
       });
       items = itemsEnter.merge(items);
       var changeset = new osmChangeset().update({ id: void 0 });
@@ -60076,7 +62978,9 @@ ${content}</tr>
       linkEnter.call(svgIcon("#iD-icon-load", "inline")).append("span").call(_t.append("commit.download_changes"));
       function mouseover(d) {
         if (d.entity) {
-          context.surface().selectAll(utilEntityOrMemberSelector([d.entity.id], context.graph())).classed("hover", true);
+          context.surface().selectAll(
+            utilEntityOrMemberSelector([d.entity.id], context.graph())
+          ).classed("hover", true);
         }
       }
       function mouseout() {
@@ -60109,7 +63013,7 @@ ${content}</tr>
         var container = selection2.selectAll("." + section).data(issues.length ? [0] : []);
         container.exit().remove();
         var containerEnter = container.enter().append("div").attr("class", "modal-section " + section + " fillL2");
-        containerEnter.append("h3").html(severity === "warning" ? _t.html("commit.warnings") : _t.html("commit.errors"));
+        containerEnter.append("h3").call(severity === "warning" ? _t.append("commit.warnings") : _t.append("commit.errors"));
         containerEnter.append("ul").attr("class", "changeset-list");
         container = containerEnter.merge(container);
         var items = container.select("ul").selectAll("li").data(issues, function(d) {
@@ -60119,7 +63023,12 @@ ${content}</tr>
         var itemsEnter = items.enter().append("li").attr("class", issueItem);
         var buttons = itemsEnter.append("button").on("mouseover", function(d3_event, d) {
           if (d.entityIds) {
-            context.surface().selectAll(utilEntityOrMemberSelector(d.entityIds, context.graph())).classed("hover", true);
+            context.surface().selectAll(
+              utilEntityOrMemberSelector(
+                d.entityIds,
+                context.graph()
+              )
+            ).classed("hover", true);
           }
         }).on("mouseout", function() {
           context.surface().selectAll(".hover").classed("hover", false);
@@ -60130,12 +63039,14 @@ ${content}</tr>
         buttons.append("strong").attr("class", "issue-message");
         buttons.filter(function(d) {
           return d.tooltip;
-        }).call(uiTooltip().title(function(d) {
-          return d.tooltip;
-        }).placement("top"));
+        }).call(
+          uiTooltip().title(function(d) {
+            return d.tooltip;
+          }).placement("top")
+        );
         items = itemsEnter.merge(items);
-        items.selectAll(".issue-message").html(function(d) {
-          return d.message(context);
+        items.selectAll(".issue-message").text("").each(function(d) {
+          return d.message(context)(select_default2(this));
         });
       }
     }
@@ -60297,7 +63208,9 @@ ${content}</tr>
       body = body.enter().append("div").attr("class", "body").merge(body);
       var changesetSection = body.selectAll(".changeset-editor").data([0]);
       changesetSection = changesetSection.enter().append("div").attr("class", "modal-section changeset-editor").merge(changesetSection);
-      changesetSection.call(changesetEditor.changesetID(context.changeset.id).tags(context.changeset.tags));
+      changesetSection.call(
+        changesetEditor.changesetID(context.changeset.id).tags(context.changeset.tags)
+      );
       body.call(commitWarnings);
       var saveSection = body.selectAll(".save-section").data([0]);
       saveSection = saveSection.enter().append("div").attr("class", "modal-section save-section fillL").merge(saveSection);
@@ -60324,7 +63237,7 @@ ${content}</tr>
       var requestReviewDomId = utilUniqueDomId("commit-input-request-review");
       var labelEnter = requestReviewEnter.append("label").attr("for", requestReviewDomId);
       if (!labelEnter.empty()) {
-        labelEnter.call(uiTooltip().title(_t.html("commit.request_review_info")).placement("top"));
+        labelEnter.call(uiTooltip().title(() => _t.append("commit.request_review_info")).placement("top"));
       }
       labelEnter.append("input").attr("type", "checkbox").attr("id", requestReviewDomId);
       labelEnter.append("span").call(_t.append("commit.request_review"));
@@ -60352,28 +63265,32 @@ ${content}</tr>
       });
       uiTooltip().destroyAny(buttonSection.selectAll(".save-button"));
       if (uploadBlockerTooltipText) {
-        buttonSection.selectAll(".save-button").call(uiTooltip().title(uploadBlockerTooltipText).placement("top"));
+        buttonSection.selectAll(".save-button").call(uiTooltip().title(() => uploadBlockerTooltipText).placement("top"));
       }
       var tagSection = body.selectAll(".tag-section.raw-tag-editor").data([0]);
       tagSection = tagSection.enter().append("div").attr("class", "modal-section tag-section raw-tag-editor").merge(tagSection);
-      tagSection.call(rawTagEditor.tags(Object.assign({}, context.changeset.tags)).render);
+      tagSection.call(
+        rawTagEditor.tags(Object.assign({}, context.changeset.tags)).render
+      );
       var changesSection = body.selectAll(".commit-changes-section").data([0]);
       changesSection = changesSection.enter().append("div").attr("class", "modal-section commit-changes-section").merge(changesSection);
       changesSection.call(commitChanges.render);
       function toggleRequestReview() {
         var rr = requestReviewInput.property("checked");
         updateChangeset({ review_requested: rr ? "yes" : void 0 });
-        tagSection.call(rawTagEditor.tags(Object.assign({}, context.changeset.tags)).render);
+        tagSection.call(
+          rawTagEditor.tags(Object.assign({}, context.changeset.tags)).render
+        );
       }
     }
     function getUploadBlockerMessage() {
       var errors = context.validator().getIssuesBySeverity({ what: "edited", where: "all" }).error;
       if (errors.length) {
-        return _t("commit.outstanding_errors_message", { count: errors.length });
+        return _t.append("commit.outstanding_errors_message", { count: errors.length });
       } else {
         var hasChangesetComment = context.changeset && context.changeset.tags.comment && context.changeset.tags.comment.trim().length;
         if (!hasChangesetComment) {
-          return _t("commit.comment_needed_message");
+          return _t.append("commit.comment_needed_message");
         }
       }
       return null;
@@ -60583,9 +63500,7 @@ ${content}</tr>
         return d;
       });
       details.append("div").attr("class", "conflict-choices").call(addChoices);
-      details.append("div").attr("class", "conflict-nav-buttons joined cf").selectAll("button").data(["previous", "next"]).enter().append("button").html(function(d) {
-        return _t.html("save.conflict." + d);
-      }).attr("class", "conflict-nav-button action col6").attr("disabled", function(d, i2) {
+      details.append("div").attr("class", "conflict-nav-buttons joined cf").selectAll("button").data(["previous", "next"]).enter().append("button").attr("class", "conflict-nav-button action col6").attr("disabled", function(d, i2) {
         return i2 === 0 && index === 0 || i2 === 1 && index === _conflictList.length - 1 || null;
       }).on("click", function(d3_event, d) {
         d3_event.preventDefault();
@@ -60593,6 +63508,8 @@ ${content}</tr>
         var sign2 = d === "previous" ? -1 : 1;
         container.selectAll(".conflict").remove();
         container.call(showConflict, index + sign2);
+      }).each(function(d) {
+        _t.append("save.conflict." + d)(select_default2(this));
       });
     }
     function addChoices(selection2) {
@@ -60683,7 +63600,7 @@ ${content}</tr>
     var section = uiSection("entity-issues", context).shouldDisplay(function() {
       return _issues.length > 0;
     }).label(function() {
-      return _t.html("inspector.title_count", { title: { html: _t.html("issues.list_title") }, count: _issues.length });
+      return _t.append("inspector.title_count", { title: _t("issues.list_title"), count: _issues.length });
     }).disclosureContent(renderDisclosureContent);
     context.validator().on("validated.entity_issues", function() {
       reloadIssues();
@@ -60766,8 +63683,8 @@ ${content}</tr>
       containers = containers.merge(containersEnter).classed("active", function(d) {
         return d.id === _activeIssueID;
       });
-      containers.selectAll(".issue-message").html(function(d) {
-        return d.message(context);
+      containers.selectAll(".issue-message").text("").each(function(d) {
+        return d.message(context)(select_default2(this));
       });
       var fixLists = containers.selectAll(".issue-fix-list");
       var fixes = fixLists.selectAll(".issue-fix-item").data(function(d) {
@@ -60804,8 +63721,8 @@ ${content}</tr>
         }
         select_default2(this).call(svgIcon("#" + iconName, "fix-icon"));
       });
-      buttons.append("span").attr("class", "fix-message").html(function(d) {
-        return d.title;
+      buttons.append("span").attr("class", "fix-message").each(function(d) {
+        return d.title(select_default2(this));
       });
       fixesEnter.merge(fixes).selectAll("button").classed("actionable", function(d) {
         return d.onClick;
@@ -60839,12 +63756,14 @@ ${content}</tr>
       selection2.each(render);
     }
     function getIcon(p, geom) {
+      if (p.isFallback && p.isFallback())
+        return geom === "vertex" ? "" : "iD-icon-" + p.id;
       if (p.icon)
         return p.icon;
       if (geom === "line")
         return "iD-other-line";
       if (geom === "vertex")
-        return p.isFallback() ? "" : "temaki-vertex";
+        return "temaki-vertex";
       return "maki-marker-stroked";
     }
     function renderPointBorder(container, drawPoint) {
@@ -60893,12 +63812,12 @@ ${content}</tr>
         fillEnter.append("path").attr("d", `M${c1} ${c1} L${c1} ${c2} L${c2} ${c2} L${c2} ${c1} Z`).attr("class", `area ${klass}`);
       });
       const rVertex = 2.5;
-      [[c1, c1], [c1, c2], [c2, c2], [c2, c1]].forEach((point) => {
-        fillEnter.append("circle").attr("class", "vertex").attr("cx", point[0]).attr("cy", point[1]).attr("r", rVertex);
+      [[c1, c1], [c1, c2], [c2, c2], [c2, c1]].forEach((point2) => {
+        fillEnter.append("circle").attr("class", "vertex").attr("cx", point2[0]).attr("cy", point2[1]).attr("r", rVertex);
       });
       const rMidpoint = 1.25;
-      [[c1, w / 2], [c2, w / 2], [h / 2, c1], [h / 2, c2]].forEach((point) => {
-        fillEnter.append("circle").attr("class", "midpoint").attr("cx", point[0]).attr("cy", point[1]).attr("r", rMidpoint);
+      [[c1, w / 2], [c2, w / 2], [h / 2, c1], [h / 2, c2]].forEach((point2) => {
+        fillEnter.append("circle").attr("class", "midpoint").attr("cx", point2[0]).attr("cy", point2[1]).attr("r", rMidpoint);
       });
       fill = fillEnter.merge(fill);
       fill.selectAll("path.stroke").attr("class", `area stroke ${tagClasses}`);
@@ -60920,8 +63839,8 @@ ${content}</tr>
       ["casing", "stroke"].forEach((klass) => {
         lineEnter.append("path").attr("d", `M${x12} ${y} L${x2} ${y}`).attr("class", `line ${klass}`);
       });
-      [[x12 - 1, y], [x2 + 1, y]].forEach((point) => {
-        lineEnter.append("circle").attr("class", "vertex").attr("cx", point[0]).attr("cy", point[1]).attr("r", r);
+      [[x12 - 1, y], [x2 + 1, y]].forEach((point2) => {
+        lineEnter.append("circle").attr("class", "vertex").attr("cx", point2[0]).attr("cy", point2[1]).attr("r", r);
       });
       line = lineEnter.merge(line);
       line.selectAll("path.stroke").attr("class", `line stroke ${tagClasses}`);
@@ -60948,8 +63867,8 @@ ${content}</tr>
         routeEnter.append("path").attr("d", `M${x2} ${y2} L${x3} ${y12}`).attr("class", `segment1 line ${klass}`);
         routeEnter.append("path").attr("d", `M${x3} ${y12} L${x4} ${y2}`).attr("class", `segment2 line ${klass}`);
       });
-      [[x12, y12], [x2, y2], [x3, y12], [x4, y2]].forEach((point) => {
-        routeEnter.append("circle").attr("class", "vertex").attr("cx", point[0]).attr("cy", point[1]).attr("r", r);
+      [[x12, y12], [x2, y2], [x3, y12], [x4, y2]].forEach((point2) => {
+        routeEnter.append("circle").attr("class", "vertex").attr("cx", point2[0]).attr("cy", point2[1]).attr("r", r);
       });
       route = routeEnter.merge(route);
       if (drawRoute) {
@@ -61014,7 +63933,7 @@ ${content}</tr>
       const picon = getIcon(p, geom);
       const isCategory = !p.setTags;
       const drawPoint = false;
-      const drawVertex = picon !== null && geom === "vertex" && !isFallback;
+      const drawVertex = picon !== null && geom === "vertex";
       const drawLine = picon && geom === "line" && !isFallback && !isCategory;
       const drawArea = picon && geom === "area" && !isFallback && !isCategory;
       const drawRoute = picon && geom === "route";
@@ -61060,12 +63979,14 @@ ${content}</tr>
     var _entityIDs = [];
     var _presets = [];
     var _tagReference;
-    var section = uiSection("feature-type", context).label(_t.html("inspector.feature_type")).disclosureContent(renderDisclosureContent);
+    var section = uiSection("feature-type", context).label(() => _t.append("inspector.feature_type")).disclosureContent(renderDisclosureContent);
     function renderDisclosureContent(selection2) {
       selection2.classed("preset-list-item", true);
       selection2.classed("mixed-types", _presets.length > 1);
       var presetButtonWrap = selection2.selectAll(".preset-list-button-wrap").data([0]).enter().append("div").attr("class", "preset-list-button-wrap");
-      var presetButton = presetButtonWrap.append("button").attr("class", "preset-list-button preset-reset").call(uiTooltip().title(_t.html("inspector.back_tooltip")).placement("bottom"));
+      var presetButton = presetButtonWrap.append("button").attr("class", "preset-list-button preset-reset").call(
+        uiTooltip().title(() => _t.append("inspector.back_tooltip")).placement("bottom")
+      );
       presetButton.append("div").attr("class", "preset-icon-container");
       presetButton.append("div").attr("class", "label").append("div").attr("class", "label-inner");
       presetButtonWrap.append("div").attr("class", "accessory-buttons");
@@ -61082,18 +64003,18 @@ ${content}</tr>
         d3_event.stopPropagation();
       });
       var geometries = entityGeometries();
-      selection2.select(".preset-list-item button").call(uiPresetIcon().geometry(_presets.length === 1 ? geometries.length === 1 && geometries[0] : null).preset(_presets.length === 1 ? _presets[0] : _mainPresetIndex.item("point")));
+      selection2.select(".preset-list-item button").call(
+        uiPresetIcon().geometry(_presets.length === 1 ? geometries.length === 1 && geometries[0] : null).preset(_presets.length === 1 ? _presets[0] : _mainPresetIndex.item("point"))
+      );
       var names = _presets.length === 1 ? [
         _presets[0].nameLabel(),
         _presets[0].subtitleLabel()
-      ].filter(Boolean) : [_t("inspector.multiple_types")];
+      ].filter(Boolean) : [_t.append("inspector.multiple_types")];
       var label = selection2.select(".label-inner");
-      var nameparts = label.selectAll(".namepart").data(names, function(d) {
-        return d;
-      });
+      var nameparts = label.selectAll(".namepart").data(names, (d) => d.stringId);
       nameparts.exit().remove();
-      nameparts.enter().append("div").attr("class", "namepart").html(function(d) {
-        return d;
+      nameparts.enter().append("div").attr("class", "namepart").text("").each(function(d) {
+        d(select_default2(this));
       });
     }
     section.entityIDs = function(val) {
@@ -61130,7 +64051,7 @@ ${content}</tr>
 
   // modules/ui/sections/preset_fields.js
   function uiSectionPresetFields(context) {
-    var section = uiSection("preset-fields", context).label(_t.html("inspector.fields")).disclosureContent(renderDisclosureContent);
+    var section = uiSection("preset-fields", context).label(() => _t.append("inspector.fields")).disclosureContent(renderDisclosureContent);
     var dispatch10 = dispatch_default("change", "revert");
     var formFields = uiFormFields(context);
     var _state;
@@ -61171,20 +64092,26 @@ ${content}</tr>
         _fieldsArr = [];
         sharedFields.forEach(function(field) {
           if (field.matchAllGeometry(geometries)) {
-            _fieldsArr.push(uiField(context, field, _entityIDs));
+            _fieldsArr.push(
+              uiField(context, field, _entityIDs)
+            );
           }
         });
         var singularEntity = _entityIDs.length === 1 && graph.hasEntity(_entityIDs[0]);
         if (singularEntity && singularEntity.isHighwayIntersection(graph) && presetsManager.field("restrictions")) {
-          _fieldsArr.push(uiField(context, presetsManager.field("restrictions"), _entityIDs));
+          _fieldsArr.push(
+            uiField(context, presetsManager.field("restrictions"), _entityIDs)
+          );
         }
         var additionalFields = utilArrayUnion(sharedMoreFields, presetsManager.universal());
         additionalFields.sort(function(field1, field2) {
-          return field1.label().localeCompare(field2.label(), _mainLocalizer.localeCode());
+          return field1.title().localeCompare(field2.title(), _mainLocalizer.localeCode());
         });
         additionalFields.forEach(function(field) {
           if (sharedFields.indexOf(field) === -1 && field.matchAllGeometry(geometries)) {
-            _fieldsArr.push(uiField(context, field, _entityIDs, { show: false }));
+            _fieldsArr.push(
+              uiField(context, field, _entityIDs, { show: false })
+            );
           }
         });
         _fieldsArr.forEach(function(field) {
@@ -61198,12 +64125,9 @@ ${content}</tr>
       _fieldsArr.forEach(function(field) {
         field.state(_state).tags(_tags);
       });
-      selection2.call(formFields.fieldsArr(_fieldsArr).state(_state).klass("grouped-items-area"));
-      selection2.selectAll(".wrap-form-field input").on("keydown", function(d3_event) {
-        if (d3_event.keyCode === 13 && context.container().select(".combobox").empty()) {
-          context.enter(modeBrowse(context));
-        }
-      });
+      selection2.call(
+        formFields.fieldsArr(_fieldsArr).state(_state).klass("grouped-items-area")
+      );
     }
     section.presets = function(val) {
       if (!arguments.length)
@@ -61251,7 +64175,7 @@ ${content}</tr>
         return "";
       var gt = entity.members.length > _maxMembers ? ">" : "";
       var count = gt + entity.members.slice(0, _maxMembers).length;
-      return _t.html("inspector.title_count", { title: { html: _t.html("inspector.members") }, count });
+      return _t.append("inspector.title_count", { title: _t("inspector.members"), count });
     }).disclosureContent(renderDisclosureContent);
     var taginfo = services.taginfo;
     var _entityIDs;
@@ -61284,17 +64208,23 @@ ${content}</tr>
       var newRole = context.cleanRelationRole(select_default2(this).property("value"));
       if (oldRole !== newRole) {
         var member = { id: d.id, type: d.type, role: newRole };
-        context.perform(actionChangeMember(d.relation.id, member, d.index), _t("operations.change_role.annotation", {
-          n: 1
-        }));
+        context.perform(
+          actionChangeMember(d.relation.id, member, d.index),
+          _t("operations.change_role.annotation", {
+            n: 1
+          })
+        );
         context.validator().validate();
       }
     }
     function deleteMember(d3_event, d) {
       utilHighlightEntities([d.id], false, context);
-      context.perform(actionDeleteMember(d.relation.id, d.index), _t("operations.delete_member.annotation", {
-        n: 1
-      }));
+      context.perform(
+        actionDeleteMember(d.relation.id, d.index),
+        _t("operations.delete_member.annotation", {
+          n: 1
+        })
+      );
       if (!context.hasEntity(d.relation.id)) {
         context.enter(modeBrowse(context));
       } else {
@@ -61364,47 +64294,53 @@ ${content}</tr>
       }).on("blur", changeRole).on("change", changeRole);
       items.select("button.member-delete").on("click", deleteMember);
       var dragOrigin, targetIndex;
-      items.call(drag_default().on("start", function(d3_event) {
-        dragOrigin = {
-          x: d3_event.x,
-          y: d3_event.y
-        };
-        targetIndex = null;
-      }).on("drag", function(d3_event) {
-        var x = d3_event.x - dragOrigin.x, y = d3_event.y - dragOrigin.y;
-        if (!select_default2(this).classed("dragging") && Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5)
-          return;
-        var index = items.nodes().indexOf(this);
-        select_default2(this).classed("dragging", true);
-        targetIndex = null;
-        selection2.selectAll("li.member-row").style("transform", function(d2, index2) {
-          var node = select_default2(this).node();
-          if (index === index2) {
-            return "translate(" + x + "px, " + y + "px)";
-          } else if (index2 > index && d3_event.y > node.offsetTop) {
-            if (targetIndex === null || index2 > targetIndex) {
-              targetIndex = index2;
-            }
-            return "translateY(-100%)";
-          } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) {
-            if (targetIndex === null || index2 < targetIndex) {
-              targetIndex = index2;
-            }
-            return "translateY(100%)";
+      items.call(
+        drag_default().on("start", function(d3_event) {
+          dragOrigin = {
+            x: d3_event.x,
+            y: d3_event.y
+          };
+          targetIndex = null;
+        }).on("drag", function(d3_event) {
+          var x = d3_event.x - dragOrigin.x, y = d3_event.y - dragOrigin.y;
+          if (!select_default2(this).classed("dragging") && // don't display drag until dragging beyond a distance threshold
+          Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5)
+            return;
+          var index = items.nodes().indexOf(this);
+          select_default2(this).classed("dragging", true);
+          targetIndex = null;
+          selection2.selectAll("li.member-row").style("transform", function(d2, index2) {
+            var node = select_default2(this).node();
+            if (index === index2) {
+              return "translate(" + x + "px, " + y + "px)";
+            } else if (index2 > index && d3_event.y > node.offsetTop) {
+              if (targetIndex === null || index2 > targetIndex) {
+                targetIndex = index2;
+              }
+              return "translateY(-100%)";
+            } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) {
+              if (targetIndex === null || index2 < targetIndex) {
+                targetIndex = index2;
+              }
+              return "translateY(100%)";
+            }
+            return null;
+          });
+        }).on("end", function(d3_event, d) {
+          if (!select_default2(this).classed("dragging"))
+            return;
+          var index = items.nodes().indexOf(this);
+          select_default2(this).classed("dragging", false);
+          selection2.selectAll("li.member-row").style("transform", null);
+          if (targetIndex !== null) {
+            context.perform(
+              actionMoveMember(d.relation.id, index, targetIndex),
+              _t("operations.reorder_members.annotation")
+            );
+            context.validator().validate();
           }
-          return null;
-        });
-      }).on("end", function(d3_event, d) {
-        if (!select_default2(this).classed("dragging"))
-          return;
-        var index = items.nodes().indexOf(this);
-        select_default2(this).classed("dragging", false);
-        selection2.selectAll("li.member-row").style("transform", null);
-        if (targetIndex !== null) {
-          context.perform(actionMoveMember(d.relation.id, index, targetIndex), _t("operations.reorder_members.annotation"));
-          context.validator().validate();
-        }
-      }));
+        })
+      );
       function bindTypeahead(d) {
         var row = select_default2(this);
         var role = row.selectAll("input.member-role");
@@ -61421,30 +64357,32 @@ ${content}</tr>
           }
           return sameletter.concat(other);
         }
-        role.call(uiCombobox(context, "member-role").fetcher(function(role2, callback) {
-          var geometry;
-          if (d.member) {
-            geometry = context.graph().geometry(d.member.id);
-          } else if (d.type === "relation") {
-            geometry = "relation";
-          } else if (d.type === "way") {
-            geometry = "line";
-          } else {
-            geometry = "point";
-          }
-          var rtype = entity.tags.type;
-          taginfo.roles({
-            debounce: true,
-            rtype: rtype || "",
-            geometry,
-            query: role2
-          }, function(err, data) {
-            if (!err)
-              callback(sort(role2, data));
-          });
-        }).on("cancel", function() {
-          role.property("value", origValue);
-        }));
+        role.call(
+          uiCombobox(context, "member-role").fetcher(function(role2, callback) {
+            var geometry;
+            if (d.member) {
+              geometry = context.graph().geometry(d.member.id);
+            } else if (d.type === "relation") {
+              geometry = "relation";
+            } else if (d.type === "way") {
+              geometry = "line";
+            } else {
+              geometry = "point";
+            }
+            var rtype = entity.tags.type;
+            taginfo.roles({
+              debounce: true,
+              rtype: rtype || "",
+              geometry,
+              query: role2
+            }, function(err, data) {
+              if (!err)
+                callback(sort(role2, data));
+            });
+          }).on("cancel", function() {
+            role.property("value", origValue);
+          })
+        );
       }
       function unbind() {
         var row = select_default2(this);
@@ -61479,7 +64417,7 @@ ${content}</tr>
       var parents = getSharedParentRelations();
       var gt = parents.length > _maxMemberships ? ">" : "";
       var count = gt + parents.slice(0, _maxMemberships).length;
-      return _t.html("inspector.title_count", { title: { html: _t.html("inspector.relations") }, count });
+      return _t.append("inspector.title_count", { title: _t("inspector.relations"), count });
     }).disclosureContent(renderDisclosureContent);
     var taginfo = services.taginfo;
     var nearbyCombo = uiCombobox(context, "parent-relation").minItems(1).fetcher(fetchNearbyRelations).itemsMouseEnter(function(d3_event, d) {
@@ -61575,16 +64513,19 @@ ${content}</tr>
       });
       if (membersToUpdate.length) {
         _inChange = true;
-        context.perform(function actionChangeMemberRoles(graph) {
-          membersToUpdate.forEach(function(member) {
-            var newMember = Object.assign({}, member, { role: newRole });
-            delete newMember.index;
-            graph = actionChangeMember(d.relation.id, newMember, member.index)(graph);
-          });
-          return graph;
-        }, _t("operations.change_role.annotation", {
-          n: membersToUpdate.length
-        }));
+        context.perform(
+          function actionChangeMemberRoles(graph) {
+            membersToUpdate.forEach(function(member) {
+              var newMember = Object.assign({}, member, { role: newRole });
+              delete newMember.index;
+              graph = actionChangeMember(d.relation.id, newMember, member.index)(graph);
+            });
+            return graph;
+          },
+          _t("operations.change_role.annotation", {
+            n: membersToUpdate.length
+          })
+        );
         context.validator().validate();
       }
       _inChange = false;
@@ -61602,13 +64543,20 @@ ${content}</tr>
         };
       }
       if (d.relation) {
-        context.perform(actionAddMembers(d.relation.id, _entityIDs, role), _t("operations.add_member.annotation", {
-          n: _entityIDs.length
-        }));
+        context.perform(
+          actionAddMembers(d.relation.id, _entityIDs, role),
+          _t("operations.add_member.annotation", {
+            n: _entityIDs.length
+          })
+        );
         context.validator().validate();
       } else {
         var relation = osmRelation();
-        context.perform(actionAddEntity(relation), actionAddMembers(relation.id, _entityIDs, role), _t("operations.add.annotation.relation"));
+        context.perform(
+          actionAddEntity(relation),
+          actionAddMembers(relation.id, _entityIDs, role),
+          _t("operations.add.annotation.relation")
+        );
         context.enter(modeSelect(context, [relation.id]).newFeature(true));
       }
     }
@@ -61620,16 +64568,19 @@ ${content}</tr>
       var indexes = d.members.map(function(member) {
         return member.index;
       });
-      context.perform(actionDeleteMembers(d.relation.id, indexes), _t("operations.delete_member.annotation", {
-        n: _entityIDs.length
-      }));
+      context.perform(
+        actionDeleteMembers(d.relation.id, indexes),
+        _t("operations.delete_member.annotation", {
+          n: _entityIDs.length
+        })
+      );
       context.validator().validate();
     }
     function fetchNearbyRelations(q, callback) {
       var newRelation = {
         relation: null,
         value: _t("inspector.new_relation"),
-        display: _t.html("inspector.new_relation")
+        display: _t.append("inspector.new_relation")
       };
       var entityID = _entityIDs[0];
       var result = [];
@@ -61726,12 +64677,14 @@ ${content}</tr>
       var newWrapEnter = newMembershipEnter.append("div").attr("class", "form-field-input-wrap form-field-input-member");
       newWrapEnter.append("input").attr("class", "member-role").property("type", "text").attr("placeholder", _t("inspector.role")).call(utilNoAuto);
       newMembership = newMembership.merge(newMembershipEnter);
-      newMembership.selectAll(".member-entity-input").on("blur", cancelEntity).call(nearbyCombo.on("accept", acceptEntity).on("cancel", cancelEntity));
+      newMembership.selectAll(".member-entity-input").on("blur", cancelEntity).call(
+        nearbyCombo.on("accept", acceptEntity).on("cancel", cancelEntity)
+      );
       var addRow = selection2.selectAll(".add-row").data([0]);
       var addRowEnter = addRow.enter().append("div").attr("class", "add-row");
       var addRelationButton = addRowEnter.append("button").attr("class", "add-relation").attr("aria-label", _t("inspector.add_to_relation"));
       addRelationButton.call(svgIcon("#iD-icon-plus", "light"));
-      addRelationButton.call(uiTooltip().title(_t.html("inspector.add_to_relation")).placement(_mainLocalizer.textDirection() === "ltr" ? "right" : "left"));
+      addRelationButton.call(uiTooltip().title(() => _t.append("inspector.add_to_relation")).placement(_mainLocalizer.textDirection() === "ltr" ? "right" : "left"));
       addRowEnter.append("div").attr("class", "space-value");
       addRowEnter.append("div").attr("class", "space-buttons");
       addRow = addRow.merge(addRowEnter);
@@ -61771,20 +64724,22 @@ ${content}</tr>
           }
           return sameletter.concat(other);
         }
-        role.call(uiCombobox(context, "member-role").fetcher(function(role2, callback) {
-          var rtype = d.relation.tags.type;
-          taginfo.roles({
-            debounce: true,
-            rtype: rtype || "",
-            geometry: context.graph().geometry(_entityIDs[0]),
-            query: role2
-          }, function(err, data) {
-            if (!err)
-              callback(sort(role2, data));
-          });
-        }).on("cancel", function() {
-          role.property("value", origValue);
-        }));
+        role.call(
+          uiCombobox(context, "member-role").fetcher(function(role2, callback) {
+            var rtype = d.relation.tags.type;
+            taginfo.roles({
+              debounce: true,
+              rtype: rtype || "",
+              geometry: context.graph().geometry(_entityIDs[0]),
+              query: role2
+            }, function(err, data) {
+              if (!err)
+                callback(sort(role2, data));
+            });
+          }).on("cancel", function() {
+            role.property("value", origValue);
+          })
+        );
       }
       function unbind() {
         var row = select_default2(this);
@@ -61807,7 +64762,7 @@ ${content}</tr>
     var section = uiSection("selected-features", context).shouldDisplay(function() {
       return _selectedIDs.length > 1;
     }).label(function() {
-      return _t.html("inspector.title_count", { title: { html: _t.html("inspector.features") }, count: _selectedIDs.length });
+      return _t.append("inspector.title_count", { title: _t("inspector.features"), count: _selectedIDs.length });
     }).disclosureContent(renderDisclosureContent);
     context.history().on("change.selectionList", function(difference) {
       if (difference) {
@@ -61883,13 +64838,13 @@ ${content}</tr>
       var header = selection2.selectAll(".header").data([0]);
       var headerEnter = header.enter().append("div").attr("class", "header fillL");
       var direction = _mainLocalizer.textDirection() === "rtl" ? "forward" : "backward";
-      headerEnter.append("button").attr("class", "preset-reset preset-choose").attr("title", _t(`icons.${direction}`)).call(svgIcon(`#iD-icon-${direction}`));
+      headerEnter.append("button").attr("class", "preset-reset preset-choose").attr("title", _t("inspector.back_tooltip")).call(svgIcon(`#iD-icon-${direction}`));
       headerEnter.append("button").attr("class", "close").attr("title", _t("icons.close")).on("click", function() {
         context.enter(modeBrowse(context));
       }).call(svgIcon(_modified ? "#iD-icon-apply" : "#iD-icon-close"));
       headerEnter.append("h2");
       header = header.merge(headerEnter);
-      header.selectAll("h2").html(_entityIDs.length === 1 ? _t.html("inspector.edit") : _t.html("inspector.edit_features"));
+      header.selectAll("h2").text("").call(_entityIDs.length === 1 ? _t.append("inspector.edit") : _t.append("inspector.edit_features"));
       header.selectAll(".preset-reset").on("click", function() {
         dispatch10.call("choose", this, _activePresets);
       });
@@ -61952,14 +64907,18 @@ ${content}</tr>
         var entityID = entityIDs[i2];
         var entity = context.entity(entityID);
         var tags = Object.assign({}, entity.tags);
-        for (var k in changed) {
-          if (!k)
-            continue;
-          var v = changed[k];
-          if (typeof v === "object") {
-            tags[k] = tags[v.oldKey];
-          } else if (v !== void 0 || tags.hasOwnProperty(k)) {
-            tags[k] = v;
+        if (typeof changed === "function") {
+          tags = changed(tags);
+        } else {
+          for (var k in changed) {
+            if (!k)
+              continue;
+            var v = changed[k];
+            if (typeof v === "object") {
+              tags[k] = tags[v.oldKey];
+            } else if (v !== void 0 || tags.hasOwnProperty(k)) {
+              tags[k] = v;
+            }
           }
         }
         if (!onInput) {
@@ -62123,7 +65082,8 @@ ${content}</tr>
       }
       function keypress(d3_event) {
         var q = search.property("value"), items = list.selectAll(".feature-list-item");
-        if (d3_event.keyCode === 13 && q.length && items.size()) {
+        if (d3_event.keyCode === 13 && // ↩ Return
+        q.length && items.size()) {
           click(d3_event, items.datum());
         }
       }
@@ -62140,7 +65100,7 @@ ${content}</tr>
           drawList();
         }
       }
-      function features2() {
+      function features() {
         var result = [];
         var graph = context.graph();
         var visibleCenter = context.map().extent().center();
@@ -62149,7 +65109,7 @@ ${content}</tr>
           return result;
         var locationMatch = sexagesimal.pair(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
         if (locationMatch) {
-          var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
+          var loc = [Number(locationMatch[0]), Number(locationMatch[1])];
           result.push({
             id: -1,
             geometry: "point",
@@ -62175,19 +65135,19 @@ ${content}</tr>
           var entity = allEntities[id2];
           if (!entity)
             continue;
-          var name2 = utilDisplayName(entity) || "";
-          if (name2.toLowerCase().indexOf(q) < 0)
+          var name = utilDisplayName(entity) || "";
+          if (name.toLowerCase().indexOf(q) < 0)
             continue;
           var matched = _mainPresetIndex.match(entity, graph);
-          var type3 = matched && matched.name() || utilDisplayType(entity.id);
+          var type2 = matched && matched.name() || utilDisplayType(entity.id);
           var extent = entity.extent(graph);
           var distance = extent ? geoSphericalDistance(visibleCenter, extent.center()) : 0;
           localResults.push({
             id: entity.id,
             entity,
             geometry: entity.geometry(graph),
-            type: type3,
-            name: name2,
+            type: type2,
+            name,
             distance
           });
           if (localResults.length > 100)
@@ -62209,13 +65169,16 @@ ${content}</tr>
             var tempEntity = osmEntity(attrs);
             var tempGraph = coreGraph([tempEntity]);
             var matched2 = _mainPresetIndex.match(tempEntity, tempGraph);
-            var type4 = matched2 && matched2.name() || utilDisplayType(id3);
+            var type3 = matched2 && matched2.name() || utilDisplayType(id3);
             result.push({
               id: tempEntity.id,
               geometry: tempEntity.geometry(tempGraph),
-              type: type4,
+              type: type3,
               name: d.display_name,
-              extent: new geoExtent([parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])], [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
+              extent: new geoExtent(
+                [Number(d.boundingbox[3]), Number(d.boundingbox[0])],
+                [Number(d.boundingbox[2]), Number(d.boundingbox[1])]
+              )
             });
           }
         });
@@ -62243,7 +65206,7 @@ ${content}</tr>
       }
       function drawList() {
         var value = search.property("value");
-        var results = features2();
+        var results = features();
         list.classed("filtered", value.length);
         var resultsIndicator = list.selectAll(".no-results-item").data([0]).enter().append("button").property("disabled", true).attr("class", "no-results-item").call(svgIcon("#iD-icon-alert", "pre-text"));
         resultsIndicator.append("span").attr("class", "entity-name");
@@ -62361,7 +65324,10 @@ ${content}</tr>
       return _t.html(`QA.improveOSM.error_types.${issueKey}.description`, d.replacements);
     }
     function improveOsmDetails(selection2) {
-      const details = selection2.selectAll(".error-details").data(_qaItem ? [_qaItem] : [], (d) => `${d.id}-${d.status || 0}`);
+      const details = selection2.selectAll(".error-details").data(
+        _qaItem ? [_qaItem] : [],
+        (d) => `${d.id}-${d.status || 0}`
+      );
       details.exit().remove();
       const detailsEnter = details.enter().append("div").attr("class", "error-details qa-details-container");
       const descriptionEnter = detailsEnter.append("div").attr("class", "qa-details-subsection");
@@ -62399,13 +65365,13 @@ ${content}</tr>
           }
         });
         if (entity) {
-          let name2 = utilDisplayName(entity);
-          if (!name2 && !isObjectLink) {
+          let name = utilDisplayName(entity);
+          if (!name && !isObjectLink) {
             const preset = _mainPresetIndex.match(entity, context.graph());
-            name2 = preset && !preset.isFallback() && preset.name();
+            name = preset && !preset.isFallback() && preset.name();
           }
-          if (name2) {
-            this.innerText = name2;
+          if (name) {
+            this.innerText = name;
           }
         }
       });
@@ -62431,7 +65397,10 @@ ${content}</tr>
       return _t.html(`QA.improveOSM.error_types.${issueKey}.title`, d.replacements);
     }
     function improveOsmHeader(selection2) {
-      const header = selection2.selectAll(".qa-header").data(_qaItem ? [_qaItem] : [], (d) => `${d.id}-${d.status || 0}`);
+      const header = selection2.selectAll(".qa-header").data(
+        _qaItem ? [_qaItem] : [],
+        (d) => `${d.id}-${d.status || 0}`
+      );
       header.exit().remove();
       const headerEnter = header.enter().append("div").attr("class", "qa-header");
       const svgEnter = headerEnter.append("div").attr("class", "qa-header-icon").classed("new", (d) => d.id < 0).append("svg").attr("width", "20px").attr("height", "30px").attr("viewbox", "0 0 20 30").attr("class", (d) => `preset-icon-28 qaItem ${d.service} itemId-${d.id} itemType-${d.itemType}`);
@@ -62467,7 +65436,10 @@ ${content}</tr>
     function improveOsmSaveSection(selection2) {
       const isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
       const isShown = _qaItem && (isSelected || _qaItem.newComment || _qaItem.comment);
-      let saveSection = selection2.selectAll(".qa-save").data(isShown ? [_qaItem] : [], (d) => `${d.id}-${d.status || 0}`);
+      let saveSection = selection2.selectAll(".qa-save").data(
+        isShown ? [_qaItem] : [],
+        (d) => `${d.id}-${d.status || 0}`
+      );
       saveSection.exit().remove();
       const saveSectionEnter = saveSection.enter().append("div").attr("class", "qa-save save-section cf");
       saveSectionEnter.append("h4").attr("class", ".qa-save-header").call(_t.append("note.newComment"));
@@ -62550,7 +65522,7 @@ ${content}</tr>
       var messagewrap = selection2.append("div").attr("class", "header fillL");
       var message = messagewrap.append("h2").call(_t.append("inspector.choose"));
       var direction = _mainLocalizer.textDirection() === "rtl" ? "backward" : "forward";
-      messagewrap.append("button").attr("class", "preset-choose").attr("title", direction).on("click", function() {
+      messagewrap.append("button").attr("class", "preset-choose").attr("title", _entityIDs.length === 1 ? _t("inspector.edit") : _t("inspector.edit_features")).on("click", function() {
         dispatch10.call("cancel", this);
       }).call(svgIcon(`#iD-icon-${direction}`));
       function initialKeydown(d3_event) {
@@ -62568,7 +65540,8 @@ ${content}</tr>
         }
       }
       function keydown(d3_event) {
-        if (d3_event.keyCode === utilKeybinding.keyCodes["\u2193"] && search.node().selectionStart === search.property("value").length) {
+        if (d3_event.keyCode === utilKeybinding.keyCodes["\u2193"] && // if insertion point is at the end of the string
+        search.node().selectionStart === search.property("value").length) {
           d3_event.preventDefault();
           d3_event.stopPropagation();
           var buttons = list.selectAll(".preset-list-button");
@@ -62578,7 +65551,8 @@ ${content}</tr>
       }
       function keypress(d3_event) {
         var value = search.property("value");
-        if (d3_event.keyCode === 13 && value.length) {
+        if (d3_event.keyCode === 13 && // ↩ Return
+        value.length) {
           list.selectAll(".preset-list-item:first-child").each(function(d) {
             d.choose.call(this);
           });
@@ -62595,7 +65569,8 @@ ${content}</tr>
             search: value
           });
         } else {
-          results = _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro(), _currLoc);
+          var entityPresets2 = _entityIDs.map((entityID) => _mainPresetIndex.match(context.graph().entity(entityID), context.graph()));
+          results = _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro(), _currLoc, entityPresets2);
           messageText = _t.html("inspector.choose");
         }
         list.call(drawList, results);
@@ -62611,7 +65586,8 @@ ${content}</tr>
         }, 0);
       }
       var listWrap = selection2.append("div").attr("class", "inspector-body");
-      var list = listWrap.append("div").attr("class", "preset-list").call(drawList, _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro(), _currLoc));
+      var entityPresets = _entityIDs.map((entityID) => _mainPresetIndex.match(context.graph().entity(entityID), context.graph()));
+      var list = listWrap.append("div").attr("class", "preset-list").call(drawList, _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro(), _currLoc, entityPresets));
       context.features().on("change.preset-list", updateForFeatureHiddenState);
     }
     function drawList(list, presets) {
@@ -62720,9 +65696,7 @@ ${content}</tr>
           }
         });
         var label = button.append("div").attr("class", "label").append("div").attr("class", "label-inner");
-        label.append("div").attr("class", "namepart").call(svgIcon(_mainLocalizer.textDirection() === "rtl" ? "#iD-icon-backward" : "#iD-icon-forward", "inline")).append("span").html(function() {
-          return preset.nameLabel() + "&hellip;";
-        });
+        label.append("div").attr("class", "namepart").call(svgIcon(_mainLocalizer.textDirection() === "rtl" ? "#iD-icon-backward" : "#iD-icon-forward", "inline")).append("span").call(preset.nameLabel()).append("span").text("\u2026");
         box = selection2.append("div").attr("class", "subgrid").style("max-height", "0px").style("opacity", 0);
         box.append("div").attr("class", "arrow");
         sublist = box.append("div").attr("class", "preset-list fillL3");
@@ -62753,8 +65727,8 @@ ${content}</tr>
           preset.nameLabel(),
           preset.subtitleLabel()
         ].filter(Boolean);
-        label.selectAll(".namepart").data(nameparts).enter().append("div").attr("class", "namepart").html(function(d) {
-          return d;
+        label.selectAll(".namepart").data(nameparts, (d) => d.stringId).enter().append("div").attr("class", "namepart").text("").each(function(d) {
+          d(select_default2(this));
         });
         wrap2.call(item.reference.button);
         selection2.call(item.reference.body);
@@ -62765,14 +65739,17 @@ ${content}</tr>
         if (!context.inIntro()) {
           _mainPresetIndex.setMostRecent(preset, entityGeometries()[0]);
         }
-        context.perform(function(graph) {
-          for (var i2 in _entityIDs) {
-            var entityID = _entityIDs[i2];
-            var oldPreset = _mainPresetIndex.match(graph.entity(entityID), graph);
-            graph = actionChangePreset(entityID, oldPreset, preset)(graph);
-          }
-          return graph;
-        }, _t("operations.change_tags.annotation"));
+        context.perform(
+          function(graph) {
+            for (var i2 in _entityIDs) {
+              var entityID = _entityIDs[i2];
+              var oldPreset = _mainPresetIndex.match(graph.entity(entityID), graph);
+              graph = actionChangePreset(entityID, oldPreset, preset)(graph);
+            }
+            return graph;
+          },
+          _t("operations.change_tags.annotation")
+        );
         context.validator().validate();
         dispatch10.call("choose", this, preset);
       };
@@ -62801,9 +65778,11 @@ ${content}</tr>
         select_default2(this).classed("disabled", isHiddenPreset);
         if (isHiddenPreset) {
           var isAutoHidden = context.features().autoHidden(hiddenPresetFeaturesId);
-          select_default2(this).call(uiTooltip().title(_t.html("inspector.hidden_preset." + (isAutoHidden ? "zoom" : "manual"), {
-            features: { html: _t.html("feature." + hiddenPresetFeaturesId + ".description") }
-          })).placement(index < 2 ? "bottom" : "top"));
+          select_default2(this).call(
+            uiTooltip().title(() => _t.append("inspector.hidden_preset." + (isAutoHidden ? "zoom" : "manual"), {
+              features: _t("feature." + hiddenPresetFeaturesId + ".description")
+            })).placement(index < 2 ? "bottom" : "top")
+          );
         }
       });
     }
@@ -62938,7 +65917,9 @@ ${content}</tr>
       }
       var footer = selection2.selectAll(".footer").data([0]);
       footer = footer.enter().append("div").attr("class", "footer").merge(footer);
-      footer.call(uiViewOnOSM(context).what(context.hasEntity(_entityIDs.length === 1 && _entityIDs[0])));
+      footer.call(
+        uiViewOnOSM(context).what(context.hasEntity(_entityIDs.length === 1 && _entityIDs[0]))
+      );
     }
     inspector.showList = function(presets) {
       presetPane.classed("hide", false);
@@ -63006,7 +65987,10 @@ ${content}</tr>
       }
     }
     function keepRightDetails(selection2) {
-      const details = selection2.selectAll(".error-details").data(_qaItem ? [_qaItem] : [], (d) => `${d.id}-${d.status || 0}`);
+      const details = selection2.selectAll(".error-details").data(
+        _qaItem ? [_qaItem] : [],
+        (d) => `${d.id}-${d.status || 0}`
+      );
       details.exit().remove();
       const detailsEnter = details.enter().append("div").attr("class", "error-details qa-details-container");
       const descriptionEnter = detailsEnter.append("div").attr("class", "qa-details-subsection");
@@ -63044,13 +66028,13 @@ ${content}</tr>
           }
         });
         if (entity) {
-          let name2 = utilDisplayName(entity);
-          if (!name2 && !isObjectLink) {
+          let name = utilDisplayName(entity);
+          if (!name && !isObjectLink) {
             const preset = _mainPresetIndex.match(entity, context.graph());
-            name2 = preset && !preset.isFallback() && preset.name();
+            name = preset && !preset.isFallback() && preset.name();
           }
-          if (name2) {
-            this.innerText = name2;
+          if (name) {
+            this.innerText = name;
           }
         }
       });
@@ -63081,7 +66065,10 @@ ${content}</tr>
       }
     }
     function keepRightHeader(selection2) {
-      const header = selection2.selectAll(".qa-header").data(_qaItem ? [_qaItem] : [], (d) => `${d.id}-${d.status || 0}`);
+      const header = selection2.selectAll(".qa-header").data(
+        _qaItem ? [_qaItem] : [],
+        (d) => `${d.id}-${d.status || 0}`
+      );
       header.exit().remove();
       const headerEnter = header.enter().append("div").attr("class", "qa-header");
       const iconEnter = headerEnter.append("div").attr("class", "qa-header-icon").classed("new", (d) => d.id < 0);
@@ -63139,7 +66126,10 @@ ${content}</tr>
     function keepRightSaveSection(selection2) {
       const isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
       const isShown = _qaItem && (isSelected || _qaItem.newComment || _qaItem.comment);
-      let saveSection = selection2.selectAll(".qa-save").data(isShown ? [_qaItem] : [], (d) => `${d.id}-${d.status || 0}`);
+      let saveSection = selection2.selectAll(".qa-save").data(
+        isShown ? [_qaItem] : [],
+        (d) => `${d.id}-${d.status || 0}`
+      );
       saveSection.exit().remove();
       const saveSectionEnter = saveSection.enter().append("div").attr("class", "qa-save save-section cf");
       saveSectionEnter.append("h4").attr("class", ".qa-save-header").call(_t.append("QA.keepRight.comment"));
@@ -63225,8 +66215,8 @@ ${content}</tr>
       }
     }
     lasso.extent = function() {
-      return lasso.coordinates.reduce(function(extent, point) {
-        return extent.extend(geoExtent(point));
+      return lasso.coordinates.reduce(function(extent, point2) {
+        return extent.extend(geoExtent(point2));
       }, geoExtent());
     };
     lasso.p = function(_) {
@@ -63322,9 +66312,12 @@ ${content}</tr>
   function uiNoteHeader() {
     var _note;
     function noteHeader(selection2) {
-      var header = selection2.selectAll(".note-header").data(_note ? [_note] : [], function(d) {
-        return d.status + d.id;
-      });
+      var header = selection2.selectAll(".note-header").data(
+        _note ? [_note] : [],
+        function(d) {
+          return d.status + d.id;
+        }
+      );
       header.exit().remove();
       var headerEnter = header.enter().append("div").attr("class", "note-header");
       var iconEnter = headerEnter.append("div").attr("class", function(d) {
@@ -63429,7 +66422,8 @@ ${content}</tr>
       }
       noteSave = noteSaveEnter.merge(noteSave).call(userDetails).call(noteSaveButtons);
       function keydown(d3_event) {
-        if (!(d3_event.keyCode === 13 && d3_event.metaKey))
+        if (!(d3_event.keyCode === 13 && // ↩ Return
+        d3_event.metaKey))
           return;
         var osm = services.osm;
         if (!osm)
@@ -63623,10 +66617,12 @@ ${content}</tr>
 
   // modules/ui/sections/privacy.js
   function uiSectionPrivacy(context) {
-    let section = uiSection("preferences-third-party", context).label(_t.html("preferences.privacy.title")).disclosureContent(renderDisclosureContent);
+    let section = uiSection("preferences-third-party", context).label(() => _t.append("preferences.privacy.title")).disclosureContent(renderDisclosureContent);
     function renderDisclosureContent(selection2) {
       selection2.selectAll(".privacy-options-list").data([0]).enter().append("ul").attr("class", "layer-list privacy-options-list");
-      let thirdPartyIconsEnter = selection2.select(".privacy-options-list").selectAll(".privacy-third-party-icons-item").data([corePreferences("preferences.privacy.thirdpartyicons") || "true"]).enter().append("li").attr("class", "privacy-third-party-icons-item").append("label").call(uiTooltip().title(_t.html("preferences.privacy.third_party_icons.tooltip")).placement("bottom"));
+      let thirdPartyIconsEnter = selection2.select(".privacy-options-list").selectAll(".privacy-third-party-icons-item").data([corePreferences("preferences.privacy.thirdpartyicons") || "true"]).enter().append("li").attr("class", "privacy-third-party-icons-item").append("label").call(
+        uiTooltip().title(() => _t.append("preferences.privacy.third_party_icons.tooltip")).placement("bottom")
+      );
       thirdPartyIconsEnter.append("input").attr("type", "checkbox").on("change", (d3_event, d) => {
         d3_event.preventDefault();
         corePreferences("preferences.privacy.thirdpartyicons", d === "true" ? "false" : "true");
@@ -63670,7 +66666,7 @@ ${content}</tr>
         updateMessage,
         privacyLink: { html: '<a target="_blank" href="https://github.com/openstreetmap/iD/blob/release/PRIVACY.md">' + _t("splash.privacy_policy") + "</a>" }
       }));
-      uiSectionPrivacy(context).label(_t.html("splash.privacy_settings")).render(modalSection);
+      uiSectionPrivacy(context).label(() => _t.append("splash.privacy_settings")).render(modalSection);
       let buttonWrap = introModal.append("div").attr("class", "modal-actions");
       let walkthrough = buttonWrap.append("button").attr("class", "walkthrough").on("click", () => {
         context.container().call(uiIntro(context));
@@ -63736,7 +66732,9 @@ ${content}</tr>
   function simplify2(str2) {
     if (typeof str2 !== "string")
       return "";
-    return import_diacritics3.default.remove(str2.replace(/&/g, "and").replace(/İ/ig, "i").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\u200b-\u200f\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\ufeff\uff01-\uff03\uff05-\uff07\uff0a\uff0c\uff0e\uff0f\uff1a\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65]+/g, "").toLowerCase());
+    return import_diacritics3.default.remove(
+      str2.replace(/&/g, "and").replace(/İ/ig, "i").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\u200b-\u200f\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\ufeff\uff01-\uff03\uff05-\uff07\uff0a\uff0c\uff0e\uff0f\uff1a\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65]+/g, "").toLowerCase()
+    );
   }
 
   // node_modules/osm-community-index/lib/resolve_strings.js
@@ -63835,11 +66833,11 @@ ${content}</tr>
         if (_oci)
           return _oci;
         if (vals[0] && Array.isArray(vals[0].features)) {
-          _mainLocations.mergeCustomGeoJSON(vals[0]);
+          _sharedLocationManager.mergeCustomGeoJSON(vals[0]);
         }
         let ociResources = Object.values(vals[1].resources);
         if (ociResources.length) {
-          return _mainLocations.mergeLocationSets(ociResources).then(() => {
+          return _sharedLocationManager.mergeLocationSets(ociResources).then(() => {
             _oci = {
               resources: ociResources,
               defaults: vals[2].defaults
@@ -63849,6 +66847,7 @@ ${content}</tr>
         } else {
           _oci = {
             resources: [],
+            // no resources?
             defaults: vals[2].defaults
           };
           return _oci;
@@ -63865,7 +66864,7 @@ ${content}</tr>
         raw += "Z";
       }
       const parsed = new Date(raw);
-      return new Date(parsed.toUTCString().substr(0, 25));
+      return new Date(parsed.toUTCString().slice(0, 25));
     }
     function success(selection2) {
       let header = selection2.append("div").attr("class", "header fillL");
@@ -63889,10 +66888,10 @@ ${content}</tr>
       }));
       ensureOSMCommunityIndex().then((oci) => {
         const loc = context.map().center();
-        const validLocations = _mainLocations.locationsAt(loc);
+        const validHere = _sharedLocationManager.locationSetsAt(loc);
         let communities = [];
         oci.resources.forEach((resource) => {
-          let area = validLocations[resource.locationSetID];
+          let area = validHere[resource.locationSetID];
           if (!area)
             return;
           const localizer = (stringID) => _t.html(`community.${stringID}`);
@@ -63924,7 +66923,9 @@ ${content}</tr>
       selection2.append("div").attr("class", "community-name").html(d.resolved.nameHTML);
       selection2.append("div").attr("class", "community-description").html(d.resolved.descriptionHTML);
       if (d.resolved.extendedDescriptionHTML || d.languageCodes && d.languageCodes.length) {
-        selection2.append("div").call(uiDisclosure(context, `community-more-${d.id}`, false).expanded(false).updatePreference(false).label(_t.html("success.more")).content(showMore));
+        selection2.append("div").call(
+          uiDisclosure(context, `community-more-${d.id}`, false).expanded(false).updatePreference(false).label(() => _t.append("success.more")).content(showMore)
+        );
       }
       let nextEvents = (d.events || []).map((event) => {
         event.date = parseEventDate(event.when);
@@ -63937,7 +66938,9 @@ ${content}</tr>
         return a.date < b.date ? -1 : a.date > b.date ? 1 : 0;
       }).slice(0, MAXEVENTS);
       if (nextEvents.length) {
-        selection2.append("div").call(uiDisclosure(context, `community-events-${d.id}`, false).expanded(false).updatePreference(false).label(_t.html("success.events")).content(showNextEvents)).select(".hide-toggle").append("span").attr("class", "badge-text").text(nextEvents.length);
+        selection2.append("div").call(
+          uiDisclosure(context, `community-events-${d.id}`, false).expanded(false).updatePreference(false).label(_t.html("success.events")).content(showNextEvents)
+        ).select(".hide-toggle").append("span").attr("class", "badge-text").text(nextEvents.length);
       }
       function showMore(selection3) {
         let more = selection3.selectAll(".community-more").data([0]);
@@ -63955,11 +66958,11 @@ ${content}</tr>
         let item = events.selectAll(".community-event").data(nextEvents);
         let itemEnter = item.enter().append("div").attr("class", "community-event");
         itemEnter.append("div").attr("class", "community-event-name").append("a").attr("target", "_blank").attr("href", (d2) => d2.url).text((d2) => {
-          let name2 = d2.name;
+          let name = d2.name;
           if (d2.i18n && d2.id) {
-            name2 = _t(`community.${communityID}.events.${d2.id}.name`, { default: name2 });
+            name = _t(`community.${communityID}.events.${d2.id}.name`, { default: name });
           }
-          return name2;
+          return name;
         });
         itemEnter.append("div").attr("class", "community-event-when").text((d2) => {
           let options2 = { weekday: "short", day: "numeric", month: "short", year: "numeric" };
@@ -63977,11 +66980,11 @@ ${content}</tr>
           return where;
         });
         itemEnter.append("div").attr("class", "community-event-description").text((d2) => {
-          let description2 = d2.description;
+          let description = d2.description;
           if (d2.i18n && d2.id) {
-            description2 = _t(`community.${communityID}.events.${d2.id}.description`, { default: description2 });
+            description = _t(`community.${communityID}.events.${d2.id}.description`, { default: description });
           }
-          return description2;
+          return description;
         });
       }
     }
@@ -64021,7 +67024,9 @@ ${content}</tr>
     return function(selection2) {
       selection2.append("a").attr("target", "_blank").attr("href", "https://github.com/openstreetmap/iD").text(currVersion);
       if (isNewVersion && !isNewUser) {
-        selection2.append("a").attr("class", "badge").attr("target", "_blank").attr("href", "https://github.com/openstreetmap/iD/blob/release/CHANGELOG.md#whats-new").call(svgIcon("#maki-gift-11")).call(uiTooltip().title(_t.html("version.whats_new", { version: currVersion })).placement("top").scrollContainer(context.container().select(".main-footer-wrap")));
+        selection2.append("a").attr("class", "badge").attr("target", "_blank").attr("href", "https://github.com/openstreetmap/iD/blob/release/CHANGELOG.md#whats-new").call(svgIcon("#maki-gift")).call(
+          uiTooltip().title(() => _t.append("version.whats_new", { version: currVersion })).placement("top").scrollContainer(context.container().select(".main-footer-wrap"))
+        );
       }
     };
   }
@@ -64031,22 +67036,22 @@ ${content}</tr>
     var zooms = [{
       id: "zoom-in",
       icon: "iD-icon-plus",
-      title: _t.html("zoom.in"),
+      title: _t.append("zoom.in"),
       action: zoomIn,
       disabled: function() {
         return !context.map().canZoomIn();
       },
-      disabledTitle: _t.html("zoom.disabled.in"),
+      disabledTitle: _t.append("zoom.disabled.in"),
       key: "+"
     }, {
       id: "zoom-out",
       icon: "iD-icon-minus",
-      title: _t.html("zoom.out"),
+      title: _t.append("zoom.out"),
       action: zoomOut,
       disabled: function() {
         return !context.map().canZoomOut();
       },
-      disabledTitle: _t.html("zoom.disabled.out"),
+      disabledTitle: _t.append("zoom.disabled.out"),
       key: "-"
     }];
     function zoomIn(d3_event) {
@@ -64127,7 +67132,7 @@ ${content}</tr>
       var count = Object.keys(_tags).filter(function(d) {
         return d;
       }).length;
-      return _t.html("inspector.title_count", { title: { html: _t.html("inspector.tags") }, count });
+      return _t.append("inspector.title_count", { title: _t("inspector.tags"), count });
     }).expandedByDefault(false).disclosureContent(renderDisclosureContent);
     var taginfo = services.taginfo;
     var dispatch10 = dispatch_default("change");
@@ -64196,7 +67201,7 @@ ${content}</tr>
       var list = wrap2.selectAll(".tag-list").data([0]);
       list = list.enter().append("ul").attr("class", "tag-list" + (_tagView !== "list" ? " hide" : "")).merge(list);
       var addRowEnter = wrap2.selectAll(".add-row").data([0]).enter().append("div").attr("class", "add-row" + (_tagView !== "list" ? " hide" : ""));
-      addRowEnter.append("button").attr("class", "add-tag").attr("aria-label", _t("inspector.add_to_tag")).call(svgIcon("#iD-icon-plus", "light")).call(uiTooltip().title(_t.html("inspector.add_to_tag")).placement(_mainLocalizer.textDirection() === "ltr" ? "right" : "left")).on("click", addTag);
+      addRowEnter.append("button").attr("class", "add-tag").attr("aria-label", _t("inspector.add_to_tag")).call(svgIcon("#iD-icon-plus", "light")).call(uiTooltip().title(() => _t.append("inspector.add_to_tag")).placement(_mainLocalizer.textDirection() === "ltr" ? "right" : "left")).on("click", addTag);
       addRowEnter.append("div").attr("class", "space-value");
       addRowEnter.append("div").attr("class", "space-buttons");
       var items = list.selectAll(".tag-row").data(rowData, function(d) {
@@ -64540,7 +67545,9 @@ ${content}</tr>
       var editor = body.selectAll(".data-editor").data([0]);
       editor.enter().append("div").attr("class", "modal-section data-editor").merge(editor).call(dataHeader.datum(_datum));
       var rte = body.selectAll(".raw-tag-editor").data([0]);
-      rte.enter().append("div").attr("class", "raw-tag-editor data-editor").merge(rte).call(rawTagEditor.tags(_datum && _datum.properties || {}).state("hover").render).selectAll("textarea.tag-text").attr("readonly", true).classed("readonly", true);
+      rte.enter().append("div").attr("class", "raw-tag-editor data-editor").merge(rte).call(
+        rawTagEditor.tags(_datum && _datum.properties || {}).state("hover").render
+      ).selectAll("textarea.tag-text").attr("readonly", true).classed("readonly", true);
     }
     dataEditor.datum = function(val) {
       if (!arguments.length)
@@ -64554,14 +67561,17 @@ ${content}</tr>
   // modules/ui/osmose_details.js
   function uiOsmoseDetails(context) {
     let _qaItem;
-    function issueString(d, type3) {
+    function issueString(d, type2) {
       if (!d)
         return "";
       const s = services.osmose.getStrings(d.itemType);
-      return type3 in s ? s[type3] : "";
+      return type2 in s ? s[type2] : "";
     }
     function osmoseDetails(selection2) {
-      const details = selection2.selectAll(".error-details").data(_qaItem ? [_qaItem] : [], (d) => `${d.id}-${d.status || 0}`);
+      const details = selection2.selectAll(".error-details").data(
+        _qaItem ? [_qaItem] : [],
+        (d) => `${d.id}-${d.status || 0}`
+      );
       details.exit().remove();
       const detailsEnter = details.enter().append("div").attr("class", "error-details qa-details-container");
       if (issueString(_qaItem, "detail")) {
@@ -64621,13 +67631,13 @@ ${content}</tr>
             }
           });
           if (entity) {
-            let name2 = utilDisplayName(entity);
-            if (!name2) {
+            let name = utilDisplayName(entity);
+            if (!name) {
               const preset = _mainPresetIndex.match(entity, context.graph());
-              name2 = preset && !preset.isFallback() && preset.name();
+              name = preset && !preset.isFallback() && preset.name();
             }
-            if (name2) {
-              this.innerText = name2;
+            if (name) {
+              this.innerText = name;
             }
           }
         });
@@ -64657,7 +67667,10 @@ ${content}</tr>
       return "title" in s ? s.title : unknown;
     }
     function osmoseHeader(selection2) {
-      const header = selection2.selectAll(".qa-header").data(_qaItem ? [_qaItem] : [], (d) => `${d.id}-${d.status || 0}`);
+      const header = selection2.selectAll(".qa-header").data(
+        _qaItem ? [_qaItem] : [],
+        (d) => `${d.id}-${d.status || 0}`
+      );
       header.exit().remove();
       const headerEnter = header.enter().append("div").attr("class", "qa-header");
       const svgEnter = headerEnter.append("div").attr("class", "qa-header-icon").classed("new", (d) => d.id < 0).append("svg").attr("width", "20px").attr("height", "30px").attr("viewbox", "0 0 20 30").attr("class", (d) => `preset-icon-28 qaItem ${d.service} itemId-${d.id} itemType-${d.itemType}`);
@@ -64717,7 +67730,10 @@ ${content}</tr>
     function osmoseSaveSection(selection2) {
       const isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
       const isShown = _qaItem && isSelected;
-      let saveSection = selection2.selectAll(".qa-save").data(isShown ? [_qaItem] : [], (d) => `${d.id}-${d.status || 0}`);
+      let saveSection = selection2.selectAll(".qa-save").data(
+        isShown ? [_qaItem] : [],
+        (d) => `${d.id}-${d.status || 0}`
+      );
       saveSection.exit().remove();
       const saveSectionEnter = saveSection.enter().append("div").attr("class", "qa-save save-section cf");
       saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
@@ -65034,7 +68050,7 @@ ${content}</tr>
       id: "draw-area"
     };
     var behavior = behaviorDrawWay(context, wayID, mode, startGraph).on("rejectedSelfIntersection.modeDrawArea", function() {
-      context.ui().flash.iconName("#iD-icon-no").label(_t.html("self_intersection.error.areas"))();
+      context.ui().flash.iconName("#iD-icon-no").label(_t.append("self_intersection.error.areas"))();
     });
     mode.wayID = wayID;
     mode.enter = function() {
@@ -65068,20 +68084,35 @@ ${content}</tr>
       var startGraph = context.graph();
       var node = osmNode({ loc });
       var way = osmWay({ tags: defaultTags });
-      context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id));
+      context.perform(
+        actionAddEntity(node),
+        actionAddEntity(way),
+        actionAddVertex(way.id, node.id),
+        actionClose(way.id)
+      );
       context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
     }
     function startFromWay(loc, edge) {
       var startGraph = context.graph();
       var node = osmNode({ loc });
       var way = osmWay({ tags: defaultTags });
-      context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id), actionAddMidpoint({ loc, edge }, node));
+      context.perform(
+        actionAddEntity(node),
+        actionAddEntity(way),
+        actionAddVertex(way.id, node.id),
+        actionClose(way.id),
+        actionAddMidpoint({ loc, edge }, node)
+      );
       context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
     }
     function startFromNode(node) {
       var startGraph = context.graph();
       var way = osmWay({ tags: defaultTags });
-      context.perform(actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id));
+      context.perform(
+        actionAddEntity(way),
+        actionAddVertex(way.id, node.id),
+        actionClose(way.id)
+      );
       context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
     }
     mode.enter = function() {
@@ -65104,20 +68135,32 @@ ${content}</tr>
       var startGraph = context.graph();
       var node = osmNode({ loc });
       var way = osmWay({ tags: defaultTags });
-      context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id));
+      context.perform(
+        actionAddEntity(node),
+        actionAddEntity(way),
+        actionAddVertex(way.id, node.id)
+      );
       context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
     }
     function startFromWay(loc, edge) {
       var startGraph = context.graph();
       var node = osmNode({ loc });
       var way = osmWay({ tags: defaultTags });
-      context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionAddMidpoint({ loc, edge }, node));
+      context.perform(
+        actionAddEntity(node),
+        actionAddEntity(way),
+        actionAddVertex(way.id, node.id),
+        actionAddMidpoint({ loc, edge }, node)
+      );
       context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
     }
     function startFromNode(node) {
       var startGraph = context.graph();
       var way = osmWay({ tags: defaultTags });
-      context.perform(actionAddEntity(way), actionAddVertex(way.id, node.id));
+      context.perform(
+        actionAddEntity(way),
+        actionAddVertex(way.id, node.id)
+      );
       context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
     }
     mode.enter = function() {
@@ -65138,16 +68181,24 @@ ${content}</tr>
       defaultTags = mode.preset.setTags(defaultTags, "point");
     function add(loc) {
       var node = osmNode({ loc, tags: defaultTags });
-      context.perform(actionAddEntity(node), _t("operations.add.annotation.point"));
+      context.perform(
+        actionAddEntity(node),
+        _t("operations.add.annotation.point")
+      );
       enterSelectMode(node);
     }
     function addWay(loc, edge) {
       var node = osmNode({ tags: defaultTags });
-      context.perform(actionAddMidpoint({ loc, edge }, node), _t("operations.add.annotation.vertex"));
+      context.perform(
+        actionAddMidpoint({ loc, edge }, node),
+        _t("operations.add.annotation.vertex")
+      );
       enterSelectMode(node);
     }
     function enterSelectMode(node) {
-      context.enter(modeSelect(context, [node.id]).newFeature(true));
+      context.enter(
+        modeSelect(context, [node.id]).newFeature(true)
+      );
     }
     function addNode(node) {
       if (Object.keys(defaultTags).length === 0) {
@@ -65158,7 +68209,10 @@ ${content}</tr>
       for (var key in defaultTags) {
         tags[key] = defaultTags[key];
       }
-      context.perform(actionChangeTags(node.id, tags), _t("operations.add.annotation.point"));
+      context.perform(
+        actionChangeTags(node.id, tags),
+        _t("operations.add.annotation.point")
+      );
       enterSelectMode(node);
     }
     function cancel() {
@@ -65267,7 +68321,7 @@ ${content}</tr>
     var mode = {
       id: "add-note",
       button: "note",
-      description: _t.html("modes.add_note.description"),
+      description: _t.append("modes.add_note.description"),
       key: _t("modes.add_note.key")
     };
     var behavior = behaviorDraw(context).on("click", add).on("cancel", cancel).on("finish", cancel);
@@ -65382,7 +68436,10 @@ ${content}</tr>
         var place = addr && (addr.town || addr.city || addr.county) || "";
         var region = addr && (addr.state || addr.country) || "";
         var separator = place && region ? _t("success.thank_you_where.separator") : "";
-        _location = _t("success.thank_you_where.format", { place, separator, region });
+        _location = _t(
+          "success.thank_you_where.format",
+          { place, separator, region }
+        );
       });
     }
     mode.selectedIDs = function() {
@@ -65530,27 +68587,27 @@ ${content}</tr>
   function uiToolDrawModes(context) {
     var tool = {
       id: "old_modes",
-      label: _t.html("toolbar.add_feature")
+      label: _t.append("toolbar.add_feature")
     };
     var modes = [
       modeAddPoint(context, {
-        title: _t.html("modes.add_point.title"),
+        title: _t.append("modes.add_point.title"),
         button: "point",
-        description: _t.html("modes.add_point.description"),
+        description: _t.append("modes.add_point.description"),
         preset: _mainPresetIndex.item("point"),
         key: "1"
       }),
       modeAddLine(context, {
-        title: _t.html("modes.add_line.title"),
+        title: _t.append("modes.add_line.title"),
         button: "line",
-        description: _t.html("modes.add_line.description"),
+        description: _t.append("modes.add_line.description"),
         preset: _mainPresetIndex.item("line"),
         key: "2"
       }),
       modeAddArea(context, {
-        title: _t.html("modes.add_area.title"),
+        title: _t.append("modes.add_area.title"),
         button: "area",
-        description: _t.html("modes.add_area.description"),
+        description: _t.append("modes.add_area.description"),
         preset: _mainPresetIndex.item("area"),
         key: "3"
       })
@@ -65596,16 +68653,18 @@ ${content}</tr>
           } else {
             context.enter(d);
           }
-        }).call(uiTooltip().placement("bottom").title(function(d) {
-          return d.description;
-        }).keys(function(d) {
-          return [d.key];
-        }).scrollContainer(context.container().select(".top-toolbar")));
+        }).call(
+          uiTooltip().placement("bottom").title(function(d) {
+            return d.description;
+          }).keys(function(d) {
+            return [d.key];
+          }).scrollContainer(context.container().select(".top-toolbar"))
+        );
         buttonsEnter.each(function(d) {
           select_default2(this).call(svgIcon("#iD-icon-" + d.button));
         });
-        buttonsEnter.append("span").attr("class", "label").html(function(mode) {
-          return mode.title;
+        buttonsEnter.append("span").attr("class", "label").text("").each(function(mode) {
+          mode.title(select_default2(this));
         });
         if (buttons.enter().size() || buttons.exit().size()) {
           context.ui().checkOverflow(".top-toolbar", true);
@@ -65628,7 +68687,7 @@ ${content}</tr>
   function uiToolNotes(context) {
     var tool = {
       id: "notes",
-      label: _t.html("modes.add_note.label")
+      label: _t.append("modes.add_note.label")
     };
     var mode = modeAddNote(context);
     function enabled() {
@@ -65676,11 +68735,13 @@ ${content}</tr>
           } else {
             context.enter(d);
           }
-        }).call(uiTooltip().placement("bottom").title(function(d) {
-          return d.description;
-        }).keys(function(d) {
-          return [d.key];
-        }).scrollContainer(context.container().select(".top-toolbar")));
+        }).call(
+          uiTooltip().placement("bottom").title(function(d) {
+            return d.description;
+          }).keys(function(d) {
+            return [d.key];
+          }).scrollContainer(context.container().select(".top-toolbar"))
+        );
         buttonsEnter.each(function(d) {
           select_default2(this).call(svgIcon(d.icon || "#iD-icon-" + d.button));
         });
@@ -65709,7 +68770,7 @@ ${content}</tr>
   function uiToolSave(context) {
     var tool = {
       id: "save",
-      label: _t.html("save.title")
+      label: _t.append("save.title")
     };
     var button = null;
     var tooltipBehavior = null;
@@ -65747,7 +68808,7 @@ ${content}</tr>
         return;
       _numChanges = val;
       if (tooltipBehavior) {
-        tooltipBehavior.title(_t.html(_numChanges > 0 ? "save.help" : "save.no_changes")).keys([key]);
+        tooltipBehavior.title(() => _t.append(_numChanges > 0 ? "save.help" : "save.no_changes")).keys([key]);
       }
       if (button) {
         button.classed("disabled", isDisabled()).style("background", bgColor(_numChanges));
@@ -65755,14 +68816,14 @@ ${content}</tr>
       }
     }
     tool.render = function(selection2) {
-      tooltipBehavior = uiTooltip().placement("bottom").title(_t.html("save.no_changes")).keys([key]).scrollContainer(context.container().select(".top-toolbar"));
+      tooltipBehavior = uiTooltip().placement("bottom").title(() => _t.append("save.no_changes")).keys([key]).scrollContainer(context.container().select(".top-toolbar"));
       var lastPointerUpType;
       button = selection2.append("button").attr("class", "save disabled bar-button").on("pointerup", function(d3_event) {
         lastPointerUpType = d3_event.pointerType;
       }).on("click", function(d3_event) {
         save(d3_event);
         if (_numChanges === 0 && (lastPointerUpType === "touch" || lastPointerUpType === "pen")) {
-          context.ui().flash.duration(2e3).iconName("#iD-icon-save").iconClass("disabled").label(_t.html("save.no_changes"))();
+          context.ui().flash.duration(2e3).iconName("#iD-icon-save").iconClass("disabled").label(_t.append("save.no_changes"))();
         }
         lastPointerUpType = null;
       }).call(tooltipBehavior);
@@ -65794,12 +68855,14 @@ ${content}</tr>
   function uiToolSidebarToggle(context) {
     var tool = {
       id: "sidebar_toggle",
-      label: _t.html("toolbar.inspect")
+      label: _t.append("toolbar.inspect")
     };
     tool.render = function(selection2) {
       selection2.append("button").attr("class", "bar-button").attr("aria-label", _t("sidebar.tooltip")).on("click", function() {
         context.ui().sidebar.toggle();
-      }).call(uiTooltip().placement("bottom").title(_t.html("sidebar.tooltip")).keys([_t("sidebar.key")]).scrollContainer(context.container().select(".top-toolbar"))).call(svgIcon("#iD-icon-sidebar-" + (_mainLocalizer.textDirection() === "rtl" ? "right" : "left")));
+      }).call(
+        uiTooltip().placement("bottom").title(() => _t.append("sidebar.tooltip")).keys([_t("sidebar.key")]).scrollContainer(context.container().select(".top-toolbar"))
+      ).call(svgIcon("#iD-icon-sidebar-" + (_mainLocalizer.textDirection() === "rtl" ? "right" : "left")));
     };
     return tool;
   }
@@ -65808,7 +68871,7 @@ ${content}</tr>
   function uiToolUndoRedo(context) {
     var tool = {
       id: "undo_redo",
-      label: _t.html("toolbar.undo_redo")
+      label: _t.append("toolbar.undo_redo")
     };
     var commands = [{
       id: "undo",
@@ -65832,11 +68895,14 @@ ${content}</tr>
       icon: "iD-icon-" + (_mainLocalizer.textDirection() === "rtl" ? "undo" : "redo")
     }];
     function editable() {
-      return context.mode() && context.mode().id !== "save" && context.map().editableDataEnabled(true);
+      return context.mode() && context.mode().id !== "save" && context.map().editableDataEnabled(
+        true
+        /* ignore min zoom */
+      );
     }
     tool.render = function(selection2) {
       var tooltipBehavior = uiTooltip().placement("bottom").title(function(d) {
-        return d.annotation() ? _t.html(d.id + ".tooltip", { action: d.annotation() }) : _t.html(d.id + ".nothing");
+        return d.annotation() ? _t.append(d.id + ".tooltip", { action: d.annotation() }) : _t.append(d.id + ".nothing");
       }).keys(function(d) {
         return [d.cmd];
       }).scrollContainer(context.container().select(".top-toolbar"));
@@ -65852,8 +68918,8 @@ ${content}</tr>
           d.action();
         }
         if (editable() && (lastPointerUpType === "touch" || lastPointerUpType === "pen")) {
-          var text2 = annotation ? _t.html(d.id + ".tooltip", { action: annotation }) : _t.html(d.id + ".nothing");
-          context.ui().flash.duration(2e3).iconName("#" + d.icon).iconClass(annotation ? "" : "disabled").label(text2)();
+          var label = annotation ? _t.append(d.id + ".tooltip", { action: annotation }) : _t.append(d.id + ".nothing");
+          context.ui().flash.duration(2e3).iconName("#" + d.icon).iconClass(annotation ? "" : "disabled").label(label)();
         }
         lastPointerUpType = null;
       }).call(tooltipBehavior);
@@ -65943,8 +69009,8 @@ ${content}</tr>
         actionableItems.append("div").attr("class", "item-content").each(function(d) {
           select_default2(this).call(d.render, bar);
         });
-        actionableItems.append("div").attr("class", "item-label").html(function(d) {
-          return d.label;
+        actionableItems.append("div").attr("class", "item-label").each(function(d) {
+          d.label(select_default2(this));
         });
       }
     }
@@ -65965,7 +69031,7 @@ ${content}</tr>
       d3_event.preventDefault();
       if (isDisabled()) {
         if (_lastPointerUpType === "touch" || _lastPointerUpType === "pen") {
-          context.ui().flash.duration(2e3).iconName("#iD-icon-framed-dot").iconClass("disabled").label(_t.html("inspector.zoom_to.no_selection"))();
+          context.ui().flash.duration(2e3).iconName("#iD-icon-framed-dot").iconClass("disabled").label(_t.append("inspector.zoom_to.no_selection"))();
         }
       } else {
         var mode = context.mode();
@@ -65978,9 +69044,9 @@ ${content}</tr>
     return function(selection2) {
       var tooltipBehavior = uiTooltip().placement(_mainLocalizer.textDirection() === "rtl" ? "right" : "left").title(function() {
         if (isDisabled()) {
-          return _t.html("inspector.zoom_to.no_selection");
+          return _t.append("inspector.zoom_to.no_selection");
         }
-        return _t.html("inspector.zoom_to.title");
+        return _t.append("inspector.zoom_to.title");
       }).keys([_t("inspector.zoom_to.key")]);
       var button = selection2.append("button").on("pointerup", pointerup).on("click", click).call(svgIcon("#iD-icon-framed-dot", "light")).call(tooltipBehavior);
       function setEnabledState() {
@@ -66050,7 +69116,7 @@ ${content}</tr>
     };
     pane.renderToggleButton = function(selection2) {
       if (!_paneTooltip) {
-        _paneTooltip = uiTooltip().placement(_mainLocalizer.textDirection() === "rtl" ? "right" : "left").title(_description).keys([_key]);
+        _paneTooltip = uiTooltip().placement(_mainLocalizer.textDirection() === "rtl" ? "right" : "left").title(() => _description).keys([_key]);
       }
       selection2.append("button").on("click", pane.togglePane).call(svgIcon("#" + _iconName, "light")).call(_paneTooltip);
     };
@@ -66064,7 +69130,7 @@ ${content}</tr>
     pane.renderPane = function(selection2) {
       _paneSelection = selection2.append("div").attr("class", "fillL map-pane hide " + id2 + "-pane").attr("pane", id2);
       var heading = _paneSelection.append("div").attr("class", "pane-heading");
-      heading.append("h2").html(_label);
+      heading.append("h2").text("").call(_label);
       heading.append("button").attr("title", _t("icons.close")).on("click", hidePane).call(svgIcon("#iD-icon-close"));
       _paneSelection.append("div").attr("class", "pane-content").call(pane.renderContent);
       if (_key) {
@@ -66076,7 +69142,7 @@ ${content}</tr>
 
   // modules/ui/sections/background_display_options.js
   function uiSectionBackgroundDisplayOptions(context) {
-    var section = uiSection("background-display-options", context).label(_t.html("background.display_options")).disclosureContent(renderDisclosureContent);
+    var section = uiSection("background-display-options", context).label(() => _t.append("background.display_options")).disclosureContent(renderDisclosureContent);
     var _storedOpacity = corePreferences("background-opacity");
     var _minVal = 0;
     var _maxVal = 3;
@@ -66215,7 +69281,7 @@ ${content}</tr>
     var _backgroundList = select_default2(null);
     var _customSource = context.background().findSource("custom");
     var _settingsCustomBackground = uiSettingsCustomBackground(context).on("change", customChanged);
-    var section = uiSection("background-list", context).label(_t.html("background.backgrounds")).disclosureContent(renderDisclosureContent);
+    var section = uiSection("background-list", context).label(() => _t.append("background.backgrounds")).disclosureContent(renderDisclosureContent);
     function previousBackgroundID() {
       return corePreferences("background-last-used-toggle");
     }
@@ -66223,19 +69289,25 @@ ${content}</tr>
       var container = selection2.selectAll(".layer-background-list").data([0]);
       _backgroundList = container.enter().append("ul").attr("class", "layer-list layer-background-list").attr("dir", "auto").merge(container);
       var bgExtrasListEnter = selection2.selectAll(".bg-extras-list").data([0]).enter().append("ul").attr("class", "layer-list bg-extras-list");
-      var minimapLabelEnter = bgExtrasListEnter.append("li").attr("class", "minimap-toggle-item").append("label").call(uiTooltip().title(_t.html("background.minimap.tooltip")).keys([_t("background.minimap.key")]).placement("top"));
+      var minimapLabelEnter = bgExtrasListEnter.append("li").attr("class", "minimap-toggle-item").append("label").call(
+        uiTooltip().title(() => _t.append("background.minimap.tooltip")).keys([_t("background.minimap.key")]).placement("top")
+      );
       minimapLabelEnter.append("input").attr("type", "checkbox").on("change", function(d3_event) {
         d3_event.preventDefault();
         uiMapInMap.toggle();
       });
       minimapLabelEnter.append("span").call(_t.append("background.minimap.description"));
-      var panelLabelEnter = bgExtrasListEnter.append("li").attr("class", "background-panel-toggle-item").append("label").call(uiTooltip().title(_t.html("background.panel.tooltip")).keys([uiCmd("\u2318\u21E7" + _t("info_panels.background.key"))]).placement("top"));
+      var panelLabelEnter = bgExtrasListEnter.append("li").attr("class", "background-panel-toggle-item").append("label").call(
+        uiTooltip().title(() => _t.append("background.panel.tooltip")).keys([uiCmd("\u2318\u21E7" + _t("info_panels.background.key"))]).placement("top")
+      );
       panelLabelEnter.append("input").attr("type", "checkbox").on("change", function(d3_event) {
         d3_event.preventDefault();
         context.ui().info.toggle("background");
       });
       panelLabelEnter.append("span").call(_t.append("background.panel.description"));
-      var locPanelLabelEnter = bgExtrasListEnter.append("li").attr("class", "location-panel-toggle-item").append("label").call(uiTooltip().title(_t.html("background.location_panel.tooltip")).keys([uiCmd("\u2318\u21E7" + _t("info_panels.location.key"))]).placement("top"));
+      var locPanelLabelEnter = bgExtrasListEnter.append("li").attr("class", "location-panel-toggle-item").append("label").call(
+        uiTooltip().title(() => _t.append("background.location_panel.tooltip")).keys([uiCmd("\u2318\u21E7" + _t("info_panels.location.key"))]).placement("top")
+      );
       locPanelLabelEnter.append("input").attr("type", "checkbox").on("change", function(d3_event) {
         d3_event.preventDefault();
         context.ui().info.toggle("location");
@@ -66253,17 +69325,21 @@ ${content}</tr>
         var item = select_default2(this).select("label");
         var span = item.select("span");
         var placement = i2 < nodes.length / 2 ? "bottom" : "top";
-        var description2 = d.description();
+        var hasDescription = d.hasDescription();
         var isOverflowing = span.property("clientWidth") !== span.property("scrollWidth");
         item.call(uiTooltip().destroyAny);
         if (d.id === previousBackgroundID()) {
-          item.call(uiTooltip().placement(placement).title("<div>" + _t.html("background.switch") + "</div>").keys([uiCmd("\u2318" + _t("background.key"))]));
-        } else if (description2 || isOverflowing) {
-          item.call(uiTooltip().placement(placement).title(description2 || d.label()));
+          item.call(
+            uiTooltip().placement(placement).title(() => _t.append("background.switch")).keys([uiCmd("\u2318" + _t("background.key"))])
+          );
+        } else if (hasDescription || isOverflowing) {
+          item.call(
+            uiTooltip().placement(placement).title(() => hasDescription ? d.description() : d.label())
+          );
         }
       });
     }
-    function drawListItems(layerList, type3, change, filter2) {
+    function drawListItems(layerList, type2, change, filter2) {
       var sources = context.background().sources(context.map().extent(), context.map().zoom(), true).filter(filter2).sort(function(a, b) {
         return a.best() && !b.best() ? -1 : b.best() && !a.best() ? 1 : descending(a.area(), b.area()) || ascending(a.name(), b.name()) || 0;
       });
@@ -66277,21 +69353,25 @@ ${content}</tr>
         return d.best();
       });
       var label = enter.append("label");
-      label.append("input").attr("type", type3).attr("name", "background-layer").attr("value", function(d) {
+      label.append("input").attr("type", type2).attr("name", "background-layer").attr("value", function(d) {
         return d.id;
       }).on("change", change);
-      label.append("span").html(function(d) {
-        return d.label();
+      label.append("span").each(function(d) {
+        d.label()(select_default2(this));
       });
       enter.filter(function(d) {
         return d.id === "custom";
-      }).append("button").attr("class", "layer-browse").call(uiTooltip().title(_t.html("settings.custom_background.tooltip")).placement(_mainLocalizer.textDirection() === "rtl" ? "right" : "left")).on("click", function(d3_event) {
+      }).append("button").attr("class", "layer-browse").call(
+        uiTooltip().title(() => _t.append("settings.custom_background.tooltip")).placement(_mainLocalizer.textDirection() === "rtl" ? "right" : "left")
+      ).on("click", function(d3_event) {
         d3_event.preventDefault();
         editCustom();
       }).call(svgIcon("#iD-icon-more"));
       enter.filter(function(d) {
         return d.best();
-      }).append("div").attr("class", "best").call(uiTooltip().title(_t.html("background.best_imagery")).placement(_mainLocalizer.textDirection() === "rtl" ? "right" : "left")).append("span").html("&#9733;");
+      }).append("div").attr("class", "best").call(
+        uiTooltip().title(() => _t.append("background.best_imagery")).placement(_mainLocalizer.textDirection() === "rtl" ? "right" : "left")
+      ).append("span").text("\u2605");
       layerList.call(updateLayerSelections);
     }
     function updateLayerSelections(selection2) {
@@ -66326,15 +69406,18 @@ ${content}</tr>
     context.background().on("change.background_list", function() {
       _backgroundList.call(updateLayerSelections);
     });
-    context.map().on("move.background_list", debounce_default(function() {
-      window.requestIdleCallback(section.reRender);
-    }, 1e3));
+    context.map().on(
+      "move.background_list",
+      debounce_default(function() {
+        window.requestIdleCallback(section.reRender);
+      }, 1e3)
+    );
     return section;
   }
 
   // modules/ui/sections/background_offset.js
   function uiSectionBackgroundOffset(context) {
-    var section = uiSection("background-offset", context).label(_t.html("background.fix_misalignment")).disclosureContent(renderDisclosureContent).expandedByDefault(false);
+    var section = uiSection("background-offset", context).label(() => _t.append("background.fix_misalignment")).disclosureContent(renderDisclosureContent).expandedByDefault(false);
     var _pointerPrefix = "PointerEvent" in window ? "pointer" : "mouse";
     var _directions = [
       ["top", [0, -0.5]],
@@ -66430,18 +69513,20 @@ ${content}</tr>
 
   // modules/ui/sections/overlay_list.js
   function uiSectionOverlayList(context) {
-    var section = uiSection("overlay-list", context).label(_t.html("background.overlays")).disclosureContent(renderDisclosureContent);
+    var section = uiSection("overlay-list", context).label(() => _t.append("background.overlays")).disclosureContent(renderDisclosureContent);
     var _overlayList = select_default2(null);
     function setTooltips(selection2) {
       selection2.each(function(d, i2, nodes) {
         var item = select_default2(this).select("label");
         var span = item.select("span");
         var placement = i2 < nodes.length / 2 ? "bottom" : "top";
-        var description2 = d.description();
+        var description = d.description();
         var isOverflowing = span.property("clientWidth") !== span.property("scrollWidth");
         item.call(uiTooltip().destroyAny);
-        if (description2 || isOverflowing) {
-          item.call(uiTooltip().placement(placement).title(description2 || d.name()));
+        if (description || isOverflowing) {
+          item.call(
+            uiTooltip().placement(placement).title(() => description || d.name())
+          );
         }
       });
     }
@@ -66457,7 +69542,7 @@ ${content}</tr>
       _overlayList.call(updateLayerSelections);
       document.activeElement.blur();
     }
-    function drawListItems(layerList, type3, change, filter2) {
+    function drawListItems(layerList, type2, change, filter2) {
       var sources = context.background().sources(context.map().extent(), context.map().zoom(), true).filter(filter2);
       var layerLinks = layerList.selectAll("li").data(sources, function(d) {
         return d.name();
@@ -66465,9 +69550,9 @@ ${content}</tr>
       layerLinks.exit().remove();
       var enter = layerLinks.enter().append("li");
       var label = enter.append("label");
-      label.append("input").attr("type", type3).attr("name", "layers").on("change", change);
-      label.append("span").html(function(d) {
-        return d.label();
+      label.append("input").attr("type", type2).attr("name", "layers").on("change", change);
+      label.append("span").each(function(d) {
+        d.label()(select_default2(this));
       });
       layerList.selectAll("li").sort(sortSources);
       layerList.call(updateLayerSelections);
@@ -66482,15 +69567,18 @@ ${content}</tr>
         return !d.isHidden() && d.overlay;
       });
     }
-    context.map().on("move.overlay_list", debounce_default(function() {
-      window.requestIdleCallback(section.reRender);
-    }, 1e3));
+    context.map().on(
+      "move.overlay_list",
+      debounce_default(function() {
+        window.requestIdleCallback(section.reRender);
+      }, 1e3)
+    );
     return section;
   }
 
   // modules/ui/panes/background.js
   function uiPaneBackground(context) {
-    var backgroundPane = uiPane("background", context).key(_t("background.key")).label(_t.html("background.title")).description(_t.html("background.description")).iconName("iD-icon-layers").sections([
+    var backgroundPane = uiPane("background", context).key(_t("background.key")).label(_t.append("background.title")).description(_t.append("background.description")).iconName("iD-icon-layers").sections([
       uiSectionBackgroundList(context),
       uiSectionOverlayList(context),
       uiSectionBackgroundDisplayOptions(context),
@@ -66755,7 +69843,7 @@ ${content}</tr>
         content: marked(text2.trim()).replace(/<code>/g, "<kbd>").replace(/<\/code>/g, "</kbd>")
       };
     });
-    var helpPane = uiPane("help", context).key(_t("help.key")).label(_t.html("help.title")).description(_t.html("help.title")).iconName("iD-icon-help");
+    var helpPane = uiPane("help", context).key(_t("help.key")).label(_t.append("help.title")).description(_t.append("help.title")).iconName("iD-icon-help");
     helpPane.renderContent = function(content) {
       function clickHelp(d, i2) {
         var rtl = _mainLocalizer.textDirection() === "rtl";
@@ -66809,7 +69897,9 @@ ${content}</tr>
         d3_event.preventDefault();
         clickHelp(d, docs.indexOf(d));
       });
-      var shortcuts = toc.append("li").attr("class", "shortcuts").call(uiTooltip().title(_t.html("shortcuts.tooltip")).keys(["?"]).placement("top")).append("a").attr("href", "#").on("click", clickShortcuts);
+      var shortcuts = toc.append("li").attr("class", "shortcuts").call(
+        uiTooltip().title(() => _t.append("shortcuts.tooltip")).keys(["?"]).placement("top")
+      ).append("a").attr("href", "#").on("click", clickShortcuts);
       shortcuts.append("div").call(_t.append("shortcuts.title"));
       var walkthrough = toc.append("li").attr("class", "walkthrough").append("a").attr("href", "#").on("click", clickWalkthrough);
       walkthrough.append("svg").attr("class", "logo logo-walkthrough").append("use").attr("xlink:href", "#iD-logo-walkthrough");
@@ -66829,7 +69919,7 @@ ${content}</tr>
       if (!_issues)
         return "";
       var issueCountText = _issues.length > 1e3 ? "1000+" : String(_issues.length);
-      return _t.html("inspector.title_count", { title: { html: _t.html("issues." + severity + "s.list_title") }, count: issueCountText });
+      return _t.append("inspector.title_count", { title: _t("issues." + severity + "s.list_title"), count: issueCountText });
     }).disclosureContent(renderDisclosureContent).shouldDisplay(function() {
       return _issues && _issues.length;
     });
@@ -66879,8 +69969,8 @@ ${content}</tr>
       });
       textEnter.append("span").attr("class", "issue-message");
       items = items.merge(itemsEnter).order();
-      items.selectAll(".issue-message").html(function(d) {
-        return d.message(context);
+      items.selectAll(".issue-message").text("").each(function(d) {
+        return d.message(context)(select_default2(this));
       });
     }
     context.validator().on("validated.uiSectionValidationIssues" + id2, function() {
@@ -66889,14 +69979,17 @@ ${content}</tr>
         section.reRender();
       });
     });
-    context.map().on("move.uiSectionValidationIssues" + id2, debounce_default(function() {
-      window.requestIdleCallback(function() {
-        if (getOptions().where === "visible") {
-          reloadIssues();
-        }
-        section.reRender();
-      });
-    }, 1e3));
+    context.map().on(
+      "move.uiSectionValidationIssues" + id2,
+      debounce_default(function() {
+        window.requestIdleCallback(function() {
+          if (getOptions().where === "visible") {
+            reloadIssues();
+          }
+          section.reRender();
+        });
+      }, 1e3)
+    );
     return section;
   }
 
@@ -66940,7 +70033,9 @@ ${content}</tr>
     function getOptions() {
       return {
         what: corePreferences("validate-what") || "edited",
+        // 'all', 'edited'
         where: corePreferences("validate-where") || "all"
+        // 'all', 'visible'
       };
     }
     function updateOptionValue(d3_event, d, val) {
@@ -66958,7 +70053,7 @@ ${content}</tr>
     var MINSQUARE = 0;
     var MAXSQUARE = 20;
     var DEFAULTSQUARE = 5;
-    var section = uiSection("issues-rules", context).disclosureContent(renderDisclosureContent).label(_t.html("issues.rules.title"));
+    var section = uiSection("issues-rules", context).disclosureContent(renderDisclosureContent).label(() => _t.append("issues.rules.title"));
     var _ruleKeys = context.validator().getRuleKeys().filter(function(key) {
       return key !== "maprules";
     }).sort(function(key1, key2) {
@@ -66980,17 +70075,19 @@ ${content}</tr>
       container = container.merge(containerEnter);
       container.selectAll(".issue-rules-list").call(drawListItems, _ruleKeys, "checkbox", "rule", toggleRule, isRuleEnabled);
     }
-    function drawListItems(selection2, data, type3, name2, change, active) {
+    function drawListItems(selection2, data, type2, name, change, active) {
       var items = selection2.selectAll("li").data(data);
       items.exit().remove();
       var enter = items.enter().append("li");
-      if (name2 === "rule") {
-        enter.call(uiTooltip().title(function(d) {
-          return _t.html("issues." + d + ".tip");
-        }).placement("top"));
+      if (name === "rule") {
+        enter.call(
+          uiTooltip().title(function(d) {
+            return _t.append("issues." + d + ".tip");
+          }).placement("top")
+        );
       }
       var label = enter.append("label");
-      label.append("input").attr("type", type3).attr("name", name2).on("change", change);
+      label.append("input").attr("type", type2).attr("name", name).on("change", change);
       label.append("span").html(function(d) {
         var params = {};
         if (d === "unsquare_way") {
@@ -67020,7 +70117,7 @@ ${content}</tr>
     function changeSquare() {
       var input = select_default2(this);
       var degStr = utilGetSetValue(input).trim();
-      var degNum = parseFloat(degStr, 10);
+      var degNum = Number(degStr);
       if (!isFinite(degNum)) {
         degNum = DEFAULTSQUARE;
       } else if (degNum > MAXSQUARE) {
@@ -67085,11 +70182,14 @@ ${content}</tr>
     function setNoIssuesText(selection2) {
       var opts = getOptions();
       function checkForHiddenIssues(cases) {
-        for (var type3 in cases) {
-          var hiddenOpts = cases[type3];
+        for (var type2 in cases) {
+          var hiddenOpts = cases[type2];
           var hiddenIssues = context.validator().getIssues(hiddenOpts);
           if (hiddenIssues.length) {
-            selection2.select(".box .details").html("").call(_t.append("issues.no_issues.hidden_issues." + type3, { count: hiddenIssues.length.toString() }));
+            selection2.select(".box .details").html("").call(_t.append(
+              "issues.no_issues.hidden_issues." + type2,
+              { count: hiddenIssues.length.toString() }
+            ));
             return;
           }
         }
@@ -67138,15 +70238,18 @@ ${content}</tr>
     context.validator().on("validated.uiSectionValidationStatus", function() {
       window.requestIdleCallback(section.reRender);
     });
-    context.map().on("move.uiSectionValidationStatus", debounce_default(function() {
-      window.requestIdleCallback(section.reRender);
-    }, 1e3));
+    context.map().on(
+      "move.uiSectionValidationStatus",
+      debounce_default(function() {
+        window.requestIdleCallback(section.reRender);
+      }, 1e3)
+    );
     return section;
   }
 
   // modules/ui/panes/issues.js
   function uiPaneIssues(context) {
-    var issuesPane = uiPane("issues", context).key(_t("issues.key")).label(_t.html("issues.title")).description(_t.html("issues.title")).iconName("iD-icon-alert").sections([
+    var issuesPane = uiPane("issues", context).key(_t("issues.key")).label(_t.append("issues.title")).description(_t.append("issues.title")).iconName("iD-icon-alert").sections([
       uiSectionValidationOptions(context),
       uiSectionValidationStatus(context),
       uiSectionValidationIssues("issues-errors", "error", context),
@@ -67221,7 +70324,7 @@ ${content}</tr>
   function uiSectionDataLayers(context) {
     var settingsCustomData = uiSettingsCustomData(context).on("change", customChanged);
     var layers = context.layers();
-    var section = uiSection("data-layers", context).label(_t.html("map_data.data_layers")).disclosureContent(renderDisclosureContent);
+    var section = uiSection("data-layers", context).label(() => _t.append("map_data.data_layers")).disclosureContent(renderDisclosureContent);
     function renderDisclosureContent(selection2) {
       var container = selection2.selectAll(".data-layer-container").data([0]);
       container.enter().append("div").attr("class", "data-layer-container").merge(container).call(drawOsmItems).call(drawQAItems).call(drawCustomDataItems).call(drawVectorItems).call(drawPanelItems);
@@ -67262,9 +70365,13 @@ ${content}</tr>
       });
       var labelEnter = liEnter.append("label").each(function(d) {
         if (d.id === "osm") {
-          select_default2(this).call(uiTooltip().title(_t.html("map_data.layers." + d.id + ".tooltip")).keys([uiCmd("\u2325" + _t("area_fill.wireframe.key"))]).placement("bottom"));
+          select_default2(this).call(
+            uiTooltip().title(() => _t.append("map_data.layers." + d.id + ".tooltip")).keys([uiCmd("\u2325" + _t("area_fill.wireframe.key"))]).placement("bottom")
+          );
         } else {
-          select_default2(this).call(uiTooltip().title(_t.html("map_data.layers." + d.id + ".tooltip")).placement("bottom"));
+          select_default2(this).call(
+            uiTooltip().title(() => _t.append("map_data.layers." + d.id + ".tooltip")).placement("bottom")
+          );
         }
       });
       labelEnter.append("input").attr("type", "checkbox").on("change", function(d3_event, d) {
@@ -67292,13 +70399,15 @@ ${content}</tr>
         return "list-item list-item-" + d.id;
       });
       var labelEnter = liEnter.append("label").each(function(d) {
-        select_default2(this).call(uiTooltip().title(_t.html("map_data.layers." + d.id + ".tooltip")).placement("bottom"));
+        select_default2(this).call(
+          uiTooltip().title(() => _t.append("map_data.layers." + d.id + ".tooltip")).placement("bottom")
+        );
       });
       labelEnter.append("input").attr("type", "checkbox").on("change", function(d3_event, d) {
         toggleLayer(d.id);
       });
-      labelEnter.append("span").html(function(d) {
-        return _t.html("map_data.layers." + d.id + ".title");
+      labelEnter.append("span").each(function(d) {
+        _t.append("map_data.layers." + d.id + ".title")(select_default2(this));
       });
       li.merge(liEnter).classed("active", function(d) {
         return d.layer.enabled();
@@ -67344,7 +70453,9 @@ ${content}</tr>
         return "list-item list-item-" + d.src;
       });
       var labelEnter = liEnter.append("label").each(function(d) {
-        select_default2(this).call(uiTooltip().title(d.tooltip).placement("top"));
+        select_default2(this).call(
+          uiTooltip().title(d.tooltip).placement("top")
+        );
       });
       labelEnter.append("input").attr("type", "radio").attr("name", "vectortile").on("change", selectVTLayer);
       labelEnter.append("span").text(function(d) {
@@ -67370,16 +70481,22 @@ ${content}</tr>
       ul.exit().remove();
       var ulEnter = ul.enter().append("ul").attr("class", "layer-list layer-list-data");
       var liEnter = ulEnter.append("li").attr("class", "list-item-data");
-      var labelEnter = liEnter.append("label").call(uiTooltip().title(_t.html("map_data.layers.custom.tooltip")).placement("top"));
+      var labelEnter = liEnter.append("label").call(
+        uiTooltip().title(() => _t.append("map_data.layers.custom.tooltip")).placement("top")
+      );
       labelEnter.append("input").attr("type", "checkbox").on("change", function() {
         toggleLayer("data");
       });
       labelEnter.append("span").call(_t.append("map_data.layers.custom.title"));
-      liEnter.append("button").attr("class", "open-data-options").call(uiTooltip().title(_t.html("settings.custom_data.tooltip")).placement(_mainLocalizer.textDirection() === "rtl" ? "right" : "left")).on("click", function(d3_event) {
+      liEnter.append("button").attr("class", "open-data-options").call(
+        uiTooltip().title(() => _t.append("settings.custom_data.tooltip")).placement(_mainLocalizer.textDirection() === "rtl" ? "right" : "left")
+      ).on("click", function(d3_event) {
         d3_event.preventDefault();
         editCustom();
       }).call(svgIcon("#iD-icon-more"));
-      liEnter.append("button").attr("class", "zoom-to-data").call(uiTooltip().title(_t.html("map_data.layers.custom.zoom")).placement(_mainLocalizer.textDirection() === "rtl" ? "right" : "left")).on("click", function(d3_event) {
+      liEnter.append("button").attr("class", "zoom-to-data").call(
+        uiTooltip().title(() => _t.append("map_data.layers.custom.zoom")).placement(_mainLocalizer.textDirection() === "rtl" ? "right" : "left")
+      ).on("click", function(d3_event) {
         if (select_default2(this).classed("disabled"))
           return;
         d3_event.preventDefault();
@@ -67403,13 +70520,17 @@ ${content}</tr>
     }
     function drawPanelItems(selection2) {
       var panelsListEnter = selection2.selectAll(".md-extras-list").data([0]).enter().append("ul").attr("class", "layer-list md-extras-list");
-      var historyPanelLabelEnter = panelsListEnter.append("li").attr("class", "history-panel-toggle-item").append("label").call(uiTooltip().title(_t.html("map_data.history_panel.tooltip")).keys([uiCmd("\u2318\u21E7" + _t("info_panels.history.key"))]).placement("top"));
+      var historyPanelLabelEnter = panelsListEnter.append("li").attr("class", "history-panel-toggle-item").append("label").call(
+        uiTooltip().title(() => _t.append("map_data.history_panel.tooltip")).keys([uiCmd("\u2318\u21E7" + _t("info_panels.history.key"))]).placement("top")
+      );
       historyPanelLabelEnter.append("input").attr("type", "checkbox").on("change", function(d3_event) {
         d3_event.preventDefault();
         context.ui().info.toggle("history");
       });
       historyPanelLabelEnter.append("span").call(_t.append("map_data.history_panel.title"));
-      var measurementPanelLabelEnter = panelsListEnter.append("li").attr("class", "measurement-panel-toggle-item").append("label").call(uiTooltip().title(_t.html("map_data.measurement_panel.tooltip")).keys([uiCmd("\u2318\u21E7" + _t("info_panels.measurement.key"))]).placement("top"));
+      var measurementPanelLabelEnter = panelsListEnter.append("li").attr("class", "measurement-panel-toggle-item").append("label").call(
+        uiTooltip().title(() => _t.append("map_data.measurement_panel.tooltip")).keys([uiCmd("\u2318\u21E7" + _t("info_panels.measurement.key"))]).placement("top")
+      );
       measurementPanelLabelEnter.append("input").attr("type", "checkbox").on("change", function(d3_event) {
         d3_event.preventDefault();
         context.ui().info.toggle("measurement");
@@ -67417,16 +70538,19 @@ ${content}</tr>
       measurementPanelLabelEnter.append("span").call(_t.append("map_data.measurement_panel.title"));
     }
     context.layers().on("change.uiSectionDataLayers", section.reRender);
-    context.map().on("move.uiSectionDataLayers", debounce_default(function() {
-      window.requestIdleCallback(section.reRender);
-    }, 1e3));
+    context.map().on(
+      "move.uiSectionDataLayers",
+      debounce_default(function() {
+        window.requestIdleCallback(section.reRender);
+      }, 1e3)
+    );
     return section;
   }
 
   // modules/ui/sections/map_features.js
   function uiSectionMapFeatures(context) {
     var _features = context.features().keys();
-    var section = uiSection("map-features", context).label(_t.html("map_data.map_features")).disclosureContent(renderDisclosureContent).expandedByDefault(false);
+    var section = uiSection("map-features", context).label(() => _t.append("map_data.map_features")).disclosureContent(renderDisclosureContent).expandedByDefault(false);
     function renderDisclosureContent(selection2) {
       var container = selection2.selectAll(".layer-feature-list-container").data([0]);
       var containerEnter = container.enter().append("div").attr("class", "layer-feature-list-container");
@@ -67443,21 +70567,26 @@ ${content}</tr>
       container = container.merge(containerEnter);
       container.selectAll(".layer-feature-list").call(drawListItems, _features, "checkbox", "feature", clickFeature, showsFeature);
     }
-    function drawListItems(selection2, data, type3, name2, change, active) {
+    function drawListItems(selection2, data, type2, name, change, active) {
       var items = selection2.selectAll("li").data(data);
       items.exit().remove();
-      var enter = items.enter().append("li").call(uiTooltip().title(function(d) {
-        var tip = _t.html(name2 + "." + d + ".tooltip");
-        if (autoHiddenFeature(d)) {
-          var msg = showsLayer("osm") ? _t.html("map_data.autohidden") : _t.html("map_data.osmhidden");
-          tip += "<div>" + msg + "</div>";
-        }
-        return tip;
-      }).placement("top"));
+      var enter = items.enter().append("li").call(
+        uiTooltip().title(function(d) {
+          var tip = _t.append(name + "." + d + ".tooltip");
+          if (autoHiddenFeature(d)) {
+            var msg = showsLayer("osm") ? _t.append("map_data.autohidden") : _t.append("map_data.osmhidden");
+            return (selection3) => {
+              selection3.call(tip);
+              selection3.append("div").call(msg);
+            };
+          }
+          return tip;
+        }).placement("top")
+      );
       var label = enter.append("label");
-      label.append("input").attr("type", type3).attr("name", name2).on("change", change);
+      label.append("input").attr("type", type2).attr("name", name).on("change", change);
       label.append("span").html(function(d) {
-        return _t.html(name2 + "." + d + ".description");
+        return _t.html(name + "." + d + ".description");
       });
       items = items.merge(enter);
       items.classed("active", active).selectAll("input").property("checked", active).property("indeterminate", autoHiddenFeature);
@@ -67481,7 +70610,7 @@ ${content}</tr>
 
   // modules/ui/sections/map_style_options.js
   function uiSectionMapStyleOptions(context) {
-    var section = uiSection("fill-area", context).label(_t.html("map_data.style_options")).disclosureContent(renderDisclosureContent).expandedByDefault(false);
+    var section = uiSection("fill-area", context).label(() => _t.append("map_data.style_options")).disclosureContent(renderDisclosureContent).expandedByDefault(false);
     function renderDisclosureContent(selection2) {
       var container = selection2.selectAll(".layer-fill-list").data([0]);
       container.enter().append("ul").attr("class", "layer-list layer-fill-list").merge(container).call(drawListItems, context.map().areaFillOptions, "radio", "area_fill", setFill, isActiveFill);
@@ -67490,21 +70619,23 @@ ${content}</tr>
         return context.surface().classed("highlight-edited");
       });
     }
-    function drawListItems(selection2, data, type3, name2, change, active) {
+    function drawListItems(selection2, data, type2, name, change, active) {
       var items = selection2.selectAll("li").data(data);
       items.exit().remove();
-      var enter = items.enter().append("li").call(uiTooltip().title(function(d) {
-        return _t.html(name2 + "." + d + ".tooltip");
-      }).keys(function(d) {
-        var key = d === "wireframe" ? _t("area_fill.wireframe.key") : null;
-        if (d === "highlight_edits")
-          key = _t("map_data.highlight_edits.key");
-        return key ? [key] : null;
-      }).placement("top"));
+      var enter = items.enter().append("li").call(
+        uiTooltip().title(function(d) {
+          return _t.append(name + "." + d + ".tooltip");
+        }).keys(function(d) {
+          var key = d === "wireframe" ? _t("area_fill.wireframe.key") : null;
+          if (d === "highlight_edits")
+            key = _t("map_data.highlight_edits.key");
+          return key ? [key] : null;
+        }).placement("top")
+      );
       var label = enter.append("label");
-      label.append("input").attr("type", type3).attr("name", name2).on("change", change);
+      label.append("input").attr("type", type2).attr("name", name).on("change", change);
       label.append("span").html(function(d) {
-        return _t.html(name2 + "." + d + ".description");
+        return _t.html(name + "." + d + ".description");
       });
       items = items.merge(enter);
       items.classed("active", active).selectAll("input").property("checked", active).property("indeterminate", false);
@@ -67526,7 +70657,7 @@ ${content}</tr>
   // modules/ui/sections/photo_overlays.js
   function uiSectionPhotoOverlays(context) {
     var layers = context.layers();
-    var section = uiSection("photo-overlays", context).label(_t.html("photo_overlays.title")).disclosureContent(renderDisclosureContent).expandedByDefault(false);
+    var section = uiSection("photo-overlays", context).label(() => _t.append("photo_overlays.title")).disclosureContent(renderDisclosureContent).expandedByDefault(false);
     function renderDisclosureContent(selection2) {
       var container = selection2.selectAll(".photo-overlay-container").data([0]);
       container.enter().append("div").attr("class", "photo-overlay-container").merge(container).call(drawPhotoItems).call(drawPhotoTypeItems).call(drawDateFilter).call(drawUsernameFilter);
@@ -67566,7 +70697,9 @@ ${content}</tr>
           titleID = "kartaview_images.tooltip";
         else
           titleID = d.id.replace(/-/g, "_") + ".tooltip";
-        select_default2(this).call(uiTooltip().title(_t.html(titleID)).placement("top"));
+        select_default2(this).call(
+          uiTooltip().title(() => _t.append(titleID)).placement("top")
+        );
       });
       labelEnter.append("input").attr("type", "checkbox").on("change", function(d3_event, d) {
         toggleLayer(d.id);
@@ -67593,7 +70726,9 @@ ${content}</tr>
         return "list-item-photo-types list-item-" + d;
       });
       var labelEnter = liEnter.append("label").each(function(d) {
-        select_default2(this).call(uiTooltip().title(_t.html("photo_overlays.photo_type." + d + ".tooltip")).placement("top"));
+        select_default2(this).call(
+          uiTooltip().title(() => _t.append("photo_overlays.photo_type." + d + ".tooltip")).placement("top")
+        );
       });
       labelEnter.append("input").attr("type", "checkbox").on("change", function(d3_event, d) {
         context.photos().togglePhotoType(d);
@@ -67615,10 +70750,12 @@ ${content}</tr>
       li.exit().remove();
       var liEnter = li.enter().append("li").attr("class", "list-item-date-filter");
       var labelEnter = liEnter.append("label").each(function(d) {
-        select_default2(this).call(uiTooltip().title(_t.html("photo_overlays.date_filter." + d + ".tooltip")).placement("top"));
+        select_default2(this).call(
+          uiTooltip().title(() => _t.append("photo_overlays.date_filter." + d + ".tooltip")).placement("top")
+        );
       });
-      labelEnter.append("span").html(function(d) {
-        return _t.html("photo_overlays.date_filter." + d + ".title");
+      labelEnter.append("span").each(function(d) {
+        _t.append("photo_overlays.date_filter." + d + ".title")(select_default2(this));
       });
       labelEnter.append("input").attr("type", "date").attr("class", "list-item-input").attr("placeholder", _t("units.year_month_day")).call(utilNoAuto).each(function(d) {
         utilGetSetValue(select_default2(this), context.photos().dateFilterValue(d) || "");
@@ -67642,7 +70779,9 @@ ${content}</tr>
       li.exit().remove();
       var liEnter = li.enter().append("li").attr("class", "list-item-username-filter");
       var labelEnter = liEnter.append("label").each(function() {
-        select_default2(this).call(uiTooltip().title(_t.html("photo_overlays.username_filter.tooltip")).placement("top"));
+        select_default2(this).call(
+          uiTooltip().title(() => _t.append("photo_overlays.username_filter.tooltip")).placement("top")
+        );
       });
       labelEnter.append("span").call(_t.append("photo_overlays.username_filter.title"));
       labelEnter.append("input").attr("type", "text").attr("class", "list-item-input").call(utilNoAuto).property("value", usernameValue).on("change", function() {
@@ -67681,7 +70820,7 @@ ${content}</tr>
 
   // modules/ui/panes/map_data.js
   function uiPaneMapData(context) {
-    var mapDataPane = uiPane("map-data", context).key(_t("map_data.key")).label(_t.html("map_data.title")).description(_t.html("map_data.description")).iconName("iD-icon-data").sections([
+    var mapDataPane = uiPane("map-data", context).key(_t("map_data.key")).label(_t.append("map_data.title")).description(_t.append("map_data.description")).iconName("iD-icon-data").sections([
       uiSectionDataLayers(context),
       uiSectionPhotoOverlays(context),
       uiSectionMapStyleOptions(context),
@@ -67692,7 +70831,7 @@ ${content}</tr>
 
   // modules/ui/panes/preferences.js
   function uiPanePreferences(context) {
-    let preferencesPane = uiPane("preferences", context).key(_t("preferences.key")).label(_t.html("preferences.title")).description(_t.html("preferences.description")).iconName("fas-user-cog").sections([
+    let preferencesPane = uiPane("preferences", context).key(_t("preferences.key")).label(_t.append("preferences.title")).description(_t.append("preferences.description")).iconName("fas-user-cog").sections([
       uiSectionPrivacy(context)
     ]);
     return preferencesPane;
@@ -67710,14 +70849,19 @@ ${content}</tr>
         if (!d3_event.composedPath)
           return;
         var isOkayTarget = d3_event.composedPath().some(function(node) {
-          return node.nodeType === 1 && (node.nodeName === "INPUT" || node.nodeName === "LABEL" || node.nodeName === "A");
+          return node.nodeType === 1 && // clicking <input> focuses it and/or changes a value
+          (node.nodeName === "INPUT" || // clicking <label> affects its <input> by default
+          node.nodeName === "LABEL" || // clicking <a> opens a hyperlink by default
+          node.nodeName === "A");
         });
         if (isOkayTarget)
           return;
         d3_event.preventDefault();
       });
       var detected = utilDetect();
-      if ("GestureEvent" in window && !detected.isMobileWebKit) {
+      if ("GestureEvent" in window && // Listening for gesture events on iOS 13.4+ breaks double-tapping,
+      // but we only need to do this on desktop Safari anyway. – #7694
+      !detected.isMobileWebKit) {
         container.on("gesturestart.ui gesturechange.ui gestureend.ui", function(d3_event) {
           d3_event.preventDefault();
         });
@@ -67739,7 +70883,7 @@ ${content}</tr>
       var map2 = context.map();
       map2.redrawEnable(false);
       map2.on("hitMinZoom.ui", function() {
-        ui.flash.iconName("#iD-icon-no").label(_t.html("cannot_zoom"))();
+        ui.flash.iconName("#iD-icon-no").label(_t.append("cannot_zoom"))();
       });
       container.append("svg").attr("id", "ideditor-defs").call(ui.svgDefs);
       container.append("div").attr("class", "sidebar").call(ui.sidebar);
@@ -67784,15 +70928,17 @@ ${content}</tr>
       footerWrap.append("div").attr("class", "scale-block").call(uiScale(context));
       var aboutList = footerWrap.append("div").attr("class", "info-block").append("ul").attr("class", "map-footer-list");
       aboutList.append("li").attr("class", "user-list").call(uiContributors(context));
-      var apiConnections = context.apiConnections();
+      var apiConnections = context.connection().apiConnections();
       if (apiConnections && apiConnections.length > 1) {
-        aboutList.append("li").attr("class", "source-switch").call(uiSourceSwitch(context).keys(apiConnections));
+        aboutList.append("li").attr("class", "source-switch").call(
+          uiSourceSwitch(context).keys(apiConnections)
+        );
       }
       aboutList.append("li").attr("class", "issues-info").call(uiIssuesInfo(context));
       aboutList.append("li").attr("class", "feature-warning").call(uiFeatureInfo(context));
       var issueLinks = aboutList.append("li");
-      issueLinks.append("a").attr("target", "_blank").attr("href", "https://github.com/openstreetmap/iD/issues").attr("aria-label", _t("report_a_bug")).call(svgIcon("#iD-icon-bug", "light")).call(uiTooltip().title(_t.html("report_a_bug")).placement("top"));
-      issueLinks.append("a").attr("target", "_blank").attr("href", "https://github.com/openstreetmap/iD/blob/develop/CONTRIBUTING.md#translating").attr("aria-label", _t("help_translate")).call(svgIcon("#iD-icon-translate", "light")).call(uiTooltip().title(_t.html("help_translate")).placement("top"));
+      issueLinks.append("a").attr("target", "_blank").attr("href", "https://github.com/openstreetmap/iD/issues").attr("aria-label", _t("report_a_bug")).call(svgIcon("#iD-icon-bug", "light")).call(uiTooltip().title(() => _t.append("report_a_bug")).placement("top"));
+      issueLinks.append("a").attr("target", "_blank").attr("href", "https://github.com/openstreetmap/iD/blob/develop/CONTRIBUTING.md#translating").attr("aria-label", _t("help_translate")).call(svgIcon("#iD-icon-translate", "light")).call(uiTooltip().title(() => _t.append("help_translate")).placement("top"));
       aboutList.append("li").attr("class", "version").call(uiVersion(context));
       if (!context.embed()) {
         aboutList.call(uiAccount(context));
@@ -67801,7 +70947,7 @@ ${content}</tr>
       map2.redrawEnable(true);
       ui.hash = behaviorHash(context);
       ui.hash();
-      if (!ui.hash.hadHash) {
+      if (!ui.hash.hadLocation) {
         map2.centerZoom([0, 0], 2);
       }
       window.onbeforeunload = function() {
@@ -67892,6 +71038,7 @@ ${content}</tr>
       if (_loadPromise)
         return _loadPromise;
       return _loadPromise = Promise.all([
+        // must have strings and presets before loading the UI
         _mainLocalizer.ensureLoaded(),
         _mainPresetIndex.ensureLoaded()
       ]).then(() => {
@@ -68011,7 +71158,7 @@ ${content}</tr>
     const dispatch10 = dispatch_default("enter", "exit", "change");
     let context = utilRebind({}, dispatch10, "on");
     let _deferred2 = /* @__PURE__ */ new Set();
-    context.version = "2.21.1";
+    context.version = package_default.version;
     context.privacyVersion = "20201202";
     context.initialHashParams = window.location.hash ? utilStringQs(window.location.hash) : {};
     context.changeset = null;
@@ -68070,13 +71217,6 @@ ${content}</tr>
       }
       return context;
     };
-    let _apiConnections;
-    context.apiConnections = function(val) {
-      if (!arguments.length)
-        return _apiConnections;
-      _apiConnections = val;
-      return context;
-    };
     context.locale = function(locale2) {
       if (!arguments.length)
         return _mainLocalizer.localeCode();
@@ -68174,20 +71314,9 @@ ${content}</tr>
     context.maxCharsForTagKey = () => 255;
     context.maxCharsForTagValue = () => 255;
     context.maxCharsForRelationRole = () => 255;
-    function cleanOsmString(val, maxChars) {
-      if (val === void 0 || val === null) {
-        val = "";
-      } else {
-        val = val.toString();
-      }
-      val = val.trim();
-      if (val.normalize)
-        val = val.normalize("NFC");
-      return utilUnicodeCharsTruncated(val, maxChars);
-    }
-    context.cleanTagKey = (val) => cleanOsmString(val, context.maxCharsForTagKey());
-    context.cleanTagValue = (val) => cleanOsmString(val, context.maxCharsForTagValue());
-    context.cleanRelationRole = (val) => cleanOsmString(val, context.maxCharsForRelationRole());
+    context.cleanTagKey = (val) => utilCleanOsmString(val, context.maxCharsForTagKey());
+    context.cleanTagValue = (val) => utilCleanOsmString(val, context.maxCharsForTagValue());
+    context.cleanRelationRole = (val) => utilCleanOsmString(val, context.maxCharsForRelationRole());
     let _inIntro = false;
     context.inIntro = function(val) {
       if (!arguments.length)
@@ -68299,10 +71428,15 @@ ${content}</tr>
     };
     let _debugFlags = {
       tile: false,
+      // tile boundaries
       collision: false,
+      // label collision bounding boxes
       imagery: false,
+      // imagery bounding polygons
       target: false,
+      // touch targets
       downloaded: false
+      // downloaded data from osm
     };
     context.debugFlags = () => _debugFlags;
     context.getDebug = (flag) => flag && _debugFlags[flag];
@@ -68409,8 +71543,8 @@ ${content}</tr>
           _mainLocalizer.preferredLocaleCodes(context.initialHashParams.locale);
         }
         _mainLocalizer.ensureLoaded();
-        _background.ensureLoaded();
         _mainPresetIndex.ensureLoaded();
+        _background.ensureLoaded();
         Object.values(services).forEach((service) => {
           if (service && typeof service.init === "function") {
             service.init();
@@ -68428,6 +71562,7 @@ ${content}</tr>
         }
         if (!context.container().empty()) {
           _ui.ensureLoaded().then(() => {
+            _background.init();
             _photos.init();
           });
         }
@@ -68454,14 +71589,15 @@ ${content}</tr>
     const nsiVersion = package_default.dependencies["name-suggestion-index"] || package_default.devDependencies["name-suggestion-index"];
     const v = (0, import_vparse2.default)(nsiVersion);
     const vMinor = `${v.major}.${v.minor}`;
+    const cdn = nsiCdnUrl.replace("{version}", vMinor);
     const sources = {
-      "nsi_data": `https://cdn.jsdelivr.net/npm/name-suggestion-index@${vMinor}/dist/nsi.min.json`,
-      "nsi_dissolved": `https://cdn.jsdelivr.net/npm/name-suggestion-index@${vMinor}/dist/dissolved.min.json`,
-      "nsi_features": `https://cdn.jsdelivr.net/npm/name-suggestion-index@${vMinor}/dist/featureCollection.min.json`,
-      "nsi_generics": `https://cdn.jsdelivr.net/npm/name-suggestion-index@${vMinor}/dist/genericWords.min.json`,
-      "nsi_presets": `https://cdn.jsdelivr.net/npm/name-suggestion-index@${vMinor}/dist/presets/nsi-id-presets.min.json`,
-      "nsi_replacements": `https://cdn.jsdelivr.net/npm/name-suggestion-index@${vMinor}/dist/replacements.min.json`,
-      "nsi_trees": `https://cdn.jsdelivr.net/npm/name-suggestion-index@${vMinor}/dist/trees.min.json`
+      "nsi_data": cdn + "dist/nsi.min.json",
+      "nsi_dissolved": cdn + "dist/dissolved.min.json",
+      "nsi_features": cdn + "dist/featureCollection.min.json",
+      "nsi_generics": cdn + "dist/genericWords.min.json",
+      "nsi_presets": cdn + "dist/presets/nsi-id-presets.min.json",
+      "nsi_replacements": cdn + "dist/replacements.min.json",
+      "nsi_trees": cdn + "dist/trees.min.json"
     };
     let fileMap = _mainFileFetcher.fileMap();
     for (const k in sources) {
@@ -68490,16 +71626,51 @@ ${content}</tr>
     ]).then((vals) => {
       _nsi = {
         data: vals[0].nsi,
+        // the raw name-suggestion-index data
         dissolved: vals[1].dissolved,
+        // list of dissolved items
         replacements: vals[2].replacements,
+        // trivial old->new qid replacements
         trees: vals[3].trees,
+        // metadata about trees, main tags
         kvt: /* @__PURE__ */ new Map(),
+        // Map (k -> Map (v -> t) )
         qids: /* @__PURE__ */ new Map(),
+        // Map (wd/wp tag values -> qids)
         ids: /* @__PURE__ */ new Map()
+        // Map (id -> NSI item)
+      };
+      const matcher = _nsi.matcher = new Matcher();
+      matcher.buildMatchIndex(_nsi.data);
+      matcher.itemLocation = /* @__PURE__ */ new Map();
+      matcher.locationSets = /* @__PURE__ */ new Map();
+      Object.keys(_nsi.data).forEach((tkv) => {
+        const items = _nsi.data[tkv].items;
+        if (!Array.isArray(items) || !items.length)
+          return;
+        items.forEach((item) => {
+          if (matcher.itemLocation.has(item.id))
+            return;
+          const locationSetID = _sharedLocationManager.locationSetID(item.locationSet);
+          matcher.itemLocation.set(item.id, locationSetID);
+          if (matcher.locationSets.has(locationSetID))
+            return;
+          const fakeFeature = { id: locationSetID, properties: { id: locationSetID, area: 1 } };
+          matcher.locationSets.set(locationSetID, fakeFeature);
+        });
+      });
+      matcher.locationIndex = (bbox2) => {
+        const validHere = _sharedLocationManager.locationSetsAt([bbox2[0], bbox2[1]]);
+        const results = [];
+        for (const [locationSetID, area] of Object.entries(validHere)) {
+          const fakeFeature = matcher.locationSets.get(locationSetID);
+          if (fakeFeature) {
+            fakeFeature.properties.area = area;
+            results.push(fakeFeature);
+          }
+        }
+        return results;
       };
-      _nsi.matcher = new Matcher();
-      _nsi.matcher.buildMatchIndex(_nsi.data);
-      _nsi.matcher.buildLocationIndex(_nsi.data, _mainLocations.loco());
       Object.keys(_nsi.data).forEach((tkv) => {
         const category = _nsi.data[tkv];
         const parts = tkv.split("/", 3);
@@ -68594,6 +71765,7 @@ ${content}</tr>
       patterns2 = {
         primary: /^(flag:name|flag:name:\w+)$/i,
         alternate: /^(flag|flag:\w+|subject|subject:\w+)$/i
+        // note: no `country`, we special-case it below
       };
     } else if (t === "brands") {
       testNameFragments = true;
@@ -68617,8 +71789,8 @@ ${content}</tr>
     if (tags.name && testNameFragments) {
       const nameParts = tags.name.split(/[\s\-\/,.]/);
       for (let split = nameParts.length; split > 0; split--) {
-        const name2 = nameParts.slice(0, split).join(" ");
-        primary.add(name2);
+        const name = nameParts.slice(0, split).join(" ");
+        primary.add(name);
       }
     }
     Object.keys(tags).forEach((osmkey) => {
@@ -68717,42 +71889,39 @@ ${content}</tr>
       return changed ? { newTags, matched: null } : null;
     }
     const tuples = gatherTuples(tryKVs, tryNames);
-    let foundPrimary = false;
-    let bestItem;
-    for (let i2 = 0; i2 < tuples.length && !foundPrimary; i2++) {
+    for (let i2 = 0; i2 < tuples.length; i2++) {
       const tuple = tuples[i2];
       const hits = _nsi.matcher.match(tuple.k, tuple.v, tuple.n, loc);
       if (!hits || !hits.length)
         continue;
       if (hits[0].match !== "primary" && hits[0].match !== "alternate")
         break;
+      let itemID, item;
       for (let j2 = 0; j2 < hits.length; j2++) {
         const hit = hits[j2];
-        const isPrimary = hits[j2].match === "primary";
-        const itemID = hit.itemID;
+        itemID = hit.itemID;
         if (_nsi.dissolved[itemID])
           continue;
-        const item = _nsi.ids.get(itemID);
+        item = _nsi.ids.get(itemID);
         if (!item)
           continue;
         const mainTag = item.mainTag;
         const itemQID = item.tags[mainTag];
         const notQID = newTags[`not:${mainTag}`];
-        if (!itemQID || itemQID === notQID || newTags.office && !item.tags.office) {
+        if (
+          // Exceptions, skip this hit
+          !itemQID || itemQID === notQID || // No `*:wikidata` or matched a `not:*:wikidata`
+          newTags.office && !item.tags.office
+        ) {
+          item = null;
           continue;
-        }
-        if (!bestItem || isPrimary) {
-          bestItem = item;
-          if (isPrimary) {
-            foundPrimary = true;
-          }
+        } else {
           break;
         }
       }
-    }
-    if (bestItem) {
-      const itemID = bestItem.id;
-      const item = JSON.parse(JSON.stringify(bestItem));
+      if (!item)
+        continue;
+      item = JSON.parse(JSON.stringify(item));
       const tkv = item.tkv;
       const parts = tkv.split("/", 3);
       const k = parts[1];
@@ -68793,9 +71962,9 @@ ${content}</tr>
         if (!isMoved) {
           const nameParts = origName.split(/[\s\-\/,.]/);
           for (let split = nameParts.length; split > 0; split--) {
-            const name2 = nameParts.slice(0, split).join(" ");
+            const name = nameParts.slice(0, split).join(" ");
             const branch = nameParts.slice(split).join(" ");
-            const nameHits = _nsi.matcher.match(k, v, name2, loc);
+            const nameHits = _nsi.matcher.match(k, v, name, loc);
             if (!nameHits || !nameHits.length)
               continue;
             if (nameHits.some((hit) => hit.itemID === itemID)) {
@@ -68840,20 +72009,55 @@ ${content}</tr>
     return false;
   }
   var nsi_default = {
+    // `init()`
+    // On init, start preparing the name-suggestion-index
+    //
     init: () => {
       setNsiSources();
-      _mainPresetIndex.ensureLoaded().then(() => loadNsiPresets()).then(() => delay(100)).then(() => _mainLocations.mergeLocationSets([])).then(() => loadNsiData()).then(() => _nsiStatus = "ok").catch(() => _nsiStatus = "failed");
-      function delay(msec) {
-        return new Promise((resolve) => {
-          window.setTimeout(resolve, msec);
-        });
-      }
+      _mainPresetIndex.ensureLoaded().then(() => loadNsiPresets()).then(() => loadNsiData()).then(() => _nsiStatus = "ok").catch(() => _nsiStatus = "failed");
     },
+    // `reset()`
+    // Reset is called when user saves data to OSM (does nothing here)
+    //
     reset: () => {
     },
+    // `status()`
+    // To let other code know how it's going...
+    //
+    // Returns
+    //   `String`: 'loading', 'ok', 'failed'
+    //
     status: () => _nsiStatus,
+    // `isGenericName()`
+    // Is the `name` tag generic?
+    //
+    // Arguments
+    //   `tags`: `Object` containing the feature's OSM tags
+    // Returns
+    //   `true` if it is generic, `false` if not
+    //
     isGenericName: (tags) => _isGenericName(tags),
+    // `upgradeTags()`
+    // Suggest tag upgrades.
+    // This function will not modify the input tags, it makes a copy.
+    //
+    // Arguments
+    //   `tags`: `Object` containing the feature's OSM tags
+    //   `loc`: Location where this feature exists, as a [lon, lat]
+    // Returns
+    //   `Object` containing the result, or `null` if no changes needed:
+    //   {
+    //     'newTags': `Object` - The tags the the feature should have
+    //     'matched': `Object` - The matched item
+    //   }
+    //
     upgradeTags: (tags, loc) => _upgradeTags(tags, loc),
+    // `cache()`
+    // Direct access to the NSI cache, useful for testing or breaking things
+    //
+    // Returns
+    //   `Object`: the internal NSI cache
+    //
     cache: () => _nsi
   };
 
@@ -68904,14 +72108,15 @@ ${content}</tr>
   }
   function loadNextTilePage(which, currZoom, url, tile) {
     var cache = _oscCache[which];
-    var bbox = tile.extent.bbox();
+    var bbox2 = tile.extent.bbox();
     var maxPages = maxPageAtZoom(currZoom);
     var nextPage = cache.nextPage[tile.id] || 1;
     var params = utilQsString({
       ipp: maxResults,
       page: nextPage,
-      bbTopLeft: [bbox.maxY, bbox.minX].join(","),
-      bbBottomRight: [bbox.minY, bbox.maxX].join(",")
+      // client_id: clientId,
+      bbTopLeft: [bbox2.maxY, bbox2.minX].join(","),
+      bbBottomRight: [bbox2.minY, bbox2.maxX].join(",")
     }, true);
     if (nextPage > maxPages)
       return;
@@ -68932,7 +72137,7 @@ ${content}</tr>
       if (!data || !data.currentPageItems || !data.currentPageItems.length) {
         throw new Error("No Data");
       }
-      var features2 = data.currentPageItems.map(function(item) {
+      var features = data.currentPageItems.map(function(item) {
         var loc = [+item.lng, +item.lat];
         var d;
         if (which === "images") {
@@ -68962,7 +72167,7 @@ ${content}</tr>
           data: d
         };
       });
-      cache.rtree.load(features2);
+      cache.rtree.load(features);
       if (data.currentPageItems.length === maxResults) {
         cache.nextPage[tile.id] = nextPage + 1;
         loadNextTilePage(which, currZoom, url, tile);
@@ -69019,9 +72224,9 @@ ${content}</tr>
       var viewport = projection2.clipExtent();
       var min3 = [viewport[0][0], viewport[1][1]];
       var max3 = [viewport[1][0], viewport[0][1]];
-      var bbox = geoExtent(projection2.invert(min3), projection2.invert(max3)).bbox();
+      var bbox2 = geoExtent(projection2.invert(min3), projection2.invert(max3)).bbox();
       var sequenceKeys = {};
-      _oscCache.images.rtree.search(bbox).forEach(function(d) {
+      _oscCache.images.rtree.search(bbox2).forEach(function(d) {
         sequenceKeys[d.data.sequence_id] = true;
       });
       var lineStrings = [];
@@ -69176,6 +72381,9 @@ ${content}</tr>
     getSequenceKeyForImage: function(d) {
       return d && d.sequence_id;
     },
+    // Updates the currently highlighted sequence and selected bubble.
+    // Reset is only necessary when interacting with the viewport because
+    // this implicitly changes the currently selected bubble/sequence
     setStyles: function(context, hovered, reset) {
       if (reset) {
         context.container().selectAll(".viewfield-group").classed("highlighted", false).classed("hovered", false).classed("currentView", false);
@@ -69357,7 +72565,14 @@ ${content}</tr>
       }
       function run() {
         var url = options2.prefix !== false ? o.url + options2.path : options2.path;
-        return oauth2.rawxhr(options2.method, url, token("oauth2_access_token"), options2.content, options2.headers, done);
+        return oauth2.rawxhr(
+          options2.method,
+          url,
+          token("oauth2_access_token"),
+          options2.content,
+          options2.headers,
+          done
+        );
       }
       function done(err, xhr) {
         if (err) {
@@ -69376,7 +72591,7 @@ ${content}</tr>
       }
       var xhr = new XMLHttpRequest();
       xhr.onreadystatechange = function() {
-        if (xhr.readyState === 4 && xhr.status !== 0) {
+        if (4 === xhr.readyState && 0 !== xhr.status) {
           if (/^20\d$/.test(xhr.status)) {
             callback(null, xhr);
           } else {
@@ -69455,17 +72670,18 @@ ${content}</tr>
   var import_rbush9 = __toESM(require_rbush_min());
   var tiler5 = utilTiler();
   var dispatch7 = dispatch_default("apiStatusChange", "authLoading", "authDone", "change", "loading", "loaded", "loadedNotes");
-  var urlroot = "https://www.openstreetmap.org";
+  var urlroot = osmApiConnections[0].url;
   var redirectPath = window.location.origin + window.location.pathname;
   var oauth = osmAuth({
     url: urlroot,
-    client_id: "0tmNTmd0Jo1dQp4AUmMBLtGiD9YpMuXzHefitcuVStc",
-    client_secret: "BTlNrNxIPitHdL4sP2clHw5KLoee9aKkA7dQbc0Bj7Q",
+    client_id: osmApiConnections[0].client_id,
+    client_secret: osmApiConnections[0].client_secret,
     scope: "read_prefs write_prefs write_api read_gpx write_notes",
     redirect_uri: redirectPath + "land.html",
     loading: authLoading,
     done: authDone
   });
+  var _apiConnections = osmApiConnections;
   var _imageryBlocklists = [/.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/];
   var _tileCache = { toLoad: {}, loaded: {}, inflight: {}, seen: {}, rtree: new import_rbush9.default() };
   var _noteCache = { toLoad: {}, loaded: {}, inflight: {}, inflightPost: {}, note: {}, closed: {}, rtree: new import_rbush9.default() };
@@ -69510,7 +72726,7 @@ ${content}</tr>
   function getLoc(attrs) {
     var lon = attrs.lon && attrs.lon.value;
     var lat = attrs.lat && attrs.lat.value;
-    return [parseFloat(lon), parseFloat(lat)];
+    return [Number(lon), Number(lat)];
   }
   function getNodes(obj) {
     var elems = obj.getElementsByTagName("nd");
@@ -69612,7 +72828,7 @@ ${content}</tr>
         timestamp: obj.timestamp,
         user: obj.user,
         uid: obj.uid && obj.uid.toString(),
-        loc: [parseFloat(obj.lon), parseFloat(obj.lat)],
+        loc: [Number(obj.lon), Number(obj.lat)],
         tags: obj.tags
       });
     },
@@ -69780,8 +72996,8 @@ ${content}</tr>
         if (coincident) {
           props.loc = geoVecAdd(props.loc, [epsilon3, epsilon3]);
         }
-        var bbox = geoExtent(props.loc).bbox();
-        coincident = _noteCache.rtree.search(bbox).length;
+        var bbox2 = geoExtent(props.loc).bbox();
+        coincident = _noteCache.rtree.search(bbox2).length;
       } while (coincident);
       for (var i2 = 0; i2 < childNodes.length; i2++) {
         var node = childNodes[i2];
@@ -69947,6 +73163,8 @@ ${content}</tr>
     noteReportURL: function(note) {
       return urlroot + "/reports/new?reportable_type=Note&reportable_id=" + note.id;
     },
+    // Generic method to load data from the OSM API
+    // Can handle either auth or unauth calls.
     loadFromAPI: function(path, callback, options2) {
       options2 = Object.assign({ skipSeen: true }, options2);
       var that = this;
@@ -70008,50 +73226,82 @@ ${content}</tr>
         return controller;
       }
     },
+    // Load a single entity by id (ways and relations use the `/full` call to include
+    // nodes and members). Parent relations are not included, see `loadEntityRelations`.
+    // GET /api/0.6/node/#id
+    // GET /api/0.6/[way|relation]/#id/full
     loadEntity: function(id2, callback) {
-      var type3 = osmEntity.id.type(id2);
+      var type2 = osmEntity.id.type(id2);
       var osmID = osmEntity.id.toOSM(id2);
       var options2 = { skipSeen: false };
-      this.loadFromAPI("/api/0.6/" + type3 + "/" + osmID + (type3 !== "node" ? "/full" : "") + ".json", function(err, entities) {
-        if (callback)
-          callback(err, { data: entities });
-      }, options2);
+      this.loadFromAPI(
+        "/api/0.6/" + type2 + "/" + osmID + (type2 !== "node" ? "/full" : "") + ".json",
+        function(err, entities) {
+          if (callback)
+            callback(err, { data: entities });
+        },
+        options2
+      );
     },
-    loadEntityVersion: function(id2, version2, callback) {
-      var type3 = osmEntity.id.type(id2);
+    // Load a single entity with a specific version
+    // GET /api/0.6/[node|way|relation]/#id/#version
+    loadEntityVersion: function(id2, version, callback) {
+      var type2 = osmEntity.id.type(id2);
       var osmID = osmEntity.id.toOSM(id2);
       var options2 = { skipSeen: false };
-      this.loadFromAPI("/api/0.6/" + type3 + "/" + osmID + "/" + version2 + ".json", function(err, entities) {
-        if (callback)
-          callback(err, { data: entities });
-      }, options2);
+      this.loadFromAPI(
+        "/api/0.6/" + type2 + "/" + osmID + "/" + version + ".json",
+        function(err, entities) {
+          if (callback)
+            callback(err, { data: entities });
+        },
+        options2
+      );
     },
+    // Load the relations of a single entity with the given.
+    // GET /api/0.6/[node|way|relation]/#id/relations
     loadEntityRelations: function(id2, callback) {
-      var type3 = osmEntity.id.type(id2);
+      var type2 = osmEntity.id.type(id2);
       var osmID = osmEntity.id.toOSM(id2);
       var options2 = { skipSeen: false };
-      this.loadFromAPI("/api/0.6/" + type3 + "/" + osmID + "/relations.json", function(err, entities) {
-        if (callback)
-          callback(err, { data: entities });
-      }, options2);
+      this.loadFromAPI(
+        "/api/0.6/" + type2 + "/" + osmID + "/relations.json",
+        function(err, entities) {
+          if (callback)
+            callback(err, { data: entities });
+        },
+        options2
+      );
     },
+    // Load multiple entities in chunks
+    // (note: callback may be called multiple times)
+    // Unlike `loadEntity`, child nodes and members are not fetched
+    // GET /api/0.6/[nodes|ways|relations]?#parameters
     loadMultiple: function(ids, callback) {
       var that = this;
       var groups = utilArrayGroupBy(utilArrayUniq(ids), osmEntity.id.type);
       Object.keys(groups).forEach(function(k) {
-        var type3 = k + "s";
+        var type2 = k + "s";
         var osmIDs = groups[k].map(function(id2) {
           return osmEntity.id.toOSM(id2);
         });
         var options2 = { skipSeen: false };
         utilArrayChunk(osmIDs, 150).forEach(function(arr) {
-          that.loadFromAPI("/api/0.6/" + type3 + ".json?" + type3 + "=" + arr.join(), function(err, entities) {
-            if (callback)
-              callback(err, { data: entities });
-          }, options2);
+          that.loadFromAPI(
+            "/api/0.6/" + type2 + ".json?" + type2 + "=" + arr.join(),
+            function(err, entities) {
+              if (callback)
+                callback(err, { data: entities });
+            },
+            options2
+          );
         });
       });
     },
+    // Create, upload, and close a changeset
+    // PUT /api/0.6/changeset/create
+    // POST /api/0.6/changeset/#id/upload
+    // PUT /api/0.6/changeset/#id/close
     putChangeset: function(changeset, changes, callback) {
       var cid = _connectionID;
       if (_changeset.inflight) {
@@ -70065,7 +73315,10 @@ ${content}</tr>
           headers: { "Content-Type": "text/xml" },
           content: JXON.stringify(changeset.asJXON())
         };
-        _changeset.inflight = oauth.xhr(options2, wrapcb(this, createdChangeset, cid));
+        _changeset.inflight = oauth.xhr(
+          options2,
+          wrapcb(this, createdChangeset, cid)
+        );
       }
       function createdChangeset(err, changesetID) {
         _changeset.inflight = null;
@@ -70080,7 +73333,10 @@ ${content}</tr>
           headers: { "Content-Type": "text/xml" },
           content: JXON.stringify(changeset.osmChangeJXON(changes))
         };
-        _changeset.inflight = oauth.xhr(options3, wrapcb(this, uploadedChangeset, cid));
+        _changeset.inflight = oauth.xhr(
+          options3,
+          wrapcb(this, uploadedChangeset, cid)
+        );
       }
       function uploadedChangeset(err) {
         _changeset.inflight = null;
@@ -70101,6 +73357,9 @@ ${content}</tr>
         }
       }
     },
+    // Load multiple users in chunks
+    // (note: callback may be called multiple times)
+    // GET /api/0.6/users?users=#id1,#id2,...,#idn
     loadUsers: function(uids, callback) {
       var toLoad = [];
       var cached = [];
@@ -70118,7 +73377,10 @@ ${content}</tr>
           return;
       }
       utilArrayChunk(toLoad, 150).forEach(function(arr) {
-        oauth.xhr({ method: "GET", path: "/api/0.6/users.json?users=" + arr.join() }, wrapcb(this, done, _connectionID));
+        oauth.xhr(
+          { method: "GET", path: "/api/0.6/users.json?users=" + arr.join() },
+          wrapcb(this, done, _connectionID)
+        );
       }.bind(this));
       function done(err, payload) {
         if (err)
@@ -70131,12 +73393,17 @@ ${content}</tr>
         }, options2);
       }
     },
+    // Load a given user by id
+    // GET /api/0.6/user/#id
     loadUser: function(uid, callback) {
       if (_userCache.user[uid] || !this.authenticated()) {
         delete _userCache.toLoad[uid];
         return callback(void 0, _userCache.user[uid]);
       }
-      oauth.xhr({ method: "GET", path: "/api/0.6/user/" + uid + ".json" }, wrapcb(this, done, _connectionID));
+      oauth.xhr(
+        { method: "GET", path: "/api/0.6/user/" + uid + ".json" },
+        wrapcb(this, done, _connectionID)
+      );
       function done(err, payload) {
         if (err)
           return callback(err);
@@ -70148,11 +73415,16 @@ ${content}</tr>
         }, options2);
       }
     },
+    // Load the details of the logged-in user
+    // GET /api/0.6/user/details
     userDetails: function(callback) {
       if (_userDetails) {
         return callback(void 0, _userDetails);
       }
-      oauth.xhr({ method: "GET", path: "/api/0.6/user/details.json" }, wrapcb(this, done, _connectionID));
+      oauth.xhr(
+        { method: "GET", path: "/api/0.6/user/details.json" },
+        wrapcb(this, done, _connectionID)
+      );
       function done(err, payload) {
         if (err)
           return callback(err);
@@ -70165,30 +73437,42 @@ ${content}</tr>
         }, options2);
       }
     },
+    // Load previous changesets for the logged in user
+    // GET /api/0.6/changesets?user=#id
     userChangesets: function(callback) {
       if (_userChangesets) {
         return callback(void 0, _userChangesets);
       }
-      this.userDetails(wrapcb(this, gotDetails, _connectionID));
+      this.userDetails(
+        wrapcb(this, gotDetails, _connectionID)
+      );
       function gotDetails(err, user) {
         if (err) {
           return callback(err);
         }
-        oauth.xhr({ method: "GET", path: "/api/0.6/changesets?user=" + user.id }, wrapcb(this, done, _connectionID));
+        oauth.xhr(
+          { method: "GET", path: "/api/0.6/changesets?user=" + user.id },
+          wrapcb(this, done, _connectionID)
+        );
       }
       function done(err, xml) {
         if (err) {
           return callback(err);
         }
-        _userChangesets = Array.prototype.map.call(xml.getElementsByTagName("changeset"), function(changeset) {
-          return { tags: getTags(changeset) };
-        }).filter(function(changeset) {
+        _userChangesets = Array.prototype.map.call(
+          xml.getElementsByTagName("changeset"),
+          function(changeset) {
+            return { tags: getTags(changeset) };
+          }
+        ).filter(function(changeset) {
           var comment = changeset.tags.comment;
           return comment && comment !== "";
         });
         return callback(void 0, _userChangesets);
       }
     },
+    // Fetch the status of the OSM API
+    // GET /api/capabilities
     status: function(callback) {
       var url = urlroot + "/api/capabilities";
       var errback = wrapcb(this, done, _connectionID);
@@ -70229,6 +73513,8 @@ ${content}</tr>
         }
       }
     },
+    // Calls `status` and dispatches an `apiStatusChange` event if the returned
+    // status differs from the cached status.
     reloadApiStatus: function() {
       if (!this.throttledReloadApiStatus) {
         var that = this;
@@ -70243,9 +73529,12 @@ ${content}</tr>
       }
       this.throttledReloadApiStatus();
     },
+    // Returns the maximum number of nodes a single way can have
     maxWayNodes: function() {
       return _maxWayNodes;
     },
+    // Load data (entities) from the API in tiles
+    // GET /api/0.6/map?bbox=
     loadTiles: function(projection2, callback) {
       if (_off)
         return;
@@ -70259,6 +73548,8 @@ ${content}</tr>
         this.loadTile(tile, callback);
       }, this);
     },
+    // Load a single data tile
+    // GET /api/0.6/map?bbox=
     loadTile: function(tile, callback) {
       if (_off)
         return;
@@ -70269,15 +73560,19 @@ ${content}</tr>
       }
       var path = "/api/0.6/map.json?bbox=";
       var options2 = { skipSeen: true };
-      _tileCache.inflight[tile.id] = this.loadFromAPI(path + tile.extent.toParam(), tileCallback, options2);
+      _tileCache.inflight[tile.id] = this.loadFromAPI(
+        path + tile.extent.toParam(),
+        tileCallback,
+        options2
+      );
       function tileCallback(err, parsed) {
         delete _tileCache.inflight[tile.id];
         if (!err) {
           delete _tileCache.toLoad[tile.id];
           _tileCache.loaded[tile.id] = true;
-          var bbox = tile.extent.bbox();
-          bbox.id = tile.id;
-          _tileCache.rtree.insert(bbox);
+          var bbox2 = tile.extent.bbox();
+          bbox2.id = tile.id;
+          _tileCache.rtree.insert(bbox2);
         }
         if (callback) {
           callback(err, Object.assign({ data: parsed }, tile));
@@ -70288,9 +73583,10 @@ ${content}</tr>
       }
     },
     isDataLoaded: function(loc) {
-      var bbox = { minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1] };
-      return _tileCache.rtree.collides(bbox);
+      var bbox2 = { minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1] };
+      return _tileCache.rtree.collides(bbox2);
     },
+    // load the tile that covers the given `loc`
     loadTileAtLoc: function(loc, callback) {
       if (Object.keys(_tileCache.toLoad).length > 50)
         return;
@@ -70305,6 +73601,8 @@ ${content}</tr>
         this.loadTile(tile, callback);
       }, this);
     },
+    // Load notes from the API in tiles
+    // GET /api/0.6/notes?bbox=
     loadNotes: function(projection2, noteOptions) {
       noteOptions = Object.assign({ limit: 1e4, closed: 7 }, noteOptions);
       if (_off)
@@ -70324,16 +73622,22 @@ ${content}</tr>
         if (_noteCache.loaded[tile.id] || _noteCache.inflight[tile.id])
           return;
         var options2 = { skipSeen: false };
-        _noteCache.inflight[tile.id] = that.loadFromAPI(path + tile.extent.toParam(), function(err) {
-          delete _noteCache.inflight[tile.id];
-          if (!err) {
-            _noteCache.loaded[tile.id] = true;
-          }
-          throttleLoadUsers();
-          dispatch7.call("loadedNotes");
-        }, options2);
+        _noteCache.inflight[tile.id] = that.loadFromAPI(
+          path + tile.extent.toParam(),
+          function(err) {
+            delete _noteCache.inflight[tile.id];
+            if (!err) {
+              _noteCache.loaded[tile.id] = true;
+            }
+            throttleLoadUsers();
+            dispatch7.call("loadedNotes");
+          },
+          options2
+        );
       });
     },
+    // Create a note
+    // POST /api/0.6/notes?params
     postNoteCreate: function(note, callback) {
       if (!this.authenticated()) {
         return callback({ message: "Not Authenticated", status: -3 }, note);
@@ -70348,7 +73652,10 @@ ${content}</tr>
         comment += " #" + note.newCategory;
       }
       var path = "/api/0.6/notes?" + utilQsString({ lon: note.loc[0], lat: note.loc[1], text: comment });
-      _noteCache.inflightPost[note.id] = oauth.xhr({ method: "POST", path }, wrapcb(this, done, _connectionID));
+      _noteCache.inflightPost[note.id] = oauth.xhr(
+        { method: "POST", path },
+        wrapcb(this, done, _connectionID)
+      );
       function done(err, xml) {
         delete _noteCache.inflightPost[note.id];
         if (err) {
@@ -70365,6 +73672,10 @@ ${content}</tr>
         }, options2);
       }
     },
+    // Update a note
+    // POST /api/0.6/notes/#id/comment?text=comment
+    // POST /api/0.6/notes/#id/close?text=comment
+    // POST /api/0.6/notes/#id/reopen?text=comment
     postNoteUpdate: function(note, newStatus, callback) {
       if (!this.authenticated()) {
         return callback({ message: "Not Authenticated", status: -3 }, note);
@@ -70386,7 +73697,10 @@ ${content}</tr>
       if (note.newComment) {
         path += "?" + utilQsString({ text: note.newComment });
       }
-      _noteCache.inflightPost[note.id] = oauth.xhr({ method: "POST", path }, wrapcb(this, done, _connectionID));
+      _noteCache.inflightPost[note.id] = oauth.xhr(
+        { method: "POST", path },
+        wrapcb(this, done, _connectionID)
+      );
       function done(err, xml) {
         delete _noteCache.inflightPost[note.id];
         if (err) {
@@ -70408,6 +73722,13 @@ ${content}</tr>
         }, options2);
       }
     },
+    /* connection options for source switcher (optional) */
+    apiConnections: function(val) {
+      if (!arguments.length)
+        return _apiConnections;
+      _apiConnections = val;
+      return this;
+    },
     switch: function(newOptions) {
       urlroot = newOptions.url;
       var oldOptions = utilObjectOmit(oauth.options(), "access_token");
@@ -70425,6 +73746,9 @@ ${content}</tr>
     isChangesetInflight: function() {
       return !!_changeset.inflight;
     },
+    // get/set cached data
+    // This is used to save/restore the state when entering/exiting the walkthrough
+    // Also used for testing purposes.
     caches: function(obj) {
       function cloneCache(source) {
         var target = {};
@@ -70514,24 +73838,28 @@ ${content}</tr>
       _tileZoom4 = val;
       return this;
     },
+    // get all cached notes covering the viewport
     notes: function(projection2) {
       var viewport = projection2.clipExtent();
       var min3 = [viewport[0][0], viewport[1][1]];
       var max3 = [viewport[1][0], viewport[0][1]];
-      var bbox = geoExtent(projection2.invert(min3), projection2.invert(max3)).bbox();
-      return _noteCache.rtree.search(bbox).map(function(d) {
+      var bbox2 = geoExtent(projection2.invert(min3), projection2.invert(max3)).bbox();
+      return _noteCache.rtree.search(bbox2).map(function(d) {
         return d.data;
       });
     },
+    // get a single note from the cache
     getNote: function(id2) {
       return _noteCache.note[id2];
     },
+    // remove a single note from the cache
     removeNote: function(note) {
       if (!(note instanceof osmNote) || !note.id)
         return;
       delete _noteCache.note[note.id];
       updateRtree4(encodeNoteRtree(note), false);
     },
+    // replace a single note in the cache
     replaceNote: function(note) {
       if (!(note instanceof osmNote) || !note.id)
         return;
@@ -70539,6 +73867,8 @@ ${content}</tr>
       updateRtree4(encodeNoteRtree(note), true);
       return note;
     },
+    // Get an array of note IDs closed during this session.
+    // Used to populate `closed:note` changeset tag
     getClosedIDs: function() {
       return Object.keys(_noteCache.closed).sort();
     }
@@ -70579,6 +73909,12 @@ ${content}</tr>
       });
       _inflight2 = {};
     },
+    /**
+     * Get the best value for the property, or undefined if not found
+     * @param entity object from wikibase
+     * @param property string e.g. 'P4' for image
+     * @param langCode string e.g. 'fr' for French
+     */
     claimToValue: function(entity, property, langCode) {
       if (!entity.claims[property])
         return void 0;
@@ -70600,6 +73936,11 @@ ${content}</tr>
         return void 0;
       }
     },
+    /**
+     * Convert monolingual property into a key-value object (language -> value)
+     * @param entity object from wikibase
+     * @param property string e.g. 'P31' for monolingual wiki page title
+     */
     monolingualClaimToValueObj: function(entity, property) {
       if (!entity || !entity.claims[property])
         return void 0;
@@ -70613,6 +73954,14 @@ ${content}</tr>
       var result = value ? "Tag:" + key + "=" + value : "Key:" + key;
       return result.replace(/_/g, " ").trim();
     },
+    //
+    // Pass params object of the form:
+    // {
+    //   key: 'string',
+    //   value: 'string',
+    //   langCode: 'string'
+    // }
+    //
     getEntity: function(params, callback) {
       var doRequest = params.debounce ? debouncedRequest : request;
       var that = this;
@@ -70662,6 +74011,9 @@ ${content}</tr>
         languagefallback: 1,
         origin: "*",
         format: "json"
+        // There is an MW Wikibase API bug https://phabricator.wikimedia.org/T212069
+        // We shouldn't use v1 until it gets fixed, but should switch to it afterwards
+        // formatversion: 2,
       };
       var url = apibase3 + "?" + utilQsString(obj);
       doRequest(url, function(err, d) {
@@ -70699,6 +74051,22 @@ ${content}</tr>
         }
       });
     },
+    //
+    // Pass params object of the form:
+    // {
+    //   key: 'string',     // required
+    //   value: 'string'    // optional
+    // }
+    //
+    // Get an result object used to display tag documentation
+    // {
+    //   title:        'string',
+    //   description:  'string',
+    //   editURL:      'string',
+    //   imageURL:     'string',
+    //   wiki:         { title: 'string', text: 'string', url: 'string' }
+    // }
+    //
     getDocs: function(params, callback) {
       var that = this;
       var langCodes = _mainLocalizer.localeCodes().map(function(code) {
@@ -70716,20 +74084,20 @@ ${content}</tr>
           return;
         }
         var i2;
-        var description2;
+        var description;
         for (i2 in langCodes) {
           let code2 = langCodes[i2];
           if (entity.descriptions[code2] && entity.descriptions[code2].language === code2) {
-            description2 = entity.descriptions[code2];
+            description = entity.descriptions[code2];
             break;
           }
         }
-        if (!description2 && Object.values(entity.descriptions).length)
-          description2 = Object.values(entity.descriptions)[0];
+        if (!description && Object.values(entity.descriptions).length)
+          description = Object.values(entity.descriptions)[0];
         var result = {
           title: entity.title,
-          description: description2 ? description2.value : "",
-          descriptionLocaleCode: description2 ? description2.language : "",
+          description: description ? description.value : "",
+          descriptionLocaleCode: description ? description.language : "",
           editURL: "https://wiki.openstreetmap.org/wiki/" + entity.title
         };
         if (entity.claims) {
@@ -70910,7 +74278,7 @@ ${content}</tr>
       if (!bubbles)
         return;
       bubbles.shift();
-      const features2 = bubbles.map((bubble) => {
+      const features = bubbles.map((bubble) => {
         if (cache.points[bubble.id])
           return null;
         const loc = [bubble.lo, bubble.la];
@@ -70920,8 +74288,14 @@ ${content}</tr>
           ca: bubble.he,
           captured_at: bubble.cd,
           captured_by: "microsoft",
+          // nbn: bubble.nbn,
+          // pbn: bubble.pbn,
+          // ad: bubble.ad,
+          // rn: bubble.rn,
           pr: bubble.pr,
+          // previous
           ne: bubble.ne,
+          // next
           pano: true,
           sequenceKey: null
         };
@@ -70937,7 +74311,7 @@ ${content}</tr>
           data: d
         };
       }).filter(Boolean);
-      cache.rtree.load(features2);
+      cache.rtree.load(features);
       connectSequences();
       if (which === "bubbles") {
         dispatch8.call("loadedImages");
@@ -71420,12 +74794,18 @@ ${content}</tr>
     return quadKeys;
   }
   var streetside_default = {
+    /**
+     * init() initialize streetside.
+     */
     init: function() {
       if (!_ssCache) {
         this.reset();
       }
       this.event = utilRebind(this, dispatch8, "on");
     },
+    /**
+     * reset() reset the cache.
+     */
     reset: function() {
       if (_ssCache) {
         Object.values(_ssCache.bubbles.inflight).forEach(abortRequest6);
@@ -71435,6 +74815,9 @@ ${content}</tr>
         sequences: {}
       };
     },
+    /**
+     * bubbles()
+     */
     bubbles: function(projection2) {
       const limit = 5;
       return searchLimited3(limit, projection2, _ssCache.bubbles.rtree);
@@ -71446,10 +74829,10 @@ ${content}</tr>
       const viewport = projection2.clipExtent();
       const min3 = [viewport[0][0], viewport[1][1]];
       const max3 = [viewport[1][0], viewport[0][1]];
-      const bbox = geoExtent(projection2.invert(min3), projection2.invert(max3)).bbox();
+      const bbox2 = geoExtent(projection2.invert(min3), projection2.invert(max3)).bbox();
       let seen = {};
       let results = [];
-      _ssCache.bubbles.rtree.search(bbox).forEach((d) => {
+      _ssCache.bubbles.rtree.search(bbox2).forEach((d) => {
         const key = d.data.sequenceKey;
         if (key && !seen[key]) {
           seen[key] = true;
@@ -71458,6 +74841,9 @@ ${content}</tr>
       });
       return results;
     },
+    /**
+     * loadBubbles()
+     */
     loadBubbles: function(projection2, margin) {
       if (margin === void 0)
         margin = 2;
@@ -71557,8 +74943,8 @@ ${content}</tr>
           let poly = [p1, p2, p3, p4, p1];
           let angle2 = (stepBy === 1 ? ca : ca + 180) * (Math.PI / 180);
           poly = geoRotate(poly, -angle2, origin);
-          let extent = poly.reduce((extent2, point) => {
-            return extent2.extend(geoExtent(point));
+          let extent = poly.reduce((extent2, point2) => {
+            return extent2.extend(geoExtent(point2));
           }, geoExtent());
           let minDist = Infinity;
           _ssCache.bubbles.rtree.search(extent.bbox()).forEach((d) => {
@@ -71591,6 +74977,9 @@ ${content}</tr>
       _sceneOptions.yaw = yaw;
       return this;
     },
+    /**
+     * showViewer()
+     */
     showViewer: function(context) {
       let wrap2 = context.container().select(".photoviewer").classed("hide", false);
       let isHidden = wrap2.selectAll(".photo-wrapper.ms-wrapper.hide").size();
@@ -71600,6 +74989,9 @@ ${content}</tr>
       }
       return this;
     },
+    /**
+     * hideViewer()
+     */
     hideViewer: function(context) {
       let viewer = context.container().select(".photoviewer");
       if (!viewer.empty())
@@ -71609,6 +75001,9 @@ ${content}</tr>
       this.updateUrlImage(null);
       return this.setStyles(context, null, true);
     },
+    /**
+     * selectImage().
+     */
     selectImage: function(context, key) {
       let that = this;
       let d = this.cachedImage(key);
@@ -71690,6 +75085,9 @@ ${content}</tr>
     getSequenceKeyForBubble: function(d) {
       return d && d.sequenceKey;
     },
+    // Updates the currently highlighted sequence and selected bubble.
+    // Reset is only necessary when interacting with the viewport because
+    // this implicitly changes the currently selected bubble/sequence
     setStyles: function(context, hovered, reset) {
       if (reset) {
         context.container().selectAll(".viewfield-group").classed("highlighted", false).classed("hovered", false).classed("currentView", false);
@@ -71730,13 +75128,16 @@ ${content}</tr>
         window.location.replace("#" + utilQsString(hash, true));
       }
     },
+    /**
+     * cache().
+     */
     cache: function() {
       return _ssCache;
     }
   };
 
   // modules/services/taginfo.js
-  var apibase4 = "https://taginfo.openstreetmap.org/api/4/";
+  var apibase4 = taginfoApiUrl;
   var _inflight3 = {};
   var _popularKeys = {};
   var _taginfoCache = {};
@@ -71784,15 +75185,15 @@ ${content}</tr>
   function clean(params) {
     return utilObjectOmit(params, ["geometry", "debounce"]);
   }
-  function filterKeys(type3) {
-    var count_type = type3 ? "count_" + type3 : "count_all";
+  function filterKeys(type2) {
+    var count_type = type2 ? "count_" + type2 : "count_all";
     return function(d) {
-      return parseFloat(d[count_type]) > 2500 || d.in_wiki;
+      return Number(d[count_type]) > 2500 || d.in_wiki;
     };
   }
   function filterMultikeys(prefix) {
     return function(d) {
-      var re2 = new RegExp("^" + prefix + "(.*)$");
+      var re2 = new RegExp("^" + prefix + "(.*)$", "i");
       var matches = d.key.match(re2) || [];
       return matches.length === 2 && matches[1].indexOf(":") === -1;
     };
@@ -71803,7 +75204,7 @@ ${content}</tr>
         return false;
       if (!allowUpperCase && d.value.match(/[A-Z*]/) !== null)
         return false;
-      return parseFloat(d.fraction) > 0;
+      return d.count > 100 || d.in_wiki;
     };
   }
   function filterRoles(geometry) {
@@ -71812,7 +75213,7 @@ ${content}</tr>
         return false;
       if (d.role.match(/[A-Z*;,]/) !== null)
         return false;
-      return parseFloat(d[tag_members_fractions[geometry]]) > 0;
+      return Number(d[tag_members_fractions[geometry]]) > 0;
     };
   }
   function valKey(d) {
@@ -71826,9 +75227,6 @@ ${content}</tr>
       value: d.value,
       title: d.description || d.value
     };
-    if (d.count) {
-      obj.count = d.count;
-    }
     return obj;
   }
   function roleKey(d) {
@@ -71882,6 +75280,7 @@ ${content}</tr>
       _inflight3 = {};
       _taginfoCache = {};
       _popularKeys = {
+        // manually exclude some keys – #5377, #7485
         postal_code: true,
         full_name: true,
         loc_name: true,
@@ -71891,6 +75290,7 @@ ${content}</tr>
         artist_name: true,
         nat_name: true,
         long_name: true,
+        via: true,
         "bridge:name": true
       };
       var params = {
@@ -72043,248 +75443,6 @@ ${content}</tr>
 
   // modules/services/vector_tile.js
   var import_fast_deep_equal11 = __toESM(require_fast_deep_equal());
-
-  // node_modules/@turf/helpers/dist/es/index.js
-  var earthRadius = 63710088e-1;
-  var factors = {
-    centimeters: earthRadius * 100,
-    centimetres: earthRadius * 100,
-    degrees: earthRadius / 111325,
-    feet: earthRadius * 3.28084,
-    inches: earthRadius * 39.37,
-    kilometers: earthRadius / 1e3,
-    kilometres: earthRadius / 1e3,
-    meters: earthRadius,
-    metres: earthRadius,
-    miles: earthRadius / 1609.344,
-    millimeters: earthRadius * 1e3,
-    millimetres: earthRadius * 1e3,
-    nauticalmiles: earthRadius / 1852,
-    radians: 1,
-    yards: earthRadius * 1.0936
-  };
-  var unitsFactors = {
-    centimeters: 100,
-    centimetres: 100,
-    degrees: 1 / 111325,
-    feet: 3.28084,
-    inches: 39.37,
-    kilometers: 1 / 1e3,
-    kilometres: 1 / 1e3,
-    meters: 1,
-    metres: 1,
-    miles: 1 / 1609.344,
-    millimeters: 1e3,
-    millimetres: 1e3,
-    nauticalmiles: 1 / 1852,
-    radians: 1 / earthRadius,
-    yards: 1.0936133
-  };
-  function feature2(geom, properties, options2) {
-    if (options2 === void 0) {
-      options2 = {};
-    }
-    var feat = { type: "Feature" };
-    if (options2.id === 0 || options2.id) {
-      feat.id = options2.id;
-    }
-    if (options2.bbox) {
-      feat.bbox = options2.bbox;
-    }
-    feat.properties = properties || {};
-    feat.geometry = geom;
-    return feat;
-  }
-  function polygon(coordinates, properties, options2) {
-    if (options2 === void 0) {
-      options2 = {};
-    }
-    for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) {
-      var ring = coordinates_1[_i];
-      if (ring.length < 4) {
-        throw new Error("Each LinearRing of a Polygon must have 4 or more Positions.");
-      }
-      for (var j2 = 0; j2 < ring[ring.length - 1].length; j2++) {
-        if (ring[ring.length - 1][j2] !== ring[0][j2]) {
-          throw new Error("First and last Position are not equivalent.");
-        }
-      }
-    }
-    var geom = {
-      type: "Polygon",
-      coordinates
-    };
-    return feature2(geom, properties, options2);
-  }
-  function lineString(coordinates, properties, options2) {
-    if (options2 === void 0) {
-      options2 = {};
-    }
-    if (coordinates.length < 2) {
-      throw new Error("coordinates must be an array of two or more positions");
-    }
-    var geom = {
-      type: "LineString",
-      coordinates
-    };
-    return feature2(geom, properties, options2);
-  }
-  function multiLineString(coordinates, properties, options2) {
-    if (options2 === void 0) {
-      options2 = {};
-    }
-    var geom = {
-      type: "MultiLineString",
-      coordinates
-    };
-    return feature2(geom, properties, options2);
-  }
-  function multiPolygon(coordinates, properties, options2) {
-    if (options2 === void 0) {
-      options2 = {};
-    }
-    var geom = {
-      type: "MultiPolygon",
-      coordinates
-    };
-    return feature2(geom, properties, options2);
-  }
-
-  // node_modules/@turf/invariant/dist/es/index.js
-  function getGeom(geojson) {
-    if (geojson.type === "Feature") {
-      return geojson.geometry;
-    }
-    return geojson;
-  }
-
-  // node_modules/@turf/bbox-clip/dist/es/lib/lineclip.js
-  function lineclip(points, bbox, result) {
-    var len = points.length, codeA = bitCode(points[0], bbox), part = [], i2, codeB, lastCode;
-    var a;
-    var b;
-    if (!result)
-      result = [];
-    for (i2 = 1; i2 < len; i2++) {
-      a = points[i2 - 1];
-      b = points[i2];
-      codeB = lastCode = bitCode(b, bbox);
-      while (true) {
-        if (!(codeA | codeB)) {
-          part.push(a);
-          if (codeB !== lastCode) {
-            part.push(b);
-            if (i2 < len - 1) {
-              result.push(part);
-              part = [];
-            }
-          } else if (i2 === len - 1) {
-            part.push(b);
-          }
-          break;
-        } else if (codeA & codeB) {
-          break;
-        } else if (codeA) {
-          a = intersect(a, b, codeA, bbox);
-          codeA = bitCode(a, bbox);
-        } else {
-          b = intersect(a, b, codeB, bbox);
-          codeB = bitCode(b, bbox);
-        }
-      }
-      codeA = lastCode;
-    }
-    if (part.length)
-      result.push(part);
-    return result;
-  }
-  function polygonclip(points, bbox) {
-    var result, edge, prev, prevInside, i2, p, inside;
-    for (edge = 1; edge <= 8; edge *= 2) {
-      result = [];
-      prev = points[points.length - 1];
-      prevInside = !(bitCode(prev, bbox) & edge);
-      for (i2 = 0; i2 < points.length; i2++) {
-        p = points[i2];
-        inside = !(bitCode(p, bbox) & edge);
-        if (inside !== prevInside)
-          result.push(intersect(prev, p, edge, bbox));
-        if (inside)
-          result.push(p);
-        prev = p;
-        prevInside = inside;
-      }
-      points = result;
-      if (!points.length)
-        break;
-    }
-    return result;
-  }
-  function intersect(a, b, edge, bbox) {
-    return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : null;
-  }
-  function bitCode(p, bbox) {
-    var code = 0;
-    if (p[0] < bbox[0])
-      code |= 1;
-    else if (p[0] > bbox[2])
-      code |= 2;
-    if (p[1] < bbox[1])
-      code |= 4;
-    else if (p[1] > bbox[3])
-      code |= 8;
-    return code;
-  }
-
-  // node_modules/@turf/bbox-clip/dist/es/index.js
-  function bboxClip(feature3, bbox) {
-    var geom = getGeom(feature3);
-    var type3 = geom.type;
-    var properties = feature3.type === "Feature" ? feature3.properties : {};
-    var coords = geom.coordinates;
-    switch (type3) {
-      case "LineString":
-      case "MultiLineString": {
-        var lines_1 = [];
-        if (type3 === "LineString") {
-          coords = [coords];
-        }
-        coords.forEach(function(line) {
-          lineclip(line, bbox, lines_1);
-        });
-        if (lines_1.length === 1) {
-          return lineString(lines_1[0], properties);
-        }
-        return multiLineString(lines_1, properties);
-      }
-      case "Polygon":
-        return polygon(clipPolygon(coords, bbox), properties);
-      case "MultiPolygon":
-        return multiPolygon(coords.map(function(poly) {
-          return clipPolygon(poly, bbox);
-        }), properties);
-      default:
-        throw new Error("geometry " + type3 + " not supported");
-    }
-  }
-  function clipPolygon(rings, bbox) {
-    var outRings = [];
-    for (var _i = 0, rings_1 = rings; _i < rings_1.length; _i++) {
-      var ring = rings_1[_i];
-      var clipped = polygonclip(ring, bbox);
-      if (clipped.length > 0) {
-        if (clipped[0][0] !== clipped[clipped.length - 1][0] || clipped[0][1] !== clipped[clipped.length - 1][1]) {
-          clipped.push(clipped[0]);
-        }
-        if (clipped.length >= 4) {
-          outRings.push(clipped);
-        }
-      }
-    }
-    return outRings;
-  }
-
-  // modules/services/vector_tile.js
   var import_fast_json_stable_stringify2 = __toESM(require_fast_json_stable_stringify());
   var import_polygon_clipping2 = __toESM(require_polygon_clipping_umd());
   var import_pbf2 = __toESM(require_pbf());
@@ -72301,7 +75459,7 @@ ${content}</tr>
     if (!Array.isArray(layers)) {
       layers = [layers];
     }
-    var features2 = [];
+    var features = [];
     layers.forEach(function(layerID) {
       var layer = vectorTile.layers[layerID];
       if (layer) {
@@ -72328,12 +75486,15 @@ ${content}</tr>
           feature3.__layerID__ = layerID.replace(/[^_a-zA-Z0-9\-]/g, "_");
           feature3.__featurehash__ = featurehash;
           feature3.__propertyhash__ = propertyhash;
-          features2.push(feature3);
+          features.push(feature3);
           if (isClipped && geometry.type === "MultiPolygon") {
             var merged = mergeCache[propertyhash];
             if (merged && merged.length) {
               var other = merged[0];
-              var coords = import_polygon_clipping2.default.union(feature3.geometry.coordinates, other.geometry.coordinates);
+              var coords = import_polygon_clipping2.default.union(
+                feature3.geometry.coordinates,
+                other.geometry.coordinates
+              );
               if (!coords || !coords.length) {
                 continue;
               }
@@ -72349,7 +75510,7 @@ ${content}</tr>
         }
       }
     });
-    return features2;
+    return features;
   }
   function loadTile2(source, tile) {
     if (source.loaded[tile.id] || source.inflight[tile.id])
@@ -72410,11 +75571,11 @@ ${content}</tr>
       var seen = {};
       var results = [];
       for (var i2 = 0; i2 < tiles.length; i2++) {
-        var features2 = source.loaded[tiles[i2].id];
-        if (!features2 || !features2.length)
+        var features = source.loaded[tiles[i2].id];
+        if (!features || !features.length)
           continue;
-        for (var j2 = 0; j2 < features2.length; j2++) {
-          var feature3 = features2[j2];
+        for (var j2 = 0; j2 < features.length; j2++) {
+          var feature3 = features[j2];
           var hash = feature3.__featurehash__;
           if (seen[hash])
             continue;
@@ -72457,6 +75618,7 @@ ${content}</tr>
     reset: function() {
       _wikidataCache = {};
     },
+    // Search for Wikidata items matching the query
     itemsForSearchQuery: function(query, callback) {
       if (!query) {
         if (callback)
@@ -72470,7 +75632,9 @@ ${content}</tr>
         formatversion: 2,
         search: query,
         type: "item",
+        // the language to search
         language: lang,
+        // the language for the label and description in the result
         uselang: lang,
         limit: 10,
         origin: "*"
@@ -72486,6 +75650,8 @@ ${content}</tr>
           callback(err.message, {});
       });
     },
+    // Given a Wikipedia language and article title,
+    // return an array of corresponding Wikidata entities.
     itemsByTitle: function(lang, title, callback) {
       if (!title) {
         if (callback)
@@ -72500,6 +75666,7 @@ ${content}</tr>
         sites: lang.replace(/-/g, "_") + "wiki",
         titles: title,
         languages: "en",
+        // shrink response by filtering to one language
         origin: "*"
       });
       json_default(url).then(function(result) {
@@ -72555,6 +75722,20 @@ ${content}</tr>
           callback(err.message, {});
       });
     },
+    // Pass `params` object of the form:
+    // {
+    //   qid: 'string'      // brand wikidata  (e.g. 'Q37158')
+    // }
+    //
+    // Get an result object used to display tag documentation
+    // {
+    //   title:        'string',
+    //   description:  'string',
+    //   editURL:      'string',
+    //   imageURL:     'string',
+    //   wiki:         { title: 'string', text: 'string', url: 'string' }
+    // }
+    //
     getDocs: function(params, callback) {
       var langs = this.languagesToQuery();
       this.entityByQID(params.qid, function(err, entity) {
@@ -72563,20 +75744,20 @@ ${content}</tr>
           return;
         }
         var i2;
-        var description2;
+        var description;
         for (i2 in langs) {
           let code = langs[i2];
           if (entity.descriptions[code] && entity.descriptions[code].language === code) {
-            description2 = entity.descriptions[code];
+            description = entity.descriptions[code];
             break;
           }
         }
-        if (!description2 && Object.values(entity.descriptions).length)
-          description2 = Object.values(entity.descriptions)[0];
+        if (!description && Object.values(entity.descriptions).length)
+          description = Object.values(entity.descriptions)[0];
         var result = {
           title: entity.id,
-          description: description2 ? description2.value : "",
-          descriptionLocaleCode: description2 ? description2.language : "",
+          description: description ? description.value : "",
+          descriptionLocaleCode: description ? description.language : "",
           editURL: "https://www.wikidata.org/wiki/" + entity.id
         };
         if (entity.claims) {
@@ -72783,11 +75964,11 @@ ${content}</tr>
       context.enter(mode);
       context.selectedNoteID(_note.id);
     }
-    function move(d3_event, entity, point) {
+    function move(d3_event, entity, point2) {
       d3_event.stopPropagation();
-      _lastLoc = context.projection.invert(point);
+      _lastLoc = context.projection.invert(point2);
       doMove(d3_event);
-      var nudge = geoViewportEdge(point, context.map().dimensions());
+      var nudge = geoViewportEdge(point2, context.map().dimensions());
       if (nudge) {
         startNudge(d3_event, nudge);
       } else {
@@ -72897,7 +76078,8 @@ ${content}</tr>
         if (activeNode && (/* @__PURE__ */ new Set(["INPUT", "TEXTAREA"])).has(activeNode.nodeName))
           return;
       }
-      if (d3_event.keyCode === 93 || d3_event.keyCode === 32) {
+      if (d3_event.keyCode === 93 || // context menu key
+      d3_event.keyCode === 32) {
         d3_event.preventDefault();
       }
       if (d3_event.repeat)
@@ -72944,7 +76126,9 @@ ${content}</tr>
       if (d3_event.buttons && d3_event.buttons !== 1)
         return;
       context.ui().closeEditMenu();
-      _longPressTimeout = window.setTimeout(didLongPress, 500, id2, "longdown-" + (d3_event.pointerType || "mouse"));
+      if (d3_event.pointerType !== "mouse") {
+        _longPressTimeout = window.setTimeout(didLongPress, 500, id2, "longdown-" + (d3_event.pointerType || "mouse"));
+      }
       _downPointers[id2] = {
         firstEvent: d3_event,
         lastEvent: d3_event
@@ -73000,7 +76184,7 @@ ${content}</tr>
       d3_event.preventDefault();
       if (!+d3_event.clientX && !+d3_event.clientY) {
         if (_lastMouseEvent) {
-          d3_event.sourceEvent = _lastMouseEvent;
+          d3_event = _lastMouseEvent;
         } else {
           return;
         }
@@ -73032,7 +76216,10 @@ ${content}</tr>
           _downPointers[selectPointerInfo.pointerId].done = true;
         }
       }
-      var isMultiselect = context.mode().id === "select" && (lastEvent && lastEvent.shiftKey || context.surface().select(".lasso").node() || _multiselectionPointerId && !multiselectEntityId);
+      var isMultiselect = context.mode().id === "select" && // and shift key is down
+      (lastEvent && lastEvent.shiftKey || // or we're lasso-selecting
+      context.surface().select(".lasso").node() || // or a pointer is down over a selected feature
+      _multiselectionPointerId && !multiselectEntityId);
       processClick(targetDatum, isMultiselect, p2, multiselectEntityId);
       function mapContains(event) {
         var rect = mapNode.getBoundingClientRect();
@@ -73062,7 +76249,7 @@ ${content}</tr>
         return null;
       }
     }
-    function processClick(datum2, isMultiselect, point, alsoSelectId) {
+    function processClick(datum2, isMultiselect, point2, alsoSelectId) {
       var mode = context.mode();
       var showMenu = _showMenu;
       var interactionType = _lastInteractionType;
@@ -73115,7 +76302,7 @@ ${content}</tr>
       }
       context.ui().closeEditMenu();
       if (showMenu)
-        context.ui().showEditMenu(point, interactionType);
+        context.ui().showEditMenu(point2, interactionType);
       resetProperties();
     }
     function cancelLongPress() {
@@ -73175,9 +76362,12 @@ ${content}</tr>
     var _entities = selectedIDs.map(function(id2) {
       return context.graph().entity(id2);
     });
-    var _geometries = Object.assign({ line: [], vertex: [] }, utilArrayGroupBy(_entities, function(entity) {
-      return entity.geometry(context.graph());
-    }));
+    var _geometries = Object.assign(
+      { line: [], vertex: [] },
+      utilArrayGroupBy(_entities, function(entity) {
+        return entity.geometry(context.graph());
+      })
+    );
     var _vertex = _geometries.vertex.length && _geometries.vertex[0];
     function candidateWays() {
       return _vertex ? context.graph().parentWays(_vertex).filter(function(parent) {
@@ -73187,7 +76377,9 @@ ${content}</tr>
     var _candidates = candidateWays();
     var operation = function() {
       var candidate = _candidates[0];
-      context.enter(modeDrawLine(context, candidate.id, context.graph(), "line", candidate.affix(_vertex.id), true));
+      context.enter(
+        modeDrawLine(context, candidate.id, context.graph(), "line", candidate.affix(_vertex.id), true)
+      );
     };
     operation.relatedEntityIds = function() {
       return _candidates.length ? [_candidates[0].id] : [];
@@ -73205,14 +76397,14 @@ ${content}</tr>
     };
     operation.tooltip = function() {
       var disable = operation.disabled();
-      return disable ? _t("operations.continue." + disable) : _t("operations.continue.description");
+      return disable ? _t.append("operations.continue." + disable) : _t.append("operations.continue.description");
     };
     operation.annotation = function() {
       return _t("operations.continue.annotation.line");
     };
     operation.id = "continue";
     operation.keys = [_t("operations.continue.key")];
-    operation.title = _t("operations.continue.title");
+    operation.title = _t.append("operations.continue.title");
     operation.behavior = behaviorOperation(context).which(operation);
     return operation;
   }
@@ -73263,7 +76455,10 @@ ${content}</tr>
       var entities = ids.map(function(id2) {
         return graph.entity(id2);
       });
-      return Object.assign({ relation: [], way: [], node: [] }, utilArrayGroupBy(entities, "type"));
+      return Object.assign(
+        { relation: [], way: [], node: [] },
+        utilArrayGroupBy(entities, "type")
+      );
     }
     function getDescendants(id2, graph, descendants) {
       var entity = graph.entity(id2);
@@ -73302,7 +76497,7 @@ ${content}</tr>
     };
     operation.tooltip = function() {
       var disable = operation.disabled();
-      return disable ? _t("operations.copy." + disable, { n: selectedIDs.length }) : _t("operations.copy.description", { n: selectedIDs.length });
+      return disable ? _t.append("operations.copy." + disable, { n: selectedIDs.length }) : _t.append("operations.copy.description", { n: selectedIDs.length });
     };
     operation.annotation = function() {
       return _t("operations.copy.annotation", { n: selectedIDs.length });
@@ -73314,7 +76509,7 @@ ${content}</tr>
     };
     operation.id = "copy";
     operation.keys = [uiCmd("\u2318C")];
-    operation.title = _t("operations.copy.title");
+    operation.title = _t.append("operations.copy.title");
     operation.behavior = behaviorOperation(context).which(operation);
     return operation;
   }
@@ -73475,17 +76670,14 @@ ${content}</tr>
     };
     operation.tooltip = function() {
       var disable = operation.disabled();
-      if (disable) {
-        return _t("operations.disconnect." + disable);
-      }
-      return _t("operations.disconnect.description." + _descriptionID);
+      return disable ? _t.append("operations.disconnect." + disable) : _t.append("operations.disconnect.description." + _descriptionID);
     };
     operation.annotation = function() {
       return _t("operations.disconnect.annotation." + _annotationID);
     };
     operation.id = "disconnect";
     operation.keys = [_t("operations.disconnect.key")];
-    operation.title = _t("operations.disconnect.title");
+    operation.title = _t.append("operations.disconnect.title");
     operation.behavior = behaviorOperation(context).which(operation);
     return operation;
   }
@@ -73500,17 +76692,17 @@ ${content}</tr>
       _affectedFeatureCount = 0;
       for (var i2 in entityIds) {
         var entityID = entityIds[i2];
-        var type3 = downgradeTypeForEntityID(entityID);
-        if (type3) {
+        var type2 = downgradeTypeForEntityID(entityID);
+        if (type2) {
           _affectedFeatureCount += 1;
-          if (downgradeType && type3 !== downgradeType) {
-            if (downgradeType !== "generic" && type3 !== "generic") {
+          if (downgradeType && type2 !== downgradeType) {
+            if (downgradeType !== "generic" && type2 !== "generic") {
               downgradeType = "building_address";
             } else {
               downgradeType = "generic";
             }
           } else {
-            downgradeType = type3;
+            downgradeType = type2;
           }
         }
       }
@@ -73542,18 +76734,18 @@ ${content}</tr>
       context.perform(function(graph) {
         for (var i2 in selectedIDs) {
           var entityID = selectedIDs[i2];
-          var type3 = downgradeTypeForEntityID(entityID);
-          if (!type3)
+          var type2 = downgradeTypeForEntityID(entityID);
+          if (!type2)
             continue;
           var tags = Object.assign({}, graph.entity(entityID).tags);
           for (var key in tags) {
-            if (type3 === "address" && addressKeysToKeep.indexOf(key) !== -1)
+            if (type2 === "address" && addressKeysToKeep.indexOf(key) !== -1)
               continue;
-            if (type3 === "building") {
+            if (type2 === "building") {
               if (buildingKeysToKeep.indexOf(key) !== -1 || key.match(/^building:.{1,}/) || key.match(/^roof:.{1,}/))
                 continue;
             }
-            if (type3 !== "generic") {
+            if (type2 !== "generic") {
               if (key.match(/^addr:.{1,}/) || key.match(/^source:.{1,}/))
                 continue;
             }
@@ -73581,7 +76773,7 @@ ${content}</tr>
     };
     operation.tooltip = function() {
       var disable = operation.disabled();
-      return disable ? _t("operations.downgrade." + disable + "." + _multi) : _t("operations.downgrade.description." + _downgradeType);
+      return disable ? _t.append("operations.downgrade." + disable + "." + _multi) : _t.append("operations.downgrade.description." + _downgradeType);
     };
     operation.annotation = function() {
       var suffix;
@@ -73594,7 +76786,7 @@ ${content}</tr>
     };
     operation.id = "downgrade";
     operation.keys = [uiCmd("\u232B")];
-    operation.title = _t("operations.downgrade.title");
+    operation.title = _t.append("operations.downgrade.title");
     operation.behavior = behaviorOperation(context).which(operation);
     return operation;
   }
@@ -73651,9 +76843,9 @@ ${content}</tr>
     operation.tooltip = function() {
       var disableReason = operation.disabled();
       if (disableReason) {
-        return _t("operations.extract." + disableReason + "." + _amount);
+        return _t.append("operations.extract." + disableReason + "." + _amount);
       } else {
-        return _t("operations.extract.description." + _geometryID + "." + _amount);
+        return _t.append("operations.extract.description." + _geometryID + "." + _amount);
       }
     };
     operation.annotation = function() {
@@ -73661,7 +76853,7 @@ ${content}</tr>
     };
     operation.id = "extract";
     operation.keys = [_t("operations.extract.key")];
-    operation.title = _t("operations.extract.title");
+    operation.title = _t.append("operations.extract.title");
     operation.behavior = behaviorOperation(context).which(operation);
     return operation;
   }
@@ -73722,21 +76914,24 @@ ${content}</tr>
       var disabled = operation.disabled();
       if (disabled) {
         if (disabled === "conflicting_relations") {
-          return _t("operations.merge.conflicting_relations");
+          return _t.append("operations.merge.conflicting_relations");
         }
         if (disabled === "restriction" || disabled === "connectivity") {
-          return _t("operations.merge.damage_relation", { relation: _mainPresetIndex.item("type/" + disabled).name() });
+          return _t.append(
+            "operations.merge.damage_relation",
+            { relation: _mainPresetIndex.item("type/" + disabled).name() }
+          );
         }
-        return _t("operations.merge." + disabled);
+        return _t.append("operations.merge." + disabled);
       }
-      return _t("operations.merge.description");
+      return _t.append("operations.merge.description");
     };
     operation.annotation = function() {
       return _t("operations.merge.annotation", { n: selectedIDs.length });
     };
     operation.id = "merge";
     operation.keys = [_t("operations.merge.key")];
-    operation.title = _t("operations.merge.title");
+    operation.title = _t.append("operations.merge.title");
     operation.behavior = behaviorOperation(context).which(operation);
     return operation;
   }
@@ -73792,9 +76987,9 @@ ${content}</tr>
       var oldGraph = context.copyGraph();
       var ids = context.copyIDs();
       if (!ids.length) {
-        return _t("operations.paste.nothing_copied");
+        return _t.append("operations.paste.nothing_copied");
       }
-      return _t("operations.paste.description", { feature: utilDisplayLabel(oldGraph.entity(ids[0]), oldGraph), n: ids.length });
+      return _t.append("operations.paste.description", { feature: utilDisplayLabel(oldGraph.entity(ids[0]), oldGraph), n: ids.length });
     };
     operation.annotation = function() {
       var ids = context.copyIDs();
@@ -73802,7 +76997,7 @@ ${content}</tr>
     };
     operation.id = "paste";
     operation.keys = [uiCmd("\u2318V")];
-    operation.title = _t("operations.paste.title");
+    operation.title = _t.append("operations.paste.title");
     return operation;
   }
 
@@ -73854,7 +77049,7 @@ ${content}</tr>
       return false;
     };
     operation.tooltip = function() {
-      return _t("operations.reverse.description." + reverseTypeID());
+      return _t.append("operations.reverse.description." + reverseTypeID());
     };
     operation.annotation = function() {
       var acts = actions();
@@ -73862,7 +77057,7 @@ ${content}</tr>
     };
     operation.id = "reverse";
     operation.keys = [_t("operations.reverse.key")];
-    operation.title = _t("operations.reverse.title");
+    operation.title = _t.append("operations.reverse.title");
     operation.behavior = behaviorOperation(context).which(operation);
     return operation;
   }
@@ -73919,9 +77114,7 @@ ${content}</tr>
     };
     operation.tooltip = function() {
       var disable = operation.disabled();
-      if (disable)
-        return _t("operations.split." + disable);
-      return _t("operations.split.description." + _geometry + "." + _waysAmount + "." + _nodesAmount + "_node");
+      return disable ? _t.append("operations.split." + disable) : _t.append("operations.split.description." + _geometry + "." + _waysAmount + "." + _nodesAmount + "_node");
     };
     operation.annotation = function() {
       return _t("operations.split.annotation." + _geometry, { n: _ways.length });
@@ -73935,7 +77128,7 @@ ${content}</tr>
     };
     operation.id = "split";
     operation.keys = [_t("operations.split.key")];
-    operation.title = _t("operations.split.title");
+    operation.title = _t.append("operations.split.title");
     operation.behavior = behaviorOperation(context).which(operation);
     return operation;
   }
@@ -74039,14 +77232,14 @@ ${content}</tr>
     };
     operation.tooltip = function() {
       var disable = operation.disabled();
-      return disable ? _t("operations.straighten." + disable + "." + _amount) : _t("operations.straighten.description." + _geometry + (_wayIDs.length === 1 ? "" : "s"));
+      return disable ? _t.append("operations.straighten." + disable + "." + _amount) : _t.append("operations.straighten.description." + _geometry + (_wayIDs.length === 1 ? "" : "s"));
     };
     operation.annotation = function() {
       return _t("operations.straighten.annotation." + _geometry, { n: _wayIDs.length ? _wayIDs.length : _nodeIDs.length });
     };
     operation.id = "straighten";
     operation.keys = [_t("operations.straighten.key")];
-    operation.title = _t("operations.straighten.title");
+    operation.title = _t.append("operations.straighten.title");
     operation.behavior = behaviorOperation(context).which(operation);
     return operation;
   }
@@ -74190,6 +77383,7 @@ ${content}</tr>
       }).filter(function(o) {
         return o.id !== "delete" && o.id !== "downgrade" && o.id !== "copy";
       }).concat([
+        // group copy/downgrade/delete operation together at the end of the list
         operationCopy(context, selectedIDs),
         operationDowngrade(context, selectedIDs),
         operationDelete(context, selectedIDs)
@@ -74256,7 +77450,7 @@ ${content}</tr>
             return;
           var moveOp = operationMove(context, selectedIDs);
           if (moveOp.disabled()) {
-            context.ui().flash.duration(4e3).iconName("#iD-operation-" + moveOp.id).iconClass("operation disabled").label(moveOp.tooltip)();
+            context.ui().flash.duration(4e3).iconName("#iD-operation-" + moveOp.id).iconClass("operation disabled").label(moveOp.tooltip())();
           } else {
             context.perform(actionMove(selectedIDs, delta, context.projection), moveOp.annotation());
             context.validator().validate();
@@ -74315,7 +77509,7 @@ ${content}</tr>
           const disabled = scalingDisabled();
           if (disabled) {
             let multi = selectedIDs.length === 1 ? "single" : "multiple";
-            context.ui().flash.duration(4e3).iconName("#iD-icon-no").iconClass("operation disabled").label(_t.html("operations.scale." + disabled + "." + multi))();
+            context.ui().flash.duration(4e3).iconName("#iD-icon-no").iconClass("operation disabled").label(_t.append("operations.scale." + disabled + "." + multi))();
           } else {
             const pivot = context.projection(extent2.center());
             const annotation = _t("operations.scale.annotation." + (isUp ? "up" : "down") + ".feature", { n: selectedIDs.length });
@@ -74336,10 +77530,16 @@ ${content}</tr>
           var choice = geoChooseEdge(context.graph().childNodes(entity), loc2, context.projection);
           var prev = entity.nodes[choice.index - 1];
           var next = entity.nodes[choice.index];
-          context.perform(actionAddMidpoint({ loc: choice.loc, edge: [prev, next] }, osmNode()), _t("operations.add.annotation.vertex"));
+          context.perform(
+            actionAddMidpoint({ loc: choice.loc, edge: [prev, next] }, osmNode()),
+            _t("operations.add.annotation.vertex")
+          );
           context.validator().validate();
         } else if (entity.type === "midpoint") {
-          context.perform(actionAddMidpoint({ loc: entity.loc, edge: entity.edge }, osmNode()), _t("operations.add.annotation.vertex"));
+          context.perform(
+            actionAddMidpoint({ loc: entity.loc, edge: entity.edge }, osmNode()),
+            _t("operations.add.annotation.vertex")
+          );
           context.validator().validate();
         }
       }
@@ -74355,7 +77555,12 @@ ${content}</tr>
           surface.selectAll(utilEntitySelector([_focusedParentWayId])).classed("related", true);
         }
         if (context.map().withinEditableZoom()) {
-          surface.selectAll(utilDeepMemberSelector(selectedIDs, context.graph(), true)).classed("selected-member", true);
+          surface.selectAll(utilDeepMemberSelector(
+            selectedIDs,
+            context.graph(),
+            true
+            /* skipMultipolgonMembers */
+          )).classed("selected-member", true);
           surface.selectAll(utilEntityOrDeepMemberSelector(selectedIDs, context.graph())).classed("selected", true);
         }
       }
@@ -74376,7 +77581,9 @@ ${content}</tr>
         }
         _focusedParentWayId = way && way.id;
         if (way) {
-          context.enter(mode.selectedIDs([way.first()]).follow(true));
+          context.enter(
+            mode.selectedIDs([way.first()]).follow(true)
+          );
         }
       }
       function lastVertex(d3_event) {
@@ -74391,7 +77598,9 @@ ${content}</tr>
         }
         _focusedParentWayId = way && way.id;
         if (way) {
-          context.enter(mode.selectedIDs([way.last()]).follow(true));
+          context.enter(
+            mode.selectedIDs([way.last()]).follow(true)
+          );
         }
       }
       function previousVertex(d3_event) {
@@ -74410,7 +77619,9 @@ ${content}</tr>
           index = length - 2;
         }
         if (index !== -1) {
-          context.enter(mode.selectedIDs([way.nodes[index]]).follow(true));
+          context.enter(
+            mode.selectedIDs([way.nodes[index]]).follow(true)
+          );
         }
       }
       function nextVertex(d3_event) {
@@ -74429,7 +77640,9 @@ ${content}</tr>
           index = 0;
         }
         if (index !== -1) {
-          context.enter(mode.selectedIDs([way.nodes[index]]).follow(true));
+          context.enter(
+            mode.selectedIDs([way.nodes[index]]).follow(true)
+          );
         }
       }
       function focusNextParent(d3_event) {
@@ -74455,7 +77668,9 @@ ${content}</tr>
         var parentIds = _focusedParentWayId ? [_focusedParentWayId] : parentWaysIdsOfSelection(false);
         if (!parentIds.length)
           return;
-        context.enter(mode.selectedIDs(parentIds));
+        context.enter(
+          mode.selectedIDs(parentIds)
+        );
         _focusedVertexIds = currentSelectedIds;
       }
       function selectChild(d3_event) {
@@ -74466,7 +77681,9 @@ ${content}</tr>
           return;
         if (currentSelectedIds.length === 1)
           _focusedParentWayId = currentSelectedIds[0];
-        context.enter(mode.selectedIDs(childIds));
+        context.enter(
+          mode.selectedIDs(childIds)
+        );
       }
     };
     mode.exit = function() {
@@ -74491,8 +77708,15 @@ ${content}</tr>
       context.ui().sidebar.hide();
       context.features().forceVisible([]);
       var entity = singular();
-      if (_newFeature && entity && entity.type === "relation" && Object.keys(entity.tags).length === 0 && context.graph().parentRelations(entity).length === 0 && (entity.members.length === 0 || entity.members.length === 1 && !entity.members[0].role)) {
-        var deleteAction = actionDeleteRelation(entity.id, true);
+      if (_newFeature && entity && entity.type === "relation" && // no tags
+      Object.keys(entity.tags).length === 0 && // no parent relations
+      context.graph().parentRelations(entity).length === 0 && // no members or one member with no role
+      (entity.members.length === 0 || entity.members.length === 1 && !entity.members[0].role)) {
+        var deleteAction = actionDeleteRelation(
+          entity.id,
+          true
+          /* don't delete untagged members */
+        );
         context.perform(deleteAction, _t("operations.delete.annotation.relation"));
         context.validator().validate();
       }
@@ -74531,7 +77755,10 @@ ${content}</tr>
           return [];
         var graph = context.graph();
         var limitToNodes;
-        if (context.map().editableDataEnabled(true) && context.map().isInWideSelection()) {
+        if (context.map().editableDataEnabled(
+          true
+          /* skipZoomCheck */
+        ) && context.map().isInWideSelection()) {
           limitToNodes = new Set(utilGetAllNodes(context.selectedIDs(), graph));
         } else if (!context.map().editableDataEnabled()) {
           return [];
@@ -74550,7 +77777,7 @@ ${content}</tr>
               var sharedParentNodes = sharedParents[0].nodes;
               return sharedParentNodes.indexOf(node1.id) - sharedParentNodes.indexOf(node2.id);
             } else {
-              return parseFloat(parents1[0].id.slice(1)) - parseFloat(parents2[0].id.slice(1));
+              return Number(parents1[0].id.slice(1)) - Number(parents2[0].id.slice(1));
             }
           } else if (parents1.length || parents2.length) {
             return parents1.length - parents2.length;
@@ -74584,8 +77811,8 @@ ${content}</tr>
     var mode = {
       button: "browse",
       id: "browse",
-      title: _t("modes.browse.title"),
-      description: _t("modes.browse.description")
+      title: _t.append("modes.browse.title"),
+      description: _t.append("modes.browse.description")
     };
     var sidebar;
     var _selectBehavior;
@@ -74674,7 +77901,10 @@ ${content}</tr>
       var center = map2.center();
       var zoom = map2.zoom();
       var precision2 = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
-      var oldParams = utilObjectOmit(utilStringQs(window.location.hash), ["comment", "source", "hashtags", "walkthrough"]);
+      var oldParams = utilObjectOmit(
+        utilStringQs(window.location.hash),
+        ["comment", "source", "hashtags", "walkthrough"]
+      );
       var newParams = {};
       delete oldParams.id;
       var selected = context.selectedIDs().filter(function(id2) {
@@ -74738,13 +77968,26 @@ ${content}</tr>
       var latestHash = computedHash();
       if (_cachedHash !== latestHash) {
         _cachedHash = latestHash;
-        window.history.replaceState(null, computedTitle(false), latestHash);
-        updateTitle(true);
+        window.history.replaceState(null, computedTitle(
+          false
+          /* includeChangeCount */
+        ), latestHash);
+        updateTitle(
+          true
+          /* includeChangeCount */
+        );
+        const q = utilStringQs(latestHash);
+        if (q.map) {
+          corePreferences("map-location", q.map);
+        }
       }
     }
     var _throttledUpdate = throttle_default(updateHashIfNeeded, 500);
     var _throttledUpdateTitle = throttle_default(function() {
-      updateTitle(true);
+      updateTitle(
+        true
+        /* includeChangeCount */
+      );
     }, 500);
     function hashchange() {
       if (window.location.hash === _cachedHash)
@@ -74782,20 +78025,23 @@ ${content}</tr>
       context.history().on("change.behaviorHash", _throttledUpdateTitle);
       context.on("enter.behaviorHash", _throttledUpdate);
       select_default2(window).on("hashchange.behaviorHash", hashchange);
-      if (window.location.hash) {
-        var q = utilStringQs(window.location.hash);
-        if (q.id) {
-          context.zoomToEntity(q.id.split(",")[0], !q.map);
-        }
-        if (q.walkthrough === "true") {
-          behavior.startWalkthrough = true;
-        }
-        if (q.map) {
-          behavior.hadHash = true;
-        }
-        hashchange();
-        updateTitle(false);
+      var q = utilStringQs(window.location.hash);
+      if (q.id) {
+        context.zoomToEntity(q.id.split(",")[0], !q.map);
+      }
+      if (q.walkthrough === "true") {
+        behavior.startWalkthrough = true;
+      }
+      if (q.map) {
+        behavior.hadLocation = true;
+      } else if (!q.id && corePreferences("map-location")) {
+        const mapArgs = corePreferences("map-location").split("/").map(Number);
+        context.map().centerZoom([mapArgs[2], Math.min(_latitudeLimit, Math.max(-_latitudeLimit, mapArgs[1]))], mapArgs[0]);
+        updateHashIfNeeded();
+        behavior.hadLocation = true;
       }
+      hashchange();
+      updateTitle(false);
     }
     behavior.off = function() {
       _throttledUpdate.cancel();
@@ -74818,7 +78064,7 @@ ${content}</tr>
   }
   var X = {
     name: "x",
-    handles: ["w", "e"].map(type2),
+    handles: ["w", "e"].map(type),
     input: function(x, e) {
       return x == null ? null : [[+x[0], e[0][1]], [+x[1], e[1][1]]];
     },
@@ -74828,7 +78074,7 @@ ${content}</tr>
   };
   var Y = {
     name: "y",
-    handles: ["n", "s"].map(type2),
+    handles: ["n", "s"].map(type),
     input: function(y, e) {
       return y == null ? null : [[e[0][0], +y[0]], [e[1][0], +y[1]]];
     },
@@ -74838,7 +78084,7 @@ ${content}</tr>
   };
   var XY = {
     name: "xy",
-    handles: ["n", "w", "e", "s", "nw", "ne", "sw", "se"].map(type2),
+    handles: ["n", "w", "e", "s", "nw", "ne", "sw", "se"].map(type),
     input: function(xy) {
       return xy == null ? null : number22(xy);
     },
@@ -74846,7 +78092,7 @@ ${content}</tr>
       return xy;
     }
   };
-  function type2(t) {
+  function type(t) {
     return { type: t };
   }