Crirc/js/doc.js
2021-01-25 11:04:34 +00:00

1087 lines
31 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

window.CrystalDocs = (window.CrystalDocs || {});
CrystalDocs.base_path = (CrystalDocs.base_path || "");
CrystalDocs.searchIndex = (CrystalDocs.searchIndex || false);
CrystalDocs.MAX_RESULTS_DISPLAY = 140;
CrystalDocs.runQuery = function(query) {
function searchType(type, query, results) {
var matches = [];
var matchedFields = [];
var name = type.full_name;
var i = name.lastIndexOf("::");
if (i > 0) {
name = name.substring(i + 2);
}
var nameMatches = query.matches(name);
if (nameMatches){
matches = matches.concat(nameMatches);
matchedFields.push("name");
}
var namespaceMatches = query.matchesNamespace(type.full_name);
if(namespaceMatches){
matches = matches.concat(namespaceMatches);
matchedFields.push("name");
}
var docMatches = query.matches(type.doc);
if(docMatches){
matches = matches.concat(docMatches);
matchedFields.push("doc");
}
if (matches.length > 0) {
results.push({
id: type.id,
result_type: "type",
kind: type.kind,
name: name,
full_name: type.full_name,
href: type.path,
summary: type.summary,
matched_fields: matchedFields,
matched_terms: matches
});
}
type.instance_methods.forEach(function(method) {
searchMethod(method, type, "instance_method", query, results);
})
type.class_methods.forEach(function(method) {
searchMethod(method, type, "class_method", query, results);
})
type.constructors.forEach(function(constructor) {
searchMethod(constructor, type, "constructor", query, results);
})
type.macros.forEach(function(macro) {
searchMethod(macro, type, "macro", query, results);
})
type.constants.forEach(function(constant){
searchConstant(constant, type, query, results);
});
type.types.forEach(function(subtype){
searchType(subtype, query, results);
});
};
function searchMethod(method, type, kind, query, results) {
var matches = [];
var matchedFields = [];
var nameMatches = query.matchesMethod(method.name, kind, type);
if (nameMatches){
matches = matches.concat(nameMatches);
matchedFields.push("name");
}
method.args.forEach(function(arg){
var argMatches = query.matches(arg.external_name);
if (argMatches) {
matches = matches.concat(argMatches);
matchedFields.push("args");
}
});
var docMatches = query.matches(type.doc);
if(docMatches){
matches = matches.concat(docMatches);
matchedFields.push("doc");
}
if (matches.length > 0) {
var typeMatches = query.matches(type.full_name);
if (typeMatches) {
matchedFields.push("type");
matches = matches.concat(typeMatches);
}
results.push({
id: method.id,
type: type.full_name,
result_type: kind,
name: method.name,
full_name: type.full_name + "#" + method.name,
args_string: method.args_string,
summary: method.summary,
href: type.path + "#" + method.id,
matched_fields: matchedFields,
matched_terms: matches
});
}
}
function searchConstant(constant, type, query, results) {
var matches = [];
var matchedFields = [];
var nameMatches = query.matches(constant.name);
if (nameMatches){
matches = matches.concat(nameMatches);
matchedFields.push("name");
}
var docMatches = query.matches(constant.doc);
if(docMatches){
matches = matches.concat(docMatches);
matchedFields.push("doc");
}
if (matches.length > 0) {
var typeMatches = query.matches(type.full_name);
if (typeMatches) {
matchedFields.push("type");
matches = matches.concat(typeMatches);
}
results.push({
id: constant.id,
type: type.full_name,
result_type: "constant",
name: constant.name,
full_name: type.full_name + "#" + constant.name,
value: constant.value,
summary: constant.summary,
href: type.path + "#" + constant.id,
matched_fields: matchedFields,
matched_terms: matches
});
}
}
var results = [];
searchType(CrystalDocs.searchIndex.program, query, results);
return results;
};
CrystalDocs.rankResults = function(results, query) {
function uniqueArray(ar) {
var j = {};
ar.forEach(function(v) {
j[v + "::" + typeof v] = v;
});
return Object.keys(j).map(function(v) {
return j[v];
});
}
results = results.sort(function(a, b) {
var matchedTermsDiff = uniqueArray(b.matched_terms).length - uniqueArray(a.matched_terms).length;
var aHasDocs = b.matched_fields.includes("doc");
var bHasDocs = b.matched_fields.includes("doc");
var aOnlyDocs = aHasDocs && a.matched_fields.length == 1;
var bOnlyDocs = bHasDocs && b.matched_fields.length == 1;
if (a.result_type == "type" && b.result_type != "type" && !aOnlyDocs) {
if(CrystalDocs.DEBUG) { console.log("a is type b not"); }
return -1;
} else if (b.result_type == "type" && a.result_type != "type" && !bOnlyDocs) {
if(CrystalDocs.DEBUG) { console.log("b is type, a not"); }
return 1;
}
if (a.matched_fields.includes("name")) {
if (b.matched_fields.includes("name")) {
var a_name = (CrystalDocs.prefixForType(a.result_type) || "") + ((a.result_type == "type") ? a.full_name : a.name);
var b_name = (CrystalDocs.prefixForType(b.result_type) || "") + ((b.result_type == "type") ? b.full_name : b.name);
a_name = a_name.toLowerCase();
b_name = b_name.toLowerCase();
for(var i = 0; i < query.normalizedTerms.length; i++) {
var term = query.terms[i].replace(/^::?|::?$/, "");
var a_orig_index = a_name.indexOf(term);
var b_orig_index = b_name.indexOf(term);
if(CrystalDocs.DEBUG) { console.log("term: " + term + " a: " + a_name + " b: " + b_name); }
if(CrystalDocs.DEBUG) { console.log(a_orig_index, b_orig_index, a_orig_index - b_orig_index); }
if (a_orig_index >= 0) {
if (b_orig_index >= 0) {
if(CrystalDocs.DEBUG) { console.log("both have exact match", a_orig_index > b_orig_index ? -1 : 1); }
if(a_orig_index != b_orig_index) {
if(CrystalDocs.DEBUG) { console.log("both have exact match at different positions", a_orig_index > b_orig_index ? 1 : -1); }
return a_orig_index > b_orig_index ? 1 : -1;
}
} else {
if(CrystalDocs.DEBUG) { console.log("a has exact match, b not"); }
return -1;
}
} else if (b_orig_index >= 0) {
if(CrystalDocs.DEBUG) { console.log("b has exact match, a not"); }
return 1;
}
}
} else {
if(CrystalDocs.DEBUG) { console.log("a has match in name, b not"); }
return -1;
}
} else if (
!a.matched_fields.includes("name") &&
b.matched_fields.includes("name")
) {
return 1;
}
if (matchedTermsDiff != 0 || (aHasDocs != bHasDocs)) {
if(CrystalDocs.DEBUG) { console.log("matchedTermsDiff: " + matchedTermsDiff, aHasDocs, bHasDocs); }
return matchedTermsDiff;
}
var matchedFieldsDiff = b.matched_fields.length - a.matched_fields.length;
if (matchedFieldsDiff != 0) {
if(CrystalDocs.DEBUG) { console.log("matched to different number of fields: " + matchedFieldsDiff); }
return matchedFieldsDiff > 0 ? 1 : -1;
}
var nameCompare = a.name.localeCompare(b.name);
if(nameCompare != 0){
if(CrystalDocs.DEBUG) { console.log("nameCompare resulted in: " + a.name + "<=>" + b.name + ": " + nameCompare); }
return nameCompare > 0 ? 1 : -1;
}
if(a.matched_fields.includes("args") && b.matched_fields.includes("args")) {
for(var i = 0; i < query.terms.length; i++) {
var term = query.terms[i];
var aIndex = a.args_string.indexOf(term);
var bIndex = b.args_string.indexOf(term);
if(CrystalDocs.DEBUG) { console.log("index of " + term + " in args_string: " + aIndex + " - " + bIndex); }
if(aIndex >= 0){
if(bIndex >= 0){
if(aIndex != bIndex){
return aIndex > bIndex ? 1 : -1;
}
}else{
return -1;
}
}else if(bIndex >= 0) {
return 1;
}
}
}
return 0;
});
if (results.length > 1) {
// if we have more than two search terms, only include results with the most matches
var bestMatchedTerms = uniqueArray(results[0].matched_terms).length;
results = results.filter(function(result) {
return uniqueArray(result.matched_terms).length + 1 >= bestMatchedTerms;
});
}
return results;
};
CrystalDocs.prefixForType = function(type) {
switch (type) {
case "instance_method":
return "#";
case "class_method":
case "macro":
case "constructor":
return ".";
default:
return false;
}
};
CrystalDocs.displaySearchResults = function(results, query) {
function sanitize(html){
return html.replace(/<(?!\/?code)[^>]+>/g, "");
}
// limit results
if (results.length > CrystalDocs.MAX_RESULTS_DISPLAY) {
results = results.slice(0, CrystalDocs.MAX_RESULTS_DISPLAY);
}
var $frag = document.createDocumentFragment();
var $resultsElem = document.querySelector(".search-list");
$resultsElem.innerHTML = "<!--" + JSON.stringify(query) + "-->";
results.forEach(function(result, i) {
var url = CrystalDocs.base_path + result.href;
var type = false;
var title = query.highlight(result.result_type == "type" ? result.full_name : result.name);
var prefix = CrystalDocs.prefixForType(result.result_type);
if (prefix) {
title = "<b>" + prefix + "</b>" + title;
}
title = "<strong>" + title + "</strong>";
if (result.args_string) {
title +=
"<span class=\"args\">" + query.highlight(result.args_string) + "</span>";
}
$elem = document.createElement("li");
$elem.className = "search-result search-result--" + result.result_type;
$elem.dataset.href = url;
$elem.setAttribute("title", result.full_name + " docs page");
var $title = document.createElement("div");
$title.setAttribute("class", "search-result__title");
var $titleLink = document.createElement("a");
$titleLink.setAttribute("href", url);
$titleLink.innerHTML = title;
$title.appendChild($titleLink);
$elem.appendChild($title);
$elem.addEventListener("click", function() {
$titleLink.click();
});
if (result.result_type !== "type") {
var $type = document.createElement("div");
$type.setAttribute("class", "search-result__type");
$type.innerHTML = query.highlight(result.type);
$elem.appendChild($type);
}
if(result.summary){
var $doc = document.createElement("div");
$doc.setAttribute("class", "search-result__doc");
$doc.innerHTML = query.highlight(sanitize(result.summary));
$elem.appendChild($doc);
}
$elem.appendChild(document.createComment(JSON.stringify(result)));
$frag.appendChild($elem);
});
$resultsElem.appendChild($frag);
CrystalDocs.toggleResultsList(true);
};
CrystalDocs.toggleResultsList = function(visible) {
if (visible) {
document.querySelector(".types-list").classList.add("hidden");
document.querySelector(".search-results").classList.remove("hidden");
} else {
document.querySelector(".types-list").classList.remove("hidden");
document.querySelector(".search-results").classList.add("hidden");
}
};
CrystalDocs.Query = function(string) {
this.original = string;
this.terms = string.split(/\s+/).filter(function(word) {
return CrystalDocs.Query.stripModifiers(word).length > 0;
});
var normalized = this.terms.map(CrystalDocs.Query.normalizeTerm);
this.normalizedTerms = normalized;
function runMatcher(field, matcher) {
if (!field) {
return false;
}
var normalizedValue = CrystalDocs.Query.normalizeTerm(field);
var matches = [];
normalized.forEach(function(term) {
if (matcher(normalizedValue, term)) {
matches.push(term);
}
});
return matches.length > 0 ? matches : false;
}
this.matches = function(field) {
return runMatcher(field, function(normalized, term) {
if (term[0] == "#" || term[0] == ".") {
return false;
}
return normalized.indexOf(term) >= 0;
});
};
function namespaceMatcher(normalized, term){
var i = term.indexOf(":");
if(i >= 0){
term = term.replace(/^::?|::?$/, "");
var index = normalized.indexOf(term);
if((index == 0) || (index > 0 && normalized[index-1] == ":")){
return true;
}
}
return false;
}
this.matchesMethod = function(name, kind, type) {
return runMatcher(name, function(normalized, term) {
var i = term.indexOf("#");
if(i >= 0){
if (kind != "instance_method") {
return false;
}
}else{
i = term.indexOf(".");
if(i >= 0){
if (kind != "class_method" && kind != "macro" && kind != "constructor") {
return false;
}
}else{
//neither # nor .
if(term.indexOf(":") && namespaceMatcher(normalized, term)){
return true;
}
}
}
var methodName = term;
if(i >= 0){
var termType = term.substring(0, i);
methodName = term.substring(i+1);
if(termType != "") {
if(CrystalDocs.Query.normalizeTerm(type.full_name).indexOf(termType) < 0){
return false;
}
}
}
return normalized.indexOf(methodName) >= 0;
});
};
this.matchesNamespace = function(namespace){
return runMatcher(namespace, namespaceMatcher);
};
this.highlight = function(string) {
if (typeof string == "undefined") {
return "";
}
function escapeRegExp(s) {
return s.replace(/[.*+?\^${}()|\[\]\\]/g, "\\$&").replace(/^[#\.:]+/, "");
}
return string.replace(
new RegExp("(" + this.normalizedTerms.map(escapeRegExp).join("|") + ")", "gi"),
"<mark>$1</mark>"
);
};
};
CrystalDocs.Query.normalizeTerm = function(term) {
return term.toLowerCase();
};
CrystalDocs.Query.stripModifiers = function(term) {
switch (term[0]) {
case "#":
case ".":
case ":":
return term.substr(1);
default:
return term;
}
}
CrystalDocs.search = function(string) {
if(!CrystalDocs.searchIndex) {
console.log("CrystalDocs search index not initialized, delaying search");
document.addEventListener("CrystalDocs:loaded", function listener(){
document.removeEventListener("CrystalDocs:loaded", listener);
CrystalDocs.search(string);
});
return;
}
document.dispatchEvent(new Event("CrystalDocs:searchStarted"));
var query = new CrystalDocs.Query(string);
var results = CrystalDocs.runQuery(query);
results = CrystalDocs.rankResults(results, query);
CrystalDocs.displaySearchResults(results, query);
document.dispatchEvent(new Event("CrystalDocs:searchPerformed"));
};
CrystalDocs.initializeIndex = function(data) {
CrystalDocs.searchIndex = data;
document.dispatchEvent(new Event("CrystalDocs:loaded"));
};
CrystalDocs.loadIndex = function() {
function loadJSON(file, callback) {
var xobj = new XMLHttpRequest();
xobj.overrideMimeType("application/json");
xobj.open("GET", file, true);
xobj.onreadystatechange = function() {
if (xobj.readyState == 4 && xobj.status == "200") {
callback(xobj.responseText);
}
};
xobj.send(null);
}
function loadScript(file) {
script = document.createElement("script");
script.src = file;
document.body.appendChild(script);
}
function parseJSON(json) {
CrystalDocs.initializeIndex(JSON.parse(json));
}
for(var i = 0; i < document.scripts.length; i++){
var script = document.scripts[i];
if (script.src && script.src.indexOf("js/doc.js") >= 0) {
if (script.src.indexOf("file://") == 0) {
// We need to support JSONP files for the search to work on local file system.
var jsonPath = script.src.replace("js/doc.js", "search-index.js");
loadScript(jsonPath);
return;
} else {
var jsonPath = script.src.replace("js/doc.js", "index.json");
loadJSON(jsonPath, parseJSON);
return;
}
}
}
console.error("Could not find location of js/doc.js");
};
// Callback for jsonp
function crystal_doc_search_index_callback(data) {
CrystalDocs.initializeIndex(data);
}
Navigator = function(sidebar, searchInput, list, leaveSearchScope){
this.list = list;
var self = this;
var performingSearch = false;
document.addEventListener('CrystalDocs:searchStarted', function(){
performingSearch = true;
});
document.addEventListener('CrystalDocs:searchDebounceStarted', function(){
performingSearch = true;
});
document.addEventListener('CrystalDocs:searchPerformed', function(){
performingSearch = false;
});
document.addEventListener('CrystalDocs:searchDebounceStopped', function(event){
performingSearch = false;
});
function delayWhileSearching(callback) {
if(performingSearch){
document.addEventListener('CrystalDocs:searchPerformed', function listener(){
document.removeEventListener('CrystalDocs:searchPerformed', listener);
// add some delay to let search results display kick in
setTimeout(callback, 100);
});
}else{
callback();
}
}
function clearMoveTimeout() {
clearTimeout(self.moveTimeout);
self.moveTimeout = null;
}
function startMoveTimeout(upwards){
/*if(self.moveTimeout) {
clearMoveTimeout();
}
var go = function() {
if (!self.moveTimeout) return;
self.move(upwards);
self.moveTimout = setTimeout(go, 600);
};
self.moveTimeout = setTimeout(go, 800);*/
}
function scrollCenter(element) {
var rect = element.getBoundingClientRect();
var middle = sidebar.clientHeight / 2;
sidebar.scrollTop += rect.top + rect.height / 2 - middle;
}
var move = this.move = function(upwards){
if(!this.current){
this.highlightFirst();
return true;
}
var next = upwards ? this.current.previousElementSibling : this.current.nextElementSibling;
if(next && next.classList) {
this.highlight(next);
scrollCenter(next);
return true;
}
return false;
};
this.moveRight = function(){
};
this.moveLeft = function(){
};
this.highlight = function(elem) {
if(!elem){
return;
}
this.removeHighlight();
this.current = elem;
this.current.classList.add("current");
};
this.highlightFirst = function(){
this.highlight(this.list.querySelector('li:first-child'));
};
this.removeHighlight = function() {
if(this.current){
this.current.classList.remove("current");
}
this.current = null;
}
this.openSelectedResult = function() {
if(this.current) {
this.current.click();
}
}
this.focus = function() {
searchInput.focus();
searchInput.select();
this.highlightFirst();
}
function handleKeyUp(event) {
switch(event.key) {
case "ArrowUp":
case "ArrowDown":
case "i":
case "j":
case "k":
case "l":
case "c":
case "h":
case "t":
case "n":
event.stopPropagation();
clearMoveTimeout();
}
}
function handleKeyDown(event) {
switch(event.key) {
case "Enter":
event.stopPropagation();
event.preventDefault();
leaveSearchScope();
self.openSelectedResult();
break;
case "Escape":
event.stopPropagation();
event.preventDefault();
leaveSearchScope();
break;
case "j":
case "c":
case "ArrowUp":
if(event.ctrlKey || event.key == "ArrowUp") {
event.stopPropagation();
self.move(true);
startMoveTimeout(true);
}
break;
case "k":
case "h":
case "ArrowDown":
if(event.ctrlKey || event.key == "ArrowDown") {
event.stopPropagation();
self.move(false);
startMoveTimeout(false);
}
break;
case "k":
case "t":
case "ArrowLeft":
if(event.ctrlKey || event.key == "ArrowLeft") {
event.stopPropagation();
self.moveLeft();
}
break;
case "l":
case "n":
case "ArrowRight":
if(event.ctrlKey || event.key == "ArrowRight") {
event.stopPropagation();
self.moveRight();
}
break;
}
}
function handleInputKeyUp(event) {
switch(event.key) {
case "ArrowUp":
case "ArrowDown":
event.stopPropagation();
event.preventDefault();
clearMoveTimeout();
}
}
function handleInputKeyDown(event) {
switch(event.key) {
case "Enter":
event.stopPropagation();
event.preventDefault();
delayWhileSearching(function(){
self.openSelectedResult();
leaveSearchScope();
});
break;
case "Escape":
event.stopPropagation();
event.preventDefault();
// remove focus from search input
leaveSearchScope();
sidebar.focus();
break;
case "ArrowUp":
event.stopPropagation();
event.preventDefault();
self.move(true);
startMoveTimeout(true);
break;
case "ArrowDown":
event.stopPropagation();
event.preventDefault();
self.move(false);
startMoveTimeout(false);
break;
}
}
sidebar.tabIndex = 100; // set tabIndex to enable keylistener
sidebar.addEventListener('keyup', function(event) {
handleKeyUp(event);
});
sidebar.addEventListener('keydown', function(event) {
handleKeyDown(event);
});
searchInput.addEventListener('keydown', function(event) {
handleInputKeyDown(event);
});
searchInput.addEventListener('keyup', function(event) {
handleInputKeyUp(event);
});
this.move();
};
CrystalDocs.initializeVersions = function () {
function loadJSON(file, callback) {
var xobj = new XMLHttpRequest();
xobj.overrideMimeType("application/json");
xobj.open("GET", file, true);
xobj.onreadystatechange = function() {
if (xobj.readyState == 4 && xobj.status == "200") {
callback(xobj.responseText);
}
};
xobj.send(null);
}
function parseJSON(json) {
CrystalDocs.loadConfig(JSON.parse(json));
}
$elem = document.querySelector("html > head > meta[name=\"crystal_docs.json_config_url\"]")
if ($elem == undefined) {
return
}
jsonURL = $elem.getAttribute("content")
if (jsonURL && jsonURL != "") {
loadJSON(jsonURL, parseJSON);
}
}
CrystalDocs.loadConfig = function (config) {
var projectVersions = config["versions"]
var currentVersion = document.querySelector("html > head > meta[name=\"crystal_docs.project_version\"]").getAttribute("content")
var currentVersionInList = projectVersions.find(function (element) {
return element.name == currentVersion
})
if (!currentVersionInList) {
projectVersions.unshift({ name: currentVersion, url: '#' })
}
$version = document.querySelector(".project-summary > .project-version")
$version.innerHTML = ""
$select = document.createElement("select")
$select.classList.add("project-versions-nav")
$select.addEventListener("change", function () {
window.location.href = this.value
})
projectVersions.forEach(function (version) {
$item = document.createElement("option")
$item.setAttribute("value", version.url)
$item.append(document.createTextNode(version.name))
if (version.name == currentVersion) {
$item.setAttribute("selected", true)
$item.setAttribute("disabled", true)
}
$select.append($item)
});
$form = document.createElement("form")
$form.setAttribute("autocomplete", "off")
$form.append($select)
$version.append($form)
}
document.addEventListener("DOMContentLoaded", function () {
CrystalDocs.initializeVersions()
})
var UsageModal = function(title, content) {
var $body = document.body;
var self = this;
var $modalBackground = document.createElement("div");
$modalBackground.classList.add("modal-background");
var $usageModal = document.createElement("div");
$usageModal.classList.add("usage-modal");
$modalBackground.appendChild($usageModal);
var $title = document.createElement("h3");
$title.classList.add("modal-title");
$title.innerHTML = title
$usageModal.appendChild($title);
var $closeButton = document.createElement("span");
$closeButton.classList.add("close-button");
$closeButton.setAttribute("title", "Close modal");
$closeButton.innerText = '×';
$usageModal.appendChild($closeButton);
$usageModal.insertAdjacentHTML("beforeend", content);
$modalBackground.addEventListener('click', function(event) {
var element = event.target || event.srcElement;
if(element == $modalBackground) {
self.hide();
}
});
$closeButton.addEventListener('click', function(event) {
self.hide();
});
$body.insertAdjacentElement('beforeend', $modalBackground);
this.show = function(){
$body.classList.add("js-modal-visible");
};
this.hide = function(){
$body.classList.remove("js-modal-visible");
};
this.isVisible = function(){
return $body.classList.contains("js-modal-visible");
}
}
document.addEventListener('DOMContentLoaded', function() {
var sessionStorage;
try {
sessionStorage = window.sessionStorage;
} catch (e) { }
if(!sessionStorage) {
sessionStorage = {
setItem: function() {},
getItem: function() {},
removeItem: function() {}
};
}
var repositoryName = document.querySelector('#repository-name').getAttribute('content');
var typesList = document.querySelector('.types-list');
var searchInput = document.querySelector('.search-input');
var parents = document.querySelectorAll('.types-list li.parent');
var scrollSidebarToOpenType = function(){
var openTypes = typesList.querySelectorAll('.current');
if (openTypes.length > 0) {
var lastOpenType = openTypes[openTypes.length - 1];
lastOpenType.scrollIntoView();
}
}
scrollSidebarToOpenType();
var setPersistentSearchQuery = function(value){
sessionStorage.setItem(repositoryName + '::search-input:value', value);
}
for(var i = 0; i < parents.length; i++) {
var _parent = parents[i];
_parent.addEventListener('click', function(e) {
e.stopPropagation();
if(e.target.tagName.toLowerCase() == 'li') {
if(e.target.className.match(/open/)) {
sessionStorage.removeItem(e.target.getAttribute('data-id'));
e.target.className = e.target.className.replace(/ +open/g, '');
} else {
sessionStorage.setItem(e.target.getAttribute('data-id'), '1');
if(e.target.className.indexOf('open') == -1) {
e.target.className += ' open';
}
}
}
});
if(sessionStorage.getItem(_parent.getAttribute('data-id')) == '1') {
_parent.className += ' open';
}
}
var leaveSearchScope = function(){
CrystalDocs.toggleResultsList(false);
window.focus();
}
var navigator = new Navigator(document.querySelector('.types-list'), searchInput, document.querySelector(".search-results"), leaveSearchScope);
CrystalDocs.loadIndex();
var searchTimeout;
var lastSearchText = false;
var performSearch = function() {
document.dispatchEvent(new Event("CrystalDocs:searchDebounceStarted"));
clearTimeout(searchTimeout);
searchTimeout = setTimeout(function() {
var text = searchInput.value;
if(text == "") {
CrystalDocs.toggleResultsList(false);
}else if(text == lastSearchText){
document.dispatchEvent(new Event("CrystalDocs:searchDebounceStopped"));
}else{
CrystalDocs.search(text);
navigator.highlightFirst();
searchInput.focus();
}
lastSearchText = text;
setPersistentSearchQuery(text);
}, 200);
};
if(location.hash.length > 3 && location.hash.substring(0,3) == "#q="){
// allows directly linking a search query which is then executed on the client
// this comes handy for establishing a custom browser search engine with https://crystal-lang.org/api/#q=%s as a search URL
// TODO: Add OpenSearch description
var searchQuery = location.hash.substring(3);
history.pushState({searchQuery: searchQuery}, "Search for " + searchQuery, location.href.replace(/#q=.*/, ""));
searchInput.value = searchQuery;
document.addEventListener('CrystalDocs:loaded', performSearch);
}
if (searchInput.value.length == 0) {
var searchText = sessionStorage.getItem(repositoryName + '::search-input:value');
if(searchText){
searchInput.value = searchText;
}
}
searchInput.addEventListener('keyup', performSearch);
searchInput.addEventListener('input', performSearch);
var usageModal = new UsageModal('Keyboard Shortcuts', '' +
'<ul class="usage-list">' +
' <li>' +
' <span class="usage-key">' +
' <kbd>s</kbd>,' +
' <kbd>/</kbd>' +
' </span>' +
' Search' +
' </li>' +
' <li>' +
' <kbd class="usage-key">Esc</kbd>' +
' Abort search / Close modal' +
' </li>' +
' <li>' +
' <span class="usage-key">' +
' <kbd>⇨</kbd>,' +
' <kbd>Enter</kbd>' +
' </span>' +
' Open highlighted result' +
' </li>' +
' <li>' +
' <span class="usage-key">' +
' <kbd>⇧</kbd>,' +
' <kbd>Ctrl+j</kbd>' +
' </span>' +
' Select previous result' +
' </li>' +
' <li>' +
' <span class="usage-key">' +
' <kbd>⇩</kbd>,' +
' <kbd>Ctrl+k</kbd>' +
' </span>' +
' Select next result' +
' </li>' +
' <li>' +
' <kbd class="usage-key">?</kbd>' +
' Show usage info' +
' </li>' +
'</ul>'
);
function handleShortkeys(event) {
var element = event.target || event.srcElement;
if(element.tagName == "INPUT" || element.tagName == "TEXTAREA" || element.parentElement.tagName == "TEXTAREA"){
return;
}
switch(event.key) {
case "?":
usageModal.show();
break;
case "Escape":
usageModal.hide();
break;
case "s":
case "/":
if(usageModal.isVisible()) {
return;
}
event.stopPropagation();
navigator.focus();
performSearch();
break;
}
}
document.addEventListener('keyup', handleShortkeys);
var scrollToEntryFromLocationHash = function() {
var hash = window.location.hash;
if (hash) {
var targetAnchor = decodeURI(hash.substr(1));
var targetEl = document.getElementById(targetAnchor)
if (targetEl) {
targetEl.offsetParent.scrollTop = targetEl.offsetTop;
}
}
};
window.addEventListener("hashchange", scrollToEntryFromLocationHash, false);
scrollToEntryFromLocationHash();
});