`;
// 2. Data Table
if (codeData.length === 0) {
html += '
`;
}
container.innerHTML = html;
}
function thbcSwitchTab(tabId) {
document.querySelectorAll('.thbc-tab-btn').forEach(b => b.classList.remove('active'));
document.querySelectorAll('.thbc-content').forEach(c => c.classList.remove('active'));
const idx = tabId === 'builder' ? 0 : 1;
document.querySelectorAll('.thbc-tab-btn')[idx].classList.add('active');
document.getElementById('thbc-' + tabId).classList.add('active');
if (tabId === 'report') {
thbcRenderReport();
}
}
function thbcLoadExample() {
if(!confirm("Overwrite current data and load example data (Texas Permanent Foundation)?")) return;
// Reset rows
document.getElementById('thbc-code-rows-container').innerHTML = '';
document.getElementById('inp-jurisdiction').value = "Austin, TX (City of)";
document.getElementById('inp-authority').value = "Building & Development Services";
document.getElementById('inp-classification').value = "IRC_Appendix_Q";
document.getElementById('inp-researcher').value = "J. Doe / Nov 2025";
thbcAddCodeRow("Minimum Size", "Min habitable floor area (excl. loft)", "150 sq ft", "IRC App Q, R101.2");
thbcAddCodeRow("Exit/Egress", "Window minimum clear opening size", "3 sq ft", "IRC App Q, R304.1.3");
thbcAddCodeRow("Loft Height", "Min ceiling height over 50% of loft area", "6' - 8\"", "IRC App Q, R401.3");
thbcAddCodeRow("Stairs/Ladder", "Required type of access to loft", "Stairs or approved fixed ladder", "IRC App Q, R403");
thbcAddCodeRow("Foundation", "Required foundation type", "Permanent foundation slab or pier & beam", "City Zoning Sec 25-10");
thbcRenderReport();
thbcSwitchTab('report');
}
/* --- PDF Generation --- */
async function thbcGeneratePDF() {
thbcRenderReport(); // Ensure report is updated
const meta = {
jurisdiction: document.getElementById('inp-jurisdiction').value || "N/A Jurisdiction",
authority: document.getElementById('inp-authority').value || "N/A Permitting Authority",
classification: document.getElementById('inp-classification').options[document.getElementById('inp-classification').selectedIndex].text,
researcher: document.getElementById('inp-researcher').value || "N/A"
};
const codeData = thbcGetCodeData();
if (codeData.length === 0) {
alert("Please add code requirements before generating the PDF.");
return;
}
const { jsPDF } = window.jspdf;
const doc = new jsPDF('p', 'mm', 'a4');
// Styling
const teal = [0, 128, 128];
let y = 20;
// Header
doc.setFillColor(...teal);
doc.rect(0, 0, 210, 20, 'F');
doc.setTextColor(255, 255, 255);
doc.setFontSize(16);
doc.text("Tiny House Code Compliance Research Report", 14, 13);
// Meta Data
doc.setTextColor(0, 0, 0);
doc.setFontSize(10);
doc.setFont(undefined, 'bold');
doc.text("JURISDICTION:", 14, y + 15);
doc.text("CLASSIFICATION:", 100, y + 15);
doc.setFont(undefined, 'normal');
doc.text(meta.jurisdiction, 14, y + 20);
doc.text(meta.classification, 100, y + 20);
y += 35;
doc.text(`Researcher: ${meta.researcher}`, 14, y);
doc.text(`Permitting Authority: ${meta.authority}`, 100, y);
y += 10;
// Table Data
doc.setFontSize(14);
doc.text("Specific Regulatory Requirements", 14, y);
y += 5;
const tableBody = codeData.map(item => [
item.area,
item.req,
item.val,
item.src
]);
doc.autoTable({
startY: y,
head: [['Code Area', 'Requirement', 'Value Found', 'Source / Reference']],
body: tableBody,
theme: 'grid',
headStyles: { fillColor: teal, fontSize: 10 },
styles: { fontSize: 9 },
columnStyles: {
0: { cellWidth: 35, fontStyle: 'bold' },
2: { cellWidth: 30, textColor: [0, 128, 128] },
3: { cellWidth: 'auto', fontStyle: 'italic' }
}
});
doc.save(`TinyHouse_Code_Report_${meta.jurisdiction.split(',')[0]}.pdf`);
}
No code requirements entered yet.
'; } else { html += `Specific Regulatory Requirements
| Code Area | Requirement | Value Found | Source / Reference |
|---|---|---|---|
| ${item.area} | ${item.req} | ${item.val || 'Undetermined'} | ${item.src} |
