2021-01-25 18:09:30 +00:00
|
|
|
|
// Processing (Searching / Filtering)
|
|
|
|
|
//
|
|
|
|
|
var CineraProps = {
|
|
|
|
|
IsMobile: IsMobile(),
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Orientation: null,
|
2021-01-25 18:09:30 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var Search = {
|
|
|
|
|
ResultsSummary: null,
|
|
|
|
|
ResultsContainer: null,
|
|
|
|
|
ProjectsContainer: null,
|
|
|
|
|
QueryElement: null,
|
|
|
|
|
|
|
|
|
|
Projects: [],
|
|
|
|
|
|
|
|
|
|
LastQuery: null,
|
|
|
|
|
MarkerList: null,
|
|
|
|
|
ProjectContainer: null,
|
|
|
|
|
ResultsMarkerIndex: -1,
|
|
|
|
|
RenderHandle: undefined,
|
|
|
|
|
Rendering: false,
|
|
|
|
|
|
|
|
|
|
Prototypes: {
|
|
|
|
|
ProjectContainer: null,
|
|
|
|
|
DayContainer: null,
|
|
|
|
|
Marker: null,
|
|
|
|
|
Highlight: null,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function hideEntriesOfProject(ProjectElement)
|
|
|
|
|
{
|
|
|
|
|
if(!ProjectElement.classList.contains("off"))
|
|
|
|
|
{
|
|
|
|
|
ProjectElement.classList.add("off");
|
|
|
|
|
}
|
|
|
|
|
var baseURL = ProjectElement.attributes.getNamedItem("data-baseURL").value;
|
|
|
|
|
var searchLocation = ProjectElement.attributes.getNamedItem("data-searchLocation").value;
|
|
|
|
|
var playerLocation = ProjectElement.attributes.getNamedItem("data-playerLocation").value;
|
|
|
|
|
for(var i = 0; i < Search.Projects.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
var ThisProject = Search.Projects[i];
|
|
|
|
|
if(baseURL === ThisProject.baseURL && searchLocation === ThisProject.searchLocation && playerLocation === ThisProject.playerLocation)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
var searchLocation = ProjectElement.attributes.getNamedItem("data-searchLocation").value;
|
|
|
|
|
var playerLocation = ProjectElement.attributes.getNamedItem("data-playerLocation").value;
|
|
|
|
|
for(var i = 0; i < Search.Projects.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
var ThisProject = Search.Projects[i];
|
|
|
|
|
if(baseURL === ThisProject.baseURL && searchLocation === ThisProject.searchLocation && playerLocation === ThisProject.playerLocation)
|
|
|
|
|
{
|
|
|
|
|
ThisProject.filteredOut = false;
|
|
|
|
|
if(ThisProject.entriesContainer != null)
|
|
|
|
|
{
|
|
|
|
|
ThisProject.entriesContainer.style.display = "flex";
|
|
|
|
|
enableSprite(ThisProject.entriesContainer.parentElement);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function hideProjectSearchResults(baseURL, searchLocation, playerLocation)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
var resultSearchLocation = cineraResultsProjects[i].attributes.getNamedItem("data-searchLocation").value;
|
|
|
|
|
var resultPlayerLocation = cineraResultsProjects[i].attributes.getNamedItem("data-playerLocation").value;
|
|
|
|
|
if(baseURL === resultBaseURL && searchLocation === resultSearchLocation && playerLocation === resultPlayerLocation)
|
|
|
|
|
{
|
|
|
|
|
cineraResultsProjects[i].style.display = "none";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function showProjectSearchResults(baseURL, searchLocation, playerLocation)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
var resultSearchLocation = cineraResultsProjects[i].attributes.getNamedItem("data-searchLocation").value;
|
|
|
|
|
var resultPlayerLocation = cineraResultsProjects[i].attributes.getNamedItem("data-playerLocation").value;
|
|
|
|
|
if(baseURL === resultBaseURL && searchLocation === resultSearchLocation && playerLocation === resultPlayerLocation)
|
|
|
|
|
{
|
|
|
|
|
cineraResultsProjects[i].style.display = "flex";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function toggleEntriesOfProjectAndChildren(ProjectFilterElement)
|
|
|
|
|
{
|
|
|
|
|
var baseURL = ProjectFilterElement.attributes.getNamedItem("data-baseURL").value;
|
|
|
|
|
var searchLocation = ProjectFilterElement.attributes.getNamedItem("data-searchLocation").value;
|
|
|
|
|
var playerLocation = ProjectFilterElement.attributes.getNamedItem("data-playerLocation").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 < Search.Projects.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
var ThisProject = Search.Projects[i];
|
|
|
|
|
|
|
|
|
|
if(baseURL === ThisProject.baseURL && searchLocation === ThisProject.searchLocation && playerLocation === ThisProject.playerLocation)
|
|
|
|
|
{
|
|
|
|
|
if(shouldShow)
|
|
|
|
|
{
|
|
|
|
|
ThisProject.filteredOut = false;
|
|
|
|
|
enableSprite(ThisProject.projectTitleElement.parentElement);
|
|
|
|
|
if(ThisProject.entriesContainer != null)
|
|
|
|
|
{
|
|
|
|
|
ThisProject.entriesContainer.style.display = "flex";
|
|
|
|
|
}
|
|
|
|
|
showProjectSearchResults(ThisProject.baseURL, ThisProject.searchLocation, ThisProject.playerLocation);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ThisProject.filteredOut = true;
|
|
|
|
|
disableSprite(ThisProject.projectTitleElement.parentElement);
|
|
|
|
|
if(ThisProject.entriesContainer != null)
|
|
|
|
|
{
|
|
|
|
|
ThisProject.entriesContainer.style.display = "none";
|
|
|
|
|
}
|
|
|
|
|
hideProjectSearchResults(ThisProject.baseURL, ThisProject.searchLocation, ThisProject.playerLocation);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var indexChildFilterProjects = ProjectFilterElement.querySelectorAll(".cineraFilterProject");
|
|
|
|
|
|
|
|
|
|
for(var j = 0; j < indexChildFilterProjects.length; ++j)
|
|
|
|
|
{
|
|
|
|
|
var ThisElement = indexChildFilterProjects[j];
|
|
|
|
|
var baseURL = ThisElement.attributes.getNamedItem("data-baseURL").value;
|
|
|
|
|
var searchLocation = ThisElement.attributes.getNamedItem("data-searchLocation").value;
|
|
|
|
|
var playerLocation = ThisElement.attributes.getNamedItem("data-playerLocation").value;
|
|
|
|
|
if(shouldShow)
|
|
|
|
|
{
|
|
|
|
|
showEntriesOfProject(ThisElement);
|
|
|
|
|
showProjectSearchResults(baseURL, searchLocation, playerLocation);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
hideEntriesOfProject(ThisElement);
|
|
|
|
|
hideProjectSearchResults(baseURL, searchLocation, playerLocation);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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.filename != null && episode.number != null && episode.title != null) {
|
|
|
|
|
episode.day = getEpisodeName(project.unit, episode.number);
|
|
|
|
|
episode.dayContainerPrototype = project.dayContainerPrototype;
|
|
|
|
|
episode.markerPrototype = Search.Prototypes.Marker;
|
|
|
|
|
episode.playerURLPrefix = project.playerURLPrefix;
|
|
|
|
|
project.episodes.push(episode);
|
|
|
|
|
}
|
|
|
|
|
episode = {};
|
|
|
|
|
mode = "none";
|
|
|
|
|
} else if (line.startsWith("location:")) {
|
|
|
|
|
episode.filename = line.slice(10);
|
|
|
|
|
} else if (line.startsWith("number:")) {
|
|
|
|
|
episode.number = line.slice(8).trim().slice(1, -1);
|
|
|
|
|
} 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");
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function prepareProjects()
|
|
|
|
|
{
|
|
|
|
|
for(var i = 0; i < Search.ProjectsContainer.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
var ID = Search.ProjectsContainer[i].attributes.getNamedItem("data-project").value;
|
|
|
|
|
var baseURL = Search.ProjectsContainer[i].attributes.getNamedItem("data-baseURL").value;
|
|
|
|
|
var searchLocation = Search.ProjectsContainer[i].attributes.getNamedItem("data-searchLocation").value;
|
|
|
|
|
var playerLocation = Search.ProjectsContainer[i].attributes.getNamedItem("data-playerLocation").value;
|
|
|
|
|
var unit = Search.ProjectsContainer[i].attributes.getNamedItem("data-unit").value;
|
|
|
|
|
var theme = Search.ProjectsContainer[i].classList.item(1);
|
|
|
|
|
|
|
|
|
|
Search.Projects[i] =
|
|
|
|
|
{
|
|
|
|
|
baseURL: baseURL,
|
|
|
|
|
searchLocation: searchLocation,
|
|
|
|
|
playerLocation: playerLocation,
|
|
|
|
|
unit: unit,
|
|
|
|
|
playerURLPrefix: (baseURL ? baseURL + "/" : "") + (playerLocation ? playerLocation + "/" : ""),
|
|
|
|
|
indexLocation: (baseURL ? baseURL + "/" : "") + (searchLocation ? searchLocation + "/" : "") + ID + ".index",
|
|
|
|
|
projectTitleElement: Search.ProjectsContainer[i].querySelector(":scope > .cineraProjectTitle"),
|
|
|
|
|
entriesContainer: Search.ProjectsContainer[i].querySelector(":scope > .cineraIndexEntries"),
|
|
|
|
|
dayContainerPrototype: Search.Prototypes.DayContainer.cloneNode(true),
|
|
|
|
|
filteredOut: false,
|
|
|
|
|
parsed: false,
|
|
|
|
|
searched: false,
|
|
|
|
|
resultsToRender: [],
|
|
|
|
|
resultsIndex: 0,
|
|
|
|
|
theme: theme,
|
|
|
|
|
episodes: [],
|
|
|
|
|
xhr: new XMLHttpRequest(),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Search.Projects[i].dayContainerPrototype.classList.add(theme);
|
|
|
|
|
Search.Projects[i].dayContainerPrototype.children[1].classList.add(theme);
|
|
|
|
|
|
|
|
|
|
document.querySelector(".spinner").classList.add("show");
|
|
|
|
|
Search.Projects[i].xhr.open("GET", Search.Projects[i].indexLocation);
|
|
|
|
|
Search.Projects[i].xhr.setRequestHeader("Content-Type", "text/plain");
|
|
|
|
|
Search.Projects[i].xhr.send();
|
|
|
|
|
prepareToParseIndexFile(Search.Projects[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getEpisodeName(unit, number) {
|
|
|
|
|
var day = null;
|
|
|
|
|
if(unit)
|
|
|
|
|
{
|
|
|
|
|
day = unit + " " + number;
|
|
|
|
|
}
|
|
|
|
|
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 < Search.Projects.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
var project = Search.Projects[i];
|
|
|
|
|
project.searched = false;
|
|
|
|
|
project.resultsToRender = [];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
IsQuery()
|
|
|
|
|
{
|
|
|
|
|
return Search.LastQuery && Search.LastQuery.length > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function runSearch(refresh) {
|
|
|
|
|
var queryStr = document.getElementById("query").value;
|
|
|
|
|
if (refresh || Search.LastQuery != queryStr) {
|
|
|
|
|
var oldResultsContainer = Search.ResultsContainer;
|
|
|
|
|
Search.ResultsContainer = oldResultsContainer.cloneNode(false);
|
|
|
|
|
oldResultsContainer.parentNode.insertBefore(Search.ResultsContainer, oldResultsContainer);
|
|
|
|
|
oldResultsContainer.remove();
|
|
|
|
|
for(var i = 0; i < Search.Projects.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
Search.Projects[i].resultsIndex = 0;
|
|
|
|
|
}
|
|
|
|
|
Search.ResultsMarkerIndex = -1;
|
|
|
|
|
}
|
|
|
|
|
Search.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 (IsQuery()) {
|
|
|
|
|
switch(Nav.ViewType)
|
|
|
|
|
{
|
|
|
|
|
case view_type.LIST:
|
|
|
|
|
{
|
|
|
|
|
Nav.List.classList.add("hidden");
|
|
|
|
|
} break;
|
|
|
|
|
case view_type.GRID:
|
|
|
|
|
{
|
|
|
|
|
Nav.GridContainer.classList.add("hidden");
|
|
|
|
|
} break;
|
|
|
|
|
}
|
|
|
|
|
Search.ResultsSummary.style.display = "block";
|
|
|
|
|
var shouldRender = false;
|
|
|
|
|
var query = new RegExp(Search.LastQuery.replace("(", "\\(").replace(")", "\\)").replace(/\|+/, "\|").replace(/\|$/, "").replace(/(^|[^\\])\\$/, "$1"), "gi");
|
|
|
|
|
|
|
|
|
|
// Visible
|
|
|
|
|
for(var i = 0; i < Search.Projects.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
var project = Search.Projects[i];
|
|
|
|
|
if(project.parsed && !project.filteredOut && project.episodes.length > 0) {
|
|
|
|
|
if(Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
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 < Search.Projects.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
var project = Search.Projects[i];
|
|
|
|
|
if(project.parsed && project.filteredOut && !project.searched && project.episodes.length > 0) {
|
|
|
|
|
if(Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
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 (Search.Rendering) {
|
|
|
|
|
clearTimeout(Search.RenderHandle);
|
|
|
|
|
}
|
|
|
|
|
renderResults();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
switch(Nav.ViewType)
|
|
|
|
|
{
|
|
|
|
|
case view_type.LIST:
|
|
|
|
|
{
|
|
|
|
|
Nav.List.classList.remove("hidden");
|
|
|
|
|
} break;
|
|
|
|
|
case view_type.GRID:
|
|
|
|
|
{
|
|
|
|
|
Nav.GridContainer.classList.remove("hidden");
|
|
|
|
|
} break;
|
|
|
|
|
}
|
|
|
|
|
Search.ResultsSummary.style.display = "none";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var totalTime = Math.floor(totalSeconds/60/60) + "h " + Math.floor(totalSeconds/60)%60 + "m " + totalSeconds%60 + "s ";
|
|
|
|
|
|
|
|
|
|
Search.ResultsSummary.textContent = "Found: " + numEpisodes + " episodes, " + numMarkers + " markers, " + totalTime + "total.";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderResults() {
|
|
|
|
|
var maxItems = 42;
|
|
|
|
|
var numItems = 0;
|
|
|
|
|
for(var i = 0; i < Search.Projects.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
var project = Search.Projects[i];
|
|
|
|
|
if (project.resultsIndex < project.resultsToRender.length) {
|
|
|
|
|
Search.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 (Search.ResultsMarkerIndex == -1) {
|
|
|
|
|
if(project.resultsIndex == 0 || project.resultsToRender[project.resultsIndex - 1].episode.playerURLPrefix != episode.playerURLPrefix)
|
|
|
|
|
{
|
|
|
|
|
Search.ProjectContainer = Search.Prototypes.ProjectContainer.cloneNode(true);
|
|
|
|
|
for(var i = 0; i < Search.Projects.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
if(Search.Projects[i].playerURLPrefix === episode.playerURLPrefix)
|
|
|
|
|
{
|
|
|
|
|
Search.ProjectContainer.setAttribute("data-baseURL", Search.Projects[i].baseURL);
|
|
|
|
|
Search.ProjectContainer.setAttribute("data-searchLocation", Search.Projects[i].searchLocation);
|
|
|
|
|
Search.ProjectContainer.setAttribute("data-playerLocation", Search.Projects[i].playerLocation);
|
|
|
|
|
if(Search.Projects[i].filteredOut)
|
|
|
|
|
{
|
|
|
|
|
Search.ProjectContainer.style.display = "none";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Search.ResultsContainer.appendChild(Search.ProjectContainer);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Search.ProjectContainer = Search.ResultsContainer.lastElementChild;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var dayContainer = episode.dayContainerPrototype.cloneNode(true);
|
|
|
|
|
var dayName = dayContainer.children[0];
|
|
|
|
|
Search.MarkerList = dayContainer.children[1];
|
|
|
|
|
|
|
|
|
|
// TODO(matt): Maybe prepend the entire lineage?
|
|
|
|
|
dayName.textContent = (project.projectTitleElement.textContent ? project.projectTitleElement.textContent + " / " : "") + (episode.day ? episode.day + ": " : "") + episode.title;
|
|
|
|
|
|
|
|
|
|
Search.ProjectContainer.appendChild(dayContainer);
|
|
|
|
|
Search.ResultsMarkerIndex = 0;
|
|
|
|
|
numItems++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (numItems < maxItems && Search.ResultsMarkerIndex < matches.length) {
|
|
|
|
|
var match = matches[Search.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 = Search.Prototypes.Highlight.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)));
|
|
|
|
|
}
|
|
|
|
|
Search.MarkerList.appendChild(marker);
|
|
|
|
|
numItems++;
|
|
|
|
|
Search.ResultsMarkerIndex++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Search.ResultsMarkerIndex == matches.length) {
|
|
|
|
|
Search.ResultsMarkerIndex = -1;
|
|
|
|
|
project.resultsIndex++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Search.RenderHandle = setTimeout(renderResults, 0);
|
|
|
|
|
} else {
|
|
|
|
|
Search.Rendering = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
InitQuery(QueryElement)
|
|
|
|
|
{
|
|
|
|
|
if(location.hash && location.hash.length > 0)
|
|
|
|
|
{
|
|
|
|
|
var initialQuery = location.hash;
|
|
|
|
|
if(initialQuery[0] == "#")
|
|
|
|
|
{
|
|
|
|
|
initialQuery = initialQuery.slice(1);
|
|
|
|
|
}
|
|
|
|
|
QueryElement.value = decodeURIComponent(initialQuery);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-16 02:52:27 +00:00
|
|
|
|
if(document.hasFocus() && IsVisible(QueryElement, GetWindowDim(CineraProps.IsMobile))) { QueryElement.focus(); }
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
InitPrototypes(ResultsContainer)
|
|
|
|
|
{
|
|
|
|
|
Search.Prototypes.ProjectContainer = document.createElement("DIV");
|
|
|
|
|
Search.Prototypes.ProjectContainer.classList.add("projectContainer");
|
|
|
|
|
|
|
|
|
|
Search.Prototypes.DayContainer = document.createElement("DIV");
|
|
|
|
|
Search.Prototypes.DayContainer.classList.add("dayContainer");
|
|
|
|
|
|
|
|
|
|
var DayName = document.createElement("SPAN");
|
|
|
|
|
DayName.classList.add("dayName");
|
|
|
|
|
Search.Prototypes.DayContainer.appendChild(DayName);
|
|
|
|
|
|
|
|
|
|
var MarkerList = document.createElement("DIV");
|
|
|
|
|
MarkerList.classList.add("markerList");
|
|
|
|
|
Search.Prototypes.DayContainer.appendChild(MarkerList);
|
|
|
|
|
|
|
|
|
|
Search.Prototypes.Marker = document.createElement("A");
|
|
|
|
|
Search.Prototypes.Marker.classList.add("marker");
|
|
|
|
|
if(ResultsContainer.getAttribute("data-single") == 0)
|
|
|
|
|
{
|
|
|
|
|
Search.Prototypes.Marker.setAttribute("target", "_blank");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Search.Prototypes.Highlight = document.createElement("B");
|
|
|
|
|
}
|
|
|
|
|
//
|
|
|
|
|
// Processing (Searching / Filtering)
|
|
|
|
|
|
|
|
|
|
// Presenting / Navigating (Laying out and traversing the grid, and sorting)
|
|
|
|
|
//
|
|
|
|
|
var state_bit =
|
|
|
|
|
{
|
|
|
|
|
DISABLE_ANIMATIONS: 1 << 0,
|
|
|
|
|
SORT_REVERSED: 1 << 1,
|
|
|
|
|
VIEW_LIST: 1 << 2,
|
|
|
|
|
VIEW_GRID: 1 << 3,
|
|
|
|
|
|
|
|
|
|
NO_SAVE: 1 << 31,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var siblings =
|
|
|
|
|
{
|
|
|
|
|
PREV: 0,
|
|
|
|
|
NEXT: 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var item_type =
|
|
|
|
|
{
|
|
|
|
|
PROJECT: 0,
|
|
|
|
|
ENTRY: 1,
|
|
|
|
|
};
|
|
|
|
|
|
2021-01-27 21:58:56 +00:00
|
|
|
|
var item_end =
|
|
|
|
|
{
|
|
|
|
|
HEAD: 0,
|
|
|
|
|
TAIL: 1,
|
|
|
|
|
};
|
|
|
|
|
|
2021-01-25 18:09:30 +00:00
|
|
|
|
var view_type =
|
|
|
|
|
{
|
|
|
|
|
LIST: 0,
|
|
|
|
|
GRID: 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var transition_type =
|
|
|
|
|
{
|
|
|
|
|
SIBLING_SHIFT_PREV: 0,
|
|
|
|
|
SIBLING_SHIFT_NEXT: 1,
|
|
|
|
|
PROJECT_ENTRY: 2,
|
|
|
|
|
PROJECT_EXIT: 3,
|
|
|
|
|
SUBDIVISION_DESCENT: 4,
|
|
|
|
|
SUBDIVISION_ASCENT: 5,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var interaction_type =
|
|
|
|
|
{
|
|
|
|
|
PUSH_BUTTON: 0,
|
|
|
|
|
SIBLING_SHIFT_PREV: 1,
|
|
|
|
|
SIBLING_SHIFT_NEXT: 2,
|
|
|
|
|
ASCEND: 3,
|
|
|
|
|
SORT: 4,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var Nav = {
|
|
|
|
|
Nexus: null,
|
|
|
|
|
GridContainer: null,
|
|
|
|
|
ButtonsContainer: null,
|
2021-02-04 00:13:55 +00:00
|
|
|
|
GridSize: {
|
|
|
|
|
X: null,
|
|
|
|
|
Y: null,
|
|
|
|
|
},
|
|
|
|
|
GridMinCellsPerDimension: 1,
|
|
|
|
|
GridMaxCellsPerDimension: 4,
|
|
|
|
|
GridDim: {
|
|
|
|
|
X: null,
|
|
|
|
|
Y: null,
|
|
|
|
|
},
|
|
|
|
|
MinButtonDim: 100,
|
2021-01-27 21:58:56 +00:00
|
|
|
|
ButtonDim: null,
|
2021-01-25 18:09:30 +00:00
|
|
|
|
GridColumnGap: null,
|
|
|
|
|
GridRowGap: null,
|
|
|
|
|
|
|
|
|
|
SortChronological: true,
|
2021-04-12 16:56:30 +00:00
|
|
|
|
ViewType: view_type.LIST,
|
2021-01-25 18:09:30 +00:00
|
|
|
|
List: null,
|
|
|
|
|
Grid: null,
|
|
|
|
|
|
|
|
|
|
// NOTE(matt): Controls
|
|
|
|
|
Controls: {
|
|
|
|
|
Header: null,
|
|
|
|
|
Sort: null,
|
|
|
|
|
View: null,
|
|
|
|
|
Anim: null,
|
|
|
|
|
Save: null,
|
|
|
|
|
|
|
|
|
|
Help: null,
|
|
|
|
|
HelpDocumentation: null,
|
|
|
|
|
HelpKeys: [],
|
|
|
|
|
|
|
|
|
|
GridTraversal: {
|
2021-02-05 20:15:40 +00:00
|
|
|
|
Container: null,
|
2021-01-25 18:09:30 +00:00
|
|
|
|
Header: null,
|
|
|
|
|
Ascend: null,
|
|
|
|
|
Prev: null,
|
|
|
|
|
PrevAscends: false,
|
|
|
|
|
Next: null,
|
|
|
|
|
NextAscends: false,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
Buttons: [],
|
|
|
|
|
|
|
|
|
|
Transition: {
|
|
|
|
|
Enabled: true,
|
|
|
|
|
ButtonsTransitionContainer: null,
|
|
|
|
|
ButtonsContainerCloneElement: null,
|
|
|
|
|
RelevantButtonElement: null,
|
|
|
|
|
|
|
|
|
|
StageDurations: [],
|
|
|
|
|
|
|
|
|
|
Transforms: {
|
|
|
|
|
ButtonsTransitionContainer: {
|
|
|
|
|
Initial: {
|
|
|
|
|
Pos: { X: 0, Y: 0, },
|
|
|
|
|
Scale: { X: 1, Y: 1, },
|
|
|
|
|
Rotation: { X: 0, Y: 0, Z: 0, },
|
|
|
|
|
Opacity: 1,
|
|
|
|
|
ScrollX: 0,
|
|
|
|
|
ZIndex: 0,
|
|
|
|
|
},
|
|
|
|
|
Current: {
|
|
|
|
|
Pos: { X: 0, Y: 0, },
|
|
|
|
|
Scale: { X: 1, Y: 1, },
|
|
|
|
|
Rotation: { X: 0, Y: 0, Z: 0, },
|
|
|
|
|
Opacity: 1,
|
|
|
|
|
ScrollX: 0,
|
|
|
|
|
ZIndex: 0,
|
|
|
|
|
},
|
|
|
|
|
TargetStages: [],
|
|
|
|
|
},
|
|
|
|
|
ButtonsContainer: {
|
|
|
|
|
Initial: {
|
|
|
|
|
Pos: { X: 0, Y: 0, },
|
|
|
|
|
Scale: { X: 1, Y: 1, },
|
|
|
|
|
Rotation: { X: 0, Y: 0, Z: 0, },
|
|
|
|
|
Opacity: 1,
|
|
|
|
|
ScrollX: 0,
|
|
|
|
|
ZIndex: 0,
|
|
|
|
|
},
|
|
|
|
|
Current: {
|
|
|
|
|
Pos: { X: 0, Y: 0, },
|
|
|
|
|
Scale: { X: 1, Y: 1, },
|
|
|
|
|
Rotation: { X: 0, Y: 0, Z: 0, },
|
|
|
|
|
Opacity: 1,
|
|
|
|
|
ScrollX: 0,
|
|
|
|
|
ZIndex: 0,
|
|
|
|
|
},
|
|
|
|
|
TargetStages: [],
|
|
|
|
|
},
|
|
|
|
|
ButtonsContainerClone: {
|
|
|
|
|
Initial: {
|
|
|
|
|
Pos: { X: 0, Y: 0, },
|
|
|
|
|
Scale: { X: 1, Y: 1, },
|
|
|
|
|
Rotation: { X: 0, Y: 0, Z: 0, },
|
|
|
|
|
Opacity: 1,
|
|
|
|
|
ScrollX: 0,
|
|
|
|
|
ZIndex: 0,
|
|
|
|
|
},
|
|
|
|
|
Current: {
|
|
|
|
|
Pos: { X: 0, Y: 0, },
|
|
|
|
|
Scale: { X: 1, Y: 1, },
|
|
|
|
|
Rotation: { X: 0, Y: 0, Z: 0, },
|
|
|
|
|
Opacity: 1,
|
|
|
|
|
ScrollX: 0,
|
|
|
|
|
ZIndex: 0,
|
|
|
|
|
},
|
|
|
|
|
TargetStages: [],
|
|
|
|
|
},
|
|
|
|
|
RelevantButton: {
|
|
|
|
|
Initial: {
|
|
|
|
|
Pos: { X: 0, Y: 0, },
|
|
|
|
|
Scale: { X: 1, Y: 1, },
|
|
|
|
|
Rotation: { X: 0, Y: 0, Z: 0, },
|
|
|
|
|
Opacity: 1,
|
|
|
|
|
ScrollX: 0,
|
|
|
|
|
ZIndex: 0,
|
|
|
|
|
},
|
|
|
|
|
Current: {
|
|
|
|
|
Pos: { X: 0, Y: 0, },
|
|
|
|
|
Scale: { X: 1, Y: 1, },
|
|
|
|
|
Rotation: { X: 0, Y: 0, Z: 0, },
|
|
|
|
|
Opacity: 1,
|
|
|
|
|
ScrollX: 0,
|
|
|
|
|
ZIndex: 0,
|
|
|
|
|
},
|
|
|
|
|
TargetStages: [],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
StartTime: undefined,
|
|
|
|
|
RequestedFrame: undefined,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
InteractionQueue: [],
|
|
|
|
|
TraversalStack: [],
|
|
|
|
|
State: null,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
StateBitIsSet(Bit)
|
|
|
|
|
{
|
|
|
|
|
return Nav.State & Bit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
StateBitIsClear(Bit)
|
|
|
|
|
{
|
|
|
|
|
return !(Nav.State & Bit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
MaintainingState()
|
|
|
|
|
{
|
|
|
|
|
return StateBitIsClear(state_bit.NO_SAVE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
SaveState()
|
|
|
|
|
{
|
|
|
|
|
localStorage.setItem("CineraState", Nav.State);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
SetStateBit(Bit)
|
|
|
|
|
{
|
|
|
|
|
if(MaintainingState()) { Nav.State |= Bit; SaveState(); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ClearStateBit(Bit)
|
|
|
|
|
{
|
|
|
|
|
if(MaintainingState()) { Nav.State &= ~Bit; SaveState(); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
SetHelpKeyAvailability(GridSize)
|
|
|
|
|
{
|
|
|
|
|
for(var i = 0; i < Nav.Controls.HelpKeys.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.HelpKeys[i].classList.remove("unavailable");
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
/* NOTE(matt): Key layout:
|
|
|
|
|
0 4 8 12
|
|
|
|
|
1 5 9 13
|
|
|
|
|
2 6 10 14
|
|
|
|
|
3 7 11 15
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if(GridSize.X < 4)
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
|
|
|
|
Nav.Controls.HelpKeys[12].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[13].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[14].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[15].classList.add("unavailable");
|
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
if(GridSize.X < 3)
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
|
|
|
|
Nav.Controls.HelpKeys[8].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[9].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[10].classList.add("unavailable");
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Controls.HelpKeys[11].classList.add("unavailable");
|
|
|
|
|
|
|
|
|
|
if(GridSize.X < 2)
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.HelpKeys[4].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[5].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[6].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[7].classList.add("unavailable");
|
|
|
|
|
|
|
|
|
|
if(GridSize.X < 1)
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.HelpKeys[0].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[1].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[2].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[3].classList.add("unavailable");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(GridSize.Y < 4)
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.HelpKeys[3].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[7].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[11].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[15].classList.add("unavailable");
|
|
|
|
|
|
|
|
|
|
if(GridSize.Y < 3)
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.HelpKeys[2].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[6].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[10].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[14].classList.add("unavailable");
|
|
|
|
|
|
|
|
|
|
if(GridSize.Y < 2)
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.HelpKeys[1].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[5].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[9].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[13].classList.add("unavailable");
|
|
|
|
|
|
|
|
|
|
if(GridSize.Y < 1)
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.HelpKeys[0].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[4].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[8].classList.add("unavailable");
|
|
|
|
|
Nav.Controls.HelpKeys[12].classList.add("unavailable");
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
InitHelpKeys(HelpDocumentation)
|
|
|
|
|
{
|
|
|
|
|
var Paragraph = HelpDocumentation.querySelector(".help_paragraph");
|
|
|
|
|
Nav.Controls.HelpKeys = Paragraph.querySelectorAll(".help_key");
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-12 16:56:30 +00:00
|
|
|
|
function
|
|
|
|
|
InitView()
|
|
|
|
|
{
|
|
|
|
|
// NOTE(matt): Nav.ViewType is initialised to view_type.LIST and InitNexus() leaves the List View visible
|
|
|
|
|
if(!StateBitIsSet(state_bit.VIEW_LIST))
|
|
|
|
|
{
|
|
|
|
|
if(GridSizeIsSupported(Nav.GridSize))
|
|
|
|
|
{
|
|
|
|
|
PickGridView();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// NOTE(matt): Silently swap the state bits, leaving the default List View visible
|
|
|
|
|
ClearStateBit(state_bit.VIEW_GRID);
|
|
|
|
|
SetStateBit(state_bit.VIEW_LIST);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-25 18:09:30 +00:00
|
|
|
|
function
|
|
|
|
|
SyncNavState()
|
|
|
|
|
{
|
|
|
|
|
Nav.State = localStorage.getItem("CineraState");
|
|
|
|
|
if(Nav.State)
|
|
|
|
|
{
|
|
|
|
|
if(MaintainingState())
|
|
|
|
|
{
|
|
|
|
|
if(StateBitIsSet(state_bit.DISABLE_ANIMATIONS)) { ToggleAnimations(); }
|
|
|
|
|
if(StateBitIsSet(state_bit.SORT_REVERSED)) { Sort(true); }
|
2021-04-12 16:56:30 +00:00
|
|
|
|
InitView();
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.Save.textContent = "Save Settings: ✘";
|
2021-04-12 16:56:30 +00:00
|
|
|
|
// Nav.ViewType was initialised to view_type.LIST
|
|
|
|
|
if(Nav.ViewType == view_type.GRID && GridSizeIsSupported(Nav.GridSize))
|
2021-02-04 00:13:55 +00:00
|
|
|
|
{
|
2021-04-12 16:56:30 +00:00
|
|
|
|
PickGridView();
|
2021-02-04 00:13:55 +00:00
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Nav.State = 0;
|
2021-04-12 16:56:30 +00:00
|
|
|
|
switch(Nav.ViewType)
|
2021-02-04 00:13:55 +00:00
|
|
|
|
{
|
2021-04-12 16:56:30 +00:00
|
|
|
|
case view_type.LIST: SetStateBit(state_bit.VIEW_LIST); break
|
|
|
|
|
case view_type.GRID: SetStateBit(state_bit.VIEW_GRID); break
|
2021-02-04 00:13:55 +00:00
|
|
|
|
}
|
2021-04-12 16:56:30 +00:00
|
|
|
|
InitView();
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
InitTraversalStack()
|
|
|
|
|
{
|
|
|
|
|
Nav.List = document.getElementById("cineraIndexList");
|
|
|
|
|
|
|
|
|
|
var Projects = Nav.List.querySelectorAll(":scope > .cineraIndexProject");
|
|
|
|
|
|
|
|
|
|
var Level = {
|
|
|
|
|
Projects: null,
|
|
|
|
|
Entries: null,
|
|
|
|
|
HeadIndex: null,
|
|
|
|
|
TailIndex: null,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(Projects.length === 1)
|
|
|
|
|
{
|
|
|
|
|
// NOTE(matt): Automatically descend into the lone project
|
2021-04-12 16:56:30 +00:00
|
|
|
|
var QueriedProjects = Projects[0].querySelectorAll(":scope > .cineraIndexProject");
|
|
|
|
|
if(QueriedProjects.length > 0)
|
|
|
|
|
{
|
|
|
|
|
Level.Projects = QueriedProjects;
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
Level.Entries = Projects[0].querySelectorAll(":scope > .cineraIndexEntries > div");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Level.Projects = Projects;
|
|
|
|
|
// NOTE(matt): The top-level "root" cannot itself contain any entries
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Nav.TraversalStack.push(Level);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ComputeFullButtonItemCount(ParentItemCount, AvailableButtonCount)
|
|
|
|
|
{
|
|
|
|
|
return ParentItemCount > 0 ? Math.ceil(ParentItemCount / AvailableButtonCount) : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
EmptyElement(Element)
|
|
|
|
|
{
|
|
|
|
|
while(Element.firstChild) { Element.removeChild(Element.firstChild); }
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
function
|
|
|
|
|
SetDim(Element, X, Y)
|
|
|
|
|
{
|
|
|
|
|
Element.style.width = X;
|
|
|
|
|
Element.style.height = Y;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-25 18:09:30 +00:00
|
|
|
|
function
|
|
|
|
|
EmptyAndResetButton(Button)
|
|
|
|
|
{
|
|
|
|
|
EmptyElement(Button.Element);
|
|
|
|
|
Button.Element.style.fontSize = null;
|
|
|
|
|
Button.Element.style.fontWeight = null;
|
2021-01-27 21:58:56 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
SetDim(Button.Element, null, null);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
|
|
|
|
for(var i = 0; i < Button.Element.classList.length;)
|
|
|
|
|
{
|
|
|
|
|
var Class = Button.Element.classList[i];
|
|
|
|
|
if(Class != "cineraButton" && Class != "subdivision")
|
|
|
|
|
{
|
|
|
|
|
Button.Element.classList.remove(Class);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Button.Projects = null;
|
|
|
|
|
Button.Entries = null;
|
|
|
|
|
Button.HeadIndex = null;
|
|
|
|
|
Button.TailIndex = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
HasPrevSibling(Level)
|
|
|
|
|
{
|
|
|
|
|
return Level.HeadIndex && Level.HeadIndex > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
HasNextSibling(Level)
|
|
|
|
|
{
|
|
|
|
|
return Level.TailIndex && Level.TailIndex < (Level.Entries ? Level.Entries.length : Level.Projects.length) - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
Diff(A, B)
|
|
|
|
|
{
|
|
|
|
|
return Math.abs(A - B);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
SetButtonInfo(NewButton, Prev, Level, Distribution)
|
|
|
|
|
{
|
|
|
|
|
var Result = {
|
|
|
|
|
HeadIndex: null,
|
|
|
|
|
TailIndex: null,
|
|
|
|
|
ItemCount: null,
|
|
|
|
|
Theme: null,
|
|
|
|
|
}
|
|
|
|
|
var ItemsToPlace = null;
|
|
|
|
|
var FullButtonItemCount = null;
|
|
|
|
|
|
|
|
|
|
if(Distribution.ProjectsToPlace)
|
|
|
|
|
{
|
|
|
|
|
ItemsToPlace = Distribution.ProjectsToPlace;
|
|
|
|
|
FullButtonItemCount = Distribution.FullButtonProjectCount;
|
|
|
|
|
}
|
|
|
|
|
else if(Distribution.EntriesToPlace)
|
|
|
|
|
{
|
|
|
|
|
ItemsToPlace = Distribution.EntriesToPlace;
|
|
|
|
|
FullButtonItemCount = Distribution.FullButtonEntryCount;
|
|
|
|
|
Result.Theme = Level.Entries[0].parentElement.parentElement.classList[1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Result.ItemCount = ItemsToPlace > FullButtonItemCount ? FullButtonItemCount : ItemsToPlace;
|
|
|
|
|
if(Result.ItemCount > 0)
|
|
|
|
|
{
|
|
|
|
|
Result.HeadIndex = Prev ? Prev.TailIndex + 1 : Level.HeadIndex ? Level.HeadIndex : 0;
|
|
|
|
|
Result.TailIndex = Result.HeadIndex + Result.ItemCount - 1;
|
|
|
|
|
if(Result.ItemCount > 1)
|
|
|
|
|
{
|
|
|
|
|
if(Result.Theme == null)
|
|
|
|
|
{
|
|
|
|
|
Result.Theme = Level.Projects[Result.HeadIndex].classList[1];
|
|
|
|
|
}
|
|
|
|
|
NewButton.HeadIndex = Result.HeadIndex;
|
|
|
|
|
NewButton.TailIndex = Result.TailIndex;
|
|
|
|
|
}
|
|
|
|
|
else if(Level.Projects && Level.Projects.length > 0)
|
|
|
|
|
{
|
|
|
|
|
if(Distribution.ProjectsToPlace)
|
|
|
|
|
{
|
|
|
|
|
NewButton.Projects = Level.Projects[Result.HeadIndex].querySelectorAll(":scope > .cineraIndexProject");
|
|
|
|
|
Result.Theme = Level.Projects[Result.HeadIndex].classList[1];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
NewButton.Entries = Level.Projects[Result.HeadIndex].querySelectorAll(":scope > .cineraIndexEntries > div");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ComputeItemDistribution(Level)
|
|
|
|
|
{
|
|
|
|
|
var Result = {
|
|
|
|
|
ProjectsToPlace: Level.Projects ? Level.HeadIndex !== null && Level.TailIndex !== null ? Diff(Level.HeadIndex, Level.TailIndex) + 1 : Level.Projects.length : 0,
|
|
|
|
|
ButtonsForProjects: 0,
|
|
|
|
|
FullButtonProjectCount: 0,
|
|
|
|
|
|
|
|
|
|
EntriesToPlace: Level.Entries ? Level.HeadIndex !== null && Level.TailIndex !== null ? Diff(Level.HeadIndex, Level.TailIndex) + 1 : Level.Entries.length : 0,
|
|
|
|
|
ButtonsForEntries: 0,
|
|
|
|
|
FullButtonEntryCount: 0,
|
|
|
|
|
};
|
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
// NOTE(matt): Reserving the top row for projects
|
|
|
|
|
if(Result.ProjectsToPlace > 0 && Result.EntriesToPlace > 0)
|
|
|
|
|
{
|
|
|
|
|
Result.ButtonsForProjects = Math.min(Nav.GridSize.X, Result.ProjectsToPlace);
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
Result.ButtonsForEntries = Nav.Buttons.length - Result.ButtonsForProjects;
|
|
|
|
|
if(Result.EntriesToPlace < Result.ButtonsForEntries)
|
|
|
|
|
{
|
|
|
|
|
Result.ButtonsForProjects += (Result.ButtonsForEntries - Result.EntriesToPlace);
|
|
|
|
|
Result.ButtonsForEntries = Nav.Buttons.length - Result.ButtonsForProjects;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Result.FullButtonProjectCount = ComputeFullButtonItemCount(Result.ProjectsToPlace, Result.ButtonsForProjects);
|
|
|
|
|
Result.FullButtonEntryCount = ComputeFullButtonItemCount(Result.EntriesToPlace, Result.ButtonsForEntries);
|
|
|
|
|
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ResetTransform(Transform, IgnoreSorting)
|
|
|
|
|
{
|
|
|
|
|
Transform.Pos.X = 0;
|
|
|
|
|
Transform.Pos.Y = 0;
|
|
|
|
|
|
|
|
|
|
Transform.Scale.X = 1;
|
|
|
|
|
Transform.Scale.Y = 1;
|
|
|
|
|
|
|
|
|
|
Transform.Rotation.X = 0;
|
|
|
|
|
Transform.Rotation.Y = 0;
|
|
|
|
|
Transform.Rotation.Z = (Nav.SortChronological || IgnoreSorting) ? 0 : 180;
|
|
|
|
|
|
|
|
|
|
Transform.Opacity = 1;
|
|
|
|
|
|
|
|
|
|
Transform.ScrollX = 0;
|
|
|
|
|
|
|
|
|
|
Transform.ZIndex = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ResetButtonsContainerClone()
|
|
|
|
|
{
|
|
|
|
|
EmptyElement(Nav.Transition.ButtonsContainerCloneElement);
|
|
|
|
|
|
|
|
|
|
ResetTransform(Nav.Transition.Transforms.ButtonsTransitionContainer.Current, true);
|
|
|
|
|
ResetTransform(Nav.Transition.Transforms.ButtonsTransitionContainer.Initial, true);
|
|
|
|
|
ApplyTransform(Nav.Transition.ButtonsTransitionContainerElement, Nav.Transition.Transforms.ButtonsTransitionContainer.Current);
|
|
|
|
|
|
|
|
|
|
ResetTransform(Nav.Transition.Transforms.ButtonsContainer.Current);
|
|
|
|
|
ResetTransform(Nav.Transition.Transforms.ButtonsContainer.Initial);
|
|
|
|
|
ApplyTransform(Nav.ButtonsContainer, Nav.Transition.Transforms.ButtonsContainer.Current);
|
|
|
|
|
|
|
|
|
|
ResetTransform(Nav.Transition.Transforms.ButtonsContainerClone.Current);
|
|
|
|
|
ResetTransform(Nav.Transition.Transforms.ButtonsContainerClone.Initial);
|
|
|
|
|
ApplyTransform(Nav.Transition.ButtonsContainerCloneElement, Nav.Transition.Transforms.ButtonsContainerClone.Current);
|
|
|
|
|
|
|
|
|
|
Nav.Transition.ButtonsContainerCloneElement.style.gridTemplateColumns = Nav.ButtonsContainer.style.gridTemplateColumns;
|
|
|
|
|
Nav.Transition.ButtonsContainerCloneElement.style.gridTemplateRows = Nav.ButtonsContainer.style.gridTemplateRows;
|
|
|
|
|
|
|
|
|
|
Nav.Transition.ButtonsContainerCloneElement.style.paddingRight = null;
|
|
|
|
|
Nav.Transition.ButtonsContainerCloneElement.style.paddingLeft = null;
|
|
|
|
|
Nav.Transition.ButtonsContainerCloneElement.style.position = "absolute";
|
|
|
|
|
|
|
|
|
|
if(Nav.Transition.RelevantButtonElement)
|
|
|
|
|
{
|
|
|
|
|
ResetTransform(Nav.Transition.Transforms.RelevantButton.Current, true);
|
|
|
|
|
ResetTransform(Nav.Transition.Transforms.RelevantButton.Initial, true);
|
|
|
|
|
ApplyTransform(Nav.Transition.RelevantButtonElement, Nav.Transition.Transforms.RelevantButton.Current);
|
|
|
|
|
Nav.Transition.RelevantButtonElement = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Nav.ButtonsContainer.style.zIndex = 1;
|
|
|
|
|
Nav.ButtonsContainer.style.order = 1;
|
|
|
|
|
Nav.Transition.ButtonsContainerCloneElement.style.order = 0;
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.ButtonsContainerCloneElement.style.display = "none";
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
CloneButtonsContainer()
|
|
|
|
|
{
|
|
|
|
|
ResetButtonsContainerClone();
|
|
|
|
|
for(var i = 0; i < Nav.ButtonsContainer.children.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
var ChildClone = Nav.ButtonsContainer.children[i].cloneNode(true);
|
|
|
|
|
Nav.Transition.ButtonsContainerCloneElement.appendChild(ChildClone);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CopyTransform(Nav.Transition.Transforms.ButtonsContainerClone.Current, Nav.Transition.Transforms.ButtonsContainer.Current);
|
|
|
|
|
ApplyTransform(Nav.Transition.ButtonsContainerCloneElement, Nav.Transition.Transforms.ButtonsContainerClone.Current);
|
|
|
|
|
Nav.Transition.ButtonsContainerCloneElement.style.zIndex = 1;
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.ButtonsContainerCloneElement.style.display = "grid";
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
GetIndexOfElement(ParentNodeList, Element)
|
|
|
|
|
{
|
|
|
|
|
var Result = null;
|
|
|
|
|
for(var i = 0; i < ParentNodeList.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
if(Element == ParentNodeList[i])
|
|
|
|
|
{
|
|
|
|
|
Result = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
GetIndexOfButton(Button)
|
|
|
|
|
{
|
|
|
|
|
var Result = null;
|
|
|
|
|
for(var i = 0; i < Nav.Buttons.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
if(Button == Nav.Buttons[i])
|
|
|
|
|
{
|
|
|
|
|
Result = i
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ApplyTransform(Element, Transform)
|
|
|
|
|
{
|
|
|
|
|
var TranslateStyle = "translate(" + Transform.Pos.X + "px, " + Transform.Pos.Y + "px)";
|
|
|
|
|
var ScaleStyle = "scale(" + Transform.Scale.X + "," + Transform.Scale.Y + ")";
|
|
|
|
|
var RotateX = "rotate3d(1, 0, 0, " + Transform.Rotation.X + "deg)";
|
|
|
|
|
var RotateY = "rotate3d(0, 1, 0, " + Transform.Rotation.Y + "deg)";
|
|
|
|
|
var RotateZ = "rotate3d(0, 0, 1, " + Transform.Rotation.Z + "deg)";
|
|
|
|
|
var RotateStyle = RotateX + " " + RotateY + " " + RotateZ;
|
|
|
|
|
|
|
|
|
|
var TransformString = TranslateStyle + " " + ScaleStyle + " " + RotateStyle;
|
|
|
|
|
|
|
|
|
|
Element.style.transform = TransformString;
|
|
|
|
|
Element.style.opacity = Transform.Opacity;
|
|
|
|
|
Element.style.zIndex = Transform.ZIndex;
|
|
|
|
|
|
|
|
|
|
if(Transform.ScrollX !== null)
|
|
|
|
|
{
|
|
|
|
|
Element.scrollLeft = Transform.ScrollX;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
CopyTransform(Dest, Src)
|
|
|
|
|
{
|
|
|
|
|
Dest.Pos.X = Src.Pos.X;
|
|
|
|
|
Dest.Pos.Y = Src.Pos.Y;
|
|
|
|
|
|
|
|
|
|
Dest.Scale.X = Src.Scale.X;
|
|
|
|
|
Dest.Scale.Y = Src.Scale.Y;
|
|
|
|
|
|
|
|
|
|
Dest.Rotation.X = Src.Rotation.X;
|
|
|
|
|
Dest.Rotation.Y = Src.Rotation.Y;
|
|
|
|
|
Dest.Rotation.Z = Src.Rotation.Z;
|
|
|
|
|
|
|
|
|
|
Dest.Opacity = Src.Opacity;
|
|
|
|
|
|
|
|
|
|
Dest.ScrollX = Src.ScrollX;
|
|
|
|
|
|
|
|
|
|
Dest.ZIndex = Src.ZIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
TransformsMatch(Current, Target)
|
|
|
|
|
{
|
|
|
|
|
var Result = true;
|
|
|
|
|
if((Target.Pos.X != null && Target.Pos.X != Current.Pos.X) ||
|
|
|
|
|
(Target.Pos.Y != null && Target.Pos.Y != Current.Pos.Y) ||
|
|
|
|
|
(Target.Scale.X != null && Target.Scale.X != Current.Scale.X) ||
|
|
|
|
|
(Target.Scale.Y != null && Target.Scale.Y != Current.Scale.Y) ||
|
|
|
|
|
(Target.Rotation.X != null && Target.Rotation.X != Current.Rotation.X) ||
|
|
|
|
|
(Target.Rotation.Y != null && Target.Rotation.Y != Current.Rotation.Y) ||
|
|
|
|
|
(Target.Rotation.Z != null && Target.Rotation.Z != Current.Rotation.Z) ||
|
|
|
|
|
(Target.Opacity != null && Target.Opacity != Current.Opacity) ||
|
|
|
|
|
(Target.ScrollX != null && Target.ScrollX != Current.ScrollX) ||
|
|
|
|
|
(Target.ZIndex != null && Target.ZIndex != Current.ZIndex))
|
|
|
|
|
{
|
|
|
|
|
Result = false;
|
|
|
|
|
}
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
TransformsComplete(TransformSet)
|
|
|
|
|
{
|
|
|
|
|
var Result = true;
|
|
|
|
|
|
|
|
|
|
var ButtonsContainer = TransformSet.ButtonsContainer;
|
|
|
|
|
var ButtonsContainerClone = TransformSet.ButtonsContainerClone;
|
|
|
|
|
var ButtonsTransitionContainer = TransformSet.ButtonsTransitionContainer;
|
|
|
|
|
var RelevantButton = TransformSet.RelevantButton;
|
|
|
|
|
|
|
|
|
|
if((ButtonsContainer.TargetStages.length && !TransformsMatch(ButtonsContainer.Current, ButtonsContainer.TargetStages[0])) ||
|
|
|
|
|
(ButtonsContainerClone.TargetStages.length && !TransformsMatch(ButtonsContainerClone.Current, ButtonsContainerClone.TargetStages[0])) ||
|
|
|
|
|
(ButtonsTransitionContainer.TargetStages.length && !TransformsMatch(ButtonsTransitionContainer.Current, ButtonsTransitionContainer.TargetStages[0])) ||
|
|
|
|
|
(RelevantButton.TargetStages.length && !TransformsMatch(RelevantButton.Current, RelevantButton.TargetStages[0])))
|
|
|
|
|
{
|
|
|
|
|
Result = false;
|
|
|
|
|
}
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
FinaliseTransforms(TransformsSet)
|
|
|
|
|
{
|
|
|
|
|
Nav.Transition.StartTime = undefined;
|
|
|
|
|
CopyTransform(TransformsSet.ButtonsTransitionContainer.Initial, TransformsSet.ButtonsTransitionContainer.Current);
|
|
|
|
|
CopyTransform(TransformsSet.ButtonsContainer.Initial, TransformsSet.ButtonsContainer.Current);
|
|
|
|
|
CopyTransform(TransformsSet.ButtonsContainerClone.Initial, TransformsSet.ButtonsContainerClone.Current);
|
|
|
|
|
CopyTransform(TransformsSet.RelevantButton.Initial, TransformsSet.RelevantButton.Current);
|
|
|
|
|
ShiftStage();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
LerpTransforms(TransformSet, t)
|
|
|
|
|
{
|
|
|
|
|
var Result = false;
|
|
|
|
|
if(TransformSet.TargetStages.length)
|
|
|
|
|
{
|
|
|
|
|
var Initial = TransformSet.Initial;
|
|
|
|
|
var Current = TransformSet.Current;
|
|
|
|
|
var Target = TransformSet.TargetStages[0];
|
|
|
|
|
if(Target.Pos.X !== null) { Current.Pos.X = Lerp(Initial.Pos.X, t, Target.Pos.X); Result = true; }
|
|
|
|
|
if(Target.Pos.Y !== null) { Current.Pos.Y = Lerp(Initial.Pos.Y, t, Target.Pos.Y); Result = true; }
|
|
|
|
|
|
|
|
|
|
if(Target.Scale.X !== null) { Current.Scale.X = Lerp(Initial.Scale.X, t, Target.Scale.X); Result = true; }
|
|
|
|
|
if(Target.Scale.Y !== null) { Current.Scale.Y = Lerp(Initial.Scale.Y, t, Target.Scale.Y); Result = true; }
|
|
|
|
|
|
|
|
|
|
if(Target.Rotation.X !== null) { Current.Rotation.X = Lerp(Initial.Rotation.X, t, Target.Rotation.X); Result = true; }
|
|
|
|
|
if(Target.Rotation.Y !== null) { Current.Rotation.Y = Lerp(Initial.Rotation.Y, t, Target.Rotation.Y); Result = true; }
|
|
|
|
|
if(Target.Rotation.Z !== null) { Current.Rotation.Z = Lerp(Initial.Rotation.Z, t, Target.Rotation.Z); Result = true; }
|
|
|
|
|
|
|
|
|
|
if(Target.Opacity !== null) { Current.Opacity = Lerp(Initial.Opacity, t, Target.Opacity); Result = true; }
|
|
|
|
|
|
|
|
|
|
if(Target.ScrollX !== null) { Current.ScrollX = Lerp(Initial.ScrollX, t, Target.ScrollX); Result = true; }
|
|
|
|
|
|
|
|
|
|
if(Target.ZIndex !== null) { Current.ZIndex = Lerp(Initial.ZIndex, t, Target.ZIndex); Result = true; }
|
|
|
|
|
}
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ShiftStage()
|
|
|
|
|
{
|
|
|
|
|
if(Nav.Transition.StageDurations.length) { Nav.Transition.StageDurations.shift(); }
|
|
|
|
|
if(Nav.Transition.Transforms.ButtonsTransitionContainer.TargetStages.length) { Nav.Transition.Transforms.ButtonsTransitionContainer.TargetStages.shift(); }
|
|
|
|
|
if(Nav.Transition.Transforms.ButtonsContainer.TargetStages.length) { Nav.Transition.Transforms.ButtonsContainer.TargetStages.shift(); }
|
|
|
|
|
if(Nav.Transition.Transforms.ButtonsContainerClone.TargetStages.length) { Nav.Transition.Transforms.ButtonsContainerClone.TargetStages.shift(); }
|
|
|
|
|
if(Nav.Transition.Transforms.RelevantButton.TargetStages.length) { Nav.Transition.Transforms.RelevantButton.TargetStages.shift(); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
MergeTransform(Dest, Src)
|
|
|
|
|
{
|
|
|
|
|
if(Src.Pos.X !== null) { Dest.Pos.X = Src.Pos.X; }
|
|
|
|
|
if(Src.Pos.Y !== null) { Dest.Pos.Y = Src.Pos.Y; }
|
|
|
|
|
|
|
|
|
|
if(Src.Scale.X !== null) { Dest.Scale.X = Src.Scale.X; }
|
|
|
|
|
if(Src.Scale.Y !== null) { Dest.Scale.Y = Src.Scale.Y; }
|
|
|
|
|
|
|
|
|
|
if(Src.Rotation.X !== null) { Dest.Rotation.X = Src.Rotation.X; }
|
|
|
|
|
if(Src.Rotation.Y !== null) { Dest.Rotation.Y = Src.Rotation.Y; }
|
|
|
|
|
if(Src.Rotation.Z !== null) { Dest.Rotation.Z = Src.Rotation.Z; }
|
|
|
|
|
|
|
|
|
|
if(Src.Opacity !== null) { Dest.Opacity = Src.Opacity; }
|
|
|
|
|
|
|
|
|
|
if(Src.ScrollX !== null) { Dest.ScrollX = Src.ScrollX; }
|
|
|
|
|
|
|
|
|
|
if(Src.ZIndex !== null) { Dest.ZIndex = Src.ZIndex; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
DoTransitionStage(Now)
|
|
|
|
|
{
|
|
|
|
|
if(Nav.Transition.StageDurations.length)
|
|
|
|
|
{
|
|
|
|
|
if(Nav.Transition.StartTime === undefined)
|
|
|
|
|
{
|
|
|
|
|
Nav.Transition.StartTime = Now;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var Elapsed = Now - Nav.Transition.StartTime;
|
|
|
|
|
var Duration = Nav.Transition.StageDurations[0];
|
|
|
|
|
if(Duration === 0)
|
|
|
|
|
{
|
|
|
|
|
// Instant transform
|
|
|
|
|
if(Nav.Transition.Transforms.ButtonsTransitionContainer.TargetStages.length)
|
|
|
|
|
{
|
|
|
|
|
MergeTransform(Nav.Transition.Transforms.ButtonsTransitionContainer.Current, Nav.Transition.Transforms.ButtonsTransitionContainer.TargetStages[0]);
|
|
|
|
|
ApplyTransform(Nav.Transition.ButtonsTransitionContainerElement, Nav.Transition.Transforms.ButtonsTransitionContainer.Current);
|
|
|
|
|
}
|
|
|
|
|
if(Nav.Transition.Transforms.ButtonsContainer.TargetStages.length)
|
|
|
|
|
{
|
|
|
|
|
MergeTransform(Nav.Transition.Transforms.ButtonsContainer.Current, Nav.Transition.Transforms.ButtonsContainer.TargetStages[0]);
|
|
|
|
|
ApplyTransform(Nav.ButtonsContainer, Nav.Transition.Transforms.ButtonsContainer.Current);
|
|
|
|
|
}
|
|
|
|
|
if(Nav.Transition.Transforms.ButtonsContainerClone.TargetStages.length)
|
|
|
|
|
{
|
|
|
|
|
MergeTransform(Nav.Transition.Transforms.ButtonsContainerClone.Current, Nav.Transition.Transforms.ButtonsContainerClone.TargetStages[0]);
|
|
|
|
|
ApplyTransform(Nav.Transition.ButtonsContainerCloneElement, Nav.Transition.Transforms.ButtonsContainerClone.Current);
|
|
|
|
|
}
|
|
|
|
|
if(Nav.Transition.Transforms.RelevantButton.TargetStages.length)
|
|
|
|
|
{
|
|
|
|
|
MergeTransform(Nav.Transition.Transforms.RelevantButton.Current, Nav.Transition.Transforms.RelevantButton.TargetStages[0]);
|
|
|
|
|
ApplyTransform(Nav.Transition.RelevantButtonElement, Nav.Transition.Transforms.RelevantButton.Current);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Lerp
|
|
|
|
|
var t = Clamp01(Elapsed / Duration);
|
|
|
|
|
if(LerpTransforms(Nav.Transition.Transforms.ButtonsTransitionContainer, t))
|
|
|
|
|
{
|
|
|
|
|
ApplyTransform(Nav.Transition.ButtonsTransitionContainerElement, Nav.Transition.Transforms.ButtonsTransitionContainer.Current);
|
|
|
|
|
}
|
|
|
|
|
if(LerpTransforms(Nav.Transition.Transforms.ButtonsContainer, t))
|
|
|
|
|
{
|
|
|
|
|
ApplyTransform(Nav.ButtonsContainer, Nav.Transition.Transforms.ButtonsContainer.Current);
|
|
|
|
|
}
|
|
|
|
|
if(LerpTransforms(Nav.Transition.Transforms.ButtonsContainerClone, t))
|
|
|
|
|
{
|
|
|
|
|
ApplyTransform(Nav.Transition.ButtonsContainerCloneElement, Nav.Transition.Transforms.ButtonsContainerClone.Current);
|
|
|
|
|
}
|
|
|
|
|
if(LerpTransforms(Nav.Transition.Transforms.RelevantButton, t))
|
|
|
|
|
{
|
|
|
|
|
ApplyTransform(Nav.Transition.RelevantButtonElement, Nav.Transition.Transforms.RelevantButton.Current);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(TransformsComplete(Nav.Transition.Transforms))
|
|
|
|
|
{
|
|
|
|
|
FinaliseTransforms(Nav.Transition.Transforms);
|
|
|
|
|
}
|
|
|
|
|
Nav.Transition.RequestedFrame = window.requestAnimationFrame(DoTransitionStage);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Nav.Transition.RequestedFrame = undefined;
|
|
|
|
|
ResetButtonsContainerClone();
|
|
|
|
|
DequeueInteraction();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
CompressTransitionStages()
|
|
|
|
|
{
|
|
|
|
|
while(Nav.Transition.StageDurations.length > 1)
|
|
|
|
|
{
|
|
|
|
|
Nav.Transition.StageDurations.shift()
|
|
|
|
|
}
|
|
|
|
|
Nav.Transition.StageDurations[0] = 0;
|
|
|
|
|
|
|
|
|
|
while(Nav.Transition.Transforms.ButtonsTransitionContainer.TargetStages.length > 1)
|
|
|
|
|
{
|
|
|
|
|
Nav.Transition.Transforms.ButtonsTransitionContainer.TargetStages.shift();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while(Nav.Transition.Transforms.ButtonsContainer.TargetStages.length > 1)
|
|
|
|
|
{
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.TargetStages.shift();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while(Nav.Transition.Transforms.ButtonsContainerClone.TargetStages.length > 1)
|
|
|
|
|
{
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainerClone.TargetStages.shift();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while(Nav.Transition.Transforms.RelevantButton.TargetStages.length > 1)
|
|
|
|
|
{
|
|
|
|
|
Nav.Transition.Transforms.RelevantButton.TargetStages.shift();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
DoTransition()
|
|
|
|
|
{
|
|
|
|
|
if(Nav.Transition.Enabled == false)
|
|
|
|
|
{
|
|
|
|
|
CompressTransitionStages();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CopyTransform(Nav.Transition.Transforms.ButtonsTransitionContainer.Initial, Nav.Transition.Transforms.ButtonsTransitionContainer.Current);
|
|
|
|
|
CopyTransform(Nav.Transition.Transforms.ButtonsContainer.Initial, Nav.Transition.Transforms.ButtonsContainer.Current);
|
|
|
|
|
CopyTransform(Nav.Transition.Transforms.ButtonsContainerClone.Initial, Nav.Transition.Transforms.ButtonsContainerClone.Current);
|
|
|
|
|
CopyTransform(Nav.Transition.Transforms.RelevantButton.Initial, Nav.Transition.Transforms.RelevantButton.Current);
|
|
|
|
|
|
|
|
|
|
Nav.Transition.StartTime = undefined;
|
|
|
|
|
Nav.Transition.RequestedFrame = window.requestAnimationFrame(DoTransitionStage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
NodesMatch(A, B)
|
|
|
|
|
{
|
|
|
|
|
var Result = false;
|
|
|
|
|
var i = 0;
|
|
|
|
|
if(A && B)
|
|
|
|
|
{
|
|
|
|
|
Result = true;
|
|
|
|
|
for(; i < A.length && i < B.length && A[i] == B[i];)
|
|
|
|
|
{
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
if(i != A.length || i != B.length)
|
|
|
|
|
{
|
|
|
|
|
Result = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(!A && !B)
|
|
|
|
|
{
|
|
|
|
|
Result = true;
|
|
|
|
|
}
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ButtonAndLevelMatch(Button, Level)
|
|
|
|
|
{
|
|
|
|
|
var Result = true;
|
|
|
|
|
if(Button && Level)
|
|
|
|
|
{
|
|
|
|
|
var ButtonProjects = Button.Projects;
|
|
|
|
|
var ButtonEntries = Button.Entries;
|
|
|
|
|
if(Button.Projects && Button.Projects.length === undefined)
|
|
|
|
|
{
|
|
|
|
|
ButtonProjects = Button.Projects.querySelectorAll(":scope > .cineraIndexProject");
|
|
|
|
|
ButtonEntries = Button.Projects.querySelectorAll(":scope > .cineraIndexEntries > div");
|
|
|
|
|
if(ButtonProjects.length == 0)
|
|
|
|
|
{
|
|
|
|
|
ButtonProjects = null;
|
|
|
|
|
}
|
|
|
|
|
if(ButtonEntries.length == 0)
|
|
|
|
|
{
|
|
|
|
|
ButtonEntries = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!NodesMatch(Level.Projects, ButtonProjects))
|
|
|
|
|
{
|
|
|
|
|
Result = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!NodesMatch(Level.Entries, ButtonEntries))
|
|
|
|
|
{
|
|
|
|
|
Result = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(Button.HeadIndex != Level.HeadIndex)
|
|
|
|
|
{
|
|
|
|
|
Result = false;
|
|
|
|
|
}
|
|
|
|
|
if(Button.TailIndex != Level.TailIndex)
|
|
|
|
|
{
|
|
|
|
|
Result = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
NullTarget()
|
|
|
|
|
{
|
|
|
|
|
let Result = {
|
|
|
|
|
Pos: { X: null, Y: null, }, Scale: { X: null, Y: null, }, Rotation: { X: null, Y: null, Z: null, },
|
|
|
|
|
Opacity: null, ScrollX: null, ZIndex: null,
|
|
|
|
|
};
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ComputeButtonGeometryRelativeToGrid(Button)
|
|
|
|
|
{
|
|
|
|
|
var Result = {
|
|
|
|
|
Pos: {
|
|
|
|
|
X: null,
|
|
|
|
|
Y: null,
|
|
|
|
|
},
|
|
|
|
|
Scale: {
|
|
|
|
|
X: null,
|
|
|
|
|
Y: null,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var ButtonStyle = window.getComputedStyle(Button);
|
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var GridDimX = Nav.GridDim.X;
|
|
|
|
|
var GridDimY = Nav.GridDim.Y;
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
|
|
|
|
var ButtonDimX = parseInt(ButtonStyle.width);
|
|
|
|
|
var ButtonDimY = parseInt(ButtonStyle.height);
|
|
|
|
|
|
|
|
|
|
Result.Scale.X = ButtonDimX / GridDimX;
|
|
|
|
|
Result.Scale.Y = ButtonDimY / GridDimY;
|
|
|
|
|
|
|
|
|
|
var ButtonPosX = Button.offsetLeft;
|
|
|
|
|
var ButtonPosY = Button.offsetTop;
|
|
|
|
|
|
|
|
|
|
if(!Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
ButtonPosX = GridDimX - ButtonPosX - ButtonDimX;
|
|
|
|
|
ButtonPosY = GridDimY - ButtonPosY - ButtonDimY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var ButtonCentreX = ButtonDimX / 2 + ButtonPosX;
|
|
|
|
|
var ButtonCentreY = ButtonDimY / 2 + ButtonPosY;
|
|
|
|
|
|
|
|
|
|
var GridCentreX = GridDimX / 2;
|
|
|
|
|
var GridCentreY = GridDimY / 2;
|
|
|
|
|
|
|
|
|
|
Result.Pos.X = ButtonCentreX - GridCentreX;
|
|
|
|
|
Result.Pos.Y = ButtonCentreY - GridCentreY;
|
|
|
|
|
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
GetItemType(Level)
|
|
|
|
|
{
|
|
|
|
|
var Result = null;
|
|
|
|
|
if(Level.Projects && Level.Projects.length !== undefined && Level.Projects.length > 0)
|
|
|
|
|
{
|
|
|
|
|
Result = item_type.PROJECT;
|
|
|
|
|
}
|
|
|
|
|
else if(Level.Entries && Level.Entries.length !== undefined && Level.Entries.length > 0)
|
|
|
|
|
{
|
|
|
|
|
Result = item_type.ENTRY;
|
|
|
|
|
}
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
GetTraversalLevelBundle()
|
|
|
|
|
{
|
|
|
|
|
var Result = {
|
|
|
|
|
Generation: null,
|
|
|
|
|
This: null,
|
|
|
|
|
Parent: null,
|
|
|
|
|
Type: null
|
|
|
|
|
};
|
|
|
|
|
Result.Generation = Nav.TraversalStack.length;
|
|
|
|
|
Result.This = Nav.TraversalStack[Result.Generation - 1];
|
|
|
|
|
Result.Parent = Nav.TraversalStack[Result.Generation - 2];
|
|
|
|
|
Result.Type = GetItemType(Result.This);
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
2021-02-04 23:19:56 +00:00
|
|
|
|
FitText(Element, ItemEnd)
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var Paragraph = Element.firstElementChild;
|
|
|
|
|
var ParagraphStyle = window.getComputedStyle(Paragraph);
|
|
|
|
|
var ElementStyle = window.getComputedStyle(Element);
|
|
|
|
|
var Width = parseInt(ElementStyle.width);
|
|
|
|
|
var Height = parseInt(ElementStyle.height);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 23:19:56 +00:00
|
|
|
|
Element.style.alignItems = "flex-start"; // NOTE(matt): Allows IsOverflowed() to work on a flex-end Element
|
2021-01-25 18:09:30 +00:00
|
|
|
|
var FontSize = parseInt(window.getComputedStyle(Element).fontSize);
|
2021-02-04 23:19:56 +00:00
|
|
|
|
while(FontSize >= 10.2 && (IsOverflowed(Element) || parseInt(ParagraphStyle.width) > Width || parseInt(ParagraphStyle.height) > Height))
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
|
|
|
|
FontSize -= 0.2;
|
|
|
|
|
Element.style.fontSize = FontSize + "px";
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
if(IsOverflowed(Element) || parseInt(ParagraphStyle.width) > Width || parseInt(ParagraphStyle.height) > Height)
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
|
|
|
|
Element.style.fontWeight = "normal";
|
|
|
|
|
}
|
2021-02-04 23:19:56 +00:00
|
|
|
|
|
|
|
|
|
var IsHeadOrTailAndTooTallForElement = ItemEnd !== undefined && (Element.scrollHeight > Element.clientHeight || parseInt(ParagraphStyle.height) > Height);
|
|
|
|
|
// NOTE(matt): Leave "flex-start" in place for tall items, to keep their beginning visible
|
|
|
|
|
if(!IsHeadOrTailAndTooTallForElement)
|
|
|
|
|
{
|
|
|
|
|
Element.style.alignItems = null;
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-27 21:58:56 +00:00
|
|
|
|
function
|
|
|
|
|
AppendItemToButton(Button, Text, ItemEnd)
|
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var ButtonElementStyle = window.getComputedStyle(Button.Element);
|
|
|
|
|
var VerticalBorderAllowance = parseInt(ButtonElementStyle.borderLeftWidth) + parseInt(ButtonElementStyle.borderRightWidth);
|
|
|
|
|
var HorizontalBorderAllowance;
|
2021-01-27 21:58:56 +00:00
|
|
|
|
var ItemElement = document.createElement("div");
|
2021-02-04 00:13:55 +00:00
|
|
|
|
ItemElement.classList.add("cineraText");
|
2021-01-27 21:58:56 +00:00
|
|
|
|
switch(ItemEnd)
|
|
|
|
|
{
|
|
|
|
|
case item_end.HEAD:
|
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
HorizonalBorderAllowance = parseInt(ButtonElementStyle.borderTopWidth);
|
2021-01-27 21:58:56 +00:00
|
|
|
|
ItemElement.classList.add("head-item");
|
|
|
|
|
} break;
|
|
|
|
|
case item_end.TAIL:
|
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
HorizonalBorderAllowance = parseInt(ButtonElementStyle.borderBottomWidth);
|
2021-01-27 21:58:56 +00:00
|
|
|
|
ItemElement.classList.add("tail-item");
|
|
|
|
|
} break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
ItemElement.style.transform = "rotate3d(0, 0, 1, 180deg)";
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var ParagraphNode = document.createElement("p");
|
|
|
|
|
ParagraphNode.textContent = Text;
|
|
|
|
|
ItemElement.appendChild(ParagraphNode);
|
2021-01-27 21:58:56 +00:00
|
|
|
|
var Item = Button.Element.appendChild(ItemElement);
|
2021-02-04 00:13:55 +00:00
|
|
|
|
|
|
|
|
|
// NOTE(matt): This enables Safari to apply height 50% to the head / tail items
|
|
|
|
|
var ButtonWidth = parseInt(ButtonElementStyle.width);
|
|
|
|
|
var ButtonHeight = parseInt(ButtonElementStyle.height);
|
|
|
|
|
SetDim(Button.Element, ButtonWidth + "px", ButtonHeight + "px");
|
|
|
|
|
////
|
|
|
|
|
|
2021-02-04 23:19:56 +00:00
|
|
|
|
FitText(Item, ItemEnd);
|
2021-01-27 21:58:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-25 18:09:30 +00:00
|
|
|
|
function
|
|
|
|
|
UpdateButtons(TransitionType, RelevantButton, PoppedLevel)
|
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
if(GridSizeIsSupported(Nav.GridSize))
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var LevelBundle = GetTraversalLevelBundle();
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Controls.GridTraversal.Prev.children[0].textContent = "←";
|
|
|
|
|
Nav.Controls.GridTraversal.PrevAscends = false;
|
|
|
|
|
Nav.Controls.GridTraversal.NextAscends = false;
|
|
|
|
|
if(LevelBundle.Generation <= 1)
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Controls.GridTraversal.Ascend.classList.add("nowhere");
|
2021-01-25 18:09:30 +00:00
|
|
|
|
Nav.Controls.GridTraversal.Prev.classList.add("nowhere");
|
|
|
|
|
Nav.Controls.GridTraversal.Next.classList.add("nowhere");
|
|
|
|
|
Nav.Controls.GridTraversal.Next.classList.remove("ascension");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Controls.GridTraversal.Ascend.classList.remove("nowhere");
|
|
|
|
|
Nav.Controls.GridTraversal.Prev.classList.remove("nowhere");
|
|
|
|
|
Nav.Controls.GridTraversal.Next.classList.remove("nowhere");
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
if(!HasPrevSibling(LevelBundle.This))
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.GridTraversal.Prev.classList.add("nowhere");
|
|
|
|
|
}
|
|
|
|
|
else if(SiblingIsLeaf(siblings.PREV))
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.GridTraversal.PrevAscends = true;
|
|
|
|
|
Nav.Controls.GridTraversal.Prev.children[0].textContent = Nav.SortChronological ? "↰" : "↲";
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
if(!HasNextSibling(LevelBundle.This))
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.GridTraversal.Next.classList.add("nowhere");
|
|
|
|
|
Nav.Controls.GridTraversal.Next.classList.remove("ascension");
|
|
|
|
|
}
|
|
|
|
|
else if(SiblingIsLeaf(siblings.NEXT))
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.GridTraversal.NextAscends = true;
|
|
|
|
|
Nav.Controls.GridTraversal.Next.classList.add("ascension");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.GridTraversal.Next.classList.remove("ascension");
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var Distribution = ComputeItemDistribution(LevelBundle.This);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
// NOTE(matt): Centre-alignment. If people would prefer left-alignment, we do that here
|
|
|
|
|
//
|
|
|
|
|
// We're doing simple 1D centring here, so would need to do correct 2D centring
|
|
|
|
|
var HalfEmptyButtonCount = (Nav.Buttons.length - (Distribution.ProjectsToPlace + Distribution.EntriesToPlace)) / 2;
|
|
|
|
|
var EmptyPadding = 0;
|
|
|
|
|
//EmptyPadding = Math.floor(HalfEmptyButtonCount); // NOTE(matt): Comment out to disable centring
|
|
|
|
|
//
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var Prev = null;
|
|
|
|
|
var ButtonInfo = { HeadIndex: null, TailIndex: null, ItemCount: null, Theme: null, };
|
|
|
|
|
var DoingEntries = false;
|
|
|
|
|
|
|
|
|
|
if(TransitionType !== undefined)
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
CloneButtonsContainer();
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
2021-02-04 00:13:55 +00:00
|
|
|
|
|
|
|
|
|
for(var ButtonIndex = 0; ButtonIndex < Nav.Buttons.length; ++ButtonIndex)
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
let This = Nav.Buttons[ButtonIndex];
|
|
|
|
|
EmptyAndResetButton(This);
|
|
|
|
|
if(EmptyPadding > 0)
|
|
|
|
|
{
|
|
|
|
|
--EmptyPadding;
|
|
|
|
|
}
|
|
|
|
|
else
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
if(Distribution.ProjectsToPlace > 0 || Distribution.EntriesToPlace > 0)
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
if(Distribution.ProjectsToPlace > 0)
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
This.Projects = LevelBundle.This.Projects;
|
|
|
|
|
if(Distribution.ProjectsToPlace == 1 || Distribution.ProjectsToPlace == Distribution.ButtonsForProjects - ButtonIndex)
|
|
|
|
|
{
|
|
|
|
|
Distribution.FullButtonProjectCount = 1;
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
ButtonInfo = SetButtonInfo(This, Prev, LevelBundle.This, Distribution);
|
|
|
|
|
if(ButtonInfo.ItemCount == 1)
|
|
|
|
|
{
|
|
|
|
|
This.Projects = LevelBundle.This.Projects[ButtonInfo.HeadIndex];
|
|
|
|
|
}
|
|
|
|
|
Distribution.ProjectsToPlace -= ButtonInfo.ItemCount;
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
if(Distribution.FullButtonProjectCount == 1)
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
This.Element.classList.add("leaf");
|
|
|
|
|
var TextElement = document.createElement("p");
|
|
|
|
|
TextElement.classList.add("cineraText");
|
|
|
|
|
if(!Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
TextElement.style.transform = "rotate3d(0, 0, 1, 180deg)";
|
|
|
|
|
}
|
|
|
|
|
var Text = LevelBundle.This.Projects[ButtonInfo.HeadIndex].querySelector(".cineraProjectTitle").innerText;
|
|
|
|
|
var TextNode = document.createTextNode(Text);
|
|
|
|
|
TextElement.appendChild(TextNode);
|
|
|
|
|
This.Element.appendChild(TextElement);
|
|
|
|
|
FitText(This.Element);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
2021-02-04 00:13:55 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var HeadText = LevelBundle.This.Projects[ButtonInfo.HeadIndex].querySelector(".cineraProjectTitle").innerText;
|
|
|
|
|
AppendItemToButton(This, HeadText, item_end.HEAD);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var TailText = LevelBundle.This.Projects[ButtonInfo.TailIndex].querySelector(".cineraProjectTitle").innerText;
|
|
|
|
|
AppendItemToButton(This, TailText, item_end.TAIL);
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
2021-02-04 00:13:55 +00:00
|
|
|
|
else
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
This.Entries = LevelBundle.This.Entries;
|
|
|
|
|
if(!DoingEntries)
|
|
|
|
|
{
|
|
|
|
|
Prev = null;
|
|
|
|
|
DoingEntries = true;
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
if(Distribution.EntriesToPlace == 1 || Distribution.EntriesToPlace == Distribution.ButtonsForEntries - ButtonIndex)
|
|
|
|
|
{
|
|
|
|
|
Distribution.FullButtonEntryCount = 1;
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
ButtonInfo = SetButtonInfo(This, Prev, LevelBundle.This, Distribution);
|
|
|
|
|
if(ButtonInfo.ItemCount == 1)
|
|
|
|
|
{
|
|
|
|
|
This.Entries = This.Entries[ButtonInfo.HeadIndex];
|
|
|
|
|
}
|
|
|
|
|
Distribution.EntriesToPlace -= ButtonInfo.ItemCount;
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
if(Distribution.FullButtonEntryCount == 1)
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
This.Element.classList.add("leaf");
|
|
|
|
|
var ButtonLink = document.createElement("a");
|
|
|
|
|
ButtonLink.classList.add("cineraText");
|
|
|
|
|
if(!Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
ButtonLink.style.transform = "rotate3d(0, 0, 1, 180deg)";
|
|
|
|
|
}
|
|
|
|
|
var EntryAddress = LevelBundle.This.Entries[ButtonInfo.HeadIndex].lastElementChild.getAttribute("href");
|
|
|
|
|
ButtonLink.setAttribute("href", EntryAddress);
|
|
|
|
|
var Text = LevelBundle.This.Entries[ButtonInfo.HeadIndex].innerText;
|
|
|
|
|
var TextNode = document.createTextNode(Text);
|
|
|
|
|
ButtonLink.appendChild(TextNode);
|
|
|
|
|
|
|
|
|
|
This.Element.appendChild(ButtonLink);
|
|
|
|
|
FitText(This.Element);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
2021-02-04 00:13:55 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var HeadText = LevelBundle.This.Entries[ButtonInfo.HeadIndex].innerText;
|
|
|
|
|
AppendItemToButton(This, HeadText, item_end.HEAD);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var TailText = LevelBundle.This.Entries[ButtonInfo.TailIndex].innerText;
|
|
|
|
|
AppendItemToButton(This, TailText, item_end.TAIL);
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
This.Element.classList.add(ButtonInfo.Theme);
|
|
|
|
|
Prev = ButtonInfo;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
This.Element.classList.add(Nav.Nexus.classList[0]);
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
2021-02-04 00:13:55 +00:00
|
|
|
|
if(PoppedLevel && !Nav.Transition.RelevantButtonElement && ButtonAndLevelMatch(This, PoppedLevel))
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.RelevantButtonElement = This.Element;
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-02-04 00:13:55 +00:00
|
|
|
|
if(TransitionType !== undefined)
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
switch(TransitionType)
|
|
|
|
|
{
|
|
|
|
|
case transition_type.SIBLING_SHIFT_PREV:
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
// Init targets
|
|
|
|
|
//// ButtonsTransitionContainer
|
|
|
|
|
let TargetA0 = NullTarget();
|
|
|
|
|
var Padding = Nav.GridColumnGap;
|
|
|
|
|
if(Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
TargetA0.ScrollX = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TargetA0.ScrollX = Nav.GridDim.X + Padding;
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.StageDurations.push(320);
|
|
|
|
|
Nav.Transition.Transforms.ButtonsTransitionContainer.TargetStages.push(TargetA0);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
// Prep
|
|
|
|
|
Nav.Transition.ButtonsContainerCloneElement.style.position = "relative";
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var ScrollX;
|
|
|
|
|
if(Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
Nav.ButtonsContainer.style.order = 0;
|
|
|
|
|
Nav.Transition.ButtonsContainerCloneElement.style.order = 1;
|
|
|
|
|
ScrollX = Nav.GridDim.X + Padding;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Nav.ButtonsContainer.style.order = 1;
|
|
|
|
|
Nav.Transition.ButtonsContainerCloneElement.style.order = 0;
|
|
|
|
|
ScrollX = 0;
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.ButtonsContainerCloneElement.style.paddingLeft = Padding + "px";
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.Transforms.ButtonsTransitionContainer.Current.ScrollX = ScrollX;
|
|
|
|
|
ApplyTransform(Nav.Transition.ButtonsTransitionContainerElement, Nav.Transition.Transforms.ButtonsTransitionContainer.Current);
|
|
|
|
|
} break;
|
|
|
|
|
case transition_type.SIBLING_SHIFT_NEXT:
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
// Init targets
|
|
|
|
|
////
|
|
|
|
|
let TargetA0 = NullTarget();
|
|
|
|
|
var Padding = Nav.GridColumnGap;
|
|
|
|
|
if(Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
TargetA0.ScrollX = Nav.GridDim.X + Padding;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TargetA0.ScrollX = 0;
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.StageDurations.push(320);
|
|
|
|
|
Nav.Transition.Transforms.ButtonsTransitionContainer.TargetStages.push(TargetA0);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
// Prep
|
|
|
|
|
Nav.Transition.ButtonsContainerCloneElement.style.position = "relative";
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var ScrollX;
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
if(Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
Nav.ButtonsContainer.style.order = 1;
|
|
|
|
|
Nav.Transition.ButtonsContainerCloneElement.style.order = 0;
|
|
|
|
|
ScrollX = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Nav.ButtonsContainer.style.order = 0;
|
|
|
|
|
Nav.Transition.ButtonsContainerCloneElement.style.order = 1;
|
|
|
|
|
ScrollX = Nav.GridDim.X + Padding;
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.ButtonsContainerCloneElement.style.paddingRight = Padding + "px";
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.Transforms.ButtonsTransitionContainer.Current.ScrollX = ScrollX;
|
|
|
|
|
ApplyTransform(Nav.Transition.ButtonsTransitionContainerElement, Nav.Transition.Transforms.ButtonsTransitionContainer.Current);
|
|
|
|
|
} break;
|
|
|
|
|
case transition_type.PROJECT_ENTRY:
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
// Init targets
|
|
|
|
|
//// ButtonsContainer
|
|
|
|
|
let TargetA0 = NullTarget();
|
|
|
|
|
let TargetA1 = NullTarget();
|
|
|
|
|
let TargetA2 = NullTarget();
|
|
|
|
|
let TargetA3 = NullTarget();
|
|
|
|
|
|
|
|
|
|
if(Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
TargetA0.Rotation.Y = 90;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TargetA0.Rotation.Y = -90;
|
|
|
|
|
}
|
|
|
|
|
TargetA1.ZIndex = 1;
|
|
|
|
|
TargetA2.Rotation.Y = 0;
|
|
|
|
|
TargetA3.Pos.X = 0;
|
|
|
|
|
TargetA3.Pos.Y = 0;
|
|
|
|
|
TargetA3.Scale.X = 1;
|
|
|
|
|
TargetA3.Scale.Y = 1;
|
|
|
|
|
|
|
|
|
|
//// RelevantButton
|
|
|
|
|
let TargetB0 = NullTarget();
|
|
|
|
|
let TargetB1 = NullTarget();
|
|
|
|
|
let TargetB2 = NullTarget();
|
|
|
|
|
|
|
|
|
|
if(Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
TargetB0.Rotation.Y = -90;
|
|
|
|
|
TargetB2.Rotation.Y = -180;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TargetB0.Rotation.Y = 90;
|
|
|
|
|
TargetB2.Rotation.Y = 180;
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
//// ButtonsContainerClone
|
|
|
|
|
let TargetC0 = NullTarget();
|
|
|
|
|
let TargetC1 = NullTarget();
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
TargetC1.ZIndex = 0;
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.StageDurations.push(80);
|
|
|
|
|
Nav.Transition.StageDurations.push(0);
|
|
|
|
|
Nav.Transition.StageDurations.push(80);
|
|
|
|
|
Nav.Transition.StageDurations.push(160);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.TargetStages.push(TargetA0);
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.TargetStages.push(TargetA1);
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.TargetStages.push(TargetA2);
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.TargetStages.push(TargetA3);
|
|
|
|
|
Nav.Transition.Transforms.RelevantButton.TargetStages.push(TargetB0);
|
|
|
|
|
Nav.Transition.Transforms.RelevantButton.TargetStages.push(TargetB1);
|
|
|
|
|
Nav.Transition.Transforms.RelevantButton.TargetStages.push(TargetB2);
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainerClone.TargetStages.push(TargetC0);
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainerClone.TargetStages.push(TargetC1);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
// Prep
|
|
|
|
|
var RelevantButtonIndex = GetIndexOfButton(RelevantButton);
|
|
|
|
|
Nav.Transition.RelevantButtonElement = Nav.Transition.ButtonsContainerCloneElement.children[RelevantButtonIndex];
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var ButtonGeometry = ComputeButtonGeometryRelativeToGrid(Nav.Transition.RelevantButtonElement);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.Current.Pos.X = ButtonGeometry.Pos.X;
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.Current.Pos.Y = ButtonGeometry.Pos.Y;
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.Current.Scale.X = ButtonGeometry.Scale.X;
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.Current.Scale.Y = ButtonGeometry.Scale.Y;
|
|
|
|
|
if(Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.Current.Rotation.Y = 180;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.Current.Rotation.Y = -180;
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.Current.ZIndex = 0;
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.Transforms.ButtonsContainerClone.Current.ZIndex = 1;
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
ApplyTransform(Nav.Transition.ButtonsContainerCloneElement, Nav.Transition.Transforms.ButtonsContainerClone.Current);
|
|
|
|
|
ApplyTransform(Nav.ButtonsContainer, Nav.Transition.Transforms.ButtonsContainer.Current);
|
|
|
|
|
} break;
|
|
|
|
|
case transition_type.PROJECT_EXIT:
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
// Init targets
|
|
|
|
|
//// ButtonsContainer
|
|
|
|
|
let TargetA0 = NullTarget();
|
|
|
|
|
let TargetA1 = NullTarget();
|
|
|
|
|
let TargetA2 = NullTarget();
|
|
|
|
|
TargetA2.ZIndex = 1;
|
|
|
|
|
|
|
|
|
|
//// RelevantButton
|
|
|
|
|
let TargetB0 = NullTarget();
|
|
|
|
|
let TargetB1 = NullTarget();
|
|
|
|
|
let TargetB2 = NullTarget();
|
|
|
|
|
let TargetB3 = NullTarget();
|
|
|
|
|
|
|
|
|
|
if(Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
TargetB1.Rotation.Y = -90;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TargetB1.Rotation.Y = 90;
|
|
|
|
|
}
|
|
|
|
|
TargetB3.Rotation.Y = 0;
|
|
|
|
|
|
|
|
|
|
//// ButtonsContainerClone
|
|
|
|
|
let TargetC0 = NullTarget();
|
|
|
|
|
let TargetC1 = NullTarget();
|
|
|
|
|
let TargetC2 = NullTarget();
|
|
|
|
|
let TargetC3 = NullTarget();
|
|
|
|
|
|
|
|
|
|
var ButtonGeometry = ComputeButtonGeometryRelativeToGrid(Nav.Transition.RelevantButtonElement);
|
|
|
|
|
TargetC0.Pos.X = ButtonGeometry.Pos.X;
|
|
|
|
|
TargetC0.Pos.Y = ButtonGeometry.Pos.Y;
|
|
|
|
|
TargetC0.Scale.X = ButtonGeometry.Scale.X;
|
|
|
|
|
TargetC0.Scale.Y = ButtonGeometry.Scale.Y;
|
|
|
|
|
if(Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
TargetC1.Rotation.Y = 90;
|
|
|
|
|
TargetC3.Rotation.Y = 180;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TargetC1.Rotation.Y = -90;
|
|
|
|
|
TargetC3.Rotation.Y = -180;
|
|
|
|
|
}
|
|
|
|
|
TargetC2.ZIndex = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Nav.Transition.StageDurations.push(160);
|
|
|
|
|
Nav.Transition.StageDurations.push(80);
|
|
|
|
|
Nav.Transition.StageDurations.push(0);
|
|
|
|
|
Nav.Transition.StageDurations.push(80);
|
|
|
|
|
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.TargetStages.push(TargetA0);
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.TargetStages.push(TargetA1);
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.TargetStages.push(TargetA2);
|
|
|
|
|
Nav.Transition.Transforms.RelevantButton.TargetStages.push(TargetB0);
|
|
|
|
|
Nav.Transition.Transforms.RelevantButton.TargetStages.push(TargetB1);
|
|
|
|
|
Nav.Transition.Transforms.RelevantButton.TargetStages.push(TargetB2);
|
|
|
|
|
Nav.Transition.Transforms.RelevantButton.TargetStages.push(TargetB3);
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainerClone.TargetStages.push(TargetC0);
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainerClone.TargetStages.push(TargetC1);
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainerClone.TargetStages.push(TargetC2);
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainerClone.TargetStages.push(TargetC3);
|
|
|
|
|
|
|
|
|
|
// Prep
|
|
|
|
|
if(Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
Nav.Transition.Transforms.RelevantButton.Current.Rotation.Y = -180;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Nav.Transition.Transforms.RelevantButton.Current.Rotation.Y = 180;
|
|
|
|
|
}
|
|
|
|
|
ApplyTransform(Nav.Transition.RelevantButtonElement, Nav.Transition.Transforms.RelevantButton.Current);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.Current.ZIndex = 0;
|
|
|
|
|
ApplyTransform(Nav.ButtonsContainer, Nav.Transition.Transforms.ButtonsContainer.Current);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.Transforms.ButtonsContainerClone.Current.ZIndex = 1;
|
|
|
|
|
ApplyTransform(Nav.Transition.ButtonsContainerCloneElement, Nav.Transition.Transforms.ButtonsContainerClone.Current);
|
|
|
|
|
} break;
|
|
|
|
|
case transition_type.SUBDIVISION_DESCENT:
|
|
|
|
|
{
|
|
|
|
|
// Init targets
|
|
|
|
|
//// ButtonsContainer
|
|
|
|
|
let TargetA0 = NullTarget();
|
|
|
|
|
let TargetA1 = NullTarget();
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
TargetA0.Opacity = 1;
|
|
|
|
|
TargetA1.Pos.X = 0;
|
|
|
|
|
TargetA1.Pos.Y = 0;
|
|
|
|
|
TargetA1.Scale.X = 1;
|
|
|
|
|
TargetA1.Scale.Y = 1;
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.StageDurations.push(160);
|
|
|
|
|
Nav.Transition.StageDurations.push(160);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.TargetStages.push(TargetA0);
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.TargetStages.push(TargetA1);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
// Prep
|
|
|
|
|
var RelevantButtonIndex = GetIndexOfButton(RelevantButton);
|
|
|
|
|
Nav.Transition.RelevantButtonElement = Nav.Transition.ButtonsContainerCloneElement.children[RelevantButtonIndex];
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var ButtonGeometry = ComputeButtonGeometryRelativeToGrid(Nav.Transition.RelevantButtonElement);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.Current.Pos.X = ButtonGeometry.Pos.X;
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.Current.Pos.Y = ButtonGeometry.Pos.Y;
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.Current.Scale.X = ButtonGeometry.Scale.X;
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.Current.Scale.Y = ButtonGeometry.Scale.Y;
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.Current.Opacity = 0;
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.Current.ZIndex = 1;
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.Transforms.ButtonsContainerClone.Current.ZIndex = 0;
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
ApplyTransform(Nav.Transition.ButtonsContainerCloneElement, Nav.Transition.Transforms.ButtonsContainerClone.Current);
|
|
|
|
|
ApplyTransform(Nav.ButtonsContainer, Nav.Transition.Transforms.ButtonsContainer.Current);
|
|
|
|
|
} break;
|
|
|
|
|
case transition_type.SUBDIVISION_ASCENT:
|
|
|
|
|
{
|
|
|
|
|
// Init targets
|
|
|
|
|
//// ButtonsContainer
|
|
|
|
|
let TargetA0 = NullTarget();
|
|
|
|
|
let TargetA1 = NullTarget();
|
|
|
|
|
let TargetA2 = NullTarget();
|
|
|
|
|
TargetA2.ZIndex = 1;
|
|
|
|
|
|
|
|
|
|
//// ButtonsContainerClone
|
|
|
|
|
let TargetC0 = NullTarget();
|
|
|
|
|
let TargetC1 = NullTarget();
|
|
|
|
|
let TargetC2 = NullTarget();
|
|
|
|
|
|
|
|
|
|
var ButtonGeometry = ComputeButtonGeometryRelativeToGrid(Nav.Transition.RelevantButtonElement);
|
|
|
|
|
TargetC0.Pos.X = ButtonGeometry.Pos.X;
|
|
|
|
|
TargetC0.Pos.Y = ButtonGeometry.Pos.Y;
|
|
|
|
|
TargetC0.Scale.X = ButtonGeometry.Scale.X;
|
|
|
|
|
TargetC0.Scale.Y = ButtonGeometry.Scale.Y;
|
|
|
|
|
TargetC1.Opacity = 0;
|
|
|
|
|
TargetC2.ZIndex = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Nav.Transition.StageDurations.push(160);
|
|
|
|
|
Nav.Transition.StageDurations.push(160);
|
|
|
|
|
Nav.Transition.StageDurations.push(0);
|
|
|
|
|
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.TargetStages.push(TargetA0);
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.TargetStages.push(TargetA1);
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.TargetStages.push(TargetA2);
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainerClone.TargetStages.push(TargetC0);
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainerClone.TargetStages.push(TargetC1);
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainerClone.TargetStages.push(TargetC2);
|
|
|
|
|
|
|
|
|
|
// Prep
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.Current.ZIndex = 0;
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainerClone.Current.ZIndex = 1;
|
|
|
|
|
ApplyTransform(Nav.Transition.ButtonsContainerCloneElement, Nav.Transition.Transforms.ButtonsContainerClone.Current);
|
|
|
|
|
ApplyTransform(Nav.ButtonsContainer, Nav.Transition.Transforms.ButtonsContainer.Current);
|
|
|
|
|
} break;
|
|
|
|
|
}
|
|
|
|
|
DoTransition();
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
PushButton(ButtonElement, Button)
|
|
|
|
|
{
|
|
|
|
|
var Level = {
|
|
|
|
|
Projects: Button.Projects,
|
|
|
|
|
Entries: Button.Entries,
|
|
|
|
|
HeadIndex: Button.HeadIndex,
|
|
|
|
|
TailIndex: Button.TailIndex,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var TransitionType = undefined;
|
|
|
|
|
if(Level.Projects !== null || Level.Entries !== null)
|
|
|
|
|
{
|
|
|
|
|
if(Level.Projects !== null)
|
|
|
|
|
{
|
|
|
|
|
if(Level.Projects.length === undefined)
|
|
|
|
|
{
|
|
|
|
|
var Entries = Level.Projects.querySelectorAll(":scope > .cineraIndexEntries > div");
|
|
|
|
|
var Projects = Level.Projects.querySelectorAll(":scope > .cineraIndexProject");
|
|
|
|
|
Level.Entries = Entries.length ? Entries : null;
|
|
|
|
|
Level.Projects = Projects.length ? Projects : null;
|
|
|
|
|
TransitionType = transition_type.PROJECT_ENTRY;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TransitionType = transition_type.SUBDIVISION_DESCENT;
|
|
|
|
|
}
|
|
|
|
|
Nav.TraversalStack.push(Level);
|
|
|
|
|
UpdateButtons(TransitionType, Button);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(Level.Entries.length === undefined)
|
|
|
|
|
{
|
|
|
|
|
var Address = ButtonElement.lastElementChild.getAttribute("href");
|
|
|
|
|
location = Address;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Nav.TraversalStack.push(Level);
|
|
|
|
|
TransitionType = transition_type.SUBDIVISION_DESCENT;
|
|
|
|
|
UpdateButtons(TransitionType, Button);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
RangeContains(Range, Target)
|
|
|
|
|
{
|
|
|
|
|
return (Range.HeadIndex == null && Range.TailIndex == null) || (Range.HeadIndex <= Target && Range.TailIndex >= Target);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
LengthOf(Level)
|
|
|
|
|
{
|
|
|
|
|
var Result = 0;
|
|
|
|
|
var Items = Level.Projects && Level.Projects.length > 0 ? Level.Projects : Level.Entries;
|
|
|
|
|
if(Items)
|
|
|
|
|
{
|
|
|
|
|
if(Level.HeadIndex !== null && Level.TailIndex !== null)
|
|
|
|
|
{
|
|
|
|
|
Result = Diff(Level.HeadIndex, Level.TailIndex) + 1;
|
|
|
|
|
}
|
|
|
|
|
else if(Items.length !== undefined)
|
|
|
|
|
{
|
|
|
|
|
Result = Items.length;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Result = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
DereferenceLevel(Generation)
|
|
|
|
|
{
|
|
|
|
|
var Level = Nav.TraversalStack[Generation - 1];
|
|
|
|
|
var Result = {
|
|
|
|
|
Projects: Level.Projects,
|
|
|
|
|
Entries: Level.Entries,
|
|
|
|
|
HeadIndex: Level.HeadIndex,
|
|
|
|
|
TailIndex: Level.TailIndex,
|
|
|
|
|
};
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
GetSubdivisionFor(LevelBundle, Index, TargetGeneration)
|
|
|
|
|
{
|
|
|
|
|
var Result = {
|
|
|
|
|
Projects: LevelBundle.This.Projects,
|
|
|
|
|
Entries: LevelBundle.This.Entries,
|
|
|
|
|
HeadIndex: null,
|
|
|
|
|
TailIndex: null,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var GenerationsToDistribute = 1;
|
|
|
|
|
var Generation = TargetGeneration ? TargetGeneration : LevelBundle.Generation;
|
|
|
|
|
var Parent = DereferenceLevel(Generation - GenerationsToDistribute);
|
|
|
|
|
while(!RangeContains(Parent, Index))
|
|
|
|
|
{
|
|
|
|
|
++GenerationsToDistribute;
|
|
|
|
|
Parent = DereferenceLevel(Generation - GenerationsToDistribute);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while(GenerationsToDistribute > 0)
|
|
|
|
|
{
|
|
|
|
|
var Distribution = ComputeItemDistribution(Parent);
|
|
|
|
|
|
|
|
|
|
var ToPlace = null;
|
|
|
|
|
var FullButtonItemCount = null;
|
|
|
|
|
var ButtonCount = null;
|
|
|
|
|
if(LevelBundle.Type == item_type.PROJECT)
|
|
|
|
|
{
|
|
|
|
|
ToPlace = Distribution.ProjectsToPlace;
|
|
|
|
|
FullButtonItemCount = Distribution.FullButtonProjectCount;
|
|
|
|
|
ButtonCount = Distribution.ButtonsForProjects;
|
|
|
|
|
}
|
|
|
|
|
else if(LevelBundle.Type = item_type.ENTRY)
|
|
|
|
|
{
|
|
|
|
|
ToPlace = Distribution.EntriesToPlace;
|
|
|
|
|
FullButtonItemCount = Distribution.FullButtonEntryCount;
|
|
|
|
|
ButtonCount = Distribution.ButtonsForEntries;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Result.HeadIndex = null;
|
|
|
|
|
Result.TailIndex = null;
|
|
|
|
|
for(var i = 0; i < ButtonCount; ++i)
|
|
|
|
|
{
|
|
|
|
|
if(ToPlace == ButtonCount - i) { FullButtonItemCount = 1; }
|
|
|
|
|
Result.HeadIndex = Result.TailIndex !== null ? Result.TailIndex + 1 : Parent.HeadIndex !== null ? Parent.HeadIndex : 0;
|
|
|
|
|
Result.TailIndex = Result.HeadIndex + Math.min(FullButtonItemCount, ToPlace) - 1;
|
|
|
|
|
if(RangeContains(Result, Index)) { break; }
|
|
|
|
|
ToPlace -= LengthOf(Result);
|
|
|
|
|
}
|
|
|
|
|
--GenerationsToDistribute;
|
|
|
|
|
Parent.HeadIndex = Result.HeadIndex;
|
|
|
|
|
Parent.TailIndex = Result.TailIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
SiblingIsLeaf(SiblingID)
|
|
|
|
|
{
|
|
|
|
|
var LevelBundle = GetTraversalLevelBundle();
|
|
|
|
|
return LengthOf(GetSubdivisionFor(LevelBundle,
|
|
|
|
|
SiblingID == siblings.PREV ? LevelBundle.This.HeadIndex - 1 : LevelBundle.This.TailIndex + 1)) == 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ShiftToSibling(SiblingID)
|
|
|
|
|
{
|
|
|
|
|
var LevelBundle = GetTraversalLevelBundle();
|
|
|
|
|
|
|
|
|
|
if((SiblingID == siblings.PREV && HasPrevSibling(LevelBundle.This)) ||
|
|
|
|
|
(SiblingID == siblings.NEXT && HasNextSibling(LevelBundle.This)))
|
|
|
|
|
{
|
|
|
|
|
var TransitionType = SiblingID == siblings.PREV ? transition_type.SIBLING_SHIFT_PREV : transition_type.SIBLING_SHIFT_NEXT;
|
|
|
|
|
var CurrentItem = LevelBundle.Type == item_type.PROJECT ? LevelBundle.This.Projects : LevelBundle.This.Entries;
|
|
|
|
|
|
|
|
|
|
var TargetIndex = SiblingID == siblings.PREV ? LevelBundle.This.HeadIndex - 1 : LevelBundle.This.TailIndex + 1;
|
|
|
|
|
LevelBundle.Parent = Nav.TraversalStack[0];
|
|
|
|
|
var TransitionLevel;
|
|
|
|
|
for(let i = 0; i < LevelBundle.Generation; ++i)
|
|
|
|
|
{
|
|
|
|
|
LevelBundle.This = Nav.TraversalStack[i];
|
|
|
|
|
|
|
|
|
|
if((LevelBundle.Type == item_type.PROJECT && CurrentItem == LevelBundle.Parent.Projects) ||
|
|
|
|
|
(LevelBundle.Type == item_type.ENTRY && CurrentItem == LevelBundle.Parent.Entries))
|
|
|
|
|
{
|
|
|
|
|
if(!RangeContains(LevelBundle.This, TargetIndex))
|
|
|
|
|
{
|
|
|
|
|
Nav.TraversalStack[i] = GetSubdivisionFor(LevelBundle, TargetIndex, i + 1);
|
|
|
|
|
if(LengthOf(Nav.TraversalStack[i]) == 1)
|
|
|
|
|
{
|
|
|
|
|
TransitionType = transition_type.SUBDIVISION_ASCENT;
|
|
|
|
|
TransitionLevel = LevelBundle.This;
|
|
|
|
|
Nav.TraversalStack.pop();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
LevelBundle.Parent = Nav.TraversalStack[i];
|
|
|
|
|
}
|
|
|
|
|
UpdateButtons(TransitionType, undefined, TransitionLevel);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DequeueInteraction();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
Ascend()
|
|
|
|
|
{
|
|
|
|
|
var LevelBundle = GetTraversalLevelBundle();
|
|
|
|
|
if(LevelBundle.Generation > 1)
|
|
|
|
|
{
|
|
|
|
|
var TransitionType = LevelBundle.This.HeadIndex !== null && LevelBundle.This.TailIndex !== null ? transition_type.SUBDIVISION_ASCENT : transition_type.PROJECT_EXIT;
|
|
|
|
|
var PoppedLevel = Nav.TraversalStack.pop();
|
|
|
|
|
UpdateButtons(TransitionType, undefined, PoppedLevel);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ShiftToPrevSibling()
|
|
|
|
|
{
|
|
|
|
|
var LevelBundle = GetTraversalLevelBundle();
|
|
|
|
|
if(LevelBundle.Generation > 1)
|
|
|
|
|
{
|
|
|
|
|
if(Nav.Controls.GridTraversal.PrevAscends == true)
|
|
|
|
|
{
|
|
|
|
|
Ascend();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ShiftToSibling(siblings.PREV);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ShiftToNextSibling()
|
|
|
|
|
{
|
|
|
|
|
ShiftToSibling(siblings.NEXT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
InputIsFocused()
|
|
|
|
|
{
|
|
|
|
|
return document.activeElement == Search.QueryElement;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ShouldFireGridEvents()
|
|
|
|
|
{
|
|
|
|
|
return Nav.ViewType == view_type.GRID && !InputIsFocused() && !IsQuery();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
2021-02-04 00:13:55 +00:00
|
|
|
|
ModifyControlKeybinding(Event)
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
|
|
|
|
// TODO(matt): Settle on the final sets of bindings
|
|
|
|
|
var Chron = Nav.SortChronological;
|
|
|
|
|
var Key = Event.key;
|
|
|
|
|
var IT = interaction_type.PUSH_BUTTON;
|
|
|
|
|
var ID = { Element: null, Button: null, }; // NOTE(matt): InteractionData
|
|
|
|
|
|
|
|
|
|
switch(Key)
|
|
|
|
|
{
|
|
|
|
|
case "?": if(!InputIsFocused()) { Nav.Controls.HelpDocumentation.classList.toggle("visible"); } break;
|
|
|
|
|
case "t": if(!InputIsFocused()) { EnqueueInteraction(interaction_type.SORT); } break;
|
|
|
|
|
case "y": if(!InputIsFocused()) { ToggleView(); } break;
|
|
|
|
|
case "m": if(!InputIsFocused()) { ToggleAnimations(); } break;
|
|
|
|
|
case "h": if(ShouldFireGridEvents()) { EnqueueInteraction(Chron ? interaction_type.SIBLING_SHIFT_PREV : interaction_type.SIBLING_SHIFT_NEXT); } break;
|
|
|
|
|
case "k": if(ShouldFireGridEvents()) { EnqueueInteraction(interaction_type.ASCEND); } break;
|
|
|
|
|
case "l": if(ShouldFireGridEvents()) { EnqueueInteraction(Chron ? interaction_type.SIBLING_SHIFT_NEXT : interaction_type.SIBLING_SHIFT_PREV); } break;
|
|
|
|
|
}
|
2021-02-04 00:13:55 +00:00
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
function
|
|
|
|
|
BindControlKeys()
|
|
|
|
|
{
|
|
|
|
|
document.addEventListener("keydown", ModifyControlKeybinding);
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
function
|
|
|
|
|
UnbindControlKeys()
|
|
|
|
|
{
|
|
|
|
|
document.removeEventListener("keydown", ModifyControlKeybinding);
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
function
|
|
|
|
|
RebindControlKeys()
|
|
|
|
|
{
|
|
|
|
|
UnbindControlKeys();
|
|
|
|
|
BindControlKeys();
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
function
|
|
|
|
|
KeyIsInGrid(GridSize, KeyPos)
|
|
|
|
|
{
|
|
|
|
|
return GridSize.X > KeyPos.X && GridSize.Y > KeyPos.Y;
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
function
|
|
|
|
|
ComputeNaturalKeyIndex(GridSize, KeyPos)
|
|
|
|
|
{
|
|
|
|
|
return KeyPos.Y * GridSize.X + KeyPos.X;
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
function
|
|
|
|
|
EnqueueGridInteraction(GridSize, KeyPos)
|
|
|
|
|
{
|
|
|
|
|
var Chron = Nav.SortChronological;
|
|
|
|
|
var LastButtonIndex = Nav.Buttons.length - 1;
|
|
|
|
|
var NaturalIndex = ComputeNaturalKeyIndex(GridSize, KeyPos);
|
|
|
|
|
var IT = interaction_type.PUSH_BUTTON;
|
|
|
|
|
var ID = { Element: null, Button: null, }; // NOTE(matt): InteractionData
|
|
|
|
|
ID.Element = Nav.Buttons[Chron ? NaturalIndex : LastButtonIndex - NaturalIndex].Element;
|
|
|
|
|
ID.Button = Nav.Buttons[Chron ? NaturalIndex : LastButtonIndex - NaturalIndex];
|
|
|
|
|
EnqueueInteraction(IT, ID);
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
function
|
|
|
|
|
Get2DPosFromIndex(Layout, Index)
|
|
|
|
|
{
|
|
|
|
|
var Result = {
|
|
|
|
|
X: null,
|
|
|
|
|
Y: null,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Result.X = Index % Layout.X;
|
|
|
|
|
Result.Y = (Index - Result.X) / Layout.Y;
|
|
|
|
|
|
|
|
|
|
return Result;
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
2021-02-04 00:13:55 +00:00
|
|
|
|
ModifyGridKeybinding(Event)
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var Key = Event.key;
|
|
|
|
|
// TODO(matt): With this, we could probably easily add a setting for keyboard layout: e.g. Dvorak
|
|
|
|
|
var PhysicalKeys = [
|
|
|
|
|
"1", "2", "3", "4",
|
|
|
|
|
"q", "w", "e", "r",
|
|
|
|
|
"a", "s", "d", "f",
|
|
|
|
|
"z", "x", "c", "v"
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
var KeyLayout = { X: 4, Y: 4 };
|
|
|
|
|
for(var i = 0; i < PhysicalKeys.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
if(Key == PhysicalKeys[i])
|
|
|
|
|
{
|
|
|
|
|
var KeyPos = Get2DPosFromIndex(KeyLayout, i);
|
|
|
|
|
if(KeyIsInGrid(Nav.GridSize, KeyPos) && ShouldFireGridEvents())
|
|
|
|
|
{
|
|
|
|
|
EnqueueGridInteraction(Nav.GridSize, KeyPos);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
2021-02-04 00:13:55 +00:00
|
|
|
|
BindGridKeys()
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
document.addEventListener("keydown", ModifyGridKeybinding);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
2021-02-04 00:13:55 +00:00
|
|
|
|
UnbindGridKeys()
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
document.removeEventListener("keydown", ModifyGridKeybinding);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
DoRotationStage(Now)
|
|
|
|
|
{
|
|
|
|
|
if(Nav.Transition.StageDurations.length)
|
|
|
|
|
{
|
|
|
|
|
if(Nav.Transition.StartTime === undefined)
|
|
|
|
|
{
|
|
|
|
|
Nav.Transition.StartTime = Now;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var Elapsed = Now - Nav.Transition.StartTime;
|
|
|
|
|
var Duration = Nav.Transition.StageDurations[0];
|
|
|
|
|
|
|
|
|
|
if(Duration === 0)
|
|
|
|
|
{
|
|
|
|
|
if(Nav.Transition.Transforms.ButtonsContainer.TargetStages.length)
|
|
|
|
|
{
|
|
|
|
|
MergeTransform(Nav.Transition.Transforms.ButtonsContainer.Current, Nav.Transition.Transforms.ButtonsContainer.TargetStages[0]);
|
|
|
|
|
ApplyTransform(Nav.ButtonsContainer, Nav.Transition.Transforms.ButtonsContainer.Current);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var t = Clamp01(Elapsed / Duration);
|
|
|
|
|
if(LerpTransforms(Nav.Transition.Transforms.ButtonsContainer, t))
|
|
|
|
|
{
|
|
|
|
|
ApplyTransform(Nav.ButtonsContainer, Nav.Transition.Transforms.ButtonsContainer.Current);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(var i = 0; i < Nav.ButtonsContainer.children.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
var This = Nav.ButtonsContainer.children[i];
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var ThisTexts = This.querySelectorAll(".cineraText");
|
2021-01-25 18:09:30 +00:00
|
|
|
|
for(var j = 0; j < ThisTexts.length; ++j)
|
|
|
|
|
{
|
|
|
|
|
ThisTexts[j].style.transform = "rotate3d(0, 0, 1, " + -Nav.Transition.Transforms.ButtonsContainer.Current.Rotation.Z + "deg)";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(TransformsComplete(Nav.Transition.Transforms))
|
|
|
|
|
{
|
|
|
|
|
FinaliseTransforms(Nav.Transition.Transforms);
|
|
|
|
|
}
|
|
|
|
|
Nav.Transition.RequestedFrame = window.requestAnimationFrame(DoRotationStage);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Nav.Transition.RequestedFrame = undefined;
|
|
|
|
|
ResetButtonsContainerClone();
|
|
|
|
|
DequeueInteraction();
|
|
|
|
|
Nav.Controls.Header.style.overflow = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
RotateButtons(Initialising)
|
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
// TODO(matt): Consider pushing this through DoTransition(), aware that it'd need to transform ".cineraText" children
|
2021-01-25 18:09:30 +00:00
|
|
|
|
CopyTransform(Nav.Transition.Transforms.ButtonsContainer.Initial, Nav.Transition.Transforms.ButtonsContainer.Current);
|
|
|
|
|
Nav.Transition.StartTime = undefined;
|
|
|
|
|
if(!Initialising && Nav.Transition.Enabled)
|
|
|
|
|
{
|
|
|
|
|
Nav.Transition.StageDurations.push(320);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Nav.Transition.StageDurations.push(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let Target = NullTarget();
|
|
|
|
|
if(Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
Target.Rotation.Z = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Target.Rotation.Z = 180;
|
|
|
|
|
}
|
|
|
|
|
Nav.Transition.Transforms.ButtonsContainer.TargetStages.push(Target);
|
|
|
|
|
|
|
|
|
|
CopyTransform(Nav.Transition.Transforms.ButtonsContainer.Initial, Nav.Transition.Transforms.ButtonsContainer.Current);
|
|
|
|
|
Nav.Transition.RequestedFrame = window.requestAnimationFrame(DoRotationStage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
AddAnimationClass()
|
|
|
|
|
{
|
|
|
|
|
Nav.Nexus.classList.add("anim");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
Sort(Initialising)
|
|
|
|
|
{
|
|
|
|
|
ResetButtonsContainerClone();
|
|
|
|
|
|
|
|
|
|
if(Initialising && Nav.Transition.Enabled) { Nav.Nexus.classList.remove("anim"); }
|
|
|
|
|
if(Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.Sort.textContent = "Sort: New to Old ⏷";
|
|
|
|
|
Nav.Nexus.classList.add("reversed");
|
|
|
|
|
for(var i = 0; i < Search.Projects.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
if(Search.Projects[i].entriesContainer)
|
|
|
|
|
{
|
|
|
|
|
Search.Projects[i].entriesContainer.style.flexFlow = "column-reverse";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.Sort.textContent = "Sort: Old to New ⏶";
|
|
|
|
|
Nav.Nexus.classList.remove("reversed");
|
|
|
|
|
for(var i = 0; i < Search.Projects.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
if(Search.Projects[i].entriesContainer)
|
|
|
|
|
{
|
|
|
|
|
Search.Projects[i].entriesContainer.style.flexFlow = "column";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(Initialising && Nav.Transition.Enabled) { setTimeout(AddAnimationClass, 320); }
|
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
UnbindGridKeys(Nav.GridSize);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
Nav.SortChronological = !Nav.SortChronological;
|
2021-02-04 00:13:55 +00:00
|
|
|
|
if(Nav.Controls.GridTraversal.PrevAscends)
|
|
|
|
|
{
|
|
|
|
|
if(Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.GridTraversal.Prev.children[0].textContent = "↰";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.GridTraversal.Prev.children[0].textContent = "↲";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-25 18:09:30 +00:00
|
|
|
|
if(MaintainingState())
|
|
|
|
|
{
|
|
|
|
|
if(Nav.SortChronological) { ClearStateBit(state_bit.SORT_REVERSED); }
|
|
|
|
|
else { SetStateBit(state_bit.SORT_REVERSED); }
|
|
|
|
|
}
|
|
|
|
|
RotateButtons(Initialising);
|
2021-02-04 00:13:55 +00:00
|
|
|
|
BindGridKeys();
|
2021-01-25 18:09:30 +00:00
|
|
|
|
runSearch(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
DequeueInteraction()
|
|
|
|
|
{
|
|
|
|
|
if(Nav.InteractionQueue.length)
|
|
|
|
|
{
|
|
|
|
|
var I = Nav.InteractionQueue.shift();
|
|
|
|
|
|
|
|
|
|
switch(I.Type)
|
|
|
|
|
{
|
|
|
|
|
case interaction_type.PUSH_BUTTON: { PushButton(I.Data.Element, I.Data.Button); } break;
|
|
|
|
|
case interaction_type.SIBLING_SHIFT_PREV: { ShiftToPrevSibling(); } break;
|
|
|
|
|
case interaction_type.SIBLING_SHIFT_NEXT: { ShiftToNextSibling(); } break;
|
|
|
|
|
case interaction_type.ASCEND: { Ascend(); } break;
|
|
|
|
|
case interaction_type.SORT: { Sort(); } break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
EnqueueInteraction(InteractionType, Data)
|
|
|
|
|
{
|
|
|
|
|
// TODO(matt): Maybe see about interpolating out of interrupted transitions?
|
|
|
|
|
//
|
|
|
|
|
// Interruptability:
|
|
|
|
|
// Reversible Pairs:
|
|
|
|
|
// SIBLING_SHIFT_PREV by SIBLING_SHIFT_NEXT
|
|
|
|
|
// SIBLING_SHIFT_NEXT (when SHIFT) by SIBLING_SHIFT_PREV
|
|
|
|
|
// SIBLING_SHIFT_NEXT (when ASCEND) by PUSH_BUTTON (of the corresponding button)
|
|
|
|
|
// PUSH_BUTTON by ASCEND
|
|
|
|
|
// ASCEND by PUSH_BUTTON (of the corresponding button)
|
|
|
|
|
// SORT by SORT
|
|
|
|
|
//
|
|
|
|
|
// Mixtures:
|
|
|
|
|
// SIBLING_SHIFT_PREV – SORT
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
var I = {
|
|
|
|
|
Type: InteractionType,
|
|
|
|
|
Data: Data,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Nav.InteractionQueue.push(I);
|
|
|
|
|
|
|
|
|
|
if(!Nav.Transition.RequestedFrame)
|
|
|
|
|
{
|
|
|
|
|
DequeueInteraction();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
function
|
|
|
|
|
UseOrientation(Orientation)
|
|
|
|
|
{
|
|
|
|
|
Nav.GridContainer.classList.remove("Portrait", "Landscape", "Left", "Right");
|
|
|
|
|
switch(Orientation)
|
|
|
|
|
{
|
|
|
|
|
case orientations.PORTRAIT:
|
|
|
|
|
{
|
2021-02-05 19:49:00 +00:00
|
|
|
|
Nav.Controls.GridTraversal.Header.insertBefore(Nav.Controls.GridTraversal.Ascend, Nav.Controls.GridTraversal.Next);
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.GridContainer.classList.add("Portrait");
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case orientations.LANDSCAPE_LEFT:
|
|
|
|
|
{
|
2021-02-05 19:49:00 +00:00
|
|
|
|
Nav.Controls.GridTraversal.Header.parentElement.insertBefore(Nav.Controls.GridTraversal.Ascend, Nav.Controls.GridTraversal.Header);
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.GridContainer.classList.add("Landscape", "Left");
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case orientations.LANDSCAPE_RIGHT:
|
|
|
|
|
{
|
2021-02-05 19:49:00 +00:00
|
|
|
|
Nav.Controls.GridTraversal.Header.parentElement.insertBefore(Nav.Controls.GridTraversal.Ascend, Nav.Controls.GridTraversal.Header);
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.GridContainer.classList.add("Landscape", "Right");
|
|
|
|
|
} break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-25 18:09:30 +00:00
|
|
|
|
function
|
|
|
|
|
InitNexus()
|
|
|
|
|
{
|
|
|
|
|
Nav.List.classList.add("hidden");
|
|
|
|
|
var ButtonsContainerPrototype = document.createElement("div");
|
|
|
|
|
ButtonsContainerPrototype.setAttribute("id", "cineraIndexGrid");
|
|
|
|
|
|
|
|
|
|
var ButtonsPrototype = document.createElement("div");
|
|
|
|
|
ButtonsPrototype.classList.add("cineraButtons");
|
|
|
|
|
|
|
|
|
|
var ButtonsClonePrototype = document.createElement("div");
|
|
|
|
|
ButtonsClonePrototype.classList.add("cineraButtons");
|
|
|
|
|
|
|
|
|
|
ButtonsContainerPrototype.appendChild(ButtonsClonePrototype);
|
|
|
|
|
ButtonsContainerPrototype.appendChild(ButtonsPrototype);
|
|
|
|
|
Nav.Transition.ButtonsTransitionContainerElement = Nav.GridContainer.appendChild(ButtonsContainerPrototype);
|
|
|
|
|
Nav.Grid = Nav.Transition.ButtonsTransitionContainerElement;
|
|
|
|
|
|
|
|
|
|
Nav.Transition.ButtonsContainerCloneElement = Nav.Transition.ButtonsTransitionContainerElement.querySelectorAll(".cineraButtons")[0];
|
|
|
|
|
Nav.ButtonsContainer = Nav.Transition.ButtonsTransitionContainerElement.querySelectorAll(".cineraButtons")[1];
|
|
|
|
|
Nav.GridColumnGap = parseInt(window.getComputedStyle(Nav.ButtonsContainer).gridColumnGap);
|
|
|
|
|
Nav.GridRowGap = parseInt(window.getComputedStyle(Nav.ButtonsContainer).gridRowGap);
|
|
|
|
|
|
|
|
|
|
// NOTE(matt): We ResetButtonsContainerClone() anyway, but without cycling this classList Safari seems to do transitions
|
|
|
|
|
// based on the wrong size grid
|
2021-04-12 16:56:30 +00:00
|
|
|
|
|
2021-01-25 18:09:30 +00:00
|
|
|
|
Nav.GridContainer.classList.add("hidden");
|
|
|
|
|
ResetButtonsContainerClone();
|
2021-04-12 16:56:30 +00:00
|
|
|
|
|
|
|
|
|
switch(Nav.ViewType)
|
|
|
|
|
{
|
|
|
|
|
case view_type.LIST: { Nav.List.classList.remove("hidden"); ScrollCondition = false; } break;
|
|
|
|
|
case view_type.GRID: { Nav.Grid.classList.remove("hidden"); ScrollCondition = true; } break;
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
|
|
|
|
Nav.Nexus.classList.add("anim");
|
|
|
|
|
|
|
|
|
|
if(CineraProps.IsMobile)
|
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
CineraProps.Orientation = GetRealOrientation(orientations.LANDSCAPE_LEFT, CineraProps.IsMobile);
|
|
|
|
|
UseOrientation(CineraProps.Orientation);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ComputePossibleCellsInDimension(AvailableSpaceInPixels, GridGap)
|
|
|
|
|
{
|
|
|
|
|
var Result = 0;
|
|
|
|
|
var SpaceToFill = AvailableSpaceInPixels;
|
|
|
|
|
if(SpaceToFill >= Nav.MinButtonDim)
|
|
|
|
|
{
|
|
|
|
|
SpaceToFill -= Nav.MinButtonDim;
|
|
|
|
|
++Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(; SpaceToFill >= (Nav.MinButtonDim + GridGap); )
|
|
|
|
|
{
|
|
|
|
|
SpaceToFill -= (Nav.MinButtonDim + GridGap);
|
|
|
|
|
++Result;
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
2021-02-04 00:13:55 +00:00
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
GridSizeMeetsMinimumSupported(GridSize)
|
|
|
|
|
{
|
|
|
|
|
return GridSize.X * GridSize.Y >= 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
GridSizeIsSupported(GridSize)
|
|
|
|
|
{
|
|
|
|
|
return (GridSize.X >= Nav.GridMinCellsPerDimension && GridSize.X <= Nav.GridMaxCellsPerDimension) &&
|
|
|
|
|
(GridSize.Y >= Nav.GridMinCellsPerDimension && GridSize.Y <= Nav.GridMaxCellsPerDimension) &&
|
|
|
|
|
GridSizeMeetsMinimumSupported(GridSize);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ComputeOptimalGridSize()
|
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var Result = {
|
|
|
|
|
X: null,
|
|
|
|
|
Y: null,
|
|
|
|
|
};
|
|
|
|
|
|
2021-02-10 22:41:46 +00:00
|
|
|
|
var WindowDim = GetWindowDim(CineraProps.IsMobile);
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var DimReduction = {
|
2021-01-25 18:09:30 +00:00
|
|
|
|
X: 0,
|
|
|
|
|
Y: Nav.Controls.Header.offsetHeight,
|
|
|
|
|
};
|
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Transition.ButtonsTransitionContainerElement.style = null;
|
|
|
|
|
Nav.ButtonsContainer.style = null;
|
2021-02-05 20:15:40 +00:00
|
|
|
|
Nav.Controls.GridTraversal.Container.style = null;
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Controls.GridTraversal.Ascend.style = null;
|
|
|
|
|
Nav.Controls.GridTraversal.Prev.style = null;
|
|
|
|
|
Nav.Controls.GridTraversal.Next.style = null;
|
|
|
|
|
|
|
|
|
|
// TODO(matt): Maybe structure it such that the grid is always not hidden at this point?
|
|
|
|
|
var GridWasHidden = Nav.GridContainer.classList.contains("hidden");
|
|
|
|
|
if(GridWasHidden)
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-12 23:48:02 +00:00
|
|
|
|
Nav.List.classList.add("hidden");
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.GridContainer.classList.remove("hidden");
|
|
|
|
|
}
|
|
|
|
|
if(CineraProps.IsMobile && (CineraProps.Orientation == orientations.LANDSCAPE_LEFT || CineraProps.Orientation == orientations.LANDSCAPE_RIGHT))
|
|
|
|
|
{
|
2021-02-05 20:15:40 +00:00
|
|
|
|
DimReduction.X += Nav.Controls.GridTraversal.Container.offsetWidth;
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-02-05 20:15:40 +00:00
|
|
|
|
DimReduction.Y += Nav.Controls.GridTraversal.Container.offsetHeight;
|
2021-02-04 00:13:55 +00:00
|
|
|
|
}
|
2021-02-12 23:48:02 +00:00
|
|
|
|
|
|
|
|
|
var MaxWidth = MaxWidthOfElement(Nav.GridContainer, WindowDim) - DimReduction.X;
|
|
|
|
|
var MaxHeight = MaxHeightOfElement(Nav.GridContainer, WindowDim) - DimReduction.Y;
|
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
if(GridWasHidden)
|
|
|
|
|
{
|
|
|
|
|
Nav.GridContainer.classList.add("hidden");
|
2021-02-12 23:48:02 +00:00
|
|
|
|
Nav.List.classList.remove("hidden");
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var BodyStyle = window.getComputedStyle(document.body);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
if(Nav.Nexus.parentNode == document.body)
|
|
|
|
|
{
|
|
|
|
|
// TODO(matt): Robustify this
|
|
|
|
|
MaxWidth -= parseInt(BodyStyle.marginRight);
|
|
|
|
|
MaxHeight -= parseInt(BodyStyle.marginBottom);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Result.X = ComputePossibleCellsInDimension(MaxWidth, Nav.GridColumnGap);
|
|
|
|
|
Result.Y = ComputePossibleCellsInDimension(MaxHeight, Nav.GridRowGap);
|
|
|
|
|
|
|
|
|
|
if(GridSizeMeetsMinimumSupported(Result))
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Result.X = Clamp(Nav.GridMinCellsPerDimension, Result.X, Nav.GridMaxCellsPerDimension);
|
|
|
|
|
Result.Y = Clamp(Nav.GridMinCellsPerDimension, Result.Y, Nav.GridMaxCellsPerDimension);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var ButtonDimBasedOnX = Math.floor((MaxWidth - Nav.GridColumnGap * (Result.X - 1)) / Result.X);
|
|
|
|
|
var ButtonDimBasedOnY = Math.floor((MaxHeight - Nav.GridRowGap * (Result.Y - 1)) / Result.Y);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.ButtonDim = Math.min(ButtonDimBasedOnX, ButtonDimBasedOnY);
|
|
|
|
|
Nav.ButtonDim -= Nav.ButtonDim % 2; // NOTE(matt): Even-length helps CSS keep the head & tail's correct size when rotated 180 degrees
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var GridTemplateColumnsStyle = "repeat(" + Result.X + ", minmax(" + Nav.ButtonDim + "px, " + Nav.ButtonDim + "px))";
|
|
|
|
|
Nav.ButtonsContainer.style.gridTemplateColumns = GridTemplateColumnsStyle;
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var GridTemplateRowsStyle = "repeat(" + Result.Y + ", " + Nav.ButtonDim + "px)";
|
|
|
|
|
Nav.ButtonsContainer.style.gridTemplateRows = GridTemplateRowsStyle;
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.GridDim.X = Nav.ButtonDim * Result.X + Nav.GridColumnGap * (Result.X - 1);
|
|
|
|
|
Nav.GridDim.Y = Nav.ButtonDim * Result.Y + Nav.GridRowGap * (Result.Y - 1);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
SetDim(Nav.Transition.ButtonsTransitionContainerElement, Nav.GridDim.X + "px", Nav.GridDim.Y + "px");
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-05 20:15:40 +00:00
|
|
|
|
Nav.Controls.GridTraversal.Container.style.maxWidth = Nav.GridDim.X + "px";
|
|
|
|
|
Nav.Controls.GridTraversal.Container.style.maxHeight = Nav.GridDim.Y + "px";
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var TraversalButtonCount = 3;
|
2021-02-05 20:15:40 +00:00
|
|
|
|
if(Nav.Controls.GridTraversal.Container.scrollWidth > Nav.Controls.GridTraversal.Container.clientWidth)
|
2021-02-04 00:13:55 +00:00
|
|
|
|
{
|
2021-02-05 20:15:40 +00:00
|
|
|
|
var TraversalButtonDim = Nav.Controls.GridTraversal.Container.clientWidth / TraversalButtonCount;
|
2021-02-04 00:13:55 +00:00
|
|
|
|
SetDim(Nav.Controls.GridTraversal.Ascend, TraversalButtonDim + "px", TraversalButtonDim + "px");
|
|
|
|
|
SetDim(Nav.Controls.GridTraversal.Prev, TraversalButtonDim + "px", TraversalButtonDim + "px");
|
|
|
|
|
SetDim(Nav.Controls.GridTraversal.Next, TraversalButtonDim + "px", TraversalButtonDim + "px");
|
|
|
|
|
}
|
2021-02-05 20:15:40 +00:00
|
|
|
|
if(Nav.Controls.GridTraversal.Container.scrollHeight > Nav.Controls.GridTraversal.Container.clientHeight)
|
2021-02-04 00:13:55 +00:00
|
|
|
|
{
|
2021-02-05 20:15:40 +00:00
|
|
|
|
var TraversalButtonDim = Nav.Controls.GridTraversal.Container.clientHeight / TraversalButtonCount;
|
2021-02-04 00:13:55 +00:00
|
|
|
|
SetDim(Nav.Controls.GridTraversal.Ascend, TraversalButtonDim + "px", TraversalButtonDim + "px");
|
|
|
|
|
SetDim(Nav.Controls.GridTraversal.Prev, TraversalButtonDim + "px", TraversalButtonDim + "px");
|
|
|
|
|
SetDim(Nav.Controls.GridTraversal.Next, TraversalButtonDim + "px", TraversalButtonDim + "px");
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
ResetButtonsContainerClone(); // NOTE(matt): This reapplies the sorting Z-rotation
|
2021-01-25 18:09:30 +00:00
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
GetScrollToElement(NodeList, UpperIndex)
|
|
|
|
|
{
|
|
|
|
|
var Result = undefined;
|
|
|
|
|
if(UpperIndex === null)
|
|
|
|
|
{
|
|
|
|
|
if(NodeList.length)
|
|
|
|
|
{
|
|
|
|
|
Result = NodeList[0].closest(".cineraIndexProject");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Result = NodeList.closest(".cineraIndexProject");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Result = NodeList[UpperIndex];
|
|
|
|
|
}
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
EmptyTraversalStack()
|
|
|
|
|
{
|
|
|
|
|
while(Nav.TraversalStack.length > 1)
|
|
|
|
|
{
|
|
|
|
|
Nav.TraversalStack.pop();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
GetContainingProjectOfLevel(Level)
|
|
|
|
|
{
|
|
|
|
|
var Result = null;
|
|
|
|
|
if(Level.Projects !== null)
|
|
|
|
|
{
|
|
|
|
|
if(Level.Projects.length)
|
|
|
|
|
{
|
|
|
|
|
Result = Level.Projects[0].parentNode;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Result = Level.Projects.parentNode;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(Level.Entries !== null)
|
|
|
|
|
{
|
|
|
|
|
if(Level.Entries.length)
|
|
|
|
|
{
|
|
|
|
|
Result = Level.Entries[0].closest(".cineraIndexProject");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Result = Level.Entries.closest(".cineraIndexProject");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
GetContainingProject(ProjectElement)
|
|
|
|
|
{
|
|
|
|
|
var Result = null;
|
|
|
|
|
if(ProjectElement.parentNode.classList.contains("cineraIndexProject"))
|
|
|
|
|
{
|
|
|
|
|
Result = ProjectElement.parentNode;
|
|
|
|
|
}
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
PushProjectOntoStack(Stack, ContainingProject)
|
|
|
|
|
{
|
|
|
|
|
var Project = {
|
|
|
|
|
Element: ContainingProject,
|
|
|
|
|
Projects: ContainingProject.querySelectorAll(":scope > .cineraIndexProject"),
|
|
|
|
|
Entries: ContainingProject.querySelectorAll(":scope > .cineraIndexEntries > div"),
|
|
|
|
|
Index: null,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if(Project.Projects.length == 0) { Project.Projects = null; }
|
|
|
|
|
if(Project.Entries.length == 0) { Project.Entries = null; }
|
|
|
|
|
|
|
|
|
|
var Siblings = ContainingProject.parentNode.querySelectorAll(":scope > .cineraIndexProject");
|
|
|
|
|
Project.Index = GetIndexOfElement(Siblings, ContainingProject);
|
|
|
|
|
Stack.push(Project);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
BuildProjectsStack(TargetLevel)
|
|
|
|
|
{
|
|
|
|
|
let ProjectsStack = [];
|
|
|
|
|
var ContainingProject = GetContainingProjectOfLevel(TargetLevel);
|
|
|
|
|
|
|
|
|
|
PushProjectOntoStack(ProjectsStack, ContainingProject);
|
|
|
|
|
|
|
|
|
|
ContainingProject = GetContainingProject(ProjectsStack[ProjectsStack.length - 1].Element);
|
|
|
|
|
while(ContainingProject)
|
|
|
|
|
{
|
|
|
|
|
PushProjectOntoStack(ProjectsStack, ContainingProject);
|
|
|
|
|
ContainingProject = GetContainingProject(ProjectsStack[ProjectsStack.length - 1].Element);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ProjectsStack;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
EmptyTraversalStackIntoProjectsStack()
|
|
|
|
|
{
|
|
|
|
|
let ProjectsStack = [];
|
|
|
|
|
while(Nav.TraversalStack.length > 1)
|
|
|
|
|
{
|
|
|
|
|
let ThisLevel = Nav.TraversalStack[Nav.TraversalStack.length - 1];
|
|
|
|
|
PushLevelProjectUniquely(ProjectsStack, ThisLevel);
|
|
|
|
|
Nav.TraversalStack.pop();
|
|
|
|
|
}
|
|
|
|
|
return ProjectsStack;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
DeriveTraversalStack(ProjectsStack, TargetLevel)
|
|
|
|
|
{
|
|
|
|
|
if(ProjectsStack && TargetLevel)
|
|
|
|
|
{
|
|
|
|
|
while(ProjectsStack.length > 0)
|
|
|
|
|
{
|
|
|
|
|
var ThisProject = ProjectsStack[ProjectsStack.length - 1];
|
|
|
|
|
|
|
|
|
|
var ButtonInfo = { HeadIndex: null, TailIndex: null, ItemCount: null, };
|
|
|
|
|
let PopulationData = {
|
|
|
|
|
Distribution: null,
|
|
|
|
|
ThisLevel: null,
|
|
|
|
|
Prev: null,
|
|
|
|
|
DoingEntries: false,
|
|
|
|
|
};
|
|
|
|
|
PopulationData.ThisLevel = Nav.TraversalStack[Nav.TraversalStack.length - 1];
|
|
|
|
|
PopulationData.Distribution = ComputeItemDistribution(PopulationData.ThisLevel);
|
|
|
|
|
|
|
|
|
|
for(var ButtonIndex = 0; ButtonIndex < Nav.Buttons.length; ++ButtonIndex)
|
|
|
|
|
{
|
|
|
|
|
let Button = {
|
|
|
|
|
Projects: null,
|
|
|
|
|
Entries: null,
|
|
|
|
|
HeadIndex: null,
|
|
|
|
|
TailIndex: null,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if(PopulationData.Distribution.ProjectsToPlace > 0 || PopulationData.Distribution.EntriesToPlace > 0)
|
|
|
|
|
{
|
|
|
|
|
ButtonInfo = PopulateButton(PopulationData, Button, ButtonIndex);
|
|
|
|
|
if(ButtonInfo.HeadIndex <= ThisProject.Index && ButtonInfo.TailIndex >= ThisProject.Index)
|
|
|
|
|
{
|
|
|
|
|
var FoundProject = PseudoPushButton(Button);
|
|
|
|
|
if(FoundProject)
|
|
|
|
|
{
|
|
|
|
|
ProjectsStack.pop();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
PopulationData.Prev = ButtonInfo;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(TargetLevel.HeadIndex !== null && TargetLevel.TailIndex !== null)
|
|
|
|
|
{
|
|
|
|
|
var Descended = false;
|
|
|
|
|
while(true)
|
|
|
|
|
{
|
|
|
|
|
let PopulationData = {
|
|
|
|
|
Distribution: null,
|
|
|
|
|
ThisLevel: null,
|
|
|
|
|
Prev: null,
|
|
|
|
|
DoingEntries: false,
|
|
|
|
|
};
|
|
|
|
|
PopulationData.ThisLevel = Nav.TraversalStack[Nav.TraversalStack.length - 1];
|
|
|
|
|
PopulationData.Distribution = ComputeItemDistribution(PopulationData.ThisLevel);
|
|
|
|
|
|
|
|
|
|
for(var ButtonIndex = 0; ButtonIndex < Nav.Buttons.length; ++ButtonIndex)
|
|
|
|
|
{
|
|
|
|
|
let Button = {
|
|
|
|
|
Projects: null,
|
|
|
|
|
Entries: null,
|
|
|
|
|
HeadIndex: null,
|
|
|
|
|
TailIndex: null,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if(PopulationData.Distribution.ProjectsToPlace > 0 || PopulationData.Distribution.EntriesToPlace > 0)
|
|
|
|
|
{
|
|
|
|
|
ButtonInfo = PopulateButton(PopulationData, Button, ButtonIndex);
|
|
|
|
|
if(ButtonInfo.HeadIndex <= TargetLevel.HeadIndex && ButtonInfo.TailIndex >= TargetLevel.TailIndex)
|
|
|
|
|
{
|
|
|
|
|
Descended = true;
|
|
|
|
|
PseudoPushButton(Button);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
PopulationData.Prev = ButtonInfo;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(!Descended)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Descended = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
GetGenerationOf(Element)
|
|
|
|
|
{
|
|
|
|
|
var Result = 0;
|
|
|
|
|
if(Element.classList.contains("cineraIndexProject"))
|
|
|
|
|
{
|
|
|
|
|
++Result;
|
|
|
|
|
var ContainingProject = GetContainingProject(Element);
|
|
|
|
|
while(ContainingProject)
|
|
|
|
|
{
|
|
|
|
|
++Result;
|
|
|
|
|
ContainingProject = GetContainingProject(ContainingProject);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ComputeTargetLevelForViewport()
|
|
|
|
|
{
|
|
|
|
|
var Result = {
|
|
|
|
|
Projects: null,
|
|
|
|
|
Entries: null,
|
|
|
|
|
HeadIndex: null,
|
|
|
|
|
TailIndex: null,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var ViewportTop = window.scrollY;
|
|
|
|
|
var ViewportBottom = ViewportTop + window.innerHeight;
|
|
|
|
|
|
|
|
|
|
var ControlsHeight = Nav.Controls.Header.offsetHeight;
|
|
|
|
|
ViewportTop += ControlsHeight;
|
|
|
|
|
|
|
|
|
|
var Elements = [];
|
|
|
|
|
for(var ProjectIndex = 0; ProjectIndex < Search.Projects.length; ++ProjectIndex)
|
|
|
|
|
{
|
|
|
|
|
var ThisProject = Search.Projects[ProjectIndex];
|
|
|
|
|
Elements.push(ThisProject.projectTitleElement);
|
|
|
|
|
if(ThisProject.entriesContainer)
|
|
|
|
|
{
|
|
|
|
|
var Entries = ThisProject.entriesContainer.querySelectorAll(":scope > div");
|
|
|
|
|
if(Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
for(var EntryIndex = 0; EntryIndex < Entries.length; ++EntryIndex)
|
|
|
|
|
{
|
|
|
|
|
Elements.push(Entries[EntryIndex]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for(var EntryIndex = Entries.length - 1; EntryIndex >= 0; --EntryIndex)
|
|
|
|
|
{
|
|
|
|
|
Elements.push(Entries[EntryIndex]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var Upper = {
|
|
|
|
|
Element: null,
|
|
|
|
|
Parent: null,
|
|
|
|
|
Type: null,
|
|
|
|
|
};
|
|
|
|
|
var Lower = {
|
|
|
|
|
Element: null,
|
|
|
|
|
Parent: null,
|
|
|
|
|
Type: null,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for(var i = 0; i < Elements.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
var ElementTop = getElementYOffsetFromPage(Elements[i]);
|
|
|
|
|
if(!Upper.Element)
|
|
|
|
|
{
|
|
|
|
|
if(ElementTop >= ViewportTop)
|
|
|
|
|
{
|
|
|
|
|
Upper.Element = Elements[i];
|
|
|
|
|
Lower.Element = Upper.Element;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var ElementBottom = ElementTop + Elements[i].scrollHeight;
|
|
|
|
|
if(ElementBottom <= ViewportBottom)
|
|
|
|
|
{
|
|
|
|
|
Lower.Element = Elements[i];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Upper.Type = Upper.Element.classList.contains("cineraProjectTitle") ? item_type.PROJECT : item_type.ENTRY;
|
|
|
|
|
Lower.Type = Lower.Element.classList.contains("cineraProjectTitle") ? item_type.PROJECT : item_type.ENTRY;
|
|
|
|
|
|
|
|
|
|
Upper.Parent = Upper.Element.closest(".cineraIndexProject");
|
|
|
|
|
Lower.Parent = Lower.Element.closest(".cineraIndexProject");
|
|
|
|
|
|
|
|
|
|
if(Upper.Parent == Lower.Parent)
|
|
|
|
|
{
|
|
|
|
|
if(Upper.Type == Lower.Type)
|
|
|
|
|
{
|
|
|
|
|
switch(Upper.Type)
|
|
|
|
|
{
|
|
|
|
|
case item_type.PROJECT:
|
|
|
|
|
{
|
|
|
|
|
Result.Projects = Upper.Parent.querySelectorAll(":scope > .cineraIndexProject");
|
|
|
|
|
Result.HeadIndex = GetIndexOfElement(Result.Projects, Upper.Element);
|
|
|
|
|
Result.TailIndex = GetIndexOfElement(Result.Projects, Lower.Element);
|
|
|
|
|
} break;
|
|
|
|
|
case item_type.ENTRY:
|
|
|
|
|
{
|
|
|
|
|
Result.Entries = Upper.Parent.querySelectorAll(":scope > .cineraIndexEntries > div");
|
|
|
|
|
Result.HeadIndex = GetIndexOfElement(Result.Entries, Upper.Element);
|
|
|
|
|
Result.TailIndex = GetIndexOfElement(Result.Entries, Lower.Element);
|
|
|
|
|
} break;
|
|
|
|
|
}
|
|
|
|
|
if(!Nav.SortChronological)
|
|
|
|
|
{
|
|
|
|
|
var Temp = Result.HeadIndex;
|
|
|
|
|
Result.HeadIndex = Result.TailIndex;
|
|
|
|
|
Result.TailIndex = Temp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Result.Projects = Upper.Parent.querySelectorAll(":scope > .cineraIndexProject");
|
|
|
|
|
if(Result.Projects.length == 0) { Result.Projects = null; }
|
|
|
|
|
Result.Entries = Upper.Parent.querySelectorAll(":scope > .cineraIndexEntries > div");
|
|
|
|
|
if(Result.Entries.length == 0) { Result.Entries = null; }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var UpperGeneration = GetGenerationOf(Upper.Parent);
|
|
|
|
|
var LowerGeneration = GetGenerationOf(Lower.Parent);
|
|
|
|
|
while(UpperGeneration > LowerGeneration)
|
|
|
|
|
{
|
|
|
|
|
Upper.Parent = GetContainingProject(Upper.Parent);
|
|
|
|
|
--UpperGeneration;
|
|
|
|
|
}
|
|
|
|
|
while(LowerGeneration > UpperGeneration)
|
|
|
|
|
{
|
|
|
|
|
Lower.Parent = GetContainingProject(Lower.Parent);
|
|
|
|
|
--LowerGeneration;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(UpperGeneration == 0 && LowerGeneration == 0)
|
|
|
|
|
{
|
|
|
|
|
Upper.Parent = null;
|
|
|
|
|
Lower.Parent = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while(Upper.Parent && Lower.Parent && Upper.Parent != Lower.Parent)
|
|
|
|
|
{
|
|
|
|
|
Upper.Parent = GetContainingProject(Upper.Parent);
|
|
|
|
|
Lower.Parent = GetContainingProject(Lower.Parent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(Upper.Parent != null)
|
|
|
|
|
{
|
|
|
|
|
Result.Projects = Upper.Parent.querySelectorAll(":scope > .cineraIndexProject");
|
|
|
|
|
if(Result.Projects.length == 0) { Result.Projects = null; }
|
|
|
|
|
Result.Entries = Upper.Parent.querySelectorAll(":scope > .cineraIndexEntries > div");
|
|
|
|
|
if(Result.Entries.length == 0) { Result.Entries = null; }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while(Elements.length > 0)
|
|
|
|
|
{
|
|
|
|
|
Elements.pop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ScrollToWithOffset(Element, Offset)
|
|
|
|
|
{
|
|
|
|
|
var ScrollTop = getElementYOffsetFromPage(Element);
|
|
|
|
|
ScrollTop -= Offset;
|
|
|
|
|
window.scrollTo(0, ScrollTop);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
2021-04-12 16:56:30 +00:00
|
|
|
|
PickGridView()
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-04-12 16:56:30 +00:00
|
|
|
|
if(GridSizeIsSupported(Nav.GridSize))
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-04-12 16:56:30 +00:00
|
|
|
|
Nav.Controls.View.textContent = "View: 𝑛-ary Grid";
|
|
|
|
|
Nav.ViewType = view_type.GRID;
|
2021-01-25 18:09:30 +00:00
|
|
|
|
if(MaintainingState())
|
|
|
|
|
{
|
2021-04-12 16:56:30 +00:00
|
|
|
|
ClearStateBit(state_bit.VIEW_LIST);
|
|
|
|
|
SetStateBit(state_bit.VIEW_GRID);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!IsQuery())
|
|
|
|
|
{
|
2021-04-12 16:56:30 +00:00
|
|
|
|
var TargetLevel = ComputeTargetLevelForViewport();
|
|
|
|
|
EmptyTraversalStack();
|
|
|
|
|
if(TargetLevel.Projects || TargetLevel.Entries)
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-04-12 16:56:30 +00:00
|
|
|
|
var ProjectsStack = BuildProjectsStack(TargetLevel);
|
|
|
|
|
DeriveTraversalStack(ProjectsStack, TargetLevel);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
2021-04-12 16:56:30 +00:00
|
|
|
|
Nav.List.classList.add("hidden");
|
|
|
|
|
Nav.GridContainer.classList.remove("hidden");
|
|
|
|
|
|
|
|
|
|
UpdateButtons();
|
|
|
|
|
|
|
|
|
|
ScrollToWithOffset(Nav.Controls.GridTraversal.Header, Nav.Controls.Header.offsetHeight);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
2021-04-12 16:56:30 +00:00
|
|
|
|
ScrollCondition = true;
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-04-12 16:56:30 +00:00
|
|
|
|
// TODO(matt): Inform user that grid view is unavailable
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
PickListView()
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.View.textContent = "View: List";
|
|
|
|
|
Nav.ViewType = view_type.LIST;
|
|
|
|
|
if(MaintainingState())
|
|
|
|
|
{
|
|
|
|
|
ClearStateBit(state_bit.VIEW_GRID);
|
|
|
|
|
SetStateBit(state_bit.VIEW_LIST);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!IsQuery())
|
|
|
|
|
{
|
|
|
|
|
Nav.List.classList.remove("hidden");
|
|
|
|
|
Nav.GridContainer.classList.add("hidden");
|
|
|
|
|
var LevelBundle = GetTraversalLevelBundle();
|
|
|
|
|
if(LevelBundle.Generation > 1)
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-04-12 16:56:30 +00:00
|
|
|
|
var Element;
|
|
|
|
|
if(LevelBundle.This.Entries !== null)
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-04-12 16:56:30 +00:00
|
|
|
|
Element = GetScrollToElement(LevelBundle.This.Entries, Nav.SortChronological ? LevelBundle.This.HeadIndex : LevelBundle.This.TailIndex);
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
2021-04-12 16:56:30 +00:00
|
|
|
|
else if(LevelBundle.This.Projects !== null)
|
2021-02-04 00:13:55 +00:00
|
|
|
|
{
|
2021-04-12 16:56:30 +00:00
|
|
|
|
Element = GetScrollToElement(LevelBundle.This.Projects, Nav.SortChronological ? LevelBundle.This.HeadIndex : LevelBundle.This.TailIndex);
|
2021-02-04 00:13:55 +00:00
|
|
|
|
}
|
2021-04-12 16:56:30 +00:00
|
|
|
|
ScrollToWithOffset(Element, Nav.Controls.Header.offsetHeight);
|
2021-02-04 00:13:55 +00:00
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
2021-04-12 16:56:30 +00:00
|
|
|
|
ScrollCondition = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ToggleView()
|
|
|
|
|
{
|
|
|
|
|
// NOTE(matt): While we only have two views, a toggle will suffice.
|
|
|
|
|
clearTimeout(ScrollerFunction);
|
|
|
|
|
ScrollTicking = false;
|
|
|
|
|
if(Nav.ViewType == view_type.GRID)
|
|
|
|
|
{
|
|
|
|
|
PickListView();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
PickGridView();
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ToggleAnimations()
|
|
|
|
|
{
|
|
|
|
|
Nav.Transition.Enabled = !Nav.Transition.Enabled;
|
|
|
|
|
if(Nav.Transition.Enabled)
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.Anim.textContent = "Animations: ✔";
|
|
|
|
|
Nav.Nexus.classList.add("anim");
|
|
|
|
|
ClearStateBit(state_bit.DISABLE_ANIMATIONS);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.Anim.textContent = "Animations: ✘";
|
|
|
|
|
Nav.Nexus.classList.remove("anim");
|
|
|
|
|
SetStateBit(state_bit.DISABLE_ANIMATIONS);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ToggleSave()
|
|
|
|
|
{
|
|
|
|
|
if(MaintainingState())
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.Save.textContent = "Save Settings: ✘";
|
|
|
|
|
Nav.State = 0;
|
|
|
|
|
Nav.State |= state_bit.NO_SAVE;
|
|
|
|
|
SaveState();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Nav.Controls.Save.textContent = "Save Settings: ✔";
|
|
|
|
|
Nav.State ^= state_bit.NO_SAVE;
|
|
|
|
|
if(!Nav.Transition.Enabled) { SetStateBit(state_bit.DISABLE_ANIMATIONS); }
|
|
|
|
|
if(!Nav.SortChronological) { SetStateBit(state_bit.SORT_REVERSED); }
|
|
|
|
|
|
|
|
|
|
if(Nav.ViewType == view_type.LIST) { SetStateBit(state_bit.VIEW_LIST); }
|
|
|
|
|
else if(Nav.ViewType == view_type.GRID) { SetStateBit(state_bit.VIEW_GRID); }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
BindMenuItem(Item)
|
|
|
|
|
{
|
|
|
|
|
// TODO(matt): Enable this to bind the "click" event, making it take a function and parameters
|
|
|
|
|
Item.addEventListener("mouseover", function(ev) { this.classList.add("focused"); });
|
|
|
|
|
Item.addEventListener("mouseout", function(ev) { this.classList.remove("focused"); });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
BindControls()
|
|
|
|
|
{
|
|
|
|
|
var SettingsMenu = Nav.Controls.Header.querySelector(".cineraMenu.ViewSettings");
|
|
|
|
|
var SettingsMenuContainer = SettingsMenu.querySelector(".cineraMenuContainer");
|
|
|
|
|
|
|
|
|
|
SettingsMenu.addEventListener("mouseenter", function(ev) {
|
|
|
|
|
SettingsMenuContainer.classList.add("visible");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
SettingsMenu.addEventListener("mouseleave", function(ev) {
|
|
|
|
|
SettingsMenuContainer.classList.remove("visible");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
SettingsMenu.addEventListener("click", function(ev) {
|
|
|
|
|
SettingsMenuContainer.classList.toggle("visible");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
BindMenuItem(Nav.Controls.Sort);
|
|
|
|
|
Nav.Controls.Sort.addEventListener("click", function(ev) { ev.stopPropagation(); EnqueueInteraction(interaction_type.SORT); });
|
|
|
|
|
BindMenuItem(Nav.Controls.View);
|
|
|
|
|
Nav.Controls.View.addEventListener("click", function(ev) { ev.stopPropagation(); ToggleView(); });
|
|
|
|
|
BindMenuItem(Nav.Controls.Anim);
|
|
|
|
|
Nav.Controls.Anim.addEventListener("click", function(ev) { ev.stopPropagation(); ToggleAnimations(); });
|
|
|
|
|
BindMenuItem(Nav.Controls.Save);
|
|
|
|
|
Nav.Controls.Save.addEventListener("click", function(ev) { ev.stopPropagation(); ToggleSave(); });
|
|
|
|
|
|
|
|
|
|
var Filter = Nav.Controls.Header.querySelector(".cineraMenu.IndexFilter");
|
|
|
|
|
if(Filter)
|
|
|
|
|
{
|
|
|
|
|
var FilterContainer = Filter.querySelector(".cineraMenuContainer");
|
|
|
|
|
// TODO(matt): Once we have multiple menus, use a menuState on this page
|
|
|
|
|
// menuState.push(Filter);
|
|
|
|
|
|
|
|
|
|
Filter.addEventListener("mouseenter", function(ev) {
|
|
|
|
|
FilterContainer.classList.add("visible");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Filter.addEventListener("mouseleave", function(ev) {
|
|
|
|
|
FilterContainer.classList.remove("visible");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Filter.addEventListener("click", function(ev) {
|
|
|
|
|
FilterContainer.classList.toggle("visible");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var IndexFilterProjects = Filter.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);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Search.QueryElement.addEventListener("input", function(ev) {
|
|
|
|
|
history.replaceState(null, null, "#" + encodeURIComponent(Search.QueryElement.value));
|
|
|
|
|
runSearch();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Nav.Controls.GridTraversal.Prev.addEventListener("click", function() { EnqueueInteraction(interaction_type.SIBLING_SHIFT_PREV) });
|
|
|
|
|
Nav.Controls.GridTraversal.Ascend.addEventListener("click", function() { EnqueueInteraction(interaction_type.ASCEND) });
|
|
|
|
|
Nav.Controls.GridTraversal.Next.addEventListener("click", function() { EnqueueInteraction(interaction_type.SIBLING_SHIFT_NEXT) });
|
|
|
|
|
|
|
|
|
|
BindHelp(Nav.Controls.Help, Nav.Controls.HelpDocumentation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
InitButtons()
|
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
if(GridSizeIsSupported(Nav.GridSize))
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var ButtonPrototype = document.createElement("div");
|
|
|
|
|
ButtonPrototype.classList.add("cineraButton", "subdivision");
|
|
|
|
|
for(var i = 0; i < Nav.GridSize.X * Nav.GridSize.Y; ++i)
|
|
|
|
|
{
|
|
|
|
|
Nav.ButtonsContainer.appendChild(ButtonPrototype.cloneNode());
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
var Buttons = Nav.ButtonsContainer.querySelectorAll(".cineraButton.subdivision");
|
|
|
|
|
for(let j = 0; j < Buttons.length; ++j)
|
|
|
|
|
{
|
|
|
|
|
let Button = {
|
|
|
|
|
Element: Buttons[j],
|
|
|
|
|
Projects: [],
|
|
|
|
|
Entries: [],
|
|
|
|
|
HeadIndex: null,
|
|
|
|
|
TailIndex: null,
|
2021-01-25 18:09:30 +00:00
|
|
|
|
};
|
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Buttons[j].addEventListener("click", function() {
|
|
|
|
|
let InteractionData = {
|
|
|
|
|
Element: this,
|
|
|
|
|
Button: Button,
|
|
|
|
|
};
|
|
|
|
|
EnqueueInteraction(interaction_type.PUSH_BUTTON, InteractionData);
|
|
|
|
|
});
|
2021-01-25 18:09:30 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
Nav.Buttons.push(Button);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ReinitButtons()
|
|
|
|
|
{
|
|
|
|
|
for(; Nav.Buttons.length > 0;)
|
|
|
|
|
{
|
|
|
|
|
Nav.Buttons[0].Element.remove();
|
|
|
|
|
Nav.Buttons.shift();
|
|
|
|
|
}
|
|
|
|
|
InitButtons();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
PushLevelProjectUniquely(Stack, Level)
|
|
|
|
|
{
|
|
|
|
|
if(Level.HeadIndex == null && Level.TailIndex == null)
|
|
|
|
|
{
|
|
|
|
|
var Found = false;
|
|
|
|
|
var ContainingProject = GetContainingProjectOfLevel(Level);
|
|
|
|
|
|
|
|
|
|
for(var i = 0; i < Stack.length; ++i)
|
|
|
|
|
{
|
|
|
|
|
if(ContainingProject === Stack[i].Element)
|
|
|
|
|
{
|
|
|
|
|
Found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!Found)
|
|
|
|
|
{
|
|
|
|
|
PushProjectOntoStack(Stack, ContainingProject);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
ButtonContainsLevel(Button, Level)
|
|
|
|
|
{
|
|
|
|
|
var Result = false;
|
|
|
|
|
if(NodesMatch(Button.Projects, Level.Projects) &&
|
|
|
|
|
NodesMatch(Button.Entries, Level.Entries) &&
|
|
|
|
|
Button.HeadIndex <= Level.HeadIndex &&
|
|
|
|
|
Button.TailIndex >= Level.TailIndex)
|
|
|
|
|
{
|
|
|
|
|
Result = true;
|
|
|
|
|
}
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
PopulateButton(PopulationData, Button, ButtonIndex)
|
|
|
|
|
{
|
|
|
|
|
var Result;
|
|
|
|
|
if(PopulationData.Distribution.ProjectsToPlace > 0)
|
|
|
|
|
{
|
|
|
|
|
Button.Projects = PopulationData.ThisLevel.Projects;
|
|
|
|
|
if(PopulationData.Distribution.ProjectsToPlace == 1 || PopulationData.Distribution.ProjectsToPlace == PopulationData.Distribution.ButtonsForProjects - ButtonIndex)
|
|
|
|
|
{
|
|
|
|
|
PopulationData.Distribution.FullButtonProjectCount = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Result = SetButtonInfo(Button, PopulationData.Prev, PopulationData.ThisLevel, PopulationData.Distribution);
|
|
|
|
|
if(Result.ItemCount == 1)
|
|
|
|
|
{
|
|
|
|
|
Button.Projects = PopulationData.ThisLevel.Projects[Result.HeadIndex];
|
|
|
|
|
}
|
|
|
|
|
PopulationData.Distribution.ProjectsToPlace -= Result.ItemCount;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Button.Entries = PopulationData.ThisLevel.Entries;
|
|
|
|
|
if(!PopulationData.DoingEntries)
|
|
|
|
|
{
|
|
|
|
|
PopulationData.Prev = null;
|
|
|
|
|
PopulationData.DoingEntries = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(PopulationData.Distribution.EntriesToPlace == 1 || PopulationData.Distribution.EntriesToPlace == PopulationData.Distribution.ButtonsForEntries - ButtonIndex)
|
|
|
|
|
{
|
|
|
|
|
PopulationData.Distribution.FullButtonEntryCount = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Result = SetButtonInfo(Button, PopulationData.Prev, PopulationData.ThisLevel, PopulationData.Distribution);
|
|
|
|
|
if(Result.ItemCount == 1)
|
|
|
|
|
{
|
|
|
|
|
Button.Entries = Button.Entries[Result.HeadIndex];
|
|
|
|
|
}
|
|
|
|
|
PopulationData.Distribution.EntriesToPlace -= Result.ItemCount;
|
|
|
|
|
}
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
PseudoPushButton(Button)
|
|
|
|
|
{
|
|
|
|
|
var ButtonIsProjectOrEntry = false;
|
|
|
|
|
|
|
|
|
|
var Level = {
|
|
|
|
|
Projects: Button.Projects,
|
|
|
|
|
Entries: Button.Entries,
|
|
|
|
|
HeadIndex: Button.HeadIndex,
|
|
|
|
|
TailIndex: Button.TailIndex,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if(Level.Projects !== null || Level.Entries !== null)
|
|
|
|
|
{
|
|
|
|
|
if(Level.Projects !== null)
|
|
|
|
|
{
|
|
|
|
|
if(Level.Projects.length === undefined)
|
|
|
|
|
{
|
|
|
|
|
var Entries = Level.Projects.querySelectorAll(":scope > .cineraIndexEntries > div");
|
|
|
|
|
var Projects = Level.Projects.querySelectorAll(":scope > .cineraIndexProject");
|
|
|
|
|
Level.Entries = Entries.length ? Entries : null;
|
|
|
|
|
Level.Projects = Projects.length ? Projects : null;
|
|
|
|
|
ButtonIsProjectOrEntry = true;
|
|
|
|
|
}
|
|
|
|
|
Nav.TraversalStack.push(Level);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(Level.Entries.length === undefined)
|
|
|
|
|
{
|
|
|
|
|
ButtonIsProjectOrEntry = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Nav.TraversalStack.push(Level);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ButtonIsProjectOrEntry;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
2021-02-04 00:13:55 +00:00
|
|
|
|
ResizeFunction()
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
CineraProps.Orientation = GetRealOrientation(orientations.LANDSCAPE_LEFT, CineraProps.IsMobile);
|
|
|
|
|
if(CineraProps.IsMobile)
|
|
|
|
|
{
|
|
|
|
|
UseOrientation(CineraProps.Orientation);
|
|
|
|
|
}
|
|
|
|
|
var NewGridSize = ComputeOptimalGridSize();
|
|
|
|
|
if(Nav.GridSize !== NewGridSize)
|
|
|
|
|
{
|
|
|
|
|
UnbindGridKeys();
|
|
|
|
|
Nav.GridSize = NewGridSize;
|
|
|
|
|
ReinitButtons();
|
|
|
|
|
BindGridKeys();
|
|
|
|
|
SetHelpKeyAvailability(Nav.GridSize)
|
|
|
|
|
if(GridSizeIsSupported(Nav.GridSize))
|
2021-01-25 18:09:30 +00:00
|
|
|
|
{
|
|
|
|
|
var TargetLevel = Nav.TraversalStack[Nav.TraversalStack.length - 1];
|
|
|
|
|
var ProjectsStack = EmptyTraversalStackIntoProjectsStack();
|
|
|
|
|
DeriveTraversalStack(ProjectsStack, TargetLevel);
|
|
|
|
|
}
|
2021-02-04 00:13:55 +00:00
|
|
|
|
else if(Nav.ViewType == view_type.GRID)
|
|
|
|
|
{
|
2021-04-12 16:56:30 +00:00
|
|
|
|
PickListView();
|
2021-02-04 00:13:55 +00:00
|
|
|
|
// TODO(matt): Inform user that we've switched to the list view
|
|
|
|
|
}
|
|
|
|
|
ScrollToWithOffset(Nav.Nexus, 0);
|
|
|
|
|
}
|
|
|
|
|
UpdateButtons();
|
|
|
|
|
}
|
2021-02-04 00:46:43 +00:00
|
|
|
|
|
2021-02-04 00:13:55 +00:00
|
|
|
|
function
|
|
|
|
|
InitResizeEventListener()
|
|
|
|
|
{
|
|
|
|
|
window.addEventListener("resize", function() {
|
|
|
|
|
if(CineraProps.IsMobile)
|
|
|
|
|
{
|
|
|
|
|
window.setTimeout(ResizeFunction, 512);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ResizeFunction();
|
|
|
|
|
}
|
2021-01-25 18:09:30 +00:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function
|
|
|
|
|
InitOrientationChangeListener()
|
|
|
|
|
{
|
|
|
|
|
window.onorientationchange = function()
|
|
|
|
|
{
|
|
|
|
|
if(CineraProps.IsMobile)
|
|
|
|
|
{
|
2021-02-04 00:13:55 +00:00
|
|
|
|
window.setTimeout(ResizeFunction, 512);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ResizeFunction();
|
2021-01-25 18:09:30 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
//
|
|
|
|
|
// Presenting / Navigating (Laying out and traversing the grid, and sorting)
|