Feng Shui Layout Generator
Your Custom Bagua Map
Align this map with your home's floor plan. The "Front" row should be along the same wall as your front door.
How to use: Stand at your front door looking *in*. Overlay this 3x3 grid onto your home's floor plan. The bottom row (Knowledge, Career, Helpful People) aligns with the wall your front door is on.
Customize Area Descriptions
Edit the keywords for each area. Your changes will appear on the Bagua Generator map.
${area.description}
`; grid.appendChild(cell); }); }, // Load data into the Configuration tab form loadConfigForm() { const form = document.getElementById('fslg-config-form'); if (!form) return; // Guard clause form.innerHTML = ''; this.state.areaData.forEach(area => { const group = document.createElement('div'); group.className = 'fslg-form-group'; group.innerHTML = ` `; form.appendChild(group); }); }, // Save descriptions from config form to state saveConfig() { try { this.state.areaData.forEach(area => { const textarea = document.getElementById(`fslg-config-${area.id}`); if (textarea) { area.description = textarea.value; } }); localStorage.setItem('fslg_customData', JSON.stringify(this.state.areaData)); alert('Your custom descriptions have been saved!'); this.drawBaguaMap(); // Update dashboard this.switchTab('dashboard'); // Move to dashboard } catch (e) { console.error("Error saving data:", e); alert("Could not save data. Local storage might be full or disabled."); } }, // Reset data to defaults resetConfig() { if (confirm("Are you sure you want to reset all descriptions to their defaults?")) { this.state.areaData = JSON.parse(JSON.stringify(this.defaultData)); localStorage.removeItem('fslg_customData'); this.loadConfigForm(); this.drawBaguaMap(); alert("Descriptions reset to default."); } }, // Load data from local storage or use defaults loadState() { try { const customData = localStorage.getItem('fslg_customData'); if (customData) { this.state.areaData = JSON.parse(customData); } else { // Use a deep copy of the default data this.state.areaData = JSON.parse(JSON.stringify(this.defaultData)); } } catch (e) { console.error("Error loading state:", e); this.state.areaData = JSON.parse(JSON.stringify(this.defaultData)); } }, // Tab Navigation switchTab(tabId) { this.state.currentTab = tabId; document.querySelectorAll('.fslg-tab-content').forEach(c => c.classList.remove('active')); document.querySelectorAll('.fslg-tab-button').forEach(b => b.classList.remove('active')); const content = document.getElementById(`fslg-tab-${tabId}`); const button = document.querySelector(`.fslg-tab-button[onclick="fslg_app.switchTab('${tabId}')"]`); if (content) content.classList.add('active'); if (button) button.classList.add('active'); this.updateNavButtons(); }, updateNavButtons() { const idx = this.state.tabs.indexOf(this.state.currentTab); const prevBtn = document.getElementById('fslg-prev-btn'); const nextBtn = document.getElementById('fslg-next-btn'); if (prevBtn) prevBtn.disabled = (idx === 0); if (nextBtn) nextBtn.disabled = (idx === this.state.tabs.length - 1); }, navigateTabs(dir) { const idx = this.state.tabs.indexOf(this.state.currentTab); const newIdx = idx + dir; if (newIdx >= 0 && newIdx < this.state.tabs.length) { this.switchTab(this.state.tabs[newIdx]); } }, // PDF Generation generatePDF() { const pdfButton = document.getElementById('fslg-download-pdf-btn'); if (!pdfButton) return; pdfButton.textContent = 'Generating...'; pdfButton.disabled = true; const contentToExport = document.getElementById('fslg-bagua-map-container'); const pdfTitle = contentToExport.querySelector('.fslg-pdf-export-only'); const pdfInstruction = document.getElementById('fslg-bagua-instruction'); if (pdfTitle) pdfTitle.style.display = 'block'; if (pdfInstruction) pdfInstruction.style.display = 'none'; html2canvas(contentToExport, { scale: 2, backgroundColor: '#ffffff' }).then(canvas => { // Restore UI if (pdfTitle) pdfTitle.style.display = 'none'; if (pdfInstruction) pdfInstruction.style.display = 'block'; const imgData = canvas.toDataURL('image/png'); const { jsPDF } = window.jspdf; // A4 size: 595.28 x 841.89 points const pdf = new jsPDF({ orientation: 'portrait', unit: 'pt', 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; let imgWidth = pdfWidth - 80; // 40pt margin on each side let imgHeight = imgWidth / ratio; // Check if height is too big if (imgHeight > pdfHeight - 80) { imgHeight = pdfHeight - 80; imgWidth = imgHeight * ratio; } const x = (pdfWidth - imgWidth) / 2; const y = 40; pdf.addImage(imgData, 'PNG', x, y, imgWidth, imgHeight); pdf.save('Your_Feng_Shui_Bagua_Map.pdf'); pdfButton.textContent = 'Download Map as PDF'; pdfButton.disabled = false; }).catch(err => { console.error("PDF generation failed:", err); if (pdfTitle) pdfTitle.style.display = 'none'; if (pdfInstruction) pdfInstruction.style.display = 'block'; pdfButton.textContent = 'Download Map as PDF'; pdfButton.disabled = false; alert("Error generating PDF. Please try again."); }); }, // Initialization init() { // Check for required libraries if (typeof jspdf === 'undefined' || typeof html2canvas === 'undefined') { console.error("PDF generation libraries (jsPDF, html2canvas) not found."); const pdfBtn = document.getElementById('fslg-download-pdf-btn'); if(pdfBtn) { pdfBtn.textContent = "PDF Libraries Missing"; pdfBtn.disabled = true; } } // Load data this.loadState(); // Initial render this.drawBaguaMap(); this.loadConfigForm(); this.updateNavButtons(); // Attach Event Listeners document.getElementById('fslg-save-config-btn')?.addEventListener('click', () => this.saveConfig()); document.getElementById('fslg-reset-config-btn')?.addEventListener('click', () => this.resetConfig()); document.getElementById('fslg-download-pdf-btn')?.addEventListener('click', () => this.generatePDF()); document.getElementById('fslg-next-btn')?.addEventListener('click', () => this.navigateTabs(1)); document.getElementById('fslg-prev-btn')?.addEventListener('click', () => this.navigateTabs(-1)); } }; // Wait for the DOM to be fully loaded before initializing the app document.addEventListener('DOMContentLoaded', () => { const container = document.getElementById('fslg-app-container'); if (container && !container.hasAttribute('data-initialized')) { container.setAttribute('data-initialized', 'true'); fslg_app.init(); } });