mirror of
https://gitlab.com/vyla-entertainment/documentation.git
synced 2026-11-09 20:17:20 -05:00
1168 lines
39 KiB
JavaScript
1168 lines
39 KiB
JavaScript
var THEME_KEY = "vyla-theme";
|
||
var ICONS = {
|
||
system: '<rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/>',
|
||
light: '<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M6.3 17.7l-1.4 1.4M19.1 4.9l-1.4 1.4"/>',
|
||
dark: '<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>',
|
||
search: '<circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/>',
|
||
menu: '<path d="M4 6h16M4 12h10M4 18h16"/>',
|
||
doc: '<path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z"/><path d="M14 3v5h5M9 13h6M9 17h6"/>',
|
||
hash: '<path d="M4 9h16M4 15h16M10 3 8 21M16 3l-2 18"/>',
|
||
copy: '<rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V6a2 2 0 0 1 2-2h9"/>',
|
||
check: '<path d="m5 12 5 5 9-10"/>',
|
||
chevron: '<path d="m6 9 6 6 6-6"/>',
|
||
chevronLeft: '<path d="m15 18-6-6 6-6"/>',
|
||
chevronRight: '<path d="m9 18 6-6-6-6"/>'
|
||
};
|
||
|
||
var SITE_TREE = [
|
||
{
|
||
id: "vyla",
|
||
title: "Vyla",
|
||
path: "/start-here/introduction",
|
||
groups: [
|
||
{
|
||
title: "Start here",
|
||
items: [
|
||
{ title: "Introduction", file: "/start-here/introduction.md", path: "/start-here/introduction" },
|
||
{ title: "Architecture", file: "/start-here/architecture.md", path: "/start-here/architecture" }
|
||
]
|
||
},
|
||
{
|
||
title: "Products",
|
||
items: [
|
||
{ title: "Home", file: "/products/home.md", path: "/products/home" },
|
||
{ title: "Vyla Go", file: "/products/go.md", path: "/products/go" },
|
||
{ title: "Tv", file: "/products/tv.md", path: "/products/tv" },
|
||
{ title: "Player", file: "/products/player.md", path: "/products/player" }
|
||
]
|
||
},
|
||
{
|
||
title: "Concepts",
|
||
items: [
|
||
{ title: "Providers", file: "/concepts/providers.md", path: "/concepts/providers" }
|
||
]
|
||
}
|
||
]
|
||
},
|
||
{
|
||
id: "sdk",
|
||
title: "SDK",
|
||
path: "/sdk",
|
||
groups: [
|
||
{
|
||
title: "Vyla SDK",
|
||
items: [
|
||
{ title: "Sdk", file: "/sdk/vyla-sdk/index.md", path: "/sdk" },
|
||
{ title: "Sdk Quickstart", file: "/sdk/vyla-sdk/quickstart.md", path: "/sdk/vyla-sdk/quickstart" }
|
||
]
|
||
}
|
||
]
|
||
},
|
||
{
|
||
id: "stream-api",
|
||
title: "Stream API",
|
||
path: "/stream-api/overview",
|
||
groups: [
|
||
{
|
||
title: "Overview",
|
||
items: [
|
||
{ title: "Overview", file: "/stream-api/overview/index.md", path: "/stream-api/overview" }
|
||
]
|
||
},
|
||
{
|
||
title: "Reference",
|
||
items: [
|
||
{ title: "Paths", file: "/stream-api/reference/paths.md", path: "/stream-api/reference/paths" },
|
||
{ title: "Media Data", file: "/stream-api/reference/media-data.md", path: "/stream-api/reference/media-data" },
|
||
{ title: "Operations", file: "/stream-api/reference/operations.md", path: "/stream-api/reference/operations" }
|
||
]
|
||
},
|
||
{
|
||
title: "Deployment",
|
||
items: [
|
||
{ title: "Self Hosting", file: "/stream-api/deployment/self-hosting.md", path: "/stream-api/deployment/self-hosting" }
|
||
]
|
||
}
|
||
]
|
||
},
|
||
{
|
||
id: "misc",
|
||
title: "Misc",
|
||
path: "/misc/project/credit",
|
||
groups: [
|
||
{
|
||
title: "Project",
|
||
items: [
|
||
{ title: "Credit", file: "/misc/project/credit.md", path: "/misc/project/credit" },
|
||
{ title: "DMCA", file: "/misc/project/DMCA.md", path: "/misc/project/DMCA" }
|
||
]
|
||
}
|
||
]
|
||
}
|
||
];
|
||
|
||
var searchData = null;
|
||
var searchResults = [];
|
||
var searchActive = 0;
|
||
|
||
function icon(name) {
|
||
return '<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">' + ICONS[name] + '</svg>';
|
||
}
|
||
|
||
function esc(s) {
|
||
return String(s).replace(/[&<>"]/g, function (c) {
|
||
return { "&": "&", "<": "<", ">": ">", '"': """ }[c];
|
||
});
|
||
}
|
||
|
||
function getThemeMode() {
|
||
var v = localStorage.getItem(THEME_KEY);
|
||
return v === "light" || v === "dark" ? v : "system";
|
||
}
|
||
|
||
function applyTheme() {
|
||
var mode = getThemeMode();
|
||
var dark = mode === "dark" || (mode === "system" && window.matchMedia("(prefers-color-scheme: dark)").matches);
|
||
document.documentElement.classList.toggle("dark", dark);
|
||
var logo = document.getElementById("brand-logo");
|
||
if (logo) logo.src = dark ? "/assets/images/logo.png" : "/assets/images/dark_logo.png";
|
||
}
|
||
|
||
function setTheme(mode) {
|
||
if (mode === "system") localStorage.removeItem(THEME_KEY);
|
||
else localStorage.setItem(THEME_KEY, mode);
|
||
applyTheme();
|
||
refreshThemeUI();
|
||
var menu = document.getElementById("theme-menu");
|
||
if (menu) menu.classList.remove("open");
|
||
}
|
||
|
||
function toggleThemeMenu(e) {
|
||
e.stopPropagation();
|
||
var menu = document.getElementById("theme-menu");
|
||
if (menu) menu.classList.toggle("open");
|
||
}
|
||
|
||
function refreshThemeUI() {
|
||
var mode = getThemeMode();
|
||
var btn = document.getElementById("theme-toggle");
|
||
if (btn) btn.innerHTML = icon(mode);
|
||
document.querySelectorAll("#theme-menu button, #theme-seg button").forEach(function (b) {
|
||
b.classList.toggle("on", b.getAttribute("data-mode") === mode);
|
||
});
|
||
}
|
||
|
||
function buildThemeControls() {
|
||
var btn = document.getElementById("theme-toggle");
|
||
var links = document.querySelector("#navbar .links");
|
||
if (!btn || !links) return;
|
||
var wrap = document.createElement("div");
|
||
wrap.className = "theme-wrap";
|
||
btn.parentNode.insertBefore(wrap, btn);
|
||
wrap.appendChild(btn);
|
||
var modes = ["system", "light", "dark"];
|
||
var menu = document.createElement("div");
|
||
menu.id = "theme-menu";
|
||
menu.innerHTML = modes.map(function (m) {
|
||
return '<button type="button" data-mode="' + m + '">' + icon(m) + '<span>' + m.charAt(0).toUpperCase() + m.slice(1) + '</span><i class="check">✓</i></button>';
|
||
}).join("");
|
||
wrap.appendChild(menu);
|
||
menu.addEventListener("click", function (e) {
|
||
var b = e.target.closest("button");
|
||
if (b) setTheme(b.getAttribute("data-mode"));
|
||
});
|
||
refreshThemeUI();
|
||
}
|
||
|
||
function dismissBanner() {
|
||
var banner = document.getElementById("banner");
|
||
if (banner) banner.setAttribute("data-dismissed", "true");
|
||
document.body.classList.add("banner-dismissed");
|
||
localStorage.setItem("vyla-banner-dismissed", "true");
|
||
var header = document.getElementById("header-container");
|
||
if (header) document.documentElement.style.setProperty("--header-height", header.offsetHeight + "px");
|
||
}
|
||
|
||
function toggleSidebar() {
|
||
var sidebar = document.getElementById("sidebar");
|
||
var nav = document.getElementById("navbar");
|
||
var top = Math.max(0, Math.round(nav.getBoundingClientRect().bottom));
|
||
sidebar.style.setProperty("--sidebar-top", top + "px");
|
||
var open = sidebar.classList.toggle("open");
|
||
document.body.classList.toggle("nav-open", open);
|
||
}
|
||
|
||
function cgSwitch(btn) {
|
||
var group = btn.closest(".code-group");
|
||
var index = btn.getAttribute("data-tab-index");
|
||
group.querySelectorAll(".code-group-tab").forEach(function (t) {
|
||
t.classList.toggle("active", t === btn);
|
||
});
|
||
group.querySelectorAll(".code-group-panel").forEach(function (p, i) {
|
||
p.classList.toggle("active", String(i) === index);
|
||
});
|
||
window.dispatchEvent(new Event("resize"));
|
||
}
|
||
|
||
function parseMarkdown(md) {
|
||
var lines = md.replace(/\r\n/g, "\n").split("\n");
|
||
var out = [];
|
||
var inPre = false;
|
||
var preLang = "";
|
||
var preCode = [];
|
||
var inTable = false;
|
||
var tableRows = [];
|
||
var inList = false;
|
||
var listType = "ul";
|
||
var inCodeGroup = false;
|
||
var codeGroupTabs = [];
|
||
var codeGroupPanels = [];
|
||
|
||
function flushList() {
|
||
if (inList) {
|
||
out.push("</" + listType + ">");
|
||
inList = false;
|
||
}
|
||
}
|
||
|
||
function flushTable() {
|
||
if (inTable) {
|
||
var html = '<div class="table-wrap"><table>';
|
||
if (tableRows.length > 0) {
|
||
html += "<thead><tr>";
|
||
var ths = tableRows[0];
|
||
for (var i = 0; i < ths.length; i++) html += "<th>" + parseInline(ths[i]) + "</th>";
|
||
html += "</tr></thead>";
|
||
}
|
||
if (tableRows.length > 1) {
|
||
html += "<tbody>";
|
||
for (var r = 1; r < tableRows.length; r++) {
|
||
html += "<tr>";
|
||
var tds = tableRows[r];
|
||
for (var c = 0; c < tds.length; c++) html += "<td>" + parseInline(tds[c]) + "</td>";
|
||
html += "</tr>";
|
||
}
|
||
html += "</tbody>";
|
||
}
|
||
html += "</table></div>";
|
||
out.push(html);
|
||
inTable = false;
|
||
tableRows = [];
|
||
}
|
||
}
|
||
|
||
function slugify(text) {
|
||
return text.toLowerCase().replace(/<[^>]+>/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
||
}
|
||
|
||
function parseInline(text) {
|
||
return text
|
||
.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, function (_, alt, src) {
|
||
var cls = src.indexOf("devices.png") !== -1 || src.indexOf("site_preview.png") !== -1 || src.indexOf("site-preview.png") !== -1 ? ' class="vyla-banner"' : "";
|
||
return '<img src="' + src + '" alt="' + esc(alt) + '" loading="lazy" decoding="async" fetchpriority="low"' + cls + " />";
|
||
})
|
||
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, function (_, label, href) {
|
||
return '<a href="' + href + '">' + label + "</a>";
|
||
})
|
||
.replace(/`([^`]+)`/g, function (_, code) {
|
||
return "<code>" + esc(code) + "</code>";
|
||
})
|
||
.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
|
||
.replace(/\*([^*]+)\*/g, "<em>$1</em>");
|
||
}
|
||
|
||
for (var i = 0; i < lines.length; i++) {
|
||
var line = lines[i];
|
||
|
||
if (line.startsWith(":::code-group")) {
|
||
flushList();
|
||
flushTable();
|
||
inCodeGroup = true;
|
||
codeGroupTabs = [];
|
||
codeGroupPanels = [];
|
||
continue;
|
||
}
|
||
|
||
if (inCodeGroup && line === ":::") {
|
||
flushList();
|
||
flushTable();
|
||
var cgHtml = '<div class="code-group"><div class="code-group-tabs">';
|
||
codeGroupTabs.forEach(function (tab, idx) {
|
||
var activeClass = idx === 0 ? " active" : "";
|
||
cgHtml += '<button class="code-group-tab' + activeClass + '" data-tab-index="' + idx + '" type="button" onclick="cgSwitch(this)">' + esc(tab) + '</button>';
|
||
});
|
||
cgHtml += '</div><div class="code-group-panels">';
|
||
codeGroupPanels.forEach(function (panel, idx) {
|
||
var activeClass = idx === 0 ? " active" : "";
|
||
var langClass = panel.lang ? ' class="language-' + panel.lang + '"' : ' class="language-none"';
|
||
cgHtml += '<div class="code-group-panel' + activeClass + '"><pre><code' + langClass + '>' + esc(panel.code) + '</code></pre></div>';
|
||
});
|
||
cgHtml += '</div></div>';
|
||
out.push(cgHtml);
|
||
inCodeGroup = false;
|
||
codeGroupTabs = [];
|
||
codeGroupPanels = [];
|
||
continue;
|
||
}
|
||
|
||
if (line.startsWith("```")) {
|
||
flushList();
|
||
flushTable();
|
||
if (!inPre) {
|
||
inPre = true;
|
||
var info = line.slice(3).trim();
|
||
var tabMatch = info.match(/\[([^\]]+)\]/);
|
||
var tabTitle = tabMatch ? tabMatch[1] : "";
|
||
preLang = info.replace(/\[([^\]]+)\]/, "").trim();
|
||
if (inCodeGroup && tabTitle) {
|
||
codeGroupTabs.push(tabTitle);
|
||
}
|
||
preCode = [];
|
||
} else {
|
||
inPre = false;
|
||
var codeContent = preCode.join("\n");
|
||
var resolvedLang = preLang;
|
||
if (!resolvedLang) {
|
||
if (codeContent.trim().startsWith("{") || codeContent.trim().startsWith("[")) resolvedLang = "json";
|
||
else if (codeContent.indexOf("=") !== -1 && !codeContent.includes("function") && !codeContent.includes(";")) resolvedLang = "ini";
|
||
else if (codeContent.startsWith("http://") || codeContent.startsWith("https://")) resolvedLang = "http";
|
||
else resolvedLang = "bash";
|
||
}
|
||
if (inCodeGroup) {
|
||
codeGroupPanels.push({ lang: resolvedLang, code: codeContent });
|
||
} else {
|
||
var langClass = resolvedLang ? ' class="language-' + resolvedLang + '"' : ' class="language-none"';
|
||
out.push('<div class="code-wrap"><pre><code' + langClass + ">" + esc(codeContent) + "</code></pre></div>");
|
||
}
|
||
}
|
||
continue;
|
||
}
|
||
|
||
if (inPre) {
|
||
preCode.push(line);
|
||
continue;
|
||
}
|
||
|
||
if (line.startsWith(":::callout-")) {
|
||
flushList();
|
||
flushTable();
|
||
var type = line.slice(11).trim();
|
||
out.push('<div class="callout callout-' + type + '"><div class="callout-content">');
|
||
continue;
|
||
}
|
||
if (line === ":::") {
|
||
flushList();
|
||
flushTable();
|
||
out.push("</div></div>");
|
||
continue;
|
||
}
|
||
|
||
if (line.startsWith("<div") || line.startsWith("</div") || line.startsWith("<span") || line.startsWith("</span") || line.startsWith("<img") || line.startsWith("<ul") || line.startsWith("</ul") || line.startsWith("<li") || line.startsWith("</li") || line.startsWith("<p class=")) {
|
||
flushList();
|
||
flushTable();
|
||
out.push(line);
|
||
continue;
|
||
}
|
||
|
||
if (line.indexOf("|") !== -1 && line.trim().startsWith("|") && line.trim().endsWith("|")) {
|
||
flushList();
|
||
var cells = line.split("|").slice(1, -1).map(function (c) { return c.trim(); });
|
||
if (cells.length > 0 && cells[0].match(/^:?-+:?$/)) {
|
||
continue;
|
||
}
|
||
inTable = true;
|
||
tableRows.push(cells);
|
||
continue;
|
||
} else {
|
||
flushTable();
|
||
}
|
||
|
||
if (line.match(/^---+$/)) {
|
||
flushList();
|
||
out.push("<hr />");
|
||
continue;
|
||
}
|
||
|
||
var hMatch = line.match(/^(#{1,6})\s+(.*)$/);
|
||
if (hMatch) {
|
||
flushList();
|
||
var level = hMatch[1].length;
|
||
var titleText = parseInline(hMatch[2]);
|
||
var rawTitle = hMatch[2];
|
||
var id = slugify(rawTitle);
|
||
out.push("<h" + level + ' id="' + id + '">' + titleText + "</h" + level + ">");
|
||
continue;
|
||
}
|
||
|
||
var olMatch = line.match(/^\d+\.\s+(.*)$/);
|
||
if (olMatch) {
|
||
if (!inList || listType !== "ol") {
|
||
flushList();
|
||
inList = true;
|
||
listType = "ol";
|
||
out.push("<ol>");
|
||
}
|
||
out.push("<li>" + parseInline(olMatch[1]) + "</li>");
|
||
continue;
|
||
}
|
||
|
||
var ulMatch = line.match(/^[-*]\s+(.*)$/);
|
||
if (ulMatch) {
|
||
if (!inList || listType !== "ul") {
|
||
flushList();
|
||
inList = true;
|
||
listType = "ul";
|
||
out.push("<ul>");
|
||
}
|
||
out.push("<li>" + parseInline(ulMatch[1]) + "</li>");
|
||
continue;
|
||
}
|
||
|
||
flushList();
|
||
|
||
if (line.trim().length === 0) {
|
||
continue;
|
||
}
|
||
|
||
if (line.startsWith("> ")) {
|
||
out.push("<blockquote><p>" + parseInline(line.slice(2)) + "</p></blockquote>");
|
||
continue;
|
||
}
|
||
|
||
out.push("<p>" + parseInline(line) + "</p>");
|
||
}
|
||
|
||
flushList();
|
||
flushTable();
|
||
return out.join("\n");
|
||
}
|
||
|
||
function resolveRoute(pathname) {
|
||
var path = pathname || window.location.pathname;
|
||
if (!path.startsWith("/")) path = "/" + path;
|
||
path = path.replace(/\/+$/, "") || "/";
|
||
|
||
for (var t = 0; t < SITE_TREE.length; t++) {
|
||
var tab = SITE_TREE[t];
|
||
for (var g = 0; g < tab.groups.length; g++) {
|
||
var grp = tab.groups[g];
|
||
for (var i = 0; i < grp.items.length; i++) {
|
||
var item = grp.items[i];
|
||
var itemPath = item.path.replace(/\/+$/, "") || "/";
|
||
if (itemPath === path) {
|
||
return { tab: tab, group: grp, item: item };
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return {
|
||
is404: true,
|
||
tab: SITE_TREE[0],
|
||
group: { title: "Error" },
|
||
item: { title: "404 Not Found", path: path }
|
||
};
|
||
}
|
||
|
||
function renderNavTabs(currentTab) {
|
||
var nav = document.getElementById("nav-tabs");
|
||
if (!nav) return;
|
||
nav.innerHTML = SITE_TREE.map(function (tab) {
|
||
var activeClass = tab.id === currentTab.id ? " active" : "";
|
||
return '<a class="tab-link' + activeClass + '" href="' + tab.path + '">' + esc(tab.title) + "</a>";
|
||
}).join("");
|
||
}
|
||
|
||
function renderSidebar(currentTab, currentItem) {
|
||
var sidebar = document.getElementById("sidebar");
|
||
if (!sidebar) return;
|
||
var html = "";
|
||
currentTab.groups.forEach(function (group) {
|
||
html += '<div class="group">';
|
||
html += '<p class="group-title">' + esc(group.title) + "</p>";
|
||
html += "<ul>";
|
||
group.items.forEach(function (item) {
|
||
var activeClass = item.path === currentItem.path ? ' class="active"' : "";
|
||
html += '<li><a' + activeClass + ' href="' + item.path + '">' + esc(item.title) + "</a></li>";
|
||
});
|
||
html += "</ul></div>";
|
||
});
|
||
sidebar.innerHTML = html;
|
||
buildMobileBar(currentTab, currentItem);
|
||
}
|
||
|
||
function buildMobileBar(currentTab, currentItem) {
|
||
var oldBar = document.getElementById("mobile-bar");
|
||
if (oldBar) oldBar.remove();
|
||
|
||
var headerContainer = document.getElementById("header-container") || document.getElementById("navbar");
|
||
if (!headerContainer) return;
|
||
|
||
var route = resolveRoute(window.location.pathname);
|
||
var bar = document.createElement("div");
|
||
bar.id = "mobile-bar";
|
||
bar.innerHTML =
|
||
'<button id="mobile-toggle" type="button" aria-label="Toggle navigation" onclick="toggleSidebar()">' + icon("menu") + '</button>' +
|
||
'<div class="mobile-crumbs">' +
|
||
'<span class="crumb-group">' + esc(route.group.title) + '</span>' +
|
||
'<span class="crumb-sep">›</span>' +
|
||
'<span class="crumb-page" id="mobile-crumb-page">' + esc(currentItem.title) + '</span>' +
|
||
'<span class="mobile-crumb-sub-btn" id="mobile-crumb-sub" style="display:none;">' +
|
||
'<span class="crumb-sep">›</span>' +
|
||
'<span class="crumb-heading" id="mobile-crumb-target"></span>' +
|
||
'</span>' +
|
||
'</div>';
|
||
headerContainer.appendChild(bar);
|
||
|
||
var pageBtn = bar.querySelector("#mobile-crumb-page");
|
||
if (pageBtn) {
|
||
pageBtn.addEventListener("click", function () {
|
||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||
});
|
||
}
|
||
|
||
var subBtn = bar.querySelector("#mobile-crumb-sub");
|
||
if (subBtn) {
|
||
subBtn.addEventListener("click", function () {
|
||
var targetId = subBtn.getAttribute("data-target-id");
|
||
var targetEl = targetId ? document.getElementById(targetId) : null;
|
||
if (targetEl) {
|
||
var header = document.getElementById("header-container") || document.getElementById("navbar");
|
||
var offset = header ? header.offsetHeight + 16 : 80;
|
||
var topPos = targetEl.getBoundingClientRect().top + window.pageYOffset - offset;
|
||
window.scrollTo({ top: Math.max(0, topPos), behavior: "smooth" });
|
||
}
|
||
});
|
||
}
|
||
|
||
var sidebar = document.getElementById("sidebar");
|
||
if (sidebar) {
|
||
var existingTabs = sidebar.querySelector(".mobile-tabs");
|
||
if (existingTabs) existingTabs.remove();
|
||
|
||
var g = document.createElement("div");
|
||
g.className = "mobile-tabs";
|
||
g.innerHTML =
|
||
'<button type="button" class="mt-btn" aria-expanded="false"><span>' + esc(currentTab.title) + "</span>" + icon("chevron") + "</button>" +
|
||
'<div class="mt-menu">' + SITE_TREE.map(function (t) {
|
||
var onClass = t.id === currentTab.id ? " on" : "";
|
||
return '<button type="button" class="mt-item' + onClass + '" data-tab-id="' + t.id + '"><span>' + esc(t.title) + "</span>" + icon("check") + "</button>";
|
||
}).join("") + "</div>";
|
||
sidebar.insertBefore(g, sidebar.firstChild);
|
||
|
||
g.querySelector(".mt-btn").addEventListener("click", function () {
|
||
g.classList.toggle("open");
|
||
});
|
||
|
||
g.querySelectorAll(".mt-item").forEach(function (btn) {
|
||
btn.addEventListener("click", function (e) {
|
||
e.stopPropagation();
|
||
var targetId = btn.getAttribute("data-tab-id");
|
||
var targetTab = SITE_TREE.find(function (t) { return t.id === targetId; });
|
||
if (targetTab) {
|
||
g.querySelectorAll(".mt-item").forEach(function (el) { el.classList.remove("on"); });
|
||
btn.classList.add("on");
|
||
g.classList.remove("open");
|
||
renderSidebar(targetTab, currentItem);
|
||
sidebar.scrollTop = 0;
|
||
}
|
||
});
|
||
});
|
||
|
||
document.addEventListener("click", function (e) {
|
||
if (!g.contains(e.target)) g.classList.remove("open");
|
||
});
|
||
}
|
||
|
||
var existingHead = sidebar.querySelector(".side-head");
|
||
if (existingHead) existingHead.remove();
|
||
|
||
var head = document.createElement("div");
|
||
head.className = "side-head";
|
||
var brand = document.querySelector("#navbar .brand");
|
||
if (brand && sidebar) {
|
||
var b = brand.cloneNode(true);
|
||
b.querySelector("img").removeAttribute("id");
|
||
|
||
var sideLinks = document.createElement("div");
|
||
sideLinks.className = "side-links";
|
||
|
||
var modes = ["system", "light", "dark"];
|
||
var seg = document.createElement("div");
|
||
seg.id = "theme-seg";
|
||
seg.innerHTML = modes.map(function (m) {
|
||
return '<button type="button" data-mode="' + m + '" aria-label="' + m + '">' + icon(m) + '</button>';
|
||
}).join("");
|
||
|
||
seg.addEventListener("click", function (e) {
|
||
var btn = e.target.closest("button");
|
||
if (btn) setTheme(btn.getAttribute("data-mode"));
|
||
});
|
||
|
||
sideLinks.appendChild(seg);
|
||
head.appendChild(b);
|
||
head.appendChild(sideLinks);
|
||
sidebar.insertBefore(head, sidebar.firstChild);
|
||
refreshThemeUI();
|
||
}
|
||
}
|
||
|
||
function updateFooterNav(currentItem) {
|
||
var allItems = [];
|
||
SITE_TREE.forEach(function (tab) {
|
||
tab.groups.forEach(function (grp) {
|
||
grp.items.forEach(function (itm) {
|
||
allItems.push(itm);
|
||
});
|
||
});
|
||
});
|
||
|
||
var idx = allItems.findIndex(function (it) {
|
||
return it.path.replace(/\/+$/, "") === currentItem.path.replace(/\/+$/, "");
|
||
});
|
||
|
||
var prev = idx > 0 ? allItems[idx - 1] : null;
|
||
var next = idx >= 0 && idx < allItems.length - 1 ? allItems[idx + 1] : null;
|
||
|
||
var prevBtn = document.getElementById("footer-prev");
|
||
var nextBtn = document.getElementById("footer-next");
|
||
|
||
if (prevBtn) {
|
||
if (prev) {
|
||
prevBtn.href = prev.path;
|
||
prevBtn.innerHTML = icon("chevronLeft") + "<span>" + esc(prev.title) + "</span>";
|
||
prevBtn.classList.remove("hidden");
|
||
} else {
|
||
prevBtn.classList.add("hidden");
|
||
}
|
||
}
|
||
|
||
if (nextBtn) {
|
||
if (next) {
|
||
nextBtn.href = next.path;
|
||
nextBtn.innerHTML = "<span>" + esc(next.title) + "</span>" + icon("chevronRight");
|
||
nextBtn.classList.remove("hidden");
|
||
} else {
|
||
nextBtn.classList.add("hidden");
|
||
}
|
||
}
|
||
}
|
||
|
||
function updateToc() {
|
||
var main = document.getElementById("main-content");
|
||
var tocList = document.getElementById("toc-list");
|
||
if (!main || !tocList) return;
|
||
var headings = main.querySelectorAll("h2");
|
||
var html = "";
|
||
headings.forEach(function (h) {
|
||
var id = h.id || h.textContent.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
||
h.id = id;
|
||
html += '<li><a href="#' + id + '">' + esc(h.textContent) + "</a></li>";
|
||
});
|
||
tocList.innerHTML = html;
|
||
initTocSpy();
|
||
}
|
||
|
||
function initTocSpy() {
|
||
var main = document.getElementById("main-content");
|
||
var links = Array.prototype.slice.call(document.querySelectorAll("aside#toc a"));
|
||
var headings = main ? Array.prototype.slice.call(main.querySelectorAll("h2, h3")) : [];
|
||
|
||
function update() {
|
||
var activeHeading = null;
|
||
headings.forEach(function (h) {
|
||
if (h.getBoundingClientRect().top <= 160) {
|
||
activeHeading = h;
|
||
}
|
||
});
|
||
|
||
if (links.length) {
|
||
var activeId = activeHeading ? activeHeading.id : "";
|
||
links.forEach(function (a) {
|
||
var href = a.getAttribute("href").replace(/^#/, "");
|
||
a.classList.toggle("active", href === activeId);
|
||
});
|
||
}
|
||
|
||
var crumbPage = document.getElementById("mobile-crumb-page");
|
||
var crumbSub = document.getElementById("mobile-crumb-sub");
|
||
var crumbTarget = document.getElementById("mobile-crumb-target");
|
||
|
||
if (crumbPage && crumbSub && crumbTarget) {
|
||
if (activeHeading && window.scrollY > 80) {
|
||
crumbPage.classList.add("muted");
|
||
crumbSub.style.display = "inline-flex";
|
||
crumbSub.setAttribute("data-target-id", activeHeading.id);
|
||
crumbTarget.textContent = activeHeading.textContent;
|
||
} else {
|
||
crumbPage.classList.remove("muted");
|
||
crumbSub.style.display = "none";
|
||
crumbSub.removeAttribute("data-target-id");
|
||
crumbTarget.textContent = "";
|
||
}
|
||
}
|
||
}
|
||
|
||
window.addEventListener("scroll", update, { passive: true });
|
||
update();
|
||
}
|
||
|
||
function addCopyButtons() {
|
||
document.querySelectorAll(".code-group").forEach(function (cg) {
|
||
if (cg.querySelector(".cg-copy-btn")) return;
|
||
var b = document.createElement("button");
|
||
b.type = "button";
|
||
b.className = "cg-copy-btn";
|
||
b.setAttribute("aria-label", "Copy");
|
||
b.innerHTML = icon("copy");
|
||
b.onclick = function () {
|
||
var activePre = cg.querySelector(".code-group-panel.active pre");
|
||
if (!activePre) return;
|
||
navigator.clipboard.writeText(activePre.innerText).then(function () {
|
||
b.innerHTML = icon("check");
|
||
setTimeout(function () { b.innerHTML = icon("copy"); }, 1500);
|
||
});
|
||
};
|
||
cg.appendChild(b);
|
||
});
|
||
|
||
document.querySelectorAll("main.prose pre").forEach(function (pre) {
|
||
if (pre.closest(".code-group")) return;
|
||
if (pre.parentNode.classList.contains("code-wrap") && pre.parentNode.querySelector(".copy-btn")) return;
|
||
var wrap = pre.parentNode.classList.contains("code-wrap") ? pre.parentNode : document.createElement("div");
|
||
if (!pre.parentNode.classList.contains("code-wrap")) {
|
||
wrap.className = "code-wrap";
|
||
pre.parentNode.insertBefore(wrap, pre);
|
||
wrap.appendChild(pre);
|
||
}
|
||
var b = document.createElement("button");
|
||
b.type = "button";
|
||
b.className = "copy-btn";
|
||
b.setAttribute("aria-label", "Copy");
|
||
b.innerHTML = icon("copy");
|
||
b.onclick = function () {
|
||
navigator.clipboard.writeText(pre.innerText).then(function () {
|
||
b.innerHTML = icon("check");
|
||
setTimeout(function () { b.innerHTML = icon("copy"); }, 1500);
|
||
});
|
||
};
|
||
wrap.appendChild(b);
|
||
});
|
||
}
|
||
|
||
function bindScrollFade(box, getScroller) {
|
||
function update() {
|
||
var el = getScroller();
|
||
var max = el.scrollWidth - el.clientWidth;
|
||
var left = 0;
|
||
var right = 0;
|
||
if (max > 1) {
|
||
left = Math.min(el.scrollLeft, 56);
|
||
right = Math.min(Math.max(Math.round(max - el.scrollLeft), 0), 56);
|
||
}
|
||
box.style.setProperty("--fade-l", left + "px");
|
||
box.style.setProperty("--fade-r", right + "px");
|
||
}
|
||
box.addEventListener("scroll", update, true);
|
||
window.addEventListener("resize", update);
|
||
update();
|
||
}
|
||
|
||
function initCodeFades() {
|
||
document.querySelectorAll("main.prose pre").forEach(function (pre) {
|
||
var code = pre.querySelector("code");
|
||
bindScrollFade(pre, function () {
|
||
return code && code.scrollWidth > code.clientWidth + 1 ? code : pre;
|
||
});
|
||
});
|
||
}
|
||
|
||
function wrapTables() {
|
||
document.querySelectorAll("main.prose table").forEach(function (table) {
|
||
if (table.parentNode.classList.contains("table-wrap")) {
|
||
bindScrollFade(table.parentNode, function () { return table.parentNode; });
|
||
}
|
||
});
|
||
}
|
||
|
||
function navigateTo(path) {
|
||
var cleanPath = path.startsWith("/") ? path : "/" + path;
|
||
if (window.location.pathname !== cleanPath) {
|
||
window.history.pushState(null, "", cleanPath);
|
||
}
|
||
loadPage();
|
||
}
|
||
|
||
function loadPage() {
|
||
var path = window.location.pathname.replace(/\/+$/, "") || "/";
|
||
var isHome = path === "/";
|
||
document.body.classList.toggle("is-home", isHome);
|
||
|
||
var contentEl = document.getElementById("main-content");
|
||
|
||
if (isHome) {
|
||
document.title = "Vyla Entertainment — Vyla";
|
||
renderNavTabs(SITE_TREE[0]);
|
||
|
||
if (contentEl) {
|
||
contentEl.classList.remove("content-animate", "caught-up");
|
||
fetch("/home.txt")
|
||
.then(function (res) {
|
||
if (!res.ok) throw new Error();
|
||
return res.text();
|
||
})
|
||
.then(function (html) {
|
||
contentEl.innerHTML = html;
|
||
window.scrollTo(0, 0);
|
||
})
|
||
.catch(function () {
|
||
contentEl.innerHTML = "<h1>Home</h1><p>Create /home.txt to customize this page.</p>";
|
||
});
|
||
}
|
||
|
||
var tocList = document.getElementById("toc-list");
|
||
if (tocList) tocList.innerHTML = "";
|
||
|
||
var prevBtn = document.getElementById("footer-prev");
|
||
var nextBtn = document.getElementById("footer-next");
|
||
if (prevBtn) prevBtn.classList.add("hidden");
|
||
if (nextBtn) nextBtn.classList.add("hidden");
|
||
|
||
var sidebar = document.getElementById("sidebar");
|
||
if (sidebar && sidebar.classList.contains("open")) {
|
||
sidebar.classList.remove("open");
|
||
document.body.classList.remove("nav-open");
|
||
}
|
||
return;
|
||
}
|
||
|
||
var route = resolveRoute(window.location.pathname);
|
||
renderNavTabs(route.tab);
|
||
renderSidebar(route.tab, route.item);
|
||
|
||
document.title = route.item.title + " — Vyla";
|
||
|
||
var contentEl = document.getElementById("main-content");
|
||
|
||
document.body.classList.remove("is-404");
|
||
|
||
var route = resolveRoute(window.location.pathname);
|
||
renderNavTabs(route.tab);
|
||
renderSidebar(route.tab, route.item);
|
||
|
||
document.title = route.item.title + " — Vyla";
|
||
|
||
var contentEl = document.getElementById("main-content");
|
||
|
||
if (route.is404) {
|
||
document.body.classList.add("is-404");
|
||
document.title = "404 - Page Not Found — Vyla";
|
||
|
||
if (contentEl) {
|
||
contentEl.classList.remove("content-animate");
|
||
contentEl.innerHTML =
|
||
'<div class="error-container">' +
|
||
' <div class="error-code">404</div>' +
|
||
' <div class="error-title">PAGE NOT FOUND</div>' +
|
||
' <p class="error-desc">But if you don\'t change your direction, and if you keep looking, you may end up where you are heading.</p>' +
|
||
' <a href="/" class="btn-home">Take me home</a>' +
|
||
'</div>';
|
||
|
||
var tocList = document.getElementById("toc-list");
|
||
if (tocList) tocList.innerHTML = "";
|
||
var prevBtn = document.getElementById("footer-prev");
|
||
var nextBtn = document.getElementById("footer-next");
|
||
if (prevBtn) prevBtn.classList.add("hidden");
|
||
if (nextBtn) nextBtn.classList.add("hidden");
|
||
window.scrollTo(0, 0);
|
||
}
|
||
var sidebar = document.getElementById("sidebar");
|
||
if (sidebar && sidebar.classList.contains("open")) {
|
||
sidebar.classList.remove("open");
|
||
document.body.classList.remove("nav-open");
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (contentEl) {
|
||
contentEl.classList.remove("content-animate");
|
||
contentEl.innerHTML =
|
||
'<div class="skeleton-wrap" aria-hidden="true">' +
|
||
'<div class="skeleton-line skeleton-eyebrow"></div>' +
|
||
'<div class="skeleton-line skeleton-title"></div>' +
|
||
'<div class="skeleton-line skeleton-desc"></div>' +
|
||
'<div class="skeleton-media"></div>' +
|
||
'<div class="skeleton-line skeleton-text w-90"></div>' +
|
||
'<div class="skeleton-line skeleton-text w-100"></div>' +
|
||
'<div class="skeleton-line skeleton-text w-75"></div>' +
|
||
'<div class="skeleton-line skeleton-text w-85"></div>' +
|
||
'</div>';
|
||
}
|
||
|
||
fetch(route.item.file)
|
||
.then(function (res) {
|
||
var ctype = res.headers.get("content-type") || "";
|
||
if (!res.ok || ctype.indexOf("text/html") !== -1) throw new Error("Not found");
|
||
return res.text();
|
||
})
|
||
.then(function (md) {
|
||
if (contentEl) {
|
||
contentEl.innerHTML = parseMarkdown(md);
|
||
|
||
if (window.Prism) {
|
||
window.Prism.highlightAllUnder(contentEl);
|
||
}
|
||
|
||
var eyebrow = document.createElement("div");
|
||
eyebrow.className = "eyebrow";
|
||
eyebrow.textContent = route.group.title;
|
||
var h1 = contentEl.querySelector("h1");
|
||
if (h1) h1.parentNode.insertBefore(eyebrow, h1);
|
||
|
||
contentEl.classList.remove("caught-up");
|
||
contentEl.classList.add("content-animate");
|
||
|
||
var catchUpOnScroll = function () {
|
||
if (contentEl.classList.contains("content-animate")) {
|
||
contentEl.classList.add("caught-up");
|
||
}
|
||
window.removeEventListener("scroll", catchUpOnScroll, { passive: true });
|
||
window.removeEventListener("wheel", catchUpOnScroll, { passive: true });
|
||
window.removeEventListener("touchmove", catchUpOnScroll, { passive: true });
|
||
};
|
||
|
||
window.addEventListener("scroll", catchUpOnScroll, { passive: true });
|
||
window.addEventListener("wheel", catchUpOnScroll, { passive: true });
|
||
window.addEventListener("touchmove", catchUpOnScroll, { passive: true });
|
||
|
||
setTimeout(function () {
|
||
contentEl.classList.add("caught-up");
|
||
window.removeEventListener("scroll", catchUpOnScroll);
|
||
window.removeEventListener("wheel", catchUpOnScroll);
|
||
window.removeEventListener("touchmove", catchUpOnScroll);
|
||
}, 700);
|
||
|
||
updateFooterNav(route.item);
|
||
updateToc();
|
||
addCopyButtons();
|
||
initCodeFades();
|
||
wrapTables();
|
||
window.scrollTo(0, 0);
|
||
}
|
||
})
|
||
.catch(function () {
|
||
if (contentEl) {
|
||
contentEl.innerHTML = "<h1>Page not found</h1><p>The requested page could not be located.</p>";
|
||
}
|
||
});
|
||
|
||
var sidebar = document.getElementById("sidebar");
|
||
if (sidebar && sidebar.classList.contains("open")) {
|
||
sidebar.classList.remove("open");
|
||
document.body.classList.remove("nav-open");
|
||
}
|
||
}
|
||
|
||
function getRecent() {
|
||
try { return JSON.parse(localStorage.getItem("vyla-recent-v2")) || []; } catch (e) { return []; }
|
||
}
|
||
|
||
function saveRecent(item) {
|
||
if (!item) return;
|
||
var slim = { title: item.title, crumbs: item.crumbs, href: item.href, type: item.type };
|
||
var list = getRecent().filter(function (r) { return r.href !== slim.href; });
|
||
list.unshift(slim);
|
||
localStorage.setItem("vyla-recent-v2", JSON.stringify(list.slice(0, 5)));
|
||
}
|
||
|
||
function loadSearchData() {
|
||
if (searchData) return Promise.resolve(searchData);
|
||
var promises = [];
|
||
SITE_TREE.forEach(function (tab) {
|
||
tab.groups.forEach(function (group) {
|
||
group.items.forEach(function (item) {
|
||
promises.push(
|
||
fetch(item.file)
|
||
.then(function (r) { return r.text(); })
|
||
.then(function (text) {
|
||
return {
|
||
title: item.title,
|
||
crumbs: [tab.title, group.title, item.title],
|
||
href: item.path,
|
||
type: "page",
|
||
text: text.replace(/[#*`[\]()]/g, " ").slice(0, 3000)
|
||
};
|
||
})
|
||
.catch(function () { return null; })
|
||
);
|
||
});
|
||
});
|
||
});
|
||
return Promise.all(promises).then(function (results) {
|
||
searchData = results.filter(Boolean);
|
||
return searchData;
|
||
});
|
||
}
|
||
|
||
function renderSearch(keepActive) {
|
||
var q = document.getElementById("search-input").value.trim().toLowerCase();
|
||
var tokens = q.split(/\s+/).filter(Boolean);
|
||
var box = document.getElementById("search-results");
|
||
if (!keepActive) searchActive = 0;
|
||
|
||
if (tokens.length) {
|
||
if (!searchData) {
|
||
searchResults = [];
|
||
box.innerHTML = '<div class="search-label">Indexing pages...</div>';
|
||
return;
|
||
}
|
||
searchResults = searchData
|
||
.map(function (it) {
|
||
var score = 0;
|
||
var t = it.title.toLowerCase();
|
||
var txt = it.text.toLowerCase();
|
||
tokens.forEach(function (tok) {
|
||
if (t.indexOf(tok) !== -1) score += 20;
|
||
if (txt.indexOf(tok) !== -1) score += 2;
|
||
});
|
||
return { it: it, s: score };
|
||
})
|
||
.filter(function (r) { return r.s > 0; })
|
||
.sort(function (a, b) { return b.s - a.s; })
|
||
.slice(0, 10)
|
||
.map(function (r) { return r.it; });
|
||
} else {
|
||
searchResults = getRecent();
|
||
}
|
||
|
||
if (!searchResults.length) {
|
||
box.innerHTML = '<div class="search-label">' + (tokens.length ? "No results found" : "No recent searches") + "</div>";
|
||
return;
|
||
}
|
||
|
||
box.innerHTML = (tokens.length ? "" : '<div class="search-label">Recent searches</div>') + searchResults.map(function (it, i) {
|
||
var crumbs = it.crumbs.join('<span class="sep">›</span>');
|
||
return '<a class="sr' + (i === searchActive ? " on" : "") + '" href="' + it.href + '" data-i="' + i + '">' +
|
||
icon("doc") +
|
||
'<div class="sr-body"><div class="t">' + esc(it.title) + "</div>" +
|
||
'<div class="p">' + crumbs + "</div></div></a>";
|
||
}).join("");
|
||
}
|
||
|
||
function openSearch() {
|
||
var input = document.getElementById("search-input");
|
||
document.getElementById("search-modal").classList.add("open");
|
||
document.body.style.overflow = "hidden";
|
||
input.value = "";
|
||
renderSearch(false);
|
||
loadSearchData().then(function () { renderSearch(false); });
|
||
input.focus();
|
||
}
|
||
|
||
function closeSearch() {
|
||
document.getElementById("search-modal").classList.remove("open");
|
||
document.body.style.overflow = "";
|
||
}
|
||
|
||
function onSearchKey(e) {
|
||
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "k") {
|
||
e.preventDefault();
|
||
openSearch();
|
||
return;
|
||
}
|
||
var modal = document.getElementById("search-modal");
|
||
if (!modal || !modal.classList.contains("open")) return;
|
||
if (e.key === "Escape") {
|
||
closeSearch();
|
||
} else if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
||
e.preventDefault();
|
||
var n = searchResults.length || 1;
|
||
searchActive = (searchActive + (e.key === "ArrowDown" ? 1 : -1) + n) % n;
|
||
renderSearch(true);
|
||
} else if (e.key === "Enter" && searchResults[searchActive]) {
|
||
var item = searchResults[searchActive];
|
||
saveRecent(item);
|
||
closeSearch();
|
||
navigateTo(item.href);
|
||
}
|
||
}
|
||
|
||
function buildSearch() {
|
||
var trigger = document.getElementById("search-trigger");
|
||
if (trigger) trigger.innerHTML = icon("search") + "<span>Search...</span><kbd>Ctrl K</kbd>";
|
||
var modal = document.createElement("div");
|
||
modal.id = "search-modal";
|
||
modal.innerHTML =
|
||
'<div class="search-backdrop"></div><div class="search-panel"><div class="search-input-row">' + icon("search") +
|
||
'<input id="search-input" type="text" placeholder="Search..." autocomplete="off" spellcheck="false"></div>' +
|
||
'<div id="search-results"></div><div class="search-footer"><span><kbd>↑</kbd><kbd>↓</kbd>Select</span><span><kbd>↵</kbd>Open</span><span class="close-hint"><kbd>esc</kbd>Close</span></div></div>';
|
||
document.body.appendChild(modal);
|
||
modal.querySelector(".search-backdrop").addEventListener("click", closeSearch);
|
||
document.getElementById("search-input").addEventListener("input", function () { renderSearch(false); });
|
||
document.getElementById("search-results").addEventListener("click", function (e) {
|
||
var a = e.target.closest(".sr");
|
||
if (a) {
|
||
e.preventDefault();
|
||
saveRecent(searchResults[Number(a.getAttribute("data-i"))]);
|
||
closeSearch();
|
||
navigateTo(a.getAttribute("href"));
|
||
}
|
||
});
|
||
document.addEventListener("keydown", onSearchKey);
|
||
}
|
||
|
||
document.addEventListener("DOMContentLoaded", function () {
|
||
if (localStorage.getItem("vyla-banner-dismissed") === "true") {
|
||
var banner = document.getElementById("banner");
|
||
if (banner) banner.setAttribute("data-dismissed", "true");
|
||
document.body.classList.add("banner-dismissed");
|
||
}
|
||
|
||
var header = document.getElementById("header-container");
|
||
if (header) {
|
||
requestAnimationFrame(function () {
|
||
document.documentElement.style.setProperty("--header-height", header.offsetHeight + "px");
|
||
});
|
||
window.addEventListener("resize", function () {
|
||
requestAnimationFrame(function () {
|
||
document.documentElement.style.setProperty("--header-height", header.offsetHeight + "px");
|
||
});
|
||
});
|
||
}
|
||
|
||
applyTheme();
|
||
buildThemeControls();
|
||
buildSearch();
|
||
loadPage();
|
||
|
||
window.addEventListener("popstate", loadPage);
|
||
|
||
document.addEventListener("click", function (e) {
|
||
var a = e.target.closest("a");
|
||
if (a) {
|
||
var href = a.getAttribute("href");
|
||
if (href && href.startsWith("#")) {
|
||
return;
|
||
}
|
||
if (href && href.startsWith("/") && !href.startsWith("//") && a.target !== "_blank") {
|
||
e.preventDefault();
|
||
navigateTo(href);
|
||
return;
|
||
}
|
||
}
|
||
|
||
var sidebar = document.getElementById("sidebar");
|
||
var menu = document.getElementById("theme-menu");
|
||
if (menu && !e.target.closest(".theme-wrap")) menu.classList.remove("open");
|
||
if (
|
||
sidebar &&
|
||
sidebar.classList.contains("open") &&
|
||
!sidebar.contains(e.target) &&
|
||
!e.target.closest("#mobile-toggle") &&
|
||
!e.target.closest(".theme-wrap") &&
|
||
!e.target.closest("#theme-seg")
|
||
) {
|
||
sidebar.classList.remove("open");
|
||
document.body.classList.remove("nav-open");
|
||
}
|
||
});
|
||
}); |