]> git.openstreetmap.org Git - rails.git/blobdiff - vendor/assets/iD/iD.js
Update to iD v2.18.5
[rails.git] / vendor / assets / iD / iD.js
index 6cc20080812c6fa2d2e2ee2ac2490ada2140c9ab..c3bc0c3eb9db668432873791dbf016cfce1bf2ec 100644 (file)
@@ -1,6 +1,10 @@
 (function () {
        var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
 
+       function getDefaultExportFromCjs (x) {
+               return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
+       }
+
        function createCommonjsModule(fn, basedir, module) {
                return module = {
                  path: basedir,
                }, fn(module, module.exports), module.exports;
        }
 
-       function getCjsExportFromNamespace (n) {
-               return n && n['default'] || n;
-       }
-
        function commonjsRequire () {
                throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
        }
                if (_loadPromise) { return _loadPromise; }
 
                return _loadPromise = Promise.all([
-                       // load the list of langauges
+                       // load the list of languages
                        _mainFileFetcher.get('languages'),
                        // load the list of supported locales
                        _mainFileFetcher.get('locales')
 
            localizer.languageName = function (code, options) {
 
-               if (_languageNames[code]) {  // name in locale langauge
+               if (_languageNames[code]) {  // name in locale language
                  // e.g. "German"
                  return _languageNames[code];
                }
                    return localizer.t('translate.language_and_code', { language: langInfo.nativeName, code: code });
 
                  } else if (langInfo.base && langInfo.script) {
-                   var base = langInfo.base;   // the code of the langauge this is based on
+                   var base = langInfo.base;   // the code of the language this is based on
 
-                   if (_languageNames[base]) {   // base language name in locale langauge
+                   if (_languageNames[base]) {   // base language name in locale language
                      var scriptCode = langInfo.script;
                      var script = _scriptNames[scriptCode] || scriptCode;
                      // e.g. "Serbian (Cyrillic)"
            if (_this.suggestion) {
              var path = presetID.split('/');
              path.pop();  // remove brand name
-             // NOTE: insert an en-dash, not a hypen (to avoid conflict with fr - nl names in Brussels etc)
+             // NOTE: insert an en-dash, not a hyphen (to avoid conflict with fr - nl names in Brussels etc)
              return _this.originalName + ' – ' + _t('presets.presets.' + path.join('/') + '.name');
            }
            return _this.t('name', { 'default': _this.originalName });
          };
 
 
-         _this.reference = function (geom) {
+         _this.reference = function () {
            // Lookup documentation on Wikidata...
            var qid = _this.tags.wikidata || _this.tags['brand:wikidata'] || _this.tags['operator:wikidata'];
            if (qid) {
            var key = _this.originalReference.key || Object.keys(utilObjectOmit(_this.tags, 'name'))[0];
            var value = _this.originalReference.value || _this.tags[key];
 
-           if (geom === 'relation' && key === 'type') {
-             if (value in _this.tags) {
-               key = value;
-               value = _this.tags[key];
-             } else {
-               return { rtype: value };
-             }
-           }
-
            if (value === '*') {
              return { key: key };
            } else {
                var sign = d3_polygonArea(points) > 0 ? 1 : -1;
                var ids, i, j, k;
 
-               // we need atleast two key nodes for the algorithm to work
+               // we need at least two key nodes for the algorithm to work
                if (!keyNodes.length) {
                    keyNodes = [nodes[0]];
                    keyPoints = [points[0]];
                }
 
                // key points and nodes are those connected to the ways,
-               // they are projected onto the circle, inbetween nodes are moved
-               // to constant intervals between key nodes, extra inbetween nodes are
+               // they are projected onto the circle, in between nodes are moved
+               // to constant intervals between key nodes, extra in between nodes are
                // added if necessary.
                for (i = 0; i < keyPoints.length; i++) {
                    var nextKeyNodeIndex = (i + 1) % keyNodes.length;
                        graph = graph.replace(node);
                    }
 
-                   // add new inbetween nodes if necessary
+                   // add new in between nodes if necessary
                    for (j = 0; j < numberNewPoints; j++) {
                        angle = startAngle + (indexRange + j) * eachAngle;
                        loc = projection.invert([
 
                    // Check for other ways that share these keyNodes..
                    // If keyNodes are adjacent in both ways,
-                   // we can add inBetween nodes to that shared way too..
+                   // we can add inBetweenNodes to that shared way too..
                    if (indexRange === 1 && inBetweenNodes.length) {
                        var startIndex1 = way.nodes.lastIndexOf(startNode.id);
                        var endIndex1 = way.nodes.lastIndexOf(endNode.id);
 
            var action = function(graph) {
                ids.forEach(function(id) {
-                   if (graph.hasEntity(id)) { // It may have been deleted aready.
+                   if (graph.hasEntity(id)) { // It may have been deleted already.
                        graph = actions[graph.entity(id).type](id)(graph);
                    }
                });
          };
        }
 
-       // Disconect the ways at the given node.
+       // Disconnect the ways at the given node.
        //
        // Optionally, disconnect only the given ways.
        //
                    extractedLoc = entity.extent(graph).center();
                }
 
-               var isBuilding = entity.tags.building && entity.tags.building !== 'no';
+               var indoorAreaValues = {
+                   area: true,
+                   corridor: true,
+                   elevator: true,
+                   level: true,
+                   room: true
+               };
+
+               var isBuilding = (entity.tags.building && entity.tags.building !== 'no') ||
+                   (entity.tags['building:part'] && entity.tags['building:part'] !== 'no');
+
+               var isIndoorArea = fromGeometry === 'area' && entity.tags.indoor && indoorAreaValues[entity.tags.indoor];
 
                var entityTags = Object.assign({}, entity.tags);  // shallow copy
                var pointTags = {};
                            key.match(/^building:.{1,}/) ||
                            key.match(/^roof:.{1,}/)) { continue; }
                    }
+                   // leave `indoor` tag on the area
+                   if (isIndoorArea && key === 'indoor') {
+                       continue;
+                   }
 
                    // copy the tag from the entity to the point
                    pointTags[key] = entityTags[key];
                    if (keysToCopyAndRetain.indexOf(key) !== -1 ||
                        key.match(/^addr:.{1,}/)) {
                        continue;
+                   } else if (isIndoorArea && key === 'level') {
+                       // leave `level` on both features
+                       continue;
                    }
 
                    // remove the tag from the entity
                    delete entityTags[key];
                }
 
-               if (!isBuilding && fromGeometry === 'area') {
+               if (!isBuilding && !isIndoorArea && fromGeometry === 'area') {
                    // ensure that areas keep area geometry
                    entityTags.area = 'yes';
                }
        QAItem.prototype.update = function update (props) {
            var this$1 = this;
 
-         // You can't override this inital information
+         // You can't override this initial information
          var ref = this;
            var loc = ref.loc;
            var service = ref.service;
                // prevent operations during low zoom selection
                if (!context.map().withinEditableZoom()) { return; }
 
+               if (_operation.availableForKeypress && !_operation.availableForKeypress()) { return; }
+
                event.preventDefault();
+
                var disabled = _operation.disabled();
 
                if (disabled) {
 
 
            function behavior(selection) {
-               _pointerId = null;
                var matchesSelector = utilPrefixDOMProperty('matchesSelector');
                var delegate = pointerdown;
 
                        for (k = 0; k < rings.length; k++) {
                            if (k === activeIndex) { continue; }
 
-                           // make sure active ring doesnt cross passive rings
+                           // make sure active ring doesn't cross passive rings
                            if (geoHasLineIntersections(rings[activeIndex].nodes, rings[k].nodes, entity.id)) {
                                return 'multipolygonRing';
                            }
          return _t(("QA.improveOSM.directions." + (compass[dir])));
        }
 
-       // Errors shouldn't obscure eachother
+       // Errors shouldn't obscure each other
        function preventCoincident(loc, bumpUp) {
          var coincident = false;
          do {
          }
        }
 
-       // Issues shouldn't obscure eachother
+       // Issues shouldn't obscure each other
        function preventCoincident$1(loc) {
          var coincident = false;
          do {
                //
                // There is some logic here to batch up clicks into a _mlyClicks array
                // because the user might click on a lot of markers quickly and nodechanged
-               // may be called out of order asychronously.
+               // may be called out of order asynchronously.
                //
                // Clicks are added to the array in `selectedImage` and removed here.
                //
                                return x.join('=');
                            }).join(','),
                        popup = window.open('about:blank', 'oauth_window', settings);
+
+                   oauth.popupWindow = popup;
+
+                   if (!popup) {
+                       var error = new Error('Popup was blocked');
+                       error.status = 'popup-blocked';
+                       throw error;
+                   }
                }
 
                // Request a request token. When this is complete, the popup
                }
            };
 
+           oauth.bringPopupWindowToFront = function() {
+               var brougtPopupToFront = false;
+               try {
+                   // This may cause a cross-origin error:
+                   // `DOMException: Blocked a frame with origin "..." from accessing a cross-origin frame.`
+                   if (oauth.popupWindow && !oauth.popupWindow.closed) {
+                       oauth.popupWindow.focus();
+                       brougtPopupToFront = true;
+                   }
+               } catch (err) {
+                   // Bringing popup window to front failed (probably because of the cross-origin error mentioned above)
+               }
+               return brougtPopupToFront;
+           };
+
            oauth.bootstrapToken = function(oauth_token, callback) {
                // ## Getting an request token
                // At this point we have an `oauth_token`, brought in from a function
            loading: authLoading,
            done: authDone
        });
-
-       var _blacklists = ['.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*'];
+       // hardcode default block of Google Maps
+       var _imageryBlocklists = [/.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/];
        var _tileCache = { toLoad: {}, loaded: {}, inflight: {}, seen: {}, rtree: new RBush() };
        var _noteCache = { toLoad: {}, loaded: {}, inflight: {}, inflightPost: {}, note: {}, closed: {}, rtree: new RBush() };
        var _userCache = { toLoad: {}, user: {} };
                return new osmNode({
                    id:  uid,
                    visible: typeof obj.visible === 'boolean' ? obj.visible : true,
-                   version: obj.version.toString(),
-                   changeset: obj.changeset.toString(),
+                   version: obj.version && obj.version.toString(),
+                   changeset: obj.changeset && obj.changeset.toString(),
                    timestamp: obj.timestamp,
                    user: obj.user,
-                   uid: obj.uid.toString(),
+                   uid: obj.uid && obj.uid.toString(),
                    loc: [parseFloat(obj.lon), parseFloat(obj.lat)],
                    tags: obj.tags
                });
                return new osmWay({
                    id:  uid,
                    visible: typeof obj.visible === 'boolean' ? obj.visible : true,
-                   version: obj.version.toString(),
-                   changeset: obj.changeset.toString(),
+                   version: obj.version && obj.version.toString(),
+                   changeset: obj.changeset && obj.changeset.toString(),
                    timestamp: obj.timestamp,
                    user: obj.user,
-                   uid: obj.uid.toString(),
+                   uid: obj.uid && obj.uid.toString(),
                    tags: obj.tags,
                    nodes: getNodesJSON(obj)
                });
                return new osmRelation({
                    id:  uid,
                    visible: typeof obj.visible === 'boolean' ? obj.visible : true,
-                   version: obj.version.toString(),
-                   changeset: obj.changeset.toString(),
+                   version: obj.version && obj.version.toString(),
+                   changeset: obj.changeset && obj.changeset.toString(),
                    timestamp: obj.timestamp,
                    user: obj.user,
-                   uid: obj.uid.toString(),
+                   uid: obj.uid && obj.uid.toString(),
                    tags: obj.tags,
                    members: getMembersJSON(obj)
                });
                        return callback(err, null);
                    }
 
-                   // update blacklists
+                   // update blocklists
                    var elements = xml.getElementsByTagName('blacklist');
                    var regexes = [];
                    for (var i = 0; i < elements.length; i++) {
-                       var regex = elements[i].getAttribute('regex');  // needs unencode?
-                       if (regex) {
-                           regexes.push(regex);
+                       var regexString = elements[i].getAttribute('regex');  // needs unencode?
+                       if (regexString) {
+                           try {
+                               var regex = new RegExp(regexString);
+                               regexes.push(regex);
+                           } catch (e) {
+                               /* noop */
+                           }
                        }
                    }
                    if (regexes.length) {
-                       _blacklists = regexes;
+                       _imageryBlocklists = regexes;
                    }
 
                    if (_rateLimitError) {
            // Calls `status` and dispatches an `apiStatusChange` event if the returned
            // status differs from the cached status.
            reloadApiStatus: function() {
-               // throttle to avoid unncessary API calls
+               // throttle to avoid unnecessary API calls
                if (!this.throttledReloadApiStatus) {
                    var that = this;
                    this.throttledReloadApiStatus = throttle(function() {
            },
 
 
-           imageryBlacklists: function() {
-               return _blacklists;
+           imageryBlocklists: function() {
+               return _imageryBlocklists;
            },
 
 
            // {
            //   key: 'string',
            //   value: 'string',
-           //   rtype: 'string',
            //   langCode: 'string'
            // }
            //
                var that = this;
                var titles = [];
                var result = {};
-               var rtypeSitelink = params.rtype ? ('Relation:' + params.rtype).replace(/_/g, ' ').trim() : false;
+               var rtypeSitelink = (params.key === 'type' && params.value) ? ('Relation:' + params.value).replace(/_/g, ' ').trim() : false;
                var keySitelink = params.key ? this.toSitelink(params.key) : false;
                var tagSitelink = (params.key && params.value) ? this.toSitelink(params.key, params.value) : false;
                var localeSitelink;
            //   key: 'string',     // required
            //   value: 'string'    // optional
            // }
-           //   -or-
-           // {
-           //   rtype: 'rtype'     // relation type  (e.g. 'multipolygon')
-           // }
            //
            // Get an result object used to display tag documentation
            // {
        }
        });
 
+       var turf_bboxClip = /*@__PURE__*/getDefaultExportFromCjs(bboxClip_1);
+
        var fastJsonStableStringify = function (data, opts) {
            if (!opts) { opts = {}; }
            if (typeof opts === 'function') { opts = { cmp: opts }; }
                        // Clip to tile bounds
                        if (geometry.type === 'MultiPolygon') {
                            var isClipped = false;
-                           var featureClip = bboxClip_1(feature, tile.extent.rectangle());
+                           var featureClip = turf_bboxClip(feature, tile.extent.rectangle());
                            if (!fastDeepEqual(feature.geometry, featureClip.geometry)) {
                                // feature = featureClip;
                                isClipped = true;
                    type: 'item',
                    // the language to search
                    language: lang,
-                   // the langauge for the label and description in the result
+                   // the language for the label and description in the result
                    uselang: lang,
                    limit: 10,
                    origin: '*'
                mainEnter
                    .append('div')
                    .attr('class', 'comment-text')
-                   .html(function(d) { return d.html; });
+                   .html(function(d) { return d.html; })
+                   .selectAll('a')
+                       .attr('rel', 'noopener nofollow')
+                       .attr('target', '_blank');
 
                comments
                    .call(replaceAvatars);
            return section;
        }
 
-       // Pass `which` object of the form:
+       // Pass `what` object of the form:
        // {
        //   key: 'string',     // required
        //   value: 'string'    // optional
        // }
        //   -or-
        // {
-       //   rtype: 'string'    // relation type  (e.g. 'multipolygon')
-       // }
-       //   -or-
-       // {
        //   qid: 'string'      // brand wikidata  (e.g. 'Q37158')
        // }
        //
 
 
            tagReference.body = function(selection) {
-               var itemID = what.qid || what.rtype || (what.key + '-' + what.value);
+               var itemID = what.qid || (what.key + '-' + (what.value || ''));
                _body = selection.selectAll('.tag-reference-body')
                    .data([itemID], function(d) { return d; });
 
                            bindTypeahead(key, value);
                        }
 
-                       var reference;
-
-                       if (typeof d.value !== 'string') {
-                           reference = uiTagReference({ key: d.key });
-                       } else {
-                           var isRelation = _entityIDs && _entityIDs.some(function(entityID) {
-                               return context.entity(entityID).type === 'relation';
-                           });
-                           if (isRelation && d.key === 'type') {
-                               reference = uiTagReference({ rtype: d.value });
-                           } else {
-                               reference = uiTagReference({ key: d.key, value: d.value });
-                           }
+                       var referenceOptions = { key: d.key };
+                       if (typeof d.value === 'string') {
+                           referenceOptions.value = d.value;
                        }
+                       var reference = uiTagReference(referenceOptions);
 
                        if (_state === 'hover') {
                            reference.showing(false);
              .append('div')
                .attr('class', 'qa-details-subsection');
 
-           // Suggested Fix (musn't exist for every issue type)
+           // Suggested Fix (mustn't exist for every issue type)
            if (issueString(_qaItem, 'fix')) {
              var div$1 = detailsEnter
                .append('div')
                  .attr('target', '_blank');
            }
 
-           // Common Pitfalls (musn't exist for every issue type)
+           // Common Pitfalls (mustn't exist for every issue type)
            if (issueString(_qaItem, 'trap')) {
              var div$2 = detailsEnter
                .append('div')
                if (includeDrawNode) {
                    if (parentWay.isClosed()) {
                        // don't test the last segment for closed ways - #4655
-                       // (still test the first segement)
+                       // (still test the first segment)
                        nodes.pop();
                    }
                } else { // discount the draw node
 
                var nextMode;
 
-               if (context.graph() === startGraph) { // we've undone back to the beginning
+               if (context.graph() === startGraph) {
+                   // We've undone back to the initial state before we started drawing.
+                   // Just exit the draw mode without undoing whatever we did before
+                   // we entered the draw mode.
                    nextMode = modeSelect(context, [wayID]);
                } else {
-                   context.history()
-                       .on('undone.draw', null);
-                   // remove whatever segment was drawn previously
-                   context.undo();
+                   // The `undo` only removed the temporary edit, so here we have to
+                   // manually undo to actually remove the last node we added. We can't
+                   // use the `undo` function since the initial "add" graph doesn't have
+                   // an annotation and so cannot be undone to.
+                   context.pop(1);
 
-                   if (context.graph() === startGraph) { // we've undone back to the beginning
-                       nextMode = modeSelect(context, [wayID]);
-                   } else {
-                       // continue drawing
-                       nextMode = mode;
-                   }
+                   // continue drawing
+                   nextMode = mode;
                }
 
-               // clear the redo stack by adding and removing an edit
+               // clear the redo stack by adding and removing a blank edit
                context.perform(actionNoop());
                context.pop(1);
 
                _headNodeID = typeof _nodeIndex === 'number' ? _origWay.nodes[_nodeIndex] :
                    (_origWay.isClosed() ? _origWay.nodes[_origWay.nodes.length - 2] : _origWay.nodes[_origWay.nodes.length - 1]);
                _wayGeometry = _origWay.geometry(context.graph());
-               _annotation = _t((_origWay.isDegenerate() ?
+               _annotation = _t((_origWay.nodes.length === (_origWay.isClosed() ? 2 : 1) ?
                    'operations.start.annotation.' :
                    'operations.continue.annotation.') + _wayGeometry
                );
 
            var operation = function() {
 
-               if (!getSelectionText()) {
-                   event.preventDefault();
-               }
-
                var graph = context.graph();
                var selected = groupEntities(getFilteredIdsToCopy(), graph);
                var canCopy = [];
            }
 
 
-           function getSelectionText() {
-               return window.getSelection().toString();
-           }
-
-
            operation.available = function() {
                return getFilteredIdsToCopy().length > 0;
            };
            };
 
 
+           operation.availableForKeypress = function() {
+               var selection = window.getSelection && window.getSelection();
+               // if the user has text selected then let them copy that, not the selected feature
+               return !selection || !selection.toString();
+           };
+
+
            operation.tooltip = function() {
                var disable = operation.disabled();
                return disable ?
                });
                _nodes = utilGetAllNodes(_wayIDs, context.graph());
                _coords = _nodes.map(function(n) { return n.loc; });
-               _extent = utilTotalExtent(ways, context.graph());
 
                // actions for connected nodes shared by at least two selected ways
                var sharedActions = [];
+               var sharedNodes = [];
                // actions for connected nodes
                var unsharedActions = [];
+               var unsharedNodes = [];
 
                _nodes.forEach(function(node) {
                    var action = actionDisconnect(node.id).limitWays(_wayIDs);
 
                        if (count > 1) {
                            sharedActions.push(action);
+                           sharedNodes.push(node);
                        } else {
                            unsharedActions.push(action);
+                           unsharedNodes.push(node);
                        }
                    }
                });
                if (sharedActions.length) {
                    // if any nodes are shared, only disconnect the selected ways from each other
                    _actions = sharedActions;
+                   _extent = utilTotalExtent(sharedNodes, context.graph());
                    _descriptionID += 'conjoined';
                    _annotationID = 'from_each_other';
                } else {
                    // if no nodes are shared, disconnect the selected ways from all connected ways
                    _actions = unsharedActions;
+                   _extent = utilTotalExtent(unsharedNodes, context.graph());
                    if (_wayIDs.length === 1) {
                        _descriptionID += context.graph().geometry(_wayIDs[0]);
                    } else {
                for (var i = 0; i < selectedIDs.length; i++) {
                    var entity = context.hasEntity(selectedIDs[i]);
                    if (!entity || entity.geometry(graph) !== 'vertex') {
-                       return [];  // selection includes some not vertexes
+                       return [];  // selection includes some not vertices
                    }
 
                    var currParents = graph.parentWays(entity).map(function(w) { return w.id; });
                    var maxdist = 500;
 
                    // Don't allow the hash location to change too much while drawing
-                   // This can happen if the user accidently hit the back button.  #3996
+                   // This can happen if the user accidentally hit the back button.  #3996
                    if (mode && mode.id.match(/^draw/) !== null && dist > maxdist) {
                        context.enter(modeBrowse(context));
                        return;
 
            function segmentBBox(segment) {
                var extent = segment.extent(head);
-               // extent can be null if the node entites aren't in the graph for some reason
+               // extent can be null if the node entities aren't in the graph for some reason
                if (!extent) { return null; }
 
                var bbox = extent.bbox();
                return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
            }
 
-
-           function getFeatureTypeForCrossingCheck(way, graph) {
-               var feature = getFeatureWithFeatureTypeTagsForWay(way, graph);
-               return getFeatureType(feature, graph);
-           }
-
            // discard
            var ignoredBuildings = {
                demolished: true, dismantled: true, proposed: true, razed: true
            }
 
 
-           function isLegitCrossing(way1, featureType1, way2, featureType2) {
-               var tags1 = way1.tags;
-               var tags2 = way2.tags;
+           function isLegitCrossing(tags1, featureType1, tags2, featureType2) {
 
                // assume 0 by default
                var level1 = tags1.level || '0';
                    var featureTypes = [featureType1, featureType2];
                    if (featureTypes.indexOf('highway') !== -1) {
                        if (featureTypes.indexOf('railway') !== -1) {
+                           if (!bothLines) { return {}; }
+
+                           var isTram = entity1.tags.railway === 'tram' || entity2.tags.railway === 'tram';
+
                            if (osmPathHighwayTagValues[entity1.tags.highway] ||
                                osmPathHighwayTagValues[entity2.tags.highway]) {
-                               // path-rail connections use this tag
-                               return bothLines ? { railway: 'crossing' } : {};
+
+                               // path-tram connections use this tag
+                               if (isTram) { return { railway: 'tram_crossing' }; }
+
+                               // other path-rail connections use this tag
+                               return { railway: 'crossing' };
                            } else {
-                               // road-rail connections use this tag
-                               return bothLines ? { railway: 'level_crossing' } : {};
+                               // path-tram connections use this tag
+                               if (isTram) { return { railway: 'tram_level_crossing' }; }
+
+                               // other road-rail connections use this tag
+                               return { railway: 'level_crossing' };
                            }
                        }
 
                var edgeCrossInfos = [];
                if (way1.type !== 'way') { return edgeCrossInfos; }
 
-               var way1FeatureType = getFeatureTypeForCrossingCheck(way1, graph);
+               var taggedFeature1 = getFeatureWithFeatureTypeTagsForWay(way1, graph);
+               var way1FeatureType = getFeatureType(taggedFeature1, graph);
                if (way1FeatureType === null) { return edgeCrossInfos; }
 
                var checkedSingleCrossingWays = {};
                var n1, n2, nA, nB, nAId, nBId;
                var segment1, segment2;
                var oneOnly;
-               var segmentInfos, segment2Info, way2, way2FeatureType;
+               var segmentInfos, segment2Info, way2, taggedFeature2, way2FeatureType;
                var way1Nodes = graph.childNodes(way1);
                var comparedWays = {};
                for (i = 0; i < way1Nodes.length - 1; i++) {
 
                        way2 = graph.hasEntity(segment2Info.wayId);
                        if (!way2) { continue; }
-
+                       taggedFeature2 = getFeatureWithFeatureTypeTagsForWay(way2, graph);
                        // only check crossing highway, waterway, building, and railway
-                       way2FeatureType = getFeatureTypeForCrossingCheck(way2, graph);
+                       way2FeatureType = getFeatureType(taggedFeature2, graph);
+
                        if (way2FeatureType === null ||
-                           isLegitCrossing(way1, way1FeatureType, way2, way2FeatureType)) {
+                           isLegitCrossing(taggedFeature1.tags, way1FeatureType, taggedFeature2.tags, way2FeatureType)) {
                            continue;
                        }
 
                        } else if (context.graph().geometry(this.entityIds[0]) === 'line' &&
                            context.graph().geometry(this.entityIds[1]) === 'line') {
 
-                           // don't recommend adding bridges to waterways since they're uncommmon
+                           // don't recommend adding bridges to waterways since they're uncommon
                            if (allowsBridge(selectedFeatureType) && selectedFeatureType !== 'waterway') {
                                fixes.push(makeAddBridgeOrTunnelFix('add_a_bridge', 'temaki-bridge', 'bridge'));
                            }
 
-                           // don't recommend adding tunnels under waterways since they're uncommmon
+                           // don't recommend adding tunnels under waterways since they're uncommon
                            var skipTunnelFix = otherFeatureType === 'waterway' && selectedFeatureType !== 'waterway';
                            if (allowsTunnel(selectedFeatureType) && !skipTunnelFix) {
                                fixes.push(makeAddBridgeOrTunnelFix('add_a_tunnel', 'temaki-tunnel', 'tunnel'));
        };
 
        var matchGroups = {adult_gaming_centre:["amenity/casino","amenity/gambling","leisure/adult_gaming_centre"],beauty:["shop/beauty","shop/hairdresser_supply"],bed:["shop/bed","shop/furniture"],beverages:["shop/alcohol","shop/beverages"],camping:["leisure/park","tourism/camp_site","tourism/caravan_site"],car_parts:["shop/car_parts","shop/car_repair","shop/tires","shop/tyres"],confectionery:["shop/candy","shop/chocolate","shop/confectionery"],convenience:["shop/beauty","shop/chemist","shop/convenience","shop/cosmetics","shop/newsagent"],coworking:["amenity/coworking_space","office/coworking","office/coworking_space"],electronics:["office/telecommunication","shop/computer","shop/electronics","shop/hifi","shop/mobile","shop/mobile_phone","shop/telecommunication"],fashion:["shop/accessories","shop/bag","shop/botique","shop/clothes","shop/department_store","shop/fashion","shop/fashion_accessories","shop/sports","shop/shoes"],financial:["amenity/bank","office/accountant","office/financial","office/financial_advisor","office/tax_advisor","shop/tax"],fitness:["leisure/fitness_centre","leisure/fitness_center","leisure/sports_centre","leisure/sports_center"],food:["amenity/cafe","amenity/fast_food","amenity/ice_cream","amenity/restaurant","shop/bakery","shop/ice_cream","shop/pastry","shop/tea","shop/coffee"],fuel:["amenity/fuel","shop/gas","shop/convenience;gas","shop/gas;convenience"],gift:["shop/gift","shop/card","shop/cards","shop/stationery"],hardware:["shop/carpet","shop/diy","shop/doityourself","shop/doors","shop/electrical","shop/flooring","shop/hardware","shop/power_tools","shop/tool_hire","shop/tools","shop/trade"],health_food:["shop/health","shop/health_food","shop/herbalist","shop/nutrition_supplements"],houseware:["shop/houseware","shop/interior_decoration"],lodging:["tourism/hotel","tourism/motel"],money_transfer:["amenity/money_transfer","shop/money_transfer"],outdoor:["shop/outdoor","shop/sports"],rental:["amenity/bicycle_rental","amenity/boat_rental","amenity/car_rental","amenity/truck_rental","amenity/vehicle_rental","shop/rental"],school:["amenity/childcare","amenity/college","amenity/kindergarten","amenity/language_school","amenity/prep_school","amenity/school","amenity/university"],supermarket:["shop/food","shop/frozen_food","shop/greengrocer","shop/grocery","shop/supermarket","shop/wholesale"],variety_store:["shop/variety_store","shop/supermarket","shop/discount","shop/convenience"],vending:["amenity/vending_machine","shop/vending_machine"],wholesale:["shop/wholesale","shop/supermarket","shop/department_store"]};
-       var match_groups = {
+       var require$$0 = {
        matchGroups: matchGroups
        };
 
-       var match_groups$1 = /*#__PURE__*/Object.freeze({
-               __proto__: null,
-               matchGroups: matchGroups,
-               'default': match_groups
-       });
-
-       var require$$0 = getCjsExportFromNamespace(match_groups$1);
-
        var matchGroups$1 = require$$0.matchGroups;
 
 
                .on('redone.validator', validator.validate);   // redo
                // but not on 'change' (e.g. while drawing)
 
-           // When user chages editing modes:
+           // When user changes editing modes:
            context
                .on('exit.validator', validator.validate);
 
                // Guess a type based on the tokens present in the template
                // (This is for 'custom' source, where we don't know)
                if (!source.type) {
-                   if (/\{(proj|wkid|bbox)\}/.test(_template)) {
+                   if (/SERVICE=WMS|\{(proj|wkid|bbox)\}/.test(_template)) {
                        source.type = 'wms';
                        source.projection = 'EPSG:3857';  // guess
                    } else if (/\{(x|y)\}/.test(_template)) {
                      switch (key) {
                        case 'width':
                        case 'height':
-                         return tileSize;
+                           return tileSize;
                        case 'proj':
-                         return projection;
+                           return projection;
                        case 'wkid':
-                         return projection.replace(/^EPSG:/, '');
+                           return projection.replace(/^EPSG:/, '');
                        case 'bbox':
-                         return minXmaxY.x + ',' + maxXminY.y + ',' + maxXminY.x + ',' + minXmaxY.y;
+                           // WMS 1.3 flips x/y for some coordinate systems including EPSG:4326 - #7557
+                           if (projection === 'EPSG:4326' &&
+                               // The CRS parameter implies version 1.3 (prior versions use SRS)
+                               /VERSION=1.3|CRS={proj}/.test(source.template())) {
+                               return maxXminY.y + ',' + minXmaxY.x + ',' + minXmaxY.y + ',' + maxXminY.x;
+                           } else {
+                               return minXmaxY.x + ',' + maxXminY.y + ',' + maxXminY.x + ',' + minXmaxY.y;
+                           }
                        case 'w':
-                         return minXmaxY.x;
+                           return minXmaxY.x;
                        case 's':
-                         return maxXminY.y;
+                           return maxXminY.y;
                        case 'n':
-                         return maxXminY.x;
+                           return maxXminY.x;
                        case 'e':
-                         return minXmaxY.y;
+                           return minXmaxY.y;
                        default:
-                         return token;
+                           return token;
                      }
                    });
 
            context.history().photoOverlaysUsed(photoOverlaysUsed);
          };
 
+         var _checkedBlocklists;
 
          background.sources = function (extent, zoom, includeCurrent) {
            if (!_imageryIndex) { return []; }   // called before init()?
 
            var currSource = baseLayer.source();
 
+           var osm = context.connection();
+           var blocklists = osm && osm.imageryBlocklists();
+
+           if (blocklists && blocklists !== _checkedBlocklists) {
+             _imageryIndex.backgrounds.forEach(function (source) {
+               source.isBlocked = blocklists.some(function(blocklist) {
+                 return blocklist.test(source.template());
+               });
+             });
+             _checkedBlocklists = blocklists;
+           }
+
            return _imageryIndex.backgrounds.filter(function (source) {
+             if (includeCurrent && currSource === source) { return true; }  // optionally always include the current imagery
+             if (source.isBlocked) { return false; }                        // even bundled sources may be blocked - #7905
              if (!source.polygon) { return true; }                          // always include imagery with worldwide coverage
-             if (includeCurrent && currSource === source) { return true; }  // optionally include the current imagery
              if (zoom && zoom < 6) { return false; }                        // optionally exclude local imagery at low zooms
              return visible[source.id];                                 // include imagery visible in given extent
            });
          background.baseLayerSource = function(d) {
            if (!arguments.length) { return baseLayer.source(); }
 
-           // test source against OSM imagery blacklists..
+           // test source against OSM imagery blocklists..
            var osm = context.connection();
            if (!osm) { return background; }
 
-           var blacklists = osm.imageryBlacklists();
+           var blocklists = osm.imageryBlocklists();
            var template = d.template();
            var fail = false;
            var tested = 0;
            var regex;
 
-           for (var i = 0; i < blacklists.length; i++) {
-             try {
-               regex = new RegExp(blacklists[i]);
-               fail = regex.test(template);
-               tested++;
-               if (fail) { break; }
-             } catch (e) {
-               /* noop */
-             }
+           for (var i = 0; i < blocklists.length; i++) {
+             regex = blocklists[i];
+             fail = regex.test(template);
+             tested++;
+             if (fail) { break; }
            }
 
            // ensure at least one test was run.
            if (!tested) {
-             regex = new RegExp('.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*');
+             regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/;
              fail = regex.test(template);
            }
 
            drawData.template = function(val, src) {
                if (!arguments.length) { return _template; }
 
-               // test source against OSM imagery blacklists..
+               // test source against OSM imagery blocklists..
                var osm = context.connection();
                if (osm) {
-                   var blacklists = osm.imageryBlacklists();
+                   var blocklists = osm.imageryBlocklists();
                    var fail = false;
                    var tested = 0;
                    var regex;
 
-                   for (var i = 0; i < blacklists.length; i++) {
-                       try {
-                           regex = new RegExp(blacklists[i]);
-                           fail = regex.test(val);
-                           tested++;
-                           if (fail) { break; }
-                       } catch (e) {
-                           /* noop */
-                       }
+                   for (var i = 0; i < blocklists.length; i++) {
+                       regex = blocklists[i];
+                       fail = regex.test(val);
+                       tested++;
+                       if (fail) { break; }
                    }
 
                    // ensure at least one test was run.
                    if (!tested) {
-                       regex = new RegExp('.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*');
+                       regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/;
                        fail = regex.test(val);
                    }
                }
 
            /**
             * drawImages()
-            * drawImages is the method that is returned (and that runs) everytime 'svgStreetside()' is called.
+            * drawImages is the method that is returned (and that runs) every time 'svgStreetside()' is called.
             * 'svgStreetside()' is called from index.js
             */
            function drawImages(selection) {
 
                function addChildVertices(entity) {
 
-                   // avoid redunant work and infinite recursion of circular relations
+                   // avoid redundant work and infinite recursion of circular relations
                    if (seenIds[entity.id]) { return; }
                    seenIds[entity.id] = true;
 
                }
 
                // Collect important vertices from the `entities` list..
-               // (during a paritial redraw, it will not contain everything)
+               // (during a partial redraw, it will not contain everything)
                for (var i = 0; i < entities.length; i++) {
                    var entity = entities[i];
                    var geometry = entity.geometry(graph);
                        );
 
                        // On Firefox Windows and Linux we always get +/- the scroll line amount (default 3)
-                       // There doesn't seem to be any scroll accelleration.
+                       // There doesn't seem to be any scroll acceleration.
                        // This multiplier increases the speed a little bit - #5512
                        if (detected.os !== 'mac') {
                            dY *= 5;
                        .selectAll('.background-info-span-vintage')
                        .text(metadata.vintage);
 
-                   // update other metdata
+                   // update other metadata
                    metadataKeys.forEach(function(k) {
                        if (k === 'zoom' || k === 'vintage') { return; }  // done already
                        var val = result[k];
                point: _t('modes.add_point.title'),
                line: _t('modes.add_line.title'),
                area: _t('modes.add_area.title'),
-               note: _t('modes.add_note.title'),
+               note: _t('modes.add_note.label'),
                delete: _t('operations.delete.title'),
                move: _t('operations.move.title'),
                orthogonalize: _t('operations.orthogonalize.title'),
                        .preset(_presets.length === 1 ? _presets[0] : _mainPresetIndex.item('point'))
                    );
 
-               // NOTE: split on en-dash, not a hypen (to avoid conflict with hyphenated names)
+               // NOTE: split on en-dash, not a hyphen (to avoid conflict with hyphenated names)
                var names = _presets.length === 1 ? _presets[0].name().split(' – ') : [_t('inspector.multiple_types')];
 
                var label = selection.select('.label-inner');
                if (!utilArrayIdentical(val, _presets)) {
                    _presets = val;
 
-                   var geometries = entityGeometries();
-                   if (_presets.length === 1 && geometries.length) {
-                       _tagReference = uiTagReference(_presets[0].reference(geometries[0]))
+                   if (_presets.length === 1) {
+                       _tagReference = uiTagReference(_presets[0].reference())
                            .showing(false);
                    }
                }
                }
 
                // Remove whatever is after the last ' – '
-               // NOTE: split/join on en-dash, not a hypen (to avoid conflict with fr - nl names in Brussels etc)
+               // NOTE: split/join on en-dash, not a hyphen (to avoid conflict with fr - nl names in Brussels etc)
                function cleanName(name) {
                    var parts = name.split(' – ');
                    if (parts.length > 1) {
              .call(langCombo)
              .merge(_langInput);
 
-           utilGetSetValue(_langInput, language()[1]);
-
            _langInput
              .on('blur', changeLang)
              .on('change', changeLang);
          }
 
 
-         function language() {
+         function defaultLanguageInfo(skipEnglishFallback) {
+           var langCode = _mainLocalizer.languageCode().toLowerCase();
+
+           for (var i in _dataWikipedia) {
+             var d = _dataWikipedia[i];
+             // default to the language of iD's current locale
+             if (d[2] === langCode) { return d; }
+           }
+
+           // fallback to English
+           return skipEnglishFallback ? ['', '', ''] : ['English', 'English', 'en'];
+         }
+
+
+         function language(skipEnglishFallback) {
            var value = utilGetSetValue(_langInput).toLowerCase();
-           var locale = _mainLocalizer.localeCode().toLowerCase();
-           var localeLanguage;
-           return _dataWikipedia.find(function (d) {
-             if (d[2] === locale) { localeLanguage = d; }
-             return d[0].toLowerCase() === value || d[1].toLowerCase() === value || d[2] === value;
-           }) || localeLanguage || ['English', 'English', 'en'];
+
+           for (var i in _dataWikipedia) {
+             var d = _dataWikipedia[i];
+             // return the language already set in the UI, if supported
+             if (d[0].toLowerCase() === value ||
+               d[1].toLowerCase() === value ||
+               d[2] === value) { return d; }
+           }
+
+           // fallback to English
+           return defaultLanguageInfo(skipEnglishFallback);
          }
 
 
          function change(skipWikidata) {
            var value = utilGetSetValue(_titleInput);
            var m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/);
-           var l = m && _dataWikipedia.find(function (d) { return m[1] === d[2]; });
+           var langInfo = m && _dataWikipedia.find(function (d) { return m[1] === d[2]; });
            var syncTags = {};
 
-           if (l) {
+           if (langInfo) {
+             var nativeLangName = langInfo[1];
              // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
              value = decodeURIComponent(m[2]).replace(/_/g, ' ');
              if (m[3]) {
                value += '#' + anchor.replace(/_/g, ' ');
              }
              value = value.slice(0, 1).toUpperCase() + value.slice(1);
-             utilGetSetValue(_langInput, l[1]);
+             utilGetSetValue(_langInput, nativeLangName);
              utilGetSetValue(_titleInput, value);
            }
 
          function updateForTags(tags) {
 
            var value = typeof tags[field.key] === 'string' ? tags[field.key] : '';
+           // Expect tag format of `tagLang:tagArticleTitle`, e.g. `fr:Paris`, with
+           // optional suffix of `#anchor`
            var m = value.match(/([^:]+):([^#]+)(?:#(.+))?/);
-           var l = m && _dataWikipedia.find(function (d) { return m[1] === d[2]; });
+           var tagLang = m && m[1];
+           var tagArticleTitle = m && m[2];
            var anchor = m && m[3];
+           var tagLangInfo = tagLang && _dataWikipedia.find(function (d) { return tagLang === d[2]; });
 
            // value in correct format
-           if (l) {
-             utilGetSetValue(_langInput, l[1]);
-             utilGetSetValue(_titleInput, m[2] + (anchor ? ('#' + anchor) : ''));
+           if (tagLangInfo) {
+             var nativeLangName = tagLangInfo[1];
+             utilGetSetValue(_langInput, nativeLangName);
+             utilGetSetValue(_titleInput, tagArticleTitle + (anchor ? ('#' + anchor) : ''));
              if (anchor) {
                try {
                  // Best-effort `anchorencode:` implementation
                  anchor = anchor.replace(/ /g, '_');
                }
              }
-             _wikiURL = 'https://' + m[1] + '.wikipedia.org/wiki/' +
-               m[2].replace(/ /g, '_') + (anchor ? ('#' + anchor) : '');
+             _wikiURL = 'https://' + tagLang + '.wikipedia.org/wiki/' +
+               tagArticleTitle.replace(/ /g, '_') + (anchor ? ('#' + anchor) : '');
 
            // unrecognized value format
            } else {
              utilGetSetValue(_titleInput, value);
              if (value && value !== '') {
                utilGetSetValue(_langInput, '');
-               _wikiURL = "https://en.wikipedia.org/wiki/Special:Search?search=" + value;
+               var defaultLangInfo = defaultLanguageInfo();
+               _wikiURL = "https://" + (defaultLangInfo[2]) + ".wikipedia.org/w/index.php?fulltext=1&search=" + value;
              } else {
+               var shownOrDefaultLangInfo = language(true /* skipEnglishFallback */);
+               utilGetSetValue(_langInput, shownOrDefaultLangInfo[1]);
                _wikiURL = '';
              }
            }
                    var graph = context.graph();
 
                    var geometries = Object.keys(_entityIDs.reduce(function(geoms, entityID) {
-                       return geoms[graph.entity(entityID).geometry(graph)] = true;
+                       geoms[graph.entity(entityID).geometry(graph)] = true;
+                       return geoms;
                    }, {}));
 
                    var presetsManager = _mainPresetIndex;
 
 
            function changeRole(d) {
-               if (d === 0) { return; }    // called on newrow (shoudn't happen)
+               if (d === 0) { return; }    // called on newrow (shouldn't happen)
                if (_inChange) { return; }  // avoid accidental recursive call #5731
 
                var oldRole = d.member.role;
 
            function deleteMembership(d) {
                this.blur();           // avoid keeping focus on the button
-               if (d === 0) { return; }   // called on newrow (shoudn't happen)
+               if (d === 0) { return; }   // called on newrow (shouldn't happen)
 
                // remove the hover-highlight styling
                utilHighlightEntities([d.relation.id], false, context);
                        .append('div')
                        .attr('class', 'label-inner');
 
-                   // NOTE: split/join on en-dash, not a hypen (to avoid conflict with fr - nl names in Brussels etc)
+                   // NOTE: split/join on en-dash, not a hyphen (to avoid conflict with fr - nl names in Brussels etc)
                    label.selectAll('.namepart')
                        .data(preset.name().split(' – '))
                        .enter()
                };
 
                item.preset = preset;
-               item.reference = uiTagReference(preset.reference(entityGeometries()[0]));
+               item.reference = uiTagReference(preset.reference());
 
                return item;
            }
                            .classed('inspector-hidden', false)
                            .classed('inspector-hover', false);
 
-                       if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), ids) || inspector.state() !== 'select') {
-                           inspector
-                               .state('select')
-                               .entityIDs(ids)
-                               .newFeature(newFeature);
+                       // reload the UI even if the ids are the same since the entities
+                       // themselves may have changed
+                       inspector
+                           .state('select')
+                           .entityIDs(ids)
+                           .newFeature(newFeature);
 
-                           inspectorWrap
-                               .call(inspector);
-                       }
+                       inspectorWrap
+                           .call(inspector);
 
                    } else {
                        inspector
          var context = utilRebind({}, dispatch$1, 'on');
          var _deferred = new Set();
 
-         context.version = '2.18.3';
+         context.version = '2.18.5';
          context.privacyVersion = '20200407';
 
          // iD will alter the hash so cache the parameters intended to setup the session