/** * Dynamic Page Content Loader * Loads page content from the API and renders it with proper formatting */ // Convert Quill Delta to HTML - accurate conversion matching backend format function convertDeltaToHtml(delta) { if (!delta || !delta.ops) return ""; let html = ""; let currentLine = ""; let inListType = null; // 'bullet' or 'ordered' const ops = delta.ops; for (let i = 0; i < ops.length; i++) { const op = ops[i]; const nextOp = ops[i + 1]; if (typeof op.insert === "string") { const text = op.insert; const inlineAttrs = op.attributes || {}; // Check if this is a standalone newline with block attributes if (text === "\n") { const blockAttrs = inlineAttrs; // Handle list transitions if (blockAttrs.list) { const newListType = blockAttrs.list; if (inListType !== newListType) { if (inListType) { html += inListType === "ordered" ? "" : ""; } html += newListType === "ordered" ? "
    " : "
" : ""; inListType = null; } // Apply block formatting if (blockAttrs.header) { html += `${currentLine}`; } else if (blockAttrs.blockquote) { html += `
${currentLine}
`; } else if (blockAttrs["code-block"]) { html += `
${currentLine}
`; } else if (currentLine) { html += `

${currentLine}

`; } } currentLine = ""; } else { // Regular text - may contain embedded newlines const parts = text.split("\n"); for (let j = 0; j < parts.length; j++) { const part = parts[j]; // Format the text part if (part.length > 0) { let formatted = escapeHtml(part); // Apply inline formatting if (inlineAttrs.bold) formatted = `${formatted}`; if (inlineAttrs.italic) formatted = `${formatted}`; if (inlineAttrs.underline) formatted = `${formatted}`; if (inlineAttrs.strike) formatted = `${formatted}`; if (inlineAttrs.code) formatted = `${formatted}`; if (inlineAttrs.link) formatted = `${formatted}`; currentLine += formatted; } // Handle embedded newlines (not the last part) if (j < parts.length - 1) { // Close any open list for embedded newlines if (inListType) { html += inListType === "ordered" ? "" : ""; inListType = null; } if (currentLine) { html += `

${currentLine}

`; } currentLine = ""; } } } } else if (op.insert && op.insert.image) { // Flush pending content if (currentLine) { if (inListType) { html += `
  • ${currentLine}
  • `; html += inListType === "ordered" ? "" : ""; inListType = null; } else { html += `

    ${currentLine}

    `; } currentLine = ""; } html += `Content image`; } } // Flush remaining content if (inListType) { if (currentLine) html += `
  • ${currentLine}
  • `; html += inListType === "ordered" ? "" : ""; } else if (currentLine) { html += `

    ${currentLine}

    `; } return html; } function escapeHtml(text) { return text .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } // Parse and render page content (handles both Delta JSON and raw HTML) function parsePageContent(content) { if (!content) return "

    Content coming soon...

    "; try { const delta = JSON.parse(content); if (delta.ops) { return convertDeltaToHtml(delta); } return content; } catch (e) { // Not JSON, return as-is (probably HTML) return content; } } // Load page content from API async function loadPageContent(slug, options = {}) { const { titleSelector = "#pageTitle", contentSelector = "#dynamicContent", staticSelector = "#staticContent", showLoading = true, } = options; const dynamicContent = document.querySelector(contentSelector); const staticContent = document.querySelector(staticSelector); const titleElement = document.querySelector(titleSelector); if (!dynamicContent) { console.warn("Dynamic content container not found:", contentSelector); return null; } if (showLoading) { dynamicContent.innerHTML = `

    Loading content...

    `; } try { const response = await fetch(`/api/pages/${slug}`); const data = await response.json(); if (data.success && data.page) { const page = data.page; // Update page title if provided if (page.title && titleElement) { titleElement.textContent = page.title; } // Parse and render content const htmlContent = parsePageContent(page.content); dynamicContent.innerHTML = htmlContent; // Hide static fallback if exists if (staticContent) { staticContent.style.display = "none"; } return page; } else { // Show static fallback dynamicContent.style.display = "none"; if (staticContent) { staticContent.style.display = "block"; } return null; } } catch (error) { console.error("Failed to load page content:", error); dynamicContent.style.display = "none"; if (staticContent) { staticContent.style.display = "block"; } return null; } } // Auto-initialize on DOMContentLoaded if data-page-slug is set document.addEventListener("DOMContentLoaded", () => { const pageContainer = document.querySelector("[data-page-slug]"); if (pageContainer) { const slug = pageContainer.dataset.pageSlug; loadPageContent(slug); } }); // Export for use in other scripts window.DynamicPage = { loadPageContent, parsePageContent, convertDeltaToHtml, };