PageSourceSearch

https://www.anses.fr/themes/custom/anses/assets/js/toc.js?tluzwr

js anses.fr collected 2026-10-02 02:15:28 UTC 5,997 bytes, 212 lines download raw bytes

1(function ($, Drupal) {
2
3  $(document).ready(function () {
4    createAnchors();
5
6    if ($('.sidebar.toc .anchor-list li').length > 0) {
7      if ($(window).width() > 1573) {
8        $('.sidebar.toc').css('display', 'block');
9      }
10    }
11    else {
12      $('.sidebar.toc:not(.sidebar--infos)').addClass('is-empty');
13    }
14  })
15
16  $(window).resize(function () {
17    if ($('.sidebar.toc .anchor-list li').length > 0) {
18      if ($(window).width() > 1573) {
19        $('.sidebar.toc').show();
20      }
21    }
22  })
23
24  Drupal.behaviors.toc = {
25    attach: () => {
26      if ($('.mobile-toggles-wrapper button').length > 0) {
27        $('.mobile-toggles-wrapper button').unbind().click(function () {
28          $('.sidebar.toc').css('display', 'flex');
29        })
30      }
31      if ($('.toc__header .close-button').length > 0) {
32        $('.toc__header .close-button').unbind().click(function () {
33          $('.sidebar.toc').fadeOut();
34        })
35      }
36    }
37  };
38
39  /**
40   * Build the anchor navigation.
41   *
42   * @return {void};
43   */
44  function createAnchors() {
45    // Content wrapper.
46    const content    = document.querySelector('.node-content');
47    const anchorList = document.getElementById('anchor-list');
48
49    if (!content || !anchorList) {
50      return;
51    }
52
53    // Get headings.
54    const anchorTarget = [
55      '.node-content h2:not(.node--card-image__title):not(.toc__title)',
56    ];
57    const anchorSelector = anchorTarget.join(',');
58    let headingList = content.querySelectorAll(anchorSelector);
59
60    // Convert NodeList to an array.
61    headingList = Array.prototype.slice.call(headingList);
62    activeAnchorLinks(headingList);
63
64    if (!headingList.length) {
65      document.getElementById('sidebar').style.display = 'none';
66      return;
67    }
68
69    generateAnchorList(headingList, anchorList);
70    handleAnchorScroll(anchorList);
71  }
72
73  function generateAnchorList(headingList, anchorList) {
74    // Create anchors.
75    let i = 0;
76    [].forEach.call(headingList, (heading) => {
77      let anchorText = heading.textContent;
78      // Remove space & empty lines.
79      anchorText = anchorText.trim();
80
81      if (!anchorText) {
82        return;
83      }
84
85      // Add unique id on headings.
86      let id = `a-${i}`;
87      if (document.getElementById(id)) {
88        id += `-${i + 1}`;
89      }
90
91      // Add the id on the headings.
92      heading.setAttribute('id', id);
93
94      // Generate link toward the anchor.
95      const link = document.createElement('a');
96      link.setAttribute('href', `#${id}`);
97      link.textContent = anchorText;
98
99      const li = document.createElement('li');
100
101      // Active the first link.
102      if (i === 0) {
103        link.classList.add('anchor--is-active');
104        li.classList.add('anchor-parent--is-active');
105      }
106
107      li.appendChild(link);
108      anchorList.appendChild(li);
109      i++;
110
111    });
112  }
113
114  function handleAnchorScroll(anchorList) {
115    // Smooth scrool.
116    const anchorItemList = anchorList.querySelectorAll('a');
117    [].forEach.call(anchorItemList, (link) => {
118      link.addEventListener('click', (e) => {
119        e.preventDefault();
120        const a = link;
121        const viewportRatio = 0.3;
122        let anchorId = a.getAttribute('href');
123
124        [].forEach.call(anchorList.querySelectorAll('a.anchor--is-active-forced'), (activeLink) => {
125          activeLink.classList.remove('anchor--is-active-forced');
126        });
127        a.classList.add('anchor--is-active-forced');
128
129        // Remove the active class after scroll.
130        const scrollDuration = 500;
131        setTimeout(() => {
132          a.classList.remove('anchor--is-active-forced');
133        }, scrollDuration);
134
135        anchorId = anchorId.substring(1, anchorId.length);
136        const anchor = document.getElementById(anchorId);
137        const bodyRect = document.body.getBoundingClientRect().top;
138        const screenHeight = window.innerHeight || document.documentElement.clientHeight;
139        const anchorOffset =  anchor.getBoundingClientRect().top - bodyRect - viewportRatio * screenHeight;
140        const anchorBlock = document.getElementById('anchor-list');
141
142        // Collapse menu.
143        anchorBlock.classList.remove('anchors--are-expanded');
144
145        // Smooth.
146        window.scrollTo(0, anchorOffset);
147        // window.scroll({
148        //   top: anchorOffset,
149        //   left: 0,
150        // });
151      });
152    });
153  }
154
155  /**
156   * Determine if the specified DOM object is in the viewport.
157   *
158   * @param {Object} el - DOM object
159   * @returns {Bool} True if element is in the viewport.
160   */
161  function isInViewport(el) {
162    const bounding = el.getBoundingClientRect();
163    const viewportRatio = 0.4;
164
165    // is above.
166    if (bounding.top + bounding.height <= 0) {
167      return false;
168    }
169
170    const screenHeight = window.innerHeight || document.documentElement.clientHeight;
171
172    // is bellow.
173    if (bounding.top > screenHeight * viewportRatio) {
174      return false;
175    }
176
177    // is in the viewport.
178    return true;
179  }
180
181  /**
182   * Highlight the current anchor.
183   *
184   * @param {NodeList} headingList
185   *  Set of headings.
186   *
187   * @return {void};
188   */
189  function activeAnchorLinks(headingList) {
190    window.addEventListener('scroll', (e) => {
191      [].forEach.call(headingList, (heading) => {
192        if (isInViewport(heading)) {
193          const id = heading.getAttribute('id');
194          const matchingLink = document.querySelector(`#anchor-list a[href="#${id}"]`);
195
196          // Remove the active state.
197          if (matchingLink) {
198            const anchorList = document.querySelectorAll('#anchor-list a');
199            [].forEach.call(anchorList, (anchor) => {
200              anchor.classList.remove('anchor--is-active');
201              anchor.parentNode.classList.remove('anchor-parent--is-active');
202            });
203
204            matchingLink.classList.add('anchor--is-active');
205            matchingLink.parentNode.classList.add('anchor-parent--is-active');
206          }
207        }
208      });
209    });
210  }
211
212})(jQuery, Drupal, drupalSettings);

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.