PageSourceSearch

https://www.relias.com/wp-content/themes/Relias-2019/js/relias-scripts.js?ver=4.2

js relias.com collected 2026-10-01 10:40:18 UTC 39,176 bytes, 1,046 lines download raw bytes

1(function ($) {
2  // Breakpoints
3  var phone = 480,
4    phablet = 768,
5    tablet = 992,
6    desktop = 1200;
7
8  function viewport() {
9    var e = window,
10      a = "inner";
11    if (!("innerWidth" in window)) {
12      a = "client";
13      e = document.documentElement || document.body;
14    }
15    return { width: e[a + "Width"], height: e[a + "Height"] };
16  }
17
18  var vpWidth = viewport().width; // This should match the media query
19
20  //----------------------------------------------------------
21  // HEADER SEARCH – Add/Remove 'active' class
22  //----------------------------------------------------------
23  function reliasHeaderSearch() {
24    $(".search-text").click(function (e) {
25      if (window.innerWidth > 1170 || (window.innerWidth < 992 && window.innerWidth > 530)) {
26        e.preventDefault();
27        $(this).parent(".search-container").toggleClass("js-active");
28        $(this).toggleClass("js-active");
29        $(this).siblings(".swiftype-searchpage-form").toggleClass("js-active");
30      }
31    });
32
33    $(document).click(function (event) {
34      var $target = $(event.target);
35      if (!$target.closest(".search-container").length && $(".search-text").hasClass("js-active")) {
36        $(".search-text").removeClass("js-active");
37        $(".swiftype-searchpage-form").removeClass("js-active");
38      }
39    });
40  }
41
42  //----------------------------------------------------------
43  // DETECT BROWSER
44  //----------------------------------------------------------
45  function reliasBrowserType() {
46    if (navigator.userAgent.indexOf("Mac OS X") != -1) {
47      $("body").addClass("mac_browsers");
48    } else {
49      $("body").addClass("windows_browser");
50    }
51  }
52
53  //----------------------------------------------------------
54  // SVG - svg tag cleanup
55  //----------------------------------------------------------
56  function reliasSVG() {
57    if ($("img.svg_img").length) {
58      $("img.svg_img").each(function () {
59        var $img = $(this);
60        var imgID = $img.attr("id");
61        var imgClass = $img.attr("class");
62        var imgURL = $img.attr("src");
63
64        $.get(
65          imgURL,
66          function (data) {
67            // Get the SVG tag, ignore the rest
68            var $svg = $(data).find("svg");
69
70            // Add replaced image's ID to the new SVG
71            if (typeof imgID !== "undefined") {
72              $svg = $svg.attr("id", imgID);
73            }
74            // Add replaced image's classes to the new SVG
75            if (typeof imgClass !== "undefined") {
76              $svg = $svg.attr("class", imgClass + " replaced-svg");
77            }
78
79            // Remove any invalid XML tags as per https://validator.w3.org
80            $svg = $svg.removeAttr("xmlns:a");
81
82            // Check if the viewport is set, else we gonna set it if we can.
83            if (!$svg.attr("viewBox") && $svg.attr("height") && $svg.attr("width")) {
84              $svg.attr("viewBox", "0 0 " + $svg.attr("height") + " " + $svg.attr("width"));
85            }
86
87            // Replace image with new SVG
88            $img.replaceWith($svg);
89          },
90          "xml",
91        );
92      });
93    }
94  }
95
96  function reliasFacetWPFilters() {
97    if (
98      $(".post-type-archive-successstories").length ||
99      $(".post-type-archive-resources").length ||
100      $("body.blog").length ||
101      $("body.archive.category").length ||
102      $("body.page-template-webinar-page").length ||
103      $("body.post-type-archive-events").length ||
104      $("body.page-template-articles-reports-page").length
105    ) {
106      $(document).on("facetwp-loaded", function () {
107        // Show a reset button & filter selections if any facets are in use
108        var in_use = false;
109        // see if any facets are in use
110        $.each(FWP.facets, function (name, val) {
111          if (name !== "paged" && val.length > 0) {
112            in_use = true;
113          }
114        });
115
116        // hide webinar selection type
117        if (in_use) {
118          if ($("body.page-template-webinar-page").length) {
119            let $webinarFacet = $('.facetwp-selections ul li[data-facet="resource_type"]');
120            console.log($(".facetwp-selections li"));
121            $webinarFacet.css({ display: "none" });
122          }
123        }
124
125        // true = show, false = hide
126        if ($("body.page-template-webinar-page").length) {
127          $(".facetwp-reset-btn, .archive-filter-selections").toggle(false);
128          $("div[data-name=resource_type] select").attr("disabled", "true");
129        } else {
130          $(".facetwp-reset-btn, .archive-filter-selections").toggle(in_use);
131        }
132
133        /*// 'Disable' empty facets
134				$.each(FWP.settings.num_choices, function(key, val) {
135					var $parent = $('.facetwp-facet-' + key).closest('.facetwp-facet');
136					(0 === val) ? $parent.addClass('js-disabled') : $parent.removeClass('js-disabled');
137				});*/
138
139        // Pagination scrolling
140        var ondemandQueryString = window.location.search;
141        var ondemandParams = new URLSearchParams(ondemandQueryString);
142        var pageParam = ondemandParams.get("_paged");
143
144        if (!$("body.page-template-webinar-page").length) {
145          //$('html, body').animate({ scrollTop: 0 }, 500);
146        } else if (pageParam) {
147          // /resource/webinars on-demand section offset
148          var ondemandOffset = $("#ondemand-top").offset().top;
149          //$('html, body').animate({ scrollTop: ondemandOffset - 140 }, 500);
150        }
151      });
152
153      // Loading icon whenever FacetWP is loading
154      // let $loading = $('.loading-gif-container');
155      // $(document).on('facetwp-refresh', function() {
156      // 	if (window.location.pathname !== '/resource/webinar')  {
157      // 		$loading.fadeIn();
158      // 	}
159      // 	console.log(window.location.pathname);
160      // 	//$('.facetwp-template').animate({ opacity: 0 }, 500);
161      // });
162      //
163      // $(document).on('facetwp-loaded', function() {
164      // 	if (window.location.pathname !== '/resource/webinar')  {
165      // 		$loading.fadeOut();
166      // 	}
167      // 	//$('.facetwp-template').animate({ opacity: 1 }, 500);
168      //
169      // });
170    }
171  }
172
173  //----------------------------------------------------------
174  // ANCHOR LINKS SMOOTH SCROLL - if a link is an anchor link/has value,
175  // smooth scroll to the section and add the hash to the URL when done scrolling
176  // .not() specifies what to ignore
177  //----------------------------------------------------------
178  function reliasAnchorLinkSmoothScroll() {
179    if (
180      $('.main-wrapper a[href*="#"]')
181        .not('[href="#"]')
182        .not('[href="#0"]')
183        .not(".relias-modal")
184        .not(".tab-link") // bootstrap tabs
185        .not(".collapsed").length
186    ) {
187      $('.main-wrapper a[href*="#"]')
188        .not('[href="#"]')
189        .not('[href="#0"]')
190        .not(".relias-modal") // bootstrap modals
191        .not(".tab-link") // boostrap tabs
192        .not(".accordion a") // bootstrap accordions
193        .not(".sticky-nav .nav-link") // handled by reliasStickyNavLinks()
194        .not("a.card-link.no-hash")
195        .on("click", function (event) {
196          // Make sure this.hash has a value before overriding default behavior
197          // Make sure this.href is equal to the current page, so smooth scrolling only works on same page links
198          if (this.hash !== "" && this.hostname == window.location.hostname) {
199            // Prevent default anchor click behavior
200            event.preventDefault();
201
202            // Store hash
203            var hash = this.hash;
204
205            console.log("clicked..?");
206            if ($(hash).length) {
207              $("html, body").animate(
208                {
209                  scrollTop: $(hash).offset().top - 90,
210                },
211                900,
212                "swing",
213              );
214            }
215          }
216        });
217    }
218  }
219
220  //----------------------------------------------------------
221  // BOOTSTRAP TABS - controls open tabs and keeps them open on repeated click
222  //----------------------------------------------------------
223  function reliasBootstrapTabs() {
224    if ($(".panel.panel-tabbed-content")) {
225      // Allow outer <li> element to become clickable
226      let $tabLinks = $(".tab-names a.tab-link");
227      let $tabListItems = $(".tab-names li");
228      let $tabs = $(".panel-tabbed-content ul.nav");
229      $tabLinks.each(function () {
230        $(this).on("click", function (event) {
231          if ($(this).hasClass("active")) {
232            event.preventDefault();
233            event.stopPropagation();
234          }
235        });
236      });
237      // Add/remove active class on click
238      $tabListItems.each(function () {
239        let $tabs = $(".tab-pane");
240        $(this).on("click", function () {
241          let $id = $(this).find("a.tab-link").attr("href");
242          $tabListItems.removeClass("active");
243          $(this).addClass("active");
244          $tabs.each(function () {
245            let $hash = "#" + $(this).attr("id");
246            if ($hash === $id) {
247              $("a.tab-link").removeClass("active");
248              $(this).addClass("active");
249            } else {
250              $(this).removeClass("active");
251            }
252          });
253        });
254      });
255      // Set width of tabs dynamically based on device width
256      let $tabsWidth = $tabs.width();
257      if (vpWidth < phablet || $tabsWidth < 1000) {
258        $tabs.css("min-width", "1000px");
259        $tabs.css("justify-content", "start");
260      }
261      $(window).resize(function () {
262        if (vpWidth < phablet || $tabsWidth < 1000) {
263          $tabs.css("min-width", "1000px");
264          $tabs.css("justify-content", "start");
265        } else if (vpWidth > phablet && $tabsWidth < 100) {
266          $tabs.css("min-width", "unset");
267          $tabs.css("justify-content", "center");
268        }
269      });
270    }
271  }
272  //----------------------------------------------------------
273  // STICKY NAVS - handles fixed nav sidebars for sticky-nav.php panel c
273ontent
274  //----------------------------------------------------------
275  function reliasStickyNav() {
276    if ($(".panel-sticky-nav").length) {
277      $(".panel-sticky-nav").each(function () {
278        var navArray = [],
279          $navPanels = $(".panel-sticky-nav");
280        for (let i = 0; i < $navPanels.length; i++) {
281          navArray.push($("#side-" + i));
282        }
283
284        function stickyNav(nav) {
285          // global (mobile and desktop) objects
286          var $sidebarHeight = nav.height(),
287            $sidebarWidth = nav.width(),
288            $sidebar = $("." + nav.attr("id") + "-anchor"),
289            $sidebarAnchor = $sidebar.offset().top,
290            $stickyNavContent = $sidebar.siblings($(".sticky-nav-content")),
291            $contentBottom = $("." + nav.attr("id") + "-anchor-bottom").offset().top,
292            $windowTop = $(window).scrollTop();
293
294          // mobile sticky nav and dropdown arrow objects
295          let $arrowDropdown = nav.find($(".arrow-dropdown")),
296            $menuItems = nav.find($(".nav-item")),
297            $closedMenuItems = nav.find($(".nav-item:not(:first)")),
298            $firstMenuItem = nav.find($(".nav-item:first")),
299            $firstMenuItemLink = $firstMenuItem.find(".nav-link");
300
301          // find first anchor and align it with the top of the sticky nav on mobile
302          let $stickyNavAnchors = $stickyNavContent.find(".anchor");
303          if (vpWidth <= tablet) {
304            $stickyNavAnchors.eq(0).css({ top: "-" + $sidebarHeight });
305          } else {
306            $stickyNavAnchors.eq(0).css({ top: "-100px" });
307          }
308
309          // arrow dropdown click handler
310          if (vpWidth <= tablet) {
311            $arrowDropdown.unbind("click").on("click", function () {
312              $menuItems.each(function () {
313                let $this = $(this);
314                if ($this.hasClass("hide")) {
315                  // if only the active item is shown
316                  $this.toggleClass("hide"); // show all others
317                } else if (!$this.hasClass("hide") && !$this.children().hasClass("active")) {
318                  $this.toggleClass("hide"); // hide the rest
319                }
320              });
321              $arrowDropdown.toggleClass("flip");
322            });
323          }
324
325          // position the mobile dropdown arrow (appearance controlled below)
326          if (vpWidth <= phablet) {
327            $arrowDropdown.css({ right: "40px" });
328          } else if (vpWidth >= phablet && vpWidth <= desktop) {
329            $arrowDropdown.css({ right: "72px" });
330          }
331
332          // update the active menu item on scroll if menu is closed
333          if (vpWidth <= tablet) {
334            let $menuItemLinks = $menuItems.children(".nav-link");
335            $menuItemLinks.each(function () {
336              let $this = $(this);
337              let $parent = $this.parent();
338              let $nav = $("nav");
339              if ($this.hasClass("active")) {
340                if ($parent.hasClass("hide")) {
341                  $parent.toggleClass("hide");
342                }
343              } else {
344                if (!$parent.hasClass("hide")) {
345                  $parent.toggleClass("hide");
346                  if (!nav.hasClass("stick")) {
347                    $menuItems.first().toggleClass("hide");
348                  }
349                }
350              }
351            });
352            $arrowDropdown.removeClass("flip");
353          }
354
355          // sticky nav behavior
356          if (vpWidth >= tablet) {
357            // screens > tablet (md) size
358            if ($windowTop + $sidebarHeight > $contentBottom - 100) {
359              nav.css({ top: ($windowTop + $sidebarHeight - $contentBottom + 36) * -1, width: $sidebarWidth });
360            } else if ($windowTop + 80 > $sidebarAnchor) {
361              nav.addClass("stick").css({ top: 80, width: $sidebarWidth });
362            } else {
363              nav.removeClass("stick").css({ top: "", width: "" });
364            }
365          } else if (vpWidth <= tablet) {
366            // screens < tablet (md) size
367            if ($windowTop + $sidebarHeight > $contentBottom) {
368              nav.css({ top: ($windowTop + $sidebarHeight - $contentBottom + 100) * -1 });
369              if ($arrowDropdown.is(":visible")) {
370                $arrowDropdown.hide();
371              }
372            } else if ($windowTop > $sidebarAnchor - 88) {
373              // scrolling sticky nav onto screen
374              if (vpWidth <= phablet) {
375                nav.addClass("stick").css({ top: 88, width: "100%", backgroundColor: "#f4f4f2", right: 0, padding: "0 40px" });
376                $stickyNavAnchors.eq(0).css({ height: $sidebarHeight + 30 + "px" });
377                if (!$arrowDropdown.is(":visible")) {
378                  $arrowDropdown.show();
379                }
380              } else if (vpWidth >= phablet) {
381                nav.addClass("stick").css({ top: 88, width: "100%", backgroundColor: "#f4f4f2", right: 0, padding: "0 72px" });
382                $stickyNavAnchors.eq(0).css({ height: $sidebarHeight + 30 + "px" });
383                if (!$arrowDropdown.is(":visible")) {
384                  $arrowDropdown.show();
385                }
386              }
387            } else {
388              // scrolling sticky nav off of screen
389              nav.removeClass("stick").css({ top: "", width: "", padding: "0" });
390              $stickyNavAnchors.eq(0).css({ height: "15px" });
391              if ($arrowDropdown.is(":visible")) {
392                $arrowDropdown.hide();
393              }
394            }
395          } else {
396            nav.removeClass("stick").css({ top: "", width: "" });
397          }
398        }
399
400        // On scroll, stickyNav() runs and checks all .panel-sticky-nav anchors/offsets on the page
401        for (let i = 0; i < navArray.length; i++) {
402          stickyNav(navArray[i]);
403        }
404      });
405    }
406  }
407
408  //----------------------------------------------------------
409  // STICKY NAV SCROLLSPY - keeps .active on the nav item for the section the
410  // reader is actually looking at.
411  //
412  // Replaces Bootstrap scrollspy for the sticky-nav panel, which activated
413  // items too early for two reasons: it caches offsets at init, so
414  // late-loading images/posts/embeds leave them stale, and it measures the
415  // .anchor divs, which sit 100px above their heading. Everything here is
416  // measured off the section headings, live, on every pass.
417  //----------------------------------------------------------
418  var STICKY_NAV_READ_BUFFER = 28, // px past the fixed chrome a heading must reach to count as read
419    STICKY_NAV_SCROLLED_OUT = 0.8, // scrolling down, hand over once this much of a section has left
420    STICKY_NAV_CLICK_GAP = 8; // where a clicked heading lands - must stay under the read buffer
421
422  var stickyNavActive = [], // active link index, per panel
423    stickyNavLastScroll = null, // previous scrollTop, for scroll direction
424    stickyNavTravel = 0, // travel accumulated since the last direction change
425    stickyNavDirection = "down"; // last confirmed scroll direction
426
427  // Direction from accumulated travel rather than a single delta: a trackpad
428  // can report 1-2px per event, and dropping those outright would leave a slow
429  // scroll up running on the scroll-down rule.
430  function reliasStickyNavTrack(delta) {
431    if (!delta) {
432      return;
433    }
434    if (delta < 0 !== stickyNavTravel < 0) {
435      stickyNavTravel = 0; // reversed - start counting this way instead
436    }
437    stickyNavTravel += delta;
438
439    if (stickyNavTravel <= -3) {
440      stickyNavDirection = "up";
441      stickyNavTravel = 0;
442    } else if (stickyNavTravel >= 3) {
443      stickyNavDirection = "down";
444      stickyNavTravel = 0;
445    }
446  }
447
448  // height of the fixed chrome covering the top of the page
449  function reliasStickyNavChrome($nav, forceStuck) {
450    var width = viewport().width,
451      $header = $("header.primary-header"),
452      chrome = width >= tablet ? 80 : 88;
453
454    if ($header.length) {
455      // Measured live off the header, so its real height wins over the
456      // fallbacks above. rect bottom rather than height picks up the offsets
457      // too - the 32px admin bar when logged in, or a header that has
458      // scrolled away when it isn't fixed (clamped to 0).
459      chrome = Math.max(0, $header[0].getBoundingClientRect().bottom);
460    }
461    // below tablet the sticky nav is fixed under the header and covers content
462    if (width < tablet && $nav.length) {
463      if ($nav.hasClass("stick")) {
464        chrome = Math.max(chrome, $nav[0].getBoundingClientRect().bottom);
465      } else if (forceStuck) {
466        chrome += $nav.outerHeight();
467      }
468    }
469    return chrome;
470  }
471
472  // the section a nav link points at, and the heading to measure it by
473  function reliasStickyNavItem($link) {
474    var target = document.getElementById(($link.attr("href") || "").slice(1)),
475      $section = target ? $(target).closest(".sticky-section") : $(),
476      $heading = $section.find(".section-title").first();
477
478    return { $section: $section, $heading: $heading.length ? $heading : $section };
479  }
480
481  function reliasStickyNavSpy() {
482    if (!$(".panel-sticky-nav").length) {
483      return;
484    }
485    var $window = $(window),
486      scrollTop = $window.scrollTop(),
487      viewportHeight = $window.height(),
488      atPageBottom = scrollTop + viewportHeight >= $(document).height() - 2,
489      delta = stickyNavLastScroll === null ? 0 : scrollTop - stickyNavLastScroll;
490
491    reliasStickyNavTrack(delta);
492    stickyNavLastScroll = scrollTop;
493
494    $(".panel-sticky-nav").each(function (panelIndex) {
495      var $panel = $(this),
496        $links = $panel.find(".sticky-nav .nav-link[href^='#']"),
497        readLine = reliasStickyNavChrome($panel.find(".sticky-nav nav"), false) + STICKY_NAV_READ_BUFFER,
498        held = stickyNavActive[panelIndex],
499        handedOver = 0, // sections that have scrolled far enough out to pass the highlight on
500        current = 0, // last section whose heading is above the read line, i.e. the one being read
501        firstOnScreen = -1, // earliest heading currently on screen
502        index;
503
504      if (!$links.length) {
505        return;
506      }
507
508      $links.each(function (i) {
509        var item = reliasStickyNavItem($(this));
510        if (!item.$section.length) {
511          return;
512        }
513        var sectionRect = item.$section[0].getBoundingClientRect(),
514          headingTop = item.$heading.length ? item.$heading[0].getBoundingClientRect().top : sectionRect.top;
515
516        if (sectionRect.top + sectionRect.height * STICKY_NAV_SCROLLED_OUT <= readLine) {
517          handedOver++;
518        }
519        if (headingTop <= readLine) {
520          current = i;
521        }
522        if (firstOnScreen < 0 && headingTop >= 0 && headingTop <= viewportHeight) {
523          firstOnScreen = i;
524        }
525      });
526
527      if (stickyNavDirection === "up") {
528        // Coming back up, hand the highlight back a step as each earlier
529        // heading comes on screen. Releasing it the moment the lower heading
530        // drops past the read line instead would light up a section while
531        // only a sliver of its bottom shows. With no heading on screen we are
532        // mid-way through a long section, so that section keeps it.
533        index = firstOnScreen >= 0 ? firstOnScreen : current;
534        if (typeof held === "number") {
535          index = Math.min(index, held); // scrolling up never moves the highlight forward
536        }
537      } else {
538        // Going down, a section keeps the highlight until STICKY_NAV_SCROLLED_OUT
539        // of it has passed the read line. Section extents are in document
540        // order, so the ones handed over are always a leading run - their
541        // count is the section now being read.
542        index = Math.min(handedOver, $links.length - 1);
543      }
544
545      if (atPageBottom) {
546        index = $links.length - 1; // a short final section never reaches the read line
547      }
548      if (index < 0) {
549        index = 0; // above the panel - keep the first item marked so the collapsed mobile nav has one
550      }
551
552      stickyNavActive[panelIndex] = index;
553      $links.removeClass("active");
554      $links.eq(index).addClass("active");
555    });
556  }
557
558  // Scroll a clicked sticky nav item's heading to just below the fixed chrome,
559  // so the landing position agrees with where the spy reads the active item
560  function reliasStickyNavLinks() {
561    $(".panel-sticky-nav .sticky-nav")
562      .find(".nav-link[href^='#']")
563      .on("click", function (event) {
564        var $link = $(this),
565          $heading = reliasStickyNavItem($link).$heading;
566
567        if (!$heading.length) {
568          return;
569        }
570        event.preventDefault();
571
572        var $nav = $link.closest(".panel-sticky-nav").find(".sticky-nav nav"),
573          top = $heading.offset().top - reliasStickyNavChrome($nav, true) - STICKY_NAV_CLICK_GAP;
574
575        $("html, body")
576          .stop(true)
577          .animate({ scrollTop: Math.max(top, 0) }, 600, "swing", reliasStickyNavSpy);
578      });
579  }
580
581  //----------------------------------------------------------
582  // SCROLLSPY - Adds .active class to nav-items as page scrolls
583  //----------------------------------------------------------
584  if ($(".sidebar-menu").length) {
585    // Legacy 2019 topic template sidebar only - the sticky-nav panel is
586    // handled by reliasStickyNavSpy() above
587    $("body").scrollspy({ target: ".sidebar-menu", offset: 1 });
588  }
589
590  //----------------------------------------------------------
591  // TABLE HEIGHTS - adjusts heights of parallel rows to match each other in Pricing Tables panel
592  //----------------------------------------------------------
593  function reliasTableHeights() {
594    if ($(".panel.panel-pricing-tables.adjusted .competitors-layout").length) {
595      let $columns = $(".competitors-layout .table-column");
596      let firstColumnRows = [];
597      $columns.each(function (index) {
598        let $rows = $(this).find(".column-row");
599        $rows.each(function (index2) {
600          // Get heights of first column rows
601          if (index == 0) {
602            firstColumnRows.push($(this).outerHeight());
603            // Then match heights against respective columns in subsequent rows (index != 0)
604          } else if ($(this).attr("id") == "column-row-" + index2 + "-" + index) {
605            let targetHeight = firstColumnRows[index2];
606            let padding = (targetHeight - 24) / 2;
607            $(this).css("padding", padding);
608          }
609        });
610      });
611    }
612  }
613
614  //----------------------------------------------------------
615  // ACCORDIONS -
616  // (commented out) Opens the first collapsed item for each accordion panel
617  // If radio button options are used, provides functionality for checking on and off
618  //----------------------------------------------------------
619  function reliasAccordion() {
620    if ($(".panel-accordion").length) {
621      // open first accordion by default on page load, only if an image is present
622      $(".panel-accordion").each(function () {
623        var $this = $(this);
624        $this.find(".card").each(function (i) {
625          $this; // parent li
626          this; // child li
627          if (i === 0 && $(this).find(".accordion-img")) {
628            // Will be done to first element.
629            $(this).find(".collapse").collapse("show");
630          }
631        });
632        // check first radio button, if it exists
633        if ($this.find(".form-control input").length) {
634          $this.find(".form-control input").each(function (a) {
635            if (a === 0) {
636              $(this).prop("checked", true);
637            }
638          });
639        }
640      });
641      $(".form-control input").each(function () {
642        $(this).on("click", function () {
643          if (!$(this).hasClass("collapsed")) {
644            $(this).prop("checked", false);
645          }
646        });
647      });
648
649      if (vpWidth >= phablet) {
650        // dynamic image functionality
651        // changes image in a set container as cells are opened/closed
652        let $accordions = $(".panel-accordion.external-images .accordion");
653        $accordions.each(function () {
654          let $cards = $(".card .accordion-btn");
655          let $images = $(".accordion-outer-img");
656          $cards.each(function (index) {
657            $(this).on("click", function () {
658              $images.addClass("d-none");
659              $images.eq(index).toggleClass("d-none");
660            });
661          });
662        });
663      }
664    }
665  }
666
667  //-----------------------------------------------------------------------
668  // QUOTE BAR -
669  // Resizes background image height to fit in case quote body is too long
670  //-----------------------------------------------------------------------
671  function reliasQuoteBarResizing() {
672    let $quoteBars = $(".panel.panel-quote-bar .panel-container.background-image");
673    if ($quoteBars.length) {
674      $quoteBars.each(function () {
675        let $panelContainer = $(this);
676        let $quoteBody = $panelContainer.find(".quote-body");
677        let $quoteHeight = $quoteBody.height() + 144;
678        $panelContainer.css({ height: $quoteHeight + 144 });
679      });
680    }
681  }
682
683  // Also adjust whenever page is resized
684  $(window).on("resize", function () {
685    reliasQuoteBarResizing();
686  });
687
688  //-----------------------------------------------------------------------
689  // QUOTE BAR VIEWPORT OBSERVER -
690  // Triggers reliasTableHeights() when quote bar enters viewport
691  //-----------------------------------------------------------------------
692  function reliasQuoteBarViewportObserver() {
693    let $quoteBars = $(".panel.panel-quote-bar .panel-container.background-image");
694
695    if ($quoteBars.length && "IntersectionObserver" in window) {
696      const observerOptions = {
697        root: null,
698        rootMargin: "0px",
699        threshold: 0.1, // Trigger when 10% of element is visible
700      };
701
702      const observer = new IntersectionObserver(function (entries, observer) {
703        entries.forEach(function (entry) {
704          if (entry.isIntersecting) {
705            // Element is in viewport - adjust heights
706            reliasQuoteBarResizing();
707
708            // Unobserve after first trigger to avoid repeated calls
709            observer.unobserve(entry.target);
710          }
711        });
712      }, observerOptions);
713
714      // Observe each quote bar element
715      $quoteBars.each(function () {
716        observer.observe(this);
717      });
718    } else if ($quoteBars.length) {
719      // Fallback for browsers without IntersectionObserver support
720      $(window).on("scroll", function () {
721        $quoteBars.each(function () {
722          let $this = $(this);
723
724          // Skip if already processed
725          if ($this.data("quote-bar-processed")) {
726            return;
727          }
728
729          let elementTop = $this.offset().top;
730          let elementBottom = elementTop + $this.outerHeight();
731          let viewportTop = $(window).scrollTop();
732          let viewportBottom = viewportTop + $(window).height();
733
734          if (elementBottom > viewportTop && elementTop < viewportBottom) {
735            reliasQuoteBarResizing();
736
737            // Mark as processed to avoid repeated calls
738            $this.data("quote-bar-processed", true);
739          }
740        });
741      });
742    }
743  }
744
745  function reliasFlickityResize() {
746    // Temporary Fix: Trigger a window resize to fix the viewport of Flickity Quote Carousel
747    if ($(".panel-quote-carousel").length) {
748      setTimeout(function () {
749        // $(window).trigger('resize');
750        $(".flickity-carousel").flickity("resize");
751      }, 1000);
752    }
753  }
754
755  // Reload page if certain panels are on screen, so that resizing doesn't cause issues
756  $(window).resize(function () {
757    if ($(".panel.panel-sticky-nav nav").hasClass("stick")) {
758      location.reload();
759    }
760  });
761
762  function reliasModals() {
763    let $modalLink = $("a.relias-modal");
764    if ($modalLink.length) {
765      // On click
766      $modalLink.on("click", function (e) {
767        e.preventDefault();
768        let $target = $(this).data("val");
769        $($target).modal("show");
770      });
771
772      // Remove focus
773      $modalLink.on("hidden.bs.modal", function () {
774        let $target = "#" + $(this).data("val");
775        $target.blur();
776      });
777    }
778  }
779
780  function reliasBootstrapMenu() {
781    // jQuery objects
782    // desktop
783    let $mainWrapper = $(".main-wrapper-inn"),
784      $reliasNav = $(".relias-nav-container"),
785      $mainMenu = $(".relias-desktop-menu"),
786      $mainItems = $(".relias-desktop-menu .menu-item-has-children"),
787      $mainItemsLinks = $(".relias-desktop-menu > li > a"),
788      $mainItemsDropdowns = $(".relias-desktop-menu .menu-item-has-children");
789    // mobile
790    let $mobileMenuParent = $(".relias-nav-buttons"), // Bootstrap adds .show to parent, not button
791      $mobileButton = $("#reliasMobileButton"),
792      $mobileMenu = $(".relias-mobile-menu.dropdown-menu"),
793      $mobileItems = $(".relias-mobile-menu .menu-item-has-children .dropdown-toggle"),
794      $subMenus = $(".relias-mobile-menu .menu-item-has-children a.dropdown-toggle"),
795      $mobileDemoButton = $(".mobile-demo-button");
796
797    // box shadow toggling
798    $mainItemsDropdowns.each(function () {
799      $(this).on("shown.bs.dropdown", function () {
800        $reliasNav.addClass("hide-before");
801      });
802      $(this).on("hidden.bs.dropdown", function () {
803        $reliasNav.removeClass("hide-before");
804      });
805    });
806
807    // hamburger button functionality
808    if ($mobileButton) {
809      $mobileButton.on("click", function () {
810        $(this).toggleClass("is-active");
811        if (!$(this).hasClass("is-active")) {
812          // hide back shade
813          $(".back_shad").fadeOut(300);
814          // close all 2nd level submenus
815          $(".dropdown-menu.mobile-titled").each(function () {
816            $(this).removeClass("show");
817          });
818        } else {
819          // show back shade
820          $(".back_shad").fadeIn(300);
821        }
822      });
823      $mobileMenu.parent().on("shown.bs.dropdown", function () {
824        $mobileDemoButton.removeClass("d-none");
825      });
826      $mobileMenu.parent().on("hidden.bs.dropdown", function () {
827        $mobileDemoButton.addClass("d-none");
828      });
829    }
830
831    // add overflow-y scroll for dropdown menu when submenus are open (not needed at first level)
832    $subMenus.parent().each(function () {
833      $(this).children("a").removeAttr("href"); // remove href so that FacetWP doesn't trigger a refresh due to hash
834      $(this).on("click", function () {
835        $(this).find(".mobile-titled").trigger("appear");
836      });
837    });
838    $subMenus.siblings().on("appear", function () {
839      if ($(this).hasClass("show")) {
840        $mobileMenu.css("overflow-y", "scroll");
841        console.log("show!");
842      } else {
843        $mobileMenu.css("overflow-y", "hidden");
844        console.log("hide!");
845      }
846    });
847
848    // keep menu and submenus from closing on clicks other than links
849    $mobileMenu.on("click", function (event) {
850      event.stopPropagation();
851      //event.preventDefault();
852    });
853
854    // submenu functionality
855    $mobileItems.each(function (index) {
856      // bootstrap default event propagation override (keeps menu from closing on menu item click)
857      $(this)
858        .on("click")
859        .click(function (event) {
860          //event.stopPropagation();
861        });
862      // hide/show submenu content
863      $(this).on("click", function (index) {
864        $(this).siblings(".dropdown-menu").toggleClass("show");
865      });
866      // create submenu title buttons
867      let $title = $(this).find('p[itemprop="name"]').html();
868      if (!$(this).siblings(".dropdown-menu").find(".title").length) {
869        $(this)
870          .siblings(".dropdown-menu")
871          .prepend('<div id="button-' + $mobileItems.index(this) + '" class="title"><span>v</span>' + $title + "</div>");
872      }
873      // create click handler functionality for submenu title buttons
874      let $buttons = $("#button-" + $mobileItems.index(this));
875      $buttons.each(function () {
876        $(this).on("click", function () {
877          $(this).parent().removeClass("show");
878        });
879      });
880    });
881
882    // adjust top value for each third-level submenu on mobile so that they all appear in the same location when opened
883    let $submenus = $(".dropdown-menu.mobile-titled");
884    let top = 0;
885    $submenus.each(function () {
886      $(this).css("top", top);
887      top = top - 62;
888    });
889
890    // reusable function that resets all menu elements to closed/inactive states
891    function menuReset() {
892      // check if main menu is visible before resetting
893      if ($mainMenu) {
894        $mainItems.each(function () {
895          if ($(this).hasClass("show")) {
896            $(this).removeClass("show");
897            $(this).find(".dropdown-menu").removeClass("show");
898          }
899        });
900      }
901      // reset aria-expanded=true states
902      $mainItemsLinks.each(function () {
903        $(this).attr("aria-expanded", "false");
904      });
905      // check if mobile menu is visible before resetting
906      if ($mobileButton) {
907        // reset visibility
908        mobileMenuReset();
909        // reset hamburger button and submenu states
910        $mobileMenuParent.on("shown.bs.dropdown", function () {
911          $mobileButton.addClass("is-active");
912        });
913        // reset submenu and description states
914        $mobileMenuParent.on("hidden.bs.dropdown", function () {
915          mobileMenuReset();
916        });
917      }
918    }
919
920    function mobileMenuReset() {
921      $mobileMenuParent.removeClass("show");
922      $mobileMenu.removeClass("show");
923      $mobileButton.removeClass("is-active");
924      $mobileDemoButton.addClass("d-none");
925      $subMenus.each(function () {
926        $(this).removeClass("show");
927      });
928      $(".dropdown-menu.mobile-titled").each(function () {
929        $(this).removeClass("show");
930      });
931      $mobileItems.each(function () {
932        $(this).siblings(".submenu-description").removeClass("hide");
933        $(this).find(".plus").removeClass("rotate");
934      });
935      $(".back_shad").fadeOut(300);
936    }
937
938    // reset objects on window resize
939    $(window).resize(function () {
940      menuReset();
941      mobileMenuReset();
942      // reset search box, if open
943      if ($(".search-text").hasClass("js-active")) {
944        $(".search-text").removeClass("js-active");
945        $(".swiftype-searchpage-form").removeClass("js-active");
946      }
947    });
948  }
949
950  // Handle iframe postmessages and reset their heights to account for progressive profiling
951  function reliasResizeIframes() {
952    let $demoIframe = $("#demoForm");
953    if ($("#demoForm").length) {
954      let height;
955      window.addEventListener("message", function (event) {
956        height = event.data;
957        $demoIframe.css({ height: height });
958      });
959    }
960  }
961
962  // Add href attributes to FacetWP pagination links for SEO value
963  function reliasFacetWPPaginationHrefs() {
964    let $pager = $(".facetwp-pager");
965    if ($pager) {
966      document.addEventListener("facetwp-loaded", function () {
967        let $pages = $(".facetwp-page");
968        $pages.on("click", function (event) {
969          event.preventDefault();
970        });
971        $pages.each(function () {
972          if (!$(this).hasClass("dots")) {
973            let $url = window.location.href;
974            let $dataPage = $(this).attr("data-page");
975            // If not on page 1
976            if ($url.includes("paged")) {
977              // if leading back to page 1, add no "?_param="
978              if ($dataPage == 1) {
979                $url = location.protocol + "//" + location.host + location.pathname;
980                // otherwise, add the param and number
981              } else {
982                $url = location.protocol + "//" + location.host + location.pathname + "?_paged=" + $dataPage;
983              }
984              $(this).attr("href", $url);
985              // If page 1, add '?_paged=' param to non-first pages
986            } else {
987              if ($(this).hasClass("first")) {
988                $(this).attr("href", $url);
989              } else {
990                let $pagedUrl = $url + "?_paged=" + $dataPage;
991                $(this).attr("href", $pagedUrl);
992              }
993            }
994          }
995        });
996      });
997    }
998  }
999
1000  // On document ready
1001  $(document).ready(function () {
1002    reliasBootstrapMenu();
1003    reliasBrowserType();
1004    reliasSVG();
1005    reliasAnchorLinkSmoothScroll();
1006    reliasFlickityResize();
1007    reliasModals();
1008    reliasAccordion();
1009    reliasFacetWPFilters();
1010    reliasTableHeights();
1011    reliasBootstrapTabs();
1012    reliasQuoteBarResizing();
1013    reliasFacetWPPaginationHrefs();
1014    reliasResizeIframes();
1015    reliasQuoteBarViewportObserver(); // Initialize viewport observer for quote bars;
1016    reliasStickyNavLinks();
1017    reliasStickyNavSpy();
1018  });
1019
1020  // On window scroll
1021  $(window).scroll(function () {
1022    reliasStickyNavSpy(); // before reliasStickyNav() - the mobile dropdown reads .active
1023    reliasStickyNav();
1024  });
1025
1026  // Re-measure once late-loading content has settled, and on resize
1027  $(window).on("load resize", function () {
1028    reliasStickyNavSpy();
1029  });
1030
1031  // Load anchors at offset from top nav
1032  if (window.location.hash) {
1033    // set scroll position to 0
1034    scroll(0, 0);
1035    // smooth scroll to the anchor id
1036    setTimeout(() => {
1037      $("html, body").animate(
1038        {
1039          scrollTop: $(window.location.hash + "-hash").offset().top - 90,
1040        },
1041        1000,
1042        "swing",
1043      );
1044    }, 2000);
1045  }
1046})(jQuery);

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.