PageSourceSearch

https://pointsoflight.org/wp-content/themes/points-of-light/assets/js/script.js?ver=1789987687

js pointsoflight.org collected 2026-09-24 08:51:10 UTC 37,163 bytes, 1,152 lines download raw bytes

1"use strict";
2
3(function ($, win, doc) {
4  if (!$ || typeof $.fn.slick !== "function") return;
5  const reduceMotion =
6    typeof win.matchMedia === "function" &&
7    win.matchMedia("(prefers-reduced-motion: reduce)").matches;
8  const TAB_SYNC_BP = 1199;
9
10  /** @param {number} width */
11  function isDesktopTabs(width) {
12    return width > TAB_SYNC_BP;
13  }
14
15  /** @param {Element | null | undefined} tabEl */
16  function scrollTabStripToTabLeft(tabEl) {
17    if (!tabEl || typeof tabEl.closest !== "function") return;
18    var strip = tabEl.closest(".js-pol-tabs-nav");
19    if (!strip || strip.scrollWidth <= strip.clientWidth + 1) return;
20
21    var tabRect = tabEl.getBoundingClientRect();
22    var stripRect = strip.getBoundingClientRect();
23    var padLeft =
24      parseFloat(win.getComputedStyle(strip).paddingLeft || "0") || 0;
25
26    var delta = tabRect.left - stripRect.left - padLeft;
27    var target = strip.scrollLeft + delta;
28    var maxScroll = Math.max(0, strip.scrollWidth - strip.clientWidth);
29    target = Math.max(0, Math.min(target, maxScroll));
30
31    strip.scrollTo({
32      left: target,
33      behavior: reduceMotion ? "auto" : "smooth",
34    });
35  }
36
37  /** @param {JQuery<Element>} $slider */
38  function initNestedTabSlider($slider) {
39    if (!$slider.length || $slider.attr("data-nested") !== "1") return;
40
41    var $wrap = $slider.closest(".tab-sliders--nested");
42    var $prev = $wrap.find(".js-pol-tab-slider-prev");
43    var $next = $wrap.find(".js-pol-tab-slider-next");
44
45    if ($slider.hasClass("slick-initialized")) {
46      $slider.slick("setPosition");
47      return;
48    }
49
50    $slider.slick({
51      slidesToShow: 1,
52      slidesToScroll: 1,
53      infinite: false,
54      speed: reduceMotion ? 0 : 280,
55      arrows: Boolean($prev.length && $next.length),
56      appendArrows: $wrap.find(".tab-sliders__nav--nested").length
57        ? $wrap.find(".tab-sliders__nav--nested")
58        : undefined,
59      prevArrow: $prev.length ? $prev : undefined,
60      nextArrow: $next.length ? $next : undefined,
61      swipe: false,
62      draggable: false,
63    });
64  }
65
66  /** @param {JQuery<Element>} $root */
67  function syncNestedTabSliders($root) {
68    var desktop = isDesktopTabs(win.innerWidth);
69
70    $root.find('.js-pol-tab-slider[data-nested="1"]').each(function () {
71      var $slider = $(this);
72      if (!desktop) {
73        if ($slider.hasClass("slick-initialized")) {
74          $slider.slick("unslick");
75        }
76        return;
77      }
78      initNestedTabSlider($slider);
79    });
80  }
81
82  /** @param {JQuery<Element>} $root */
83  function initPolTabs($root) {
84    var $nav = $root.find(".js-pol-tabs-nav");
85    var $content = $root.find(".js-pol-tabs-for");
86    var $tabs = $root.find('[role="tab"]');
87    var $panels = $content.find('[role="tabpanel"]');
88    var $arrowHost = $content.siblings(".tab-sliders__nav").first();
89    var $prev = $root.find(".js-pol-tabs-prev");
90    var $next = $root.find(".js-pol-tabs-next");
91    var resizeTimer = null;
92
93    if (!$nav.length || !$content.length || !$tabs.length) return;
94
95    /** @param {number} index */
96    function updateAria(index) {
97      $tabs.each(function (i) {
98        var selected = i === index;
99        $(this)
100          .attr("aria-selected", selected ? "true" : "false")
101          .attr("tabindex", selected ? "0" : "-1");
102      });
103      $panels.each(function (i) {
104        $(this).attr("aria-hidden", i === index ? "false" : "true");
105      });
106    }
107
108    /** @param {number} index */
109    /** @param {boolean} [animate] */
110    function goToSlide(index, animate) {
111      if (!$content.hasClass("slick-initialized")) return;
112      var max = $tabs.length - 1;
113      var next = Math.max(0, Math.min(index, max));
114      $content.slick("slickGoTo", next, animate === false);
115      updateAria(next);
116      if (isDesktopTabs(win.innerWidth)) {
117        var $panel = $($panels.get(next));
118        initNestedTabSlider($panel.find('.js-pol-tab-slider[data-nested="1"]'));
119      }
120    }
121
122    function initContentSlider() {
123      if ($content.hasClass("slick-initialized")) return;
124
125      var mobile = !isDesktopTabs(win.innerWidth);
126
127      $content.slick({
128        slidesToShow: 1,
129        slidesToScroll: 1,
130        infinite: false,
131        speed: reduceMotion ? 0 : 280,
132        arrows: mobile && Boolean($prev.length && $next.length),
133        appendArrows: $arrowHost.length ? $arrowHost : undefined,
134        prevArrow: $prev.length ? $prev : undefined,
135        nextArrow: $next.length ? $next : undefined,
136        swipe: mobile,
137        draggable: mobile,
138      });
139
140      $content.on("afterChange.polTabs", function (_evt, _slick, currentSlide) {
141        updateAria(currentSlide);
142        var tab = $tabs.get(currentSlide);
143        if (tab) scrollTabStripToTabLeft(tab);
144        if (isDesktopTabs(win.innerWidth)) {
145          var $panel = $($panels.get(currentSlide));
146          initNestedTabSlider($panel.find('.js-pol-tab-slider[data-nested="1"]'));
147        }
148      });
149    }
150
151    function syncMode() {
152      initContentSlider();
153
154      if (!$content.hasClass("slick-initialized")) return;
155
156      var desktop = isDesktopTabs(win.innerWidth);
157      $content.slick(
158        "slickSetOption",
159        "arrows",
160        !desktop && Boolean($prev.length && $next.length),
161        true
162      );
163      $content.slick("slickSetOption", "swipe", !desktop, false);
164      $content.slick("slickSetOption", "draggable", !desktop, false);
165      $content.slick("setPosition");
166      syncNestedTabSliders($root);
167
168      if (desktop) {
169        var activeIndex = $tabs.index(
170          $tabs.filter('[aria-selected="true"]').first()
171        );
172        if (activeIndex < 0) activeIndex = 0;
173        var $panel = $($panels.get(activeIndex));
174        initNestedTabSlider($panel.find('.js-pol-tab-slider[data-nested="1"]'));
175      }
176    }
177
178    $tabs.on("click.polTabs", function () {
179      var $btn = $(this);
180      var index = $tabs.index(this);
181      if (
182        $btn.attr("aria-selected") === "true" &&
183        isDesktopTabs(win.innerWidth)
184      ) {
185        return;
186      }
187
188      goToSlide(index);
189      win.requestAnimationFrame(function () {
190        scrollTabStripToTabLeft($btn[0]);
191      });
192    });
193
194    $tabs.on("keydown.polTabs", function (evt) {
195      var i = $tabs.index(this);
196      var max = $tabs.length - 1;
197      var nextIndex = null;
198      if (evt.key === "ArrowRight" || evt.key === "ArrowDown")
199        nextIndex = Math.min(max, i + 1);
200      else if (evt.key === "ArrowLeft" || evt.key === "ArrowUp")
201        nextIndex = Math.max(0, i - 1);
202      else if (evt.key === "Home") nextIndex = 0;
203      else if (evt.key === "End") nextIndex = max;
204
205      if (nextIndex === null || nextIndex === i) return;
206      evt.preventDefault();
207      $($tabs[nextIndex]).focus().trigger("click");
208    });
209
210    syncMode();
211
212    $(win).on("resize.polTabs", function () {
213      win.clearTimeout(resizeTimer);
214      resizeTimer = win.setTimeout(syncMode, 150);
215    });
216  }
217
218  $(function () {
219    $(".js-pol-tabs").each(function () {
220      initPolTabs($(this));
221    });
222  });
223})(typeof jQuery !== "undefined" ? jQuery : null, window, document);
224
225(function ($, win) {
226  if (!$ || typeof $.fn.slick !== "function") return;
227
228  var LOGO_SLIDER_BP_TABLET = 751;
229  var LOGO_SLIDER_BP_DESKTOP = 992;
230  var logoSliderInstanceCount = 0;
231  var reduceMotion =
232    typeof win.matchMedia === "function" &&
233    win.matchMedia("(prefers-reduced-motion: reduce)").matches;
234
235  /** @param {number} width */
236  function getLogoSlidesToShow(width) {
237    if (width < LOGO_SLIDER_BP_TABLET) return 1;
238    if (width < LOGO_SLIDER_BP_DESKTOP) return 3;
239    return 4;
240  }
241
242  /** @param {number} width */
243  function isDesktopLogoViewport(width) {
244    return width >= LOGO_SLIDER_BP_DESKTOP;
245  }
246
247  /** @param {JQuery<Element>} $root */
248  function initThreeColDisplayLogo($root) {
249    var $track = $root.find(".js-three-col-logo-grid");
250    var $prev = $root.find(".js-three-col-logo-prev");
251    var $next = $root.find(".js-three-col-logo-next");
252    var $nav = $root.find(".three-col-display__logo-nav");
253    var slideCount = $root.find(".three-col-display__column").length;
254    var autoplay =
255      String($root.data("logo-autoplay")) === "1" ||
256      $root.data("logo-autoplay") === 1;
257    var resizeTimer = null;
258    var instanceId = ++logoSliderInstanceCount;
259    var resizeNs = ".threeColLogo-" + instanceId;
260
261    if (!$track.length) return;
262
263    function mountSlider() {
264      if ($track.hasClass("slick-initialized")) return;
265
266      $track.slick({
267        slidesToShow: 4,
268        slidesToScroll: 1,
269        infinite: false,
270        speed: reduceMotion ? 0 : 280,
271        arrows: Boolean($prev.length && $next.length),
272        appendArrows: $nav.length ? $nav : undefined,
273        prevArrow: $prev.length ? $prev : undefined,
274        nextArrow: $next.length ? $next : undefined,
275        swipe: true,
276        draggable: true,
277        responsive: [
278          {
279            breakpoint: LOGO_SLIDER_BP_DESKTOP,
280            settings: {
281              slidesToShow: 3,
282              slidesToScroll: 1,
283            },
284          },
285          {
286            breakpoint: LOGO_SLIDER_BP_TABLET,
287            settings: {
288              slidesToShow: 1,
289              slidesToScroll: 1,
290            },
291          },
292        ],
293      });
294    }
295
296    function unmountSlider() {
297      if (!$track.hasClass("slick-initialized")) return;
298      $track.slick("unslick");
299    }
300
301    function syncMode() {
302      mountSlider();
303
304      if (!$track.hasClass("slick-initialized")) return;
305
306      var slidesToShow = getLogoSlidesToShow(win.innerWidth);
307      var needsInteraction = slideCount > slidesToShow;
308      var hasArrows = slideCount > 1 && Boolean($prev.length && $next.length);
309      var shouldAutoplay =
310        autoplay &&
311        !reduceMotion &&
312        needsInteraction &&
313        isDesktopLogoViewport(win.innerWidth);
314
315      $root.toggleClass("is-logo-nav-needed", needsInteraction && hasArrows);
316      $track.slick("slickSetOption", "arrows", needsInteraction && hasArrows, true);
317      $track.slick("slickSetOption", "swipe", needsInteraction, false);
318      $track.slick("slickSetOption", "draggable", needsInteraction, false);
319      $track.slick("slickSetOption", "infinite", shouldAutoplay, true);
320      $track.slick("slickSetOption", "autoplay", shouldAutoplay, true);
321      $track.slick("slickSetOption", "autoplaySpeed", 5000, false);
322      $track.slick("slickSetOption", "pauseOnHover", true, false);
323      $track.slick("slickSetOption", "pauseOnFocus", true, false);
324      $track.slick("setPosition");
325
326      if (shouldAutoplay) {
327        $track.slick("slickPlay");
328      } else {
329        $track.slick("slickPause");
330      }
331    }
332
333    syncMode();
334
335    $(win).on("resize" + resizeNs, function () {
336      win.clearTimeout(resizeTimer);
337      resizeTimer = win.setTimeout(syncMode, 150);
338    });
339
340    $root.data("threeColLogoCleanup", function () {
341      $(win).off(resizeNs);
342      if (resizeTimer) win.clearTimeout(resizeTimer);
343      $root.removeClass("is-logo-nav-needed");
344      unmountSlider();
345    });
346  }
347
348  $(function () {
349    $(".js-three-col-display--logo").each(function () {
350      initThreeColDisplayLogo($(this));
351    });
352  });
353})(typeof jQuery !== "undefined" ? jQuery : null, window);
354
355(function ($, win) {
356  if (!$) return;
357
358  var reduceMotion =
359    typeof win.matchMedia === "function" &&
360    win.matchMedia("(prefers-reduced-motion: reduce)").matches;
361
362  $(function () {
363    $(".js-drawers-accordion").each(function () {
364      var $root = $(this);
365      var $items = $root.find(".drawers__item");
366      var duration = reduceMotion ? 0 : 300;
367
368      $root.find(".drawers__trigger").on("click", function () {
369        var $btn = $(this);
370        var $item = $btn.closest(".drawers__item");
371        var $panel = $item.find(".drawers__panel");
372        var isOpen = $btn.attr("aria-expanded") === "true";
373
374        if (!$panel.length) return;
375
376        if (isOpen) {
377          $btn.attr("aria-expanded", "false");
378          $item.removeClass("drawers__item--open");
379          $panel.stop(true, true).slideUp(duration, function () {
380            $panel.attr("hidden", "");
381          });
382          return;
383        }
384
385        $items.not($item).each(function () {
386          var $other = $(this);
387          var $otherBtn = $other.find(".drawers__trigger");
388          var $otherPanel = $other.find(".drawers__panel");
389
390          if ($otherBtn.attr("aria-expanded") !== "true") {
391            return;
392          }
393
394          $otherBtn.attr("aria-expanded", "false");
395          $other.removeClass("drawers__item--open");
396          $otherPanel.stop(true, true).slideUp(duration, function () {
397            $otherPanel.attr("hidden", "");
398          });
399        });
400
401        $btn.attr("aria-expanded", "true");
402        $item.addClass("drawers__item--open");
403        $panel.removeAttr("hidden");
404        $panel.stop(true, true).slideDown(duration);
405      });
406    });
407  });
408})(typeof jQuery !== "undefined" ? jQuery : null, window);
409
410(function ($, win, doc) {
411  if (!$) return;
412
413  function polBiosIsEditorContext() {
414    if (doc.body.classList.contains("block-editor-page")) {
415      return true;
416    }
417    if (doc.body.classList.contains("wp-admin")) {
418      return true;
419    }
420    if ($(".bios--editor-preview").length) {
421      return true;
422    }
423    return false;
424  }
425
426  /** @param {JQuery<Element>} $root */
427  function closeBioDialog($root) {
428    var $dialog = $root.find(".js-bios-dialog");
429    var $overlay = $root.find(".js-bios-overlay");
430
431    $root.removeClass("bios--open");
432    $dialog.attr("hidden", "");
433    $overlay.attr("hidden", "").attr("aria-hidden", "true");
434    doc.body.classList.remove("bios-modal-open");
435    $(doc).off("keydown.bios");
436
437    var $trigger = $root.data("bios-last-trigger");
438    if ($trigger && $trigger.length) {
439      $trigger.trigger("focus");
440    }
441  }
442
443  /** @param {JQuery<Element>} $root @param {JQuery<Element>} $card */
444  function openBioDialog($root, $card) {
445    var $dialog = $root.find(".js-bios-dialog");
446    var $overlay = $root.find(".js-bios-overlay");
447    var name = $card.data("bio-name") || "";
448    var title = $card.data("bio-title") || "";
449    var organization = $card.data("bio-organization") || "";
450    var image = $card.data("bio-image") || "";
451    var alt = $card.data("bio-alt") || "";
452    var linkedin = $card.data("bio-linkedin") || "";
453    var email = $card.data("bio-email") || "";
454    var bioId = $card.data("bio-id");
455
456    $root.find(".js-bios-dialog-name").text(name);
457
458    var $dialogTitle = $root.find(".js-bios-dialog-title");
459    if (title) {
460      $dialogTitle.text(title).removeAttr("hidden");
461    } else {
462      $dialogTitle.text("").attr("hidden", "");
463    }
464
465    var $dialogOrg = $root.find(".js-bios-dialog-organization");
466    if (organization) {
467      $dialogOrg.text(organization).removeAttr("hidden");
468    } else {
469      $dialogOrg.text("").attr("hidden", "");
470    }
471
472    var $copy = $root.find(".js-bios-dialog-copy");
473    var $bioSource = bioId ? $root.find("#bios-bio-" + bioId) : $();
474    if ($bioSource.length) {
475      $copy.html($bioSource.html());
476    } else {
477      $copy.empty();
478    }
479
480    var $photo = $root.find(".js-bios-dialog-photo");
481    var $photoWrap = $photo.closest(".bios__dialog-media");
482    if ($photo.length && image) {
483      $photo.attr("src", image).attr("alt", alt);
484      $photoWrap.removeAttr("hidden");
485    } else if ($photoWrap.length) {
486      $photo.attr("src", "").attr("alt", "");
487      $photoWrap.attr("hidden", "");
488    }
489
490    var $social = $root.find(".js-bios-dialog-social");
491    var $socialList = $social.find(".bios__social-list");
492    var emailIcon = $social.data("email-icon") || "";
493    var linkedinIcon = $social.data("linkedin-icon") || "";
494
495    if (email) {
496      var $emailItem = $socialList.find(".js-bios-social-email");
497      if (!$emailItem.length) {
498        $socialList.append(
499          '<li class="bios__social-item js-bios-social-email">' +
500            '<a class="bios__social-link" href="mailto:' +
501            email +
502            '" title="Email ' +
503            name +
504            '">' +
505            '<figure class="bios__social-icon" aria-hidden="true">' +
506            '<img src="' +
507            emailIcon +
508            '" alt="" width="21" height="17" decoding="async" />' +
509            "</figure></a></li>"
510        );
511      } else {
512        $emailItem
513          .find("a")
514          .attr("href", "mailto:" + email)
515          .attr("title", "Email " + name);
516      }
517    } else {
518      $socialList.find(".js-bios-social-email").remove();
519    }
520
521    if (linkedin) {
522      var $linkedinItem = $socialList.find(".js-bios-social-linkedin");
523      if (!$linkedinItem.length) {
524        $socialList.append(
525          '<li class="bios__social-item js-bios-social-linkedin">' +
526            '<a class="bios__social-link" href="' +
527            linkedin +
528            '" target="_blank" rel="noopener noreferrer" title="View ' +
529            name +
530            ' on LinkedIn">' +
531            '<figure class="bios__social-icon" aria-hidden="true">' +
532            '<img src="' +
533            linkedinIcon +
534            '" alt="" width="18" height="18" decoding="async" />' +
535            "</figure></a></li>"
536        );
537      } else {
538        $linkedinItem
539          .find("a")
540          .attr("href", linkedin)
541          .attr("title", "View " + name + " on LinkedIn");
542      }
543    } else {
544      $socialList.find(".js-bios-social-linkedin").remove();
545    }
546
547    if ($socialList.children().length) {
548      $social.removeAttr("hidden");
549    } else {
550      $social.attr("hidden", "");
551    }
552
553    $root.data("bios-last-trigger", $card);
554    $root.addClass("bios--open");
555    $overlay.removeAttr("hidden").attr("aria-hidden", "false");
556    $dialog.removeAttr("hidden");
557    doc.body.classList.add("bios-modal-open");
558
559    $root.find(".js-bios-close").trigger("focus");
560
561    $(doc).on("keydown.bios", function (evt) {
562      if (evt.key === "Escape") {
563        closeBioDialog($root);
564      }
565    });
566  }
567
568  $(function () {
569    if (polBiosIsEditorContext()) {
570      return;
571    }
572
573    $(".js-bios").each(function () {
574      var $root = $(this);
575
576      if ($root.hasClass("bios--editor-preview")) {
577        return;
578      }
579
580      $root.on("click", ".bios__card", function (evt) {
581        evt.preventDefault();
582        openBioDialog($root, $(this));
583      });
584
585      $root.on("click", ".js-bios-close", function () {
586        closeBioDialog($root);
587      });
588
589      $root.on("click", ".js-bios-overlay", function () {
590        closeBioDialog($root);
591      });
592
593      $root.on("click", ".js-bios-dialog", function (evt) {
594        evt.stopPropagation();
595      });
596    });
597  });
598})(typeof jQuery !== "undefined" ? jQuery : null, window, document);
599
600(function ($, win, doc) {
601  if (!$ || typeof $.fn.slick !== "function") return;
602
603  var TIMELINE_DESKTOP_BP = 1200;
604  var timelineInstanceCount = 0;
605  var reduceMotion =
606    typeof win.matchMedia === "function" &&
607    win.matchMedia("(prefers-reduced-motion: reduce)").matches;
608
609  /** @param {number} width */
610  function isDesktopTimeline(width) {
611    return width >= TIMELINE_DESKTOP_BP;
612  }
613
614  /** @param {JQuery<Element>} $root */
615  function destroyTimeline($root) {
616    var cleanup = $root.data("timeline-cleanup");
617    if (typeof cleanup === "function") {
618      cleanup();
619      $root.removeData("timeline-cleanup");
620    }
621  }
622
623  /** @param {JQuery<Element>} $root */
624  function initTimeline($root) {
625    destroyTimeline($root);
626    var $years = $root.find(".js-timeline-year");
627    var $panels = $root
628      .find(".js-timeline-panel")
629      .not('[data-year-index="all"]');
630    var $filter = $root.find(".js-timeline-filter");
631    var $filterTrigger = $root.find(".js-timeline-filter-trigger");
632    var $filterMenu = $root.find(".js-timeline-filter-menu");
633    var $filterValue = $root.find(".js-timeline-filter-value");
634    var $filterCloseValue = $root.find(".js-timeline-filter-close-value");
635    var $filterOptions = $root.find(".js-timeline-filter-option");
636    var $filterClose = $root.find(".js-timeline-filter-close");
637    /** @type {"all" | number} */
638    var filterMode = "all";
639    var activeIndex = 0;
640    var chainAnimating = false;
641    var resizeTimer = null;
642    var chainFadeMs = reduceMotion ? 0 : 280;
643    var instanceId = ++timelineInstanceCount;
644    var filterNs = ".timelineFilter-" + instanceId;
645    var resizeNs = ".timeline-" + instanceId;
646    var chainNs = ".timelineChain-" + instanceId;
647    var allLabel =
648      ($root.attr("data-timeline-all-label") || "All Years").trim() ||
649      "All Years";
650
651    if (!$panels.length) return;
652
653    function isAllMode() {
654      return filterMode === "all" && !isDesktopTimeline(win.innerWidth);
655    }
656
657    function syncFilterClass() {
658      $root.toggleClass("timeline--filter-all", isAllMode());
659    }
660
661    /** @param {string} label @param {number} activeYearIndex */
662    function updateFilterUi(label, activeYearIndex) {
663      if ($filterValue.length) $filterValue.text(label);
664      if ($filterCloseValue.length) $filterCloseValue.text(label);
665      $filterOptions.each(function () {
666        var $opt = $(this);
667        var raw = $opt.attr("data-year-index");
668        var selected = isAllMode()
669          ? raw === "all"
670          : raw !== "all" && Number(raw) === activeYearIndex;
671        $opt.attr("aria-current", selected ? "true" : "false");
672      });
673    }
674
675    /** @param {JQuery<Element>} $panel */
676    function initPanelSlider($panel) {
677      var $track = $panel.find(".js-timeline-text");
678      var $prev = $panel.find(".js-timeline-prev");
679      var $next = $panel.find(".js-timeline-next");
680
681      if (!$track.length || $track.hasClass("slick-initialized")) return;
682
683      $track.slick({
684        slidesToShow: 1,
685        slidesToScroll: 1,
686        infinite: false,
687        speed: reduceMotion ? 0 : 280,
688        arrows: Boolean($prev.length && $next.length),
689        appendArrows: $panel.find(".timeline__arrows"),
690        prevArrow: $prev.length ? $prev : undefined,
691        nextArrow: $next.length ? $next : undefined,
692        swipe: !isDesktopTimeline(win.innerWidth),
693        draggable: !isDesktopTimeline(win.innerWidth),
694      });
695    }
696
697    /** @param {JQuery<Element>} $panel */
698    function syncPanelSlider($panel) {
699      var $track = $panel.find(".js-timeline-text");
700      if (!$track.hasClass("slick-initialized")) return;
701
702      var desktop = isDesktopTimeline(win.innerWidth);
703      $track.slick("slickSetOption", "swipe", !desktop, false);
704      $track.slick("slickSetOption", "draggable", !desktop, false);
705      $track.slick("setPosition");
706    }
707
708    /** @param {JQuery<Element>} $panel */
709    function activatePanelSlider($panel) {
710      var $track = $panel.find(".js-timeline-text");
711
712      if (!$track.length) return;
713
714      if (!$track.hasClass("slick-initialized")) {
715        initPanelSlider($panel);
716        return;
717      }
718
719      win.requestAnimationFrame(function () {
720        syncPanelSlider($panel);
721        win.requestAnimationFrame(function () {
722          if ($track.hasClass("slick-initialized")) {
723            $track.slick("setPosition");
724          }
725        });
726      });
727    }
728
729    /** @param {JQuery<Element>} $panel @param {number} panelIndex */
730    function syncChainArrows($panel, panelIndex) {
731      var $prev = $panel.find(".js-timeline-prev");
732      var $next = $panel.find(".js-timeline-next");
733
734      if (!isAllMode()) {
735        $prev.removeClass("is-chain-disabled").attr("aria-disabled", "false");
736        $next.removeClass("is-chain-disabled").attr("aria-disabled", "false");
737        return;
738      }
739
740      var $track = $panel.find(".js-timeline-text");
741      if (!$track.hasClass("slick-initialized")) return;
742
743      var current = $track.slick("slickCurrentSlide");
744      var count = $track.slick("getSlick").slideCount;
745      var canPrev = panelIndex > 0 || current > 0;
746      var canNext = panelIndex < $panels.length - 1 || current < count - 1;
747
748      $prev.toggleClass("is-chain-disabled", !canPrev);
749      $next.toggleClass("is-chain-disabled", !canNext);
750      $prev.attr("aria-disabled", !canPrev ? "true" : "false");
751      $next.attr("aria-disabled", !canNext ? "true" : "false");
752    }
753
754    /** @param {number} index @param {number} [slideIndex] @param {{ animate?: boolean }} [options] */
755    function setActivePanel(index, slideIndex, options) {
756      options = options || {};
757      var next = Math.max(0, Math.min(index, $panels.length - 1));
758      var shouldAnimate =
759        options.animate &&
760        isAllMode() &&
761        chainFadeMs > 0 &&
762        next !== activeIndex &&
763        !chainAnimating;
764
765      if (shouldAnimate) {
766        chainAnimating = true;
767        var $from = $panels.eq(activeIndex);
768        $from.addClass("timeline__panel--chain-out");
769        win.setTimeout(function () {
770          $from.removeClass("timeline__panel--chain-out");
771          finishSetActivePanel(next, slideIndex, true);
772          chainAnimating = false;
773        }, chainFadeMs);
774        return;
775      }
776
777      finishSetActivePanel(next, slideIndex, false);
778    }
779
780    /** @param {number} next @param {number} [slideIndex] @param {boolean} withFadeIn */
781    function finishSetActivePanel(next, slideIndex, withFadeIn) {
782      activeIndex = next;
783
784      $years.each(function (i) {
785        $(this).attr("aria-current", i === next ? "true" : "false");
786      });
787
788      $panels.each(function (i) {
789        var $panel = $(this);
790        if (i === next) {
791          $panel.removeAttr("hidden");
792          if (withFadeIn) $panel.addClass("timeline__panel--chain-in");
793          activatePanelSlider($panel);
794        } else {
795          $panel.removeClass("timeline__panel--chain-in timeline__panel--chain-out");
796          $panel.attr("hidden", "");
797        }
798      });
799
800      if (isAllMode()) {
801        updateFilterUi(allLabel, next);
802      } else if (typeof filterMode === "number") {
803        var yearLabel = $years.eq(next).find(".timeline__year-label").text().trim();
804        if (yearLabel) updateFilterUi(yearLabel, next);
805      }
806
807      syncFilterClass();
808
809      if (typeof slideIndex === "number") {
810        var $track = $panels.eq(next).find(".js-timeline-text");
811        if ($track.hasClass("slick-initialized")) {
812          $track.slick("slickGoTo", slideIndex, withFadeIn ? false : !reduceMotion);
813        }
814      }
815
816      var $activePanel = $panels.eq(next);
817      syncChainArrows($activePanel, next);
818
819      if (withFadeIn) {
820        win.requestAnimationFrame(function () {
821          $activePanel.removeClass("timeline__panel--chain-in");
822        });
823      }
824    }
825
826    /** @param {number} index */
827    function setActiveYear(index) {
828      filterMode = index;
829      setActivePanel(index, 0, { animate: false });
830    }
831
832    /** @param {number} yearIndex @param {number} slideIndex @param {{ animate?: boolean }} [options] */
833    function goToPanelSlide(yearIndex, slideIndex, options) {
834      options = options || {};
835      if (yearIndex < 0 || yearIndex >= $panels.length || chainAnimating) return;
836
837      if (yearIndex === activeIndex) {
838        if (typeof slideIndex === "number") {
839          var $track = $panels.eq(yearIndex).find(".js-timeline-text");
840          if ($track.hasClass("slick-initialized")) {
841            $track.slick("slickGoTo", slideIndex, !reduceMotion);
842          }
843        }
844        syncChainArrows($panels.eq(yearIndex), yearIndex);
845        return;
846      }
847
848      setActivePanel(yearIndex, slideIndex, {
849        animate: options.animate !== false,
850      });
851    }
852
853    /** @param {JQuery<Element>} $panel */
854    function handleChainNext($panel) {
855      if (!isAllMode() || chainAnimating) return;
856      var idx = $panels.index($panel);
857      if (idx < 0) return;
858      var $track = $panel.find(".js-timeline-text");
859      if (!$track.hasClass("slick-initialized")) return;
860      var current = $track.slick("slickCurrentSlide");
861      var count = $track.slick("getSlick").slideCount;
862      if (current >= count - 1 && idx < $panels.length - 1) {
863        goToPanelSlide(idx + 1, 0, { animate: true });
864      }
865    }
866
867    /** @param {JQuery<Element>} $panel */
868    function handleChainPrev($panel) {
869      if (!isAllMode() || chainAnimating) return;
870      var idx = $panels.index($panel);
871      if (idx < 0) return;
872      var $track = $panel.find(".js-timeline-text");
873      if (!$track.hasClass("slick-initialized")) return;
874      var current = $track.slick("slickCurrentSlide");
875      if (current <= 0 && idx > 0) {
876        var $prevTrack = $panels.eq(idx - 1).find(".js-timeline-text");
877        var prevCount = $prevTrack.hasClass("slick-initialized")
878          ? $prevTrack.slick("getSlick").slideCount
879          : 1;
880        goToPanelSlide(idx - 1, prevCount - 1, { animate: true });
881      }
882    }
883
884    $panels.each(function (panelIndex) {
885      var $panel = $(this);
886      initPanelSlider($panel);
887      var $track = $panel.find(".js-timeline-text");
888      $track.on("afterChange" + chainNs, function () {
889        syncChainArrows($panel, panelIndex);
890      });
891      $track.on("edge" + chainNs, function (_evt, _slick, direction) {
892        if (!isAllMode() || chainAnimating) return;
893        if (direction === "left" && panelIndex < $panels.length - 1) {
894          goToPanelSlide(panelIndex + 1, 0, { animate: true });
895        } else if (direction === "right" && panelIndex > 0) {
896          var $prevTrack = $panels.eq(panelIndex - 1).find(".js-timeline-text");
897          if (!$prevTrack.hasClass("slick-initialized")) return;
898          var prevCount = $prevTrack.slick("getSlick").slideCount;
899          goToPanelSlide(panelIndex - 1, prevCount - 1, { animate: true });
900        }
901      });
902    });
903
904    $root.on("click" + chainNs, ".js-timeline-next", function () {
905      if ($(this).attr("aria-disabled") === "true") return;
906      handleChainNext($(this).closest(".js-timeline-panel"));
907    });
908
909    $root.on("click" + chainNs, ".js-timeline-prev", function () {
910      if ($(this).attr("aria-disabled") === "true") return;
911      handleChainPrev($(this).closest(".js-timeline-panel"));
912    });
913
914    function closeFilterMenu() {
915      if (!$filter.length) return;
916      $filter.removeClass("timeline__filter--open");
917      $filterTrigger.attr("aria-expanded", "false");
918      $filterMenu.attr("hidden", "");
919    }
920
921    function openFilterMenu() {
922      if (!$filter.length) return;
923      $filter.addClass("timeline__filter--open");
924      $filterTrigger.attr("aria-expanded", "true");
925      $filterMenu.removeAttr("hidden");
926    }
927
928    $years.on("click.timeline", function () {
929      var index = $years.index(this);
930      setActiveYear(index);
931      if (this.blur) this.blur();
932    });
933
934    $filterTrigger.on("click.timeline", function () {
935      if ($filterMenu.attr("hidden") !== undefined) {
936        openFilterMenu();
937      } else {
938        closeFilterMenu();
939      }
940    });
941
942    $filterOptions.on("click.timeline", function () {
943      var raw = $(this).attr("data-year-index");
944      if (raw === "all") {
945        filterMode = "all";
946        setActivePanel(0, 0, { animate: false });
947      } else {
948        var index = Number(raw);
949        if (Number.isNaN(index)) return;
950        setActiveYear(index);
951      }
952      closeFilterMenu();
953    });
954
955    $filterClose.on("click.timeline", function () {
956      closeFilterMenu();
957    });
958
959    $(doc).on("click" + filterNs, function (evt) {
960      if (!$filter.length || $filterMenu.attr("hidden") !== undefined) return;
961      if ($(evt.target).closest(".js-timeline-filter", $root[0]).length) return;
962      closeFilterMenu();
963    });
964
965    $(win).on("keydown" + filterNs, function (evt) {
966      if (evt.key === "Escape") closeFilterMenu();
967    });
968
969    $(win).on("resize" + resizeNs, function () {
970      win.clearTimeout(resizeTimer);
971      resizeTimer = win.setTimeout(function () {
972        syncFilterClass();
973        $panels.each(function () {
974          syncPanelSlider($(this));
975        });
976        syncChainArrows($panels.eq(activeIndex), activeIndex);
977      }, 150);
978    });
979
980    $root.data("timeline-cleanup", function () {
981      $years.off(".timeline");
982      $filterTrigger.off(".timeline");
983      $filterOptions.off(".timeline");
984      $filterClose.off(".timeline");
985      $root.off(chainNs);
986      $(doc).off("click" + filterNs);
987      $(win).off("keydown" + filterNs);
988      $(win).off("resize" + resizeNs);
989      if (resizeTimer) win.clearTimeout(resizeTimer);
990      $panels.each(function () {
991        var $track = $(this).find(".js-timeline-text");
992        $track.off(chainNs);
993        if ($track.hasClass("slick-initialized")) {
994          $track.slick("unslick");
995        }
996      });
997    });
998
999    filterMode = "all";
1000    setActivePanel(0, 0, { animate: false });
1001  }
1002
1003  $(function () {
1004    $(".js-timeline").each(function () {
1005      initTimeline($(this));
1006    });
1007  });
1008})(typeof jQuery !== "undefined" ? jQuery : null, window, document);
1009
1010// ─── CB10 Media carousel + video lightbox ────────────────────────────────────
1011(function ($, win, doc) {
1012  if (!$ || typeof $.fn.slick !== "function") return;
1013
1014  var reduceMotion =
1015    typeof win.matchMedia === "function" &&
1016    win.matchMedia("(prefers-reduced-motion: reduce)").matches;
1017
1018  /** @param {string} url */
1019  function getVideoEmbedUrl(url) {
1020    if (!url) return "";
1021
1022    var ytMatch = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([^&?/]+)/);
1023    if (ytMatch) {
1024      return "https://www.youtube.com/embed/" + ytMatch[1] + "?autoplay=1";
1025    }
1026
1027    var vimeoMatch = url.match(/vimeo\.com\/(\d+)/);
1028    if (vimeoMatch) {
1029      return "https://player.vimeo.com/video/" + vimeoMatch[1] + "?autoplay=1";
1030    }
1031
1032    return "";
1033  }
1034
1035  /** @param {JQuery<Element>} $root */
1036  function closeMediaLightbox($root) {
1037    var $lightbox = $root.find(".js-media-lightbox");
1038    var $iframe = $root.find(".js-media-lightbox-iframe");
1039
1040    $lightbox.attr("hidden", "");
1041    $iframe.attr("src", "");
1042    doc.body.classList.remove("media-lightbox-open");
1043    $(doc).off("keydown.mediaLightbox");
1044
1045    var $trigger = $root.data("media-last-trigger");
1046    if ($trigger && $trigger.length) {
1047      $trigger.trigger("focus");
1048    }
1049  }
1050
1051  /** @param {JQuery<Element>} $root @param {JQuery<Element>} $trigger */
1052  function openMediaLightbox($root, $trigger) {
1053    var videoUrl = $trigger.attr("data-video-url") || "";
1054    var embedUrl = getVideoEmbedUrl(videoUrl);
1055    if (!embedUrl) return;
1056
1057    var $lightbox = $root.find(".js-media-lightbox");
1058    var $iframe = $root.find(".js-media-lightbox-iframe");
1059
1060    $root.data("media-last-trigger", $trigger);
1061    $iframe.attr("src", embedUrl);
1062    $lightbox.removeAttr("hidden");
1063    doc.body.classList.add("media-lightbox-open");
1064    $root.find(".js-media-lightbox-close").first().trigger("focus");
1065
1066    $(doc).on("keydown.mediaLightbox", function (evt) {
1067      if (evt.key === "Escape") {
1068        closeMediaLightbox($root);
1069      }
1070    });
1071  }
1072
1073  /** @param {JQuery<Element>} $root */
1074  function initMedia($root) {
1075    var $track = $root.find(".js-media-track");
1076    var $nav = $root.find(".media__nav");
1077    var $prev = $root.find(".js-media-prev");
1078    var $next = $root.find(".js-media-next");
1079    var resizeTimer = null;
1080    var resizeNs = ".media-" + String(Math.random()).slice(2);
1081    var slideCount = $track.children(".media__slide").length;
1082    var hasCarousel = slideCount >= 2;
1083
1084    if (!$track.length) return;
1085
1086    if (!hasCarousel) {
1087      $nav.attr("hidden", "");
1088    }
1089
1090    if (hasCarousel && !$track.hasClass("slick-initialized")) {
1091      $track.slick({
1092        centerMode: true,
1093        slidesToShow: 1,
1094        slidesToScroll: 1,
1095        variableWidth: true,
1096        infinite: true,
1097        speed: reduceMotion ? 0 : 300,
1098        arrows: Boolean($prev.length && $next.length),
1099        appendArrows: $nav.length ? $nav : undefined,
1100        prevArrow: $prev.length ? $prev : undefined,
1101        nextArrow: $next.length ? $next : undefined,
1102        swipe: true,
1103        draggable: true,
1104      });
1105
1106      $track.on("afterChange.media init.media", function () {
1107        $track.slick("setPosition");
1108      });
1109
1110      $track.trigger("init.media");
1111    }
1112
1113    $(win).on("resize" + resizeNs, function () {
1114      clearTimeout(resizeTimer);
1115      resizeTimer = win.setTimeout(function () {
1116        if ($track.hasClass("slick-initialized")) {
1117          $track.slick("setPosition");
1118        }
1119      }, 150);
1120    });
1121
1122    $root.on("click.mediaVideo", ".js-media-video-trigger", function () {
1123      openMediaLightbox($root, $(this));
1124    });
1125
1126    $root.on("click.mediaLightboxClose", ".js-media-lightbox-close", function () {
1127      closeMediaLightbox($root);
1128    });
1129
1130    $root.on("click.mediaLightboxPanel", ".media__lightbox-panel", function (evt) {
1131      evt.stopPropagation();
1132    });
1133
1134    $root.data("media-cleanup", function () {
1135      $(win).off(resizeNs);
1136      $track.off(".media");
1137      $root.off(".mediaVideo .mediaLightboxClose .mediaLightboxPanel");
1138      closeMediaLightbox($root);
1139      if ($track.hasClass("slick-initialized")) {
1140        $track.slick("unslick");
1141      }
1142    });
1143  }
1144
1145  $(function () {
1146    $(".js-media").each(function () {
1147      initMedia($(this));
1148    });
1149  });
1150})(typeof jQuery !== "undefined" ? jQuery : null, window, document);
1151
1152//# sourceMappingURL=script.js.map

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.