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.