PageSourceSearch

https://worldmaps3d.com/js/find-search-autocomplete.js?v52

js worldmaps3d.com collected 2026-10-02 01:49:10 UTC 11,406 bytes, 422 lines download raw bytes

1/**
2 * Find hero: suggestions via /api/geocode (maps.co) with debounce + throttle.
3 */
4(function () {
5  var DEBOUNCE_MS = 350;
6  var MIN_INTERVAL_MS = 900;
7
8  var form = document.querySelector("form[data-place-autocomplete]");
9  if (!form) {
10    return;
11  }
12
13  var input = form.querySelector("input[name='mapQuery']");
14  var clearBtn = form.querySelector(".search-panel-clear");
15  var list = form.querySelector(".search-suggest");
16  if (!input || !list) {
17    return;
18  }
19
20  var debounceTimer = null;
21  var throttleTimer = null;
22  var lastFetchStart = 0;
23  var pendingQuery = null;
24  var abortCtl = null;
25  var activeIndex = -1;
26  var results = [];
27
28  function closeList() {
29    list.hidden = true;
30    list.innerHTML = "";
31    results = [];
32    activeIndex = -1;
33    input.setAttribute("aria-expanded", "false");
34  }
35
36  function openList() {
37    if (list.children.length) {
38      list.hidden = false;
39      input.setAttribute("aria-expanded", "true");
40    }
41  }
42
43  var inputRow = form.querySelector(".search-panel-input-row");
44  if (inputRow && clearBtn && typeof worldmaps3dBindInputClear === "function") {
45    worldmaps3dBindInputClear(inputRow, {
46      input: input,
47      clearBtn: clearBtn,
48      onClear: function () {
49        closeList();
50      },
51    });
52  }
53
54  function goNextFromPickedQuery(queryText) {
55    var q = String(queryText || "").trim();
56    var flow = form.getAttribute("data-search-flow");
57    if (flow === "loading-3d") {
58      window.location.href = q
59        ? "find-loading?q=" + encodeURIComponent(q) + "&entry=3d"
60        : "3d-maps";
61    } else if (flow === "loading-dark") {
62      window.location.href = q
63        ? "find-loading?q=" + encodeURIComponent(q) + "&entry=dark"
64        : "dark-maps";
65    } else if (flow === "loading-radar") {
66      window.location.href = q
67        ? "find-loading?q=" + encodeURIComponent(q) + "&entry=radar"
68        : "radar-maps";
69    } else if (flow === "loading-streets") {
70      window.location.href = q
71        ? "find-loading?q=" + encodeURIComponent(q) + "&entry=streets"
72        : "streets-maps";
73    } else if (flow === "loading-weather") {
74      window.location.href = q
75        ? "find-loading?q=" + encodeURIComponent(q) + "&entry=weather"
76        : "weather-maps";
77    } else if (flow === "loading") {
78      window.location.href = q
79        ? "find-loading?q=" + encodeURIComponent(q)
80        : "find";
81    } else if (flow === "pwa-loading" || flow === "pwa-inline") {
82      if (!q) {
83        return;
84      }
85      window.dispatchEvent(
86        new CustomEvent("worldmaps3d-pwa-search", {
87          detail: { q: q },
88        })
89      );
90    } else {
91      window.location.href = q
92        ? "find?q=" + encodeURIComponent(q)
93        : "find";
94    }
95  }
96
97  function goNextFromPickedItem(item, queryText) {
98    var q = String(queryText || "").trim();
99    if (!q) {
100      return;
101    }
102    var flow = form.getAttribute("data-search-flow");
103    /* Inline map search (/maps-search): stay on page, let maps-search.js handle flyTo */
104    if (flow === "maps-app") {
105      var hitMa = worldmaps3dFirstNominatimResult([item]);
106      if (!hitMa) {
107        return;
108      }
109      if (
110        typeof worldmaps3dSchedulePersistNominatimItem === "function" &&
111        item &&
112        !item._fromGeoCache
113      ) {
114        worldmaps3dSchedulePersistNominatimItem(item, q);
115      }
116      window.dispatchEvent(
117        new CustomEvent("worldmaps3d-maps-search-pick", {
118          detail: { hit: hitMa, queryText: q, item: item },
119        })
120      );
121      return;
122    }
123    if (flow === "loading-3d") {
124      var hit3d = worldmaps3dFirstNominatimResult([item]);
125      if (!hit3d) {
126        goNextFromPickedQuery(q);
127        return;
128      }
129      window.location.href =
130        "find-loading?q=" +
131        encodeURIComponent(q) +
132        "&lat=" +
133        encodeURIComponent(String(hit3d.lat)) +
134        "&lon=" +
135        encodeURIComponent(String(hit3d.lon)) +
136        "&entry=3d";
137      return;
138    }
139    if (flow === "loading-dark") {
140      var hitDark = worldmaps3dFirstNominatimResult([item]);
141      if (!hitDark) {
142        goNextFromPickedQuery(q);
143        return;
144      }
145      window.location.href =
146        "find-loading?q=" +
147        encodeURIComponent(q) +
148        "&lat=" +
149        encodeURIComponent(String(hitDark.lat)) +
150        "&lon=" +
151        encodeURIComponent(String(hitDark.lon)) +
152        "&entry=dark";
153      return;
154    }
155    if (flow === "loading-radar") {
156      var hitR = worldmaps3dFirstNominatimResult([item]);
157      if (!hitR) {
158        goNextFromPickedQuery(q);
159        return;
160      }
161      window.location.href =
162        "find-loading?q=" +
163        encodeURIComponent(q) +
164        "&lat=" +
165        encodeURIComponent(String(hitR.lat)) +
166        "&lon=" +
167        encodeURIComponent(String(hitR.lon)) +
168        "&entry=radar";
169      return;
170    }
171    if (flow === "loading-streets") {
172      var hitS = worldmaps3dFirstNominatimResult([item]);
173      if (!hitS) {
174        goNextFromPickedQuery(q);
175        return;
176      }
177      window.location.href =
178        "find-loading?q=" +
179        encodeURIComponent(q) +
180        "&lat=" +
181        encodeURIComponent(String(hitS.lat)) +
182        "&lon=" +
183        encodeURIComponent(String(hitS.lon)) +
184        "&entry=streets";
185      return;
186    }
187    if (flow === "loading-weather") {
188      var hitW = worldmaps3dFirstNominatimResult([item]);
189      if (!hitW) {
190        goNextFromPickedQuery(q);
191        return;
192      }
193      window.location.href =
194        "find-loading?q=" +
195        encodeURIComponent(q) +
196        "&lat=" +
197        encodeURIComponent(String(hitW.lat)) +
198        "&lon=" +
199        encodeURIComponent(String(hitW.lon)) +
200        "&entry=weather";
201      return;
202    }
203    if (flow === "pwa-loading" || flow === "pwa-inline") {
204      var hitPwa = worldmaps3dFirstNominatimResult([item]);
205      if (!hitPwa) {
206        goNextFromPickedQuery(q);
207        return;
208      }
209      if (
210        typeof worldmaps3dSchedulePersistNominatimItem === "function" &&
211        item &&
212        !item._fromGeoCache
213      ) {
214        worldmaps3dSchedulePersistNominatimItem(item, q);
215      }
216      window.dispatchEvent(
217        new CustomEvent("worldmaps3d-pwa-search", {
218          detail: {
219            q: q,
220            lat: hitPwa.lat,
221            lon: hitPwa.lon,
222            address: String(hitPwa.display_name || "").trim(),
223          },
224        })
225      );
226      return;
227    }
228    if (flow !== "loading") {
229      goNextFromPickedQuery(q);
230      return;
231    }
232    var hit = worldmaps3dFirstNominatimResult([item]);
233    if (!hit) {
234      goNextFromPickedQuery(q);
235      return;
236    }
237    window.location.href =
238      "find-loading" +
239      "?q=" +
240      encodeURIComponent(q) +
241      "&lat=" +
242      encodeURIComponent(String(hit.lat)) +
243      "&lon=" +
244      encodeURIComponent(String(hit.lon));
245  }
246
247  function render(items) {
248    list.innerHTML = "";
249    results = items || [];
250    activeIndex = -1;
251    if (!results.length) {
252      closeList();
253      return;
254    }
255    results.forEach(function (item, idx) {
256      var lines = worldmaps3dNominatimItemToLines(item);
257      var row = document.createElement("button");
258      row.type = "button";
259      row.className = "search-suggest-item";
260      row.setAttribute("role", "option");
261      row.id = "find-suggest-opt-" + idx;
262      row.dataset.index = String(idx);
263      var t = document.createElement("div");
264      t.className = "search-suggest-title";
265      t.textContent = lines.title;
266      row.appendChild(t);
267      if (lines.subtitle) {
268        var s = document.createElement("div");
269        s.className = "search-suggest-sub";
270        s.textContent = lines.subtitle;
271        row.appendChild(s);
272      }
273      row.addEventListener("mousedown", function (e) {
274        e.preventDefault();
275      });
276      row.addEventListener("click", function () {
277        pickIndex(idx);
278      });
279      list.appendChild(row);
280    });
281    list.setAttribute("role", "listbox");
282    list.removeAttribute("hidden");
283    openList();
284  }
285
286  function pickIndex(idx) {
287    if (idx < 0 || idx >= results.length) {
288      return;
289    }
290    var item = results[idx];
291    var text = String(item.display_name || "").trim();
292    if (!text) {
293      var lines = worldmaps3dNominatimItemToLines(item);
294      text = lines.title + (lines.subtitle ? ", " + lines.subtitle : "");
295    }
296    input.value = text;
297    closeList();
298    goNextFromPickedItem(item, text);
299  }
300
301  function highlight(i) {
302    var nodes = list.querySelectorAll(".search-suggest-item");
303    nodes.forEach(function (el, j) {
304      el.classList.toggle("is-active", j === i);
305      el.setAttribute("aria-selected", j === i ? "true" : "false");
306    });
307    activeIndex = i;
308  }
309
310  function runFetch(q) {
311    if (abortCtl) {
312      abortCtl.abort();
313    }
314    abortCtl = new AbortController();
315    var myQuery = q;
316    var searchFn =
317      typeof worldmaps3dSearchPlaceWithCache === "function"
318        ? worldmaps3dSearchPlaceWithCache
319        : worldmaps3dSearchPlace;
320    searchFn(q, { signal: abortCtl.signal })
321      .then(function (data) {
322        lastFetchStart = Date.now();
323        var raw = [];
324        if (Array.isArray(data)) {
325          raw = data;
326        } else if (data && Array.isArray(data.results)) {
327          raw = data.results;
328        }
329
330        if (String(input.value).trim() !== myQuery) {
331          return;
332        }
333        render(raw.slice(0, 5));
334      })
335      .catch(function (err) {
336        if (err && err.name === "AbortError") {
337          return;
338        }
339        lastFetchStart = Date.now();
340        closeList();
341      });
342  }
343
344  function scheduleFetch(q) {
345    pendingQuery = q;
346    var now = Date.now();
347    var elapsed = lastFetchStart ? now - lastFetchStart : MIN_INTERVAL_MS;
348    var gap = Math.max(0, MIN_INTERVAL_MS - elapsed);
349
350    clearTimeout(throttleTimer);
351    throttleTimer = setTimeout(function () {
352      runFetch(pendingQuery);
353    }, gap);
354  }
355
356  function onInputValue() {
357    var q = String(input.value || "").trim();
358    clearTimeout(debounceTimer);
359    if (q.length < 2) {
360      if (abortCtl) {
361        abortCtl.abort();
362      }
363      closeList();
364      return;
365    }
366    debounceTimer = setTimeout(function () {
367      scheduleFetch(q);
368    }, DEBOUNCE_MS);
369  }
370
371  input.addEventListener("input", onInputValue);
372  input.addEventListener("focus", function () {
373    if (list.children.length) {
374      openList();
375    }
376  });
377
378  input.addEventListener("keydown", function (e) {
379    var k = e.key;
380    if (k === "Escape") {
381      closeList();
382      return;
383    }
384    if (k === "ArrowDown") {
385      if (list.hidden || !results.length) {
386        return;
387      }
388      e.preventDefault();
389      var next = activeIndex + 1;
390      if (next >= results.length) {
391        next = 0;
392      }
393      highlight(next);
394    } else if (k === "ArrowUp") {
395      if (list.hidden || !results.length) {
396        return;
397      }
398      e.preventDefault();
399      var prev = activeIndex - 1;
400      if (prev < 0) {
401        prev = results.length - 1;
402      }
403      highlight(prev);
404    } else if (k === "Enter") {
405      if (!list.hidden && activeIndex >= 0 && results[activeIndex]) {
406        e.preventDefault();
407        pickIndex(activeIndex);
408      }
409    }
410  });
411
412  document.addEventListener("click", function (e) {
413    if (form.contains(e.target)) {
414      return;
415    }
416    closeList();
417  });
418
419  form.addEventListener("submit", function () {
420    closeList();
421  });
422})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.