626 lines
22 KiB
JavaScript
626 lines
22 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 indexControl = document.getElementById("cineraIndexControl");
|
|
var indexSort = indexControl.querySelector("#cineraIndexSort");
|
|
var indexSortChronological = true;
|
|
|
|
var filterMenu = indexControl.querySelector(".cineraIndexFilter");
|
|
if(filterMenu)
|
|
{
|
|
var filterContainer = filterMenu.querySelector(".filter_container");
|
|
//menuState.push(linkMenu);
|
|
|
|
filterMenu.addEventListener("mouseenter", function(ev) {
|
|
filterContainer.style.display = "block";
|
|
});
|
|
|
|
filterMenu.addEventListener("mouseleave", function(ev) {
|
|
filterContainer.style.display = "none";
|
|
});
|
|
}
|
|
|
|
function hideEntriesOfProject(ProjectElement)
|
|
{
|
|
if(!ProjectElement.classList.contains("off"))
|
|
{
|
|
ProjectElement.classList.add("off");
|
|
}
|
|
var baseURL = ProjectElement.attributes.getNamedItem("data-baseURL").value;
|
|
for(var i = 0; i < projects.length; ++i)
|
|
{
|
|
var ThisProject = projects[i];
|
|
if(ThisProject.baseURL === baseURL)
|
|
{
|
|
ThisProject.filteredOut = true;
|
|
if(ThisProject.entriesContainer != null)
|
|
{
|
|
ThisProject.entriesContainer.style.display = "none";
|
|
disableSprite(ThisProject.entriesContainer.parentElement);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function showEntriesOfProject(ProjectElement)
|
|
{
|
|
if(ProjectElement.classList.contains("off"))
|
|
{
|
|
ProjectElement.classList.remove("off");
|
|
}
|
|
var baseURL = ProjectElement.attributes.getNamedItem("data-baseURL").value;
|
|
for(var i = 0; i < projects.length; ++i)
|
|
{
|
|
var ThisProject = projects[i];
|
|
if(ThisProject.baseURL === baseURL)
|
|
{
|
|
ThisProject.filteredOut = false;
|
|
if(ThisProject.entriesContainer != null)
|
|
{
|
|
ThisProject.entriesContainer.style.display = "flex";
|
|
enableSprite(ThisProject.entriesContainer.parentElement);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function hideProjectSearchResults(baseURL)
|
|
{
|
|
var cineraResults = document.getElementById("cineraResults");
|
|
if(cineraResults)
|
|
{
|
|
var cineraResultsProjects = cineraResults.querySelectorAll(".projectContainer");
|
|
for(var i = 0; i < cineraResultsProjects.length; ++i)
|
|
{
|
|
var resultBaseURL = cineraResultsProjects[i].attributes.getNamedItem("data-baseURL").value;
|
|
if(baseURL === resultBaseURL)
|
|
{
|
|
cineraResultsProjects[i].style.display = "none";
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function showProjectSearchResults(baseURL)
|
|
{
|
|
var cineraResults = document.getElementById("cineraResults");
|
|
if(cineraResults)
|
|
{
|
|
var cineraResultsProjects = cineraResults.querySelectorAll(".projectContainer");
|
|
for(var i = 0; i < cineraResultsProjects.length; ++i)
|
|
{
|
|
var resultBaseURL = cineraResultsProjects[i].attributes.getNamedItem("data-baseURL").value;
|
|
if(baseURL === resultBaseURL)
|
|
{
|
|
cineraResultsProjects[i].style.display = "flex";
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function toggleEntriesOfProjectAndChildren(ProjectFilterElement)
|
|
{
|
|
var baseURL = ProjectFilterElement.attributes.getNamedItem("data-baseURL").value;
|
|
var shouldShow = ProjectFilterElement.classList.contains("off");
|
|
if(shouldShow)
|
|
{
|
|
ProjectFilterElement.classList.remove("off");
|
|
enableSprite(ProjectFilterElement);
|
|
}
|
|
else
|
|
{
|
|
ProjectFilterElement.classList.add("off");
|
|
disableSprite(ProjectFilterElement);
|
|
}
|
|
|
|
for(var i = 0; i < projects.length; ++i)
|
|
{
|
|
var ThisProject = projects[i];
|
|
if(ThisProject.baseURL === baseURL)
|
|
{
|
|
if(shouldShow)
|
|
{
|
|
ThisProject.filteredOut = false;
|
|
enableSprite(ThisProject.projectTitleElement.parentElement);
|
|
if(ThisProject.entriesContainer != null)
|
|
{
|
|
ThisProject.entriesContainer.style.display = "flex";
|
|
}
|
|
showProjectSearchResults(projects[i].baseURL);
|
|
}
|
|
else
|
|
{
|
|
ThisProject.filteredOut = true;
|
|
disableSprite(ThisProject.projectTitleElement.parentElement);
|
|
if(ThisProject.entriesContainer != null)
|
|
{
|
|
ThisProject.entriesContainer.style.display = "none";
|
|
}
|
|
hideProjectSearchResults(ThisProject.baseURL);
|
|
}
|
|
}
|
|
}
|
|
|
|
var indexChildFilterProjects = ProjectFilterElement.querySelectorAll(".cineraFilterProject");
|
|
|
|
for(var j = 0; j < indexChildFilterProjects.length; ++j)
|
|
{
|
|
var ThisElement = indexChildFilterProjects[j];
|
|
if(shouldShow)
|
|
{
|
|
var baseURL = ThisElement.attributes.getNamedItem("data-baseURL").value;
|
|
showEntriesOfProject(ThisElement);
|
|
showProjectSearchResults(baseURL);
|
|
}
|
|
else
|
|
{
|
|
var baseURL = ThisElement.attributes.getNamedItem("data-baseURL").value;
|
|
hideEntriesOfProject(ThisElement);
|
|
hideProjectSearchResults(baseURL);
|
|
}
|
|
}
|
|
}
|
|
|
|
var indexFilter = indexControl.querySelector(".cineraIndexFilter");
|
|
if(indexFilter)
|
|
{
|
|
var indexFilterProjects = indexFilter.querySelectorAll(".cineraFilterProject");
|
|
for(var i = 0; i < indexFilterProjects.length; ++i)
|
|
{
|
|
indexFilterProjects[i].addEventListener("mouseover", function(ev) {
|
|
ev.stopPropagation();
|
|
this.classList.add("focused");
|
|
focusSprite(this);
|
|
});
|
|
indexFilterProjects[i].addEventListener("mouseout", function(ev) {
|
|
ev.stopPropagation();
|
|
this.classList.remove("focused");
|
|
unfocusSprite(this);
|
|
});
|
|
indexFilterProjects[i].addEventListener("click", function(ev) {
|
|
ev.stopPropagation();
|
|
toggleEntriesOfProjectAndChildren(this);
|
|
});
|
|
}
|
|
}
|
|
|
|
var resultsSummary = document.getElementById("cineraResultsSummary");
|
|
var resultsContainer = document.getElementById("cineraResults");
|
|
|
|
var indexContainer = document.getElementById("cineraIndex");
|
|
|
|
var projectsContainer = indexContainer.querySelectorAll(".cineraIndexProject");
|
|
|
|
var projectContainerPrototype = document.createElement("DIV");
|
|
projectContainerPrototype.classList.add("projectContainer");
|
|
|
|
var dayContainerPrototype = document.createElement("DIV");
|
|
dayContainerPrototype.classList.add("dayContainer");
|
|
|
|
var dayNamePrototype = document.createElement("SPAN");
|
|
dayNamePrototype.classList.add("dayName");
|
|
dayContainerPrototype.appendChild(dayNamePrototype);
|
|
|
|
var markerListPrototype = document.createElement("DIV");
|
|
markerListPrototype.classList.add("markerList");
|
|
dayContainerPrototype.appendChild(markerListPrototype);
|
|
|
|
var markerPrototype = document.createElement("A");
|
|
markerPrototype.classList.add("marker");
|
|
if(resultsContainer.getAttribute("data-single") == 0)
|
|
{
|
|
markerPrototype.setAttribute("target", "_blank");
|
|
}
|
|
|
|
function prepareToParseIndexFile(project)
|
|
{
|
|
project.xhr.addEventListener("load", function() {
|
|
var contents = project.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.name != null && episode.title != null) {
|
|
episode.filename = episode.name;
|
|
episode.day = getEpisodeName(episode.filename + ".html.md");
|
|
episode.dayContainerPrototype = project.dayContainerPrototype;
|
|
episode.markerPrototype = markerPrototype;
|
|
episode.playerURLPrefix = project.playerURLPrefix;
|
|
project.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");
|
|
project.parsed = true;
|
|
runSearch(true);
|
|
});
|
|
project.xhr.addEventListener("error", function() {
|
|
console.error("Failed to load content");
|
|
});
|
|
}
|
|
|
|
var projects = [];
|
|
function prepareProjects()
|
|
{
|
|
for(var i = 0; i < projectsContainer.length; ++i)
|
|
{
|
|
var ID = projectsContainer[i].attributes.getNamedItem("data-project").value;
|
|
var baseURL = projectsContainer[i].attributes.getNamedItem("data-baseURL").value;
|
|
var playerLocation = projectsContainer[i].attributes.getNamedItem("data-playerLocation").value;
|
|
var theme = projectsContainer[i].classList.item(1);
|
|
|
|
projects[i] =
|
|
{
|
|
baseURL: baseURL,
|
|
playerURLPrefix: (baseURL ? baseURL + "/" : "") + (playerLocation ? playerLocation + "/" : ""),
|
|
indexLocation: (baseURL ? baseURL + "/" : "") + ID + ".index",
|
|
projectTitleElement: projectsContainer[i].querySelector(":scope > .cineraProjectTitle"),
|
|
entriesContainer: projectsContainer[i].querySelector(":scope > .cineraIndexEntries"),
|
|
dayContainerPrototype: dayContainerPrototype.cloneNode(true),
|
|
filteredOut: false,
|
|
parsed: false,
|
|
searched: false,
|
|
resultsToRender: [],
|
|
resultsIndex: 0,
|
|
theme: theme,
|
|
episodes: [],
|
|
xhr: new XMLHttpRequest(),
|
|
}
|
|
|
|
projects[i].dayContainerPrototype.classList.add(theme);
|
|
projects[i].dayContainerPrototype.children[1].classList.add(theme);
|
|
|
|
document.querySelector(".spinner").classList.add("show");
|
|
projects[i].xhr.open("GET", projects[i].indexLocation);
|
|
projects[i].xhr.setRequestHeader("Content-Type", "text/plain");
|
|
projects[i].xhr.send();
|
|
prepareToParseIndexFile(projects[i]);
|
|
}
|
|
}
|
|
prepareProjects();
|
|
|
|
indexSort.addEventListener("click", function(ev) {
|
|
if(indexSortChronological)
|
|
{
|
|
this.firstChild.nodeValue = "Sort: New to Old ⏷"
|
|
for(var i = 0; i < projects.length; ++i)
|
|
{
|
|
if(projects[i].entriesContainer)
|
|
{
|
|
projects[i].entriesContainer.style.flexFlow = "column-reverse";
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.firstChild.nodeValue = "Sort: Old to New ⏶"
|
|
for(var i = 0; i < projects.length; ++i)
|
|
{
|
|
if(projects[i].entriesContainer)
|
|
{
|
|
projects[i].entriesContainer.style.flexFlow = "column";
|
|
}
|
|
}
|
|
}
|
|
indexSortChronological = !indexSortChronological;
|
|
runSearch(true);
|
|
});
|
|
|
|
var lastQuery = null;
|
|
var markerList = null;
|
|
var projectContainer = null;
|
|
var resultsMarkerIndex = -1;
|
|
var rendering = false;
|
|
|
|
var highlightPrototype = document.createElement("B");
|
|
|
|
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 resetProjectsForSearch()
|
|
{
|
|
for(var i = 0; i < projects.length; ++i)
|
|
{
|
|
var project = projects[i];
|
|
project.searched = false;
|
|
project.resultsToRender = [];
|
|
}
|
|
}
|
|
|
|
var renderHandle;
|
|
|
|
function runSearch(refresh) {
|
|
var queryStr = document.getElementById("query").value;
|
|
if (refresh || lastQuery != queryStr) {
|
|
var oldResultsContainer = resultsContainer;
|
|
resultsContainer = oldResultsContainer.cloneNode(false);
|
|
oldResultsContainer.parentNode.insertBefore(resultsContainer, oldResultsContainer);
|
|
oldResultsContainer.remove();
|
|
for(var i = 0; i < projects.length; ++i)
|
|
{
|
|
projects[i].resultsIndex = 0;
|
|
}
|
|
resultsMarkerIndex = -1;
|
|
}
|
|
lastQuery = queryStr;
|
|
|
|
resetProjectsForSearch();
|
|
|
|
var numEpisodes = 0;
|
|
var numMarkers = 0;
|
|
var totalSeconds = 0;
|
|
|
|
// NOTE(matt): Function defined within runSearch() so that we can modify numEpisodes, numMarkers and totalSeconds
|
|
function runSearchInterior(resultsToRender, query, episode)
|
|
{
|
|
var matches = [];
|
|
for (var k = 0; k < episode.markers.length; ++k) {
|
|
query.lastIndex = 0;
|
|
var result = query.exec(episode.markers[k].text);
|
|
if (result && result[0].length > 0) {
|
|
numMarkers++;
|
|
matches.push(episode.markers[k]);
|
|
if (k < episode.markers.length-1) {
|
|
totalSeconds += episode.markers[k+1].totalTime - episode.markers[k].totalTime;
|
|
}
|
|
}
|
|
}
|
|
if (matches.length > 0) {
|
|
numEpisodes++;
|
|
resultsToRender.push({
|
|
query: query,
|
|
episode: episode,
|
|
matches: matches
|
|
});
|
|
}
|
|
}
|
|
|
|
if (queryStr && queryStr.length > 0) {
|
|
indexContainer.style.display = "none";
|
|
resultsSummary.style.display = "block";
|
|
var shouldRender = false;
|
|
var query = new RegExp(queryStr.replace("(", "\\(").replace(")", "\\)").replace(/\|+/, "\|").replace(/\|$/, "").replace(/(^|[^\\])\\$/, "$1"), "gi");
|
|
|
|
// Visible
|
|
for(var i = 0; i < projects.length; ++i)
|
|
{
|
|
var project = projects[i];
|
|
if(project.parsed && !project.filteredOut && project.episodes.length > 0) {
|
|
if(indexSortChronological)
|
|
{
|
|
for(var j = 0; j < project.episodes.length; ++j) {
|
|
var episode = project.episodes[j];
|
|
runSearchInterior(project.resultsToRender, query, episode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(var j = project.episodes.length; j > 0; --j) {
|
|
var episode = project.episodes[j - 1];
|
|
runSearchInterior(project.resultsToRender, query, episode);
|
|
}
|
|
}
|
|
|
|
shouldRender = true;
|
|
project.searched = true;
|
|
|
|
}
|
|
}
|
|
|
|
// Invisible
|
|
for(var i = 0; i < projects.length; ++i)
|
|
{
|
|
var project = projects[i];
|
|
if(project.parsed && project.filteredOut && !project.searched && project.episodes.length > 0) {
|
|
if(indexSortChronological)
|
|
{
|
|
for(var j = 0; j < project.episodes.length; ++j) {
|
|
var episode = project.episodes[j];
|
|
runSearchInterior(project.resultsToRender, query, episode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(var j = project.episodes.length; j > 0; --j) {
|
|
var episode = project.episodes[j - 1];
|
|
runSearchInterior(project.resultsToRender, query, episode);
|
|
}
|
|
}
|
|
|
|
shouldRender = true;
|
|
project.searched = true;
|
|
|
|
}
|
|
}
|
|
|
|
if(shouldRender)
|
|
{
|
|
if (rendering) {
|
|
clearTimeout(renderHandle);
|
|
}
|
|
renderResults();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
indexContainer.style.display = "block";
|
|
resultsSummary.style.display = "none";
|
|
}
|
|
|
|
var totalTime = Math.floor(totalSeconds/60/60) + "h " + Math.floor(totalSeconds/60)%60 + "m " + totalSeconds%60 + "s ";
|
|
|
|
resultsSummary.textContent = "Found: " + numEpisodes + " episodes, " + numMarkers + " markers, " + totalTime + "total.";
|
|
}
|
|
|
|
function renderResults() {
|
|
var maxItems = 42;
|
|
var numItems = 0;
|
|
for(var i = 0; i < projects.length; ++i)
|
|
{
|
|
var project = projects[i];
|
|
if (project.resultsIndex < project.resultsToRender.length) {
|
|
rendering = true;
|
|
while (numItems < maxItems && project.resultsIndex < project.resultsToRender.length) {
|
|
var query = project.resultsToRender[project.resultsIndex].query;
|
|
var episode = project.resultsToRender[project.resultsIndex].episode;
|
|
var matches = project.resultsToRender[project.resultsIndex].matches;
|
|
if (resultsMarkerIndex == -1) {
|
|
if(project.resultsIndex == 0 || project.resultsToRender[project.resultsIndex - 1].episode.playerURLPrefix != episode.playerURLPrefix)
|
|
{
|
|
projectContainer = projectContainerPrototype.cloneNode(true);
|
|
for(var i = 0; i < projects.length; ++i)
|
|
{
|
|
if(projects[i].playerURLPrefix === episode.playerURLPrefix)
|
|
{
|
|
projectContainer.setAttribute("data-baseURL", projects[i].baseURL);
|
|
if(projects[i].filteredOut)
|
|
{
|
|
projectContainer.style.display = "none";
|
|
}
|
|
}
|
|
}
|
|
resultsContainer.appendChild(projectContainer);
|
|
}
|
|
else
|
|
{
|
|
projectContainer = resultsContainer.lastElementChild;
|
|
}
|
|
|
|
|
|
var dayContainer = episode.dayContainerPrototype.cloneNode(true);
|
|
var dayName = dayContainer.children[0];
|
|
markerList = dayContainer.children[1];
|
|
dayName.textContent = episode.day + ": " + episode.title;
|
|
projectContainer.appendChild(dayContainer);
|
|
resultsMarkerIndex = 0;
|
|
numItems++;
|
|
}
|
|
|
|
while (numItems < maxItems && resultsMarkerIndex < matches.length) {
|
|
var match = matches[resultsMarkerIndex];
|
|
var marker = episode.markerPrototype.cloneNode(true);
|
|
marker.setAttribute("href", episode.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);
|
|
numItems++;
|
|
resultsMarkerIndex++;
|
|
}
|
|
|
|
if (resultsMarkerIndex == matches.length) {
|
|
resultsMarkerIndex = -1;
|
|
project.resultsIndex++;
|
|
}
|
|
}
|
|
renderHandle = setTimeout(renderResults, 0);
|
|
} else {
|
|
rendering = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
function IsVisible(el) {
|
|
var xPos = 0;
|
|
var yPos = 0;
|
|
var Height = parseInt(getComputedStyle(el).height);
|
|
|
|
while (el) {
|
|
if (el.tagName == "BODY") {
|
|
var xScroll = el.scrollLeft || document.documentElement.scrollLeft;
|
|
var yScroll = el.scrollTop || document.documentElement.scrollTop;
|
|
|
|
xPos += (el.offsetLeft - xScroll + el.clientLeft)
|
|
yPos += (el.offsetTop - yScroll + el.clientTop)
|
|
} else {
|
|
xPos += (el.offsetLeft - el.scrollLeft + el.clientLeft);
|
|
yPos += (el.offsetTop - el.scrollTop + el.clientTop);
|
|
}
|
|
|
|
el = el.offsetParent;
|
|
}
|
|
return ((xPos > 0 && xPos < window.innerWidth) && (yPos > 0 && yPos + Height < window.innerHeight));
|
|
}
|
|
|
|
var queryEl = document.getElementById("query");
|
|
if(document.hasFocus() && IsVisible(queryEl)) { queryEl.focus(); }
|
|
queryEl.addEventListener("input", function(ev) {
|
|
history.replaceState(null, null, "#" + encodeURIComponent(queryEl.value));
|
|
runSearch();
|
|
});
|
|
|
|
runSearch();
|
|
|
|
// Testing
|