Fantasy Kingdom Law & Governance Document Generator
Draft: The Royal Edicts (Editable)
Define your kingdom's laws using the "Configuration" tab and click "Generate Document."
I. Kingdom Identity & Sovereignty
II. Law & Justice
III. Citizens & Economy
Proclaimed by His ${escapeHTML(data.rulerTitle)} on this day, ${data.today}.
I. FOUNDATIONAL PRINCIPLES
${clause('foundational', 'Article I.I. Source of Authority:', data.foundational)}
II. SOVEREIGNTY AND SUCCESSION
${clause('rulerTitle', 'Article II.I. Title of the Sovereign:', `${data.rulerTitle}`)}
${clause('succession', 'Article II.II. Law of Succession:', data.succession)}
III. JUSTICE AND JUDGMENT
${clause('justiceSystem', 'Article III.I. Judicial Authority:', `Justice shall be administered through the Royal Courts using the ${data.justiceSystem} system.`)}
${clause('crime', 'Article III.II. Highest Penalty:', `The maximum penalty for the crime of Treason against the Crown or the Realm shall be ${data.crime}.`)}
IV. RIGHTS AND RESPONSIBILITIES
${clause('rights', 'Article IV.I. Inalienable Rights of Citizens:', data.rights)}
${clause('labor', 'Article IV.II. Fealty and Labor:', `Every able-bodied subject owes a service of ${data.labor} to the local Lord or the Crown.`)}
V. ECONOMY AND TAXATION
${clause('tax', 'Article V.I. The Royal Tithe:', `All income and yields are subject to the Royal Tithe at a rate of ${data.tax}.`)}
`;
setupDashboardListeners();
if (!isInitial) showTab('fklg-tab-dashboard');
}
/**
* Attaches listeners to the dashboard elements for editing
*/
function setupDashboardListeners() {
const output = dashboardOutput;
if (!output) return;
// Listen for input/change events to update the underlying config values
output.querySelectorAll('input, textarea').forEach(element => {
element.addEventListener('input', handleDashboardUpdate);
element.addEventListener('change', handleDashboardUpdate);
});
}
/**
* Handles updates made directly to the dashboard inputs/textareas
*/
function handleDashboardUpdate(e) {
const target = e.target;
const value = target.value;
// Update Config Inputs based on Dashboard edits (two-way binding)
if (target.id === 'dash-name') configName.value = value;
else if (target.id === 'dash-ruler-name') configRulerName.value = value;
else if (target.id === 'dash-foundational') configFoundational.value = value;
else if (target.id === 'dash-rulerTitle') configRulerTitle.value = value;
else if (target.id === 'dash-succession') configSuccession.value = value;
else if (target.id === 'dash-justiceSystem') configJusticeSystem.value = value.replace(/Justice shall be administered through the Royal Courts using the (.*?) system\./, '$1').trim(); // Extract value from sentence
else if (target.id === 'dash-crime') configCrime.value = value;
else if (target.id === 'dash-rights') configRights.value = value;
else if (target.id === 'dash-labor') configLabor.value = value;
else if (target.id === 'dash-tax') configTaxRate.value = value;
}
/**
* Generates a PDF report from the dashboard data
*/
function downloadPDF() {
if (typeof window.jspdf === 'undefined') {
alert("Error: PDF library (jsPDF) could not be loaded. Please try again.");
return;
}
// Get ALL content from the dashboard output area (which is editable)
const content = dashboardOutput.innerText;
const { jsPDF } = window.jspdf;
const doc = new jsPDF("p", "pt", "a4");
const margin = 50;
const usableWidth = doc.internal.pageSize.getWidth() - margin * 2;
doc.setFont("times", "normal");
doc.setFontSize(11);
// Since the dashboard contains complex formatting (headings, indents),
// we use doc.text with splitTextTo preserve line breaks and formatting
const lines = doc.splitTextToSize(content, usableWidth);
let yPos = margin;
const lineHeight = 14;
lines.forEach((line) => {
if (yPos > doc.internal.pageSize.getHeight() - margin) {
doc.addPage();
yPos = margin;
}
doc.text(line, margin, yPos);
yPos += lineHeight;
});
const fileName = configName.value ? `${configName.value.replace(/ /g,"_")}_Governance_Edict.pdf` : "Fantasy_Governance_Edict.pdf";
doc.save(fileName);
}
/**
* Helper to escape HTML
*/
function escapeHTML(str) {
if (!str) return "";
return str
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
// --- 3. INITIALIZATION & EVENT LISTENERS ---
// Tab Listeners
tabButtons.forEach((btn) => {
btn.addEventListener("click", () => showTab(btn.dataset.target));
});
navButtons.forEach((btn) => {
btn.addEventListener("click", () => showTab(btn.dataset.target));
});
// Config Tab Listeners
if (generateBtn) {
generateBtn.addEventListener("click", () => renderDashboard(false));
}
// PDF Button
if (pdfBtn) {
pdfBtn.addEventListener("click", downloadPDF);
}
// Initial State: Generate dashboard with samples
renderDashboard();
showTab("fklg-tab-dashboard");
});
