]> git.openstreetmap.org Git - rails.git/blob - app/assets/javascripts/index.js
Merge remote-tracking branch 'upstream/pull/5686'
[rails.git] / app / assets / javascripts / index.js
1 //= require_self
2 //= require leaflet.sidebar
3 //= require leaflet.sidebar-pane
4 //= require leaflet.locatecontrol/dist/L.Control.Locate.umd
5 //= require leaflet.locate
6 //= require leaflet.layers
7 //= require leaflet.key
8 //= require leaflet.note
9 //= require leaflet.share
10 //= require leaflet.polyline
11 //= require leaflet.query
12 //= require leaflet.contextmenu
13 //= require index/contextmenu
14 //= require index/search
15 //= require index/layers/data
16 //= require index/export
17 //= require index/layers/notes
18 //= require index/history
19 //= require index/note
20 //= require index/new_note
21 //= require index/directions
22 //= require index/changeset
23 //= require index/query
24 //= require index/home
25 //= require router
26
27 $(document).ready(function () {
28   var map = new L.OSM.Map("map", {
29     zoomControl: false,
30     layerControl: false,
31     contextmenu: true,
32     worldCopyJump: true
33   });
34
35   OSM.loadSidebarContent = function (path, callback) {
36     var content_path = path;
37
38     map.setSidebarOverlaid(false);
39
40     $("#sidebar_loader").show().addClass("delayed-fade-in");
41
42     // Prevent caching the XHR response as a full-page URL
43     // https://github.com/openstreetmap/openstreetmap-website/issues/5663
44     if (content_path.indexOf("?") >= 0) {
45       content_path += "&xhr=1";
46     } else {
47       content_path += "?xhr=1";
48     }
49
50     $("#sidebar_content")
51       .empty();
52
53     fetch(content_path, { headers: { "accept": "text/html", "x-requested-with": "XMLHttpRequest" } })
54       .then(response => {
55         $("#flash").empty();
56         $("#sidebar_loader").removeClass("delayed-fade-in").hide();
57
58         const title = response.headers.get("X-Page-Title");
59         if (title) document.title = decodeURIComponent(title);
60
61         return response.text();
62       })
63       .then(html => {
64         const content = $(html);
65
66         $("head")
67           .find("link[type=\"application/atom+xml\"]")
68           .remove();
69
70         $("head")
71           .append(content.filter("link[type=\"application/atom+xml\"]"));
72
73         $("#sidebar_content").html(content.not("link[type=\"application/atom+xml\"]"));
74
75         if (callback) {
76           callback();
77         }
78       });
79   };
80
81   var params = OSM.mapParams();
82
83   map.attributionControl.setPrefix("");
84
85   map.updateLayers(params.layers);
86
87   map.on("baselayerchange", function (e) {
88     if (map.getZoom() > e.layer.options.maxZoom) {
89       map.setView(map.getCenter(), e.layer.options.maxZoom, { reset: true });
90     }
91   });
92
93   var sidebar = L.OSM.sidebar("#map-ui")
94     .addTo(map);
95
96   var position = $("html").attr("dir") === "rtl" ? "topleft" : "topright";
97
98   function addControlGroup(controls) {
99     for (const control of controls) control.addTo(map);
100
101     var firstContainer = controls[0].getContainer();
102     $(firstContainer).find(".control-button").first()
103       .addClass("control-button-first");
104
105     var lastContainer = controls[controls.length - 1].getContainer();
106     $(lastContainer).find(".control-button").last()
107       .addClass("control-button-last");
108   }
109
110   addControlGroup([
111     L.OSM.zoom({ position: position }),
112     L.OSM.locate({ position: position })
113   ]);
114
115   addControlGroup([
116     L.OSM.layers({
117       position: position,
118       layers: map.baseLayers,
119       sidebar: sidebar
120     }),
121     L.OSM.key({
122       position: position,
123       sidebar: sidebar
124     }),
125     L.OSM.share({
126       "position": position,
127       "sidebar": sidebar,
128       "short": true
129     })
130   ]);
131
132   addControlGroup([
133     L.OSM.note({
134       position: position,
135       sidebar: sidebar
136     })
137   ]);
138
139   addControlGroup([
140     L.OSM.query({
141       position: position,
142       sidebar: sidebar
143     })
144   ]);
145
146   L.control.scale()
147     .addTo(map);
148
149   OSM.initializeContextMenu(map);
150
151   if (OSM.STATUS !== "api_offline" && OSM.STATUS !== "database_offline") {
152     OSM.initializeNotesLayer(map);
153     if (params.layers.indexOf(map.noteLayer.options.code) >= 0) {
154       map.addLayer(map.noteLayer);
155     }
156
157     OSM.initializeDataLayer(map);
158     if (params.layers.indexOf(map.dataLayer.options.code) >= 0) {
159       map.addLayer(map.dataLayer);
160     }
161
162     if (params.layers.indexOf(map.gpsLayer.options.code) >= 0) {
163       map.addLayer(map.gpsLayer);
164     }
165   }
166
167   $(".leaflet-control .control-button").tooltip({ placement: "left", container: "body" });
168
169   var expiry = new Date();
170   expiry.setYear(expiry.getFullYear() + 10);
171
172   map.on("moveend baselayerchange overlayadd overlayremove", function () {
173     updateLinks(
174       map.getCenter().wrap(),
175       map.getZoom(),
176       map.getLayersCode(),
177       map._object);
178
179     Cookies.set("_osm_location", OSM.locationCookie(map), { secure: true, expires: expiry, path: "/", samesite: "lax" });
180   });
181
182   if (Cookies.get("_osm_welcome") !== "hide") {
183     $(".welcome").removeAttr("hidden");
184   }
185
186   $(".welcome .btn-close").on("click", function () {
187     $(".welcome").hide();
188     Cookies.set("_osm_welcome", "hide", { secure: true, expires: expiry, path: "/", samesite: "lax" });
189   });
190
191   var bannerExpiry = new Date();
192   bannerExpiry.setYear(bannerExpiry.getFullYear() + 1);
193
194   $("#banner .btn-close").on("click", function (e) {
195     var cookieId = e.target.id;
196     $("#banner").hide();
197     e.preventDefault();
198     if (cookieId) {
199       Cookies.set(cookieId, "hide", { secure: true, expires: bannerExpiry, path: "/", samesite: "lax" });
200     }
201   });
202
203   if (OSM.MATOMO) {
204     map.on("baselayerchange overlayadd", function (e) {
205       if (e.layer.options) {
206         var goal = OSM.MATOMO.goals[e.layer.options.layerId];
207
208         if (goal) {
209           $("body").trigger("matomogoal", goal);
210         }
211       }
212     });
213   }
214
215   if (params.bounds) {
216     map.fitBounds(params.bounds);
217   } else {
218     map.setView([params.lat, params.lon], params.zoom);
219   }
220
221   if (params.marker) {
222     L.marker([params.mlat, params.mlon]).addTo(map);
223   }
224
225   function remoteEditHandler(bbox, object) {
226     var remoteEditHost = "http://127.0.0.1:8111",
227         osmHost = location.protocol + "//" + location.host,
228         query = new URLSearchParams({
229           left: bbox.getWest() - 0.0001,
230           top: bbox.getNorth() + 0.0001,
231           right: bbox.getEast() + 0.0001,
232           bottom: bbox.getSouth() - 0.0001
233         });
234
235     if (object && object.type !== "note") query.set("select", object.type + object.id); // can't select notes
236     sendRemoteEditCommand(remoteEditHost + "/load_and_zoom?" + query, function () {
237       if (object && object.type === "note") {
238         const noteQuery = new URLSearchParams({ url: osmHost + OSM.apiUrl(object) });
239         sendRemoteEditCommand(remoteEditHost + "/import?" + noteQuery);
240       }
241     });
242
243     function sendRemoteEditCommand(url, callback) {
244       fetch(url, { mode: "no-cors", signal: AbortSignal.timeout(5000) })
245         .then(callback)
246         .catch(function () {
247           // eslint-disable-next-line no-alert
248           alert(I18n.t("site.index.remote_failed"));
249         });
250     }
251
252     return false;
253   }
254
255   $("a[data-editor=remote]").click(function (e) {
256     var params = OSM.mapParams(this.search);
257     remoteEditHandler(map.getBounds(), params.object);
258     e.preventDefault();
259   });
260
261   if (OSM.params().edit_help) {
262     $("#editanchor")
263       .removeAttr("title")
264       .tooltip({
265         placement: "bottom",
266         title: I18n.t("javascripts.edit_help")
267       })
268       .tooltip("show");
269
270     $("body").one("click", function () {
271       $("#editanchor").tooltip("hide");
272     });
273   }
274
275   OSM.Index = function (map) {
276     var page = {};
277
278     page.pushstate = page.popstate = function () {
279       map.setSidebarOverlaid(true);
280       document.title = I18n.t("layouts.project_name.title");
281     };
282
283     page.load = function () {
284       const params = new URLSearchParams(location.search);
285       if (params.has("query")) {
286         $("#sidebar .search_form input[name=query]").value(params.get("query"));
287       }
288       if (!("autofocus" in document.createElement("input"))) {
289         $("#sidebar .search_form input[name=query]").focus();
290       }
291       return map.getState();
292     };
293
294     return page;
295   };
296
297   OSM.Browse = function (map, type) {
298     var page = {};
299
300     page.pushstate = page.popstate = function (path, id) {
301       OSM.loadSidebarContent(path, function () {
302         addObject(type, id);
303       });
304     };
305
306     page.load = function (path, id) {
307       addObject(type, id, true);
308     };
309
310     function addObject(type, id, center) {
311       var hashParams = OSM.parseHash(window.location.hash);
312       map.addObject({ type: type, id: parseInt(id, 10) }, function (bounds) {
313         if (!hashParams.center && bounds.isValid() &&
314             (center || !map.getBounds().contains(bounds))) {
315           OSM.router.withoutMoveListener(function () {
316             map.fitBounds(bounds);
317           });
318         }
319       });
320     }
321
322     page.unload = function () {
323       map.removeObject();
324     };
325
326     return page;
327   };
328
329   OSM.OldBrowse = function () {
330     var page = {};
331
332     page.pushstate = page.popstate = function (path) {
333       OSM.loadSidebarContent(path);
334     };
335
336     return page;
337   };
338
339   var history = OSM.History(map);
340
341   OSM.router = OSM.Router(map, {
342     "/": OSM.Index(map),
343     "/search": OSM.Search(map),
344     "/directions": OSM.Directions(map),
345     "/export": OSM.Export(map),
346     "/note/new": OSM.NewNote(map),
347     "/history/friends": history,
348     "/history/nearby": history,
349     "/history": history,
350     "/user/:display_name/history": history,
351     "/note/:id": OSM.Note(map),
352     "/node/:id(/history)": OSM.Browse(map, "node"),
353     "/node/:id/history/:version": OSM.OldBrowse(),
354     "/way/:id(/history)": OSM.Browse(map, "way"),
355     "/way/:id/history/:version": OSM.OldBrowse(),
356     "/relation/:id(/history)": OSM.Browse(map, "relation"),
357     "/relation/:id/history/:version": OSM.OldBrowse(),
358     "/changeset/:id": OSM.Changeset(map),
359     "/query": OSM.Query(map),
360     "/account/home": OSM.Home(map)
361   });
362
363   if (OSM.preferred_editor === "remote" && document.location.pathname === "/edit") {
364     remoteEditHandler(map.getBounds(), params.object);
365     OSM.router.setCurrentPath("/");
366   }
367
368   OSM.router.load();
369
370   $(document).on("click", "a", function (e) {
371     if (e.isDefaultPrevented() || e.isPropagationStopped() || $(e.target).data("turbo")) {
372       return;
373     }
374
375     // Open links in a new tab as normal.
376     if (e.which > 1 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {
377       return;
378     }
379
380     // Ignore cross-protocol and cross-origin links.
381     if (location.protocol !== this.protocol || location.host !== this.host) {
382       return;
383     }
384
385     if (OSM.router.route(this.pathname + this.search + this.hash)) {
386       e.preventDefault();
387       if (this.pathname !== "/directions") {
388         $("header").addClass("closed");
389       }
390     }
391   });
392
393   $(document).on("click", "#sidebar_content .btn-close", function () {
394     OSM.router.route("/" + OSM.formatHash(map));
395   });
396 });