Virtual Artifact Viewer

Virtual Artifact Viewer

No artifacts loaded. Go to the 'Data Configuration' tab to add items.

Add New Artifact

Current Artifacts

× Artifact Image

Artifact Title

Origin / Date

Description goes here.

No artifacts added yet. Use the form above.

"; return; } artifactData.forEach((artifact) => { const item = document.createElement("div"); item.className = "vav-artifact-item"; item.innerHTML = `

${escapeHTML(artifact.title)}

`; listBody.appendChild(item); }); // Add event listeners to new buttons listBody.querySelectorAll(".vav-edit-btn").forEach((btn) => { btn.addEventListener("click", () => handleEditClick(btn.dataset.id)); }); listBody .querySelectorAll(".vav-delete-btn") .forEach((btn) => { btn.addEventListener("click", () => handleDeleteClick(btn.dataset.id) ); }); } /** * Handles config form submission. */ function handleFormSubmit(e) { e.preventDefault(); const id = artifactIdInput.value; const artifact = { title: titleInput.value.trim(), imageUrl: imageUrlInput.value.trim(), origin: originInput.value.trim(), description: descInput.value.trim() }; if (!artifact.title || !artifact.imageUrl || !artifact.origin) { alert("Please fill out Title, Image URL, and Origin fields."); return; } if (id) { // Update const index = artifactData.findIndex((a) => a.id == id); if (index !== -1) { artifactData[index] = { ...artifactData[index], ...artifact }; } } else { // Add new artifact.id = Date.now(); artifactData.push(artifact); } resetForm(); renderAll(); } /** * Resets the config form. */ function resetForm() { if (!formTitle || !artifactIdInput || !artifactForm || !saveBtn) return; formTitle.textContent = "Add New Artifact"; artifactIdInput.value = ""; artifactForm.reset(); saveBtn.textContent = "Save Artifact"; } /** * Populates form for editing. */ function handleEditClick(id) { const artifact = artifactData.find((a) => a.id == id); if (!artifact || !formTitle || !artifactIdInput || !titleInput || !imageUrlInput || !originInput || !descInput || !saveBtn) return; formTitle.textContent = "Edit Artifact"; artifactIdInput.value = artifact.id; titleInput.value = artifact.title; imageUrlInput.value = artifact.imageUrl; originInput.value = artifact.origin; descInput.value = artifact.description; saveBtn.textContent = "Update Artifact"; titleInput.focus(); } /** * Deletes an artifact. */ function handleDeleteClick(id) { if (confirm("Are you sure you want to delete this artifact?")) { artifactData = artifactData.filter((a) => a.id != id); renderAll(); resetForm(); // In case we were editing the deleted item } } // --- PDF Generation --- /** * Generates and downloads a PDF catalog of all artifacts. */ function downloadPDF() { if (typeof jsPDF === "undefined") { alert("Error: PDF generation library failed to load."); return; } try { const doc = new jsPDF(); const pageMargin = 20; const pageWidth = doc.internal.pageSize.getWidth() - pageMargin * 2; let yPos = 25; const title = "Virtual Artifact Catalog"; doc.setFont("helvetica", "bold"); doc.setFontSize(18); doc.setTextColor("#0d47a1"); doc.text(title, pageMargin, yPos); yPos += 15; artifactData.forEach((artifact, index) => { // Check for page break if (yPos > 260) { doc.addPage(); yPos = 20; } // Artifact Title doc.setFont("helvetica", "bold"); doc.setFontSize(14); doc.setTextColor("#333333"); doc.text(artifact.title, pageMargin, yPos); yPos += 7; // Artifact Origin doc.setFont("helvetica", "italic"); doc.setFontSize(11); doc.setTextColor("#555555"); doc.text(artifact.origin, pageMargin, yPos); yPos += 7; // Artifact Description doc.setFont("helvetica", "normal"); doc.setFontSize(11); doc.setTextColor("#333333"); const descLines = doc.splitTextToSize(artifact.description || "(No description)", pageWidth); doc.text(descLines, pageMargin, yPos); yPos += (descLines.length * 4.5) + 10; // Spacing for next item // Separator if (index < artifactData.length - 1) { doc.setDrawColor(222, 226, 230); // Light gray line doc.line(pageMargin, yPos, pageMargin + pageWidth, yPos); yPos += 7; } }); doc.save("Artifact-Catalog.pdf"); } catch (error) { console.error("Failed to generate PDF:", error); alert("An error occurred while generating the PDF."); } } // --- Utility --- function escapeHTML(str) { if (!str) return ""; return str .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function loadSampleData() { artifactData = [ { id: 1, title: "Liberty Bell", imageUrl: "https://upload.wikimedia.org/wikipedia/commons/4/47/Liberty_Bell_2015.jpg", origin: "Philadelphia, USA, 1752", description: "An iconic symbol of American independence, located in Philadelphia, Pennsylvania. The bell famously cracked when first rung after its arrival in Philadelphia." }, { id: 2, title: "Declaration of Independence", imageUrl: "https://upload.wikimedia.org/wikipedia/commons/8/8f/United_States_Declaration_of_Independence.jpg", origin: "USA, 1776", description: "The founding document of the United States, adopted by the Second Continental Congress on July 4, 1776. It announced the separation of the thirteen American colonies from Great Britain." }, { id: 3, title: "Apollo 11 Command Module 'Columbia'", imageUrl: "https://upload.wikimedia.org/wikipedia/commons/a/a2/Apollo_11_Command_Module_%2848520216431%29.jpg", origin: "USA, 1969", description: "The spacecraft that carried astronauts Neil Armstrong, Buzz Aldrin, and Michael Collins on the first human mission to land on the Moon." } ]; } // --- Event Listeners --- if (tabButtons[1]) tabButtons[1].onclick = () => showTab(1); if (tabButtons[2]) tabButtons[2].onclick = () => showTab(2); if (prevBtn) prevBtn.onclick = () => showTab(currentTab - 1); if (nextBtn) nextBtn.onclick = () => showTab(currentTab + 1); // Modal if (modalCloseBtn) modalCloseBtn.addEventListener("click", closeModal); if (modalBackdrop) modalBackdrop.addEventListener("click", (e) => { if (e.target === modalBackdrop) closeModal(); // Close on backdrop click }); // Config if (artifactForm) artifactForm.addEventListener("submit", handleFormSubmit); if (clearBtn) clearBtn.addEventListener("click", resetForm); // PDF if (downloadPdfBtn) downloadPdfBtn.addEventListener("click", downloadPDF); // --- Initialization --- function init() { loadSampleData(); renderAll(); showTab(1); // Set initial tab state } init(); });