PageSourceSearch

https://www.drought.gov/modules/custom/general_page_js_css/js/map-helpers/ol-click.js?tlrx84

js drought.gov collected 2026-10-02 09:49:14 UTC 29,307 bytes, 756 lines download raw bytes

1(function (drupalSettings, olCounties, abbr, d3, gaHelp, umrbClick) {
2  "use strict";
3
4  (function main() {
5    exportConfig();
6    bindEventListeners();
7  })();
8
9  /**
10   * Sets all properties on a vector layer to allow users to click on them.
11   * @param {ol.layer.Vector} layer - A vector layer. Generally the Data Layer or state/county lines.
12   */
13  function makeVectorLayerInteractive(layer) {
14    _makeVectorLayerClickable(layer);
15    _makeVectorLayerHighlightable(layer);
16  }
17
18  /**
19   * Sets property on all features to allow the layer to be clickable.
20   * @param {ol.layer.Vector} layer - A vector layer. Generally the Data Layer or state/county lines.
21   */
22  function _makeVectorLayerClickable(layer) {
23    if (layer.getSource().getFeatures()[0]?.get("clickable") === "true") {
24      return;
25    }
26    layer.getSource().forEachFeature(_makeVectorFeatureClickable);
27  }
28
29  /**
30   * Sets property on all features to allow the layer to be highlighted on mouseover.
31   * @param {ol.layer.Vector} layer - A vector layer. Generally the Data Layer or state/county lines.
32   */
33  function _makeVectorLayerHighlightable(layer) {
34    if (layer.getSource().getFeatures()[0]?.get("highlight") === "true") {
35      return;
36    }
37    layer.getSource().forEachFeature(_makeVectorFeatureHighlightable);
38  }
39
40  /**
41   * Sets property on a feature to allow the feature to be clickable.
42   * @param {ol.Feature} feature - A vector feature. Generally the Data Layer or state/county lines.
43   */
44  function _makeVectorFeatureClickable(feature) {
45    feature.set("clickable", "true");
46  }
47
48  /**
49   * Sets property on a feature to allow the feature to be highlighted on mouseover.
50   * @param {ol.Feature} feature - A vector feature. Generally the Data Layer or state/county lines.
51   */
52  function _makeVectorFeatureHighlightable(feature) {
53    feature.set("highlight", "true");
54  }
55
56  /**
57   * @param e {ol.event.Event}
58   */
59  function handlePointerMove(e) {
60    if (e.dragging) {
61      return;
62    }
63    const map = this;
64    if (map.highlight) {
65      map.highlightLayer.getSource().clear();
66      map.highlight.set("isHighlighted", undefined);
67      map.highlight = false;
68    }
69    const pixel = map.getEventPixel(e.originalEvent);
70    const features = getInteractiveFeatureFromPixel(map, pixel);
71    highlightFeature(map, pixel, features.highlight);
72    updateCursorType(map, pixel, features.click);
73  }
74
75  /**
76   * Highlights the feature underneath the mouse, if it should be. Clears highlight otherwise.
77   * @param {ol.Map} map - OpenLayers map object with extended config.
78   * @param {ol.pixel} pixel - Pixel the user is moused over.
79   */
80  function highlightFeature(map, pixel, cachedFeature) {
81    if (map.mapElem.closest(".map-screenshot-overlay")) {
82      return;
83    }
84    const feature = cachedFeature;
85    // Only hightlight the states layer on specified pages. The USGS streamflow layer has a STATE property
86    if (
87      feature &&
88      (!feature.getProperties().STATE ||
89        map.dataLayer.getSource().getFeatures().includes(feature) ||
90        checkStatesClickable())
91    ) {
92      if (feature !== map.highlight) {
93        if (map.highlight) {
94          map.highlightLayer.getSource().clear();
95          map.highlight.set("isHighlighted", undefined);
96        }
97        feature.set("isHighlighted", true);
98        if (!(map.dataLayer.getSource().hasFeature && map.dataLayer.getSource().hasFeature(feature))) {
99          // Only utilize the highlight layer styles for area polygons, ie state/county/watershed/etc
100          map.highlightLayer.getSource().addFeature(feature);
101        }
102        map.highlight = feature;
103      }
104      return;
105    }
106    if (map.highlight) {
107      map.highlightLayer.getSource().clear();
108      map.highlight.set("isHighlighted", undefined);
109      map.highlight = false;
110    }
111  }
112
113  /**
114   * Updates cursor to a pointer if the user is hovering over a clickable data layer.
115   * @param {ol.Map} map - Map which is being hovered over.
116   * @param {ol.pixel} pixel - Pixel which is being hovered over.
117   */
118  function updateCursorType(map, pixel, cachedFeature) {
119    const feature = cachedFeature;
120    const props = feature ? feature.getProperties() : {};
121    const dataLayer = map.dataLayer;
122    let cursorType = "";
123    if (feature && props.clickable && dataLayer.getSource().hasFeature && dataLayer.getSource().hasFeature(feature)) {
124      cursorType = "pointer";
125    }
126    if (feature && props.clickable && map.abv === "tribal" && map.tribalPage.getSource().hasFeature(feature)) {
127      cursorType = "pointer";
128    }
129    map.getTargetElement().style.cursor = cursorType;
130  }
131
132  /**
133   * Finds the Vector Feature which was clicked, if one exists.
134   * @param {ol.Map} map - OpenLayers map object with extended config.
135   * @param {ol.pixel} pixel - Pixel the user is moused over.
136   * @return {ol.Feature|undefined} - The Vector feature.
137   */
138  function getClickableFeatureFromPixel(map, pixel) {
139    let clickFeature,
140      foundDataFeatureFlag = false;
141    map.forEachFeatureAtPixel(
142      pixel,
143      function (feature) {
144        if (feature.get("clickable") === "true" && foundDataFeatureFlag === false) {
145          clickFeature = feature;
146        }
147        if (map.dataLayer.getSource().hasFeature && map.dataLayer.getSource().hasFeature(clickFeature)) {
148          foundDataFeatureFlag = true;
149        }
150        if (checkStatesClickable() && map.states.getSource().hasFeature(clickFeature)) {
151          foundDataFeatureFlag = true;
152        }
153        if (map.abv === "tribal" && map.tribalPage.getSource().hasFeature(clickFeature)) {
154          foundDataFeatureFlag = true;
155        }
156      },
157      {
158        layerFilter(layer) {
159          return !!(map.clickableLayers && map.clickableLayers.indexOf(layer) !== -1);
160        },
161      }
162    );
163    return clickFeature;
164  }
165
166  /**
167   * Finds the Vector Feature which was clicked, if one exists.
168   * @param {ol.Map} map - OpenLayers map object with extended config.
169   * @param {ol.pixel} pixel - Pixel the user is moused over.
170   * @return {ol.Feature|undefined} - The Vector feature.
171   */
172  function getInteractiveFeatureFromPixel(map, pixel) {
173    let clickFeature,
174      highlightableFeature,
175      foundClickFeatureFlag = false,
176      foundHighlightFeatureFlag = false;
177    map.forEachFeatureAtPixel(
178      pixel,
179      function (feature) {
180        if (feature.get("clickable") === "true" && foundClickFeatureFlag === false) {
181          clickFeature = feature;
182        }
183        if (feature.get("highlight") === "true" && foundHighlightFeatureFlag === false) {
184          highlightableFeature = feature;
185        }
186        if (map.dataLayer.getSource().hasFeature) {
187          if (clickFeature && map.dataLayer.getSource().hasFeature(clickFeature)) {
188            foundClickFeatureFlag = true;
189          }
190          if (highlightableFeature && map.dataLayer.getSource().hasFeature(highlightableFeature)) {
191            foundHighlightFeatureFlag = true;
192          }
193        }
194        if (checkStatesClickable()) {
195          if (clickFeature && map.states.getSource().hasFeature(clickFeature)) {
196            foundClickFeatureFlag = true;
197          }
198          if (highlightableFeature && map.states.getSource().hasFeature(highlightableFeature)) {
199            foundHighlightFeatureFlag = true;
200          }
201        }
202        if (map.abv === "tribal") {
203          if (clickFeature && map.tribalPage.getSource().hasFeature(clickFeature)) {
204            foundClickFeatureFlag = true;
205          }
206          if (highlightableFeature && map.tribalPage.getSource().hasFeature(highlightableFeature)) {
207            foundHighlightFeatureFlag = true;
208          }
209        }
210        if (foundClickFeatureFlag || foundHighlightFeatureFlag) {
211          //return true;
212        }
213      },
214      {
215        layerFilter(layer) {
216          return [map.dataLayer, map.states, map.counties, map.watersheds, map.tribalPage].includes(layer);
217        },
218      }
219    );
220    return {
221      click: clickFeature,
222      highlight: highlightableFeature,
223    };
224  }
225
226  /**
227   * Handles when a user clicks on a map.
228   * @param {Event} e - Click event
229   * @constructor
230   */
231  function OLMapSingleClick(e) {
232    const map = this;
233    if (map.mapElem.closest(".map-screenshot-overlay")) {
234      return;
235    }
236
237    const dataSource = map.dataLayer.getSource();
238    let dataUrl = "";
239    if (dataSource.getUrl && typeof dataSource.getUrl === "function") {
240      dataUrl = map.dataLayer.getSource().getUrl();
241    } else if (dataSource.getUrls && typeof dataSource.getUrls === "function") {
242      dataUrl = map.dataLayer.getSource().getUrls()[0];
243    }
244    if (_checkMapIsNotClickable(dataUrl) && map.abv !== "tribal") {
245      return;
246    }
247    const coordinate = e.coordinate;
248    const feature = getClickableFeatureFromPixel(map, map.getEventPixel(e.originalEvent));
249    if (!feature) {
250      return;
251    }
252    const properties = feature.getProperties();
253    const popupContent = makePopupContent(map, properties, dataUrl);
254    const popupBody = map.popupContainer.querySelector(".popup-content");
255    if (popupContent) {
256      if (typeof popupContent === "string") {
257        popupBody.innerHTML = popupContent;
258      } else {
259        popupBody.replaceChildren(popupContent);
260      }
261      if (!map.popupOverlay.autoPan) {
262        map.getView().animate({
263          center: coordinate,
264          duration: 250,
265        });
266        if (map.highlight) {
267          map.highlightLayer.getSource().removeFeature(map.highlight);
268        }
269        map.highlightLayer.getSource().addFeature(feature);
270        map.highlight = feature;
271      }
272      map.popupOverlay.setPosition(coordinate);
273    }
274  }
275
276  /**
277   * Checks if the data layer for a map is clickable.
278   *
279   * Generally also means the layer has will create a popup and should have a tabular view. No exceptions to this
280   * currently exist. Currently is the Streamflow layer, the NWS map, and the dams & lakes on the ACF/ACT pages.
281   *
282   * @param {string} url - URL for the primary data layer of the map.
283   * @return {boolean} - true if layer is clickable, false if not.
284   */
285  function checkDataLayerIsClickable(url) {
286    return (
287      url.indexOf("strmflow") !== -1 ||
288      url.indexOf("usace-dams") !== -1 ||
289      url.indexOf("apclakes") !== -1 ||
290      url.indexOf("nws-dgt-pnts") !== -1 ||
291      url.indexOf("sector/fire/fires.") !== -1 ||
292      umrbClick.testClickable(url) ||
293      url.includes("mrb/water/nwps") ||
294      (url.toLowerCase().includes("usace") && url.toLowerCase().includes("hydrograph")) ||
295      url.includes("acf/groundwater") ||
296      checkStatesClickable()
297    );
298  }
299
300  /**
301   * Checks if a map is NOT clickable.
302   *
303   * Currently lets users click on maps if there are counties, watersheds, or data layers defined in
304   * checkDataLayerIsClickable.
305   * @param {string} dataUrl - URL for the primary data layer of the map.
306   * @return {boolean} - true if a map does not have a clickable layer, false if it doesn't
307   * @private
308   */
309  function _checkMapIsNotClickable(dataUrl) {
310    return (
311      !drupalSettings.drawCountyOutlines && !drupalSettings.drawWatershedOutlines && !checkDataLayerIsClickable(dataUrl)
312    );
313  }
314
315  /**
316   * Builds a temporary skeleton for popup content.
317   * @return {HTMLDivElement} - The skeleton
318   */
319  function makePopupSkeleton() {
320    const div = document.createElement("div");
321    div.classList.add("bg-base-lightest", "radius-md", "height-6");
322    const spinner = document.createElement("div");
323    spinner.classList.add("spinner", "spinner--centered", "icon-size--large");
324    div.appendChild(spinner);
325    return div;
326  }
327
328  /**
329   * Builds and places a popup containing a copy of the map's legend
330   * @param {ol.Map} map - The OpenLayers Map object
331   */
332  function handleLegendPopup(map) {
333    const legendElem = map.mapElem.closest(".map-content").querySelector(".legend-wrapper");
334    const legendCopy = legendElem.cloneNode(true);
335    const popupBody = map.popupContainer.querySelector(".popup-content");
336    const center = map.getView().getCenter();
337    popupBody.replaceChildren(legendCopy);
338    map.popupOverlay.setPosition(center);
339  }
340
341  /**
342   * Builds the HTML element which populate the popup when a map is clicked.
343   * @param {ol.Map} map - Customized map which was clicked on.
344   * @param {{}} properties - Properties of the feature which was clicked on.
345   * @param {string} dataUrl - URL of the main data layer for the map.
346   * @return {string|Element} - HTML element or string of popup content.
347   */
348  function makePopupContent(map, properties, dataUrl) {
349    if ((drupalSettings.drawCountyOutlines && properties.FIPS) || dataUrl === "county") {
350      // county popup
351      const countyName = properties.NAME;
352      const stateName =
353        drupalSettings.state_name ||
354        drupalSettings.full_state?.toLowerCase().replace(" ", "-") ||
355        abbr.toSlug(properties.STAB);
356      const path = "/states/" + stateName + "/county/" + countyName;
357      const countyType = olCounties.makeCountyType(map.abv || properties.STAB, true);
358      return _makePopupLinks([`Open ${countyName} ${countyType} Page`, path]);
359    }
360    if (dataUrl === "state-county") {
361      // county within state popup
362      return _makePopupLinks(
363        [`Open ${properties.name} Page`, `/states/${properties.slug}`],
364        [`Load ${properties.name} Counties`, "#"]
365      );
366    }
367    if (
368      (drupalSettings.drawWatershedOutlines && properties.Name && drupalSettings.watershedClickable !== false) ||
369      dataUrl === "watershed"
370    ) {
371      return makeWatershedPopup(properties);
372    }
373    if (
374      dataUrl.includes("acf/groundwater") ||
375      (map.metadata && map.metadata.classes && (
376        (map.metadata.classes.includes("gw-percent") || map.metadata.classes.includes("gw-compare"))
377      ))
378    ) {
379      const html = [];
380      if (properties.statnum) {
381        html.push(`<div class="margin-bottom-05"><strong>Monitoring Location</strong>: ${properties.statnum}</div>`);
382      }
383      if (properties.value !== undefined) {
384        html.push(`<div class="margin-bottom-05"><strong>Depth to water</strong>: ${properties.value} ft</div>`);
385      }
386      if (properties.stats_record_length_years !== undefined) {
387        html.push(`<div class="margin-bottom-05"><strong>Record length</strong>: ${properties.stats_record_length_years} years</div>`);
388      }
389      if (properties.observation_date) {
390        html.push(`<div class="margin-bottom-05"><strong>Latest measurement date</strong>: ${(new Date(properties.observation_date)).toLocaleDateString("en-US", {month: "2-digit", day: "2-digit", year: "numeric"})}</div>`);
391      }
392      if (properties.waterdata_link) {
393        html.push(`<div><a class="c--sbtn width-full" href="${properties.waterdata_link}" target="_blank">View More Data</a></div>`);
394      }
395      return html.join("");
396    }
397    if (dataUrl.indexOf("strmflow") !== -1 && properties.MONITORING_LOCATION_ID) {
398      let link = properties.URL;
399      if (!link) {
400        link = "https://waterdata.usgs.gov/monitoring-location/" + properties.MONITORING_LOCATION_ID.toString();
401      }
402      const name = properties.MONITORING_LOCATION_NAME || properties.MONITORING_LOCATION_ID;
403      return `<div class="margin-bottom-05 padding-bottom-2px"><strong>${name}</strong></div><div class="margin-bottom-05 padding-bottom-2px">Streamflow percentile: ${properties.PERCENTILE}</div><div class="margin-bottom-05 padding-bottom-2px"><a href="${properties.URL}" target="_blank" class="c--sbtn width-full">Monitoring Location Page</a></div><div><a href="${properties.DAILY_DURATION_URL}" target="_blank" class="c--sbtn width-full">Statistical Hydrographs</a></div>`;
404      //return _makePopupLinks([name, link, "ext"]);
405    }
406    if (dataUrl.indexOf("strmflow") !== -1 && properties.STAID) {
407      // Old Waterwatch -- should likely be removed after February
408      let link = properties.URL;
409      if (!link) {
410        link = "https://waterdata.usgs.gov/monitoring-location/" + properties.STAID.toString();
411      }
412      return _makePopupLinks([properties.STANAME, link, "ext"]);
413    }
414    if (dataUrl.indexOf("strmflow") !== -1 && properties.CLASS) {
415      // Old Waterwatch -- should likely be removed after February
416      const gaugeId = properties.NAME.split(" ")[1];
417      return (
418        '<a href="https://waterdata.usgs.gov/monitoring-location/' +
419        gaugeId +
420        '" target="_blank">' +
421        properties.NAME +
422        "</a>"
423      );
424    }
425    if (dataUrl.indexOf("usace-dams") !== -1) {
426      const lakeName = properties.LAKE;
427      const curLevel = properties.MDNGHTPOOL === "M" ? "Missing Data" : `${properties.MDNGHTPOOL} ft MSL`;
428      const oneWeekForecast = properties.WK1FRCST;
429      let popupContent = `<div class="text-primary text-bold">${lakeName}</div><div>Current Level: ${curLevel}</div>`;
430      if (oneWeekForecast !== "-9.0") {
431        popupContent += `<div>
431One Week Forecast: ${oneWeekForecast} ft MSL</div>`;
432      }
433      return popupContent;
434    }
435    if (dataUrl.indexOf("apclakes") !== -1) {
436      const lakeName = properties.LAKE;
437      const urlFragment = properties.URLKEY;
438      return `<div class="text-primary text-bold"><a href="https://apcshorelines.com/lake/${urlFragment}" target="_blank">View ${lakeName} Lake conditions.</a></div>`;
439    }
440    if (dataUrl.toLowerCase().includes("usace") && dataUrl.toLowerCase().includes("hydrograph")) {
441      const name = `<span class="font-body-md text-bold">${properties["Gauge Name"]}</span>`;
442      const link = `<a href="${properties["Hydrograph Link"]}" class="c--btn margin-top-05" target="_blank">View Hydrograph</a>`;
443      return `<div>${name}</div><div>${link}</div>`;
444    }
445    if (dataUrl.indexOf("nws-dgt-pnts") !== -1) {
446      const wfo = properties.WFO;
447      const city = properties.CITY;
448      const state = properties.STATE;
449      const date = properties.DATE;
450      if (wfo) {
451        const line1 = _makePopupText(`<b>${city}, ${state} Weather Forecast Office (${wfo})</b>`);
452        const line2 = _makePopupText(`<b>Statement Date: </b> ${date}`);
453        const btn = _makePopupLinks(["View This Statement", properties.URL, "ext"]);
454        const wrapper = document.createElement("div");
455        wrapper.append(line1, line2, btn);
456        return wrapper;
457      }
458    } else if (dataUrl === "dews") {
459      const dewsName =
460        properties.dews === "california-nevada"
461          ? "California-Nevada"
462          : properties.dews
463              .split("-")
464              .map((word) => {
465                return word[0].toUpperCase() + word.substring(1);
466              })
467              .join(" ");
468      const path = "/dews/" + properties.dews;
469      return _makePopupLinks([`Open ${dewsName} Page`, path]);
470    } else if (dataUrl.includes("sector/fire/fires.")) {
471      return _makeFirePopup(properties);
472    } else if (dataUrl === "locale") {
473      const address =
474        properties.RegionAbbr === "HI"
475          ? encodeURIComponent(properties.LongLabel)
476          : encodeURIComponent(properties.Match_addr);
477      return _makePopupLinks([
478        `Open ${properties.City}, ${properties.Region} (${properties.Postal}) Page`,
479        `/location/${address}`,
480      ]);
481    }
482    if (dataUrl.includes("mrb/water/nwps")) {
483      return _makeRiverStagePopup(properties);
484    }
485    if (umrbClick.testClickable(dataUrl)) {
486      return umrbClick.createPopup(dataUrl, properties, map.metadata);
487    }
488    if (checkStatesClickable() || dataUrl === "state") {
489      return _handleFrontClick(properties);
490    }
491    if (map.abv === "tribal") {
492      return _handleTribalClick(properties);
493    }
494  }
495
496  /**
497   * Creates a popup for the active wildfires layer. Currently found on sectors/wildfire-management.
498   * https://storage.googleapis.com/noaa-nidis-drought-gov-data/current-conditions/json/v1/sector/fire/fires.geojson
499   * @param {{}} properties - Properties of the clicked feature.
500   * @return {string} - Formatted string for popup content.
501   */
502  function _makeFirePopup(properties) {
503    const name = properties.firename;
504    const date = properties.reportdate.replaceAll("\\", "");
505    const area = properties.area.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
506    let dateStr = "";
507    if (date !== "01/20/70") {
508      dateStr = `<br><strong>Fire Discovery Date:</strong> ${date}`;
509    }
510    return `<div><strong>Name:</strong> ${name}${dateStr}<br><strong>Size:</strong> ${area} acres</div>`;
511  }
512
513  /**
514   * Creates popup for River Gauges
515   * @param {{}} properties - Feature properties
516   * @return {string} HTML as string.
517   */
518  function _makeRiverStagePopup(properties) {
519    const title = `<div class="padding-bottom-05 margin-bottom-1 border-bottom-1px border-black"><strong>${properties.NAME}: ${properties.GAUGE_ID}</strong></div>`;
520    const low = `<div class="margin-bottom-05"><strong>Low Water Threshold</strong>: ${_getRiverVal(properties, "LOW_THRESH")}</div>`;
521    const current = `<div class="margin-bottom-05"><strong>Current Level</strong>: ${_getRiverVal(properties, "OBSERVED")}</div>`;
522    const max = `<div class="margin-bottom-05"><strong>
522Max Forecast Level</strong>: ${_getRiverVal(properties, "FORECAST")}</div>`;
523    const link = `<div><a class="c--sbtn" target="_blank" href="${properties.URL}">View More Gauge Info</a></div>`;
524    return ["<div>", title, low, current, max, link, "</div>"].join("");
525  }
526
527  /**
528   * Creates the popup for Watersheds.
529   * @param {{}} properties - Feature properties. "Name" is the only property required.
530   * @return {HTMLDivElement} - Div with however many links are needed.
531   */
532  function makeWatershedPopup(properties) {
533    let watershedName = properties.Name;
534    const watershedLinkName = watershedName.replace(" Region", "").replace(" ", "-");
535    const path = "/watersheds/" + watershedLinkName;
536    const links = [];
537    if (watershedName === "Mississippi River Basin Dashboard") {
538      return _makePopupLinks(["Open Mississippi River Basin Dashboard", "/watersheds/mississippi-river-dashboard"]);
539    }
540    if (watershedName === "Colorado") {
541      watershedName = "Colorado River Basin";
542    }
543    if (watershedName === "Upper Colorado Region" || watershedName === "Lower Colorado Region") {
544      links.push(["Open Colorado River Basin Page", "/watersheds/colorado"]);
545    }
546    if (watershedName === "Colorado River Basin") {
547      links.push(
548        ["Open Upper Colorado Region Page", "/watersheds/upper-colorado"],
549        ["Open Lower Colorado Region Page", "/watersheds/lower-colorado"]
550      );
551    }
552    links.push([`Open ${watershedName} Page`, path]);
553    if (
554      watershedName === "Arkansas-White-Red Region" ||
555      watershedName === "Lower Mississippi Region" ||
556      watershedName === "Missouri Region" ||
557      watershedName === "Ohio Region" ||
558      watershedName === "Tennessee Region" ||
559      watershedName === "Upper Mississippi Region"
560    ) {
561      links.push(["Open Mississippi River Basin Dashboard", "/watersheds/mississippi-river-dashboard"]);
562    }
563    return _makePopupLinks(...links);
564  }
565
566  /**
567   * Gets value from a River Gauge feature.
568   * @param {{}} properties - Feature Properties
569   * @param {string} key - Key of feature.
570   * @return {string} - Not Available or Value formatted as feet.
571   */
572  function _getRiverVal(properties, key) {
573    if (!properties || !properties[key] || properties[key] === "null") {
574      return "Not Available";
575    }
576    return properties[key] + "'";
577  }
578
579  /**
580   * Creates text wrapped in a p tag.
581   * @param {string} text - Text for a popup. Can contain sub HTML tags.
582   * @return {HTMLParagraphElement} - Text wrapped in a p tag.
583   */
584  function _makePopupText(text) {
585    const p = document.createElement("p");
586    p.innerHTML = text;
587    return p;
588  }
589
590  /**
591   * Creates multiple popup links.
592   * @param {[string, string]|[string, string, string]} args - Any number of 2/3 element arrays. First element is link
593   *   text, second is link href and third is an optional flag.
594   * @return {HTMLDivElement} - Div with however many links are needed.
595   */
596  function _makePopupLinks(...args) {
597    const div = document.createElement("div");
598    div.className = "c--sbtn-stack";
599    args.forEach((arg) => {
600      div.appendChild(_makePopupLink(arg));
601    });
602    div.lastElementChild.classList.add("c--sbtn");
603    return div;
604  }
605
606  /**
607   * Creates an individual link for a popup. Arguments must be passed as an array.
608   * @param {string} text - Text for the link
609   * @param {string} link - URL for the link.
610   * @param {string} flag - Makes link external if the value is exactly "ext".
611   * @return {HTMLAnchorElement} - Link for the popup.
612   */
613  function _makePopupLink([text, link, flag]) {
614    const a = document.createElement("a");
615    a.href = link;
616    a.textContent = text;
617    if (flag === "ext") {
618      a.target = "_blank";
619      a.classList.add("ext");
620    }
621    return a;
622  }
623
624  /**
625   * Gets the HTML element for the state popup. Inserted via replaceChildren.
626   * @param {{}} properties - From https://openlayers.org/en/latest/apidoc/module-ol_Feature-Feature.html#getProperties
627   * @return {Element} - HTML element for the state popup. Link to state formatted as button.
628   */
629  function _handleFrontClick(properties) {
630    const stateProperties = abbr.getAll(properties.STATE);
631    if (stateProperties.name === "District of Columbia") {
632      stateProperties.slug = "District-Of-Columbia/county/District%20Of%20Columbia";
633    }
634    const links = _makePopupLinks([`Go to ${stateProperties.name} Page`, `/states/${stateProperties.slug}`]);
635    links.classList.add("c--state-link");
636    return links;
637  }
638
639  /**
640   * Creates the HTML for the tribal popup. Inserted via replaceChildren.
641   * @param {{}} properties - From https://openlayers.org/en/latest/apidoc/module-ol_Feature-Feature.html#getProperties
642   * @return {Element} The conents for the tribal popup. Currently just the title of the area.
643   */
644  function _handleTribalClick(properties) {
645    const wrapper = document.createElement("div");
646    const title = document.createElement("h4");
647    title.textContent = properties.NAMELSAD;
648    title.className = "margin-0";
649    wrapper.append(title);
650    return wrapper;
651  }
652
653  /**
654   * Checks if the states should be clickable. Currently current conditions, front, national, and outlooks and forecasts
655   * pages.
656   * @return {Element} - Used as a truthy/falsy variable.
657   */
658  function checkStatesClickable() {
659    return (
660      document.querySelector(".page-node-type-current-condition") ||
661      document.querySelector(".page-node-type-front-page") ||
662      document.querySelector(".page-node-type-national") ||
663      document.querySelector(".page-node-type-outlooks-and-forecasts")
664    );
665  }
666
667  /**
668   * Creates click specific custom events.
669   */
670  function bindEventListeners() {
671    document.addEventListener("click", _handleFrontClickEvent);
672    document.addEventListener("click", handleMabTabClick);
673  }
674
675  /**
676   * Delegates the event for when a user clicks on a state link from a map.
677   * @param {MouseEvent} e - The event.
678   */
679  function _handleFrontClickEvent(e) {
680    const t = e.target;
681    if (!checkStatesClickable() || t.tagName.toLowerCase() !== "a" || !t.closest(".c--state-link")) {
682      return;
683    }
684    gaHelp.registerEvent("map_interaction", {
685      action: "state_link_click",
686      direction: _getStateLinkState(t.textContent),
687      section: _getStateLinkPageType(),
688    });
689  }
690
691  /**
692   * Clears a map popup if it exists when the map is changed.
693   * @param {MouseEvent} e - The event.
694   */
695  function handleMabTabClick(e) {
696    const t = e.target;
697    if (t.tagName.toLowerCase() !== "a" || !t.classList.contains("c--tab")) {
698      return;
699    }
700    const mapElem = window.mapsUtil.findMap(t);
701    if (!mapElem) {
702      return;
703    }
704    const map = window.jQuery(mapElem).data("map");
705    if (!map?.popupOverlay) {
706      return;
707    }
708    map.popupOverlay.setPosition(undefined);
709  }
710
711  /**
712   * Gets human readable name of the page type.
713   * @return {string} - The page type.
714   */
715  function _getStateLinkPageType() {
716    if (document.querySelector(".page-node-type-current-condition")) {
717      return "Current Conditions";
718    }
719    if (document.querySelector(".page-node-type-front-page")) {
720      return "Front";
721    }
722    if (document.querySelector(".page-node-type-national")) {
723      return "National";
724    }
725    if (document.querySelector(".page-node-type-outlooks-and-forecasts")) {
726      return "Outlooks and Forecasts";
727    }
728  }
729
730  /**
731   * Gets the name of the target state from the link text.
732   * @param {string} text - Link Text. Expects "Go to STATENAME page" as defined in _handleFrontClick.
733   * @return {string} - Name of state
734   */
735  function _getStateLinkState(text) {
736    return text.replace("Go to ", "").replace(" page", "");
737  }
738
739  /**
740   * Exports useful functions.
741   */
742  function exportConfig() {
743    window.olClick = {
744      checkDataLayerIsClickable,
745      checkStatesClickable,
746      makeVectorLayerInteractive,
747      highlightFeature,
748      handlePointerMove,
749      getClickableFeatureFromPixel,
750      handleLegendPopup,
751      OLMapSingleClick,
752      makePopupSkeleton,
753      makePopupContent,
754    };
755  }
756})(window.drupalSettings, window.olCounties, window.abbr, window.d3, window.gaHelp, window.umrbClick);

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.