PageSourceSearch

https://www.ghostery.com/assets/gh-table-of-contents-9f70ac10aa593…d2593f0b81144d21286966fa11d76f7f710b8218.js

js ghostery.com collected 2026-10-01 08:27:20 UTC 5,801 bytes, 247 lines download raw bytes

1import { define, html } from '/assets/vendor/hybrids-0b0ebcfb715640d1dc44879b91142375c3f7b28190b996216eac97a868fba07f.js';
2
3function getSlug(text) {
4  return text
5    .toLowerCase()
6    .replace(/\s/g, '-')
7    .replace(/[^a-z0-9-]/g, '');
8}
9
10function scroll(id) {
11  const element = document.getElementById(id);
12  element.scrollIntoView({ behavior: 'smooth' });
13}
14
15function scrollToHeading(host, event) {
16  event.preventDefault();
17
18  scroll(event.target.hash.slice(1));
19
20  history.pushState(null, null, event.target.hash);
21}
22
23function toggleOpen(host, event) {
24  if (window.innerWidth < 992) {
25    host.open = !host.open;
26  }
27}
28
29define({
30  tag: 'gh-table-of-contents',
31  headings: {
32    value: undefined,
33    connect(host, key) {
34      const headings = Array.from(host.querySelectorAll('h2'));
35
36      host[key] = headings.map((heading) => {
37        heading.id = getSlug(heading.textContent);
38        return heading.textContent;
39      });
40
41      // Set initial scroll position
42      setTimeout(() => {
43        const hash = window.location.hash.slice(1);
44        if (hash) scroll(hash);
45      });
46
47      const cb = (event) => {
48        const atBottom =
49          window.innerHeight + window.scrollY >=
50          document.documentElement.scrollHeight - 1;
51
52        let index = -1;
53
54        if (atBottom) {
55          index = headings.length - 1;
56        } else {
57          let current = Infinity;
58          headings.forEach((heading, i) => {
59            const { top } = heading.getBoundingClientRect();
60
61            if (top >= 0 && top < current) {
62              index = i;
63              current = top;
64            }
65          });
66        }
67
68        host.shadowRoot.querySelectorAll('li').forEach((li, i) => {
69          if (i === index) {
70            li.classList.add('active');
71          } else {
72            li.classList.remove('active');
73          }
74        });
75      };
76
77      window.addEventListener('scroll', cb, { passive: true });
78
79      return () => {
80        window.removeEventListener('scroll', cb);
81      };
82    },
83  },
84  open: { value: false, reflect: true },
85  order: { value: false, reflect: true },
86  transparent: { value: false, reflect: true },
87  render: ({ headings }) => html`
88    <template layout="relative row content:center items:start gap:10">
89      <div
90        id="table"
91        role="button"
92        tabindex="0"
93        layout="fixed bottom left right column padding:2:3 gap:3 layer:900"
94        layout@992px="sticky top:4 padding:3 width:300px shrink:0"
95        onclick="${toggleOpen}"
96      >
97        <div
98          layout="row content:space-between ::ds:font:display-xs ::ds:color:white ::ds:text-transform:uppercase"
99        >
100          <slot name="header"></slot>
101          <svg
102            width="16"
103            height="16"
104            viewBox="0 0 16 16"
105            fill="none"
106            xmlns="http://www.w3.org/2000/svg"
107          >
108            <path
109              d="M12 10L8 6L4 10"
110              stroke="var(--ds-color-white)"
111              stroke-width="1.5"
112              stroke-linecap="round"
113              stroke-linejoin="round"
114            />
115          </svg>
116        </div>
117        <ol layout="relative column gap:2 margin:0 padding:0">
118          ${headings.map(
119            (heading) => html`
120              <li layout="relative ::ds:font:body-m margin:left:3">
121                <a href="#${getSlug(heading)}" onclick="${scrollToHeading}"
122                  >${heading}</a
123                >
124              </li>
125            `,
126          )}
127        </ol>
128      </div>
129      <div layout="width:::full"><slot></slot></div>
130    </template>
131  `.css`
132    #table {
133      border-top: 1px solid var(--ds-color-gray-600, #3F4146);
134      box-shadow: 0px 0px 40px 0px rgba(0, 0, 0, 0.40);
135      background: var(--ds-color-gray-700);
136      transform: translateY(calc(100% - 50px));
137      transition: transform 0.2s ease-in-out;
138      cursor: pointer;
139    }
140
141    :host([open]) #table {
142      cursor: default;
143      transform: translateY(0);
144    }
145
146    :host([open]) #table svg {
147      transform: rotate(180deg);
148    }
149
150    ol::before {
151      display: block;
152      position: absolute;
153      content: '';
154      top: 0;
155      bottom: 0;
156      left: -8px;
157      width: 2px;
158      border-radius: 2px;
159      background: var(--ds-color-gray-900, #121317);
160    }
161
162    :host(:not([order])) ol {
163      list-style: none;
164    }
165
166    li {
167      transition: color 0.2s ease-in-out;
168    }
169
170    :host(:not([order])) li {
171      margin-left: 8px;
172    }
173
174    li a {
175      display: block;
176      color: inherit;
177      text-decoration: none;
178    }
179
180    li.active {
181      color: var(--ds-color-white);
182    }
183
184    li.active::before {
185      position: absolute;
186      content: '';
187      left: -32px;
188      top: 0;
189      bottom: 0;
190      width: 2px;
191      border-radius: 2px;
192      background: var(--ds-color-primary-500);
193    }
194
195    :host(:not([order])) li.active::before {
196      left: -16px;
197    }
198
199    @media screen and (hover: hover) {
200      li:has(a:hover) {
201        color: var(--ds-color-primary-500);
202      }
203    }
204
205    @media screen and (min-width: 992px) {
206      :host(:not([order])) li {
207        margin-left: 0px;
208      }
209
210      :host([transparent]) li.active {
211        margin-left: 16px;
212      }
213
214      :host([transparent]) li.active::before {
215        left: -16px;
216      }
217
218      #table {
219        box-shadow: none;
220        border-top: none;
221        border-radius: 16px;
222        cursor: default;
223        transform: none;
224        transition: none;
225      }
226
227      :host([transparent]) #table {
228        padding: 0;
229        background: transparent;
230      }
231
232      #table svg {
233        display: none;
234      }
235
236      ol::before {
237        display: none;
238      }
239
240      li.active::before {
241        left: -48px;
242        width: 4px;
243        border-radius: 0px 2px 2px 0px;
244      }
245    }
246  `,
247});

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.