(function () { var preview = document.querySelector("[data-source-url][data-file-name][data-content-type]"); if (!preview) { return; } var SMALL_TEXT_BYTES = 50 * 1024; var LARGE_PREVIEW_BYTES = 500 * 1024; var state = { fileName: preview.dataset.fileName || "", contentType: (preview.dataset.contentType || "").toLowerCase(), previewKind: preview.dataset.previewKind || "", sizeBytes: Number(preview.dataset.sizeBytes || 0), sizeLabel: preview.dataset.fileSize || "", sourceURL: preview.dataset.sourceUrl || "", downloadURL: preview.dataset.downloadUrl || "", iconURL: preview.dataset.iconUrl || "", activeMode: "", defaultMode: "default", pendingMode: "", textSource: "", textLoaded: false, rawLoaded: false, prismLoaded: false, renderLoaded: false, renderFullscreenFallback: false, confirmedLargeModes: {}, tabs: [] }; var els = { tabs: preview.querySelector("[data-preview-tabs]"), modeLabel: preview.querySelector("[data-preview-mode-label]"), defaultPane: preview.querySelector("[data-default-preview]"), imagePane: preview.querySelector("[data-image-preview]"), videoPane: preview.querySelector("[data-video-preview]"), browserAudioPane: preview.querySelector("[data-browser-audio-preview]"), rawPane: preview.querySelector("[data-raw-preview]"), rawOutput: preview.querySelector("[data-raw-output]"), codePane: preview.querySelector("[data-code-preview]"), codeOutput: preview.querySelector("[data-code-output]"), renderPane: preview.querySelector("[data-render-preview]"), fullscreenButton: preview.querySelector("[data-render-fullscreen]"), gatePane: preview.querySelector("[data-large-preview-gate]"), gateConfirm: preview.querySelector("[data-large-preview-confirm]"), gateCancel: preview.querySelector("[data-large-preview-cancel]"), placeholder: preview.querySelector("[data-preview-placeholder]") }; var fileType = detectFileType(); state.tabs = buildTabs(fileType); state.defaultMode = chooseDefaultMode(fileType, state.tabs); bindLargeGate(); bindThemeChanges(); bindRenderFullscreen(); renderTabs(); selectMode(state.defaultMode); function detectFileType() { var extension = extensionFor(state.fileName); var baseType = state.contentType.split(";")[0].trim(); var language = languageFor(extension, baseType); var isImage = state.previewKind === "image" || baseType.indexOf("image/") === 0 && baseType !== "image/svg+xml"; var isVideo = state.previewKind === "video" || baseType.indexOf("video/") === 0; var isAudio = state.previewKind === "audio" || baseType.indexOf("audio/") === 0; return { extension: extension, baseType: baseType, language: language, isTextLike: Boolean(language), isHTML: language === "html", isMarkdown: language === "markdown", isImage: isImage, isVideo: isVideo, isAudio: isAudio, isMobile: window.matchMedia && window.matchMedia("(max-width: 720px), (pointer: coarse)").matches }; } function buildTabs(type) { var tabs = [{ mode: "default", label: "Default" }]; if (type.isImage) { tabs.push({ mode: "image", label: "Image Preview" }); return tabs; } if (type.isVideo) { tabs.push({ mode: "video", label: "Video Preview" }); return tabs; } if (type.isAudio) { tabs.push({ mode: "browser-audio", label: "Browser Preview" }); return tabs; } if (type.isTextLike) { if (type.isHTML || type.isMarkdown) { tabs.push({ mode: "render", label: "Render Preview" }); } tabs.push({ mode: "raw", label: "Raw Preview" }); tabs.push({ mode: "code", label: "Code Preview" }); } return tabs; } function chooseDefaultMode(type, tabs) { if (state.sizeBytes > LARGE_PREVIEW_BYTES) { if (type.isAudio && hasMode(tabs, "browser-audio")) { return "browser-audio"; } return "default"; } if (type.isImage) { return "image"; } if (type.isVideo) { return "video"; } if (type.isAudio) { return "browser-audio"; } if (type.isTextLike && state.sizeBytes > SMALL_TEXT_BYTES) { return "raw"; } if (type.isMarkdown) { return "render"; } if (type.isTextLike) { return "code"; } return "default"; } function renderTabs() { if (!els.tabs) { return; } els.tabs.innerHTML = ""; state.tabs.forEach(function (tab) { var button = document.createElement("button"); button.className = "preview-tab"; button.type = "button"; button.dataset.previewTab = tab.mode; button.textContent = tab.label; button.addEventListener("click", function () { selectMode(tab.mode); }); els.tabs.appendChild(button); }); } function selectMode(mode) { if (!hasMode(state.tabs, mode)) { mode = "default"; } if (mode !== "render") { exitRenderFullscreen(); } if (requiresLargeConfirmation(mode)) { showLargeGate(mode); return; } state.activeMode = mode; updateTabs(mode); updateRenderFullscreenButton(); hideAllPanes(); setModeLabel(labelForMode(mode)); if (mode === "default") { show(els.defaultPane); } else if (mode === "image") { show(els.imagePane); } else if (mode === "video") { show(els.videoPane); } else if (mode === "browser-audio") { show(els.browserAudioPane); } else if (mode === "raw") { show(els.rawPane); ensureRawPreview(); } else if (mode === "code") { show(els.codePane); ensurePrismPreview(); } else if (mode === "render") { show(els.renderPane); if (fileType.isMarkdown) { ensureMarkdownRenderPreview(); } else { ensureHTMLRenderPreview(); } } } function requiresLargeConfirmation(mode) { if (state.sizeBytes <= LARGE_PREVIEW_BYTES || state.confirmedLargeModes[mode]) { return false; } return mode === "raw" || mode === "code" || mode === "render"; } function showLargeGate(mode) { state.pendingMode = mode; updateTabs(state.activeMode || state.defaultMode); updateRenderFullscreenButton(false); hideAllPanes(); show(els.gatePane); setModeLabel("Large preview"); } function bindLargeGate() { if (els.gateConfirm) { els.gateConfirm.addEventListener("click", function () { if (!state.pendingMode) { return; } state.confirmedLargeModes[state.pendingMode] = true; selectMode(state.pendingMode); }); } if (els.gateCancel) { els.gateCancel.addEventListener("click", function () { state.pendingMode = ""; selectMode(state.activeMode || state.defaultMode || "default"); }); } } function bindThemeChanges() { var themeSelect = document.querySelector("[data-theme-select]"); if (!themeSelect) { return; } themeSelect.addEventListener("change", function () { window.setTimeout(function () { if (!fileType.isMarkdown || !state.renderLoaded) { return; } state.renderLoaded = false; if (state.activeMode === "render") { ensureMarkdownRenderPreview(); } }, 0); }); } function bindRenderFullscreen() { if (!els.fullscreenButton) { return; } els.fullscreenButton.addEventListener("click", function () { if (isRenderFullscreen()) { exitRenderFullscreen(); return; } enterRenderFullscreen(); }); document.addEventListener("fullscreenchange", updateRenderFullscreenButton); } function ensureTextLoaded() { if (state.textLoaded) { return Promise.resolve(state.textSource); } showLoading("Loading preview..."); return fetch(state.sourceURL, { credentials: "same-origin" }) .then(function (response) { if (!response.ok) { throw new Error("Preview failed"); } return response.text(); }) .then(function (source) { state.textSource = source; state.textLoaded = true; hide(els.placeholder); return source; }) .catch(function (error) { showError("Preview unavailable"); throw error; }); } function ensureRawPreview() { if (state.rawLoaded) { return; } ensureTextLoaded().then(function (source) { els.rawOutput.textContent = source; state.rawLoaded = true; if (state.activeMode === "raw") { hide(els.placeholder); show(els.rawPane); } }); } function ensurePrismPreview() { if (state.prismLoaded) { return; } showLoading("Loading syntax preview..."); Promise.all([ensureTextLoaded(), loadPrism()]) .then(function (results) { var source = results[0]; var language = fileType.language; if (language === "json") { source = formatJSON(source); } els.codeOutput.className = "language-" + language; els.codeOutput.textContent = source; if (window.Prism) { window.Prism.highlightElement(els.codeOutput); } state.prismLoaded = true; if (state.activeMode === "code") { hide(els.placeholder); show(els.codePane); } }) .catch(function () { showError("Syntax preview unavailable"); }); } function ensureHTMLRenderPreview() { if (state.renderLoaded) { return; } showLoading("Rendering preview..."); ensureTextLoaded() .then(function (source) { els.renderPane.srcdoc = withBaseElement(source); state.renderLoaded = true; if (state.activeMode === "render") { hide(els.placeholder); show(els.renderPane); } }) .catch(function () { showError("Render preview unavailable"); }); } function ensureMarkdownRenderPreview() { if (state.renderLoaded) { return; } showLoading("Rendering Markdown..."); Promise.all([ensureTextLoaded(), loadMarkdownLibs()]) .then(function (results) { var markdown = results[0]; var html = parseMarkdown(markdown); var clean = window.DOMPurify.sanitize(html); els.renderPane.srcdoc = markdownDocument(clean); state.renderLoaded = true; if (state.activeMode === "render") { hide(els.placeholder); show(els.renderPane); } }) .catch(function () { showError("Markdown preview unavailable"); }); } function showLoading(message) { if (!els.placeholder) { return; } var text = els.placeholder.querySelector("p"); if (text) { text.textContent = message; } show(els.placeholder); } function showError(message) { hideAllPanes(); var text = els.placeholder && els.placeholder.querySelector("p"); if (text) { text.textContent = message; } show(els.placeholder); } function hideAllPanes() { hide(els.defaultPane); hide(els.imagePane); hide(els.videoPane); hide(els.browserAudioPane); hide(els.rawPane); hide(els.codePane); hide(els.renderPane); hide(els.gatePane); hide(els.placeholder); } function enterRenderFullscreen() { if (state.activeMode !== "render") { return; } if (preview.requestFullscreen) { var request = preview.requestFullscreen(); if (request && typeof request.catch === "function") { request.catch(function () { state.renderFullscreenFallback = true; preview.classList.add("is-render-fullscreen"); updateRenderFullscreenButton(); }); } return; } state.renderFullscreenFallback = true; preview.classList.add("is-render-fullscreen"); updateRenderFullscreenButton(); } function exitRenderFullscreen() { if (document.fullscreenElement === preview && document.exitFullscreen) { var exit = document.exitFullscreen(); if (exit && typeof exit.catch === "function") { exit.catch(function () {}); } } state.renderFullscreenFallback = false; preview.classList.remove("is-render-fullscreen"); updateRenderFullscreenButton(); } function isRenderFullscreen() { return document.fullscreenElement === preview || state.renderFullscreenFallback; } function updateRenderFullscreenButton(forceVisible) { if (!els.fullscreenButton) { return; } var visible = typeof forceVisible === "boolean" ? forceVisible : state.activeMode === "render"; els.fullscreenButton.hidden = !visible; els.fullscreenButton.textContent = isRenderFullscreen() ? "Exit Full Screen" : "Full Screen"; els.fullscreenButton.setAttribute("aria-pressed", isRenderFullscreen() ? "true" : "false"); } function updateTabs(mode) { if (!els.tabs) { return; } Array.prototype.forEach.call(els.tabs.querySelectorAll("[data-preview-tab]"), function (button) { button.classList.toggle("is-active", button.dataset.previewTab === mode); }); } function show(element) { if (element) { element.hidden = false; } } function hide(element) { if (element) { element.hidden = true; } } function setModeLabel(label) { if (els.modeLabel) { els.modeLabel.textContent = label; } } function hasMode(tabs, mode) { return tabs.some(function (tab) { return tab.mode === mode; }); } function labelForMode(mode) { var labels = { "default": "Default", "image": "Image preview", "video": "Video preview", "browser-audio": "Browser preview", "raw": "Raw preview", "code": "Code preview", "render": "Render preview" }; return labels[mode] || "Preview"; } function loadPrism() { if (window.Prism) { return Promise.resolve(); } window.Prism = window.Prism || {}; window.Prism.manual = true; return Promise.all([ loadStyle("/static/lib/prismjs/prism.css"), loadScript("/static/lib/prismjs/prism.js") ]); } function loadMarkdownLibs() { if (window.marked && window.DOMPurify) { return Promise.resolve(); } return Promise.all([ loadScript("/static/lib/markdown/marked.umd.js"), loadScript("/static/lib/markdown/purify.min.js") ]); } function loadScript(src) { return new Promise(function (resolve, reject) { var existing = document.querySelector('script[src="' + src + '"]'); if (existing) { if (existing.dataset.loaded === "true") { resolve(); return; } existing.addEventListener("load", resolve, { once: true }); existing.addEventListener("error", reject, { once: true }); return; } var script = document.createElement("script"); script.async = true; script.src = src; script.addEventListener("load", function () { script.dataset.loaded = "true"; resolve(); }, { once: true }); script.addEventListener("error", reject, { once: true }); document.head.appendChild(script); }); } function loadStyle(href) { if (document.querySelector('link[href="' + href + '"]')) { return Promise.resolve(); } return new Promise(function (resolve, reject) { var link = document.createElement("link"); link.rel = "stylesheet"; link.href = href; link.addEventListener("load", resolve, { once: true }); link.addEventListener("error", reject, { once: true }); document.head.appendChild(link); }); } function parseMarkdown(source) { if (window.marked && typeof window.marked.parse === "function") { return window.marked.parse(source); } if (window.marked && window.marked.marked && typeof window.marked.marked.parse === "function") { return window.marked.marked.parse(source); } throw new Error("Marked unavailable"); } function markdownDocument(body) { var theme = currentTheme(); var base = ''; return '' + base + '' + '' + '
' + body + '
'; } function markdownThemeStyle(theme) { var themes = { revamp: ["dark", "#0b0b16", "#f5f3ff", "#aaa4d6", "#17142d", "#211b3e", "rgba(168,150,255,0.24)", "#67e8f9", "#a78bfa", "#1b1724", "#0f111a", "#f8fafc", "rgba(248,250,252,0.16)", "rgba(0,0,0,0.28)", "Inter,system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif", "Consolas,Monaco,\"Andale Mono\",\"Ubuntu Mono\",monospace"], classic: ["dark", "#09090b", "#fafafa", "#a1a1aa", "#18181b", "#27272a", "rgba(255,255,255,0.13)", "#e4e4e7", "#d4d4d8", "#111113", "#09090b", "#fafafa", "rgba(250,250,250,0.15)", "rgba(0,0,0,0.3)", "Inter,system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif", "Consolas,Monaco,\"Andale Mono\",\"Ubuntu Mono\",monospace"], retro: ["light", "#c0c0c0", "#000000", "#404040", "#ffffff", "#dfdfdf", "#000000", "#000078", "#000078", "#ffffff", "#000000", "#f5f5f5", "#808080", "transparent", "\"PixeloidSans\",\"PixelOperator\",\"Microsoft Sans Serif\",Tahoma,sans-serif", "\"PixelOperatorMono\",Consolas,monospace"], gruvbox: ["dark", "#1d2021", "#ebdbb2", "#bdae93", "#282828", "#32302f", "rgba(235,219,178,0.2)", "#fabd2f", "#d79921", "#1b1d1e", "#161819", "#fbf1c7", "rgba(251,241,199,0.18)", "rgba(0,0,0,0.26)", "Inter,system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif", "Consolas,Monaco,\"Andale Mono\",\"Ubuntu Mono\",monospace"], cyberpunk: ["dark", "#08070d", "#fff36f", "#9bfaff", "#16131f", "#251d34", "rgba(255,242,0,0.34)", "#00f0ff", "#ff2a6d", "#100d18", "#07060b", "#f8fafc", "rgba(0,240,255,0.26)", "rgba(255,42,109,0.14)", "Inter,system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif", "Consolas,Monaco,\"Andale Mono\",\"Ubuntu Mono\",monospace"] }; var t = themes[theme] || themes.revamp; var vars = "color-scheme:" + t[0] + ";--md-bg:" + t[1] + ";--md-fg:" + t[2] + ";--md-muted:" + t[3] + ";--md-panel:" + t[4] + ";--md-panel-2:" + t[5] + ";--md-border:" + t[6] + ";--md-link:" + t[7] + ";--md-accent:" + t[8] + ";--md-code-bg:" + t[9] + ";--md-block-code-bg:" + t[10] + ";--md-block-code-fg:" + t[11] + ";--md-block-code-border:" + t[12] + ";--md-shadow:" + t[13] + ";--md-font:" + t[14] + ";--md-mono:" + t[15] + ";"; return ":root{" + vars + "}*{box-sizing:border-box}html,body{min-height:100%;margin:0;background:var(--md-bg);color:var(--md-fg);font-family:var(--md-font)}body{padding:clamp(1rem,4vw,2.25rem);font-size:16px;line-height:1.65}main{max-width:54rem;margin:0 auto;padding:clamp(1rem,3vw,2rem);border:1px solid var(--md-border);border-radius:10px;background:var(--md-panel);box-shadow:0 20px 60px var(--md-shadow)}a{color:var(--md-link)}h1,h2,h3,h4,h5,h6{color:var(--md-fg);line-height:1.2}code,kbd,pre{font-family:var(--md-mono)}pre{overflow:auto;padding:1rem;border:1px solid var(--md-block-code-border)!important;background:var(--md-block-code-bg)!important;color:var(--md-block-code-fg)!important}code{background:var(--md-code-bg);border-radius:4px;padding:.12rem .3rem}pre code,pre>code,pre code[class*=\"language-\"]{padding:0!important;background:transparent!important;color:inherit!important;border:0!important}blockquote{margin:1rem 0;padding:.2rem 1rem;border-left:3px solid var(--md-accent);color:var(--md-muted);background:var(--md-panel-2)}table{width:100%;border-collapse:collapse;display:block;overflow:auto}th,td{border:1px solid var(--md-border);padding:.5rem .7rem}hr{border:0;border-top:1px solid var(--md-border)}img,video{max-width:100%;height:auto}"; } function currentTheme() { var theme = document.documentElement.dataset.theme || "revamp"; return /^(revamp|classic|retro|gruvbox|cyberpunk)$/.test(theme) ? theme : "revamp"; } function withBaseElement(source) { var base = ''; if (/]/i.test(source)) { return source.replace(/]*)>/i, "" + base); } return base + source; } function escapeAttribute(value) { return value.replace(/&/g, "&").replace(/"/g, """).replace(/ 1 ? parts.pop() : ""; } function languageFor(extension, baseType) { var extensionMap = { "c": "c", "cc": "cpp", "conf": "nginx", "cpp": "cpp", "cs": "csharp", "css": "css", "csv": "csv", "diff": "diff", "dockerfile": "docker", "go": "go", "h": "c", "hpp": "cpp", "htm": "html", "html": "html", "ini": "ini", "java": "java", "js": "javascript", "json": "json", "jsx": "jsx", "kt": "kotlin", "log": "log", "lua": "lua", "md": "markdown", "mdown": "markdown", "markdown": "markdown", "php": "php", "pl": "perl", "properties": "properties", "py": "python", "rb": "ruby", "rs": "rust", "sh": "bash", "sql": "sql", "swift": "swift", "toml": "toml", "ts": "typescript", "tsx": "tsx", "txt": "text", "xml": "xml", "yaml": "yaml", "yml": "yaml", "zig": "zig" }; var typeMap = { "application/javascript": "javascript", "application/json": "json", "application/ld+json": "json", "application/markdown": "markdown", "application/xml": "xml", "application/x-httpd-php": "php", "application/x-sh": "bash", "image/svg+xml": "xml", "text/css": "css", "text/csv": "csv", "text/html": "html", "text/javascript": "javascript", "text/markdown": "markdown", "text/plain": "text", "text/x-go": "go", "text/xml": "xml" }; if (extensionMap[extension]) { return extensionMap[extension]; } if (typeMap[baseType]) { return typeMap[baseType]; } if (baseType.indexOf("+json") !== -1) { return "json"; } if (baseType.indexOf("+xml") !== -1) { return "xml"; } if (baseType.indexOf("text/") === 0) { return "text"; } return ""; } })();