• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3{
4  function setupTheme() {
5    const kCustomPreference = 'customDarkTheme';
6    const userSettings = sessionStorage.getItem(kCustomPreference);
7    const themeToggleButton = document.getElementById('theme-toggle-btn');
8
9    if (userSettings === null && window.matchMedia) {
10      const mq = window.matchMedia('(prefers-color-scheme: dark)');
11
12      if ('onchange' in mq) {
13        function mqChangeListener(e) {
14          document.documentElement.classList.toggle('dark-mode', e.matches);
15        }
16        mq.addEventListener('change', mqChangeListener);
17        if (themeToggleButton) {
18          themeToggleButton.addEventListener(
19            'click',
20            function() {
21              mq.removeEventListener('change', mqChangeListener);
22            },
23            { once: true },
24          );
25        }
26      }
27
28      if (mq.matches) {
29        document.documentElement.classList.add('dark-mode');
30      }
31    } else if (userSettings === 'true') {
32      document.documentElement.classList.add('dark-mode');
33    }
34
35    if (themeToggleButton) {
36      themeToggleButton.hidden = false;
37      themeToggleButton.addEventListener('click', function() {
38        sessionStorage.setItem(
39          kCustomPreference,
40          document.documentElement.classList.toggle('dark-mode'),
41        );
42      });
43    }
44  }
45
46  function setupPickers() {
47    function closeAllPickers() {
48      for (const picker of pickers) {
49        picker.parentNode.classList.remove('expanded');
50      }
51
52      window.removeEventListener('click', closeAllPickers);
53      window.removeEventListener('keydown', onKeyDown);
54    }
55
56    function onKeyDown(e) {
57      if (e.key === 'Escape') {
58        closeAllPickers();
59      }
60    }
61
62    const pickers = document.querySelectorAll('.picker-header > a');
63
64    for (const picker of pickers) {
65      const parentNode = picker.parentNode;
66
67      picker.addEventListener('click', function(e) {
68        e.preventDefault();
69
70        /*
71          closeAllPickers as window event trigger already closed all the pickers,
72          if it already closed there is nothing else to do here
73        */
74        if (parentNode.classList.contains('expanded')) {
75          return;
76        }
77
78        /*
79          In the next frame reopen the picker if needed and also setup events
80          to close pickers if needed.
81        */
82
83        requestAnimationFrame(function() {
84          parentNode.classList.add('expanded');
85          window.addEventListener('click', closeAllPickers);
86          window.addEventListener('keydown', onKeyDown);
87        });
88      });
89    }
90  }
91
92  function setupStickyHeaders() {
93    const header = document.querySelector('.header');
94    let ignoreNextIntersection = false;
95
96    new IntersectionObserver(
97      function(e) {
98        const currentStatus = header.classList.contains('is-pinned');
99        const newStatus = e[0].intersectionRatio < 1;
100
101        // Same status, do nothing
102        if (currentStatus === newStatus) {
103          return;
104        } else if (ignoreNextIntersection) {
105          ignoreNextIntersection = false;
106          return;
107        }
108
109        /*
110          To avoid flickering, ignore the next changes event that is triggered
111          as the visible elements in the header change once we pin it.
112
113          The timer is reset anyway after few milliseconds.
114        */
115        ignoreNextIntersection = true;
116        setTimeout(function() {
117          ignoreNextIntersection = false;
118        }, 50);
119
120        header.classList.toggle('is-pinned', newStatus);
121      },
122      { threshold: [1] },
123    ).observe(header);
124  }
125
126  function setupAltDocsLink() {
127    const linkWrapper = document.getElementById('alt-docs');
128
129    function updateHashes() {
130      for (const link of linkWrapper.querySelectorAll('a')) {
131        link.hash = location.hash;
132      }
133    }
134
135    addEventListener('hashchange', updateHashes);
136    updateHashes();
137  }
138
139  function setupCopyButton() {
140    const buttons = document.querySelectorAll('.copy-button');
141    buttons.forEach((button) => {
142      button.addEventListener('click', (el) => {
143        const parentNode = el.target.parentNode;
144
145        const flavorSelector = parentNode.querySelector('.js-flavor-selector');
146
147        let code = '';
148
149        if (flavorSelector) {
150          if (flavorSelector.checked) {
151            code = parentNode.querySelector('.mjs').textContent;
152          } else {
153            code = parentNode.querySelector('.cjs').textContent;
154          }
155        } else {
156          code = parentNode.querySelector('code').textContent;
157        }
158
159        button.textContent = 'Copied';
160        navigator.clipboard.writeText(code);
161
162        setTimeout(() => {
163          button.textContent = 'Copy';
164        }, 2500);
165      });
166    });
167  }
168
169  function bootstrap() {
170    // Check if we have JavaScript support.
171    document.documentElement.classList.add('has-js');
172
173    // Restore user mode preferences.
174    setupTheme();
175
176    // Handle pickers with click/taps rather than hovers.
177    setupPickers();
178
179    // Track when the header is in sticky position.
180    setupStickyHeaders();
181
182    // Make link to other versions of the doc open to the same hash target (if it exists).
183    setupAltDocsLink();
184
185    setupCopyButton();
186  }
187
188  if (document.readyState === 'loading') {
189    document.addEventListener('DOMContentLoaded', bootstrap, { once: true });
190  } else {
191    bootstrap();
192  }
193}
194