PageSourceSearch

https://www.dfwconnectedcare.com/-/media/project/bsw/sites/bswhealth/scripts/bswh-anchor-nav.js?v=31

js dfwconnectedcare.com collected 2026-10-07 16:03:27 UTC 18,178 bytes, 477 lines download raw bytes

1window.addEventListener("DOMContentLoaded", () => {
2    jQuery(function ($) {
3        const anchornavClass = function (config) {
4            // Merge defaults and config
5            this.settings = $.extend({}, this.defaults, config);
6        };
7
8        anchornavClass.prototype.defaults = {
9            anchorNav: ".anchor-nav",
10            anchorLink: ".anchor-link",
11            backTopLink: ".back-top-link",
12        };
13
14        anchornavClass.prototype.settings = {
15            skipping: null,
16            anchorNavBottom: 0,
17            bannerHeight: 0,
18        };
19
20        anchornavClass.prototype.init = function () {
21            this.setupNavLinks();
22            this.handleStickyAnchorNav();
23            this.backToTop();
24            this.checkScroll();
25        };
26
27        anchornavClass.prototype.checkScroll = function () {
28            const hash = window.location.hash;
29
30            if (hash) {
31                setTimeout(() => {
32                    this.scrollPageToEl(hash.substring(1), 150);
33                });
34            }
35        };
36
37        anchornavClass.prototype.toSlug = function (value) {
38            const lowercased = value.toLowerCase();
39            const slug = lowercased
40                .replace(/\./g, "")
41                .replace(/[^\w\s-]/g, " ")
42                .replace(/\s+/g, "-");
43            return slug;
44        };
45
46        anchornavClass.prototype.hasBSWHBanner = function () {
47            const bswhealthBanner = $(".bswh-page-banner");
48
49            return bswhealthBanner.length > 0;
50        };
51
52        anchornavClass.prototype.scrollPageToEl = function (
53            elemId,
54            overridetimeToTake = null
55        ) {
56            ScrollTrigger.refresh();
57
58            if (!elemId) {
59                return;
60            }
61
62            const elToScrollTo = $(`[data-href="${elemId}"]`)[0];
63
64            if (!elToScrollTo) {
65                return;
66            }
67
68            let timeToTake = isNaN(overridetimeToTake)
69                ? 1000
70                : overridetimeToTake;
71            let extraOffset = 0;
72            const anchorNav = $(".anchor-nav");
73            const { height: anchorHeight, bottom: anchorBottom } =
74                anchorNav[0].getBoundingClientRect();
75            const windowScroll = $(window).scrollTop();
76            const sectionPadding = elToScrollTo.closest(".section-padding");
77            const header = $("header");
78            const { height: headerHeight } = header[0].getBoundingClientRect();
79            let offset = $(elToScrollTo).offset().top;
80
81            if (this.hasBSWHBanner()) {
82                extraOffset = anchorBottom;
83            } else {
84                if (windowScroll <= 0) {
85                    // Not sure why it needs double the height
86                    // of the anchor navigation, but this works.
87                    extraOffset = headerHeight + anchorHeight;
88                } else {
89                    extraOffset = anchorBottom;
90                }
91            }
92
93            if (sectionPadding) {
94                offset = $(sectionPadding).offset().top;
95            }
96
97            // The 1 is just to make sure we get over the line to detect we're
98            // in the section.
99            const scrollTo = offset - extraOffset + 1;
100
101            // Want to check how much we are scrolling and always use a
102            // positive number to ease calculations.
103            const scrollDiff = Math.abs(windowScroll - scrollTo);
104
105            // Don't want to take the full time if the selected element is close.
106            if (scrollDiff <= 800 && isNaN(overridetimeToTake)) {
107                timeToTake = 500;
108            }
109
110            $("html, body").animate({ scrollTop: scrollTo }, timeToTake);
111        };
112
113        anchornavClass.prototype.createNavLinkList = function (sectionList) {
114            const linkList = [];
115
116            for (const sectionElem of sectionList) {
117                const $elem = $(sectionElem);
118                let datahref = $elem.attr("data-href");
119                let datahrefLabel = $elem.attr("data-hreflabel");
120
121                if (datahref == "" || datahref == "undefined") {
122                    return;
123                } else {
124                    if (!datahrefLabel) {
125                        datahrefLabel = datahref;
126                        datahref = this.toSlug(datahref);
127                    }
128                    const $divContainer = $("<div>", {
129                        class: "anchor-link-wrapper",
130                    });
131                    const $anchor = $("<a>", {
132                        href: `#${datahref}`,
133                        class: "anchor-link",
134                        text: datahrefLabel,
135                        "data-scrollsection": datahref,
136                        "data-analytics-element": "anchor_nav",
137                    });
138                    $divContainer.append($anchor);
139                    $divContainer.append(
140                        $("<div>", {
141                            class: "anchor-link--marker",
142                        })
143                    );
144
145                    linkList.push($divContainer);
146                }
147            }
148
149            return linkList;
150        };
151
152        anchornavClass.prototype.setupNavLinks = function () {
153            const thatAnchorNav = this;
154            const $navLinkContainer = $(".anchor-nav .nav-link-wrapper");
155            $navLinkContainer.empty();
156            const $fragment = $(new DocumentFragment());
157            const sectionList = $("div[data-href]");
158            const linkList = this.createNavLinkList(sectionList);
159
160            for (const section of sectionList) {
161                const datahref = $(section).attr("data-href");
162
163                for (const link of linkList) {
164                    if (
165                        $($(link).children()[0]).attr("href") === `#${datahref}`
166                    ) {
167                        $(link).click((e) => {
168                            e.preventDefault();
169
170                            for (const linkInternal of linkList) {
171                                $(linkInternal).removeClass("active");
172                            }
173
174                            $(link).addClass("active");
175                            thatAnchorNav.settings.skipping = datahref;
176                            history.pushState(null, null, `#${datahref}`);
177                            thatAnchorNav.scrollPageToEl(datahref);
178                        });
179
180                        $fragment.append(link);
181                    }
182                }
183            }
184
185            $navLinkContainer.append($fragment);
186        };
187
188        anchornavClass.prototype.handleStickyAnchorNav = function () {
189            const thatClass = this;
190            const navLinkWrapper = document.querySelector(".nav-link-wrapper");
191
192            if (
193                typeof gsap !== "undefined" &&
194                typeof ScrollTrigger !== "undefined"
195            ) {
196                const sections = gsap.utils.toArray("div[data-href]");
197                const anchorLinkWrappers = gsap.utils.toArray(
198                    ".anchor-link-wrapper"
199                );
200                const anchorNav = document.querySelector(".anchor-nav");
201                const toTop = document.querySelector(".back-top-link-wrapper");
202
203                gsap.to(toTop, {
204                    ease: "none",
205                    duration: 0.25,
206                    transformOrigin: "right",
207                    scrollTrigger: {
208                        trigger: "body",
209                        start: "top top",
210                        end: "bottom center",
211                        toggleActions: "play none none reverse",
212                        onEnter: () => {
213                            gsap.to(toTop, {
214                                display: "flex",
215                                opacity: 1,
216                            });
217                        },
218                        onLeaveBack: () => {
219                            gsap.to(toTop, {
220                                display: "none",
221                                opacity: 0,
222                            });
223                            thatClass.settings.skipping = null;
224                        },
225                    },
226                });
227                let scrollTriggerTimeout = null;
228                const sectionObserver = new ResizeObserver(() => {
229                    if (scrollTriggerTimeout) {
230                        clearTimeout(scrollTriggerTimeout);
231                    }
232
233                    scrollTriggerTimeout = setTimeout(() => {
234                        ScrollTrigger.refresh();
235                    }, 100);
236                });
237
238                [...sections].forEach((section, i) => {
239                    const marker = anchorLinkWrappers[i]?.querySelector(
240                        ".anchor-link--marker"
241                    );
242
243                    if (!marker) return;
244
245                    let href = "";
246                    const anchorLink =
247                        anchorLinkWrappers[i]?.querySelector(".anchor-link");
248
249                    const componentWithHref = section.querySelector(
250                        ".component[data-href]"
251                    );
252
253                    if (componentWithHref) {
254                        href = componentWithHref.dataset.href;
255                    } else {
256                        href = section.dataset.href;
257                    }
258
259                    let finalSection = section;
260                    const hasSectionPadding =
261                        section.closest(".section-padding");
262
263                    if (hasSectionPadding) {
264                        finalSection = hasSectionPadding;
265                    }
266
267                    const enterMarker = () => {
268                        const { skipping } = thatClass.settings;
269
270                        if (!skipping || (skipping && skipping === href)) {
271                            gsap.to(marker, {
272                                transformOrigin: "left",
273                                scaleX: 1,
274                            });
275                            thatClass.settings.skipping = null;
276                            const {
277                                left: anchorLinkLeft,
278                                width: anchorLinkWidth,
279                            } = anchorLink.getBoundingClientRect();
280                            const { width: navLinkWrapperWidth } =
281                                navLinkWrapper.getBoundingClientRect();
282                            const menuScrollLeft = navLinkWrapper.scrollLeft;
283                            const anchorLinkPosition =
284                                anchorLinkLeft +
285                                anchorLinkWidth / 2 +
286                                menuScrollLeft -
287                                navLinkWrapperWidth / 2;
288                            navLinkWrapper.scrollLeft = anchorLinkPosition;
289                            anchorLink.classList.add("active");
290                        }
291                    };
292                    const leaveMarker = () => {
293                        gsap.to(marker, {
294                            transformOrigin: "right",
295                            scaleX: 0,
296                        });
297                        anchorLink.classList.remove("active");
298                    };
299                    let anchorNavBottom =
300                        anchorNav.getBoundingClientRect().bottom;
301
302                    gsap.to(marker, {
303                        ease: "none",
304                        duration: 0.25,
305                        overwrite: "auto",
306                        scrollTrigger: {
307                            trigger: finalSection,
308                            start: () => {
309                                return `top ${anchorNavBottom}px`;
310                            },
311                            end: () => {
312                                return `bottom ${anchorNavBottom}px`;
313                            },
314                            toggleActions: "play none none reverse",
315                            scrub: false,
316                            markers: false,
317                            fastScrollEnd: true,
318                            preventOverlaps: true,
319                            onEnter: enterMarker,
320                            onEnterBack: enterMarker,
321                            onLeave: leaveMarker,
322                            onLeaveBack: leaveMarker,
323                            onUpdate: (self) => {
324                                // Because of the nature of things changing during scroll
325                                // we need to do our best to update data during the lifecycle
326                                // so we can better show what the current state is.
327                                const newAnchorNavBottom =
328                                    anchorNav.getBoundingClientRect().bottom;
329
330                                // Only update when needed.
331                                if (newAnchorNavBottom !== anchorNavBottom) {
332                                    anchorNavBottom = newAnchorNavBottom;
333                                    self.start = newAnchorNavBottom;
334                                    self.end = newAnchorNavBottom;
335                                    self.refresh();
336                                }
337                            },
338                        },
339                    });
340
341                    sectionObserver.observe(section);
342                });
343            }
344        };
345
346        /**
347         * Want to scroll to the top of the page and use a ratio to determine
348         * how long it should take to scroll to the top.
349         */
350        anchornavClass.prototype.backToTop = function () {
351            const thatClass = this;
352
353            $(this.settings.backTopLink).on("click", function (e) {
354                e.stopPropagation();
355
356                thatClass.settings.skipping = "top";
357                const pageHeight = $("body").height();
358                const scrollLoc = $(window).scrollTop();
359                const percentage = Math.floor((scrollLoc / pageHeight) * 100);
360                const timeToTake = (1000 * percentage) / 100;
361
362                $("html, body").animate({ scrollTop: 0 }, timeToTake);
363            });
364        };
365
366        anchornavClass.prototype.setAnchorNavTop = function () {
367            const header = $("#global-multi-header");
368            const { height: headerHeight } = header[0].getBoundingClientRect();
369
370            $(".anchor-nav").css({ top: `${headerHeight}px` });
371        };
372
373        anchornavClass.prototype.mobileFunctions = function () {
374            const buttonGroup = document.querySelector(
375                ".anchor-nav .cta-wrapper"
376            );
377
378            if (buttonGroup) {
379                const body = document.body;
380
381                ScrollTrigger.create({
382                    id: "ctaMobileAnchorNav",
383                    markers: false,
384                    trigger: body,
385                    start: "top -20%",
386                    onUpdate: (self) => {
387                        if (self.direction === 1) {
388                            body.classList.add("scrolling-down");
389                            body.classList.remove("scrolling-up");
390                            gsap.to(buttonGroup, { yPercent: 100 });
391                        } else {
392                            body.classList.add("scrolling-up");
393                            body.classList.remove("scrolling-down");
394                            gsap.to(buttonGroup, { yPercent: 0 });
395                        }
396                    },
397                    onLeaveBack: () => {
398                        body.classList.remove("scrolling-up");
399                    },
400                });
401            }
402
403            return () => {
404                const ctaMobileAnchorNav =
405                    ScrollTrigger.getById("ctaMobileAnchorNav");
406
407                if (ctaMobileAnchorNav) {
408                    ctaMobileAnchorNav.kill(true);
409                }
410            };
411        };
412
413        $(function () {
414            let timer = null;
415            let anchornavtInit = new anchornavClass();
416
417            window.scrollPageToEl =
418                anchornavtInit.scrollPageToEl.bind(anchornavtInit);
419
420            if (
421                typeof gsap !== "undefined" &&
422                typeof ScrollTrigger !== "undefined"
423            ) {
424                gsap.registerPlugin(ScrollTrigger);
425
426                const mmBanner = gsap.matchMedia();
427                mmBanner.add(
428                    `(max-width: 991px)`,
429                    anchornavtInit.mobileFunctions
430                );
431            } else {
432                console.error("Error initializing GSAP in Anchor Navigation");
433            }
434
435            $(".anchor-nav").length && anchornavtInit.init();
436
437            if (!anchornavtInit.hasBSWHBanner()) {
438                anchornavtInit.setAnchorNavTop();
439
440                $(".alert-closebtn").click(() => {
441                    let visibleTimer = null;
442
443                    try {
444                        const header = $("header");
445                        const { bottom: startHeaderBottom } =
446                            header[0].getBoundingClientRect();
447
448                        visibleTimer = setInterval(() => {
449                            const { bottom: currentHeaderBottom } =
450                                header[0].getBoundingClientRect();
451
452                            if (currentHeaderBottom !== startHeaderBottom) {
453                                anchornavtInit.setAnchorNavTop();
454                                ScrollTrigger.refresh();
455                                clearInterval(visibleTimer);
456                            }
457                        }, 100);
458                    } catch (e) {
459                        console.error(e);
460                    }
461                });
462            }
463
464            $(window).on("resize", () => {
465                if (timer) {
466                    clearTimeout(timer);
467                }
468
469                timer = setTimeout(() => {
470                    if (!anchornavtInit.hasBSWHBanner()) {
471                        anchornavtInit.setAnchorNavTop();
472                    }
473                }, 300);
474            });
475        });
476    });
477});

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.