PageSourceSearch

https://aframe.io/js/common.js?v=f768970a

js aframe.io collected 2026-09-24 06:43:28 UTC 6,854 bytes, 217 lines download raw bytes

1(function () {
2  initHelpers();
3  initHeadingAnchors();
4  initTableOfContents();
5  initSidebarScrollState();
6
7  function initHeadingAnchors () {
8    function anchorId (str) {
9      return (str || '').replace(/[^-a-zA-Z0-9,&\s]+/g, '')
10                        .replace(/-/g, '_')
11                        .replace(/\s+/g, ' ')
12                        .trim()
13                        .replace(/\s/g, '_')
14                        .trim()
15                        .toLowerCase();
16    }
17    var anchorHeadingsSelector = [
18      '.copy__wrap > table[id] tr th:first-child',
19      '.copy__wrap > table tr[id] td:first-child'
20    ].join(',');
21    var tableIds = {};
22    var rowIds = {};
23    var content = $('.content');
24
25    if (content) {
26      var tablesEls = $$('.content .copy__wrap > table');
27      tablesEls.forEach(function (tableEl, idx) {
28        // Find the closest sibling section heading.
29        var siblingEl = tableEl;
30        var tableHeading = '';
31        while (siblingEl) {
32          siblingEl = siblingEl.previousElementSibling;
33          if (siblingEl && siblingEl.nodeName === 'H2') {
34            tableHeading = siblingEl.textContent;
35            break;
36          }
37        }
38
39        var tableId = anchorId(tableHeading);
40
41        // If this `id` has already been used, append at `-{num}` (starting at `-2`).
42        if (tableIds[tableId]) {
43          tableId += ++tableIds[tableId];
44        } else {
45          tableIds[tableId] = 1;
46        }
47
48        tableEl.setAttribute('id', tableId);
49
50        $$('tr', tableEl).forEach(function (trEl) {
51          var td = trEl.querySelector('td');
52          if (!td) { return; }
53
54          var trId = anchorId(td.textContent);
55
56          // If this `id` has already been used, append at `-{num}` (starting at `-2`).
57          if (rowIds[trId]) {
58            trId += '-' + (++rowIds[trId])
59          } else {
60            rowIds[trId] = 1;
61          }
62
63          trEl.setAttribute('id', tableId + '_' + trId);
64        });
65      });
66
67      content.addEventListener('click', function (e) {
68        var el = e.target;
69        if (el.matches && el.matches(anchorHeadingsSelector)) {
70          // Use the click target if it has an `id` (or a parent with an `id`).
71          var closestEl = el.closest('[id]');
72          if (!closestEl) { return; }
73          window.location.hash = '#' + closestEl.id;
74        }
75      });
76    }
77  }
78
79  function initTableOfContents () {
80    if (!document.querySelector('#table-of-contents')) { return; }
81
82    // Sort sections by their offsets and grab reference to table of contents link.
83    var sectionScrolls = [];
84    $$('.content h2, .content h3, .anchor-heading').forEach(function (heading) {
85      var link = document.querySelector('#table-of-contents [href="#' + heading.getAttribute('id') + '"]');
86      if (!link) { return; }
87      sectionScrolls.push([heading.offsetTop, link]);
88    });
89
90    if (!sectionScrolls.length) { return; }
91
92    sectionScrolls.sort(function (a, b) {
93      if (a[0] < b[0]) { return -1; }
94      if (a[0] > b[0]) { return 1; }
95      return 0;
96    });
97
98    function highlightHeading (scrollPos) {
99      // Account for heading size.
100      scrollPos += 40;
101
102      // Reset.
103      for (var i = 0; i < sectionScrolls.length; i++) {
104        sectionScrolls[i][1].classList.remove('current');
105      }
106
107      // Highlight active section's heading link.
108      var found = false;
109      for (i = 0; i < sectionScrolls.length; i++) {
110        if (scrollPos >= sectionScrolls[i][0] &&
111            (!sectionScrolls[i + 1] || scrollPos < sectionScrolls[i + 1][0])) {
112          sectionScrolls[i][1].classList.add('current');
113          found = true;
114        }
115      }
116
117      // Default to top link.
118      if (!found) {
119        sectionScrolls[0][1].classList.add('current');
120      }
121    }
122
123    // Throttle scroll listener.
124    var scrollPos = 0;
125    var ticking = false;
126    window.addEventListener('scroll', function () {
127      scrollPos = window.scrollY;
128      if (!ticking) {
129        window.requestAnimationFrame(function () {
130          highlightHeading(scrollPos);
131          ticking = false;
132        });
133      }
134      ticking = true;
135    });
136
137    sectionScrolls[0][1].classList.add('current');
138  }
139
140  function initSidebarScrollState () {
141    // Sidebar scroll state.
142    var sidebarEl = document.querySelector('#sidebar');
143    var sidebarLinks = document.querySelectorAll('#sidebar a');
144
145    if (sidebarEl) {
146      // Add listeners to links to tell if user is navigating away from aframe.io.
147      var sidebarLinkClicked = false;
148      function markSidebarLinkClicked () { sidebarLinkClicked = true; }
149      for (var i = 0; i < sidebarLinks.length; i++) {
150        sidebarLinks[i].addEventListener('click', markSidebarLinkClicked);
151      }
152
153      // Store scroll state in localStorage.
154      var lsKey = 'sidebar-scroll';
155      var scrollTop = localStorage.getItem(lsKey);
156      if (scrollTop) { sidebarEl.scrollTop = scrollTop; }
157      sidebarEl.style.visibility = 'visible';
158      sidebarEl.addEventListener('scroll', function () {
159        localStorage.setItem(lsKey, sidebarEl.scrollTop);
160      });
161
162      // Clear scroll state if navigating away to different site.
163      window.addEventListener('beforeunload', function () {
164        if (sidebarLinkClicked) { return; }
165        localStorage.setItem(lsKey, null);
166      });
167    }
168  }
169
170  function initHelpers () {
171    /**
172     * Wraps `querySelector` like jQuery's `$`.
173     *
174     * @param {String|Element} sel CSS selector to match an element.
175     * @param {Element=} parent Parent from which to query.
176     * @returns {Element} Element matched by selector.
177     */
178    window.$ = function (sel, parent) {
179      var el = sel;
180      if (sel && typeof sel === 'string') {
181        el = (parent || document).querySelector(sel);
182      }
183      return el;
184    };
185
186    /**
187     * Wraps `querySelectorAll` like jQuery's `$`.
188     *
189     * @param {String|Element} sel CSS selector to match elements.
190     * @param {Element=} parent Parent from which to query.
191     * @returns {Array} Array of elements matched by selector.
192     */
193    window.$$ = function (sel, parent) {
194      if (Array.isArray(sel)) { return sel; }
195      var els = sel;
196      if (sel && typeof sel === 'string') {
197        els = (parent || document).querySelectorAll(sel);
198      }
199      return toArray(els);
200    };
201
202    /**
203     * Turns an array-like object into an array.
204     *
205     * @param {String|Element} obj CSS selector to match elements.
206     * @param {Array|NamedNodeMap|NodeList|HTMLCollection} arr An array-like object.
207     * @returns {Array} Array of elements matched by selector.
208     */
209    var toArray = function (obj) {
210      if (Array.isArray(obj)) { return obj; }
211      if (typeof obj === 'object' && typeof obj.length === 'number') {
212        return Array.prototype.slice.call(obj);
213      }
214      return [obj];
215    };
216  }
217})();

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.