// --- file:// protocol helper functions starts here --- const locatePathsForFileProtocol = () => { const htmlLocation = window.location.pathname; const htmlPathSegments = htmlLocation.split("/"); const outputIndex = htmlPathSegments.lastIndexOf("output"); if (outputIndex !== -1) { const filteredSegments = htmlPathSegments .slice(outputIndex + 1) .filter((segment) => segment !== "" && segment !== "index.html"); const MDPath = "pages/" + filteredSegments.join("/") + ".md"; const pdfBasePath = "../".repeat(filteredSegments.length) + "pdfs"; return { MDPath, pdfBasePath, }; } }; const fetchLocalJSON = () => { try { return JSON.parse(JSON.stringify(PDFConfigData)); } catch (error) { console.error("PDFConfigData is invalid or not found"); throw Error(error); } }; const generatePDFLinkAndTitleForFileProtocol = () => { // Generates the PDF link and title for the current page let { MDPath, pdfBasePath } = locatePathsForFileProtocol(); let data = fetchLocalJSON(); let matchingContent = false; for (const section of data.sections) { section.contents.forEach((content) => { let type = typeof content; if (type === "object") { Object.values(content).forEach((subMDFiles) => { subMDFiles.forEach((subMDFile) => { if (subMDFile === MDPath) { matchingContent = true; } }); }); Object.keys(content).forEach((parentMdFile) => { if (parentMdFile === MDPath) { matchingContent = true; } }); } else { if (content === MDPath) { matchingContent = true; } } }); if (matchingContent) { const sectionTitle = section.title; const pdfTitle = MDPath.split("/").pop().replace(".md", ""); const pdfFileName = `${pdfTitle}.pdf`; const pdfLink = `${pdfBasePath}/${sectionTitle}/${pdfFileName}`; return { pdfLink, pdfTitle, }; } } return { pdfLink: null, pdfTitle: null, }; }; const generateProjectPDFLinkAndTitleForFileProtocol = async () => { // Generates the PDF link and title for the current project, // the PDF file with all the contents of the current project // covers the FTP local development path let { pdfBasePath } = locatePathsForFileProtocol(); let data = fetchLocalJSON(); let matchingContent = false; try { data = await fetchJSON(JSONPath); } catch (defaultError) { throw defaultError; } // get the section title from the breadcrumbs const breadCrumbArray = $( ".sticky-breadcrumb-panel ul li" ); for (var breadCrumb in breadCrumbArray.toArray()) { breadCrumbSectionTitle=$(breadCrumbArray[breadCrumb]).text() if ( breadCrumbSectionTitle === null || breadCrumbSectionTitle === undefined || breadCrumbSectionTitle === "" ) { console.error( "Error generating PDF link and title: breadcrumb title is null or undefined" ); } for (const section of data.sections) { if (section.title === breadCrumbSectionTitle) { matchingContent = true; } if (matchingContent) { const sectionTitle = section.title; const pdfFileName = `${sectionTitle}.pdf`; const projectPDFLink = `${pdfBasePath}/${pdfFileName}`; return { projectPDFLink, sectionTitle, }; } } } return { pdfLink: null, sectionTitle: null, }; }; // --- file:// protocol helper functions ends here --- // ___________________________________________________________________________________________________________________ //| NOTE: | //| 1. normal functions are used when the html files are running from the pelican server | //| 2. failover functions are used when the html files are running directly from the output folder using a http server| //|___________________________________________________________________________________________________________________| const locatePaths = () => { // Reads the current URL and returns // the path to the MD file, the pelican output folder, and the PDF JSON config file (auto-generated) const htmlLocation = window.location.pathname; const protocol = window.location.protocol; const host = window.location.host; // Find the position of homeDir and outputDir in the URL const homeDirIndex = htmlLocation.indexOf(homeDir); const outputDirIndex = htmlLocation.indexOf(outputDir); let mdFilePath; let outputPath; let JSONPath; if (homeDirIndex !== -1) { // NOTE: the html file is being served directly from the output folder // if outputDir is empty, then the html file is being served from the root of the output folder const dirSubstring = outputDir === "" || outputDirIndex === -1 ? homeDir : outputDir; // Trim the URL up to outputDir and remove /index.html, then replace with .md mdFilePath = htmlLocation.substring(htmlLocation.indexOf(dirSubstring) + dirSubstring.length, htmlLocation.length - "/index.html".length) + ".md"; outputPath = `${protocol}//${host}${homeDir}`.replace(/\/$/, ""); JSONPath = `${protocol}//${host}${homeDir}/${pdfConfig}`; } else { // NOTE: the html file is being served from the pelican server mdFilePath = htmlLocation.replace("/index.html", ".md"); outputPath = `${protocol}//${host}`; JSONPath = `/${pdfConfig}`; } const MDPath = `pages/${mdFilePath[0] === '/' ? mdFilePath.substring(1) : mdFilePath}`; return { MDPath, outputPath, JSONPath, }; }; const fetchJSON = async (jsonPath) => { try { const response = await fetch(jsonPath); if (!response.ok) { const errorData = await response.json(); // Attempt to parse error response const errorMessage = errorData.message || "Network response was not ok."; throw new Error(`Network error: ${response.status} - ${errorMessage}`); } return response.json(); } catch (error) { throw new Error(`Error fetching JSON: ${error.message}`); } }; const generatePDFLinkAndTitle = async () => { // Generates the PDF link and title for the current page let { MDPath, outputPath, JSONPath } = locatePaths(); console.log("MDPath: ", MDPath); console.log("outputPath: ", outputPath); console.log("JSONPath: ", JSONPath); let data; try { data = await fetchJSON(JSONPath); console.log("✅ PDF config fetched successfully"); } catch (defaultError) { throw defaultError; } // now we have pdf config data, let's find the matching content let matchingContent = false; for (const section of data.sections) { section.contents.forEach((content) => { let type = typeof content; if (type === "object") { Object.values(content).forEach((subMDFiles) => { subMDFiles.forEach((subMDFile) => { if (subMDFile === MDPath) { matchingContent = true; } }); }); Object.keys(content).forEach((parentMdFile) => { if (parentMdFile === MDPath) { matchingContent = true; } }); } else { if (content === MDPath) { matchingContent = true; } } }); if (matchingContent) { const sectionTitle = section.title; const pdfTitle = MDPath.split("/").pop().replace(".md", ""); const pdfFileName = `${pdfTitle}.pdf`; const pdfLink = `${outputPath}/pdfs/${sectionTitle}/${pdfFileName}`; return { pdfLink, pdfTitle, }; } } return { pdfLink: null, pdfTitle: null, }; }; const generateProjectPDFLinkAndTitle = async () => { // Generates the PDF link and title for the current project, // the PDF file with all the contents of the current project // Covers the publication path let { outputPath, JSONPath } = locatePaths(); let data; let matchingContent = false; try { data = await fetchJSON(JSONPath); } catch (defaultError) { throw defaultError; } // get the section title from the breadcrumbs const breadCrumbArray = $( ".sticky-breadcrumb-panel ul li" ); for (var breadCrumb in breadCrumbArray.toArray()) { breadCrumbSectionTitle=$(breadCrumbArray[breadCrumb]).text() if ( breadCrumbSectionTitle === null || breadCrumbSectionTitle === undefined || breadCrumbSectionTitle === "" ) { console.error( "Error generating PDF link and title: breadcrumb title is null or undefined" ); } for (const section of data.sections) { if (section.title === breadCrumbSectionTitle) { matchingContent = true; } if (matchingContent) { const sectionTitle = section.title; const pdfFileName = `${sectionTitle}.pdf`; const projectPDFLink = `${outputPath}/pdfs/${pdfFileName}`; return { projectPDFLink, sectionTitle, }; } } } return { pdfLink: null, sectionTitle: null, }; }; const isFileProtocol = () => { if (window.location.protocol === "file:") return true || false; }; // --- Main function starts here --- $(window).ready(async function () { // Hide the 'Download PDF' button and 'Download Project' button $(".panel-pdf").css("display", "none"); $(".panel-download").css("display", "none"); const runningOnFileProtocol = isFileProtocol(); let pdfLink, pdfTitle; if (runningOnFileProtocol) { console.log("Running on 'file://' protocol"); // ({ pdfLink, pdfTitle } = generatePDFLinkAndTitleForFileProtocol()); } else { console.log("Running on 'http://' protocol"); const result = await generatePDFLinkAndTitle(); pdfLink = result.pdfLink; pdfTitle = result.pdfTitle; console.log("PDF link:", pdfLink); } // Render handlers for 'Download PDF' button and 'Download Project' button if (pdfLink !== null && pdfLink !== undefined) { $(".panel-pdf").css("display", "inline-block"); $(".panel-download").css("display", "inline-block"); } // Handle 'Download PDF' button click $(".panel-pdf").click(function () { try { const link = document.createElement("a"); link.href = pdfLink; link.download = `${pdfTitle}.pdf`; link.click(); } catch (error) { console.error("Error generating PDF link and title:", error); } }); // Handle 'Download Project' button click $(".panel-download").click(async function () { let sectionTitle, projectPDFLink; if (runningOnFileProtocol) { ({ projectPDFLink, sectionTitle } = generateProjectPDFLinkAndTitleForFileProtocol()); } else { const result = await generateProjectPDFLinkAndTitle(); projectPDFLink = result.projectPDFLink; sectionTitle = result.sectionTitle; } try { const link = document.createElement("a"); link.href = projectPDFLink; link.download = `${sectionTitle}.pdf`; link.click(); } catch (error) { console.error("Error generating PDF link and title:", error); } }); }); // --- Main function ends here ---