diff --git a/beango/index.html b/beango/index.html index a3474d8..de4ea93 100644 --- a/beango/index.html +++ b/beango/index.html @@ -16,9 +16,14 @@
-
-

Beango!

- Bean + +
+ +
+ +

Beango!

+ Bean +
@@ -65,6 +70,32 @@
+ + +
+ +
+ + +
+
+ + +

The bean image will appear next to the text.

+
+ +
+ +
diff --git a/beango/js/beango.js b/beango/js/beango.js index 65211a7..0076fc2 100644 --- a/beango/js/beango.js +++ b/beango/js/beango.js @@ -14,12 +14,17 @@ const LS_GRADIENT_COLOR_1 = 'beango_gradientColor1'; const LS_GRADIENT_COLOR_2 = 'beango_gradientColor2'; const LS_GRADIENT_DIRECTION = 'beango_gradientDirection'; const LS_ORIGINAL_ITEMS = 'beango_originalItems'; // User's raw input +const LS_HEADER_TYPE = 'beango_headerType'; // 'text' or 'image' +const LS_HEADER_CONTENT = 'beango_headerContent'; // Text string or Image URL // --- Default Values --- const DEFAULT_SOLID_COLOR = '#ff7e5f'; // Default to first color of gradient const DEFAULT_GRADIENT_COLOR_1 = '#ff7e5f'; // From main page gradient const DEFAULT_GRADIENT_COLOR_2 = '#feb47b'; // From main page gradient const DEFAULT_GRADIENT_DIRECTION = '135deg'; // From main page gradient +const DEFAULT_HEADER_TYPE = 'text'; +const DEFAULT_HEADER_TEXT = 'Beango!'; +const DEFAULT_HEADER_IMAGE_URL = ''; // No default image // --- Notification Function --- function showNotification(message, type = 'info', duration = 3000) { @@ -173,12 +178,15 @@ function saveBoardState() { const sizeInput = document.getElementById('board-size'); const size = sizeInput ? parseInt(sizeInput.value, 10) : 5; // Default to 5 if input missing const markedIndices = getMarkedIndices(); + const originalUserItems = getItemsFromInput(); // Get current text from textarea localStorage.setItem(LS_BOARD_SIZE, size); localStorage.setItem(LS_CELL_ITEMS, JSON.stringify(currentItems)); // Save items potentially on board (padded/sliced) localStorage.setItem(LS_DISPLAYED_ITEMS, JSON.stringify(displayedItems)); // Save displayed items localStorage.setItem(LS_MARKED_INDICES, JSON.stringify(markedIndices)); - localStorage.setItem(LS_ORIGINAL_ITEMS, JSON.stringify(getItemsFromInput())); // Save user's raw input + localStorage.setItem(LS_ORIGINAL_ITEMS, JSON.stringify(originalUserItems)); // Save user's raw input from textarea + saveBackgroundSettings(); // Save background settings from inputs + saveHeaderSettings(); // Save header settings from inputs } function generateBoard() { @@ -192,7 +200,7 @@ function generateBoard() { // Get items from input and store them as the canonical list const originalUserItems = getItemsFromInput(); - localStorage.setItem(LS_ORIGINAL_ITEMS, JSON.stringify(originalUserItems)); // Save user's raw input + // localStorage.setItem(LS_ORIGINAL_ITEMS, JSON.stringify(originalUserItems)); // Moved to saveBoardState const requiredItems = size * size; let itemsForBoard = [...originalUserItems]; // Start with a copy of the user's raw input @@ -220,7 +228,7 @@ function generateBoard() { } // Save the items that are actually available for the board (potentially padded/sliced) - localStorage.setItem(LS_CELL_ITEMS, JSON.stringify(currentItems)); + // localStorage.setItem(LS_CELL_ITEMS, JSON.stringify(currentItems)); // Moved to saveBoardState // Shuffle the items specifically for display (use currentItems which has the right size) displayedItems = shuffleArray([...currentItems]); @@ -240,10 +248,10 @@ function generateBoard() { clearMarks(false); // Clear previous marks visually but don't save yet equalizeCellSizes(); // Add this call - saveBoardState(); // Save the newly generated board state (size, items, displayed, marks) + // saveBoardState(); // REMOVED first call - Save the newly generated board state (size, items, displayed, marks) - // Explicitly save background settings before reload - saveBackgroundSettings(); + // Explicitly save background settings before reload - NO, save everything together + // saveBackgroundSettings(); // Get the new background value and inject a style override before reload const newBackgroundValue = setBackground(); // Apply and get value @@ -258,6 +266,9 @@ function generateBoard() { showNotification(notificationMessage, notificationType); + // *** Save the complete final state just before reload *** + saveBoardState(); // Includes size, items, displayed, marks, original items, background, header + // Reload the page to ensure correct rendering based on saved state location.reload(); @@ -278,6 +289,13 @@ function generateBoard() { document.getElementById('gradient-direction').value = DEFAULT_GRADIENT_DIRECTION; toggleBackgroundControls(); // Ensure correct controls are visible + // Reset header inputs to defaults + document.querySelector('input[name="header-type"][value="text"]').checked = true; + document.getElementById('header-text-input').value = DEFAULT_HEADER_TEXT; + document.getElementById('header-image-url-input').value = DEFAULT_HEADER_IMAGE_URL; + toggleHeaderInputs(); // Ensure correct header inputs are visible + updateHeaderDisplay(); // Apply default header display + // Clear the board display const boardHeader = document.getElementById('board-header'); if (boardHeader) boardHeader.style.maxWidth = ''; @@ -398,6 +416,13 @@ function resetSettings() { document.getElementById('gradient-direction').value = DEFAULT_GRADIENT_DIRECTION; toggleBackgroundControls(); // Ensure correct controls are visible + // Reset header inputs to defaults + document.querySelector('input[name="header-type"][value="text"]').checked = true; + document.getElementById('header-text-input').value = DEFAULT_HEADER_TEXT; + document.getElementById('header-image-url-input').value = DEFAULT_HEADER_IMAGE_URL; + toggleHeaderInputs(); // Ensure correct header inputs are visible + updateHeaderDisplay(); // Apply default header display + // Clear the board display const board = document.getElementById('bingo-board'); board.innerHTML = '
Settings Reset. Generate a new board!
'; @@ -443,6 +468,8 @@ function loadFromLocalStorage() { const savedMarkedIndices = localStorage.getItem(LS_MARKED_INDICES); const configIsOpen = localStorage.getItem(LS_CONFIG_OPEN) === 'true'; const savedOriginalItemsText = localStorage.getItem(LS_ORIGINAL_ITEMS); + const savedHeaderType = localStorage.getItem(LS_HEADER_TYPE) || DEFAULT_HEADER_TYPE; + const savedHeaderContent = localStorage.getItem(LS_HEADER_CONTENT) || (savedHeaderType === 'text' ? DEFAULT_HEADER_TEXT : DEFAULT_HEADER_IMAGE_URL); // Restore Config Pane State const pane = document.getElementById('config-pane'); @@ -530,6 +557,17 @@ function loadFromLocalStorage() { setBackground(); // --- End Restore Background Settings --- + // --- Restore Header Settings --- + document.querySelector(`input[name="header-type"][value="${savedHeaderType}"]`).checked = true; + if (savedHeaderType === 'text') { + document.getElementById('header-text-input').value = savedHeaderContent; + } else { + document.getElementById('header-image-url-input').value = savedHeaderContent; + } + toggleHeaderInputs(); // Show/hide the correct input fields + updateHeaderDisplay(); // Apply the loaded header + // --- End Restore Header Settings --- + // Restore the textarea with the original user input if available if (savedOriginalItemsText) { try { @@ -747,4 +785,118 @@ document.addEventListener('DOMContentLoaded', () => { setBackground(); saveBackgroundSettings(); }); + + // Header listeners + document.querySelectorAll('input[name="header-type"]').forEach(radio => { + radio.addEventListener('change', () => { + toggleHeaderInputs(); + // Update display immediately on type change - Order changed + saveHeaderSettings(); // Save the new type first + updateHeaderDisplay(); // Then update display from saved state + }); + }); + document.getElementById('header-text-input').addEventListener('input', () => { + // Order changed + saveHeaderSettings(); // Save the new text first + updateHeaderDisplay(); // Then update display from saved state + }); + document.getElementById('header-image-url-input').addEventListener('input', () => { + // Order changed + saveHeaderSettings(); // Save the new URL first + updateHeaderDisplay(); // Then update display from saved state + }); + + // Call equalizeCellSizes on resize and load + window.addEventListener('resize', equalizeCellSizes); + equalizeCellSizes(); // Initial call after load }); + +// --- Save current header settings to localStorage --- +function saveHeaderSettings() { + const headerType = document.querySelector('input[name="header-type"]:checked').value; + localStorage.setItem(LS_HEADER_TYPE, headerType); + + if (headerType === 'text') { + localStorage.setItem(LS_HEADER_CONTENT, document.getElementById('header-text-input').value); + } else { // image + localStorage.setItem(LS_HEADER_CONTENT, document.getElementById('header-image-url-input').value); + } +} + +// --- Function to manage visibility of header controls --- +function toggleHeaderInputs() { + const headerType = document.querySelector('input[name="header-type"]:checked').value; + const textSettings = document.getElementById('header-text-settings'); + const imageSettings = document.getElementById('header-image-settings'); + + if (headerType === 'text') { + textSettings.style.display = 'block'; + imageSettings.style.display = 'none'; + } else { // image + textSettings.style.display = 'none'; + imageSettings.style.display = 'block'; + } +} + +// --- Function to update the header display --- +function updateHeaderDisplay() { + const headerType = localStorage.getItem(LS_HEADER_TYPE) || DEFAULT_HEADER_TYPE; + const headerContent = localStorage.getItem(LS_HEADER_CONTENT) || (headerType === 'text' ? DEFAULT_HEADER_TEXT : DEFAULT_HEADER_IMAGE_URL); + const headerContainer = document.getElementById('custom-header-content'); + + if (!headerContainer) return; + + // Clear existing content + headerContainer.innerHTML = ''; + + if (headerType === 'text') { + // Create H1 for text + const h1 = document.createElement('h1'); + h1.className = 'text-4xl font-bold text-green-800'; + h1.textContent = headerContent || DEFAULT_HEADER_TEXT; // Fallback to default text + headerContainer.appendChild(h1); + + // Create and add bean image + const img = document.createElement('img'); + img.id = 'bean'; + img.src = '../bean.svg'; + img.alt = 'Bean'; + img.className = 'w-16 h-16 cursor-pointer'; // Updated class for size + img.onclick = explodeBeans; + headerContainer.appendChild(img); + } else { // image + if (headerContent) { + // Create img for custom image URL + const img = document.createElement('img'); + img.src = headerContent; + img.alt = 'Custom Header Image'; + // Add some basic styling for the custom image - adjust as needed + img.className = 'max-h-20 max-w-full object-contain'; // Limit height, allow natural width up to container + img.onerror = () => { // Handle broken image links + headerContainer.innerHTML = ''; // Clear the broken image attempt + const errorText = document.createElement('p'); + errorText.textContent = 'Could not load header image.'; + errorText.className = 'text-red-500 text-sm'; + headerContainer.appendChild(errorText); + // Optionally revert to default text header on error + // localStorage.setItem(LS_HEADER_TYPE, DEFAULT_HEADER_TYPE); + // localStorage.setItem(LS_HEADER_CONTENT, DEFAULT_HEADER_TEXT); + // updateHeaderDisplay(); + }; + headerContainer.appendChild(img); + } else { + // If image type is selected but URL is empty, show default text header + const h1 = document.createElement('h1'); + h1.className = 'text-4xl font-bold text-green-800'; + h1.textContent = DEFAULT_HEADER_TEXT; + headerContainer.appendChild(h1); + const beanImg = document.createElement('img'); + beanImg.id = 'bean'; + beanImg.src = '../bean.svg'; + beanImg.alt = 'Bean'; + beanImg.className = 'w-16 h-16 cursor-pointer'; + beanImg.onclick = explodeBeans; + headerContainer.appendChild(beanImg); + } + } +}