//= 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);
})
unHighlightChangeset($(this).data("changeset").id);
});
- var group = L.featureGroup()
+ const group = L.featureGroup()
.on("mouseover", function (e) {
highlightChangeset(e.layer.id);
})
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");
}
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);
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);
}
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;