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 dragend", markerDragListener);
13 input.on("keydown", inputKeydownListener);
14 input.on("change", inputChangeListener);
17 endpoint.disableListeners = function () {
18 endpoint.marker.off("drag dragend", markerDragListener);
19 input.off("keydown", inputKeydownListener);
20 input.off("change", inputChangeListener);
23 function markerDragListener(e) {
24 const { lat, lng } = OSM.cropLocation(e.target.getLatLng(), map.getZoom());
25 const latlng = L.latLng(lat, lng);
27 if (endpoint.geocodeRequest) endpoint.geocodeRequest.abort();
28 delete endpoint.geocodeRequest;
31 setInputValueFromLatLng(latlng);
32 endpoint.value = input.val();
33 if (e.type === "dragend") getReverseGeocode();
34 dragCallback(e.type === "drag");
37 function inputKeydownListener() {
38 input.removeClass("is-invalid");
41 function inputChangeListener(e) {
42 // make text the same in both text boxes
43 const value = e.target.value;
44 endpoint.setValue(value);
47 endpoint.setValue = function (value) {
48 if (endpoint.geocodeRequest) endpoint.geocodeRequest.abort();
49 delete endpoint.geocodeRequest;
50 input.removeClass("is-invalid");
52 const coordinatesMatch = value.match(/^\s*([+-]?\d+(?:\.\d*)?)(?:\s+|\s*[/,]\s*)([+-]?\d+(?:\.\d*)?)\s*$/);
53 const latlng = coordinatesMatch && L.latLng(coordinatesMatch[1], coordinatesMatch[2]);
55 if (latlng && endpoint.cachedReverseGeocode && endpoint.cachedReverseGeocode.latlng.equals(latlng)) {
57 if (endpoint.cachedReverseGeocode.notFound) {
58 endpoint.value = value;
59 input.addClass("is-invalid");
61 endpoint.value = endpoint.cachedReverseGeocode.value;
63 input.val(endpoint.value);
68 endpoint.value = value;
74 setInputValueFromLatLng(latlng);
77 } else if (endpoint.value) {
82 endpoint.clearValue = function () {
83 if (endpoint.geocodeRequest) endpoint.geocodeRequest.abort();
84 delete endpoint.geocodeRequest;
86 delete endpoint.value;
88 map.removeLayer(endpoint.marker);
91 endpoint.swapCachedReverseGeocodes = function (otherEndpoint) {
92 const g0 = endpoint.cachedReverseGeocode;
93 const g1 = otherEndpoint.cachedReverseGeocode;
94 delete endpoint.cachedReverseGeocode;
95 delete otherEndpoint.cachedReverseGeocode;
96 if (g0) otherEndpoint.cachedReverseGeocode = g0;
97 if (g1) endpoint.cachedReverseGeocode = g1;
100 function findPreferredEntrance(entrances, types) {
101 if (!entrances) return null;
103 for (const t of types) {
104 const matchId = entrances.find(e => e.type === t);
105 if (matchId) return matchId;
111 function getGeocode() {
112 const viewbox = map.getBounds().toBBoxString(), // <sw lon>,<sw lat>,<ne lon>,<ne lat>
113 geocodeUrl = OSM.NOMINATIM_URL + "search?" + new URLSearchParams({ q: endpoint.value, format: "json", viewbox, limit: 1, entrances: 1 });
115 endpoint.geocodeRequest = new AbortController();
116 fetch(geocodeUrl, { signal: endpoint.geocodeRequest.signal })
121 function success(json) {
122 delete endpoint.geocodeRequest;
123 if (json.length === 0) {
124 input.addClass("is-invalid");
125 OSM.showAlert(OSM.i18n.t("javascripts.directions.errors.no_place.title"),
126 OSM.i18n.t("javascripts.directions.errors.no_place.body", { place: endpoint.value }));
130 setLatLng(L.latLng(findPreferredEntrance(json[0].entrances, ["main", "yes"]) || json[0]));
132 endpoint.value = json[0].display_name;
133 input.val(json[0].display_name);
139 function getReverseGeocode() {
140 const latlng = endpoint.latlng.clone(),
141 { lat, lng } = latlng,
142 reverseGeocodeUrl = OSM.NOMINATIM_URL + "reverse?" + new URLSearchParams({ lat, lon: lng, format: "json" });
144 endpoint.geocodeRequest = new AbortController();
145 fetch(reverseGeocodeUrl, { signal: endpoint.geocodeRequest.signal })
150 function success(json) {
151 delete endpoint.geocodeRequest;
152 if (!json || !json.display_name) {
153 endpoint.cachedReverseGeocode = { latlng: latlng, notFound: true };
157 endpoint.value = json.display_name;
158 input.val(json.display_name);
159 endpoint.cachedReverseGeocode = { latlng: latlng, value: endpoint.value };
163 function setLatLng(ll) {
165 .attr("data-lat", ll.lat)
166 .attr("data-lon", ll.lng);
167 endpoint.latlng = ll;
173 function removeLatLng() {
175 .removeAttr("data-lat")
176 .removeAttr("data-lon");
177 delete endpoint.latlng;
180 function setInputValueFromLatLng(latlng) {
181 input.val(latlng.lat + ", " + latlng.lng);