]> git.openstreetmap.org Git - rails.git/blobdiff - vendor/assets/iD/iD.js
Update to iD v2.19.3
[rails.git] / vendor / assets / iD / iD.js
index af0d06f981ae714e9ae20f37e5e673e86450fcdd..4d89db7c5aaced0e7ca6315120fc94acb2724ea5 100644 (file)
          return action;
        }
 
+       var geojsonRewind = rewind;
+
+       function rewind(gj, outer) {
+         var type = gj && gj.type,
+             i;
+
+         if (type === 'FeatureCollection') {
+           for (i = 0; i < gj.features.length; i++) {
+             rewind(gj.features[i], outer);
+           }
+         } else if (type === 'GeometryCollection') {
+           for (i = 0; i < gj.geometries.length; i++) {
+             rewind(gj.geometries[i], outer);
+           }
+         } else if (type === 'Feature') {
+           rewind(gj.geometry, outer);
+         } else if (type === 'Polygon') {
+           rewindRings(gj.coordinates, outer);
+         } else if (type === 'MultiPolygon') {
+           for (i = 0; i < gj.coordinates.length; i++) {
+             rewindRings(gj.coordinates[i], outer);
+           }
+         }
+
+         return gj;
+       }
+
+       function rewindRings(rings, outer) {
+         if (rings.length === 0) return;
+         rewindRing(rings[0], outer);
+
+         for (var i = 1; i < rings.length; i++) {
+           rewindRing(rings[i], !outer);
+         }
+       }
+
+       function rewindRing(ring, dir) {
+         var area = 0;
+
+         for (var i = 0, len = ring.length, j = len - 1; i < len; j = i++) {
+           area += (ring[i][0] - ring[j][0]) * (ring[j][1] + ring[i][1]);
+         }
+
+         if (area >= 0 !== !!dir) ring.reverse();
+       }
+
        function actionExtract(entityID) {
          var extractedNodeID;
 
            var fromGeometry = entity.geometry(graph);
            var keysToCopyAndRetain = ['source', 'wheelchair'];
            var keysToRetain = ['area'];
-           var buildingKeysToRetain = ['architect', 'building', 'height', 'layer'];
-           var extractedLoc = d3_geoCentroid(entity.asGeoJSON(graph));
+           var buildingKeysToRetain = ['architect', 'building', 'height', 'layer']; // d3_geoCentroid is wrong for counterclockwise-wound polygons, so wind them clockwise
+
+           var extractedLoc = d3_geoCentroid(geojsonRewind(Object.assign({}, entity.asGeoJSON(graph)), true));
 
            if (!extractedLoc || !isFinite(extractedLoc[0]) || !isFinite(extractedLoc[1])) {
              extractedLoc = entity.extent(graph).center();
                  loc: loc,
                  key: feature.properties.key,
                  value: feature.properties.value,
-                 "package": feature.properties["package"],
-                 detections: feature.properties.detections
+                 detections: feature.properties.detections,
+                 direction: feature.properties.direction,
+                 accuracy: feature.properties.accuracy,
+                 first_seen_at: feature.properties.first_seen_at,
+                 last_seen_at: feature.properties.last_seen_at
                };
              }
 
                  key: feature.properties.key,
                  image_key: feature.properties.image_key,
                  value: feature.properties.value,
-                 "package": feature.properties["package"],
                  shape: feature.properties.shape
                };
 
          return _boolean(subject, clipping, UNION);
        }
 
+       /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
        var read$6 = function read(buffer, offset, isLE, mLen, nBytes) {
          var e, m;
          var eLen = nBytes * 8 - mLen - 1;
                    if (buildingKeysToKeep.indexOf(key) !== -1 || key.match(/^building:.{1,}/) || key.match(/^roof:.{1,}/)) continue;
                  }
 
-                 if (type !== 'generic' && key.match(/^addr:.{1,}/)) continue;
+                 if (type !== 'generic') {
+                   if (key.match(/^addr:.{1,}/) || key.match(/^source:.{1,}/)) continue;
+                 }
+
                  delete tags[key];
                }
 
            layer.selectAll('.viewfield-group.currentView').attr('transform', transform);
          }
 
-         context.photos().on('change.streetside', update);
-
          function filterBubbles(bubbles) {
            var fromDate = context.photos().fromDate();
            var toDate = context.photos().toDate();
 
            if (svgStreetside.enabled) {
              showLayer();
+             context.photos().on('change.streetside', update);
            } else {
              hideLayer();
+             context.photos().on('change.streetside', null);
            }
 
            dispatch.call('change');
            return t;
          }
 
-         context.photos().on('change.mapillary_images', update);
-
          function filterImages(images) {
            var showsPano = context.photos().showsPanoramic();
            var showsFlat = context.photos().showsFlat();
 
            if (svgMapillaryImages.enabled) {
              showLayer();
+             context.photos().on('change.mapillary_images', update);
            } else {
              hideLayer();
+             context.photos().on('change.mapillary_images', null);
            }
 
            dispatch.call('change');
            }
          }
 
+         function filterData(detectedFeatures) {
+           var service = getService();
+           var fromDate = context.photos().fromDate();
+           var toDate = context.photos().toDate();
+           var usernames = context.photos().usernames();
+
+           if (fromDate) {
+             var fromTimestamp = new Date(fromDate).getTime();
+             detectedFeatures = detectedFeatures.filter(function (feature) {
+               return new Date(feature.last_seen_at).getTime() >= fromTimestamp;
+             });
+           }
+
+           if (toDate) {
+             var toTimestamp = new Date(toDate).getTime();
+             detectedFeatures = detectedFeatures.filter(function (feature) {
+               return new Date(feature.first_seen_at).getTime() <= toTimestamp;
+             });
+           }
+
+           if (usernames && service) {
+             detectedFeatures = detectedFeatures.filter(function (feature) {
+               return feature.detections.some(function (detection) {
+                 var imageKey = detection.image_key;
+                 var image = service.cachedImage(imageKey);
+                 return image && usernames.indexOf(image.captured_by) !== -1;
+               });
+             });
+           }
+
+           return detectedFeatures;
+         }
+
          function update() {
            var service = getService();
            var data = service ? service.signs(projection) : [];
+           data = filterData(data);
            var selectedImageKey = service.getSelectedImageKey();
            var transform = svgPointTransform(projection);
            var signs = layer.selectAll('.icon-sign').data(data, function (d) {
 
            if (svgMapillarySigns.enabled) {
              showLayer();
+             context.photos().on('change.mapillary_signs', update);
            } else {
              hideLayer();
+             context.photos().on('change.mapillary_signs', null);
            }
 
            dispatch.call('change');
            }
          }
 
+         function filterData(detectedFeatures) {
+           var service = getService();
+           var fromDate = context.photos().fromDate();
+           var toDate = context.photos().toDate();
+           var usernames = context.photos().usernames();
+
+           if (fromDate) {
+             var fromTimestamp = new Date(fromDate).getTime();
+             detectedFeatures = detectedFeatures.filter(function (feature) {
+               return new Date(feature.last_seen_at).getTime() >= fromTimestamp;
+             });
+           }
+
+           if (toDate) {
+             var toTimestamp = new Date(toDate).getTime();
+             detectedFeatures = detectedFeatures.filter(function (feature) {
+               return new Date(feature.first_seen_at).getTime() <= toTimestamp;
+             });
+           }
+
+           if (usernames && service) {
+             detectedFeatures = detectedFeatures.filter(function (feature) {
+               return feature.detections.some(function (detection) {
+                 var imageKey = detection.image_key;
+                 var image = service.cachedImage(imageKey);
+                 return image && usernames.indexOf(image.captured_by) !== -1;
+               });
+             });
+           }
+
+           return detectedFeatures;
+         }
+
          function update() {
            var service = getService();
            var data = service ? service.mapFeatures(projection) : [];
+           data = filterData(data);
            var selectedImageKey = service && service.getSelectedImageKey();
            var transform = svgPointTransform(projection);
            var mapFeatures = layer.selectAll('.icon-map-feature').data(data, function (d) {
 
            if (svgMapillaryMapFeatures.enabled) {
              showLayer();
+             context.photos().on('change.mapillary_map_features', update);
            } else {
              hideLayer();
+             context.photos().on('change.mapillary_map_features', null);
            }
 
            dispatch.call('change');
            return t;
          }
 
-         context.photos().on('change.openstreetcam_images', update);
-
          function filterImages(images) {
            var fromDate = context.photos().fromDate();
            var toDate = context.photos().toDate();
 
            if (svgOpenstreetcamImages.enabled) {
              showLayer();
+             context.photos().on('change.openstreetcam_images', update);
            } else {
              hideLayer();
+             context.photos().on('change.openstreetcam_images', null);
            }
 
            dispatch.call('change');
            return result;
          }
 
+         var _isImperial = !_mainLocalizer.usesMetric();
+
          function redraw(selection) {
            var graph = context.graph();
            var selectedNoteID = context.selectedNoteID();
            var osm = services.osm;
-           var isImperial = !_mainLocalizer.usesMetric();
            var localeCode = _mainLocalizer.localeCode();
            var heading;
            var center, location, centroid;
                  if (geometry === 'line' || geometry === 'area') {
                    closed = entity.type === 'relation' || entity.isClosed() && !entity.isDegenerate();
                    var feature = entity.asGeoJSON(graph);
-                   length += radiansToMeters(d3_geoLength(toLineString(feature)));
-                   centroid = d3_geoCentroid(feature);
+                   length += radiansToMeters(d3_geoLength(toLineString(feature))); // d3_geoCentroid is wrong for counterclockwise-wound polygons, so wind them clockwise
+
+                   centroid = d3_geoCentroid(geojsonRewind(Object.assign({}, feature), true));
 
                    if (closed) {
                      area += steradiansToSqmeters(entity.area(graph));
            }
 
            if (area) {
-             list.append('li').html(_t.html('info_panels.measurement.area') + ':').append('span').html(displayArea(area, isImperial));
+             list.append('li').html(_t.html('info_panels.measurement.area') + ':').append('span').html(displayArea(area, _isImperial));
            }
 
            if (length) {
-             list.append('li').html(_t.html('info_panels.measurement.' + (closed ? 'perimeter' : 'length')) + ':').append('span').html(displayLength(length, isImperial));
+             list.append('li').html(_t.html('info_panels.measurement.' + (closed ? 'perimeter' : 'length')) + ':').append('span').html(displayLength(length, _isImperial));
            }
 
            if (typeof distance === 'number') {
-             list.append('li').html(_t.html('info_panels.measurement.distance') + ':').append('span').html(displayLength(distance, isImperial));
+             list.append('li').html(_t.html('info_panels.measurement.distance') + ':').append('span').html(displayLength(distance, _isImperial));
            }
 
            if (location) {
            }
 
            if (length || area || typeof distance === 'number') {
-             var toggle = isImperial ? 'imperial' : 'metric';
+             var toggle = _isImperial ? 'imperial' : 'metric';
              selection.append('a').html(_t.html('info_panels.measurement.' + toggle)).attr('href', '#').attr('class', 'button button-toggle-units').on('click', function (d3_event) {
                d3_event.preventDefault();
-               isImperial = !isImperial;
+               _isImperial = !_isImperial;
                selection.call(redraw);
              });
            }
            context.history().on('change.intro', function (changed) {
              wasChanged = true;
              timeout(function () {
-               if (context.history().undoAnnotation() === _t('operations.split.annotation.line')) {
+               if (context.history().undoAnnotation() === _t('operations.split.annotation.line', {
+                 n: 1
+               })) {
                  _washingtonSegmentID = changed.created()[0].id;
                  continueTo(didSplit);
                } else {
            wrap.selectAll('.preset-input-access').on('change', change).on('blur', change);
          }
 
-         function change(d) {
+         function change(d3_event, d) {
            var tag = {};
            var value = context.cleanTagValue(utilGetSetValue(select(this))); // don't override multiple values with blank string
 
            wrap.selectAll('.preset-input-cycleway').on('change', change).on('blur', change);
          }
 
-         function change(key) {
+         function change(d3_event, key) {
            var newValue = context.cleanTagValue(utilGetSetValue(select(this))); // don't override multiple values with blank string
 
            if (!newValue && (Array.isArray(_tags.cycleway) || Array.isArray(_tags[key]))) return;
            _titleInput = titleContainer.selectAll('input.wiki-title').data([0]);
            _titleInput = _titleInput.enter().append('input').attr('type', 'text').attr('class', 'wiki-title').attr('id', field.domId).call(utilNoAuto).call(titleCombo).merge(_titleInput);
 
-           _titleInput.on('blur', blur).on('change', change);
+           _titleInput.on('blur', function () {
+             change(true);
+           }).on('change', function () {
+             change(false);
+           });
 
            var link = titleContainer.selectAll('.wiki-link').data([0]);
            link = link.enter().append('button').attr('class', 'form-field-button wiki-link').attr('title', _t('icons.view_on', {
            change(true);
          }
 
-         function blur() {
-           change(true);
-         }
-
          function change(skipWikidata) {
            var value = utilGetSetValue(_titleInput);
            var m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/);
            });
          }).disclosureContent(renderDisclosureContent);
          var taginfo = services.taginfo;
-         var nearbyCombo = uiCombobox(context, 'parent-relation').minItems(1).fetcher(fetchNearbyRelations).itemsMouseEnter(function (d) {
+         var nearbyCombo = uiCombobox(context, 'parent-relation').minItems(1).fetcher(fetchNearbyRelations).itemsMouseEnter(function (d3_event, d) {
            if (d.relation) utilHighlightEntities([d.relation.id], true, context);
-         }).itemsMouseLeave(function (d) {
+         }).itemsMouseLeave(function (d3_event, d) {
            if (d.relation) utilHighlightEntities([d.relation.id], false, context);
          });
          var _inChange = false;
              }
            };
 
-           sidebar.toggle = function (d3_event, moveMap) {
-             var e = d3_event;
-
-             if (e && e.sourceEvent) {
-               e.sourceEvent.preventDefault();
-             } else if (e) {
-               e.preventDefault();
-             } // Don't allow sidebar to toggle when the user is in the walkthrough.
-
-
+           sidebar.toggle = function (moveMap) {
+             // Don't allow sidebar to toggle when the user is in the walkthrough.
              if (context.inIntro()) return;
              var isCollapsed = selection.classed('collapsed');
              var isCollapsing = !isCollapsed;
                endMargin = 0;
              }
 
+             if (!isCollapsing) {
+               // unhide the sidebar's content before it transitions onscreen
+               selection.classed('collapsed', isCollapsing);
+             }
+
              selection.transition().style(xMarginProperty, endMargin + 'px').tween('panner', function () {
                var i = d3_interpolateNumber(startMargin, endMargin);
                return function (t) {
                  context.ui().onResize(moveMap ? undefined : [dx * scaleX, 0]);
                };
              }).on('end', function () {
-               selection.classed('collapsed', isCollapsing); // switch back from px to %
+               if (isCollapsing) {
+                 // hide the sidebar's content after it transitions offscreen
+                 selection.classed('collapsed', isCollapsing);
+               } // switch back from px to %
+
 
                if (!isCollapsing) {
                  var containerWidth = container.node().getBoundingClientRect().width;
            }; // toggle the sidebar collapse when double-clicking the resizer
 
 
-           resizer.on('dblclick', sidebar.toggle); // ensure hover sidebar is closed when zooming out beyond editable zoom
+           resizer.on('dblclick', function (d3_event) {
+             d3_event.preventDefault();
+
+             if (d3_event.sourceEvent) {
+               d3_event.sourceEvent.preventDefault();
+             }
+
+             sidebar.toggle();
+           }); // ensure hover sidebar is closed when zooming out beyond editable zoom
 
            context.map().on('crossEditableZoom.sidebar', function (within) {
              if (!within && !selection.select('.inspector-hover').empty()) {
            selection.append('a').attr('target', '_blank').attr('href', 'https://github.com/openstreetmap/iD').html(currVersion); // only show new version indicator to users that have used iD before
 
            if (isNewVersion && !isNewUser) {
-             selection.append('div').attr('class', 'badge').append('a').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', {
+             selection.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'));
+             })).placement('top').scrollContainer(context.container().select('.main-footer-wrap')));
            }
          };
        }
 
            var itemsEnter = items.enter().append('li').attr('class', function (d) {
              return 'issue severity-' + d.severity;
-           }).on('click', function (d3_event, d) {
+           });
+           var labelsEnter = itemsEnter.append('button').attr('class', 'issue-label').on('click', function (d3_event, d) {
              context.validator().focusIssue(d);
            }).on('mouseover', function (d3_event, d) {
              utilHighlightEntities(d.entityIds, true, context);
            }).on('mouseout', function (d3_event, d) {
              utilHighlightEntities(d.entityIds, false, context);
            });
-           var labelsEnter = itemsEnter.append('div').attr('class', 'issue-label');
            var textEnter = labelsEnter.append('span').attr('class', 'issue-text');
            textEnter.append('span').attr('class', 'issue-icon').each(function (d) {
              var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
            var overMap = content.append('div').attr('class', 'over-map'); // HACK: Mobile Safari 14 likes to select anything selectable when long-
            // pressing, even if it's not targeted. This conflicts with long-pressing
            // to show the edit menu. We add a selectable offscreen element as the first
-           // child to trick Safari into not showing the selection UI.  
+           // child to trick Safari into not showing the selection UI.
 
            overMap.append('div').attr('class', 'select-trap').text('t');
-           overMap.append('div').attr('class', 'spinner').call(uiSpinner(context));
-           overMap.append('div').attr('class', 'attribution-wrap').attr('dir', 'ltr').call(uiAttribution(context)); // Map controls
+           overMap.call(uiMapInMap(context)).call(uiNotice(context));
+           overMap.append('div').attr('class', 'spinner').call(uiSpinner(context)); // Map controls
 
            var controls = overMap.append('div').attr('class', 'map-controls');
            controls.append('div').attr('class', 'map-control zoombuttons').call(uiZoom(context));
              controls.append('div').attr('class', 'map-control map-pane-control ' + pane.id + '-control').call(pane.renderToggleButton);
              panes.call(pane.renderPane);
            });
-           ui.info = uiInfo(context); // Add absolutely-positioned elements that sit on top of the map
-           // This should happen after the map is ready (center/zoom)
-
-           overMap.call(uiMapInMap(context)).call(ui.info).call(uiNotice(context));
+           ui.info = uiInfo(context);
+           overMap.call(ui.info);
            overMap.append('div').attr('class', 'photoviewer').classed('al', true) // 'al'=left,  'ar'=right
-           .classed('hide', true).call(ui.photoviewer); // Add footer
+           .classed('hide', true).call(ui.photoviewer);
+           overMap.append('div').attr('class', 'attribution-wrap').attr('dir', 'ltr').call(uiAttribution(context)); // Add footer
 
            var about = content.append('div').attr('class', 'map-footer');
            about.append('div').attr('class', 'api-status').call(uiStatus(context));
              delete _needWidth[selector];
            }
 
-           var element = select(selector);
-           var scrollWidth = element.property('scrollWidth');
-           var clientWidth = element.property('clientWidth');
+           var selection = context.container().select(selector);
+           if (selection.empty()) return;
+           var scrollWidth = selection.property('scrollWidth');
+           var clientWidth = selection.property('clientWidth');
            var needed = _needWidth[selector] || scrollWidth;
 
            if (scrollWidth > clientWidth) {
              // overflow happening
-             element.classed('narrow', true);
+             selection.classed('narrow', true);
 
              if (!_needWidth[selector]) {
                _needWidth[selector] = scrollWidth;
              }
            } else if (scrollWidth >= needed) {
-             element.classed('narrow', false);
+             selection.classed('narrow', false);
            }
          };
 
 
          var _deferred = new Set();
 
-         context.version = '2.19.0';
+         context.version = '2.19.3';
          context.privacyVersion = '20200407'; // iD will alter the hash so cache the parameters intended to setup the session
 
          context.initialHashParams = window.location.hash ? utilStringQs(window.location.hash) : {};