Rock Identification Guide

Rock Identification Guide

Follow the Guide

Answer the questions to identify your rock.

Rock Identification Wizard

Click "Start" to begin the identification process.

Identification Result

Please complete the guide on the "Identification Guide" tab to see your result.

Data Configuration

Manage the guide's questions and final rock identifications.

Manage Rocks (Final Results)

IDNameActions

Manage Questions (Wizard Logic)

The "Link ID" must match a Unique ID from the Rocks or Questions tables.
IDQuestion TextActions

Please complete the guide on the "Identification Guide" tab to see your result.

'; } /** * Displays a node (question or result) based on its ID. */ function rig_showNode(nodeId) { // 1. Is it a question? const question = rig_questions.find(q => q.id === nodeId); if (question) { if (!rig_questionText || !rig_optionsContainer) return; rig_questionText.textContent = question.text; rig_optionsContainer.innerHTML = ''; // Clear old options question.answers.forEach(answer => { const button = document.createElement('button'); button.type = 'button'; button.className = 'rig-btn rig-option-btn'; button.textContent = answer.text; button.onclick = () => rig_showNode(answer.next); rig_optionsContainer.appendChild(button); }); return; } // 2. Is it a rock (result)? const rock = rig_rocks.find(t => t.id === nodeId); if (rock) { rig_showResult(rock); return; } // 3. Not found console.error(`RIG Tool: Node ID "${nodeId}" not found in rocks or questions.`); alert(`Error: Logic path broken. Could not find node "${nodeId}".`); rig_resetGuide(); } // --- TAB 2 (RESULT) FUNCTIONS --- /** * Populates the result tab with rock data and switches to it. */ function rig_showResult(rock) { if (!rig_resultContent) return; let featuresHtml = '
    '; if (Array.isArray(rock.features)) { featuresHtml += rock.features.map(f => `
  • ${f}
  • `).join(''); } featuresHtml += '
'; rig_resultContent.innerHTML = `

${rock.name}

${rock.desc}

Key Features:

${featuresHtml} `; // Auto-navigate to result tab (Spec II.B.4.o) rig_showTab(1); } /** * Handles the PDF download request. */ function rig_downloadPDF() { const element = rig_container.querySelector('#rig-result-pdf-output'); // Check if there is a result to download if (element.querySelector('.rig-no-results')) { alert("No result to download. Please complete the identification guide first."); return; } // Clone the element to style it for PDF const elementToPrint = element.cloneNode(true); const pdfContainer = document.createElement('div'); const pdfTitle = document.createElement('h1'); pdfTitle.textContent = 'Rock Identification Result'; pdfTitle.style.textAlign = 'center'; pdfTitle.style.color = '#2c3e50'; pdfTitle.style.fontSize = '24px'; pdfTitle.style.marginBottom = '20px'; pdfContainer.appendChild(pdfTitle); pdfContainer.appendChild(elementToPrint); pdfContainer.style.padding = '20px'; pdfContainer.style.fontFamily = 'Arial, sans-serif'; // Apply styles directly to the clone for PDF consistency elementToPrint.style.color = '#333'; elementToPrint.style.border = 'none'; elementToPrint.style.backgroundColor = '#fff'; elementToPrint.style.padding = '0'; elementToPrint.querySelectorAll('h2').forEach(h2 => { h2.style.color = '#3498db'; h2.style.textAlign = 'center'; }); elementToPrint.querySelectorAll('p').forEach(p => { p.style.textAlign = 'center'; p.style.fontSize = '14px'; }); elementToPrint.querySelectorAll('h3').forEach(h3 => { h3.style.color = '#34495e'; h3.style.textAlign = 'left'; h3.style.borderBottom = '1px solid #ccc'; }); elementToPrint.querySelectorAll('ul').forEach(ul => { ul.style.marginLeft = '20px'; }); const options = { margin: 1, // in filename: 'rock_identification_result.pdf', image: { type: 'jpeg', quality: 0.98 }, html2canvas: { scale: 2 }, jsPDF: { unit: 'in', format: 'letter', orientation: 'portrait' } }; if (typeof html2pdf === 'function') { html2pdf().from(pdfContainer).set(options).save(); } else { console.error("RIG Tool: html2pdf.js library is not loaded."); alert("Error: PDF generation library is missing."); } } // --- TAB 3 (CONFIG) FUNCTIONS --- // --- Rock Management --- function rig_renderRockTable() { if (!rig_rockTableBody) return; rig_rockTableBody.innerHTML = ''; rig_rocks.forEach(rock => { const row = document.createElement('tr'); row.innerHTML = ` ${rock.id} ${rock.name} `; rig_rockTableBody.appendChild(row); }); } function rig_handleRockFormSubmit(e) { e.preventDefault(); if (!rig_rockId || !rig_rockName || !rig_rockDesc || !rig_rockFeatures) return; const id = rig_rockId.value.trim(); if (!id) { alert("Unique ID is required."); return; } // Check for duplicate ID const idExists = rig_rocks.some(t => t.id === id) || rig_questions.some(q => q.id === id); if (idExists && id !== rig_editingRockId) { alert("Error: This Unique ID is already in use by another rock or question. Please choose a different one."); return; } const rockData = { id: id, name: rig_rockName.value.trim(), desc: rig_rockDesc.value.trim(), features: rig_rockFeatures.value.trim().split('\n').filter(f => f) }; if (rig_editingRockId) { rig_rocks = rig_rocks.map(t => t.id === rig_editingRockId ? rockData : t); } else { rig_rocks.push(rockData); } rig_renderRockTable(); rig_resetRockForm(); } window.rig_editRock = function(id) { const rock = rig_rocks.find(t => t.id === id); if (!rock) return; if (!rig_rockEditId || !rig_rockId || !rig_rockName || !rig_rockDesc || !rig_rockFeatures || !rig_rockCancelBtn) return; rig_editingRockId = id; rig_rockEditId.value = id; rig_rockId.value = rock.id; rig_rockId.disabled = true; // Don't allow editing ID rig_rockName.value = rock.name; rig_rockDesc.value = rock.desc; rig_rockFeatures.value = rock.features.join('\n'); rig_rockCancelBtn.classList.remove('rig-hidden'); } function rig_resetRockForm() { if (!rig_rockForm || !rig_rockCancelBtn || !rig_rockId) return; rig_editingRockId = null; rig_rockForm.reset(); rig_rockId.disabled = false; rig_rockCancelBtn.classList.add('rig-hidden'); } window.rig_deleteRock = function(id) { if (confirm(`Are you sure you want to delete the rock "${id}"? This could break the guide logic if any question links to it.`)) { rig_rocks = rig_rocks.filter(t => t.id !== id); rig_renderRockTable(); rig_resetGuide(); // Reset guide in case data changed } } // --- Question Management --- function rig_renderQuestionTable() { if (!rig_questionTableBody) return; rig_questionTableBody.innerHTML = ''; rig_questions.forEach(q => { const row = document.createElement('tr'); row.innerHTML = ` ${q.id} ${q.text} `; rig_questionTableBody.appendChild(row); }); } function rig_handleQuestionFormSubmit(e) { e.preventDefault(); if (!rig_questionId || !rig_questionTextEl || !rig_questionAnswers) return; const id = rig_questionId.value.trim(); if (!id) { alert("Unique ID is required."); return; } // Check for duplicate ID const idExists = rig_rocks.some(t => t.id === id) || rig_questions.some(q => q.id === id); if (idExists && id !== rig_editingQuestionId) { alert("Error: This Unique ID is already in use by another rock or question. Please choose a different one."); return; } // Parse answers (Manual Input format: Text|LinkID) const answerLines = rig_questionAnswers.value.trim().split('\n'); const answers = answerLines.map(line => { const parts = line.split('|'); if (parts.length !== 2) return null; return { text: parts[0].trim(), next: parts[1].trim() }; }).filter(a => a && a.text && a.next); if (answers.length === 0) { alert("Please provide at least one valid answer in the format 'Answer Text|Link ID'."); return; } const questionData = { id: id, text: rig_questionTextEl.value.trim(), answers: answers }; if (rig_editingQuestionId) { rig_questions = rig_questions.map(q => q.id === rig_editingQuestionId ? questionData : q); } else { rig_questions.push(questionData); } rig_renderQuestionTable(); rig_resetQuestionForm(); rig_resetGuide(); // Reset guide as logic may have changed } window.rig_editQuestion = function(id) { const q = rig_questions.find(q => q.id === id); if (!q) return; if (!rig_questionEditId || !rig_questionId || !rig_questionTextEl || !rig_questionAnswers || !rig_questionCancelBtn) return; rig_editingQuestionId = id; rig_questionEditId.value = id; rig_questionId.value = q.id; rig_questionId.disabled = true; rig_questionTextEl.value = q.text; // Re-format answers array back into string for textarea rig_questionAnswers.value = q.answers.map(a => `${a.text}|${a.next}`).join('\n'); rig_questionCancelBtn.classList.remove('rig-hidden'); } function rig_resetQuestionForm() { if (!rig_questionForm || !rig_questionCancelBtn || !rig_questionId) return; rig_editingQuestionId = null; rig_questionForm.reset(); rig_questionId.disabled = false; rig_questionCancelBtn.classList.add('rig-hidden'); } window.rig_deleteQuestion = function(id) { if (id === RIG_START_NODE_ID) { alert(`Cannot delete the starting question ("${RIG_START_NODE_ID}").`); return; } if (confirm(`Are you sure you want to delete the question "${id}"? This could break the guide logic if any other question links to it.`)) { rig_questions = rig_questions.filter(q => q.id !== id); rig_renderQuestionTable(); rig_resetGuide(); // Reset guide in case data changed } } // --- IV. INITIALIZE THE TOOL --- rig_init(); }); // End of DOMContentLoaded