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");
});