(function() {
function query(selector, root) {
return (root || document).querySelector(selector);
}
function ready(callback) {
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", callback, { once: true });
} else {
callback();
}
}
function createAutocomplete(input) {
var form = input.form;
var results = document.createElement("div");
var list = document.createElement("ul");
var requestTimer = null;
var controller = null;
var items = [];
var activeIndex = -1;
var blurTimer = null;
if (!form) return;
results.className = "ac_results";
results.hidden = true;
results.setAttribute("role", "listbox");
results.id = input.id + "_results";
list.setAttribute("role", "presentation");
results.appendChild(list);
input.setAttribute("autocomplete", "off");
input.setAttribute("aria-autocomplete", "list");
input.setAttribute("aria-controls", results.id);
input.setAttribute("aria-expanded", "false");
form.appendChild(results);
function syncResultsWidth() {
results.style.width = input.offsetWidth + "px";
}
function hideResults() {
results.hidden = true;
input.setAttribute("aria-expanded", "false");
input.removeAttribute("aria-activedescendant");
activeIndex = -1;
items = [];
list.innerHTML = "";
}
function setActive(index) {
if (!items.length) return;
activeIndex = (index + items.length) % items.length;
items.forEach(function(item, itemIndex) {
item.element.classList.toggle("ac_over", itemIndex === activeIndex);
});
input.setAttribute("aria-activedescendant", items[activeIndex].element.id);
}
function selectItem(item) {
input.value = item.values[1];
window.location.href = item.values[3];
}
function renderItems(lines) {
syncResultsWidth();
list.innerHTML = "";
items = lines.map(function(line, index) {
var values = line.split(",");
var element = document.createElement("li");
var label = document.createElement("span");
var namespace = document.createElement("small");
element.id = results.id + "_item_" + index;
element.setAttribute("role", "option");
element.className = index % 2 === 0 ? "ac_even" : "ac_odd";
label.textContent = values[0];
element.appendChild(label);
if (values[1] !== "") {
namespace.textContent = "(" + values[1] + ")";
element.appendChild(document.createTextNode(" "));
element.appendChild(namespace);
}
element.addEventListener("mouseenter", function() {
setActive(index);
});
element.addEventListener("mousedown", function(event) {
event.preventDefault();
selectItem(items[index]);
});
list.appendChild(element);
return { element: element, values: values };
});
if (items.length) {
results.hidden = false;
input.setAttribute("aria-expanded", "true");
setActive(0);
} else {
hideResults();
}
}
function fetchResults(term) {
if (controller) controller.abort();
controller = new AbortController();
input.classList.add("ac_loading");
fetch(
form.action +
"?q=" +
encodeURIComponent(term) +
"&_=" +
new Date().getTime(),
{
headers: {
"X-Requested-With": "XMLHttpRequest"
},
signal: controller.signal
}
)
.then(function(response) {
return response.text();
})
.then(function(text) {
var lines = text
.split("\n")
.map(function(line) {
return line.trim();
})
.filter(Boolean);
renderItems(lines);
})
.catch(function(error) {
if (error.name !== "AbortError") hideResults();
})
.finally(function() {
input.classList.remove("ac_loading");
});
}
input.addEventListener("input", function() {
clearTimeout(requestTimer);
if (blurTimer) clearTimeout(blurTimer);
if (!input.value.trim()) {
hideResults();
return;
}
requestTimer = setTimeout(function() {
fetchResults(input.value.trim());
}, 200);
});
input.addEventListener("keydown", function(event) {
if (results.hidden && (event.key === "ArrowDown" || event.key === "ArrowUp")) {
if (!input.value.trim()) return;
fetchResults(input.value.trim());
return;
}
if (event.key === "ArrowDown") {
event.preventDefault();
setActive(activeIndex + 1);
} else if (event.key === "ArrowUp") {
event.preventDefault();
setActive(activeIndex - 1);
} else if (event.key === "Enter") {
if (activeIndex >= 0 && items[activeIndex]) {
event.preventDefault();
selectItem(items[activeIndex]);
}
} else if (event.key === "Escape") {
hideResults();
}
});
input.addEventListener("blur", function() {
blurTimer = setTimeout(hideResults, 150);
});
input.addEventListener("focus", function() {
syncResultsWidth();
if (items.length) {
results.hidden = false;
input.setAttribute("aria-expanded", "true");
}
});
document.addEventListener("click", function(event) {
if (!form.contains(event.target)) hideResults();
});
window.addEventListener("resize", syncResultsWidth);
syncResultsWidth();
}
ready(function() {
var input = query("#search_box");
if (input) createAutocomplete(input);
});
})();