]> git.openstreetmap.org Git - rails.git/blobdiff - app/assets/javascripts/index/history.js
Update history.js - Correct function name for updating changeset locations
[rails.git] / app / assets / javascripts / index / history.js
index c6ba0c2edc9831853eedfec9f533531d71d5f341..289081ca1a9c2feee28951e10c213532ed1f4871 100644 (file)
@@ -1,10 +1,11 @@
 //= require jquery-simulate/jquery.simulate
 //= require jquery-simulate/jquery.simulate
+//= require ./history-changesets-layer
 
 OSM.History = function (map) {
 
 OSM.History = function (map) {
-  var page = {};
+  const page = {};
 
   $("#sidebar_content")
 
   $("#sidebar_content")
-    .on("click", ".changeset_more a", loadMore)
+    .on("click", ".changeset_more a", loadMoreChangesets)
     .on("mouseover", "[data-changeset]", function () {
       highlightChangeset($(this).data("changeset").id);
     })
     .on("mouseover", "[data-changeset]", function () {
       highlightChangeset($(this).data("changeset").id);
     })
@@ -12,7 +13,7 @@ OSM.History = function (map) {
       unHighlightChangeset($(this).data("changeset").id);
     });
 
       unHighlightChangeset($(this).data("changeset").id);
     });
 
-  var group = L.featureGroup()
+  const changesetsLayer = new OSM.HistoryChangesetsLayer()
     .on("mouseover", function (e) {
       highlightChangeset(e.layer.id);
     })
     .on("mouseover", function (e) {
       highlightChangeset(e.layer.id);
     })
@@ -23,19 +24,72 @@ OSM.History = function (map) {
       clickChangeset(e.layer.id, e.originalEvent);
     });
 
       clickChangeset(e.layer.id, e.originalEvent);
     });
 
-  group.getLayerId = function (layer) {
-    return layer.id;
-  };
+  let changesetIntersectionObserver;
+
+  function disableChangesetIntersectionObserver() {
+    if (changesetIntersectionObserver) {
+      changesetIntersectionObserver.disconnect();
+      changesetIntersectionObserver = null;
+    }
+  }
+
+  function enableChangesetIntersectionObserver() {
+    disableChangesetIntersectionObserver();
+    if (!window.IntersectionObserver) return;
+
+    let ignoreIntersectionEvents = true;
+
+    changesetIntersectionObserver = new IntersectionObserver((entries) => {
+      if (ignoreIntersectionEvents) {
+        ignoreIntersectionEvents = false;
+        return;
+      }
+
+      let closestTargetToTop,
+          closestDistanceToTop = Infinity,
+          closestTargetToBottom,
+          closestDistanceToBottom = Infinity;
+
+      for (const entry of entries) {
+        if (entry.isIntersecting) continue;
+
+        const distanceToTop = entry.rootBounds.top - entry.boundingClientRect.bottom;
+        const distanceToBottom = entry.boundingClientRect.top - entry.rootBounds.bottom;
+        if (distanceToTop >= 0 && distanceToTop < closestDistanceToTop) {
+          closestDistanceToTop = distanceToTop;
+          closestTargetToTop = entry.target;
+        }
+        if (distanceToBottom >= 0 && distanceToBottom <= closestDistanceToBottom) {
+          closestDistanceToBottom = distanceToBottom;
+          closestTargetToBottom = entry.target;
+        }
+      }
+
+      if (closestTargetToTop && closestDistanceToTop < closestDistanceToBottom) {
+        const id = $(closestTargetToTop).data("changeset")?.id;
+        if (id) {
+          OSM.router.replace(location.pathname + "?" + new URLSearchParams({ before: id }) + location.hash);
+        }
+      } else if (closestTargetToBottom) {
+        const id = $(closestTargetToBottom).data("changeset")?.id;
+        if (id) {
+          OSM.router.replace(location.pathname + "?" + new URLSearchParams({ after: id }) + location.hash);
+        }
+      }
+    }, { root: $("#sidebar")[0] });
+
+    $("#sidebar_content .changesets ol").children().each(function () {
+      changesetIntersectionObserver.observe(this);
+    });
+  }
 
   function highlightChangeset(id) {
 
   function highlightChangeset(id) {
-    var layer = group.getLayer(id);
-    if (layer) layer.setStyle({ fillOpacity: 0.3, color: "#FF6600", weight: 3 });
+    changesetsLayer.highlightChangeset(id);
     $("#changeset_" + id).addClass("selected");
   }
 
   function unHighlightChangeset(id) {
     $("#changeset_" + id).addClass("selected");
   }
 
   function unHighlightChangeset(id) {
-    var layer = group.getLayer(id);
-    if (layer) layer.setStyle({ fillOpacity: 0, color: "#FF9500", weight: 2 });
+    changesetsLayer.unHighlightChangeset(id);
     $("#changeset_" + id).removeClass("selected");
   }
 
     $("#changeset_" + id).removeClass("selected");
   }
 
@@ -45,102 +99,163 @@ OSM.History = function (map) {
 
   function displayFirstChangesets(html) {
     $("#sidebar_content .changesets").html(html);
 
   function displayFirstChangesets(html) {
     $("#sidebar_content .changesets").html(html);
+
+    if (location.pathname === "/history") {
+      setPaginationMapHashes();
+    }
+  }
+
+  function displayMoreChangesets(div, html) {
+    const sidebar = $("#sidebar")[0];
+    const previousScrollHeightMinusTop = sidebar.scrollHeight - sidebar.scrollTop;
+
+    const oldList = $("#sidebar_content .changesets ol");
+
+    div.replaceWith(html);
+
+    const prevNewList = oldList.prevAll("ol");
+    if (prevNewList.length) {
+      prevNewList.next(".changeset_more").remove();
+      prevNewList.children().prependTo(oldList);
+      prevNewList.remove();
+
+      // restore scroll position only if prepending
+      sidebar.scrollTop = sidebar.scrollHeight - previousScrollHeightMinusTop;
+    }
+
+    const nextNewList = oldList.nextAll("ol");
+    if (nextNewList.length) {
+      nextNewList.prev(".changeset_more").remove();
+      nextNewList.children().appendTo(oldList);
+      nextNewList.remove();
+    }
+
+    if (location.pathname === "/history") {
+      setPaginationMapHashes();
+    }
   }
 
   }
 
-  function displayMoreChangesets(html) {
-    $("#sidebar_content .changeset_more").replaceWith(html);
-    var oldList = $("#sidebar_content .changesets ol").first();
-    var newList = oldList.next("ol");
-    newList.children().appendTo(oldList);
-    newList.remove();
+  function setPaginationMapHashes() {
+    $("#sidebar .pagination a").each(function () {
+      $(this).prop("hash", OSM.formatHash({
+        center: map.getCenter(),
+        zoom: map.getZoom()
+      }));
+    });
   }
 
   }
 
-  function update() {
-    var data = { list: "1" };
+  function loadFirstChangesets() {
+    const data = new URLSearchParams();
+
+    disableChangesetIntersectionObserver();
 
 
-    if (window.location.pathname === "/history") {
-      data.bbox = map.getBounds().wrap().toBBoxString();
-      var feedLink = $("link[type=\"application/atom+xml\"]"),
-          feedHref = feedLink.attr("href").split("?")[0];
-      feedLink.attr("href", feedHref + "?bbox=" + data.bbox);
+    if (location.pathname === "/history") {
+      setBboxFetchData(data);
+      const feedLink = $("link[type=\"application/atom+xml\"]"),
+            feedHref = feedLink.attr("href").split("?")[0];
+      feedLink.attr("href", feedHref + "?" + data);
     }
 
     }
 
-    $.ajax({
-      url: window.location.pathname,
-      method: "GET",
-      data: data,
-      success: function (html) {
+    setListFetchData(data, location);
+
+    fetch(location.pathname + "?" + data)
+      .then(response => response.text())
+      .then(function (html) {
         displayFirstChangesets(html);
         displayFirstChangesets(html);
+        enableChangesetIntersectionObserver();
+
+        if (data.has("before")) {
+          const [firstItem] = $("#sidebar_content .changesets ol").children().first();
+          firstItem?.scrollIntoView();
+        } else if (data.has("after")) {
+          const [lastItem] = $("#sidebar_content .changesets ol").children().last();
+          lastItem?.scrollIntoView(false);
+        } else {
+          const [sidebar] = $("#sidebar");
+          sidebar.scrollTop = 0;
+        }
+
         updateMap();
         updateMap();
-      }
-    });
+      });
   }
 
   }
 
-  function loadMore(e) {
+  function loadMoreChangesets(e) {
     e.preventDefault();
     e.stopPropagation();
 
     e.preventDefault();
     e.stopPropagation();
 
-    var div = $(this).parents(".changeset_more");
+    const div = $(this).parents(".changeset_more");
 
 
-    $(this).hide();
-    div.find(".loader").show();
+    div.find(".pagination").addClass("invisible");
+    div.find("[hidden]").prop("hidden", false);
 
 
-    $.get($(this).attr("href"), function (html) {
-      displayMoreChangesets(html);
-      updateMap();
-    });
-  }
+    const data = new URLSearchParams();
 
 
-  var changesets = [];
+    if (location.pathname === "/history") {
+      setBboxFetchData(data);
+    }
 
 
-  function updateBounds() {
-    group.clearLayers();
+    const url = new URL($(this).attr("href"), location);
+    setListFetchData(data, url);
 
 
-    changesets.forEach(function (changeset) {
-      var bottomLeft = map.project(L.latLng(changeset.bbox.minlat, changeset.bbox.minlon)),
-          topRight = map.project(L.latLng(changeset.bbox.maxlat, changeset.bbox.maxlon)),
-          width = topRight.x - bottomLeft.x,
-          height = bottomLeft.y - topRight.y,
-          minSize = 20; // Min width/height of changeset in pixels
+    fetch(url.pathname + "?" + data)
+      .then(response => response.text())
+      .then(function (html) {
+        displayMoreChangesets(div, html);
+        enableChangesetIntersectionObserver();
 
 
-      if (width < minSize) {
-        bottomLeft.x -= ((minSize - width) / 2);
-        topRight.x += ((minSize - width) / 2);
-      }
+        updateMap();
+      });
+  }
 
 
-      if (height < minSize) {
-        bottomLeft.y += ((minSize - height) / 2);
-        topRight.y -= ((minSize - height) / 2);
-      }
+  function setBboxFetchData(data) {
+    const crs = map.options.crs;
+    const sw = map.getBounds().getSouthWest();
+    const ne = map.getBounds().getNorthEast();
+    const swClamped = crs.unproject(crs.project(sw));
+    const neClamped = crs.unproject(crs.project(ne));
 
 
-      changeset.bounds = L.latLngBounds(map.unproject(bottomLeft),
-                                        map.unproject(topRight));
-    });
+    if (sw.lat >= swClamped.lat || ne.lat <= neClamped.lat || ne.lng - sw.lng < 360) {
+      data.set("bbox", map.getBounds().toBBoxString());
+    }
+  }
 
 
-    changesets.sort(function (a, b) {
-      return b.bounds.getSize() - a.bounds.getSize();
-    });
+  function setListFetchData(data, url) {
+    const params = new URLSearchParams(url.search);
+
+    data.set("list", "1");
+
+    if (params.has("before")) {
+      data.set("before", params.get("before"));
+    }
+    if (params.has("after")) {
+      data.set("after", params.get("after"));
+    }
+  }
 
 
-    for (var i = 0; i < changesets.length; ++i) {
-      var changeset = changesets[i],
-          rect = L.rectangle(changeset.bounds,
-                             { weight: 2, color: "#FF9500", opacity: 1, fillColor: "#FFFFAF", fillOpacity: 0 });
-      rect.id = changeset.id;
-      rect.addTo(group);
+  function moveEndListener() {
+    if (location.pathname === "/history") {
+      OSM.router.replace("/history" + window.location.hash);
+      loadFirstChangesets();
+    } else {
+      changesetsLayer.updateChangesetLocations(map);
     }
   }
 
     }
   }
 
+  function zoomEndListener() {
+    changesetsLayer.updateChangesetShapes(map);
+  }
+
   function updateMap() {
   function updateMap() {
-    changesets = $("[data-changeset]").map(function (index, element) {
+    const changesets = $("[data-changeset]").map(function (index, element) {
       return $(element).data("changeset");
     }).get().filter(function (changeset) {
       return changeset.bbox;
     });
 
       return $(element).data("changeset");
     }).get().filter(function (changeset) {
       return changeset.bbox;
     });
 
-    updateBounds();
+    changesetsLayer.updateChangesets(map, changesets);
 
 
-    if (window.location.pathname !== "/history") {
-      var bounds = group.getBounds();
+    if (location.pathname !== "/history") {
+      const bounds = changesetsLayer.getBounds();
       if (bounds.isValid()) map.fitBounds(bounds);
     }
   }
       if (bounds.isValid()) map.fitBounds(bounds);
     }
   }
@@ -150,20 +265,17 @@ OSM.History = function (map) {
   };
 
   page.load = function () {
   };
 
   page.load = function () {
-    map.addLayer(group);
-
-    if (window.location.pathname === "/history") {
-      map.on("moveend", update);
-    }
-
-    map.on("zoomend", updateBounds);
-
-    update();
+    map.addLayer(changesetsLayer);
+    map.on("moveend", moveEndListener);
+    map.on("zoomend", zoomEndListener);
+    loadFirstChangesets();
   };
 
   page.unload = function () {
   };
 
   page.unload = function () {
-    map.removeLayer(group);
-    map.off("moveend", update);
+    map.removeLayer(changesetsLayer);
+    map.off("moveend", moveEndListener);
+    map.off("zoomend", zoomEndListener);
+    disableChangesetIntersectionObserver();
   };
 
   return page;
   };
 
   return page;