PageSourceSearch

https://www.pasitoblanco.com/wp-content/plugins/draw-attention/public/assets/js/public.js?ver=2.1.10

js pasitoblanco.com collected 2026-10-07 04:49:16 UTC 31,977 bytes, 1,107 lines download raw bytes

1(function ($, hotspots, undefined) {
2  "use strict";
3
4  var Leaflet = L.noConflict();
5
6  var ua = window.navigator.userAgent,
7    isiOS = !!ua.match(/iPad/i) || !!ua.match(/iPhone/i),
8    isWebkit = !!ua.match(/WebKit/i),
9    isWebkitiOS = isiOS && isWebkit,
10    isMobileSafari = isiOS && isWebkit && !ua.match(/CriOS/i),
11    getBrowserVersion = function () {
12      var temp,
13        M =
14          ua.match(
15            /(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i,
16          ) || [];
17
18      M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, "-?"];
19      if ((temp = ua.match(/version\/(\d+)/i)) != null) {
20        M.splice(1, 1, temp[1]);
21      }
22      return parseInt(M[1]);
23    };
24
25  // Store all the leaflets on the page in an array for access later
26  var leaflets = [];
27
28  // Store all image maps on the page in an object for access later
29  var imageMaps = {};
30
31  // Store all the more info area hotspots in an object for access later
32  hotspots.infoSpots = {};
33
34  // Intersection Observer to detect when hidden hotspots become visible
35  var hotspotVisibilityObserver = null;
36
37  const layoutsThatShowTitleOnFocus = [
38    "left",
39    "right",
40    "bottom",
41    "top",
42    "lightbox",
43  ];
44
45  var setupHotspotVisibilityObserver = function () {
46    if (!window.IntersectionObserver) {
47      return; // Browser doesn't support Intersection Observer
48    }
49
50    hotspotVisibilityObserver = new IntersectionObserver(
51      function (entries) {
52        entries.forEach(function (entry) {
53          if (entry.isIntersecting) {
54            // Hotspot container has become visible
55            var container = entry.target;
56            var containerJQuery = $(container);
57
58            // Check if this container was previously hidden
59            if (containerJQuery.data("was-hidden")) {
60              console.log("Hotspot container became visible:", container);
61
62              hotspots.init(); // Reinitialize hotspots
63              containerJQuery.removeData("was-hidden");
64            }
65          } else {
66            // Hotspot container has become hidden
67            var container = entry.target;
68            var containerJQuery = $(container);
69
70            containerJQuery.data("was-hidden", true);
71          }
72        });
73      },
74      {
75        threshold: 0.1,
76        rootMargin: "10px",
77      },
78    );
79  };
80
81  var observeHotspotContainer = function (container) {
82    if (hotspotVisibilityObserver && container.length) {
83      var containerElement = container.get(0);
84      var rect = containerElement.getBoundingClientRect();
85      var isInitiallyHidden =
86        rect.width === 0 ||
87        rect.height === 0 ||
88        window.getComputedStyle(containerElement).display === "none" ||
89        window.getComputedStyle(containerElement).visibility === "hidden";
90
91      if (isInitiallyHidden) {
92        container.data("was-hidden", true);
93      }
94
95      hotspotVisibilityObserver.observe(containerElement);
96    }
97  };
98
99  var mapSetup = function () {
100    $(".da-error").hide();
101    $(".hotspot-info").addClass("da-hidden");
102
103    setupHotspotVisibilityObserver();
104
105    var images = $("img.hotspots-image, picture.hotspots-image img"); // Select images as well as images inside picture elements
106
107    images.each(function () {
108      var img = $(this);
109
110      var tester = new Image();
111
112      tester.onload = function () {
113        markLoaded(img);
114        moreInfoSetup(img);
115        leafletSetup(img);
116        var container = img.parents(".hotspots-container");
117        observeHotspotContainer(container);
118      };
119      tester.src = img.attr("src");
120    });
121
122    if (isMobileSafari) {
123      window.onpageshow = function (event) {
124        if (event.persisted) {
125          window.location.reload();
126        }
127      };
128    }
129  };
130
131  var markLoaded = function (img) {
132    img.data("status", "loaded");
133    var container = img.parents(".hotspots-container").addClass("loaded");
134    img.attr("aria-hidden", true);
135  };
136
137  var stopAudioVideo = function (el) {
138    var iframe = el.querySelector("iframe[src*=youtube]");
139    var video = el.querySelector("video");
140    var audio = el.querySelector("audio");
141    if (iframe) {
142      var iframeSrc = iframe.src;
143      iframe.src = iframeSrc;
144    }
145    if (video) {
146      video.pause();
147    }
148    if (audio) {
149      audio.pause();
150    }
151  };
152
153  var moreInfoSetup = function (img) {
154    var container = img.parents(".hotspots-container");
155
156    if (container.data("layout") == "tooltip") {
157      return;
158    }
159
160    if (container.data("layout") == "lightbox") {
161      lightboxSetup(container, img);
162      return;
163    }
164
165    infoboxSetup(container, img);
166  };
167
168  var infoboxSetup = function (container, img) {
169    var initial = container.find(".hotspot-initial");
170    var content = container.find(".hotspots-placeholder");
171    initial.addClass("visible");
172    container.on("active.responsilight inactive.responsilight", function (e) {
173      var data = $(e.target).data("areaData");
174      var info;
175      if (e.type === "active") {
176        info = $(data.href);
177      } else {
178        info = initial;
179      }
180
181      var visibleContent = content.children(".visible");
182      stopAudioVideo(visibleContent.get(0));
183      visibleContent.removeClass("visible");
184      info.removeClass("da-hidden").addClass("visible").appendTo(content);
185
186      // Check for an embedded video player
187      var video = info.find(".wp-video");
188      if (video.length) {
189        if (!info.data("video-resized")) {
190          info.data("video-resized", true);
191          window.dispatchEvent(new Event("resize"));
192        }
193      }
194    });
195  };
196
197  var lightboxSetup = function (container, img) {
198    container.on("active.responsilight", function (e) {
199      var data = $(e.target).data("areaData"),
200        info = $(data.href),
201        container = info.parent(),
202        target = $(e.target),
203        currentLightbox = $(".featherlight");
204
205      if (e.type === "active" && currentLightbox.length === 0) {
206        $.featherlight('<div class="hotspot-info"></div>', {
207          closeLabel: drawattentionData.closeLabel,
208          closeSpeed: 250,
209          closeOnEsc: false,
210          afterContent: function () {
211            var content = $(".featherlight-inner"),
212              lb = $(".featherlight-content"),
213              mapNo = img.data("id"),
214              lightboxContainer = $(".featherlight");
215
216            info.appendTo(content).show();
217            lb.addClass("lightbox-" + mapNo);
218
219            // Apply accessibility attributes
220            var hotspotTitle =
221              info.find(".hotspot-title").text() || data.title || "";
222            lightboxContainer.attr({
223              tabindex: "-1",
224              role: "dialog",
225              "aria-modal": "true",
226              "aria-label": hotspotTitle,
227            });
228
229            // Fix accessibility issue - links not focusable by keyboard
230            var untabbables = info.find("a[tabindex=-1]");
231            untabbables.attr("tabindex", 0);
232
233            setTimeout(function () {
234              var img = content.find("img"),
235                contentHeight = content.get(0).scrollHeight,
236                imgHeight = img.height(),
237                lbHeight = lb.outerHeight();
238
239              if (contentHeight > lbHeight) {
240                var diff = contentHeight - lbHeight + 50,
241                  newHeight = imgHeight - diff,
242                  minHeight = $(window).innerHeight() / 2;
243
244                newHeight = newHeight < minHeight ? minHeight : newHeight;
245
246                var naturalHeight = img.prop("naturalHeight");
247
248                if (newHeight < naturalHeight) {
249                  img.css({ width: "auto" });
250                  img.animate(
251                    {
252                      height: newHeight,
253                    },
254                    200,
255                  );
256                }
257              }
258            }, 100);
259
260            lightboxAnchorLinks(content, target);
261            lightboxTrapFocus();
262            window.dispatchEvent(new Event("resize")); // Trigger window resize event for [video] shortcode JS
263          },
264          afterOpen: function () {
265            $("body").on("keyup", documentEsc);
266          },
267          afterClose: function () {
268            target.removeClass("hotspot-active");
269            $("body").off("keyup", documentEsc);
270          },
271          beforeClose: function () {
272            stopAudioVideo(document.querySelector(".featherlight-content"));
273            setTimeout(function () {
274              info.hide().appendTo(container);
275            }, 500); // delay hiding content and moving it back outside the lightbox
276          },
277        });
278      }
279    });
280  };
281
282  var documentEsc = function (e) {
283    if (e.keyCode === 27) {
284      $.featherlight.current().close();
285    }
286  };
287
288  var lightboxAnchorLinks = function (content, hotspot) {
289    var links = content.find('.hotspot-content a[href^="#"]');
290    links.on("click", function (e) {
291      e.preventDefault();
292      var targetEl = $(e.target.hash);
293      var current = $.featherlight.current();
294      if (!current) return;
295      current.afterClose = function () {
296        hotspot.removeClass("hotspot-active");
297        $("html").removeClass("with-featherlight");
298        $("html, body").animate(
299          {
300            scrollTop: targetEl.offset().top,
301          },
302          500,
303        );
304      };
305      current.close();
306    });
307  };
308
309  var lightboxTrapFocus = function () {
310    var lightbox = $(".featherlight");
311    var focusableElements = lightbox.find(
312      'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex]:not([tabindex="-1"])',
313    );
314
315    if (focusableElements.length === 0) {
316      return;
317    }
318
319    var firstFocusable = focusableElements.first();
320    var lastFocusable = focusableElements.last();
321
322    // Set initial focus to the lightbox container
323    lightbox.focus();
324
325    lightbox.on("keydown.focustrap", function (e) {
326      if (e.key !== "Tab" && e.keyCode !== 9) {
327        return;
328      }
329
330      if (e.shiftKey) {
331        if (
332          document.activeElement === firstFocusable.get(0) ||
333          document.activeElement === lightbox.get(0)
334        ) {
335          e.preventDefault();
336          lastFocusable.focus();
337        }
338      } else {
339        if (document.activeElement === lastFocusable.get(0)) {
340          e.preventDefault();
341          firstFocusable.focus();
342        }
343      }
344    });
345
346    // Clean up when lightbox closes
347    lightbox.on("closed.featherlight", function () {
348      lightbox.off("keydown.focustrap");
349    });
350  };
351
352  var showTooltip = function (shape, areaData) {
353    var container = $(shape._map._container);
354    var content = $(areaData.href).html();
355
356    var tip = Leaflet.responsivePopup({
357      autoPan: false,
358      closeButton: areaData.trigger == "click",
359      hasTip: container.width() > 840,
360      maxHeight: container.height() * 0.9,
361      offset: new Leaflet.Point(0, 0),
362    });
363
364    tip.setContent(content);
365    shape.bindPopup(tip);
366
367    if (areaData.trigger === "click") {
368      shape.on("click", function (e) {
369        if (shape._path.classList.contains("hotspot-active")) {
370          shape.closePopup();
371        } else {
372          shape.openPopup();
373        }
374      });
375    } else {
376      shape.on("mouseover", function () {
377        shape.openPopup();
378      });
379      shape.on("mouseout", function () {
380        shape.closePopup();
381      });
382    }
383
384    container.on("click", function (e) {
385      e.stopPropagation();
386    });
387    $(document).on("click", function (e) {
388      shape.closePopup();
389      shape._path.classList.remove("hotspot-active");
390    });
391  };
392
393  var leafletSetup = function (img) {
394    var id = img.data("id");
395    var container = $(
396      '<div id="hotspots-map-container-' +
397        id +
398        '" class="hotspots-map-container"></div>',
399    );
400    var imgWidth = img.width();
401    var imgHeight = img.height();
402
403    container.css({
404      width: imgWidth + "px",
405      height: imgHeight + "px",
406    });
407    img.after(container);
408
409    // Check to be sure the container is on the page
410    // To prevent errors when loaded in Elementor popup
411    var containerTest = $("#hotspots-map-container-" + id);
412    if (!containerTest.length) {
413      return;
414    }
415
416    var map = Leaflet.map("hotspots-map-container-" + id, {
417      attributionControl: false,
418      boxZoom: false,
419      crs: Leaflet.CRS.Simple,
420      doubleClickZoom: false,
421      dragging: false,
422      keyboard: false,
423      minZoom: -20,
424      scrollWheelZoom: false,
425      // tap: !isWebkitiOS,
426      tap: true,
427      touchZoom: false,
428      zoomControl: false,
429      zoomSnap: 0,
430    });
431
432    var domImg = img.get(0);
433    var natHeight = domImg.naturalHeight;
434    var natWidth = domImg.naturalWidth;
435    img.data("natW", natWidth);
436    img.data("natH", natHeight);
437    var bounds = [
438      [0, 0],
439      [natHeight, natWidth],
440    ];
441    var imageAlt = img.attr("alt") || img.data("image-title") || "";
442    var imageLayer = Leaflet.imageOverlay(img.attr("src"), bounds, {
443      alt: imageAlt,
444    }).addTo(map);
445    map.fitBounds(bounds);
446
447    leaflets.push({
448      map: map,
449      img: img,
450    });
451
452    drawSpots(img, map);
453  };
454
455  var showContextMenu = function (shape, areaData, e) {
456    var hash = areaData.href;
457    var container = $(shape._map._container);
458
459    if (!hash) {
460      return;
461    }
462
463    $(".da-address-wrapper").remove();
464
465    var windowAddress = window.location.href.split("#")[0];
466    var shapeAddress = windowAddress + hash;
467    var div = $('<div class="da-address-wrapper"></div>')
468      .css({
469        left: e.originalEvent.pageX + "px",
470        top: e.originalEvent.pageY + "px",
471      })
472      .append("<p>" + shapeAddress + "</p>")
473      .append('<span class="da-address-close">&times;</span>')
474      .appendTo(document.body);
475
476    div.find(".da-address-close").on("click", function () {
477      div.remove();
478    });
479  };
480
481  var drawSpots = function (img, map) {
482    var id = img.data("id");
483
484    /* If the image has a usemap attribute, detach the map and store it in the imageMaps object */
485    if (img[0].hasAttribute("usemap")) {
486      var mapName = img.attr("usemap").replace("#", "");
487      var imageMap = $('map[name="' + mapName + '"]');
488      imageMaps[id] = imageMap.detach();
489      img.removeAttr("usemap");
490    } else {
491      /* Else we've already removed the image map, so we just need to get it */
492      var imageMap = imageMaps[id];
493    }
494
495    var areas = imageMap.find("area");
496    var container = img.parents(".hotspots-container");
497
498    areas.each(function () {
499      var area = $(this);
500      var shape = area.attr("shape");
501      var coords = area.attr("coords").split(",");
502      var areaData = {
503        style: area.data("color-scheme")
504          ? area.data("color-scheme")
505          : "default",
506        title: area.attr("title"),
507        href: area.attr("href"),
508        spot: area.data("id"),
509        target: area.attr("target"),
510        action: area.data("action"),
511        layout: container.data("layout"),
512        trigger: container.data("trigger"),
513      };
514      switch (shape) {
515        case "circle":
516          renderCircle(coords, map, img, areaData);
517          break;
518        case "rect":
519          renderPoly(coords, map, img, areaData);
520          break;
521        case "polygon":
522          renderPoly(coords, map, img, areaData);
523          break;
524        case "poly": // Leaving this here for backward compatibility
525          renderPoly(coords, map, img, areaData);
526          break;
527      }
528    });
529    // A11y fixes after all the spots are drawn
530    a11yFixes(img, map);
531
532    // Link to area after all the spots are drawn
533    hotspots.linkToArea();
534  };
535
536  var renderCircle = function (coords, map, img, areaData) {
537    var x = coords[0];
538    var y = img.data("natH") - coords[1];
539    var rad = coords[2];
540    var circle = Leaflet.circle([y, x], {
541      radius: rad,
542      className: "hotspot-" + areaData.style,
543      title: areaData.title,
544    });
545
546    circle.on("add", function (e) {
547      var $circle = $(e.target.getElement());
548      $circle.data("areaData", areaData);
549      $circle.attr("tabindex", "0");
550      $circle.attr("aria-label", areaData.title);
551      $circle.attr("name", areaData.title);
552    });
553
554    circle.addTo(map);
555
556    // If this is a more info hotspot, add it to the infoSpots object
557    if (areaData.href.charAt(0) === "#") {
558      var spotName = areaData.href.replace("#", "");
559      hotspots.infoSpots[spotName] = circle;
560    }
561
562    shapeEvents(circle, areaData);
563  };
564
565  var renderPoly = function (coords, map, img, areaData) {
566    var xCoords = [];
567    var yCoords = [];
568    for (var i = 0; i < coords.length; i++) {
569      if (i % 2 == 0) {
570        xCoords.push(coords[i]);
571      } else {
572        yCoords.push(coords[i]);
573      }
574    }
575
576    var polyCoords = yCoords.map(function (coord, index) {
577      return [img.data("natH") - coord, xCoords[index]];
578    });
579
580    var poly = Leaflet.polygon(polyCoords, {
581      className: "hotspot-" + areaData.style,
582      title: areaData.title,
583    });
584
585    // Attach the area data to the path as soon as it's added to the map, a11y attributes
586    poly.on("add", function (e) {
587      var $poly = $(e.target.getElement());
588      $poly.data("areaData", areaData);
589      $poly.attr("tabindex", "0");
590      $poly.attr("aria-label", areaData.title);
591      $poly.attr("name", areaData.title);
592    });
593
594    poly.addTo(map);
595
596    // If this is a more info hotspot, add it to the infoSpots object
597    if (areaData.href.charAt(0) === "#") {
598      var spotName = areaData.href.replace("#", "");
599      hotspots.infoSpots[spotName] = poly;
600    }
601
602    shapeEvents(poly, areaData);
603  };
604
605  var shapeOver = function (shape, areaData, e) {
606    var $shape = $(e.target.getElement());
607    $shape.trigger("over.responsilight");
608    if (
609      areaData.trigger === "hover" &&
610      (e.type !== "touchstart") & (e.type !== "keypress")
611    ) {
612      $shape.addClass("hotspot-active");
613      $shape.trigger("active.responsilight");
614    }
615  };
616
617  var shapeOut = function (shape, areaData, e) {
618    var $shape = $(e.target.getElement());
619    $shape.trigger("out.responsilight");
620    if (areaData.trigger === "hover" && e.type !== "keypress") {
621      $shape.removeClass("hotspot-active");
622      $shape.trigger("inactive.responsilight");
623      // If mouse user, blur hover spots when mouse moves out
624      if (e.type === "mouseout") {
625        $shape.trigger("blur");
626      }
627    }
628  };
629
630  var shapeClick = function (shape, areaData, e) {
631    var $shape = $(e.target.getElement());
632    $shape.trigger("areaClick.responsilight");
633    if (
634      areaData.trigger === "hover" &&
635      e.type !== "touchstart" &&
636      e.type !== "keypress" &&
637      !isMobileSafari
638    ) {
639      return;
640    }
641    $shape.toggleClass("hotspot-active");
642    if ($shape.hasClass("hotspot-active")) {
643      $shape.trigger("active.responsilight");
644    } else {
645      $shape.trigger("inactive.responsilight");
646    }
647    var oldActive = $shape.siblings(".hotspot-active");
648    if (oldActive.length) {
649      oldActive.removeClass("hotspot-active");
650    }
651  };
652
653  /**
654   * Very Custom logic to just handle displaying the popup on focus
655   * Why we had to do it this way:
656   * shape.on('focus') doesn't fire similar to the other events
657   * openPopup() would steal the focus and it's hard to then rely on focusout on shape to remove the popup, cause the focusout would fire right after calling openPopup()
658   * closePopup() would also programmatically control/alter the tab behavior and that is another problem
659   *
660   */
661  var maybeDisplayPopupOnFocus = function (shape, areaData, nativeEvent) {
662    if (areaData.trigger !== "hover") return;
663    if (areaData.layout !== "tooltip") return;
664
665    var $shape = $(nativeEvent.target);
666    if ($shape.hasClass("hotspot-active")) {
667      return;
668    }
669
670    $shape.addClass("hotspot-active");
671    $shape.trigger("over.responsilight");
672    shape.openPopup();
673
674    setTimeout(() => {
675      const activeElement = document.activeElement;
676      if (!activeElement.classList.contains("leaflet-rrose-content")) {
677        return;
678      }
679      $shape.focus();
680
681      const closePopupAfterFocusIsLost = (e) => {
682        shape.closePopup();
683        $shape.removeClass("hotspot-active");
684        $shape.trigger("out.responsilight");
685
686        $shape.off("focusout", closePopupAfterFocusIsLost);
687      };
688      $shape.on("focusout", closePopupAfterFocusIsLost);
689    }, 0);
690  };
691
692  /**
693   * Left, right, top, bottom & lightbox layout + click trigger
694   * Display Title on focus
695   */
696  var maybeDisplayTitleOnFocusForLayout = function (
697    shape,
698    areaData,
699    nativeEvent,
700  ) {
701    if (nativeEvent.type !== "focus") return;
702    if (areaData.trigger !== "click") return;
703    if (!layoutsThatShowTitleOnFocus.includes(areaData.layout)) return;
704
705    const target = nativeEvent.target;
706    target.dispatchEvent(new MouseEvent("mouseover", { bubbles: true }));
707    target.addEventListener("focusout", function handleFocusOut(e) {
708      target.dispatchEvent(new MouseEvent("mouseout", { bubbles: true }));
709      target.removeEventListener("focusout", handleFocusOut);
710    });
711  };
712
713  var shapeEvents = function (shape, areaData) {
714    // Handle URL spots
715    if (areaData.action == "url") {
716      if (areaData.title) {
717        shape.bindTooltip(areaData.title);
718      }
719      shape.on("click", function (e) {
720        if (areaData.target == "_new" && !isMobileSafari) {
721          // new window
722          window.open(areaData.href, "_blank");
723        } else {
724          // same window
725          var first = areaData.href.charAt(0);
726          var targetElem;
727          try {
728            targetElem = first === "#" ? $(areaData.href) : null;
729          } catch (error) {
730            targetElem = null;
731          }
732          if (targetElem && targetElem.length) {
733            // hash link to existing target
734            $("html, body").animate(
735              {
736                scrollTop: targetElem.offset().top - 50,
737              },
738              750,
739              function () {
740                // callback after scrolling
741                history.pushState({}, "", areaData.href);
742              },
743            );
744          } else {
745            window.location = areaData.href;
746          }
747        }
748      });
749      return;
750    }
751
752    // Show right-click context menu for logged-in admins only
753    if (drawattentionData.isLoggedIn && drawattentionData.isAdmin) {
754      shape.on("contextmenu", function (e) {
755        showContextMenu(shape, areaData, e);
756      });
757    }
758
759    // Handle tooltip spots
760    if (areaData.layout === "tooltip") {
761      showTooltip(shape, areaData);
762    }
763
764    // Add styled tooltip to all non-hover areas
765    if (
766      areaData.action === "url" ||
767      (areaData.trigger === "click" && areaData.layout !== "tooltip")
768    ) {
769      if (areaData.title) {
770        shape.bindTooltip(areaData.title);
771      }
772    }
773
774    // Handle all other spots
775    var moved = false;
776
777    shape.on(
778      "touchstart touchmove touchend click mouseover mouseout keypress focus blur",
779      function (e) {
780        switch (e.type) {
781          case "touchstart":
782            moved = false;
783            break;
784          case "touchmove":
785            moved = true;
786            break;
787          case "touchend":
788            if (moved) {
789              return;
790            }
791            shapeOver(shape, areaData, e);
792            shapeClick(shape, areaData, e);
793            break;
794          case "click":
795            shapeClick(shape, areaData, e);
796            break;
797          case "keypress":
798            var key = e.originalEvent.which;
799            if (key == 13 || key == 32) {
800              e.originalEvent.preventDefault();
801              shapeClick(shape, areaData, e);
802            }
803            break;
804          case "mouseover":
805            if (!isMobileSafari || (isMobileSafari && getBrowserVersion < 13)) {
806              shapeOver(shape, areaData, e);
807            }
808            if (isMobileSafari && !(getBrowserVersion() >= 12)) {
809              shapeClick(shape, areaData, e);
810            }
811            break;
812          case "focus":
813            shapeOver(shape, areaData, e);
814            break;
815          case "blur":
816            shapeOut(shape, areaData, e);
817            break;
818          case "mouseout":
819            shapeOut(shape, areaData, e);
820            break;
821        }
822      },
823    );
824
825    $(shape._path).on("focus", function (nativeEvent) {
826      maybeDisplayPopupOnFocus(shape, areaData, nativeEvent);
827      maybeDisplayTitleOnFocusForLayout(shape, areaData, nativeEvent);
828    });
829  };
830
831  var a11yFixes = function (img, map) {
832    let svg = img
833      .siblings(".leaflet-container")
834      .find(".leaflet-overlay-pane svg");
835    let id = img.data("id");
836
837    // Add title and description to SVG
838    svg.prepend(
839      '<desc id="img-desc-' +
840        id +
841        '">' +
842        img.data("image-description") +
843        "</desc>",
844    );
845    svg.prepend(
846      '<title id="img-title-' +
847        id +
848        '">' +
849        img.data("image-title") +
850        "</title>",
851    );
852
853    // Use title and desc to describe the svg
854    svg.attr("aria-labelledby", "img-title-" + id);
855    svg.attr("aria-describedby", "img-desc-" + id);
856
857    // Make svg screen reader traversable
858    svg.attr("role", "group");
859
860    // Create a traversable list
861    var group = svg.find("g").attr("role", "list");
862    group.children().attr("role", "listitem");
863
864    // Handle tooltips for the map
865    map.on("popupopen", function (popup) {
866      // shift focus to the popup when it opens
867      $(popup.popup._container)
868        .find(".leaflet-rrose-content")
869        .attr("tabindex", "-1")
870        .focus();
871
872      // move the close button to the end of the popup content so screen readers reach it
873      // after the main popup content, not before
874      var close = $(popup.popup._container)
875        .find(".leaflet-rrose-close-button")
876        .detach();
877      close.attr("aria-label", "Close item");
878      $(popup.popup._container).append(close);
879
880      $(document).on("keydown", function (e) {
881        if (e.which == 27) {
882          map.closePopup();
883        }
884      });
885    });
886
887    // return focus to the icon we started from before opening the pop up
888    map.on("popupclose", function (popup) {
889      $(document).off("keydown");
890      $(popup.popup._source._path).focus();
891    });
892  };
893
894  hotspots.linkToArea = function () {
895    // Called after the shapes are drawn
896    var hash = window.location.hash;
897    if (!hash) return;
898
899    var spotName = hash.replace("#", "");
900    if (!hotspots.infoSpots.hasOwnProperty(spotName)) return;
901
902    Object.keys(hotspots.infoSpots).forEach(function (key) {
903      hotspots.infoSpots[key].closeTooltip();
904    });
905
906    const spot = hotspots.infoSpots[spotName];
907
908    if (!spot._path.classList.contains("hotspot-active")) {
909      spot.fire("click");
910    }
911
912    scrollIntoViewIfNeeded(spot._map?.getContainer());
913  };
914
915  hotspots.setup = function () {
916    mapSetup();
917  };
918
919  hotspots.resizeTimer = null;
920  hotspots.resizing = false;
921
922  hotspots.init = function () {
923    // For backward compatibility - resets the size of the leaflet on demand
924    leaflets.forEach(function (item) {
925      var isLoaded = item.img.data("status") === "loaded";
926
927      if (!isLoaded) {
928        return;
929      }
930
931      item.img.next(".hotspots-map-container").css({
932        width: item.img.width() + "px",
933        height: item.img.height() + "px",
934      });
935      item.map.invalidateSize(true);
936      item.map.fitBounds([
937        [0, 0],
938        [item.img.data("natH"), item.img.data("natW")],
939      ]);
940    });
941  };
942
943  hotspots.compatibilityFixes = function () {
944    if (window.Foundation) {
945      /* Fix for Foundation firing tag change event indiscriminately when some items are clicked on the page */
946      $(window).on("change.zf.tabs", function (e) {
947        if (e.target.tagName !== "INPUT") {
948          hotspots.init();
949        }
950      });
951    }
952
953    $(window).on("pageloaded load", function () {
954      /* Listen for pageloaded and load events on the window */
955      hotspots.init();
956    });
957
958    $(window).on("et_hashchange", function () {
959      /* Listen for Divi hashchange */
960      setTimeout(function () {
961        hotspots.init();
962      }, 1000);
963    });
964
965    $(".ult_tabs").on("click", ".ult_tab a", function () {
966      /* Ultimate tabs */
967      setTimeout(function () {
968        hotspots.init();
969      }, 2000);
970    });
971
972    $(
973      "a[data-vc-accordion], .et_pb_tabs .et_pb_tabs_controls li, .et_pb_toggle_title",
974    ).on("click", function () {
975      /* Divi tabs, accordion, toggle */
976      setTimeout(function () {
977        hotspots.init();
978      }, 1000);
979    });
980    $(".vc_tta-tabs-container").on("click", ".vc_tta-tab", function () {
981      /* Visual composer tabs */
982      setTimeout(function () {
983        hotspots.init();
984      }, 1000);
985    });
986
987    $(".ui-tabs-anchor, .nav-tabs > li").on("click", function () {
988      /* UI Tabs */
989      setTimeout(function () {
990        hotspots.init();
991      }, 750);
992    });
993
994    $(".fl-accordion-button").on("click", function () {
995      /* Beaver Builder accordion */
996      setTimeout(function () {
997        hotspots.init();
998      }, 500);
999    });
1000
1001    $(".responsive-tabs").on(
1002      "click",
1003      ".responsive-tabs__list__item",
1004      function () {
1005        /* Responsive tabs */
1006        hotspots.init();
1007      },
1008    );
1009
1010    $(".elementor-tabs").on("click", ".elementor-tab-title", function () {
1011      /* Elementor tabs */
1012      hotspots.init();
1013    });
1014
1015    $(".fl-tabs").on("click", "a.fl-tabs-label", function () {
1016      /* Beaver Builder tabs */
1017      hotspots.init();
1018    });
1019
1020    $(".uabb-adv-accordion-button .uabb-tabs").on("click", function () {
1021      /* Ultimate Beaver Builder addons accordion and tabs */
1022      setTimeout(function () {
1023        hotspots.init();
1024      }, 250);
1025    });
1026    $(".w-tabs-item").on("click", function () {
1027      /* Custom Woocommerce tabs */
1028      setTimeout(function () {
1029        hotspots.init();
1030      }, 1000);
1031    });
1032  };
1033})(jQuery, (window.hotspots = window.hotspots || {}));
1034
1035jQuery(function () {
1036  hotspots.setup();
1037  hotspots.compatibilityFixes();
1038});
1039
1040jQuery(document).on("elementor/popup/show", function () {
1041  hotspots.setup();
1042  hotspots.compatibilityFixes();
1043});
1044
1045jQuery(window).on("resize orientationchange", function (e) {
1046  var $window = jQuery(this);
1047  if (!hotspots.resizing) {
1048    $window.trigger("resizeStart.responsilight");
1049    hotspots.resizing = true;
1050  }
1051  clearTimeout(hotspots.resizeTimer);
1052  hotspots.resizeTimer = setTimeout(function () {
1053    hotspots.init();
1054    $window.trigger("resizeComplete.responsilight");
1055    hotspots.resizing = false;
1056  }, 250);
1057});
1058
1059jQuery(window).on("hashchange", function () {
1060  hotspots.linkToArea();
1061});
1062
1063window.onerror = function (errorMsg, url, lineNumber) {
1064  // This should be a fun little experiement!
1065  var errorBox = jQuery(".da-error").show();
1066  if (errorBox.length) {
1067    var contents = errorBox.html();
1068    errorBox.html(
1069      contents +
1070        "<br/><br/><strong>Error:</strong> " +
1071        errorMsg +
1072        "<br/>Line " +
1073        lineNumber +
1074        ": " +
1075        url,
1076    );
1077  }
1078  return false;
1079};
1080
1081/* Fix for Elementor bug - duplicating DA images when popup opens */
1082jQuery(document).on("elementor/popup/show", () => {
1083  setTimeout(function () {
1084    var imageContainers = jQuery(".hotspots-image-container");
1085    imageContainers.each(function () {
1086      var extraMapContainers = jQuery(this)
1087        .find(".hotspots-map-container:not(:first)")
1088        .remove(); // find all but the first one and remove them
1089    });
1090  }, 250);
1091});
1092
1093function scrollIntoViewIfNeeded(el, options = {}) {
1094  if (!el) return;
1095  const rect = el.getBoundingClientRect();
1096
1097  const fullyVisible =
1098    rect.top >= 0 &&
1099    rect.left >= 0 &&
1100    rect.bottom <=
1101      (window.innerHeight || document.documentElement.clientHeight) &&
1102    rect.right <= (window.innerWidth || document.documentElement.clientWidth);
1103
1104  if (!fullyVisible) {
1105    el.scrollIntoView(options);
1106  }
1107}

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.