Constellation Identifier

Constellation Identifier

Constellation Identifier 🔭

Please select a constellation to see its details.

A complete guide to all configured constellations. This content will be included in the PDF download.

Add, edit, or remove constellations from the database. (Spec #4, #6)

Full Constellation Guide

e.g., [{"x":10, "y":20, "name":"Star1"}, ...]

e.g., [[0,1], [1,2]] (connects star 0 to 1, 1 to 2)

`; return itemDiv; } /** * Saves the data from the config tab back to constellationData */ window.saveConstellationConfig = function () { const newConstellationData = {}; configContainer .querySelectorAll(".config-constellation") .forEach((item) => { const name = item.querySelector(".config-name").value.trim(); if (name) { newConstellationData[name] = { season: item.querySelector(".config-season").value, mythology: item.querySelector(".config-myth").value, starsJSON: item.querySelector(".config-stars-json").value, linesJSON: item.querySelector(".config-lines-json").value, }; } }); constellationData = newConstellationData; // Update the global data object buildDashboard(); // Re-build dashboard (Tab 1) buildFullGuide(); // Re-build full guide (Tab 2) if (saveMessage) { saveMessage.style.display = "inline"; setTimeout(() => { saveMessage.style.display = "none"; }, 2000); } showConstellationTab("constellation-tab-dashboard"); }; /** * Adds a new empty item to the config tab */ window.addConstellationConfigItem = function () { const defaultData = { season: "N/A", mythology: "", starsJSON: '[{"x":50,"y":50,"name":"New Star"}]', linesJSON: '[]' }; configContainer.appendChild( createConfigItem("New Constellation", defaultData) ); }; /** * Removes a DOM element from the config tab */ window.removeConstellationElement = function (elementId) { const element = document.getElementById(elementId); if (element && element.parentNode) { element.parentNode.removeChild(element); } }; /** * Switches the active tab * (Spec II.B.4) */ window.showConstellationTab = function (tabId) { tabContents.forEach((content) => { content.classList.remove("active"); }); tabButtons.forEach((button) => { button.classList.remove("active"); }); const contentToShow = document.getElementById(tabId); const buttonToActivate = document.querySelector( `[onclick="showConstellationTab('${tabId}')"]` ); if (contentToShow) contentToShow.classList.add("active"); if (buttonToActivate) buttonToActivate.classList.add("active"); if (tabId === "constellation-tab-config") { buildConfig(); } }; /** * Navigates between tabs using Next/Previous buttons * (Spec II.B.4.o) */ window.navigateConstellationTabs = function (direction) { let currentIndex = -1; tabButtons.forEach((button, index) => { if (button.classList.contains("active")) { currentIndex = index; } }); if (currentIndex === -1) return; let nextIndex; if (direction === "next") { nextIndex = (currentIndex + 1) % tabButtons.length; } else { nextIndex = (currentIndex - 1 + tabButtons.length) % tabButtons.length; } const nextButton = tabButtons[nextIndex]; const onclickAttr = nextButton.getAttribute("onclick"); const tabId = onclickAttr.match(/'([^']*)'/)[1]; if (tabId) showConstellationTab(tabId); }; // Make tab functions global for inline onclick window.showConstellationTab = showConstellationTab; window.navigateConstellationTabs = navigateConstellationTabs; window.addConstellationConfigItem = addConstellationConfigItem; window.removeConstellationElement = removeConstellationElement; /** * Downloads the output as a PDF * (Spec II.C) */ function downloadConstellationPDF() { if ( typeof jspdf === "undefined" || typeof html2canvas === "undefined" ) { console.error("PDF libraries not loaded."); return; } if (pdfGuideAll.children.length === 0) { console.error("No guide content to download."); return; } const { jsPDF } = jspdf; const pdfOutputElement = document.getElementById("pdf-output-content"); pdfOutputElement.style.display = "block"; pdfOutputElement.style.position = "absolute"; pdfOutputElement.style.left = "-9999px"; html2canvas(pdfOutputElement, { scale: 2, backgroundColor: "#ffffff", }).then((canvas) => { pdfOutputElement.style.display = "none"; pdfOutputElement.style.position = "static"; const imgData = canvas.toDataURL("image/png"); const pdf = new jsPDF({ orientation: "p", unit: "px", format: "a4", }); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); const canvasWidth = canvas.width; const canvasHeight = canvas.height; const ratio = canvasWidth / canvasHeight; const imgWidth = pdfWidth - 40; // 20px margin const imgHeight = imgWidth / ratio; const pageMargin = 20; if (imgHeight <= pdfHeight - 2 * pageMargin) { pdf.addImage(imgData, "PNG", pageMargin, pageMargin, imgWidth, imgHeight); } else { // Handle multiple pages let heightLeft = canvasHeight; let canvasPosition = 0; const pageHeightInPixels = (pdfHeight - 2 * pageMargin) * (canvasWidth / imgWidth); while (heightLeft > 0.1) { const pageHeight = Math.min(heightLeft, pageHeightInPixels); const pageImgHeight = (pageHeight * imgWidth) / canvasWidth; const tempCanvas = document.createElement("canvas"); tempCanvas.width = canvasWidth; tempCanvas.height = pageHeight; const ctx = tempCanvas.getContext("2d"); ctx.drawImage( canvas, 0, canvasPosition, canvasWidth, pageHeight, 0, 0, canvasWidth, pageHeight ); const pageImgData = tempCanvas.toDataURL("image/png"); pdf.addImage( pageImgData, "PNG", pageMargin, pageMargin, imgWidth, pageImgHeight ); heightLeft -= pageHeight; canvasPosition += pageHeight; if (heightLeft > 0.1) pdf.addPage(); } } pdf.save("constellation-guide.pdf"); }); } // --- 5. Event Listeners (Spec IV.C) --- pdfDownloadBtn.addEventListener("click", downloadConstellationPDF); addConstellationBtn.addEventListener("click", window.addConstellationConfigItem); saveConfigBtn.addEventListener("click", window.saveConstellationConfig); // --- 6. Initialization --- buildDashboard(); buildFullGuide(); buildConfig(); showConstellationTab("constellation-tab-dashboard"); });