937 lines
25 KiB
JavaScript
937 lines
25 KiB
JavaScript
|
|
//#region Helpers
|
|
|
|
function getAbsoluteRootPath()
|
|
{
|
|
if (typeof rootPath == 'undefined') setupRootPath(document);
|
|
return new URL(window.location.href + "/../" + rootPath).pathname;
|
|
}
|
|
|
|
function getURLPath(url = window.location.pathname)
|
|
{
|
|
let absoluteRoot = getAbsoluteRootPath();
|
|
let pathname = url.substring(absoluteRoot.length);
|
|
return pathname;
|
|
}
|
|
|
|
function getURLRootPath(url = window.location.pathname)
|
|
{
|
|
let path = getURLPath(url);
|
|
let splitPath = path.split("/");
|
|
let rootPath = "";
|
|
for (let i = 0; i < splitPath.length - 1; i++)
|
|
{
|
|
rootPath += "../";
|
|
}
|
|
return rootPath;
|
|
}
|
|
|
|
async function setTreeCollapsed(element, collapsed, animate = true)
|
|
{
|
|
if (!element || !element.classList.contains("mod-collapsible")) return;
|
|
|
|
let children = element.querySelector(".tree-item-children");
|
|
|
|
if (collapsed)
|
|
{
|
|
element.classList.add("is-collapsed");
|
|
if(animate) slideUp(children, 100);
|
|
else children.style.display = "none";
|
|
}
|
|
else
|
|
{
|
|
element.classList.remove("is-collapsed");
|
|
if(animate) slideDown(children, 100);
|
|
else children.style.removeProperty("display");
|
|
}
|
|
}
|
|
|
|
async function setTreeCollapsedAll(elements, collapsed, animate = true)
|
|
{
|
|
let childrenList = [];
|
|
elements.forEach(async element =>
|
|
{
|
|
if (!element || !element.classList.contains("mod-collapsible")) return;
|
|
|
|
let children = element.querySelector(".tree-item-children");
|
|
|
|
if (collapsed)
|
|
{
|
|
element.classList.add("is-collapsed");
|
|
}
|
|
else
|
|
{
|
|
element.classList.remove("is-collapsed");
|
|
}
|
|
|
|
childrenList.push(children);
|
|
});
|
|
|
|
if (collapsed)
|
|
{
|
|
if(animate) slideUpAll(childrenList, 100);
|
|
else childrenList.forEach(async children => children.style.display = "none");
|
|
}
|
|
else
|
|
{
|
|
if(animate) slideDownAll(childrenList, 100);
|
|
else childrenList.forEach(async children => children.style.removeProperty("display"));
|
|
}
|
|
}
|
|
|
|
function toggleTreeCollapsed(element)
|
|
{
|
|
if (!element) return;
|
|
setTreeCollapsed(element, !element.classList.contains("is-collapsed"));
|
|
}
|
|
|
|
function toggleTreeCollapsedAll(elements)
|
|
{
|
|
if (!elements) return;
|
|
setTreeCollapsedAll(elements, !elements[0].classList.contains("is-collapsed"));
|
|
}
|
|
|
|
function getHeaderEl(headerDiv)
|
|
{
|
|
let possibleChildHeader = headerDiv.firstChild;
|
|
let isHeader = false;
|
|
|
|
while (possibleChildHeader != null)
|
|
{
|
|
isHeader = possibleChildHeader ? /[Hh][1-6]/g.test(possibleChildHeader.tagName) : false;
|
|
if (isHeader) break;
|
|
|
|
possibleChildHeader = possibleChildHeader.nextElementSibling;
|
|
}
|
|
|
|
return possibleChildHeader;
|
|
}
|
|
|
|
function getPreviousHeader(headerDiv)
|
|
{
|
|
let possibleParent = headerDiv.previousElementSibling;
|
|
let isHeader = false;
|
|
|
|
while (possibleParent != null)
|
|
{
|
|
let possibleChildHeader = getHeaderEl(possibleParent);
|
|
isHeader = possibleChildHeader ? /[Hh][1-6]/g.test(possibleChildHeader.tagName) : false;
|
|
if (isHeader) break;
|
|
|
|
possibleParent = possibleParent.previousElementSibling;
|
|
}
|
|
|
|
return possibleParent;
|
|
}
|
|
|
|
function setHeaderOpen(headerDiv, open, openParents = true)
|
|
{
|
|
if(headerDiv.tagName != "DIV" || !getHeaderEl(headerDiv))
|
|
{
|
|
console.error("setHeaderOpen() must be called with a header div");
|
|
return;
|
|
}
|
|
|
|
// let selector = getHeadingContentsSelector(header);
|
|
if (open)
|
|
{
|
|
headerDiv.classList.remove("is-collapsed");
|
|
headerDiv.style.display = "";
|
|
}
|
|
if (!open)
|
|
{
|
|
headerDiv.classList.add("is-collapsed");
|
|
}
|
|
|
|
let headerEl = getHeaderEl(headerDiv);
|
|
|
|
let childHeaders = [];
|
|
|
|
let possibleChild = headerDiv.nextElementSibling;
|
|
|
|
// loop through next siblings showing/ hiding children until we reach a header of the same or lower level
|
|
while (possibleChild != null)
|
|
{
|
|
let possibleChildHeader = getHeaderEl(possibleChild);
|
|
|
|
if(possibleChildHeader)
|
|
{
|
|
// if header is a sibling of this header then break
|
|
if (possibleChildHeader.tagName <= headerEl.tagName) break;
|
|
|
|
// save child headers to be re closed afterwards
|
|
childHeaders.push(possibleChild);
|
|
}
|
|
|
|
if (!open) possibleChild.style.display = "none";
|
|
else possibleChild.style.display = "";
|
|
|
|
possibleChild = possibleChild.nextElementSibling;
|
|
}
|
|
|
|
if(open)
|
|
{
|
|
// if we are opening the header then we need to make sure that all closed child headers stay closed
|
|
childHeaders.forEach(function(item)
|
|
{
|
|
if (item.classList.contains("is-collapsed"))
|
|
{
|
|
setHeaderOpen(item, false);
|
|
}
|
|
});
|
|
|
|
// if we are opening the header then we need to make sure that all parent headers are open
|
|
if (openParents)
|
|
{
|
|
let previousHeader = getPreviousHeader(headerDiv);
|
|
|
|
while (previousHeader != null)
|
|
{
|
|
let previousHeaderEl = getHeaderEl(previousHeader);
|
|
|
|
if (previousHeaderEl.tagName < headerEl.tagName)
|
|
{
|
|
// if header is a parent of this header then unhide
|
|
setHeaderOpen(previousHeader, true);
|
|
break;
|
|
}
|
|
|
|
previousHeader = getPreviousHeader(previousHeader);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let slideUp = (target, duration=500) => {
|
|
|
|
target.style.transitionProperty = 'height, margin, padding';
|
|
target.style.transitionDuration = duration + 'ms';
|
|
target.style.boxSizing = 'border-box';
|
|
target.style.height = target.offsetHeight + 'px';
|
|
target.offsetHeight;
|
|
target.style.overflow = 'hidden';
|
|
target.style.height = 0;
|
|
target.style.paddingTop = 0;
|
|
target.style.paddingBottom = 0;
|
|
target.style.marginTop = 0;
|
|
target.style.marginBottom = 0;
|
|
window.setTimeout(async () => {
|
|
target.style.display = 'none';
|
|
target.style.removeProperty('height');
|
|
target.style.removeProperty('padding-top');
|
|
target.style.removeProperty('padding-bottom');
|
|
target.style.removeProperty('margin-top');
|
|
target.style.removeProperty('margin-bottom');
|
|
target.style.removeProperty('overflow');
|
|
target.style.removeProperty('transition-duration');
|
|
target.style.removeProperty('transition-property');
|
|
}, duration);
|
|
}
|
|
|
|
let slideUpAll = (targets, duration=500) => {
|
|
|
|
targets.forEach(async target => {
|
|
target.style.transitionProperty = 'height, margin, padding';
|
|
target.style.transitionDuration = duration + 'ms';
|
|
target.style.boxSizing = 'border-box';
|
|
target.style.height = target.offsetHeight + 'px';
|
|
target.offsetHeight;
|
|
target.style.overflow = 'hidden';
|
|
target.style.height = 0;
|
|
target.style.paddingTop = 0;
|
|
target.style.paddingBottom = 0;
|
|
target.style.marginTop = 0;
|
|
target.style.marginBottom = 0;
|
|
});
|
|
|
|
window.setTimeout(async () => {
|
|
targets.forEach(async target => {
|
|
target.style.display = 'none';
|
|
target.style.removeProperty('height');
|
|
target.style.removeProperty('padding-top');
|
|
target.style.removeProperty('padding-bottom');
|
|
target.style.removeProperty('margin-top');
|
|
target.style.removeProperty('margin-bottom');
|
|
target.style.removeProperty('overflow');
|
|
target.style.removeProperty('transition-duration');
|
|
target.style.removeProperty('transition-property');
|
|
});
|
|
}, duration);
|
|
}
|
|
|
|
let slideDown = (target, duration=500) => {
|
|
|
|
target.style.removeProperty('display');
|
|
let display = window.getComputedStyle(target).display;
|
|
if (display === 'none') display = 'block';
|
|
target.style.display = display;
|
|
let height = target.offsetHeight;
|
|
target.style.overflow = 'hidden';
|
|
target.style.height = 0;
|
|
target.style.paddingTop = 0;
|
|
target.style.paddingBottom = 0;
|
|
target.style.marginTop = 0;
|
|
target.style.marginBottom = 0;
|
|
target.offsetHeight;
|
|
target.style.boxSizing = 'border-box';
|
|
target.style.transitionProperty = "height, margin, padding";
|
|
target.style.transitionDuration = duration + 'ms';
|
|
target.style.height = height + 'px';
|
|
target.style.removeProperty('padding-top');
|
|
target.style.removeProperty('padding-bottom');
|
|
target.style.removeProperty('margin-top');
|
|
target.style.removeProperty('margin-bottom');
|
|
window.setTimeout(async () => {
|
|
target.style.removeProperty('height');
|
|
target.style.removeProperty('overflow');
|
|
target.style.removeProperty('transition-duration');
|
|
target.style.removeProperty('transition-property');
|
|
}, duration);
|
|
}
|
|
|
|
let slideDownAll = (targets, duration=500) => {
|
|
|
|
targets.forEach(async target => {
|
|
target.style.removeProperty('display');
|
|
let display = window.getComputedStyle(target).display;
|
|
if (display === 'none') display = 'block';
|
|
target.style.display = display;
|
|
let height = target.offsetHeight;
|
|
target.style.overflow = 'hidden';
|
|
target.style.height = 0;
|
|
target.style.paddingTop = 0;
|
|
target.style.paddingBottom = 0;
|
|
target.style.marginTop = 0;
|
|
target.style.marginBottom = 0;
|
|
target.offsetHeight;
|
|
target.style.boxSizing = 'border-box';
|
|
target.style.transitionProperty = "height, margin, padding";
|
|
target.style.transitionDuration = duration + 'ms';
|
|
target.style.height = height + 'px';
|
|
target.style.removeProperty('padding-top');
|
|
target.style.removeProperty('padding-bottom');
|
|
target.style.removeProperty('margin-top');
|
|
target.style.removeProperty('margin-bottom');
|
|
});
|
|
|
|
window.setTimeout( async () => {
|
|
targets.forEach(async target => {
|
|
target.style.removeProperty('height');
|
|
target.style.removeProperty('overflow');
|
|
target.style.removeProperty('transition-duration');
|
|
target.style.removeProperty('transition-property');
|
|
});
|
|
}, duration);
|
|
}
|
|
|
|
var slideToggle = (target, duration = 500) => {
|
|
if (window.getComputedStyle(target).display === 'none') {
|
|
return slideDown(target, duration);
|
|
} else {
|
|
return slideUp(target, duration);
|
|
}
|
|
}
|
|
|
|
var slideToggleAll = (targets, duration = 500) => {
|
|
if (window.getComputedStyle(targets[0]).display === 'none') {
|
|
return slideDownAll(targets, duration);
|
|
} else {
|
|
return slideUpAll(targets, duration);
|
|
}
|
|
}
|
|
|
|
//#endregion
|
|
|
|
async function loadDocument(url, pushHistory = true, scrollTo = true)
|
|
{
|
|
console.log("Loading document: " + url);
|
|
|
|
// change the active file
|
|
setActiveDocument(url, scrollTo, pushHistory);
|
|
|
|
let response;
|
|
|
|
// if(typeof embeddedDocuments == 'undefined')
|
|
// {
|
|
try
|
|
{
|
|
response = await fetch(url);
|
|
}
|
|
catch (error)
|
|
{
|
|
console.log("Cannot use fetch API (likely due to CORS), just loading the page normally.");
|
|
window.location.assign(url);
|
|
return;
|
|
}
|
|
// }
|
|
// else
|
|
// {
|
|
// response = new Response(embeddedDocuments[url], {status: 200, statusText: "OK"});
|
|
// }
|
|
|
|
let doc = document.implementation.createHTMLDocument();
|
|
|
|
if (response.ok)
|
|
{
|
|
let html = (await response.text()).replaceAll("<!DOCTYPE html>", "").replaceAll("<html>", "").replaceAll("</html>", "");
|
|
doc.documentElement.innerHTML = html;
|
|
|
|
// copy document content and outline tree
|
|
document.querySelector(".document-container").innerHTML = doc.querySelector(".document-container").innerHTML;
|
|
document.querySelector(".outline-tree").innerHTML = doc.querySelector(".outline-tree").innerHTML;
|
|
|
|
// if the url has a heading, scroll to it
|
|
let splitURL = url.split("#");
|
|
let pathnameTarget = splitURL[0] ?? url;
|
|
let headingTarget = splitURL.length > 1 ? splitURL[1] : null;
|
|
if (headingTarget) document.getElementById(headingTarget).scrollIntoView();
|
|
|
|
// Change the root path to match the match from the new page
|
|
setupRootPath(doc);
|
|
|
|
// initialize events on the new page
|
|
initializePage(document.querySelector(".document-container"));
|
|
initializePage(document.querySelector(".outline-tree"));
|
|
|
|
document.title = doc.title;
|
|
}
|
|
else
|
|
{
|
|
// if the page is not able to load instead add a header saying the page doesn't exist
|
|
document.querySelector(".markdown-preview-view").innerHTML =
|
|
`
|
|
<div>
|
|
<center style='position: relative; transform: translateY(20vh); width: 100%; text-align: center;'>
|
|
<h1 style>Page Not Found</h1>
|
|
</center>
|
|
</div>
|
|
`;
|
|
|
|
document.querySelector(".outline-tree").innerHTML = "";
|
|
|
|
console.log("Page not found: " + getAbsoluteRootPath() + url);
|
|
let newRootPath = getURLRootPath(getAbsoluteRootPath() + url);
|
|
rootPath = newRootPath;
|
|
document.querySelector("base").href = newRootPath;
|
|
|
|
document.title = "Page Not Found";
|
|
}
|
|
|
|
return doc;
|
|
}
|
|
|
|
function setActiveDocument(url, scrollTo = true, pushHistory = true)
|
|
{
|
|
let pathnameTarget = url.split("#")[0] ?? url; // path with no header
|
|
|
|
// switch active file in file tree
|
|
document.querySelector(".tree-item.mod-active")?.classList.remove("mod-active");
|
|
let treeItems = Array.from(document.querySelectorAll(".tree-item > .tree-item-contents > .tree-item-link"));
|
|
let treeItem = undefined;
|
|
for (let item of treeItems)
|
|
{
|
|
if (item.getAttribute("href") == url)
|
|
{
|
|
let parent = item.parentElement.parentElement;
|
|
|
|
parent.classList.add("mod-active");
|
|
treeItem = parent;
|
|
|
|
while (parent.hasAttribute("data-depth"))
|
|
{
|
|
setTreeCollapsed(parent, false, false);
|
|
parent = parent.parentElement.parentElement;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if(scrollTo) treeItem?.scrollIntoView({block: "center", inline: "nearest"});
|
|
|
|
// set the active file in th graph view
|
|
if(typeof nodes != 'undefined' && window.renderWorker)
|
|
{
|
|
let activeNode = nodes?.paths.findIndex(function(item) { return item.endsWith(pathnameTarget); }) ?? -1;
|
|
|
|
if(activeNode >= 0)
|
|
{
|
|
window.renderWorker.activeNode = activeNode;
|
|
}
|
|
}
|
|
|
|
if(pushHistory && window.location.protocol != "file:") window.history.pushState({ path: pathnameTarget }, '', pathnameTarget);
|
|
}
|
|
|
|
//#region Initialization
|
|
function setupThemeToggle(setupOnNode)
|
|
{
|
|
if (localStorage.getItem("theme_toggle") != null)
|
|
{
|
|
setThemeToggle(localStorage.getItem("theme_toggle") == "true");
|
|
}
|
|
|
|
var lastScheme = "theme-dark";
|
|
|
|
// change theme to match current system theme
|
|
if (localStorage.getItem("theme_toggle") == null && window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)
|
|
{
|
|
setThemeToggle(true);
|
|
lastScheme = "theme-dark";
|
|
}
|
|
if (localStorage.getItem("theme_toggle") == null && window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches)
|
|
{
|
|
setThemeToggle(true);
|
|
lastScheme = "theme-light";
|
|
}
|
|
|
|
// set initial toggle state based on body theme class
|
|
if (document.body.classList.contains("theme-light"))
|
|
{
|
|
setThemeToggle(false);
|
|
}
|
|
else
|
|
{
|
|
setThemeToggle(true);
|
|
}
|
|
|
|
function setThemeToggle(state, instant = false)
|
|
{
|
|
let toggle = document.querySelector(".theme-toggle-input");
|
|
|
|
toggle.checked = state;
|
|
|
|
if (instant)
|
|
{
|
|
var oldTransition = document.body.style.transition;
|
|
document.body.style.transition = "none";
|
|
}
|
|
|
|
if(!toggle.classList.contains("is-checked") && state)
|
|
{
|
|
toggle.classList.add("is-checked");
|
|
}
|
|
else if (toggle.classList.contains("is-checked") && !state)
|
|
{
|
|
toggle.classList.remove("is-checked");
|
|
}
|
|
|
|
if(!state)
|
|
{
|
|
if (document.body.classList.contains("theme-dark"))
|
|
{
|
|
document.body.classList.remove("theme-dark");
|
|
}
|
|
|
|
if (!document.body.classList.contains("theme-light"))
|
|
{
|
|
document.body.classList.add("theme-light");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (document.body.classList.contains("theme-light"))
|
|
{
|
|
document.body.classList.remove("theme-light");
|
|
}
|
|
|
|
if (!document.body.classList.contains("theme-dark"))
|
|
{
|
|
document.body.classList.add("theme-dark");
|
|
}
|
|
}
|
|
|
|
if (instant)
|
|
{
|
|
setTimeout(function()
|
|
{
|
|
document.body.style.transition = oldTransition;
|
|
}, 100);
|
|
}
|
|
|
|
localStorage.setItem("theme_toggle", state ? "true" : "false");
|
|
}
|
|
|
|
setupOnNode.querySelector(".theme-toggle-input")?.addEventListener("change", event =>
|
|
{
|
|
setThemeToggle(!(localStorage.getItem("theme_toggle") == "true"));
|
|
});
|
|
|
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event =>
|
|
{
|
|
// return if we are printing
|
|
if (window.matchMedia('print').matches)
|
|
{
|
|
printing = true;
|
|
return;
|
|
}
|
|
|
|
let newColorScheme = event.matches ? "theme-dark" : "theme-light";
|
|
|
|
if (newColorScheme == lastScheme) return;
|
|
|
|
if (newColorScheme == "theme-dark")
|
|
{
|
|
setThemeToggle(true);
|
|
}
|
|
|
|
if (newColorScheme == "theme-light")
|
|
{
|
|
setThemeToggle(false);
|
|
}
|
|
|
|
lastScheme = newColorScheme;
|
|
});
|
|
|
|
}
|
|
|
|
function setupHeaders(setupOnNode)
|
|
{
|
|
// MAKE HEADERS COLLAPSIBLE
|
|
setupOnNode.querySelectorAll(".heading-collapse-indicator").forEach(function (element)
|
|
{
|
|
element.addEventListener("click", function ()
|
|
{
|
|
var isOpen = !this.parentElement.parentElement.classList.contains("is-collapsed");
|
|
setHeaderOpen(this.parentElement.parentElement, !isOpen);
|
|
});
|
|
});
|
|
|
|
// unfold header when an internal link that points to that header is clicked
|
|
setupOnNode.querySelectorAll("a.internal-link, a.tree-item-link").forEach(function (element)
|
|
{
|
|
element.addEventListener("click", function (event)
|
|
{
|
|
event.preventDefault();
|
|
let target = this.getAttribute("href");
|
|
|
|
// if the target is a header uncollapse it
|
|
if (target.startsWith("#"))
|
|
{
|
|
console.log("Uncollapsing header: " + target);
|
|
let header = document.getElementById(target.substring(1));
|
|
setHeaderOpen(header.parentElement, true);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function setupTrees(setupOnNode)
|
|
{
|
|
const fileTreeItems = Array.from(setupOnNode.querySelectorAll(".tree-container.file-tree .tree-item"));
|
|
|
|
setupOnNode.querySelectorAll(".tree-item-contents > .collapse-icon").forEach(function(item)
|
|
{
|
|
item.addEventListener("click", function()
|
|
{
|
|
toggleTreeCollapsed(item.parentElement.parentElement);
|
|
});
|
|
});
|
|
|
|
let fileTreeCollapse = setupOnNode.querySelector(".tree-container.file-tree .collapse-tree-button");
|
|
if (fileTreeCollapse) fileTreeCollapse.addEventListener("click", async function()
|
|
{
|
|
let fileTreeIsCollapsed = fileTreeCollapse.classList.contains("is-collapsed");
|
|
|
|
setTreeCollapsedAll(fileTreeItems, !fileTreeIsCollapsed, fileTreeItems.length < 100);
|
|
|
|
fileTreeCollapse.classList.toggle("is-collapsed");
|
|
fileTreeCollapse.querySelector("iconify-icon").setAttribute("icon", fileTreeIsCollapsed ? "ph:arrows-out-line-horizontal-bold" : "ph:arrows-in-line-horizontal-bold");
|
|
});
|
|
|
|
|
|
let outlineTreeCollapse = setupOnNode.querySelector(".tree-container.outline-tree .collapse-tree-button");
|
|
if(outlineTreeCollapse) outlineTreeCollapse.addEventListener("click", async function()
|
|
{
|
|
let outlineTreeIsCollapsed = outlineTreeCollapse.classList.contains("is-collapsed");
|
|
|
|
let items = Array.from(outlineTreeCollapse.parentElement.parentElement.querySelectorAll(".tree-item"));
|
|
setTreeCollapsedAll(items, !outlineTreeIsCollapsed, items.length < 100);
|
|
|
|
outlineTreeCollapse.classList.toggle("is-collapsed");
|
|
outlineTreeCollapse.querySelector("iconify-icon").setAttribute("icon", outlineTreeIsCollapsed ? "ph:arrows-out-line-horizontal-bold" : "ph:arrows-in-line-horizontal-bold");
|
|
});
|
|
|
|
// start with all closed
|
|
setupOnNode.querySelectorAll(".tree-container .tree-item").forEach(function(item)
|
|
{
|
|
if (item.classList.contains("is-collapsed")) setTreeCollapsed(item, true, false);
|
|
});
|
|
|
|
// make sure the icons match their starting collaped state
|
|
setupOnNode.querySelectorAll(".tree-container > .tree-header > .collapse-tree-button").forEach(function(item)
|
|
{
|
|
if (item.classList.contains("is-collapsed"))
|
|
{
|
|
item.querySelector("iconify-icon").setAttribute("icon", "ph:arrows-out-line-horizontal-bold");
|
|
}
|
|
else
|
|
{
|
|
item.querySelector("iconify-icon").setAttribute("icon", "ph:arrows-in-line-horizontal-bold");
|
|
}
|
|
});
|
|
}
|
|
|
|
function setupCallouts(setupOnNode)
|
|
{
|
|
// MAKE CALLOUTS COLLAPSIBLE
|
|
// if the callout title is clicked, toggle the display of .callout-content
|
|
setupOnNode.querySelectorAll(".callout.is-collapsible .callout-title").forEach(function (element)
|
|
{
|
|
element.addEventListener("click", function ()
|
|
{
|
|
var parent = this.parentElement;
|
|
var isCollapsed = parent.classList.contains("is-collapsed");
|
|
|
|
parent.classList.toggle("is-collapsed");
|
|
element.querySelector(".callout-fold").classList.toggle("is-collapsed");
|
|
|
|
slideToggle(parent.querySelector(".callout-content"), 100);
|
|
});
|
|
});
|
|
|
|
}
|
|
|
|
function setupCheckboxes(setupOnNode)
|
|
{
|
|
// Fix checkboxed toggling .is-checked
|
|
setupOnNode.querySelectorAll(".task-list-item-checkbox").forEach(function (element)
|
|
{
|
|
element.addEventListener("click", function ()
|
|
{
|
|
var parent = this.parentElement;
|
|
parent.classList.toggle("is-checked");
|
|
parent.setAttribute("data-task", parent.classList.contains("is-checked") ? "x" : " ");
|
|
});
|
|
});
|
|
|
|
setupOnNode.querySelectorAll(`.plugin-tasks-list-item input[type="checkbox"]`).forEach(function(checkbox)
|
|
{
|
|
checkbox.checked = checkbox.parentElement.classList.contains("is-checked");
|
|
});
|
|
|
|
setupOnNode.querySelectorAll('.kanban-plugin__item.is-complete').forEach(function(checkbox)
|
|
{
|
|
checkbox.querySelector('input[type="checkbox"]').checked = true;
|
|
});
|
|
}
|
|
|
|
function setupCanvas(setupOnNode)
|
|
{
|
|
let focusedNode = null;
|
|
|
|
// make canvas nodes selectable
|
|
setupOnNode.querySelectorAll(".canvas-node-content-blocker").forEach(function (element)
|
|
{
|
|
element.addEventListener("click", function ()
|
|
{
|
|
var parent = this.parentElement.parentElement;
|
|
parent.classList.toggle("is-focused");
|
|
this.style.display = "none";
|
|
|
|
if (focusedNode)
|
|
{
|
|
focusedNode.classList.remove("is-focused");
|
|
focusedNode.querySelector(".canvas-node-content-blocker").style.display = "";
|
|
}
|
|
|
|
focusedNode = parent;
|
|
});
|
|
});
|
|
|
|
// make canvas node deselect when clicking outside
|
|
// document.addEventListener("click", function (event)
|
|
// {
|
|
// if (!event.target.closest(".canvas-node"))
|
|
// {
|
|
// document.querySelectorAll(".canvas-node").forEach(function (node)
|
|
// {
|
|
// node.classList.remove("is-focused");
|
|
// node.querySelector(".canvas-node-content-blocker").style.display = "";
|
|
// });
|
|
// }
|
|
// });
|
|
|
|
}
|
|
|
|
function setupCodeblocks(setupOnNode)
|
|
{
|
|
// make code snippet block copy button copy the code to the clipboard
|
|
setupOnNode.querySelectorAll(".copy-code-button").forEach(function (element)
|
|
{
|
|
element.addEventListener("click", function ()
|
|
{
|
|
var code = this.parentElement.querySelector("code").textContent;
|
|
navigator.clipboard.writeText(code);
|
|
this.textContent = "Copied!";
|
|
// set a timeout to change the text back
|
|
setTimeout(function ()
|
|
{
|
|
setupOnNode.querySelectorAll(".copy-code-button").forEach(function (button)
|
|
{
|
|
button.textContent = "Copy";
|
|
});
|
|
}, 2000);
|
|
});
|
|
});
|
|
}
|
|
|
|
function setupLinks(setupOnNode)
|
|
{
|
|
setupOnNode.querySelectorAll(".internal-link, .footnote-link, .tree-item-link").forEach(function(link)
|
|
{
|
|
link.addEventListener("click", function(event)
|
|
{
|
|
let target = link.getAttribute("href");
|
|
event.preventDefault();
|
|
|
|
// this is linking to a different page
|
|
if (!target.startsWith("#"))
|
|
{
|
|
// load doc, if it is a tree link then don't scroll to the active doc in the file tree
|
|
loadDocument(target, true, !link.classList.contains("tree-item-link"));
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
let headerTarget = document.getElementById(target.substring(1));
|
|
setHeaderOpen(headerTarget.parentElement, true);
|
|
headerTarget.scrollIntoView();
|
|
}
|
|
});
|
|
});
|
|
|
|
window.onpopstate = function(event)
|
|
{
|
|
loadDocument(getURLPath(), false);
|
|
}
|
|
}
|
|
|
|
|
|
// What the FUCK is this abomination??? - Endermanch, 2023
|
|
/* let sidebarWidth = undefined;
|
|
let lineWidth = undefined;
|
|
function setupResize(setupOnNode)
|
|
{
|
|
if (setupOnNode != document) return;
|
|
|
|
function updateSidebars()
|
|
{
|
|
let rightSidebar = document.querySelector(".sidebar-right");
|
|
let leftSidebar = document.querySelector(".sidebar-left");
|
|
let sidebarCount = (rightSidebar ? 1 : 0) + (leftSidebar ? 1 : 0);
|
|
|
|
if (sidebarCount == 0) return;
|
|
|
|
if(!sidebarWidth) sidebarWidth = Math.max(rightSidebar?.clientWidth, leftSidebar?.clientWidth);
|
|
|
|
if (!lineWidth)
|
|
{
|
|
let docWidthTestEl = document.createElement("div");
|
|
document.querySelector(".markdown-preview-view").appendChild(docWidthTestEl);
|
|
docWidthTestEl.style.width = "var(--line-width)";
|
|
docWidthTestEl.style.minWidth = "var(--line-width)";
|
|
docWidthTestEl.style.maxWidth = "var(--line-width)";
|
|
lineWidth = docWidthTestEl.clientWidth;
|
|
docWidthTestEl.remove();
|
|
}
|
|
|
|
let letHideRightThreshold = sidebarWidth * sidebarCount + lineWidth / 2;
|
|
|
|
if (window.innerWidth < letHideRightThreshold)
|
|
{
|
|
rightSidebar.style.display = "none";
|
|
}
|
|
else
|
|
{
|
|
rightSidebar.style.display = "";
|
|
}
|
|
|
|
let letHideLeftThreshold = lineWidth / 2 + sidebarWidth;
|
|
|
|
if (window.innerWidth < letHideLeftThreshold)
|
|
{
|
|
leftSidebar.style.display = "none";
|
|
}
|
|
else
|
|
{
|
|
leftSidebar.style.display = "";
|
|
}
|
|
}
|
|
|
|
window.addEventListener("resize", function()
|
|
{
|
|
updateSidebars();
|
|
});
|
|
|
|
updateSidebars();
|
|
}
|
|
*/
|
|
|
|
function setupRootPath(fromDocument)
|
|
{
|
|
let basePath = fromDocument.querySelector("#root-path").getAttribute("root-path");
|
|
document.querySelector("base").href = basePath;
|
|
document.querySelector("#root-path").setAttribute("root-path", basePath);
|
|
rootPath = basePath;
|
|
}
|
|
|
|
let touchDrag = false;
|
|
|
|
function initializePage(setupOnNode)
|
|
{
|
|
setupThemeToggle(setupOnNode);
|
|
setupHeaders(setupOnNode);
|
|
setupTrees(setupOnNode);
|
|
setupCallouts(setupOnNode);
|
|
setupCheckboxes(setupOnNode);
|
|
setupCanvas(setupOnNode);
|
|
setupCodeblocks(setupOnNode);
|
|
setupLinks(setupOnNode);
|
|
|
|
setupOnNode.querySelectorAll("*").forEach(function(element)
|
|
{
|
|
element.addEventListener("touchend", function(event)
|
|
{
|
|
if (touchDrag)
|
|
{
|
|
touchDrag = false;
|
|
event.stopPropagation();
|
|
return;
|
|
}
|
|
|
|
if (element instanceof HTMLElement) element.click();
|
|
});
|
|
});
|
|
|
|
if(setupOnNode == document)
|
|
{
|
|
document.body.addEventListener("touchmove", function(event)
|
|
{
|
|
event.stopImmediatePropagation();
|
|
touchDrag = true;
|
|
});
|
|
|
|
setupRootPath(document);
|
|
setActiveDocument(getURLPath());
|
|
}
|
|
}
|
|
|
|
function initializeForFileProtocol()
|
|
{
|
|
let graphEl = document.querySelector(".graph-view-placeholder");
|
|
if(graphEl)
|
|
{
|
|
console.log("Running locally, skipping graph view initialization and hiding graph.");
|
|
graphEl.style.display = "none";
|
|
graphEl.previousElementSibling.style.display = "none"; // hide the graph's header
|
|
}
|
|
}
|
|
|
|
//#endregion
|
|
|
|
window.onload = function()
|
|
{
|
|
if (window.location.protocol == "file:") initializeForFileProtocol();
|
|
initializePage(document);
|
|
} |