PageSourceSearch

https://www.georgetowncoc.com/assets/controllers/staff_nav_scroll_controller-be5bb9d5.js

js georgetowncoc.com collected 2026-09-25 04:06:06 UTC 2,919 bytes, 99 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2
3export default class extends Controller {
4  static targets = ["scroller", "bottomHint"]
5
6  connect() {
7    this.onScroll = this.onScroll.bind(this)
8    this.onResize = this.onResize.bind(this)
9    this.syncLayout = this.syncLayout.bind(this)
10
11    if (!this.hasScrollerTarget) return
12
13    this.scrollerTarget.addEventListener("scroll", this.onScroll, { passive: true })
14    window.addEventListener("resize", this.onResize)
15
16    if ("ResizeObserver" in window) {
17      this.resizeObserver = new ResizeObserver(this.syncLayout)
18      this.resizeObserver.observe(this.scrollerTarget)
19    }
20
21    this.syncLayout()
22    requestAnimationFrame(() => this.syncLayout())
23    this.settleTimeout = setTimeout(() => this.syncLayout(), 80)
24  }
25
26  disconnect() {
27    if (this.hasScrollerTarget) {
28      this.scrollerTarget.removeEventListener("scroll", this.onScroll)
29      this.scrollerTarget.classList.remove("is-scrolling")
30    }
31
32    window.removeEventListener("resize", this.onResize)
33    this.resizeObserver?.disconnect()
34    clearTimeout(this.settleTimeout)
35    clearTimeout(this.scrollIdleTimeout)
36  }
37
38  onScroll() {
39    this.syncHints()
40    this.flagScrolling()
41  }
42
43  flagScrolling() {
44    if (!this.hasScrollerTarget) return
45
46    this.scrollerTarget.classList.add("is-scrolling")
47    clearTimeout(this.scrollIdleTimeout)
48    this.scrollIdleTimeout = setTimeout(() => {
49      this.scrollerTarget.classList.remove("is-scrolling")
50    }, 800)
51  }
52
53  onResize() {
54    this.syncLayout()
55  }
56
57  syncLayout() {
58    this.syncHints()
59    this.revealActiveItem()
60  }
61
62  revealActiveItem() {
63    if (!this.hasScrollerTarget || this.scrollerTarget.clientHeight === 0) return
64
65    const activeItem = this.scrollerTarget.querySelector("[aria-current='page']")
66    if (!activeItem) return
67
68    const containerRect = this.scrollerTarget.getBoundingClientRect()
69    const itemRect = activeItem.getBoundingClientRect()
70    const margin = 20
71
72    const aboveFold = itemRect.top < containerRect.top + margin
73    const belowFold = itemRect.bottom > containerRect.bottom - margin
74
75    if (aboveFold || belowFold) {
76      const currentScrollTop = this.scrollerTarget.scrollTop
77      const itemTopWithinScroller = currentScrollTop + (itemRect.top - containerRect.top)
78      const targetTop = Math.max(
79        0,
80        itemTopWithinScroller - (this.scrollerTarget.clientHeight * 0.35)
81      )
82
83      this.scrollerTarget.scrollTop = targetTop
84      this.syncHints()
85    }
86  }
87
88  syncHints() {
89    if (!this.hasScrollerTarget || this.scrollerTarget.clientHeight === 0) return
90
91    const { scrollTop, scrollHeight, clientHeight } = this.scrollerTarget
92    const overflow = scrollHeight - clientHeight > 12
93    const atBottom = scrollTop + clientHeight >= scrollHeight - 6
94
95    if (this.hasBottomHintTarget) {
96      this.bottomHintTarget.classList.toggle("hidden", !overflow || atBottom)
97    }
98  }
99}

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.