PageSourceSearch

https://www.drought.gov/modules/custom/general_page_js_css/js/general/location-search.js?tlrx84

js drought.gov collected 2026-09-24 08:36:26 UTC 6,124 bytes, 186 lines download raw bytes

1(function (TomSelect, gaHelp) {
2  (function main() {
3    exportConfig();
4    initializeSearches();
5  })();
6
7  /**
8   * Sets up the main Location Searches (in the header and home page).
9   */
10  function initializeSearches() {
11    document.querySelectorAll(".location-search").forEach((input) => {
12      const select = initializeLocationSearch(input, false);
13      select.on("change", _handleLocationSelect);
14    });
15  }
16
17  /**
18   * Builds a Location Search field.
19   * @param {HTMLSelectElement} input - The select element to house the Location Search
20   * @param {Boolean} [addressOption = false] - Whether to allow address and lat/lon searches
21   * @return {TomSelect} - A generated Location Search field
22   */
23  function initializeLocationSearch(input, addressOption = false) {
24    const urlBase = "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/suggest?text=";
25    const latLonBase =
26      "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/findAddressCandidates" +
27      "?f=pjson&maxLocations=1&forStorage=false&category=XY&Address=";
28    let urlSuffix =
29      "&f=json&countryCode=USA&returnCollections=false&category=Postal,Neighborhood,District,City,Subregion,Region";
30    if (addressOption) {
31      urlSuffix = "&f=json&countryCode=USA&returnCollections=false&category=Postal,Neighborhood,District,City,Address";
32      input.setAttribute("placeholder", "Enter Address, Zip Code, City, or Long/Lat Location");
33    } else {
34      input.setAttribute("placeholder", "Enter Zip Code, City, County, or State");
35    }
36
37    const settings = {
38      valueField: "magicKey",
39      labelField: "location",
40      searchField: [],
41      maxOptions: 5,
42      maxItems: 1,
43      create: false,
44      persist: false,
45      shouldLoad(query) {
46        return query.length > 2;
47      },
48      load(query, callback) {
49        let url;
50        // Regex to detect long/lat inputs
51        if (addressOption && query.match(/^((\-?|\+?)?\d+(\.\d+)?),\s*((\-?|\+?)?\d+(\.\d+)?)$/)) {
52          url = latLonBase + encodeURIComponent(query);
53        } else {
54          url = urlBase + encodeURIComponent(query) + urlSuffix;
55        }
56        fetch(url)
57          .then((response) => response.json())
58          .then((json) => {
59            this.clearOptions();
60            if (json.candidates) {
61              convertCandidates(json);
62            }
63            callback(filterSuggestions(json.suggestions));
64          })
65          .catch(() => {
66            callback();
67          });
68      },
69      render: {
70        options: [],
71        option(item, escape) {
72          return `<div data-magicKey=${escape(item.magicKey)}>${escape(item.text)}</div>`;
73        },
74        item(item, escape) {
75          return `<div id="selected-location" data-magicKey=${escape(item.magicKey)}>${escape(item.text)}</div>`;
76        },
77      },
78      plugins: {
79        dropdown_header: {
80          title: "Select a location from the list below",
81          labelClass: "dropdown-header-label font-sans-3xs",
82          html: (data) => {
83            return `<div class="${data.headerClass}">
84                  <div class="${data.titleRowClass}">
85                    <span class="${data.labelClass}">${data.title}</span>
86                  </div>
87                </div>`;
88          },
89        },
90      },
91    };
92    return new TomSelect(input, settings);
93  }
94
95  /**
96   * Responses to lon/lat searches need to fit the generic syntax. This replaces the values of the passed array.
97   * @param {{}} json - List of possible locations for the user provided string.
98   * @return {undefined|bool} - 
99   */
100  function convertCandidates(json) {
101    const coord = validateCoordinates(json.candidates[0]);
102    if (!coord) {
103      return false;
104    }
105    json.suggestions = [
106      {
107        text: `X:${coord.x}, Y:${coord.y}`,
108        magicKey: "",
109      },
110    ];
111  }
112
113  /**
114   * Validator for US/PR coordinates
115   * @param {{}} coord - 
116   * @return {{ x: number, y: number }|boolean} - The coordinate if it is in the US or false.
117   */
118  function validateCoordinates(coord) {
119    const x = coord.location.x;
120    const y = coord.location.y;
121    let checked = false;
122    const bb = {
123      conus: [-124.73, 24.51, -66.98, 49.38],
124      AK: [-179.15, 51.22, -129.9801315, 71.35],
125      HI: [-160.2500505, 18.92, -154.8100651, 22.23],
126      PR: [-67.9402974, 17.88, -65.2203047, 18.52],
127    };
128    Object.values(bb).forEach((bound) => {
129      if (bound[0] <= x && bound[1] <= y && bound[2] >= x && bound[3] >= y) {
130        checked = { x, y };
131      }
132    });
133    return checked;
134  }
135
136  /**
137   * Filters out erroneous suggestions.
138   *
139   * ArcGIS bug currently returns Palau addresses so "PLW" are removed. The API also returns Washington County in DC
140   * which hasn't existed since 1871.
141   * @param {[]} suggestions - Array of potential locations returned by the api
142   * @return {[]} Filtered list
143   */
144  function filterSuggestions(suggestions) {
145    return suggestions
146      .filter((item) => {
147        return !item.text.endsWith("PLW");
148      })
149      .filter((item) => {
150        return item.text !== "Washington, DC, USA (Washington County)";
151      });
152  }
153
154  /**
155   * Processes the selection of an item within the main Location Searches
156   */
157  function _handleLocationSelect() {
158    const val = this.getValue();
159    const text = this.getItem(val).innerText;
160    registerSearchEvent(this.input, val);
161    document.location.href = "/location/" + encodeURIComponent(text) + "?key=" + val;
162  }
163
164  /**
165   * Logs a GA4 event associated with the Location Search.
166   * @param {HTMLSelectElement} input - The select element that is the event target
167   * @param {string} value - The text content of the search
168   */
169  function registerSearchEvent(input, value) {
170    const section = input.id === "front-page-location-search" ? "main" : "menu";
171    gaHelp.registerEvent("search", {
172      action: "location",
173      direction: value,
174      section,
175    });
176  }
177
178  /**
179   * Generic export function
180   */
181  function exportConfig() {
182    window.locationSearch = {
183      initializeLocationSearch,
184    };
185  }
186})(window.TomSelect, window.gaHelp);

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.