]> git.openstreetmap.org Git - rails.git/blob - app/assets/javascripts/index_modules/search.js
Merge pull request #7191 from CommanderStorm/fix-attribution-listener-leak
[rails.git] / app / assets / javascripts / index_modules / search.js
1 export default function (map) {
2   $("#sidebar_content")
3     .on("click", ".search_more a", clickSearchMore)
4     .on("click", ".search_results_entry a.set_position", clickSearchResult);
5
6   const markers = L.layerGroup().addTo(map);
7   let processedResults = 0;
8
9   function clickSearchMore(e) {
10     e.preventDefault();
11     e.stopPropagation();
12
13     const div = $(this).parents(".search_more");
14
15     $(this).hide();
16     div.find(".loader").prop("hidden", false);
17
18     fetchReplace(this, div);
19   }
20
21   function fetchReplace({ href }, $target) {
22     return fetch(href, {
23       method: "POST",
24       body: new URLSearchParams(OSM.csrf)
25     })
26       .then(response => response.text())
27       .then(html => {
28         const result = $(html);
29         $target.replaceWith(result);
30         result.filter("ul").children().each(showSearchResult);
31       });
32   }
33
34   function showSearchResult() {
35     const index = processedResults++;
36     const listItem = $(this);
37     const inverseGoldenAngle = (Math.sqrt(5) - 1) * 180;
38     const color = `hwb(${(index * inverseGoldenAngle) % 360}deg 5% 5%)`;
39     listItem.css("--marker-color", color);
40     const data = listItem.find("a.set_position").data();
41     const marker = L.marker([data.lat, data.lon], { icon: OSM.getMarker({ color, className: "activatable" }) });
42     marker.on("mouseover", () => listItem.addClass("bg-body-secondary"));
43     marker.on("mouseout", () => listItem.removeClass("bg-body-secondary"));
44     marker.on("click", function (e) {
45       OSM.router.click(e.originalEvent, listItem.find("a.set_position").attr("href"));
46     });
47     markers.addLayer(marker);
48     listItem.on("mouseover", () => $(marker.getElement()).addClass("active"));
49     listItem.on("mouseout", () => $(marker.getElement()).removeClass("active"));
50   }
51
52   function panToSearchResult(data) {
53     if (data.minLon && data.minLat && data.maxLon && data.maxLat) {
54       map.fitBounds([[data.minLat, data.minLon], [data.maxLat, data.maxLon]]);
55     } else {
56       map.setView([data.lat, data.lon], data.zoom);
57     }
58   }
59
60   function clickSearchResult(e) {
61     const data = $(this).data();
62
63     panToSearchResult(data);
64
65     // Let clicks to object browser links propagate.
66     if (data.type && data.id) return;
67
68     e.preventDefault();
69     e.stopPropagation();
70   }
71
72   const page = {};
73
74   page.pushstate = page.popstate = function (path) {
75     const params = new URLSearchParams(path.substring(path.indexOf("?")));
76     if (params.has("query")) {
77       $(".search_form input[name=query]").val(params.get("query"));
78     } else if (params.has("lat") && params.has("lon")) {
79       $(".search_form input[name=query]").val(params.get("lat") + ", " + params.get("lon"));
80     }
81     OSM.loadSidebarContent(path, page.load);
82   };
83
84   page.load = function () {
85     $(".search_results_entry[data-href]").each(function (index) {
86       const entry = $(this);
87       fetchReplace(this.dataset, entry.children().first())
88         .then(() => {
89           // go to first result of first geocoder
90           if (index === 0) {
91             const firstResult = entry.find("*[data-lat][data-lon]:first").first();
92             if (firstResult.length) {
93               panToSearchResult(firstResult.data());
94             }
95           }
96         });
97     });
98
99     return map.getState();
100   };
101
102   page.unload = function () {
103     markers.clearLayers();
104     processedResults = 0;
105   };
106
107   return page;
108 }