"use strict"; window.Aiken = (function () { /* Global Object */ const self = {}; /* Public Properties */ self.hashOffset = undefined; /* Public Methods */ self.getProperty = function (property) { let value; try { value = localStorage.getItem(`Aiken.${property}`); } catch (_error) {} if (-1 < [null, undefined].indexOf(value)) { return aikenConfig[property].values[0].value; } return value; }; self.icons = function () { return Array.from(arguments).reduce( (acc, name) => `${acc} `, "" ); }; self.scrollToHash = function () { const locationHash = arguments[0] || window.location.hash; const query = locationHash ? locationHash : "body"; const hashTop = document.querySelector(query).offsetTop; window.scrollTo(0, hashTop - self.hashOffset); return locationHash; }; self.toggleSidebar = function () { const previousState = bodyClasses.contains("drawer-open") ? "open" : "closed"; let state; if (0 < arguments.length) { state = false === arguments[0] ? "closed" : "open"; } else { state = "open" === previousState ? "closed" : "open"; } bodyClasses.remove(`drawer-${previousState}`); bodyClasses.add(`drawer-${state}`); if ("open" === state) { document.addEventListener("click", closeSidebar, false); } }; /* Private Properties */ const html = document.documentElement; const body = document.body; const bodyClasses = body.classList; const sidebar = document.querySelector(".sidebar"); const sidebarToggles = document.querySelectorAll(".sidebar-toggle"); const displayControls = document.createElement("div"); displayControls.classList.add("display-controls"); sidebar.appendChild(displayControls); /* Private Methods */ const initProperty = function (property) { const config = aikenConfig[property]; displayControls.insertAdjacentHTML( "beforeend", config.values.reduce( (acc, item, index) => { const tooltip = item.label ? `alt="${item.label}" title="${item.label}"` : ""; let inner; if (item.icons) { inner = self.icons(...item.icons); } else if (item.label) { inner = item.label; } else { inner = ""; } return ` ${acc} ${inner} `; }, ` ` ); setProperty(null, property, function () { return self.getProperty(property); }); }; const setProperty = function (_event, property) { const previousValue = self.getProperty(property); const update = 2 < arguments.length ? arguments[2] : aikenConfig[property].update; const value = update(); try { localStorage.setItem("Aiken." + property, value); } catch (_error) {} bodyClasses.remove(`${property}-${previousValue}`); bodyClasses.add(`${property}-${value}`); const isDefault = value === aikenConfig[property].values[0].value; const toggleClasses = document.querySelector( `#${property}-toggle` ).classList; toggleClasses.remove(`toggle-${isDefault ? 1 : 0}`); toggleClasses.add(`toggle-${isDefault ? 0 : 1}`); try { aikenConfig[property].callback(value); } catch (_error) {} return value; }; const setHashOffset = function () { const el = document.createElement("div"); el.style.cssText = ` height: var(--hash-offset); pointer-events: none; position: absolute; visibility: hidden; width: 0; `; body.appendChild(el); self.hashOffset = parseInt( getComputedStyle(el).getPropertyValue("height") || "0" ); body.removeChild(el); }; const closeSidebar = function (event) { if (!event.target.closest(".sidebar-toggle")) { document.removeEventListener("click", closeSidebar, false); self.toggleSidebar(false); } }; const addEvent = function (el, type, handler) { if (el.attachEvent) el.attachEvent("on" + type, handler); else el.addEventListener(type, handler); }; const searchLoaded = function (index, docs) { const preview_words_after = 10; const preview_words_before = 5; const previews = 3; const searchInput = document.getElementById("search-input"); const searchNavButton = document.getElementById("search-nav-button"); const searchResults = document.getElementById("search-results"); let currentInput; let currentSearchIndex = 0; function showSearch() { document.documentElement.classList.add("search-active"); } searchNavButton.addEventListener("click", function (e) { e.stopPropagation(); showSearch(); setTimeout(function () { searchInput.focus(); }, 0); }); function hideSearch() { document.documentElement.classList.remove("search-active"); } function update() { currentSearchIndex++; const input = searchInput.value; showSearch(); if (input === currentInput) { return; } currentInput = input; searchResults.innerHTML = ""; if (input === "") { return; } let results = index.query(function (query) { const tokens = lunr.tokenizer(input); query.term(tokens, { boost: 10, }); query.term(tokens, { wildcard: lunr.Query.wildcard.TRAILING, }); }); if (results.length == 0 && input.length > 2) { const tokens = lunr.tokenizer(input).filter(function (token, i) { return token.str.length < 20; }); if (tokens.length > 0) { results = index.query(function (query) { query.term(tokens, { editDistance: Math.round(Math.sqrt(input.length / 2 - 1)), }); }); } } if (results.length == 0) { const noResultsDiv = document.createElement("div"); noResultsDiv.classList.add("search-no-result"); noResultsDiv.innerText = "No results found"; searchResults.appendChild(noResultsDiv); } else { const resultsList = document.createElement("ul"); resultsList.classList.add("search-results-list"); searchResults.appendChild(resultsList); addResults(resultsList, results, 0, 10, 100, currentSearchIndex); } function addResults( resultsList, results, start, batchSize, batchMillis, searchIndex ) { if (searchIndex != currentSearchIndex) { return; } for (let i = start; i < start + batchSize; i++) { if (i == results.length) { return; } addResult(resultsList, results[i]); } setTimeout(function () { addResults( resultsList, results, start + batchSize, batchSize, batchMillis, searchIndex ); }, batchMillis); } function addResult(resultsList, result) { const doc = docs[result.ref]; const resultsListItem = document.createElement("li"); resultsListItem.classList.add("search-results-list-item"); resultsList.appendChild(resultsListItem); const resultLink = document.createElement("a"); resultLink.classList.add("search-result"); resultLink.setAttribute("href", `${window.breadcrumbs}/${doc.url}`); resultsListItem.appendChild(resultLink); const resultTitle = document.createElement("div"); resultTitle.classList.add("search-result-title"); resultLink.appendChild(resultTitle); const resultDoc = document.createElement("div"); resultDoc.classList.add("search-result-doc"); resultDoc.innerHTML = ''; resultTitle.appendChild(resultDoc); const resultDocTitle = document.createElement("div"); resultDocTitle.classList.add("search-result-doc-title"); resultDocTitle.innerHTML = doc.doc; resultDoc.appendChild(resultDocTitle); let resultDocOrSection = resultDocTitle; if (doc.doc != doc.title) { resultDoc.classList.add("search-result-doc-parent"); const resultSection = document.createElement("div"); resultSection.classList.add("search-result-section"); resultSection.innerHTML = doc.title; resultTitle.appendChild(resultSection); resultDocOrSection = resultSection; } const metadata = result.matchData.metadata; const titlePositions = []; const contentPositions = []; for (let j in metadata) { const meta = metadata[j]; if (meta.title) { const positions = meta.title.position; for (let k in positions) { titlePositions.push(positions[k]); } } if (meta.content) { const positions = meta.content.position; for (let k in positions) { const position = positions[k]; let previewStart = position[0]; let previewEnd = position[0] + position[1]; let ellipsesBefore = true; let ellipsesAfter = true; for (let k = 0; k < preview_words_before; k++) { const nextSpace = doc.content.lastIndexOf( " ", previewStart - 2 ); const nextDot = doc.content.lastIndexOf(". ", previewStart - 2); if (nextDot >= 0 && nextDot > nextSpace) { previewStart = nextDot + 1; ellipsesBefore = false; break; } if (nextSpace < 0) { previewStart = 0; ellipsesBefore = false; break; } previewStart = nextSpace + 1; } for (let k = 0; k < preview_words_after; k++) { const nextSpace = doc.content.indexOf(" ", previewEnd + 1); const nextDot = doc.content.indexOf(". ", previewEnd + 1); if (nextDot >= 0 && nextDot < nextSpace) { previewEnd = nextDot; ellipsesAfter = false; break; } if (nextSpace < 0) { previewEnd = doc.content.length; ellipsesAfter = false; break; } previewEnd = nextSpace; } contentPositions.push({ highlight: position, previewStart: previewStart, previewEnd: previewEnd, ellipsesBefore: ellipsesBefore, ellipsesAfter: ellipsesAfter, }); } } } if (titlePositions.length > 0) { titlePositions.sort(function (p1, p2) { return p1[0] - p2[0]; }); resultDocOrSection.innerHTML = ""; addHighlightedText( resultDocOrSection, doc.title, 0, doc.title.length, titlePositions ); } if (contentPositions.length > 0) { contentPositions.sort(function (p1, p2) { return p1.highlight[0] - p2.highlight[0]; }); let contentPosition = contentPositions[0]; let previewPosition = { highlight: [contentPosition.highlight], previewStart: contentPosition.previewStart, previewEnd: contentPosition.previewEnd, ellipsesBefore: contentPosition.ellipsesBefore, ellipsesAfter: contentPosition.ellipsesAfter, }; const previewPositions = [previewPosition]; for (let j = 1; j < contentPositions.length; j++) { contentPosition = contentPositions[j]; if (previewPosition.previewEnd < contentPosition.previewStart) { previewPosition = { highlight: [contentPosition.highlight], previewStart: contentPosition.previewStart, previewEnd: contentPosition.previewEnd, ellipsesBefore: contentPosition.ellipsesBefore, ellipsesAfter: contentPosition.ellipsesAfter, }; previewPositions.push(previewPosition); } else { previewPosition.highlight.push(contentPosition.highlight); previewPosition.previewEnd = contentPosition.previewEnd; previewPosition.ellipsesAfter = contentPosition.ellipsesAfter; } } const resultPreviews = document.createElement("div"); resultPreviews.classList.add("search-result-previews"); resultLink.appendChild(resultPreviews); const content = doc.content; for ( let j = 0; j < Math.min(previewPositions.length, previews); j++ ) { const position = previewPositions[j]; const resultPreview = document.createElement("div"); resultPreview.classList.add("search-result-preview"); resultPreviews.appendChild(resultPreview); if (position.ellipsesBefore) { resultPreview.appendChild(document.createTextNode("... ")); } addHighlightedText( resultPreview, content, position.previewStart, position.previewEnd, position.highlight ); if (position.ellipsesAfter) { resultPreview.appendChild(document.createTextNode(" ...")); } } } const resultRelUrl = document.createElement("span"); resultRelUrl.classList.add("search-result-rel-url"); resultRelUrl.innerText = doc.url; resultTitle.appendChild(resultRelUrl); } function addHighlightedText(parent, text, start, end, positions) { let index = start; for (let i in positions) { const position = positions[i]; const span = document.createElement("span"); span.innerHTML = text.substring(index, position[0]); parent.appendChild(span); index = position[0] + position[1]; const highlight = document.createElement("span"); highlight.classList.add("search-result-highlight"); highlight.innerHTML = text.substring(position[0], index); parent.appendChild(highlight); } const span = document.createElement("span"); span.innerHTML = text.substring(index, end); parent.appendChild(span); } } addEvent(searchInput, "focus", function () { setTimeout(update, 0); }); addEvent(searchInput, "keyup", function (e) { switch (e.keyCode) { case 27: // When esc key is pressed, hide the results and clear the field searchInput.value = ""; break; case 38: // arrow up case 40: // arrow down case 13: // enter e.preventDefault(); return; } update(); }); addEvent(searchInput, "keydown", function (e) { let active; switch (e.keyCode) { case 38: // arrow up e.preventDefault(); active = document.querySelector(".search-result.active"); if (active) { active.classList.remove("active"); if (active.parentElement.previousSibling) { const previous = active.parentElement.previousSibling.querySelector( ".search-result" ); previous.classList.add("active"); } } return; case 40: // arrow down e.preventDefault(); active = document.querySelector(".search-result.active"); if (active) { if (active.parentElement.nextSibling) { const next = active.parentElement.nextSibling.querySelector( ".search-result" ); active.classList.remove("active"); next.classList.add("active"); } } else { const next = document.querySelector(".search-result"); if (next) { next.classList.add("active"); } } return; case 13: // enter e.preventDefault(); active = document.querySelector(".search-result.active"); if (active) { active.click(); } else { const first = document.querySelector(".search-result"); if (first) { first.click(); } } return; } }); addEvent(document, "click", function (e) { if (e.target != searchInput) { hideSearch(); } }); }; self.initSearch = function initSeach(docs) { // enable support for hyphenated search words lunr.tokenizer.separator = /[\s/]+/; const index = lunr(function () { this.ref("id"); this.field("title", { boost: 200 }); this.field("content", { boost: 2 }); this.field("url"); this.metadataWhitelist = ["position"]; for (let [i, entry] of docs.entries()) { this.add({ id: i, title: entry.title, content: entry.content, url: `${window.breadcrumbs}/${entry.url}`, }); } }); searchLoaded(index, docs); }; const init = function () { for (let property in aikenConfig) { initProperty(property); const toggle = document.querySelector(`#${property}-toggle`); toggle.addEventListener("click", function (event) { setProperty(event, property); }); } sidebarToggles.forEach(function (sidebarToggle) { sidebarToggle.addEventListener("click", function (event) { event.preventDefault(); self.toggleSidebar(); }); }); setHashOffset(); window.addEventListener("load", function (_event) { self.scrollToHash(); }); window.addEventListener("hashchange", function (_event) { self.scrollToHash(); }); document .querySelectorAll( ` .module-name > a, .member-name a[href^='#'] ` ) .forEach(function (title) { title.innerHTML = title.innerHTML.replace( /([A-Z])|([_/])/g, "$2$1" ); }); }; /* Initialise */ init(); return self; })();