X-Git-Url: https://git.openstreetmap.org/rails.git/blobdiff_plain/067b0de4391a32124aef23c56d73e2bb492df8ca..b21cf2908e235aaa15cb99ed8c1a95fc9d68a808:/app/assets/javascripts/index/history.js diff --git a/app/assets/javascripts/index/history.js b/app/assets/javascripts/index/history.js index e8d912685..85b5e96eb 100644 --- a/app/assets/javascripts/index/history.js +++ b/app/assets/javascripts/index/history.js @@ -1,10 +1,10 @@ //= require jquery-simulate/jquery.simulate OSM.History = function (map) { - var page = {}; + const page = {}; $("#sidebar_content") - .on("click", ".changeset_more a", loadMore) + .on("click", ".changeset_more a", loadMoreChangesets) .on("mouseover", "[data-changeset]", function () { highlightChangeset($(this).data("changeset").id); }) @@ -12,7 +12,7 @@ OSM.History = function (map) { unHighlightChangeset($(this).data("changeset").id); }); - var group = L.featureGroup() + const group = L.featureGroup() .on("mouseover", function (e) { highlightChangeset(e.layer.id); }) @@ -27,14 +27,73 @@ OSM.History = function (map) { 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) { - var layer = group.getLayer(id); + const layer = group.getLayer(id); if (layer) layer.setStyle({ fillOpacity: 0.3, color: "#FF6600", weight: 3 }); $("#changeset_" + id).addClass("selected"); } function unHighlightChangeset(id) { - var layer = group.getLayer(id); + const layer = group.getLayer(id); if (layer) layer.setStyle({ fillOpacity: 0, color: "#FF9500", weight: 2 }); $("#changeset_" + id).removeClass("selected"); } @@ -45,63 +104,147 @@ OSM.History = function (map) { 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(); - 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); + disableChangesetIntersectionObserver(); + + 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); + 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(); - } - }); + }); } - function loadMore(e) { + function loadMoreChangesets(e) { 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(); + + if (location.pathname === "/history") { + setBboxFetchData(data); + } + + const url = new URL($(this).attr("href"), location); + setListFetchData(data, url); + + fetch(url.pathname + "?" + data) + .then(response => response.text()) + .then(function (html) { + displayMoreChangesets(div, html); + enableChangesetIntersectionObserver(); + + updateMap(); + }); } - var changesets = []; + function setBboxFetchData(data) { + data.set("bbox", map.getBounds().wrap().toBBoxString()); + } + + 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")); + } + } + + function reloadChangesetsBecauseOfMapMovement() { + OSM.router.replace("/history" + window.location.hash); + loadFirstChangesets(); + } + + let changesets = []; function updateBounds() { group.clearLayers(); - 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 + for (const changeset of changesets) { + const 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 if (width < minSize) { bottomLeft.x -= ((minSize - width) / 2); @@ -115,16 +258,15 @@ OSM.History = function (map) { changeset.bounds = L.latLngBounds(map.unproject(bottomLeft), map.unproject(topRight)); - }); + } changesets.sort(function (a, b) { return b.bounds.getSize() - a.bounds.getSize(); }); - 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 }); + for (const changeset of changesets) { + const rect = L.rectangle(changeset.bounds, + { weight: 2, color: "#FF9500", opacity: 1, fillColor: "#FFFFAF", fillOpacity: 0 }); rect.id = changeset.id; rect.addTo(group); } @@ -139,34 +281,33 @@ OSM.History = function (map) { updateBounds(); - if (window.location.pathname !== "/history") { - var bounds = group.getBounds(); + if (location.pathname !== "/history") { + const bounds = group.getBounds(); if (bounds.isValid()) map.fitBounds(bounds); } } page.pushstate = page.popstate = function (path) { - $("#history_tab").addClass("current"); OSM.loadSidebarContent(path, page.load); }; page.load = function () { map.addLayer(group); - if (window.location.pathname === "/history") { - map.on("moveend", update); + if (location.pathname === "/history") { + map.on("moveend", reloadChangesetsBecauseOfMapMovement); } map.on("zoomend", updateBounds); - update(); + loadFirstChangesets(); }; page.unload = function () { map.removeLayer(group); - map.off("moveend", update); - - $("#history_tab").removeClass("current"); + map.off("moveend", reloadChangesetsBecauseOfMapMovement); + map.off("zoomend", updateBounds); + disableChangesetIntersectionObserver(); }; return page;