]> git.openstreetmap.org Git - rails.git/blob - app/assets/javascripts/user.js
Extend MapLibre GeolocateControl class
[rails.git] / app / assets / javascripts / user.js
1 //= require maplibre.map
2 //= require maplibre.combinedcontrolgroup
3 //= require maplibre/controls
4 //= require maplibre/dom_util
5 //= require ./home_location_name-endpoint
6
7 $(function () {
8   let map, marker, deleted_lat, deleted_lon, deleted_home_name, homeLocationNameGeocoder, savedLat, savedLon;
9
10   if ($("#social_links").length) {
11     $("#add-social-link").on("click", function () {
12       const newIndex = -Date.now();
13
14       $("#social_links template").contents().clone().appendTo("#social_links")
15         .find("input").attr("name", `user[social_links_attributes][${newIndex}][url]`).trigger("focus");
16
17       renumberSocialLinks();
18     });
19
20     $("#social_links").on("click", "button", function () {
21       const row = $(this).closest(".row");
22       const [destroyCheckbox] = row.find(".social_link_destroy input[type='checkbox']");
23
24       if (destroyCheckbox) {
25         destroyCheckbox.checked = true;
26         row.addClass("d-none");
27       } else {
28         row.remove();
29       }
30
31       renumberSocialLinks();
32     });
33
34     $(".social_link_destroy input[type='checkbox']:checked").each(function () {
35       $(this).closest(".row").addClass("d-none");
36     });
37
38     renumberSocialLinks();
39   }
40
41   function renumberSocialLinks() {
42     $("#social_links .row:not(.d-none)").each(function (i) {
43       const inputLabel = OSM.i18n.t("javascripts.profile.social_link_n", { n: i + 1 });
44       const removeButtonLabel = OSM.i18n.t("javascripts.profile.remove_social_link_n", { n: i + 1 });
45
46       $(this).find("input[type='text']")
47         .attr("placeholder", inputLabel)
48         .attr("aria-label", inputLabel);
49       $(this).find("button")
50         .attr("title", removeButtonLabel);
51     });
52   }
53
54   if ($("#map").length) {
55     map = new maplibregl.Map(OSM.MapLibre.defaultSecondaryMapOptions);
56
57     savedLat = $("#home_lat").val();
58     savedLon = $("#home_lon").val();
59     homeLocationNameGeocoder = OSM.HomeLocationNameGeocoder($("#home_lat"), $("#home_lon"), $("#home_location_name"));
60
61     const position = $("html").attr("dir") === "rtl" ? "top-left" : "top-right";
62     const navigationControl = new OSM.MapLibre.NavigationControl();
63     const geolocateControl = new OSM.MapLibre.GeolocateControl();
64     map.addControl(new OSM.MapLibre.CombinedControlGroup([navigationControl, geolocateControl]), position);
65     map.touchZoomRotate.disableRotation();
66     map.keyboard.disableRotation();
67
68     marker = new OSM.MapLibre.Marker();
69
70     if (OSM.home) {
71       marker.setLngLat([OSM.home.lon, OSM.home.lat]);
72       marker.addTo(map);
73     }
74
75     map.on("click", function (e) {
76       if (!$("#updatehome").is(":checked")) return;
77
78       const [lat, lon] = OSM.cropLocation(e.lngLat, map.getZoom() + 1);
79
80       $("#home_lat").val(lat);
81       $("#home_lon").val(lon);
82
83       clearDeletedText();
84       respondToHomeLatLonUpdate();
85     });
86     map.on("moveend", function () {
87       const lat = $("#home_lat").val().trim(),
88             lon = $("#home_lon").val().trim();
89       let location;
90
91       try {
92         if (lat && lon) {
93           location = new maplibregl.LngLat(lon, lat);
94         }
95       } catch (error) {
96         // keep location undefined
97       }
98
99       $("#home_show").prop("disabled", !location || isCloseEnoughToMapCenter(location));
100     });
101
102     $("#home_lat, #home_lon").on("input", function () {
103       clearDeletedText();
104       respondToHomeLatLonUpdate();
105     });
106
107     $("#home_location_name").on("input", function () {
108       homeLocationNameGeocoder.autofill = false;
109       clearDeletedText();
110
111       respondToHomeLatLonUpdate(false);
112     });
113
114     $("#home_show").click(function () {
115       const lat = $("#home_lat").val(),
116             lon = $("#home_lon").val();
117
118       map.flyTo({ center: [lon, lat], zoom: OSM.MapLibre.defaultHomeZoom });
119     });
120
121     $("#home_delete").click(function () {
122       const lat = $("#home_lat").val(),
123             lon = $("#home_lon").val(),
124             locationName = $("#home_location_name").val();
125
126       $("#home_lat, #home_lon, #home_location_name").val("");
127       deleted_lat = lat;
128       deleted_lon = lon;
129       deleted_home_name = locationName;
130
131       respondToHomeLatLonUpdate(false);
132       $("#home_undelete").trigger("focus");
133     });
134
135     $("#home_undelete").click(function () {
136       $("#home_lat").val(deleted_lat);
137       $("#home_lon").val(deleted_lon);
138       $("#home_location_name").val(deleted_home_name);
139       clearDeletedText();
140
141       respondToHomeLatLonUpdate(false);
142       $("#home_delete").trigger("focus");
143     });
144   }
145
146   function respondToHomeLatLonUpdate(updateLocationName = true) {
147     const lat = $("#home_lat").val().trim(),
148           lon = $("#home_lon").val().trim(),
149           locationName = $("#home_location_name").val().trim();
150     let location;
151
152     try {
153       if (lat && lon) {
154         location = new maplibregl.LngLat(lon, lat);
155         if (updateLocationName) {
156           if (savedLat && savedLon && $("#home_location_name").val().trim()) {
157             homeLocationNameGeocoder.updateHomeLocationName(false, savedLat, savedLon, () => {
158               savedLat = savedLon = null;
159               homeLocationNameGeocoder.updateHomeLocationName();
160             });
161           } else {
162             savedLat = savedLon = null;
163             homeLocationNameGeocoder.updateHomeLocationName();
164           }
165         }
166       }
167       $("#home_lat, #home_lon").removeClass("is-invalid");
168     } catch (error) {
169       if (lat && isNaN(lat)) $("#home_lat").addClass("is-invalid");
170       if (lon && isNaN(lon)) $("#home_lon").addClass("is-invalid");
171     }
172
173     $("#home_message").toggleClass("invisible", Boolean(location));
174     $("#home_show").prop("hidden", !location);
175     $("#home_delete").prop("hidden", !location && !locationName);
176     $("#home_undelete").prop("hidden", !(
177       (!location || !locationName) &&
178       ((deleted_lat && deleted_lon) || deleted_home_name)
179     ));
180     if (location) {
181       marker.setLngLat([lon, lat]);
182       marker.addTo(map);
183       map.panTo([lon, lat]);
184     } else {
185       marker.remove();
186     }
187   }
188
189   function isCloseEnoughToMapCenter(location) {
190     const inputPt = map.project(location);
191     const centerPt = map.project(map.getCenter());
192
193     return centerPt.dist(inputPt) < 10;
194   }
195
196   function clearDeletedText() {
197     deleted_lat = null;
198     deleted_lon = null;
199     deleted_home_name = null;
200   }
201
202   $("input#user_avatar").on("change", function () {
203     $("#user_avatar_action_new").prop("checked", true);
204   });
205
206   $("#content.user_confirm").each(function () {
207     $(this).hide();
208     $(this).find("#confirm").submit();
209   });
210
211   $("input[name=legale]").change(function () {
212     $("#contributorTerms").html("<div class='spinner-border' role='status'><span class='visually-hidden'>" + OSM.i18n.t("browse.start_rjs.loading") + "</span></div>");
213     fetch(this.dataset.url, { headers: { "x-requested-with": "XMLHttpRequest" } })
214       .then(r => r.text())
215       .then(html => { $("#contributorTerms").html(html); });
216   });
217
218   $("#read_ct").on("click", function () {
219     $("#continue").prop("disabled", !($(this).prop("checked") && $("#read_tou").prop("checked")));
220   });
221
222   $("#read_tou").on("click", function () {
223     $("#continue").prop("disabled", !($(this).prop("checked") && $("#read_ct").prop("checked")));
224   });
225 });