PageSourceSearch

https://www.bracco.com/themes/custom/frankly_theme/js/script.js?tlvc40

js bracco.com collected 2026-09-24 19:05:42 UTC 100,464 bytes, 3,116 lines download raw bytes

1(function ($) {
2  $(document).ready(function () {
3    $(".location-settings").click(function (e) {
4      $(".location-switcher").toggle();
5      e.preventDefault();
6    });
7    $(".toggle-timeline-event").click(function (e) {
8      e.preventDefault();
9      $(".toggle-timeline-event").removeClass("active");
10      $(this).addClass("active");
11      $(".timeline-event").hide();
12      $("[data-year-id=" + $(this).data("year-id") + "]").show();
13    });
14
15    // -------------------
16    // HERO CAROUSEL
17    // -------------------
18
19    if ($(".hero-carousel").length) {
20      $(".hero-carousel>.hero-carousel-slides").slick({
21        dots: true,
22        infinite: true,
23        speed: 600,
24        slidesToShow: 1,
25        slidesToScroll: 1,
26        arrows: true,
27        autoplay: true,
28      });
29    }
30
31    // -------------------
32    // SIMPLE CAROUSEL
33    // -------------------
34
35    if ($(".carousel").length) {
36      // Carousel layout if less than 2 items
37      $(".carousel>.slides-length-small").slick({
38        dots: true,
39        arrows: true,
40        infinite: true,
41        speed: 600,
42        slidesToShow: 1,
43        slidesToScroll: 1,
44        centerMode: false,
45        initialSlide: 0,
46        autoplay: true,
47        responsive: [
48          {
49            breakpoint: 768,
50            settings: {
51              arrows: false,
52            },
53          },
54        ],
55      });
56
57      // Carousel layout if greater than 2 but less than 4 items
58      $(".carousel>.slides-length-medium").slick({
59        dots: true,
60        arrows: true,
61        infinite: true,
62        speed: 600,
63        slidesToShow: 2,
64        slidesToScroll: 1,
65        centerMode: false,
66        initialSlide: 0,
67        autoplay: true,
68        responsive: [
69          {
70            breakpoint: 768,
71            settings: {
72              slidesToShow: 1,
73              slidesToScroll: 1,
74              arrows: false,
75            },
76          },
77        ],
78      });
79
80      // Carousel layout if greater than 4 items
81      $(".carousel>.slides-length-large").slick({
82        dots: true,
83        arrows: true,
84        infinite: true,
85        speed: 600,
86        slidesToShow: 4,
87        slidesToScroll: 1,
88        centerMode: false,
89        initialSlide: 0,
90        autoplay: true,
91        responsive: [
92          {
93            breakpoint: 1280,
94            settings: {
95              slidesToShow: 3,
96              slidesToScroll: 1,
97            },
98          },
99          {
100            breakpoint: 850,
101            settings: {
102              slidesToShow: 2,
103              slidesToScroll: 1,
104            },
105          },
106          {
107            breakpoint: 768,
108            settings: {
109              slidesToShow: 1,
110              slidesToScroll: 1,
111              arrows: false,
112            },
113          },
114        ],
115      });
116    }
117
118    // -------------------
119    // CARD CAROUSEL
120    // -------------------
121
122    if ($(".card-carousel").length) {
123      $(".card-carousel .card-carousel__slides").slick({
124        dots: true,
125        arrows: true,
126        infinite: true,
127        speed: 500,
128        variableWidth: true,
129        slidesToScroll: 1,
130        centerMode: false,
131        initialSlide: 0,
132        autoplay: false,
133        responsive: [
134          {
135            breakpoint: 768,
136            settings: {
137              variableWidth: true,
138              slidesToScroll: 1,
139              arrows: false,
140              infinite: true,
141            },
142          },
143        ],
144      });
145    }
146
147    // -------------------
148    // DISCOVER SLIDER
149    // -------------------
150    if ($(".discover").length) {
151      // Carousel layout if less than 2 items
152      $(".discover-cards").slick({
153        dots: true,
154        arrows: true,
155        infinite: true,
156        speed: 300,
157        slidesToShow: 3,
158        slidesToScroll: 1,
159        centerMode: false,
160        initialSlide: 0,
161        autoplay: false,
162        responsive: [
163          {
164            breakpoint: 1023,
165            settings: {
166              slidesToShow: 2,
167              slidesToScroll: 1,
168              arrows: false,
169            },
170          },
171          {
172            breakpoint: 768,
173            settings: {
174              slidesToShow: 1,
175              arrows: false,
176            },
177          },
178        ],
179      });
180    }
181
182    let $tags = $(".discover-tag-toggle");
183    let discoverParent = document.querySelector(".discover-carousel");
184
185    $(".discover-tag-toggle").on("click", function () {
186      let isActive = $(this).attr("data-color") === "white";
187
188      $tags.not($(this)).attr("data-color", "white");
189      $(this).attr("data-color", isActive ? "green" : "white");
190
191      let currentFilter = isActive ? $(this).data("tag") : null;
192
193      if (currentFilter) {
194        discoverParent.classList.add("show");
195        $(".discover-cards").slick("slickUnfilter");
196        $(".discover-cards").slick("slickFilter", function () {
197          let filterCard = false;
198          let cardTags = $(this).data("tag");
199
200          if (cardTags.includes(`${currentFilter}`)) {
201            filterCard = true;
202          }
203
204          return filterCard;
205        });
206      } else {
207        discoverParent.classList.remove("show");
208      }
209    });
210
211    // -------------------
212    // ANIMATED PARALLEL
213    // -------------------
214    if ($(".animated-parallel").length) {
215      function animatedParallel() {
216        const parent = document.querySelectorAll(".animated-parallel");
217        const arr = [...parent];
218
219        arr.forEach((el) => {
220          const dataAttr = el.getAttribute("data-scroll-trigger");
221
222          if (dataAttr == "true") {
223            return createAnimatedParallelObserver(el);
224          } else {
225            const button = el.querySelector(".block-heading button");
226            if (button) {
227              button.addEventListener("click", () => {
228                el.classList.add("active");
229              });
230            }
231          }
232
233          // Active animation if element is empty and intersecting
234          function createAnimatedParallelObserver(element) {
235            let options = { threshold: 1 };
236            const aniParObserver = new IntersectionObserver(
237              handleAnimatedParallelObserver,
238              options,
239            );
240
241            aniParObserver.observe(element);
242          }
243
244          function handleAnimatedParallelObserver(entries, options) {
245            entries.forEach((entry) => {
246              if (entry.isIntersecting) {
247                entry.target.classList.add("active");
248              }
249            });
250          }
251        });
252      }
253      animatedParallel();
254    }
255
256    // -------------------
257    // INFO BANNER - COUNTER
258    // -------------------
259    if ($(".info-banner").length) {
260      function infoBanner() {
261        // Animate numbers
262        function createInfoBannerObserver() {
263          let options = {
264            threshold: 1,
265          };
266
267          const infoBannerObserver = new IntersectionObserver(
268            handleInfoBannerIntersect,
269            options,
270          );
271          const bannerContainers = document.querySelectorAll(".info-banner");
272          let arr = Array.from(bannerContainers);
273
274          // Set observer to observe each banner
275          arr.forEach((banner) => {
276            infoBannerObserver.observe(banner);
277          });
278        }
279        createInfoBannerObserver();
280
281        function handleInfoBannerIntersect(entries, options) {
282          let speed = 10;
283          entries.forEach((entry) => {
284            if (entry.isIntersecting) {
285              const counterValues = entry.target.querySelectorAll(
286                ".info-banner-items .info-banner-content-item .counter span",
287              );
288
289              counterValues.forEach((counterValue) => {
290                setMinWidth(counterValue.closest(".info-banner-content-item"));
291                animateCountUp(counterValue);
292              });
293
294              function setMinWidth(el) {
295                el.style.minWidth = `${el.offsetWidth}px`;
296              }
297
298              function animateCountUp(el) {
299                let counterValue = el.getAttribute("data-count-to");
300                let isCommaNum = false;
301                let countFrom = 0;
302                let counterFixed = 0;
303                let decimals = 0;
304
305                if (counterValue.includes(",")) {
306                  // comma
307                  let numberLength = counterValue.length - 1;
308                  let indexDotComma = counterValue.indexOf(",");
309                  decimals = numberLength - indexDotComma;
310
311                  counterValue = counterValue.replace(",", ".");
312
313                  isCommaNum = true;
314                } else if (counterValue.includes(".")) {
315                  // dot
316                  let numberLength = counterValue.length - 1;
317                  let indexDotComma = counterValue.indexOf(".");
318                  decimals = numberLength - indexDotComma;
319                }
320
321                const COUNTER_VALUE_NUM = parseInt(counterValue);
322                let plusVariabel = COUNTER_VALUE_NUM / 100;
323
324                let interval = setInterval(countingStraight, speed);
325
326                function countingStraight() {
327                  if (
328                    counterFixed >= COUNTER_VALUE_NUM ||
329                    countFrom >= COUNTER_VALUE_NUM
330                  ) {
331                    clearInterval(interval);
332
333                    if (isCommaNum) {
334                      counterValue = counterValue.replace(".", ",");
335                      el.innerHTML = counterValue;
336                    } else {
337                      el.innerHTML = counterValue;
338                    }
339                  } else {
340                    countFrom = countFrom + plusVariabel;
341                    if (decimals >= 1) {
342                      counterFixed = countFrom.toFixed(decimals);
343                      el.innerHTML = counterFixed;
344                    } else {
345                      el.innerHTML = Math.ceil(countFrom);
346                    }
347                  }
348                }
349              }
350            }
351          });
352        }
353      }
354      infoBanner();
355    }
356
357    // -----------------------------
358    // RESPONSIVE TABLES
359    // -----------------------------
360    if ($(".text-formatted table").length) {
361      function mobileTables() {
362        //	------------------------------------------------------------
363        // 	Variables
364        //  - list of dom tables on page
365        //  - mobile breakpoint where table display could change
366        //	------------------------------------------------------------
367        const tableList = document.querySelectorAll(".text-formatted table");
368        const breakpointMobile = 480; // 30rem
369
370        //	------------------------------------------------------------
371        // 	Initialize tables
372        //	------------------------------------------------------------
373        tableList.forEach((table) => {
374          createTableWrapper(table);
375          createTableRoles(table);
376          createTableTitles(table);
377          watchTableScroll(table);
378        });
379
380        //	------------------------------------------------------------
381        // 	Create table wrapper to handle overflow scroll
382        //	sticky headers etc.
383        //	------------------------------------------------------------
384        function createTableWrapper(table) {
385          const wrapper = document.createElement("div");
386          wrapper.classList.add("table-wrapper");
387          table.insertAdjacentElement("beforebegin", wrapper);
388          wrapper.append(table);
389        }
390
391        //	------------------------------------------------------------
392        // 	Add table headings as data attributes to allow
393        // 	switching to grid/block layout
394        //	------------------------------------------------------------
395        function createTableTitles(table) {
396          // Early exit
397          const headerRows = table.querySelectorAll("thead tr");
398          const hasFooter = table.querySelector("tfoot");
399          const hasBody = table.querySelector("tbody");
400
401          if (headerRows.length !== 1 || hasFooter || !hasBody) return;
402
403          // Can have mobile layout
404          table.classList.add("can-be-mobile");
405
406          const headerList = table.querySelectorAll("thead th");
407          const rowList = [...table.querySelectorAll("tbody tr")];
408          const tableList = rowList.map((row) => [
409            ...row.querySelectorAll("td, th"),
410          ]);
411
412          // Exit if we have no table header cells
413          if (headerList.length === 0) return;
414
415          // In order to match spanned columns and rows the table is "normalized"
416          // by replacing missing cells with a clone of the original cell
417          // this is done in two steps - first replacing colspans then rowspans
418          // Since the original cells are linked to the dom we can then match
419          // cells by column index and apply data-title attributes
420
421          // Fill in missing cells based on colspan
422          tableList.forEach((rowList, rowIndex, tableList) => {
423            rowList.forEach((col, colIndex) => {
424              // number of colspans
425              const colSpan = parseInt(col.getAttribute("colspan")) || 1;
426              // create a clone of the table cell
427              const clone = col.cloneNode(true);
428              // remove colspan attribute on clone
429              clone.removeAttribute("colspan");
430              // fill out the blanks
431              for (let c = 1; c < colSpan; ++c) {
432                tableList[rowIndex].splice(colIndex + c, 0, clone);
433              }
434            });
435          });
436
437          // Fill in missing cells based on rowspan
438          tableList.forEach((rowList, rowIndex, tableList) => {
439            rowList.forEach((col, colIndex) => {
440              // number of rowspans
441              const rowSpan = parseInt(col.getAttribute("rowspan")) || 1;
442              // create a clone of the table cell
443              const clone = col.cloneNode(true);
444              // remove colspan attribute on clone
445              clone.removeAttribute("rowspan");
446              // fill out the blanks
447              for (let r = 1; r < rowSpan; ++r) {
448                tableList[rowIndex + r].splice(colIndex, 0, clone);
449              }
450            });
451          });
452
453          // Add matching header text as data attribute
454          tableList.forEach((rowList, rowIndex, tableList) => {
455            rowList.forEach((col, colIndex) => {
456              if (headerList[colIndex].innerHTML === "&nbsp;") {
457                headerList[colIndex].innerHTML = "";
458              }
459
460              const innerHTMLToString = headerList[colIndex].innerHTML
461                .toString()
462                .replace(/<[^>]*>/g, " ")
463                .replace(/\s{2,}/g, " ")
464                .trim();
465
466              col.dataset.title = innerHTMLToString;
467            });
468          });
469        }
470
471        //	------------------------------------------------------------
472        //	Add ARIA roles to allow changing display type without
473        //	messing up accessibility
474        //	------------------------------------------------------------
475        function createTableRoles(table) {
476          const getNodes = (sel) => table.querySelectorAll(sel);
477          const setRole = (role) => (el) => el.setAttribute("role", role);
478
479          setRole("table")(table);
480          getNodes("caption").forEach(setRole("caption"));
481          getNodes("thead, tbody, tfoot").forEach(setRole("rowgroup"));
482          getNodes("tr").forEach(setRole("row"));
483          getNodes("td").forEach(setRole("cell"));
484          getNodes("th").forEach(setRole("columnheader"));
485          getNodes("th[scope=row]").forEach(setRole("rowheader"));
486        }
487
488        //	------------------------------------------------------------
489        //	Detect table scroll when the table exceeds the width
490        // 	of the wrapping container
491        //	------------------------------------------------------------
492        function watchTableScroll(table) {
493          const root = table.parentNode;
494          let breakpointLocal;
495
496          const observer = new ResizeObserver((entries) => {
497            entries.forEach((entry) => {
498              // prevent ResizeObserver - loop limit exceeded
499              requestAnimationFrame(() => {
500                // detect scroll bý comparing scroll and offset width
501                const hasScroll = root.scrollWidth > root.offsetWidth;
502
503                // if scrolling is detected add scroll class and save
504                // the current window width as a local breakpoint
505                // the local breakpoint is used together with the
506                // mobile breakpoibt to avoid re-style loops
507                if (hasScroll) {
508                  breakpointLocal = innerWidth;
509                  root.classList.add("has-scroll");
510                } else if (
511                  innerWidth > Math.min(breakpointLocal, breakpointMobile)
512                ) {
513                  root.classList.remove("has-scroll");
514                }
515              });
516            });
517          });
518          observer.observe(root);
519        }
520      }
521      mobileTables();
522    }
523
524    // -----------------------------
525    // INTERACTIVE TABS (- Product)
526    // -----------------------------
527    if ($(".interactive-tabs, .interactive-tabs-prod").length) {
528      function interactiveTabs() {
529        const MOBILE_TABS_BREAKPOINT = 1024;
530
531        document
532          .querySelectorAll(".interactive-tabs, .interactive-tabs-prod")
533          .forEach(($parent) => {
534            // initializing
535            const $tablist = $parent.querySelector('[role="tablist"]');
536            const $$tabs = [...$tablist.querySelectorAll("button")];
537            const $$tabpanels = [
538              ...$parent.querySelectorAll('[role="tabpanel"]'),
539            ];
540            const $firstTab = $$tabs[0];
541            const $lastTab = $$tabs[$$tabs.length - 1];
542            let $currentTab = $firstTab;
543
544            // mode for tabs or accordion depending on size, tabs is always horizontal
545            let isTabs = true;
546
547            setActiveTab($firstTab, false, false);
548
549            function getTabForToken(token) {
550              if (!token) {
551                return null;
552              }
553
554              const normalizedToken = token.trim();
555              if (!normalizedToken) {
556                return null;
557              }
558
559              if (/^\d+$/.test(normalizedToken)) {
560                const tabIndex = Number.parseInt(normalizedToken, 10) - 1;
561                if (tabIndex >= 0 && tabIndex < $$tabs.length) {
562                  return $$tabs[tabIndex];
563                }
564              }
565
566              let matchingTab = $$tabs.find(
567                (tab) =>
568                  tab.id === normalizedToken ||
569                  tab.getAttribute("aria-controls") === normalizedToken,
570              );
571
572              if (matchingTab) {
573                return matchingTab;
574              }
575
576              const matchingPanel = $$tabpanels.find(
577                (panel) => panel.id === normalizedToken,
578              );
579
580              if (matchingPanel) {
581                matchingTab = $$tabs.find(
582                  (tab) =>
583                    tab.getAttribute("aria-controls") === matchingPanel.id,
584                );
585              }
586
587              return matchingTab || null;
588            }
589
590            function activateTabFromUrl() {
591              const url = new URL(window.location.href);
592              const urlTabToken =
593                url.searchParams.get("tab") ||
594                url.searchParams.get("tabs") ||
595                url.searchParams.get("panel");
596
597              let tabToActivate = getTabForToken(urlTabToken);
598              const hashToken = decodeURIComponent(url.hash.replace(/^#/, ""));
599              let hashTargetInsidePanel = null;
600
601              if (!tabToActivate && hashToken) {
602                tabToActivate = getTabForToken(hashToken);
603
604                if (!tabToActivate) {
605                  const hashTarget = document.getElementById(hashToken);
606                  if (hashTarget) {
607                    const hashTargetPanel =
608                      hashTarget.closest('[role="tabpanel"]');
609                    if (hashTargetPanel && $parent.contains(hashTargetPanel)) {
610                      tabToActivate = $$tabs.find(
611                        (tab) =>
612                          tab.getAttribute("aria-controls") ===
613                          hashTargetPanel.id,
614                      );
615                      hashTargetInsidePanel = hashTarget;
616                    }
617                  }
618                }
619              }
620
621              if (tabToActivate && tabToActivate !== $currentTab) {
622                setActiveTab(tabToActivate, false, false);
623              }
624
625              if (hashTargetInsidePanel) {
626                requestAnimationFrame(() => {
627                  hashTargetInsidePanel.scrollIntoView({
628                    block: "start",
629                    behavior: "auto",
630                  });
631                });
632              }
633            }
634
635            // functions
636
637            function setMode() {
638              let containerStyle = getComputedStyle($parent);
639
640              let containerWidth =
641                $parent.getBoundingClientRect().width -
642                parseInt(containerStyle.getPropertyValue("padding-left")) -
643                parseInt(containerStyle.getPropertyValue("padding-right"));
644
645              const tabsRequiredWidth = $$tabs.reduce(
646                (totalWidth, tab) => totalWidth + tab.scrollWidth,
647                0,
648              );
649
650              const isMobileViewport =
651                window.innerWidth <= MOBILE_TABS_BREAKPOINT;
652
653              // On mobile we always collapse tabs into the non-horizontal mode.
654              isTabs = !isMobileViewport && tabsRequiredWidth <= containerWidth;
655
656              if (!isTabs) {
657                $parent.classList.add("dropdown-mode");
658                $parent.classList.add("accordion-mode");
659
660                $$tabs.forEach((tab) => (tab.tabIndex = "0"));
661                handleTabsDropdown($currentTab);
662                $tablist.style.removeProperty("--tabs-sticky-top");
663              } else {
664                $parent.classList.remove("dropdown-mode");
665                $parent.classList.remove("accordion-mode");
666              }
667
668              setStickyOffset();
669            }
670            setMode();
671            activateTabFromUrl();
672
673            function setStickyOffset() {
674              if (!isTabs) {
675                return 0;
676              }
677
678              const $header = document.querySelector("header.region-header");
679              let stickyTop = 0;
680
681              if ($header) {
682                const headerStyle = getComputedStyle($header);
683                if (
684                  headerStyle.position === "fixed" ||
685                  headerStyle.position === "sticky"
686                ) {
687                  stickyTop = Math.max(
688                    0,
689                    Math.ceil($header.getBoundingClientRect().bottom),
690                  );
691                }
692              }
693
694              $tablist.style.setProperty("--tabs-sticky-top", `${stickyTop}px`);
695              return stickyTop;
696            }
697
698            function setContainerHeight() {
699              const $tabpanel = $parent.querySelector(
700                `#${$currentTab.getAttribute("aria-controls")}`,
701              );
702              if (!$tabpanel) {
703                return;
704              }
705
706              // Recalculate static chrome height every time to avoid stale values
707              // when tab content changes after async/media render.
708              const parentRect = $parent.getBoundingClientRect();
709              const panelRect = $tabpanel.getBoundingClientRect();
710              const chromeHeight = Math.max(
711                0,
712                parentRect.height - panelRect.height,
713              );
714              const panelHeight = Math.max(
715                panelRect.height,
716                $tabpanel.scrollHeight,
717              );
718
719              $parent.style.setProperty(
720                "--height",
721                chromeHeight + panelHeight + "px",
722              );
723            }
724
725            function scrollToTablistTop() {
726              if (!isTabs) {
727                return;
728              }
729
730              const stickyTop = setStickyOffset();
731              const targetY =
732                $parent.getBoundingClientRect().top +
733                window.scrollY -
734                stickyTop -
735                12;
736
737              window.scrollTo({
738                top: Math.max(0, targetY),
739                behavior: "smooth",
740              });
741            }
742
743            function setActiveTab(
744              $activeTab,
745              willFocus = true,
746              shouldScroll = true,
747            ) {
748              $currentTab.setAttribute("aria-selected", "false");
749              if (isTabs) {
750                $currentTab.tabIndex = "-1";
751              }
752
753              $parent
754                .querySelector(`#${$currentTab.getAttribute("aria-controls")}`)
755                .classList.add("hidden");
756
757              $currentTab = $activeTab;
758              if (willFocus) {
759                $currentTab.focus();
760              }
761              $currentTab.setAttribute("aria-selected", "true");
762              $currentTab.tabIndex = "0";
763              $parent
764                .querySelector(`#${$currentTab.getAttribute("aria-controls")}`)
765                .classList.remove("hidden");
766              setIndicator($currentTab);
767              setContainerHeight($currentTab);
768              requestAnimationFrame(() => setContainerHeight());
769
770              // Some inner components render a bit later (images/carousel/tables).
771              // Re-check once more after transitions to prevent truncated content.
772              setTimeout(() => setContainerHeight(), 320);
773
774              if (
775                $parent
776                  .querySelector(
777                    `#${$currentTab.getAttribute("aria-controls")}`,
778                  )
779                  .querySelector(".text-formatted table")
780              ) {
781                mobileTables();
782              }
783
784              if (!isTabs) {
785                handleTabsDropdown($currentTab);
786                hideTablist();
787              }
788
789              if (shouldScroll) {
790                scrollToTablistTop();
791              }
792            }
793
794            function setActiveToNextTab() {
795              if ($currentTab === $lastTab) {
796                setActiveTab($firstTab);
797              } else {
798                let i = $$tabs.indexOf($currentTab);
799                setActiveTab($$tabs[i + 1]);
800              }
801            }
802
803            function setActiveToPrevTab() {
804              if ($currentTab === $firstTab) {
805                setActiveTab($lastTab);
806              } else {
807                let i = $$tabs.indexOf($currentTab);
808                setActiveTab($$tabs[i - 1]);
809              }
810            }
811
812            function setIndicator() {
813              $tablist.style.setProperty(
814                "--indicator-x",
815                $currentTab.getBoundingClientRect().x -
816                  $tablist.getBoundingClientRect().x +
817                  "px",
818              );
819              $tablist.style.setProperty(
820                "--indicator-width",
821                $currentTab.getBoundingClientRect().width + "px",
822              );
823            }
824
825            const handleResize = debounce(() => {
826              setMode();
827              setStickyOffset();
828              setIndicator();
829              setContainerHeight();
830            });
831
832            function handleClick() {
833              setActiveTab(this);
834            }
835
836            function handleKeydown(e) {
837              let flag = false;
838
839              switch (e.key) {
840                case "ArrowLeft":
841                  setActiveToPrevTab();
842                  flag = true;
843                  break;
844
845                case "ArrowRight":
846                  setActiveToNextTab();
847                  flag = true;
848                  break;
849
850                case "Home":
851                  setActiveTab($firstTab);
852                  flag = true;
853                  break;
854
855                case "End":
856                  setActiveTab($lastTab);
857                  flag = true;
858                  break;
859
860                default:
861                  break;
862              }
863
864              if (flag) {
865                e.stopPropagation();
866                e.preventDefault();
867              }
868            }
869
870            function debounce(func, timeout = 300) {
871              let timer;
872              return (...args) => {
873                clearTimeout(timer);
874                timer = setTimeout(() => {
875                  func.apply(this, args);
876                }, timeout);
877              };
878            }
879
880            // event listeners
881            window.addEventListener("load", handleResize); //resize after images loaded
882            window.addEventListener("resize", handleResize);
883            window.addEventListener("hashchange", activateTabFromUrl);
884
885            $$tabs.forEach(($tab) => {
886              $tab.addEventListener("click", handleClick);
887              $tab.addEventListener("keydown", handleKeydown);
888            });
889
890            function handleTabsDropdown(tab) {
891              if (isTabs) {
892                return;
893              } else {
894                const tabsToggleBtn = $parent.querySelector(".dropdown-toggle");
895                tabsToggleBtn.innerText = tab.innerText;
896
897                tabsToggleBtn.addEventListener("click", hideTablist);
898              }
899            }
900
901            function hideTablist() {
902              $tablist.classList.toggle("dropdown-visible");
903            }
904
905            let hasAccordion = $parent.querySelectorAll(".accordion-list");
906            if (hasAccordion.length) {
907              // Resize observer for height changes in panel
908              const tabsResizeObserver = new ResizeObserver((entries) => {
909                setContainerHeight();
910              });
911              hasAccordion.forEach((accordion) => {
912                accordion
913                  .querySelectorAll('[role="accordionpanel"]')
914                  .forEach((x) => tabsResizeObserver.observe(x));
915              });
916            }
917
918            // Keep active panel height in sync when tab content changes dynamically.
919            if (window.ResizeObserver) {
920              const panelResizeObserver = new ResizeObserver(() => {
921                setContainerHeight();
922              });
923
924              $$tabpanels.forEach((panel) =>
925                panelResizeObserver.observe(panel),
926              );
927            }
928
929            // Fallback for late-loading media that can change panel height.
930            $$tabpanels.forEach((panel) => {
931              panel
932                .querySelectorAll("img, iframe, video")
933                .forEach((mediaElement) => {
934                  mediaElement.addEventListener("load", setContainerHeight);
935                  mediaElement.addEventListener(
936                    "loadedmetadata",
937                    setContainerHeight,
938                  );
939                });
940            });
941          });
942      }
943      interactiveTabs();
944    }
945
946    // -----------------------------
947    // ACCORDION
948    // -----------------------------
949    if ($(".accordion-list").length) {
950      function accordion() {
951        document.querySelectorAll(".accordion-list").forEach((parent) => {
952          // initializing
953          const detailslist = parent.querySelector(".details-wrapper");
954          const summaries = [...detailslist.querySelectorAll("button.summary")];
955
956          let currentPanel = summaries[0];
957          let expanded;
958
959          setActivePanel(currentPanel);
960
961          function setActivePanel(activePanel) {
962            currentPanel = activePanel;
963
964            expanded =
965              currentPanel.getAttribute("aria-expanded") == "true"
966                ? true
967                : false;
968            expanded = !expanded;
969
970            // toggle current panel visibility
971            currentPanel.setAttribute("aria-expanded", expanded);
972            parent
973              .querySelector(`#${currentPanel.getAttribute("aria-controls")}`)
974              .setAttribute("aria-hidden", !expanded);
975
976            if (
977              parent
978                .querySelector(`#${currentPanel.getAttribute("aria-controls")}`)
979                .querySelector(".text-formatted table")
980            ) {
981              setTimeout(() => {
982                mobileTables();
983              }, 400);
984            }
985
986            setPanelHeight(currentPanel, expanded);
987          }
988
989          const handleResize = debounce(() => {
990            setPanelHeight(currentPanel, expanded);
991          });
992
993          function handleClick() {
994            setActivePanel(this);
995          }
996
997          function debounce(func, timeout = 300) {
998            let timer;
999            return (...args) => {
1000              clearTimeout(timer);
1001              timer = setTimeout(() => {
1002                func.apply(this, args);
1003              }, timeout);
1004            };
1005          }
1006
1007          // event listeners
1008          window.addEventListener("load", handleResize); //resize after images loaded
1009          window.addEventListener("resize", handleResize);
1010
1011          summaries.forEach((summary) => {
1012            summary.addEventListener("click", handleClick);
1013          });
1014        });
1015
1016        function setPanelHeight(currentPanel, expanded) {
1017          const content = currentPanel.parentElement.querySelector(
1018            `#${currentPanel.getAttribute("aria-controls")}`,
1019          );
1020
1021          let bh = currentPanel.getBoundingClientRect().height;
1022
1023          if (bh <= 0) {
1024            return;
1025          }
1026
1027          const threshold = 1.25;
1028          let ch = expanded
1029            ? content.getBoundingClientRect().height * threshold
1030            : 0;
1031
1032          let panelHeight =
1033            currentPanel.getBoundingClientRect().height + ch + "px";
1034
1035          currentPanel.parentElement.style.setProperty("--height", panelHeight);
1036        }
1037      }
1038      accordion();
1039    }
1040
1041    // -------------------
1042    // VIDEO BANNERS
1043    // -------------------
1044    if ($("[data-is-video-banner]").length) {
1045      function videoBanners() {
1046        let [...videoBanners] = document.querySelectorAll(
1047          "[data-is-video-banner]",
1048        );
1049
1050        videoBanners.forEach((banner) => {
1051          createVideoBannerObserver(banner);
1052        });
1053
1054        function createVideoBannerObserver(banner) {
1055          let options = { threshold: 0.25 };
1056          const videoBannerObserver = new IntersectionObserver(
1057            handleVideoBannerObserver,
1058            options,
1059          );
1060          videoBannerObserver.observe(banner);
1061        }
1062
1063        function handleVideoBannerObserver(entries, options) {
1064          entries.forEach((entry) => {
1065            let playState = null;
1066            const video = entry.target.querySelector("video");
1067            if (!entry.isIntersecting) {
1068              video.pause();
1069              playState = false;
1070            } else {
1071              video.play();
1072              playState = true;
1073            }
1074          });
1075        }
1076      }
1077      videoBanners();
1078    }
1079
1080    // -------------------
1081    // STORIES SEARCH + FILTER
1082    // -------------------
1083    const createStory = function (wrapper) {
1084      let filters = new Set();
1085      let query = "";
1086
1087      function liveSearch() {
1088        wrapper.querySelectorAll(".story-item").forEach((story) => {
1089          // at least one filter must be on for filtering to happen
1090          if (filters.size && !filters.has(story.dataset.term)) {
1091            return (story.dataset.visible = false);
1092          }
1093
1094          // search multiple elements
1095          const content = [".term", ".type", ".title", ".copy"]
1096            .map((q) => story.querySelector(q).innerText)
1097            .join(" || ")
1098            .toLowerCase();
1099
1100          // look for multiple words, false as soon as one query is missing
1101          for (const q of query.toLowerCase().split(" ")) {
1102            if (!content.includes(q)) {
1103              return (story.dataset.visible = false);
1104            }
1105          }
1106
1107          return (story.dataset.visible = true);
1108        });
1109      }
1110
1111      function toggleFilter(el) {
1112        // reset filter on other elements
1113        let siblings = wrapper.querySelectorAll(".story-tag-toggle");
1114        Array.from(siblings)
1115          .filter((x) => x != el)
1116          .forEach((sibling) => {
1117            sibling.dataset.toggled = "false";
1118            sibling.dataset.color = "white";
1119            filters.delete(sibling.dataset.tag);
1120          });
1121
1122        // filter on single element
1123        if (el.dataset.toggled == "true") {
1124          // turning on to off
1125          el.dataset.toggled = "false";
1126          el.dataset.color = "white";
1127          filters.delete(el.dataset.tag);
1128        } else {
1129          // turning off to on
1130          el.dataset.toggled = "true";
1131          el.dataset.color = "green";
1132          filters.add(el.dataset.tag);
1133        }
1134        liveSearch();
1135      }
1136
1137      wrapper
1138        .querySelectorAll(".story-tag-toggle")
1139        .forEach((el) => el.addEventListener("click", () => toggleFilter(el)));
1140
1141      // on input change; would need a delay?
1142      wrapper.querySelector(".search").addEventListener("input", (e) => {
1143        e.preventDefault();
1144        query = e.target.value;
1145        liveSearch();
1146      });
1147
1148      // on submit
1149      wrapper.querySelector(".search").addEventListener("submit", (e) => {
1150        e.preventDefault();
1151        query = e.target.q.value;
1152        liveSearch();
1153      });
1154    };
1155
1156    document
1157      .querySelectorAll(".stories")
1158      .forEach((wrapper) => createStory(wrapper));
1159
1160    // -------------------
1161    // NEWS HIGHLIGHTS
1162    // -------------------
1163    if ($(".news-highlights-collection").length) {
1164      function newsHighlights() {
1165        let [...newsHighlightsCol] = document.querySelectorAll(
1166          ".news-highlights-collection",
1167        );
1168
1169        newsHighlightsCol.forEach((col) => {
1170          const btn = col.querySelector(".expand-btn");
1171          btn.addEventListener("click", () => {
1172            col.classList.add("expanded");
1173          });
1174        });
1175      }
1176      newsHighlights();
1177    }
1178
1179    // -------------------
1180    // Pardot form
1181    // -------------------
1182    // Extract everything after the matched language code
1183    function getTextAfterLanguage(url) {
1184      //const regex = /\/[a-z]{2}-[a-z]{2}\//i;
1184 // This matches patterns like "/en-us/", "/fr-fr/", etc.
1185      //Specific pattern for 'en-us'
1186      const enUsPattern = /\/en-us\//i; // This matches exactly "/en-us/"
1187      const match = url.match(enUsPattern);
1188      if (match) {
1189        return url.substring(match.index + match[0].length);
1190      }
1191      return "";
1192    }
1193    // Is a product modality page
1194    function isValidPath(url) {
1195      const validProducts = [
1196        "x-ray-ct",
1197        "mri",
1198        "ultrasound",
1199        "nuclear-medicine",
1200        "cardiology",
1201      ];
1202      const extractedText = getTextAfterLanguage(url);
1203      if (validProducts.includes(extractedText)) {
1204        return extractedText; // Return the extracted text if valid
1205      }
1206
1207      return false; // Return false if it's not valid
1208    }
1209    // Autofill slug on Pardot form.
1210    function addProductNameToForm() {
1211      if ($("input[name='product_url']")) {
1212        // var hash = location.hash.replace('#', '');
1213        var locPathname = window.location.pathname;
1214
1215        const urlParams = new URLSearchParams(window.location.search);
1216
1217        if (urlParams.has("product")) {
1218          const product = urlParams.get("product");
1219          $("input[name='product_url']").val(product);
1220          //console.log('product_url',hash);
1221        } else {
1222          // if no hash, try to check for product
1223          let productName = isValidPath(locPathname);
1224          if (productName !== false) {
1225            $("input[name='product_url']").val(productName);
1226            //console.log('product_url',productName);
1227          } else {
1228            $("input[name='product_url']").val("citraclear");
1229            //console.log('product_url',"citraclear");
1230          }
1231        }
1232      }
1233    }
1234    addProductNameToForm();
1235
1236    // -------------------
1237    // WINDOW LOCATION ANCHOR PRESENT
1238    // -------------------
1239    window.addEventListener("hashchange", function () {
1240      addProductNameToForm();
1241    });
1242
1243    // -------------------
1244    // WINDOW LOCATION ANCHOR PRESENT
1245    // -------------------
1246    if (window.location.hash) {
1247      let hash = window.location.hash;
1248      let scrollTop = window.innerWidth < 1023 ? 100 : 147;
1249      $("html, body").animate(
1250        {
1251          scrollTop: $(hash).offset().top - scrollTop,
1252        },
1253        500,
1254      );
1255    }
1256
1257    if ($("#edit-discard-changes-onclick").length) {
1258      $("#edit-discard-changes-onclick").on("click", function (e) {
1259        $("#edit-discard-changes").click();
1260      });
1261    }
1262  });
1263
1264  // -------------------
1265  // Main nav menu
1266  // -------------------
1267  if (window.innerWidth > 1024) {
1268    // initMainNavMenu();
1269  }
1270  initMainNavMenu();
1271
1272  if (window.innerWidth < 1023) {
1273    document.querySelector("html").style.scrollBehavior = "auto";
1274    mobileScrollToAnchor();
1275  }
1276
1277  setTimeout(() => {
1278    handleHamburgerMenu();
1279  }, 800);
1280  initComparisonSlider();
1281  initZoomAnimation();
1282
1283  // SEARCH BAR IN MAIN MENU
1284  const searchButton = document.querySelector(".search-menu form button");
1285
1286  searchButton.addEventListener(
1287    "click",
1288    function (e) {
1289      e.preventDefault();
1290      const previousElement = this.previousElementSibling;
1291      if (previousElement) {
1292        previousElement.classList.add("visible");
1293        setTimeout(function () {
1294          previousElement.focus();
1295        }, 500);
1296      }
1297    },
1298    { once: true },
1299  );
1300
1301  // -------------------
1302  // Lazy loading videos
1303  // -------------------
1304  var lazyVideos = [].slice.call(document.querySelectorAll("video.lazy"));
1305
1306  if ("IntersectionObserver" in window) {
1307    var lazyVideoObserver = new IntersectionObserver(function (
1308      entries,
1309      observer,
1310    ) {
1311      entries.forEach(function (video) {
1312        if (video.isIntersecting) {
1313          for (var source in video.target.children) {
1314            var videoSource = video.target.children[source];
1315            if (
1316              typeof videoSource.tagName === "string" &&
1317              videoSource.tagName === "SOURCE"
1318            ) {
1319              videoSource.src = videoSource.dataset.src;
1320            }
1321          }
1322
1323          video.target.load();
1324          video.target.classList.remove("lazy");
1325          lazyVideoObserver.unobserve(video.target);
1326        }
1327      });
1328    });
1329
1330    lazyVideos.forEach(function (lazyVideo) {
1331      lazyVideoObserver.observe(lazyVideo);
1332    });
1333  }
1334})(jQuery);
1335
1336// -------------------
1337// COMPARISON SLIDER
1338// -------------------
1339function initComparisonSlider() {
1340  const sliderContainer = document.querySelector(
1341    ".comparison-slider-container",
1342  );
1343  const sliderContainerRangeBtn = document.querySelector(
1344    ".comparison-slider-container > .slider-range",
1345  );
1346  if (sliderContainer) {
1347    sliderContainerRangeBtn.addEventListener("input", (e) => {
1348      sliderContainer.style.setProperty("--position", `${e.target.value}%`);
1349    });
1350  }
1351}
1352
1353//	–––––––––––––––––––––––––––––––––
1354//	Breakpoint Handler
1355//	–––––––––––––––––––––––––––––––––
1356//	Breakpoints (no overlaps)
1357const breakpoints = [
1358  {
1359    key: "mobile",
1360    value: "(min-width: 0px) and (max-width: 1023px)",
1361  },
1362  {
1363    key: "desktop",
1364    value: "(min-width: 1024px)",
1365  },
1366];
1367
1368let prevBreakpoint;
1369function breakpointHandler() {
1370  //	find current breakpoint
1371  const breakpoint = breakpoints
1372    .filter((bp) => matchMedia(bp.value).matches)
1373    .pop().key;
1374
1375  //	exit if breakpoint is unchanged
1376  if (breakpoint === prevBreakpoint) return;
1377  prevBreakpoint = breakpoint;
1378
1379  //  call functions
1380  switch (breakpoint) {
1381    case "mobile": {
1382      //console.log('dispatch m');
1383      dispatchEvent(new CustomEvent("mediaMobile"));
1384      break;
1385    }
1386    case "desktop": {
1387      //console.log('dispatch');
1388      dispatchEvent(new CustomEvent("mediaDesktop"));
1389      break;
1390    }
1391  }
1392}
1393
1394// function setMenuHeight(mobileView) {
1395//   if (!mobileView) {
1396//     initMainNavMenu();
1397//   }
1398// }
1399
1400function initMainNavMenu() {
1401  jQuery(".first-level-menu .megamenu")
1402    .on("mouseenter", function () {
1403      if (window.innerWidth > 1024) {
1404        jQuery(".first-level-menu .megamenu").removeClass("clicked");
1405        jQuery(".first-level-menu .megamenu > button").removeClass("active");
1406        jQuery(this).addClass("clicked");
1407      }
1408    })
1409    .on("mouseleave", function () {
1410      if (window.innerWidth > 1024) {
1411        jQuery(this).removeClass("clicked");
1412      }
1413    });
1414  jQuery(".second-level .has-sub-level")
1415    .on("mouseenter", function () {
1416      if (window.innerWidth > 1024) {
1417        jQuery(".second-level .has-sub-level").removeClass("clicked");
1418        jQuery(this).addClass("clicked");
1419      }
1420    })
1421    .on("mouseleave", function () {
1422      if (window.innerWidth > 1024) {
1423        jQuery(this).removeClass("clicked");
1424      }
1425    });
1426  jQuery("body").on("click", function (e) {
1427    if (!jQuery(e.target).closest(".megamenu").length) {
1428      jQuery(".first-level-menu .megamenu").removeClass("clicked");
1429      jQuery(".second-level .has-sub-level").removeClass("clicked");
1430    }
1431  });
1432}
1433
1434// -------------------
1435// HAMBURGER MENU
1436// -------------------
1437function handleHamburgerMenu() {
1438  // elements
1439  const header = document.querySelector("header.region-header");
1440  const mobileMenuContainer = header.querySelector(".mobile-menu");
1441  const mobileMenuButton = mobileMenuContainer.querySelector(
1442    ".hamburger-menu-button",
1443  );
1444  // -- elements in first level menu
1445  const firstLevelMenu = header.querySelector(
1446    ".header-inner .block-main-menu .first-level-menu",
1447  );
1448  const productsMegamenu = firstLevelMenu.querySelector(
1449    ".menu-items .products-megamenu button.link",
1450  );
1451  let [...goBackButtons] = firstLevelMenu.querySelectorAll(
1452    ".menu-items .megamenu .second-level-menu > .go-back-burgermenu",
1453  );
1454  let [...goToSecondLevelButtons] = firstLevelMenu.querySelectorAll(
1455    ".menu-items .megamenu .third-level .go-back-burgermenu",
1456  );
1457  let [...firstLevelLinks] = firstLevelMenu.querySelectorAll(
1458    ".header-inner .block-main-menu .first-level-menu .menu-items .megamenu>.link",
1459  );
1460
1461  let [...secondLevelLinks] = firstLevelMenu.querySelectorAll(
1462    ".header-inner .block-main-menu .second-level-menu .menu-items .has-sub-level>.link",
1463  );
1464  let [...menuMainLinks] = firstLevelMenu.querySelectorAll(
1465    ".header-inner .block-main-menu .first-level-menu>.menu-items>.menu-item>.link",
1466  );
1467  let [...closeMegaMenuBtnDesktop] = header.querySelectorAll(
1468    ".second-level-menu .close-megamenu",
1469  );
1470
1471  function toggleHeaderExpansion(externalTrigger) {
1472    header.classList.toggle("expanded");
1473    openMegaMenuMobile = !openMegaMenuMobile;
1474
1475    if (openMenuItem && !externalTrigger) {
1476      collapseMenu();
1477    }
1478    if (externalTrigger) {
1479      openMenuItem = productsMegamenu;
1480      expandMenu();
1481    }
1482  }
1483
1484  // -------------------
1485  // Event listeners
1486  // -------------------
1487  if (mobileMenuButton) {
1488    mobileMenuButton.addEventListener("click", () => {
1489      toggleHeaderExpansion(false);
1490    });
1491  }
1492  // Keyup event listeners for tab key on main menu links
1493  menuMainLinks.forEach((el) => {
1494    el.addEventListener("keyup", (e) => {
1495      if (e.key === "Tab" && openMenuItem) {
1496        collapseMenu();
1497      }
1498    });
1499  });
1500  // Keyup event listeners for escape key
1501  header.addEventListener("keyup", (e) => {
1502    if (e.key === "Escape") {
1503      if (openMenuItem) {
1504        collapseMenu();
1505      } else {
1506        mobileMenuButton.click();
1507      }
1508    }
1509  });
1510  // Go back to first level menu on mobile
1511  goBackButtons.forEach((el) => {
1512    el.addEventListener("click", () => {
1513      collapseMenu();
1514    });
1515  });
1516  // Go back to second level menu on mobile
1517  goToSecondLevelButtons.forEach((el) => {
1518    el.addEventListener("click", () => {
1519      backToSecond();
1520    });
1521  });
1522  // Close desktop mega menu on click
1523  closeMegaMenuBtnDesktop.forEach((btn) => {
1524    btn.addEventListener("click", collapseMenu);
1525  });
1526
1527  // Trigger first level button events
1528  firstLevelLinks.forEach((el) => {
1529    // listen for clicks on first level buttons
1530    el.addEventListener("click", (e) => {
1531      if (openMenuItem) collapseMenu();
1532      openMenuItem = e.target;
1533      expandMenu();
1534    });
1535    // listen for mouseenter on first level buttons
1536    el.addEventListener("mouseenter", (e) => {
1537      if (!mobileView && openMenuItem) {
1538        if (openMenuItem !== e.target) collapseMenu();
1539      }
1540    });
1541  });
1542  // Trigger second level button events
1543  secondLevelLinks.forEach((el) => {
1544    // listen for clicks on first level buttons
1545    el.addEventListener("click", (e) => {
1546      //if (openMenuItem) collapseMenu();
1547      //openMenuItem = e.target;
1548      expandThirdLevelMenu(e.target);
1549    });
1550  });
1551  // collapse submenu on mouseleave if submenu is open
1552  header
1553    .querySelector(".block-main-menu")
1554    .addEventListener("mouseleave", (e) => {
1555      if (!mobileView && openMenuItem) collapseMenu();
1556    });
1557  // collapse submenu on click or tab outside
1558  ["focusin", "click"].forEach((event) => {
1559    addEventListener(event, (e) => {
1560      if (!openMenuItem && !openMegaMenuMobile) return;
1561      if (
1562        e.target.classList.contains("show-products-menu") ||
1563        e.target.closest(".region-header")
1564      )
1565        return;
1566      if (openMenuItem) return collapseMenu();
1567      if (openMegaMenuMobile) return toggleHeaderExpansion(false);
1568    });
1569  });
1570}
1571
1572// --------------------- //
1573// Toggle submenu states //
1574// --------------------- //
1575let openMenuItem;
1576let openMegaMenuMobile = false;
1577
1578const header = document.querySelector("header.region-header");
1579
1580const setAriaAttributes = (state, item, menu) => {
1581  item.setAttribute("aria-expanded", `${state}`);
1582  menu.setAttribute("aria-hidden", `${!state}`);
1583  return;
1584};
1585
1586function expandMenu() {
1587  const menu = document.querySelector(
1588    `#${openMenuItem.getAttribute("aria-controls")}`,
1589  );
1590  setAriaAttributes(true, openMenuItem, menu);
1591
1592  return setTimeout(() => {
1593    menu.classList.add("expanded");
1594  }, 100);
1595}
1596function expandThirdLevelMenu(el) {
1597  const thirdLevelMenu = document.querySelector(
1598    `#${el.getAttribute("aria-controls")}`,
1599  );
1600
1601  return setTimeout(() => {
1602    thirdLevelMenu.classList.add("expanded");
1603  }, 100);
1604}
1605
1606function collapseMenu() {
1607  if (!openMenuItem) return;
1608
1609  let item = openMenuItem;
1610
1611  const menu = document.querySelector(`#${item.getAttribute("aria-controls")}`);
1612  menu.classList.remove("expanded");
1613
1614  const time = mobileView ? 400 : 0;
1615
1616  setTimeout(() => {
1617    setAriaAttributes(false, item, menu);
1618  }, time);
1619
1620  return (openMenuItem = null);
1621}
1622
1623function backToSecond() {
1624  const menu = document.querySelector(`.menu-items.third-level.expanded`);
1625  menu.classList.remove("expanded");
1626}
1627
1628// ----------------------------------------
1629// Handle click activation of products menu
1630// ----------------------------------------
1631let [...anchortags] = document.querySelectorAll(".layout .show-products-menu");
1632if (anchortags.length) {
1633  const productsMegamenu = document.querySelector(
1634    ".header-inner .menu-items .products-megamenu button.link",
1635  );
1636  anchortags.forEach((a) => {
1637    a.addEventListener("click", (event) => {
1638      mobileView && header.classList.add("expanded");
1639      productsMegamenu.focus();
1640      openMenuItem = productsMegamenu;
1641      expandMenu();
1642      event.preventDefault;
1643    });
1644  });
1645}
1646
1647// -------------------
1648// Zoom In/Out animation
1649// -------------------
1650function initZoomAnimation() {
1651  let [...animatedImages] = document.querySelectorAll(".zoom-animation img");
1652
1653  if (animatedImages.length) {
1654    ScrollTrigger.defaults({
1655      scroller: "body",
1656    });
1657
1658    animatedImages.forEach((animatedImage) => {
1659      gsap.to(animatedImage, {
1660        scrollTrigger: {
1661          trigger: animatedImage,
1662          start: "20% bottom",
1663          end: "bottom top",
1664          scrub: true,
1665        },
1666        scale: 1.3,
1667        ease: "none",
1668      });
1669    });
1670  }
1671}
1672
1673// -------------------
1674// Mobile Scroll to Anchor from menu item click
1675// -------------------
1676function mobileScrollToAnchor() {
1677  const navLinks = document.querySelectorAll(".menu-item a.link");
1678  const mobileMenu = document.querySelector("header");
1679
1680  if (navLinks) {
1681    navLinks.forEach((anchor) => {
1682      anchor.addEventListener("click", function () {
1683        window.addEventListener("hashchange", function () {
1684          const id = window.location.hash;
1685          const yOffset = 200;
1686          const element = document.querySelector(id);
1687          const y =
1688            element.getBoundingClientRect().top + window.pageYOffset - yOffset;
1689
1690          window.scrollTo({ top: y, behavior: "smooth" });
1691
1692          setTimeout(function () {
1693            mobileMenu.classList.remove("expanded");
1694          }, 500);
1695        });
1696      });
1697    });
1698  }
1699}
1700
1701// -------------------
1702// Inquiry form
1703// -------------------
1704// Function to fetch and extract the locationCode
1705async function fetchLocationCode() {
1706  let ret = drupalSettings.locationCode || "";
1707  if (ret) {
1708    return ret;
1709  }
1710  try {
1711    if (
1712      drupalSettings.exception === 403 ||
1713      drupalSettings.exception === 404 ||
1714      drupalSettings.path.currentPathIsAdmin
1715    ) {
1716      return;
1717    }
1718    const response = await fetch("/api/location_get", {
1719      method: "GET",
1720      headers: {
1721        "Content-Type": "application/json",
1722      },
1723    });
1724
1725    if (!response.ok) {
1726      throw new Error(`HTTP error! Status: ${response.status}`);
1727    }
1728    const data = await response.json();
1729    drupalSettings.locationCode = data.data?.locationCode || "";
1730    return drupalSettings.locationCode;
1731  } catch (error) {
1732    console.error("Error fetching location code:", error);
1733    return "";
1734  }
1735}
1736
1737async function fetchLocationCodeByUrl() {
1738  if (
1739    drupalSettings.exception === 403 ||
1740    drupalSettings.exception === 404 ||
1741    drupalSettings.path.currentPathIsAdmin
1742  ) {
1743    return "";
1744  }
1745  let ret = drupalSettings.locationCodeByUrl || "";
1746  if (ret) {
1747    return ret;
1748  }
1749
1750  try {
1751    let url = window.location.origin + "/";
1752    if (drupalSettings.path.pathPrefix !== "") {
1753      url = url + drupalSettings.path.pathPrefix;
1754    }
1755    const response = await fetch(url + "api/location_get_by_language", {
1756      method: "GET",
1757      headers: {
1758        "Content-Type": "application/json",
1759      },
1760    });
1761
1762    if (!response.ok) {
1763      throw new Error(`HTTP error! Status: ${response.status}`);
1764    }
1765    const data = await response.json();
1766    drupalSettings.locationCodeByUrl = data.data?.locationCode || "";
1767    return drupalSettings.locationCodeByUrl;
1768  } catch (error) {
1769    console.error("Error fetching location code:", error);
1770    return "";
1771  }
1772}
1773
1774if (document.querySelector(".block-inquiry-form")) {
1775  const countryDropdownBtn = document.querySelectorAll(".dropdown-wrapper-btn");
1776
1777  function initProductDropdown(ref) {
1778    // Clear the label in the product dropdown each time the country dropdown is changed
1779    const refBtn = ref.querySelector('[data-comp="ref-button"]');
1780    refBtn.textContent = "...";
1781    refBtn.removeAttribute("lang");
1782
1783    return disableNextBtn(ref);
1784  }
1785
1786  function disableNextBtn(item) {
1787    // Disable the "Next" button until a new product selection is made
1788    const nextButtonWrapper = item.querySelector(".button-wrapper");
1789    const nextButtonLinks = nextButtonWrapper.querySelector("a");
1790
1791    nextButtonWrapper.classList.add("disabled");
1792
1793    if (nextButtonLinks) {
1794      nextButtonLinks.removeAttribute("href"); // Clear the href
1795      delete nextButtonLinks.dataset.productName;
1796      nextButtonLinks.style.pointerEvents = "none"; // Disable click
1797    }
1798  }
1799
1800  function updateDropdownPosition() {
1801    const dropdownMenus = document.querySelectorAll(
1802      ".dropdown-wrapper-content",
1803    );
1804    dropdownMenus.forEach(function (menu) {
1805      const visibleItems = Array.from(menu.querySelectorAll("li")).filter(
1806        (li) => li.style.display !== "none",
1807      );
1808      // Add or remove "bottom-pos" based on the number of visible items
1809      if (visibleItems.length > 4) {
1810        menu.classList.add("bottom-pos");
1811      } else {
1812        menu.classList.remove("bottom-pos");
1813      }
1814    });
1815  }
1816
1817  function enableRefChooser(ref) {
1818    ref.classList.remove("disabled"); // Enable interaction
1819    ref.querySelector('[data-comp="ref-button"]').classList.remove("disabled");
1820  }
1821
1822  function filterRefDropdown(selectedLang, refWrapper) {
1823    // Function to filter product dropdown items based on selected language
1824    enableRefChooser(refWrapper);
1825    const dropdownItems = refWrapper.querySelectorAll(
1826      ".dropdown-wrapper-content li",
1827    );
1828
1829    let hasMatchingItems = false;
1830
1831    // Show/hide items based on lang attribute
1832    dropdownItems.forEach(function (item) {
1833      const link = item.querySelector("a");
1834
1835      if (link && link.getAttribute("lang") === selectedLang) {
1836        item.style.display = "";
1837        hasMatchingItems = true;
1838      } else {
1839        item.style.display = "none";
1840      }
1841    });
1842
1843    // If no items match, show an "Empty" option
1844    if (!hasMatchingItems) {
1845      refWrapper
1846        .querySelectorAll(".dropdown-wrapper-content .empty-item")
1847        .forEach(function (emptyItem) {
1848          emptyItem.parentElement.style.display = "none";
1849        });
1850
1851      refWrapper
1852        .querySelectorAll(".dropdown-wrapper-content .empty-item")
1853        .forEach(function (emptyItem) {
1854          emptyItem.parentElement.style.display = "";
1855        });
1856
1857      // Disable the "Next" button until a new product selection is made
1858      disableNextBtn(refWrapper);
1859    }
1860  }
1861
1862  function closeAllDropdowns() {
1863    document
1864      .querySelectorAll(".dropdown-wrapper-toggle.selected")
1865      .forEach(function (toggle) {
1866        toggle.classList.remove("selected");
1867      });
1868  }
1869
1870  // REVIEW: Add a document click listener to close dropdowns if clicked outside
1871  document.addEventListener("click", function (event) {
1872    let activeDropdowns = document.querySelectorAll(
1873      ".dropdown-wrapper-toggle.selected",
1874    );
1875    if (activeDropdowns && activeDropdowns.length == 0) return;
1876
1877    const clickedInsideDropdown = event.target.closest(".dropdown-wrapper");
1878
1879    if (!clickedInsideDropdown) {
1880      closeAllDropdowns();
1881    }
1882  });
1883
1884  // Function to preset the language
1885  async function presetLanguage() {
1886    try {
1887      // Fetch the locationCode from your API
1888      const fetchedLocationCode = await fetchLocationCodeByUrl();
1889
1890      const dropdownWrapper = document.querySelectorAll(
1891        ".country-selector-wrapper > .dropdown-wrapper",
1892      );
1893
1894      dropdownWrapper.forEach(function (item) {
1895        const dropdownBtn = item.querySelector(".dropdown-wrapper-btn");
1896        const dropdownContent = item.querySelector(".dropdown-wrapper-content");
1897
1898        // Find the corresponding <a> element in the dropdown
1899        const presetItem = Array.from(
1900          dropdownContent.querySelectorAll("li > a"),
1901        ).find((a) => a.getAttribute("lang") === fetchedLocationCode);
1902
1903        if (presetItem) {
1904          const selectedUrl = presetItem.getAttribute("href");
1905          // Set the dropdown button's text and lang attribute
1906          dropdownBtn.textContent = presetItem.textContent.trim();
1907          dropdownBtn.setAttribute("lang", fetchedLocationCode);
1908
1909          const ref = dropdownContent.getAttribute("data-ref-comp");
1910          const refWrapper = document.querySelector(`[data-comp="${ref}"]`);
1911          const refDropdown = dropdownContent.getAttribute("data-comp");
1912
1913          if (!refWrapper) {
1914            return;
1915          }
1916
1917          if (refDropdown == "language-dropdown") {
1918            initProductDropdown(refWrapper);
1919            updateDropdownPosition();
1920            filterRefDropdown(fetchedLocationCode, refWrapper);
1921          }
1922          if (refDropdown == "secondary-dropdown") {
1923            // Update the "Next" button with the preset URL
1924            let nextButtonWrapperPharma = refWrapper.querySelector(
1925              ".button-wrapper.next",
1926            );
1927            let nextButton = nextButtonWrapperPharma.querySelector("a");
1928
1929            if (selectedUrl !== "#") {
1930              nextButton.setAttribute("href", selectedUrl);
1931              nextButton.style.pointerEvents = "auto";
1932              nextButtonWrapperPharma.classList.remove("disabled");
1933            } else {
1934              nextButtonWrapperPharma.classList.add("disabled");
1935              nextButton.setAttribute("href", "#");
1936              nextButton.style.pointerEvents = "none";
1937            }
1938          }
1939        }
1940      });
1941    } catch (error) {
1942      console.error("Error presetting language:", error);
1943    }
1944  }
1945
1946  // Initialize the dropdown preset and attach event listeners
1947  function initializeDropdown() {
1948    if (countryDropdownBtn.length === 0) {
1949      return;
1950    }
1951    presetLanguage(); // Preset the language
1952    countryDropdownBtn.forEach(function (item) {
1953      // foreach country selection dropdown
1954      item.addEventListener("click", function (e) {
1955        // If the item is disabled, ignore the click
1956        if (item.classList.contains("disabled")) return;
1957
1958        e.preventDefault();
1959        e.stopPropagation();
1960
1961        let toggle = item
1962          .closest(".dropdown-wrapper")
1963          .querySelector(".dropdown-wrapper-toggle");
1964        const dropdownContent = item
1965          .closest(".dropdown-wrapper")
1966          .querySelector(".dropdown-wrapper-content");
1967
1968        const ref = dropdownContent.getAttribute("data-ref-comp");
1969        const refWrapper =
1970          ref && document.querySelector(`[data-comp="${ref}"]`);
1971        const refDropdown = dropdownContent.getAttribute("data-comp");
1972
1973        toggle.classList.toggle("selected"); // Toggle the 'selected' class
1974
1975        // Close other open dropdowns
1976        document
1977          .querySelectorAll(".dropdown-wrapper-toggle.selected")
1978          .forEach(function (openToggle) {
1979            if (openToggle !== toggle) {
1980              openToggle.classList.remove("selected");
1981            }
1982          });
1983
1984        // Add event listeners to all <a> elements inside the dropdown content
1985        dropdownContent.querySelectorAll("li > a").forEach(function (listItem) {
1986          if (!listItem.dataset.listenerAdded) {
1987            listItem.dataset.listenerAdded = true; // Mark as added
1988
1989            listItem.addEventListener("click", function (e) {
1990              e.preventDefault(); // Prevent navigation
1991              e.stopPropagation();
1992
1993              // Get the text content and lang attribute from the clicked <a>
1994              const selectedText = listItem.textContent.trim();
1995              const selectedLang = listItem.getAttribute("lang");
1996              const selectedUrl = listItem.getAttribute("href"); // Get the URL of the selected product
1997              item.textContent = selectedText;
1998              item.setAttribute("lang", selectedLang);
1999              toggle.classList.remove("selected");
2000
2001              if (refDropdown == "language-dropdown") {
2002                initProductDropdown(refWrapper);
2003                updateDropdownPosition();
2004                filterRefDropdown(selectedLang, refWrapper);
2005              }
2006              // Update the "Next" button with the selected URL and enable it
2007              let nextButtonWrapper =
2008                refWrapper.querySelector(".button-wrapper");
2009              let nextButton = nextButtonWrapper.querySelector("a");
2010
2011              if (selectedUrl !== "#") {
2012                nextButton.setAttribute("href", selectedUrl);
2013                nextButton.dataset.leadFormLang = selectedLang || "";
2014                nextButton.dataset.productName = selectedText;
2015                nextButton.style.pointerEvents = "auto";
2016                nextButtonWrapper.classList.remove("disabled");
2017              } else {
2018                nextButtonWrapper.classList.add("disabled");
2019                nextButton.setAttribute("href", "#");
2020                nextButton.dataset.leadFormLang = "";
2021                delete nextButton.dataset.productName;
2022                nextButton.style.pointerEvents = "none";
2023              }
2024            });
2025          }
2026        });
2027      });
2028    });
2029  }
2030
2031  function initializeLeadFormModal() {
2032    const triggerSelector =
2033      ".block-inquiry-form .product-references-wrapper .button-wrapper.p-next a";
2034    const triggers = document.querySelectorAll(triggerSelector);
2035
2036    if (!triggers.length) {
2037      return;
2038    }
2039
2040    let modal = document.querySelector(".lead-form-modal");
2041    let frame = null;
2042    let detachLeadFormAutoResize = null;
2043    let lockedScrollY = 0;
2044
2045    function lockPageScroll() {
2046      lockedScrollY = window.scrollY || window.pageYOffset || 0;
2047      document.body.style.position = "fixed";
2048      document.body.style.top = `-${lockedScrollY}px`;
2049      document.body.style.left = "0";
2050      document.body.style.right = "0";
2051      document.body.style.width = "100%";
2052      document.body.style.overflow = "hidden";
2053    }
2054
2055    function unlockPageScroll() {
2056      document.body.style.position = "";
2057      document.body.style.top = "";
2058      document.body.style.left = "";
2059      document.body.style.right = "";
2060      document.body.style.width = "";
2061      document.body.style.overflow = "";
2062      window.scrollTo(0, lockedScrollY);
2063    }
2064
2065    function clearLeadFormAutoResize() {
2066      if (typeof detachLeadFormAutoResize === "function") {
2067        detachLeadFormAutoResize();
2068      }
2069      detachLeadFormAutoResize = null;
2070    }
2071
2072    function applyLeadFormModalStyles(frameDoc) {
2073      if (!frameDoc || !frameDoc.head) {
2074        return;
2075      }
2076
2077      if (frameDoc.getElementById("lead-form-modal-inline-styles")) {
2078        return;
2079      }
2080
2081      const style = frameDoc.createElement("style");
2082      style.id = "lead-form-modal-inline-styles";
2083      style.textContent = `
2084        .webform-submission-form input:not([type="checkbox"]):not([type="radio"]):not([type="tel"]),
2085        .webform-submission-form textarea {
2086          min-height: 44px;
2087          border-radius: 3.955px;
2088          border: 1.318px solid #002d72;
2089          box-sizing: border-box;
2090        }
2091
2092        .webform-submission-form input:not([type="checkbox"]):not([type="radio"]):not([type="tel"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not(.form-select):not(.choices__input),
2093        .webform-submission-form textarea {
2094          background: #fff;
2095        }
2096
2097        .webform-submission-form select,
2098        .webform-submission-form .form-select,
2099        .webform-submission-form .choices,
2100        .webform-submission-form .choices__inner,
2101        .webform-submission-form .select2-container .select2-selection,
2102        .webform-submission-form .chosen-container .chosen-single {
2103          width: 100%;
2104          min-height: 44px;
2105          border-radius: 3.955px;
2106          border: 1.318px solid #002d72;
2107          background: #fff;
2108          color: #303030;
2109          box-sizing: border-box;
2110        }
2111
2112        .webform-submission-form .choices__inner,
2113        .webform-submission-form .select2-container .select2-selection,
2114        .webform-submission-form .chosen-container .chosen-single {
2115          padding: 10px 12px;
2116        }
2117
2118        .webform-submission-form .select,
2119        .webform-submission-form .form-select,
2120        .webform-submission-form .choices__inner,
2121        .webform-submission-form .select2-container .select2-selection,
2122        .webform-submission-form .chosen-container .chosen-single {
2123          box-shadow: inset 0 0 0 1px rgba(0, 45, 114, 0.04);
2124        }
2125
2126        .webform-submission-form select,
2127        .webform-submission-form .form-select {
2128          appearance: none;
2129          -webkit-appearance: none;
2130          padding-right: 2.75rem;
2131          background-image: linear-gradient(45deg, transparent 50%, #002d72 50%), linear-gradient(135deg, #002d72 50%, transparent 50%);
2132          background-position: calc(100% - 1.15rem) calc(50% + 0.06rem), calc(100% - 0.82rem) calc(50% + 0.06rem);
2133          background-size: 0.42rem 0.42rem, 0.42rem 0.42rem;
2134          background-repeat: no-repeat;
2135        }
2136
2137        .webform-submission-form .choices[data-type*="select-one"],
2138        .webform-submission-form .select2-container,
2139        .webform-submission-form .chosen-container {
2140          position: relative;
2141        }
2142
2143        .webform-submission-form .select2-container .select2-selection--single,
2144        .webform-submission-form .chosen-container .chosen-single,
2145        .webform-submission-form .choices[data-type*="select-one"] .choices__inner {
2146          position: relative;
2147          padding-right: 2.75rem;
2148        }
2149
2150        .webform-submission-form .select2-container .select2-selection--single::after,
2151        .webform-submission-form .chosen-container .chosen-single::after,
2152        .webform-submission-form .choices[data-type*="select-one"]::after {
2153          content: "";
2154          position: absolute;
2155          right: 1rem;
2156          top: 50%;
2157          width: 0.55rem;
2158          height: 0.55rem;
2159          border-right: 2px solid #002d72;
2160          border-bottom: 2px solid #002d72;
2161          transform: translateY(-75%) rotate(45deg);
2162          transform-origin: center;
2163          transition: none;
2164          pointer-events: none;
2165        }
2166
2167        .webform-submission-form .iti {
2168          width: 100% !important;
2169          display: block;
2170        }
2171
2172        .webform-submission-form .iti input[type="tel"],
2173        .webform-submission-form .iti input.form-tel {
2174          width: 100%;
2175          min-height: 44px;
2176          padding-right: 12px;
2177          box-sizing: border-box;
2178        }
2179
2180        .webform-submission-form .iti--allow-dropdown input[type="tel"],
2181        .webform-submission-form .iti--allow-dropdown input.form-tel {
2182          padding-left: 56px !important;
2183          border-radius: 3.955px;
2184        }
2185
2186        .webform-submission-form .iti__selected-flag {
2187          height: 100%;
2188          padding: 0 10px;
2189          border-right: 1.318px solid #002d72;
2190          border-top: 0;
2191          border-bottom: 0;
2192          border-left: 0;
2193          border-radius: 0;
2194          background: #fff;
2195          box-sizing: border-box;
2196        }
2197
2198        .webform-submission-form .iti__country-list {
2199          min-width: 280px;
2200          max-width: none;
2201          width: max-content;
2202          box-sizing: border-box;
2203          z-index: 9999;
2204          overflow-y: auto;
2205          max-height: 220px;
2206        }
2207
2208        .webform-submission-form .iti__country-list .iti__country {
2209          display: flex;
2210          align-items: center;
2211          gap: 8px;
2212          white-space: nowrap;
2213          padding: 6px 12px;
2214        }
2215
2216        .webform-submission-form .iti__country-list .iti__country-name {
2217          flex: 1;
2218          overflow: visible;
2219          white-space: nowrap;
2220        }
2221
2222        .webform-submission-form .iti__country-list .iti__dial-code {
2223          flex-shrink: 0;
2224          margin-left: 4px;
2225        }
2226
2227        .webform-submission-form .iti__flag-container {
2228          position: absolute;
2229          z-index: 9999;
2230        }
2231
2232        .webform-submission-form .form-type-checkbox,
2233        .webform-submission-form .js-form-type-checkbox,
2234        .webform-submission-form .webform-type-checkbox {
2235          display: grid;
2236          grid-template-columns: 1.125rem minmax(0, 1fr);
2237          column-gap: 10px;
2238          row-gap: 0.25rem;
2239          align-items: start;
2240        }
2241
2242        .webform-submission-form .form-type-checkbox[style*="display:block"],
2243        .webform-submission-form .form-type-checkbox[style*="display: block"],
2244        .webform-submission-form .js-form-type-checkbox[style*="display:block"],
2245        .webform-submission-form .js-form-type-checkbox[style*="display: block"],
2246        .webform-submission-form .webform-type-checkbox[style*="display:block"],
2247        .webform-submission-form .webform-type-checkbox[style*="display: block"] {
2248          display: grid !important;
2249        }
2250
2251        .webform-submission-form .form-type-checkbox input[type="checkbox"],
2252        .webform-submission-form .js-form-type-checkbox input[type="checkbox"],
2253        .webform-submission-form .webform-type-checkbox input[type="checkbox"] {
2254          grid-column: 1;
2255          grid-row: 1;
2256          margin: 0.2rem 0 0;
2257          width: 1.125rem;
2258          height: 1.125rem;
2259          appearance: none;
2260          -webkit-appearance: none;
2261          border: 1.5px solid #002d72;
2262          border-radius: 0.35rem;
2263          background: #fff;
2264          display: grid;
2265          place-content: center;
2266          cursor: pointer;
2267          box-sizing: border-box;
2268        }
2269
2270        .webform-submission-form .form-type-checkbox input[type="checkbox"]::before,
2271        .webform-submission-form .js-form-type-checkbox input[type="checkbox"]::before,
2272        .webform-submission-form .webform-type-checkbox input[type="checkbox"]::before {
2273          content: "";
2274          width: 0.62rem;
2275          height: 0.62rem;
2276          transform: scale(0);
2277          transition: transform 120ms ease-in-out;
2278          border-radius: 0.18rem;
2279          background: #002d72;
2280        }
2281
2282        .webform-submission-form .form-type-checkbox input[type="checkbox"]:checked::before,
2283        .webform-submission-form .js-form-type-checkbox input[type="checkbox"]:checked::before,
2284        .webform-submission-form .webform-type-checkbox input[type="checkbox"]:checked::before {
2285          transform: scale(1);
2286        }
2287
2288        .webform-submission-form .form-type-checkbox input[type="checkbox"]:focus-visible,
2289        .webform-submission-form .js-form-type-checkbox input[type="checkbox"]:focus-visible,
2290        .webform-submission-form .webform-type-checkbox input[type="checkbox"]:focus-visible {
2291          outline: 2px solid rgba(0, 45, 114, 0.35);
2292          outline-offset: 2px;
2293        }
2294
2295        .webform-submission-form .form-type-checkbox label,
2296        .webform-submission-form .form-type-checkbox .form-item__label,
2297        .webform-submission-form .form-type-checkbox .description,
2298        .webform-submission-form .form-type-checkbox .form-item__description,
2299        .webform-submission-form .js-form-type-checkbox label,
2300        .webform-submission-form .js-form-type-checkbox .form-item__label,
2301        .webform-submission-form .js-form-type-checkbox .description,
2302        .webform-submission-form .js-form-type-checkbox .form-item__description,
2303        .webform-submission-form .webform-type-checkbox label,
2304        .webform-submission-form .webform-type-checkbox .form-item__label,
2305        .webform-submission-form .webform-type-checkbox .description,
2306        .webform-submission-form .webform-type-checkbox .form-item__description {
2307          grid-column: 2;
2308          margin: 0;
2309          min-width: 0;
2310        }
2311
2312        .webform-submission-form label,
2313        .webform-submission-form .form-item__label,
2314        .webform-submission-form .form-item > label {
2315          color: #002d72;
2316        }
2317
2318        .webform-submission-form .form-actions,
2319        .webform-submission-form [data-drupal-selector="edit-actions"] {
2320          margin-top: 2rem;
2321          padding-top: 0.5rem;
2322        }
2323
2324        .webform-submission-form .webform-button--submit,
2325        .webform-submission-form input[type="submit"] {
2326          display: inline-flex;
2327          align-items: center;
2328          justify-content: center;
2329          margin-top: 0.5rem;
2330          padding: 0.3rem 2.5rem;
2331          border-radius: 100px;
2332          border: 2px solid #002d72;
2333          background-color: #002d72;
2334          color: #fff;
2335          font: var(--fw-400) var(--fs-0) / var(--lh-1) var(--ff-0);
2336          font-weight: var(--fw-500);
2337          text-transform: uppercase;
2338          letter-spacing: 0.02em;
2339          box-shadow: none;
2340          border-radius: 100px !important;
2341          transition: transform 300ms ease, background-color 300ms ease,
2342            border-color 300ms ease;
2343        }
2344
2345        .webform-submission-form .webform-button--submit:hover,
2346        .webform-submission-form .webform-button--submit:focus,
2347        .webform-submission-form .webform-button--submit:focus-visible,
2348        .webform-submission-form input[type="submit"]:hover,
2349        .webform-submission-form input[type="submit"]:focus,
2350        .webform-submission-form input[type="submit"]:focus-visible {
2351          transform: scale(1.07);
2352          cursor: pointer;
2353        }
2354      `;
2355
2356      frameDoc.head.appendChild(style);
2357    }
2358
2359    function closeModal() {
2360      if (!modal) {
2361        return;
2362      }
2363
2364      clearLeadFormAutoResize();
2365      modal.close();
2366      unlockPageScroll();
2367      if (frame) {
2368        frame.setAttribute("src", "about:blank");
2369      }
2370    }
2371
2372    function buildLeadFormUrl(trigger) {
2373      const selectedHref = trigger.getAttribute("href");
2374      const selectedLang = (trigger.dataset.leadFormLang || "").toLowerCase();
2375      const currentPathLang = (
2376        window.location.pathname.split("/")[1] || ""
2377      ).toLowerCase();
2378      const langPattern = /^[a-z]{2}(?:-[a-z]{2})?$/;
2379      const normalizeProductSlug = (value) =>
2380        value
2381          .toString()
2382          .normalize("NFD")
2383          .replace(/[\u0300-\u036f]/g, "")
2384          .toLowerCase()
2385          .replace(/[^a-z0-9]+/g, "-")
2386          .replace(/^-+|-+$/g, "");
2387      const extractProductSlugFromPathname = (pathname) => {
2388        co
2388nst segments = pathname.split("/").filter(Boolean);
2389        if (segments.length < 2) {
2390          return "";
2391        }
2392
2393        if (langPattern.test(segments[0])) {
2394          return decodeURIComponent(segments[1] || "");
2395        }
2396
2397        return decodeURIComponent(segments[segments.length - 1] || "");
2398      };
2399
2400      const leadFormId =
2401        selectedLang === "en-us" || currentPathLang === "en-us"
2402          ? "citraclear-new-lead-form"
2403          : "lead-form";
2404
2405      const leadFormUrl = new URL(
2406        `/form/${leadFormId}`,
2407        window.location.origin,
2408      );
2409
2410      if (langPattern.test(selectedLang) && selectedLang !== "other") {
2411        leadFormUrl.pathname = `/${selectedLang}/form/${leadFormId}`;
2412      } else if (
2413        langPattern.test(currentPathLang) &&
2414        currentPathLang !== "other"
2415      ) {
2416        leadFormUrl.pathname = `/${currentPathLang}/form/${leadFormId}`;
2417      }
2418
2419      if (selectedHref && selectedHref !== "#") {
2420        const selectedProductUrl = new URL(
2421          selectedHref,
2422          window.location.origin,
2423        );
2424        const selectedProductName = (
2425          trigger.dataset.productName ||
2426          trigger
2427            .closest(".product-references-wrapper")
2428            ?.querySelector('[data-comp="ref-button"]')?.textContent ||
2429          ""
2430        ).trim();
2431        const productSlug =
2432          selectedProductUrl.searchParams.get("product") ||
2433          extractProductSlugFromPathname(selectedProductUrl.pathname) ||
2434          normalizeProductSlug(selectedProductName);
2435
2436        if (productSlug) {
2437          leadFormUrl.searchParams.set("product", productSlug);
2438        }
2439
2440        if (selectedProductName) {
2441          leadFormUrl.searchParams.set("product_name", selectedProductName);
2442        }
2443      }
2444
2445      return leadFormUrl.toString();
2446    }
2447
2448    function openModal(url) {
2449      if (!url) {
2450        return;
2451      }
2452
2453      if (!modal) {
2454        modal = document.createElement("dialog");
2455        modal.className = "lead-form-modal";
2456        modal.innerHTML = `
2457          <div class="lead-form-modal__panel" role="document">
2458            <button class="lead-form-modal__close" type="button" aria-label="Close">&times;</button>
2459            <iframe class="lead-form-modal__iframe" title="Lead form" loading="lazy"></iframe>
2460          </div>
2461        `;
2462        document.body.appendChild(modal);
2463
2464        frame = modal.querySelector(".lead-form-modal__iframe");
2465        modal
2466          .querySelector(".lead-form-modal__close")
2467          .addEventListener("click", closeModal);
2468
2469        modal.addEventListener("click", (event) => {
2470          if (event.target === modal) {
2471            closeModal();
2472          }
2473        });
2474
2475        modal.addEventListener("close", () => {
2476          unlockPageScroll();
2477        });
2478      }
2479
2480      if (!frame) {
2481        frame = modal.querySelector(".lead-form-modal__iframe");
2482      }
2483
2484      frame.onload = () => {
2485        try {
2486          const frameDoc = frame.contentDocument;
2487          if (!frameDoc) {
2488            return;
2489          }
2490
2491          clearLeadFormAutoResize();
2492
2493          applyLeadFormModalStyles(frameDoc);
2494          const frameRoot = frameDoc.querySelector(".page-main");
2495
2496          frameDoc.documentElement.style.overflowX = "hidden";
2497          frameDoc.body.style.overflowX = "hidden";
2498          frameDoc.body.style.margin = "0";
2499          frameDoc.documentElement.style.overflowY = "auto";
2500          frameDoc.body.style.overflowY = "auto";
2501
2502          let touchLastY = null;
2503          let touchRemainder = 0;
2504
2505          const onFrameTouchStart = (event) => {
2506            if (!event.touches || !event.touches.length) {
2507              touchLastY = null;
2508              return;
2509            }
2510            touchLastY = event.touches[0].clientY;
2511          };
2512
2513          const onFrameTouchMove = (event) => {
2514            if (
2515              touchLastY === null ||
2516              !event.touches ||
2517              !event.touches.length
2518            ) {
2519              return;
2520            }
2521
2522            const currentY = event.touches[0].clientY;
2523            const rawDeltaY = touchLastY - currentY;
2524            touchLastY = currentY;
2525
2526            if (!modal) {
2527              return;
2528            }
2529
2530            // Keep very small movement instead of dropping it to avoid jitter.
2531            const deltaWithRemainder = rawDeltaY + touchRemainder;
2532            const step =
2533              deltaWithRemainder > 0
2534                ? Math.floor(deltaWithRemainder)
2535                : Math.ceil(deltaWithRemainder);
2536            touchRemainder = deltaWithRemainder - step;
2537
2538            if (step !== 0) {
2539              modal.scrollTop += step;
2540            }
2541
2542            event.preventDefault();
2543          };
2544
2545          const onFrameTouchEnd = () => {
2546            touchLastY = null;
2547            touchRemainder = 0;
2548          };
2549
2550          frameDoc.addEventListener("touchstart", onFrameTouchStart, {
2551            passive: true,
2552            capture: true,
2553          });
2554          frameDoc.addEventListener("touchmove", onFrameTouchMove, {
2555            passive: false,
2556            capture: true,
2557          });
2558          frameDoc.addEventListener("touchend", onFrameTouchEnd, true);
2559          frameDoc.addEventListener("touchcancel", onFrameTouchEnd, true);
2560
2561          frameDoc
2562            .querySelectorAll(
2563              "header, .region-header, .breadcrumb, .region-breadcrumb, .region-footer, .footer, footer",
2564            )
2565            .forEach((el) => {
2566              el.style.display = "none";
2567            });
2568
2569          let resizeRafId = null;
2570
2571          const scrollModalToTop = () => {
2572            if (modal) {
2573              modal.scrollTop = 0;
2574            }
2575            frameDoc.documentElement.scrollTop = 0;
2576            frameDoc.body.scrollTop = 0;
2577            if (frameRoot) {
2578              frameRoot.scrollIntoView({ block: "start", behavior: "auto" });
2579            }
2580          };
2581
2582          const resizeModalToContent = () => {
2583            const isDesktop = window.matchMedia("(min-width: 768px)").matches;
2584
2585            if (frameRoot) {
2586              frameRoot.style.maxWidth = isDesktop ? "60rem" : "100%";
2587              frameRoot.style.margin = "0 auto";
2588              frameRoot.style.padding = isDesktop
2589                ? "0 1.75rem 0.75rem"
2590                : "0 1rem 1.5rem";
2591              frameRoot.style.boxSizing = "border-box";
2592            }
2593
2594            const contentHeight = Math.max(
2595              frameDoc.documentElement.scrollHeight,
2596              frameDoc.body.scrollHeight,
2597              frameRoot ? frameRoot.scrollHeight : 0,
2598            );
2599
2600            frame.style.height = `${contentHeight + 4}px`;
2601            frameDoc.documentElement.style.overflowY = "hidden";
2602            frameDoc.body.style.overflowY = "hidden";
2603            modal.style.overflowY = "auto";
2604            modal.style.overscrollBehaviorY = "contain";
2605            modal.style.webkitOverflowScrolling = "touch";
2606          };
2607
2608          const requestResizeModalToContent = () => {
2609            if (resizeRafId !== null) {
2610              cancelAnimationFrame(resizeRafId);
2611            }
2612
2613            resizeRafId = requestAnimationFrame(() => {
2614              resizeRafId = null;
2615              resizeModalToContent();
2616            });
2617          };
2618
2619          const onViewportResize = () => {
2620            requestResizeModalToContent();
2621          };
2622
2623          window.addEventListener("resize", onViewportResize);
2624          window.addEventListener("orientationchange", onViewportResize);
2625
2626          frameDoc.addEventListener("input", requestResizeModalToContent, true);
2627          frameDoc.addEventListener(
2628            "change",
2629            requestResizeModalToContent,
2630            true,
2631          );
2632          frameDoc.addEventListener(
2633            "submit",
2634            () => {
2635              requestResizeModalToContent();
2636              requestAnimationFrame(scrollModalToTop);
2637            },
2638            true,
2639          );
2640
2641          let frameResizeObserver = null;
2642          if (typeof ResizeObserver !== "undefined") {
2643            frameResizeObserver = new ResizeObserver(() => {
2644              requestResizeModalToContent();
2645            });
2646            frameResizeObserver.observe(frameDoc.body);
2647            if (frameRoot) {
2648              frameResizeObserver.observe(frameRoot);
2649            }
2650          }
2651
2652          const frameMutationObserver = new MutationObserver(() => {
2653            requestResizeModalToContent();
2654          });
2655          frameMutationObserver.observe(frameDoc.body, {
2656            childList: true,
2657            subtree: true,
2658            attributes: true,
2659            characterData: true,
2660          });
2661
2662          detachLeadFormAutoResize = () => {
2663            window.removeEventListener("resize", onViewportResize);
2664            window.removeEventListener("orientationchange", onViewportResize);
2665            frameDoc.removeEventListener(
2666              "input",
2667              requestResizeModalToContent,
2668              true,
2669            );
2670            frameDoc.removeEventListener(
2671              "change",
2672              requestResizeModalToContent,
2673              true,
2674            );
2675            frameDoc.removeEventListener("touchstart", onFrameTouchStart, true);
2676            frameDoc.removeEventListener("touchmove", onFrameTouchMove, true);
2677            frameDoc.removeEventListener("touchend", onFrameTouchEnd, true);
2678            frameDoc.removeEventListener("touchcancel", onFrameTouchEnd, true);
2679            frameMutationObserver.disconnect();
2680            if (frameResizeObserver) {
2681              frameResizeObserver.disconnect();
2682            }
2683            if (resizeRafId !== null) {
2684              cancelAnimationFrame(resizeRafId);
2685              resizeRafId = null;
2686            }
2687          };
2688
2689          requestResizeModalToContent();
2690          scrollModalToTop();
2691          setTimeout(requestResizeModalToContent, 200);
2692          setTimeout(requestResizeModalToContent, 700);
2693          setTimeout(scrollModalToTop, 200);
2694        } catch (err) {
2695          // Ignore iframe DOM access issues (e.g. unexpected cross-origin responses).
2696        }
2697      };
2698
2699      frame.setAttribute("src", url);
2700
2701      if (typeof modal.showModal === "function") {
2702        lockPageScroll();
2703        modal.showModal();
2704        modal.scrollTop = 0;
2705      } else {
2706        window.location.href = url;
2707      }
2708    }
2709
2710    triggers.forEach((trigger) => {
2711      trigger.addEventListener("click", (event) => {
2712        const href = trigger.getAttribute("href");
2713        const url = buildLeadFormUrl(trigger);
2714
2715        if (!href || href === "#") {
2716          return;
2717        }
2718
2719        event.preventDefault();
2720        event.stopPropagation();
2721        event.stopImmediatePropagation();
2722        openModal(url);
2723      });
2724    });
2725  }
2726
2727  initializeDropdown();
2728  initializeLeadFormModal();
2729}
2730
2731(async function locationPopupUS() {
2732  const markup = `<dialog class="location-detection-popup">
2733  <div class="location-detection">
2734  <p class="title">We can see you're in the United States.</p>
2735  <p>Do you want to visit the Bracco US site?</p>
2736  <div class="button-wrapper">
2737  <dc-login-button size="medium" language="en" loginClientId="" redirectUri="" state=""></dc-login-button>
2738  <a data-theme="transparent" data-color="blue" class="link cancel" href="#">No thanks</a>
2739  <a data-theme="fill" data-color="blue" class="link" href="https://www.bracco.com/en-us">Take me there</a>
2740  </div>
2741  </div>
2742  </dialog>`;
2743
2744  try {
2745    // prevent multi call.
2746    const hasLocationPopup = document.querySelector(
2747      ".location-detection-popup",
2748    );
2749    if (hasLocationPopup) {
2750      return;
2751    }
2752    // Fetch the locationCode
2753    const fetchedLocationCode = await fetchLocationCode();
2754    // don't show popup if already on US site
2755    if (
2756      document.documentElement.lang != "en-us" &&
2757      fetchedLocationCode === "en-us" &&
2758      sessionStorage.getItem("locationPopupUSDisplayed") === null
2759    ) {
2760      // insert markup
2761      document
2762        .querySelector(".page-main")
2763        .insertAdjacentHTML("beforeend", markup);
2764      // Show the location popup
2765      const locationPopup = document.querySelector(".location-detection-popup");
2766      locationPopup.showModal();
2767
2768      locationPopup.querySelectorAll("a").forEach((link) => {
2769        link.addEventListener("click", function (e) {
2770          e.preventDefault();
2771          // Save data to sessionStorage
2772          sessionStorage.setItem("locationPopupUSDisplayed", "true");
2773          if (link.classList.contains("cancel")) {
2774            locationPopup.close();
2775          } else {
2776            window.location.href =
2777              drupalSettings.pathToProductUs === null
2778                ? drupalSettings.pathToHomepageUs
2779                : drupalSettings.pathToProductUs;
2780          }
2781        });
2782      });
2783      locationPopup.addEventListener("close", function () {
2784        sessionStorage.getItem("locationPopupUSDisplayed") === null &&
2785          sessionStorage.setItem("locationPopupUSDisplayed", "true");
2786      });
2787    }
2788  } catch (error) {
2789    console.error("Error presetting language:", error);
2790  }
2791})();
2792
2793(async function htmlPopup() {
2794  try {
2795    const htmlPopup = document.querySelector(".block-html-popup");
2796    if (!htmlPopup) {
2797      return;
2798    }
2799
2800    const getBackgroundImageUrl = (element) => {
2801      const backgroundImage = window
2802        .getComputedStyle(element)
2803        .getPropertyValue("background-image");
2804      const match = backgroundImage.match(/^url\(["']?(.*?)["']?\)$/);
2805      return match ? match[1] : "";
2806    };
2807
2808    const applyPopupSizeFromImage = (image) => {
2809      if (!image.naturalWidth || !image.naturalHeight) {
2810        return;
2811      }
2812
2813      const rootFontSize =
2814        parseFloat(
2815          window.getComputedStyle(document.documentElement).fontSize,
2816        ) || 16;
2817      const maxWidth = Math.min(window.innerWidth * 0.92, 48 * rootFontSize);
2818      const maxHeight = Math.min(window.innerHeight * 0.88, 52 * rootFontSize);
2819      const ratio = image.naturalWidth / image.naturalHeight;
2820
2821      let popupWidth = Math.floor(maxWidth);
2822      let popupHeight = Math.floor(popupWidth / ratio);
2823
2824      if (popupHeight > maxHeight) {
2825        popupHeight = Math.floor(maxHeight);
2826        popupWidth = Math.floor(popupHeight * ratio);
2827      }
2828
2829      htmlPopup.style.setProperty("--popup-dynamic-width", `${popupWidth}px`);
2830      htmlPopup.style.setProperty("--popup-dynamic-height", `${popupHeight}px`);
2831    };
2832
2833    const syncPopupSizeWithImage = () => {
2834      const imageUrl = getBackgroundImageUrl(htmlPopup);
2835      if (!imageUrl) {
2836        htmlPopup.style.removeProperty("--popup-dynamic-width");
2837        htmlPopup.style.removeProperty("--popup-dynamic-height");
2838        return;
2839      }
2840
2841      const img = new Image();
2842      img.onload = () => applyPopupSizeFromImage(img);
2843      img.src = imageUrl;
2844    };
2845
2846    syncPopupSizeWithImage();
2847
2848    let popupResizeTimer;
2849    window.addEventListener("resize", () => {
2850      clearTimeout(popupResizeTimer);
2851      popupResizeTimer = setTimeout(syncPopupSizeWithImage, 120);
2852    });
2853
2854    htmlPopup.addEventListener("click", function (event) {
2855      const popupBounds = htmlPopup.getBoundingClientRect();
2856      const clickedOutsidePopup =
2857        event.clientX < popupBounds.left ||
2858        event.clientX > popupBounds.right ||
2859        event.clientY < popupBounds.top ||
2860        event.clientY > popupBounds.bottom;
2861
2862      if (clickedOutsidePopup) {
2863        htmlPopup.close();
2864      }
2865    });
2866
2867    setTimeout(function () {
2868      htmlPopup.showModal();
2869    }, 1000);
2870  } catch (error) {
2871    console.error("Error showing html popup:", error);
2872  }
2873})();
2874
2875// Hero highlight ticker: auto-scroll text only when it overflows.
2876function initHeroHighlightTicker() {
2877  const textContainers = document.querySelectorAll(
2878    ".block-hero-highlight .news-strip__text, .layout__region--hero-highlight .news-strip__text",
2879  );
2880
2881  textContainers.forEach((container) => {
2882    let track = container.querySelector(".news-strip__text-track");
2883    let mainText = container.querySelector(".news-strip__text-main");
2884
2885    if (!track || !mainText) {
2886      track = document.createElement("div");
2887      track.className = "news-strip__text-track";
2888
2889      mainText = document.createElement("div");
2890      mainText.className = "news-strip__text-main";
2891
2892      while (container.firstChild) {
2893        mainText.appendChild(container.firstChild);
2894      }
2895
2896      track.appendChild(mainText);
2897      container.appendChild(track);
2898    }
2899
2900    const oldClone = track.querySelector(".news-strip__text-clone");
2901    if (oldClone) {
2902      oldClone.remove();
2903    }
2904
2905    track.style.animation = "none";
2906    track.style.transform = "translateX(0)";
2907
2908    // Skip hidden containers; ticker will be re-initialized on load/resize.
2909    if (container.clientWidth < 8) {
2910      return;
2911    }
2912
2913    const hasOverflow = mainText.scrollWidth > container.clientWidth + 2;
2914    if (!hasOverflow) {
2915      return;
2916    }
2917
2918    const clone = document.createElement("div");
2919    clone.className = "news-strip__text-clone";
2920    clone.innerHTML = mainText.innerHTML;
2921    track.appendChild(clone);
2922
2923    const gap = 48; // Matches CSS left padding used on clone.
2924    const distance = mainText.scrollWidth + gap;
2925    const speed = 40; // px/s
2926    const duration = Math.max(12, distance / speed);
2927
2928    track.style.setProperty("--news-strip-scroll-distance", `-${distance}px`);
2929    track.style.animation = `newsStripTicker ${duration}s linear infinite`;
2930  });
2931}
2932
2933if (document.readyState === "loading") {
2934  document.addEventListener("DOMContentLoaded", initHeroHighlightTicker);
2935} else {
2936  initHeroHighlightTicker();
2937}
2938
2939let heroHighlightResizeTimer;
2940window.addEventListener("resize", () => {
2941  clearTimeout(heroHighlightResizeTimer);
2942  heroHighlightResizeTimer = setTimeout(initHeroHighlightTicker, 150);
2943});
2944
2945window.addEventListener("load", initHeroHighlightTicker);
2946
2947// Dosage Calculator
2948function initDosageCalculator() {
2949  if (document.querySelector(".block-dosage-calculator")) {
2950    const volumeMatrix = [
2951      {
2952        volume: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],
2953        quantity: [
2954          0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0,
2955        ],
2956      },
2957      {
2958        volume: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],
2959        quantity: [
2960          1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0,
2961          14.0,
2962        ],
2963      },
2964      {
2965        volume: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28],
2966        quantity: [
2967          1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0,
2968          14.0,
2969        ],
2970      },
2971    ];
2972
2973    function calculate() {
2974      const value = parseFloat(document.getElementById("inputValue").value);
2975      const errorMsg = document.getElementById("error-message");
2976      errorMsg.textContent = "";
2977
2978      if (value >= 10 && value <= 140) {
2979        const index = (value - 10) / 10;
2980        const lowerIndex = Math.floor(index);
2981        const upperIndex = Math.ceil(index);
2982        const t = index - lowerIndex;
2983
2984        volumeMatrix.forEach((row, i) => {
2985          const volLower = row.volume[lowerIndex];
2986          const volUpper = row.volume[upperIndex];
2987          const qtyLower = row.quantity[lowerIndex];
2988          const qtyUpper = row.quantity[upperIndex];
2989
2990          const interpolatedVol = volLower + (volUpper - volLower) * t;
2991          const interpolatedQty = qtyLower + (qtyUpper - qtyLower) * t;
2992
2993          // Volume formatting
2994          const formattedVol =
2995            interpolatedVol % 1 === 0
2996              ? interpolatedVol.toFixed(0) // show as integer
2997              : interpolatedVol.toFixed(2); // show 2 decimals
2998
2999          // Quantity formatting
3000          const formattedQty =
3001            interpolatedQty % 1 === 0
3002              ? interpolatedQty.toFixed(1) // show 1 decimal (e.g., 2.0)
3003              : interpolatedQty.toFixed(2); // show 1 decimal (e.g., 2.3)
3004
3005          document.getElementById(`volume${i + 1}`).textContent = formattedVol;
3006          document.getElementById(`quantity${i + 1}`).textContent =
3007            formattedQty;
3008        });
3009      } else {
3010        clearValues();
3011        errorMsg.textContent = "Please enter a number between 10 and 140.";
3012      }
3013    }
3014
3015    function clearResults() {
3016      document.getElementById("inputValue").value = "";
3017      clearValues();
3018      document.getElementById("error-message").textContent = "";
3019    }
3020
3021    function clearValues() {
3022      for (let i = 1; i <= 3; i++) {
3023        document.getElementById(`volume${i}`).textContent = "";
3024        document.getElementById(`quantity${i}`).textContent = "";
3025      }
3026    }
3027
3028    const calculateBtn = document.getElementById("calculateBtn");
3029    const clearBtn = document.getElementById("clearBtn");
3030    const inputField = document.getElementById("inputValue");
3031    const rows = document.querySelectorAll(".row");
3032
3033    if (calculateBtn) calculateBtn.addEventListener("click", calculate);
3034    if (clearBtn) clearBtn.addEventListener("click", clearResults);
3035    if (inputField) {
3036      inputField.addEventListener("input", () => {
3037        clearValues();
3038      });
3039    }
3040
3041    const observer = new ResizeObserver((entries) => {
3042      entries.forEach((entry) => {
3043        const row = entry.target;
3044        const height = entry.contentRect.height;
3045
3046        row.style.setProperty("--row-height", `${height}px`);
3047        if (rows.length > 0) {
3048          const firstRowHeight = rows[0].offsetHeight;
3049
3050          rows.forEach((row, index) => {
3051            const height = row.offsetHeight;
3052            const textWrappers = row.querySelectorAll(".text-wrapper");
3053
3054            row.style.setProperty("--row-height", `${height}px`);
3055
3056            if (index !== 0) {
3057              row.style.setProperty(
3058                "--row-height-first",
3059                `${firstRowHeight}px`,
3060              );
3061            }
3062
3063            textWrappers.forEach((wrapper) => {
3064              const wrapperHeight = wrapper.offsetHeight;
3065              wrapper.style.setProperty(
3066                "--text-wrapper-height",
3067                `${wrapperHeight}px`,
3068              );
3069              row.style.setProperty(
3070                `--text-wrapper-height`,
3071                `${wrapperHeight}px`,
3072              );
3073            });
3074          });
3075        }
3076      });
3077    });
3078
3079    if (rows) rows.forEach((row) => observer.observe(row));
3080  }
3081}
3082initDosageCalculator();
3083
3084let mobileView = false;
3085
3086window.addEventListener("mediaMobile", () => {
3087  mobileView = true;
3088  collapseMenu();
3089});
3090window.addEventListener("mediaDesktop", () => {
3091  mobileView = false;
3092  collapseMenu();
3093  // initMainNavMenu();
3094});
3095
3096window.addEventListener("resize", () => {
3097  breakpointHandler();
3098  // -------------------
3099  // Header height
3100  // -------------------
3101  // if (window.innerWidth > 1024) {
3102  //   mobileView = false;
3103  // } else {
3104  //   mobileView = true;
3105  // }
3106  // setMenuHeight(mobileView);
3107});
3108
3109window.addEventListener("load", () => {
3110  breakpointHandler();
3111  initDosageCalculator();
3112  // -------------------
3113  // Header height
3114  // -------------------
3115  // setMenuHeight();
3116});

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.