1 /* exported Endpoint */
2 function Endpoint(map, input, marker, dragCallback, changeCallback) {
5 endpoint.marker = L.marker([0, 0], {
6 icon: OSM.getMarker(marker),
11 endpoint.enableListeners = function () {
12 endpoint.marker.on("drag", markerDragListener);
13 endpoint.marker.on("dragend", markerDragListener);
14 input.on("keydown", inputKeydownListener);
15 input.on("change", inputChangeListener);
18 endpoint.disableListeners = function () {
19 endpoint.marker.off("drag", markerDragListener);
20 endpoint.marker.off("dragend", markerDragListener);
21 input.off("keydown", inputKeydownListener);
22 input.off("change", inputChangeListener);
25 function markerDragListener(e) {
26 const { lat, lng } = OSM.cropLocation(e.target.getLatLng(), map.getZoom());
27 const latlng = L.latLng(lat, lng);
29 if (endpoint.geocodeRequest) endpoint.geocodeRequest.abort();
30 delete endpoint.geocodeRequest;
33 setInputValueFromLatLng(latlng);
34 endpoint.value = input.val();
35 if (e.type === "dragend") getReverseGeocode();
36 dragCallback(e.type === "drag");
39 function inputKeydownListener() {
40 input.removeClass("is-invalid");
43 function inputChangeListener(e) {
44 // make text the same in both text boxes
45 const value = e.target.value;
46 endpoint.setValue(value);
49 endpoint.setValue = function (value) {
50 if (endpoint.geocodeRequest) endpoint.geocodeRequest.abort();
51 delete endpoint.geocodeRequest;
52 input.removeClass("is-invalid");
54 const coordinatesMatch = value.match(/^\s*([+-]?\d+(?:\.\d*)?)(?:\s+|\s*[/,]\s*)([+-]?\d+(?:\.\d*)?)\s*$/);
55 const latlng = coordinatesMatch && L.latLng(coordinatesMatch[1], coordinatesMatch[2]);
57 if (latlng && endpoint.cachedReverseGeocode && endpoint.cachedReverseGeocode.latlng.equals(latlng)) {
59 if (endpoint.cachedReverseGeocode.notFound) {
60 endpoint.value = value;
61 input.addClass("is-invalid");
63 endpoint.value = endpoint.cachedReverseGeocode.value;
65 input.val(endpoint.value);
70 endpoint.value = value;
76 setInputValueFromLatLng(latlng);
79 } else if (endpoint.value) {
84 endpoint.clearValue = function () {
85 if (endpoint.geocodeRequest) endpoint.geocodeRequest.abort();
86 delete endpoint.geocodeRequest;
88 delete endpoint.value;
90 map.removeLayer(endpoint.marker);
93 endpoint.swapCachedReverseGeocodes = function (otherEndpoint) {
94 const g0 = endpoint.cachedReverseGeocode;
95 const g1 = otherEndpoint.cachedReverseGeocode;
96 delete endpoint.cachedReverseGeocode;
97 delete otherEndpoint.cachedReverseGeocode;
98 if (g0) otherEndpoint.cachedReverseGeocode = g0;
99 if (g1) endpoint.cachedReverseGeocode = g1;
102 function findPreferredEntrance(entrances, types) {
103 if (!entrances) return null;
105 for (const t of types) {
106 const matchId = entrances.find(e => e.type === t);
107 if (matchId) return matchId;
113 function getGeocode() {
114 const viewbox = map.getBounds().toBBoxString(), // <sw lon>,<sw lat>,<ne lon>,<ne lat>
115 geocodeUrl = OSM.NOMINATIM_URL + "search?" + new URLSearchParams({ q: endpoint.value, format: "json", viewbox, limit: 1, entrances: 1 });
117 endpoint.geocodeRequest = new AbortController();
118 fetch(geocodeUrl, { signal: endpoint.geocodeRequest.signal })
123 function success(json) {
124 delete endpoint.geocodeRequest;
125 if (json.length === 0) {
126 input.addClass("is-invalid");
127 OSM.showAlert(OSM.i18n.t("javascripts.directions.errors.no_place.title"),
128 OSM.i18n.t("javascripts.directions.errors.no_place.body", { place: endpoint.value }));
132 setLatLng(L.latLng(findPreferredEntrance(json[0].entrances, ["main", "yes"]) || json[0]));
134 endpoint.value = json[0].display_name;
135 input.val(json[0].display_name);
141 function getReverseGeocode() {
142 const latlng = endpoint.latlng.clone(),
143 { lat, lng } = latlng,
144 reverseGeocodeUrl = OSM.NOMINATIM_URL + "reverse?" + new URLSearchParams({ lat, lon: lng, format: "json" });
146 endpoint.geocodeRequest = new AbortController();
147 fetch(reverseGeocodeUrl, { signal: endpoint.geocodeRequest.signal })
152 function success(json) {
153 delete endpoint.geocodeRequest;
154 if (!json || !json.display_name) {
155 endpoint.cachedReverseGeocode = { latlng: latlng, notFound: true };
159 endpoint.value = json.display_name;
160 input.val(json.display_name);
161 endpoint.cachedReverseGeocode = { latlng: latlng, value: endpoint.value };
165 function setLatLng(ll) {
167 .attr("data-lat", ll.lat)
168 .attr("data-lon", ll.lng);
169 endpoint.latlng = ll;
175 function removeLatLng() {
177 .removeAttr("data-lat")
178 .removeAttr("data-lon");
179 delete endpoint.latlng;
182 function setInputValueFromLatLng(latlng) {
183 input.val(latlng.lat + ", " + latlng.lng);