Annotation-System/cinera/cinera_search.js

268 lines
9.6 KiB
JavaScript

document.body.style.overflowY = "scroll";
if (location.hash && location.hash.length > 0) {
var initialQuery = location.hash;
if (initialQuery[0] == "#") {
initialQuery = initialQuery.slice(1);
}
document.getElementById("query").value = decodeURIComponent(initialQuery);
}
var indexContainer = document.getElementById("cineraIndex");
var projectID = indexContainer.attributes.getNamedItem("data-project").value;
var theme = indexContainer.classList.item(0);
var baseURL = indexContainer.attributes.getNamedItem("data-baseURL").value;
var playerLocation = indexContainer.attributes.getNamedItem("data-playerLocation").value;
var indexSort = indexContainer.querySelector("#cineraIndexSort");
var indexEntries = indexContainer.querySelector("#cineraIndexEntries");
var indexSortChronological = true;
indexSort.addEventListener("click", function(ev) {
if(indexSortChronological)
{
this.firstChild.nodeValue = "Sort: New to Old ⏷"
indexEntries.style.flexFlow = "column-reverse";
}
else
{
this.firstChild.nodeValue = "Sort: Old to New ⏶"
indexEntries.style.flexFlow = "column";
}
indexSortChronological = !indexSortChronological;
});
var lastQuery = null;
var resultsToRender = [];
var resultsIndex = 0;
var resultsMarkerIndex = 0;
var resultsContainer = document.getElementById("cineraResults");
var rendering = false;
var dayContainerPrototype = document.createElement("DIV");
dayContainerPrototype.classList.add("dayContainer");
dayContainerPrototype.classList.add(theme);
var dayNamePrototype = document.createElement("SPAN");
dayNamePrototype.classList.add("dayName");
dayContainerPrototype.appendChild(dayNamePrototype);
var markerListPrototype = document.createElement("DIV");
markerListPrototype.classList.add("markerList");
markerListPrototype.classList.add(theme);
dayContainerPrototype.appendChild(markerListPrototype);
var markerPrototype = document.createElement("A");
markerPrototype.classList.add("marker");
markerPrototype.setAttribute("target", "_blank");
var highlightPrototype = document.createElement("B");
var episodes = [];
function getEpisodeName(filename) {
var day = filename;
var dayParts = day.match(/([a-zA-Z_-]+)([0-9]+)?([a-zA-Z]+)?/);
day = dayParts[1].slice(0, 1).toUpperCase() + dayParts[1].slice(1) + (dayParts[2] ? " " + dayParts[2] : "") + (dayParts[3] ? " " + dayParts[3].toUpperCase() : "");
return day;
}
function markerTime(totalTime) {
var markTime = "(";
var hours = Math.floor(totalTime / 60 / 60);
var minutes = Math.floor(totalTime / 60) % 60;
var seconds = totalTime % 60;
if (hours > 0) {
markTime += padTimeComponent(hours) + ":";
}
markTime += padTimeComponent(minutes) + ":" + padTimeComponent(seconds) + ")";
return markTime;
}
function padTimeComponent(component) {
return (component < 10 ? "0" + component : component);
}
function runSearch() {
var queryStr = document.getElementById("query").value;
if (lastQuery != queryStr) {
var oldResultsContainer = resultsContainer;
resultsContainer = oldResultsContainer.cloneNode(false);
oldResultsContainer.parentNode.insertBefore(resultsContainer, oldResultsContainer);
oldResultsContainer.remove();
resultsIndex = 0;
resultsMarkerIndex = 0;
}
lastQuery = queryStr;
resultsToRender = [];
var numEpisodes = 0;
var numMarkers = 0;
var totalSeconds = 0;
if (queryStr && queryStr.length > 0) {
indexContainer.style.display = "none";
if (episodes.length > 0) {
var query = new RegExp(queryStr.replace("(", "\\(").replace(")", "\\)").replace(/\|+/, "\|").replace(/\|$/, "").replace(/(^|[^\\])\\$/, "$1"), "gi");
for (var i = 0; i < episodes.length; ++i) {
var episode = episodes[i];
var matches = [];
for (var j = 0; j < episode.markers.length; ++j) {
query.lastIndex = 0;
var result = query.exec(episode.markers[j].text);
if (result && result[0].length > 0) {
numMarkers++;
matches.push(episode.markers[j]);
if (j < episode.markers.length-1) {
totalSeconds += episode.markers[j+1].totalTime - episode.markers[j].totalTime;
}
}
}
if (matches.length > 0) {
numEpisodes++;
resultsToRender.push({
query: query,
episode: episode,
matches: matches
});
}
}
if (!rendering) {
renderResults();
}
} else {
document.querySelector(".spinner").classList.add("show");
}
}
else
{
indexContainer.style.display = "block";
}
var totalTime = Math.floor(totalSeconds/60/60) + "h " + Math.floor(totalSeconds/60)%60 + "m " + totalSeconds%60 + "s ";
document.getElementById("cineraResultsSummary").textContent = "Found: " + numEpisodes + " episodes, " + numMarkers + " markers, " + totalTime + "total.";
}
function renderMatches(renderStart) {
var query = resultsToRender[resultsIndex].query;
var episode = resultsToRender[resultsIndex].episode;
var matches = resultsToRender[resultsIndex].matches;
var markerList = null;
if (resultsMarkerIndex == 0) {
var dayContainer = dayContainerPrototype.cloneNode(true);
var dayName = dayContainer.children[0];
markerList = dayContainer.children[1];
dayName.textContent = episode.day + ": " + episode.title;
resultsContainer.appendChild(dayContainer);
} else {
markerList = document.querySelector("#cineraResults > .dayContainer:nth-child(" + (resultsIndex+1) + ") .markerList");
}
do {
var match = matches[resultsMarkerIndex];
var marker = markerPrototype.cloneNode();
var playerURLPrefix = (baseURL ? baseURL + "/" : "") + (playerLocation ? playerLocation + "/" : "");
marker.setAttribute("href", playerURLPrefix + episode.filename.replace(/"/g, "") + "/#" + match.totalTime);
query.lastIndex = 0;
var cursor = 0;
var text = match.text;
var result = null;
marker.appendChild(document.createTextNode(match.prettyTime + " "));
while (result = query.exec(text)) {
if (result.index > cursor) {
marker.appendChild(document.createTextNode(text.slice(cursor, result.index)));
}
var highlightEl = highlightPrototype.cloneNode();
highlightEl.textContent = result[0];
marker.appendChild(highlightEl);
cursor = result.index + result[0].length;
}
if (cursor < text.length) {
marker.appendChild(document.createTextNode(text.slice(cursor, text.length)));
}
markerList.appendChild(marker);
resultsMarkerIndex++;
} while (resultsMarkerIndex < matches.length && performance.now() - renderStart < 1);
return resultsMarkerIndex == matches.length;
}
function renderResults() {
if (resultsIndex < resultsToRender.length) {
rendering = true;
var renderStart = performance.now();
while (resultsIndex < resultsToRender.length && performance.now() - renderStart < 1) {
var done = renderMatches(renderStart);
if (done) {
resultsMarkerIndex = 0;
resultsIndex++;
}
}
requestAnimationFrame(renderResults);
} else {
rendering = false;
}
}
var queryEl = document.getElementById("query")
queryEl.addEventListener("input", function(ev) {
history.replaceState(null, null, "#" + encodeURIComponent(queryEl.value));
runSearch();
});
var xhr = new XMLHttpRequest();
xhr.addEventListener("load", function() {
var contents = xhr.response;
var lines = contents.split("\n");
var mode = "none";
var episode = null;
for (var i = 0; i < lines.length; ++i) {
var line = lines[i];
if (line.trim().length == 0) { continue; }
if (line == "---") {
if (episode != null) {
episode.filename = episode.name;
episode.day = getEpisodeName(episode.filename + ".html.md");
episodes.push(episode);
}
episode = {};
mode = "none";
} else if (line.startsWith("name:")) {
episode.name = line.slice(6);
} else if (line.startsWith("title:")) {
episode.title = line.slice(7).trim().slice(1, -1);
} else if (line.startsWith("markers")) {
mode = "markers";
episode.markers = [];
} else if (mode == "markers") {
var match = line.match(/"(\d+)": "(.+)"/);
if (match == null) {
console.log(name, line);
} else {
var totalTime = parseInt(line.slice(1));
var marker = {
totalTime: totalTime,
prettyTime: markerTime(totalTime),
text: match[2].replace(/\\"/g, "\"")
}
episode.markers.push(marker);
}
}
}
document.querySelector(".spinner").classList.remove("show");
runSearch();
});
xhr.addEventListener("error", function() {
console.error("Failed to load content");
});
var indexLocation = (baseURL ? baseURL + "/" : "") + projectID + ".index";
xhr.open("GET", indexLocation);
xhr.setRequestHeader("Content-Type", "text/plain");
xhr.send();
runSearch();