1(function (jtd, undefined) { 2 3// Event handling 4 5jtd.addEvent = function(el, type, handler) { 6 if (el.attachEvent) el.attachEvent('on'+type, handler); else el.addEventListener(type, handler); 7} 8jtd.removeEvent = function(el, type, handler) { 9 if (el.detachEvent) el.detachEvent('on'+type, handler); else el.removeEventListener(type, handler); 10} 11jtd.onReady = function(ready) { 12 // in case the document is already rendered 13 if (document.readyState!='loading') ready(); 14 // modern browsers 15 else if (document.addEventListener) document.addEventListener('DOMContentLoaded', ready); 16 // IE <= 8 17 else document.attachEvent('onreadystatechange', function(){ 18 if (document.readyState=='complete') ready(); 19 }); 20} 21 22// Show/hide mobile menu 23 24function initNav() { 25 jtd.addEvent(document, 'click', function(e){ 26 var target = e.target; 27 while (target && !(target.classList && target.classList.contains('nav-list-expander'))) { 28 target = target.parentNode; 29 } 30 if (target) { 31 e.preventDefault(); 32 target.parentNode.classList.toggle('active'); 33 } 34 }); 35 36 const siteNav = document.getElementById('site-nav'); 37 const mainHeader = document.getElementById('main-header'); 38 const menuButton = document.getElementById('menu-button'); 39 40 jtd.addEvent(menuButton, 'click', function(e){ 41 e.preventDefault(); 42 43 if (menuButton.classList.toggle('nav-open')) { 44 siteNav.classList.add('nav-open'); 45 mainHeader.classList.add('nav-open'); 46 } else { 47 siteNav.classList.remove('nav-open'); 48 mainHeader.classList.remove('nav-open'); 49 } 50 }); 51} 52// Site search 53 54function initSearch() { 55 var request = new XMLHttpRequest(); 56 request.open('GET', 'https://cmajor.dev/assets/js/search-data.json', true); 57 58 request.onload = function(){ 59 if (request.status >= 200 && request.status < 400) { 60 var docs = JSON.parse(request.responseText); 61 62 lunr.tokenizer.separator = /[\s/]+/ 63 64 var index = lunr(function(){ 65 this.ref('id'); 66 this.field('title', { boost: 200 }); 67 this.field('content', { boost: 2 }); 68 this.field('relUrl'); 69 this.metadataWhitelist = ['position'] 70 71 for (var i in docs) { 72 this.add({ 73 id: i, 74 title: docs[i].title, 75 content: docs[i].content, 76 relUrl: docs[i].relUrl 77 }); 78 } 79 }); 80 81 searchLoaded(index, docs); 82 } else { 83 console.log('Error loading ajax request. Request status:' + request.status); 84 } 85 }; 86 87 request.onerror = function(){ 88 console.log('There was a connection error'); 89 }; 90 91 request.send(); 92} 93 94function searchLoaded(index, docs) { 95 var index = index; 96 var docs = docs; 97 var searchInput = document.getElementById('search-input'); 98 var searchResults = document.getElementById('search-results'); 99 var mainHeader = document.getElementById('main-header'); 100 var currentInput; 101 var currentSearchIndex = 0; 102 103 function showSearch() { 104 document.documentElement.classList.add('search-active'); 105 } 106 107 function hideSearch() { 108 document.documentElement.classList.remove('search-active'); 109 } 110 111 function update() { 112 currentSearchIndex++; 113 114 var input = searchInput.value; 115 if (input === '') { 116 hideSearch(); 117 } else { 118 showSearch(); 119 // scroll search input into view, workaround for iOS Safari 120 window.scroll(0, -1); 121 setTimeout(function(){ window.scroll(0, 0); }, 0); 122 } 123 if (input === currentInput) { 124 return; 125 } 126 currentInput = input; 127 searchResults.innerHTML = ''; 128 if (input === '') { 129 return; 130 } 131 132 var results = index.query(function (query) { 133 var tokens = lunr.tokenizer(input) 134 query.term(tokens, { 135 boost: 10 136 }); 137 query.term(tokens, { 138 wildcard: lunr.Query.wildcard.TRAILING 139 }); 140 }); 141 142 if ((results.length == 0) && (input.length > 2)) { 143 var tokens = lunr.tokenizer(input).filter(function(token, i) { 144 return token.str.length < 20; 145 }) 146 if (tokens.length > 0) { 147 results = index.query(function (query) { 148 query.term(tokens, { 149 editDistance: Math.round(Math.sqrt(input.length / 2 - 1)) 150 }); 151 }); 152 } 153 } 154 155 if (results.length == 0) { 156 var noResultsDiv = document.createElement('div'); 157 noResultsDiv.classList.add('search-no-result'); 158 noResultsDiv.innerText = 'No results found'; 159 searchResults.appendChild(noResultsDiv); 160 161 } else { 162 var resultsList = document.createElement('ul'); 163 resultsList.classList.add('search-results-list'); 164 searchResults.appendChild(resultsList); 165 166 addResults(resultsList, results, 0, 10, 100, currentSearchIndex); 167 } 168 169 function addResults(resultsList, results, start, batchSize, batchMillis, searchIndex) { 170 if (searchIndex != currentSearchIndex) { 171 return; 172 } 173 for (var i = start; i < (start + batchSize); i++) { 174 if (i == results.length) { 175 return; 176 } 177 addResult(resultsList, results[i]); 178 } 179 setTimeout(function() { 180 addResults(resultsList, results, start + batchSize, batchSize, batchMillis, searchIndex); 181 }, batchMillis); 182 } 183 184 function addResult(resultsList, result) { 185 var doc = docs[result.ref]; 186 187 var resultsListItem = document.createElement('li'); 188 resultsListItem.classList.add('search-results-list-item'); 189 resultsList.appendChild(resultsListItem); 190 191 var resultLink = document.createElement('a'); 192 resultLink.classList.add('search-result'); 193 resultLink.setAttribute('href', doc.url); 194 resultsListItem.appendChild(resultLink); 195 196 var resultTitle = document.createElement('div'); 197 resultTitle.classList.add('search-result-title'); 198 resultLink.appendChild(resultTitle); 199 200 var resultDoc = document.createElement('div');
201 resultDoc.classList.add('search-result-doc'); 202 resultDoc.innerHTML = '<svg viewBox="0 0 24 24" class="search-result-icon"><use xlink:href="#svg-doc"></use></svg>'; 203 resultTitle.appendChild(resultDoc); 204 205 var resultDocTitle = document.createElement('div'); 206 resultDocTitle.classList.add('search-result-doc-title'); 207 resultDocTitle.innerHTML = doc.doc; 208 resultDoc.appendChild(resultDocTitle); 209 var resultDocOrSection = resultDocTitle; 210 211 if (doc.doc != doc.title) { 212 resultDoc.classList.add('search-result-doc-parent'); 213 var resultSection = document.createElement('div'); 214 resultSection.classList.add('search-result-section'); 215 resultSection.innerHTML = doc.title; 216 resultTitle.appendChild(resultSection); 217 resultDocOrSection = resultSection; 218 } 219 220 var metadata = result.matchData.metadata; 221 var titlePositions = []; 222 var contentPositions = []; 223 for (var j in metadata) { 224 var meta = metadata[j]; 225 if (meta.title) { 226 var positions = meta.title.position; 227 for (var k in positions) { 228 titlePositions.push(positions[k]); 229 } 230 } 231 if (meta.content) { 232 var positions = meta.content.position; 233 for (var k in positions) { 234 var position = positions[k]; 235 var previewStart = position[0]; 236 var previewEnd = position[0] + position[1]; 237 var ellipsesBefore = true; 238 var ellipsesAfter = true; 239 for (var k = 0; k < 3; k++) { 240 var nextSpace = doc.content.lastIndexOf(' ', previewStart - 2); 241 var nextDot = doc.content.lastIndexOf('. ', previewStart - 2); 242 if ((nextDot >= 0) && (nextDot > nextSpace)) { 243 previewStart = nextDot + 1; 244 ellipsesBefore = false; 245 break; 246 } 247 if (nextSpace < 0) { 248 previewStart = 0; 249 ellipsesBefore = false; 250 break; 251 } 252 previewStart = nextSpace + 1; 253 } 254 for (var k = 0; k < 3; k++) { 255 var nextSpace = doc.content.indexOf(' ', previewEnd + 1); 256 var nextDot = doc.content.indexOf('. ', previewEnd + 1); 257 if ((nextDot >= 0) && (nextDot < nextSpace)) { 258 previewEnd = nextDot; 259 ellipsesAfter = false; 260 break; 261 } 262 if (nextSpace < 0) { 263 previewEnd = doc.content.length; 264 ellipsesAfter = false; 265 break; 266 } 267 previewEnd = nextSpace; 268 } 269 contentPositions.push({ 270 highlight: position, 271 previewStart: previewStart, previewEnd: previewEnd, 272 ellipsesBefore: ellipsesBefore, ellipsesAfter: ellipsesAfter 273 }); 274 } 275 } 276 } 277 278 if (titlePositions.length > 0) { 279 titlePositions.sort(function(p1, p2){ return p1[0] - p2[0] }); 280 resultDocOrSection.innerHTML = ''; 281 addHighlightedText(resultDocOrSection, doc.title, 0, doc.title.length, titlePositions); 282 } 283 284 if (contentPositions.length > 0) { 285 contentPositions.sort(function(p1, p2){ return p1.highlight[0] - p2.highlight[0] }); 286 var contentPosition = contentPositions[0]; 287 var previewPosition = { 288 highlight: [contentPosition.highlight], 289 previewStart: contentPosition.previewStart, previewEnd: contentPosition.previewEnd, 290 ellipsesBefore: contentPosition.ellipsesBefore, ellipsesAfter: contentPosition.ellipsesAfter 291 }; 292 var previewPositions = [previewPosition]; 293 for (var j = 1; j < contentPositions.length; j++) { 294 contentPosition = contentPositions[j]; 295 if (previewPosition.previewEnd < contentPosition.previewStart) { 296 previewPosition = { 297 highlight: [contentPosition.highlight], 298 previewStart: contentPosition.previewStart, previewEnd: contentPosition.previewEnd, 299 ellipsesBefore: contentPosition.ellipsesBefore, ellipsesAfter: contentPosition.ellipsesAfter 300 } 301 previewPositions.push(previewPosition); 302 } else { 303 previewPosition.highlight.push(contentPosition.highlight); 304 previewPosition.previewEnd = contentPosition.previewEnd; 305 previewPosition.ellipsesAfter = contentPosition.ellipsesAfter; 306 } 307 } 308 309 var resultPreviews = document.createElement('div'); 310 resultPreviews.classList.add('search-result-previews'); 311 resultLink.appendChild(resultPreviews); 312 313 var content = doc.content; 314 for (var j = 0; j < Math.min(previewPositions.length, 2); j++) { 315 var position = previewPositions[j]; 316 317 var resultPreview = document.createElement('div'); 318 resultPreview.classList.add('search-result-preview'); 319 resultPreviews.appendChild(resultPreview); 320 321 if (position.ellipsesBefore) { 322 resultPreview.appendChild(document.createTextNode('... ')); 323 } 324 addHighlightedText(resultPreview, content, position.previewStart, position.previewEnd, position.highlight); 325 if (position.ellipsesAfter) { 326 resultPreview.appendChild(document.createTextNode(' ...')); 327 } 328 } 329 } 330 var resultRelUrl = document.createElement('span'); 331 resultRelUrl.classList.add('search-result-rel-url'); 332 resultRelUrl.innerText = doc.relUrl; 333 resultTitle.appendChild(resultRelUrl); 334 } 335 336 function addHighlightedText(parent, text, start, end, positions) { 337 var index = start; 338 for (var i in positions) { 339 var position = positions[i]; 340 var span = document.createElement('span'); 341 span.innerHTML = text.substring(index, position[0]); 342 parent.appendChild(span); 343 index = position[0] + position[1]; 344 var highlight = document.createElement('span'); 345 highlight.classList.add('search-result-highlight'); 346 highlight.innerHTML = text.substring(position[0], index); 347 parent.appendChild(highlight); 348 } 349 var span = document.createElement('span'); 350 span.innerHTML = text.substring(index, end); 351 parent.appendChild(span); 352 } 353 } 354 355 jtd.addEvent(searchInput, 'focus', function(){ 356 setTimeout(update, 0); 357 }); 358 359 jtd.addEvent(searchInput, 'keyup', function(e){ 360 switch (e.keyCode) { 361 case 27: // When esc key is pressed, hide the results and clear the field 362 searchInput.value = ''; 363 break; 364 case 38: // arrow up 365 case 40: // arrow down 366 case 13: // enter 367 e.preventDefault(); 368 return; 369 } 370 update(); 371 }); 372 373 jtd.addEvent(searchInput, 'keydown', function(e){ 374 switch (e.keyCode) { 375 case 38: // arrow up 376 e.preventDefault(); 377 var active = document.querySelector('.search-result.active'); 378 if (active) { 379 active.classList.remove('active'); 380 if (active.parentElement.previousSibling) { 381 var previous = active.parentElement.previousSibling.querySelector('.search-result'); 382 previous.classList.add('active'); 383 } 384 } 385 return; 386 case 40: // arrow down 387 e.preventDefault(); 388 var active = document.querySelector('.search-result.active'); 389 if (active) { 390 if (active.parentElement.nextSibling) { 391 var next = active.parentElement.nextSibling.querySelector('.search-result'); 392 active.classList.remove('active'); 393 next.classList.add('active'); 394 } 395 } else { 396 var next = document.querySelector('.search-result'); 397 if (next) { 398 next.classList.add('active'); 399 } 400 } 401 return; 402 case 13: // enter 403 e.preventDefault(); 404 var active = document.querySelector('.search-result.active'); 405 if (active) { 406 active.click(); 407 } else { 408 var first = document.querySelector('.search-result'); 409 if (first) { 410 first.click(); 411 } 412 } 413 return; 414 } 415 }); 416 417 jtd.addEvent(document, 'click', function(e){ 418 if (e.target != searchInput) { 419 hideSearch(); 420 } 421 }); 422} 423 424// Switch theme 425 426jtd.getTheme = function() { 427 var cssFileHref = document.querySelector('[rel="stylesheet"]').getAttribute('href'); 428 return cssFileHref.substring(cssFileHref.lastIndexOf('-') + 1, cssFileHref.length - 4); 429} 430 431jtd.setTheme = function(theme) { 432 var cssFile = document.querySelector('[rel="stylesheet"]'); 433 cssFile.setAttribute('href', 'https://cmajor.dev/assets/css/just-the-docs-' + theme + '.css'); 434} 435 436// Document ready 437 438jtd.onReady(function(){ 439 initNav(); 440 initSearch(); 441}); 442 443})(window.jtd = window.jtd || {}); 444 445// jquerify.js 446// Add jQuery to any page that does not have it already. 447 448(function () { 449 450 if ( !window.jQuery ) { 451 var s = document.createElement('script'); 452 s.setAttribute('src', '//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js'); 453 document.body?.appendChild(s); 454 console.log('jquery loaded!'); 455 } 456 457 })();
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.