/** * Koozali SME Server Table Fade Transitions * * This script creates smooth fade transitions between tables * with 1-2 second fade-in and fade-out effects. * * Works with the koozali_table_styles.css file. * Compatible with all modern browsers and IE9+. * * FIXED VERSION: Ensures table selector is always created at the top */ document.addEventListener('DOMContentLoaded', function() { // Find all tables with class 'node' or 'node-unclaimed' var tables = document.querySelectorAll('table.node, table.node-unclaimed'); if (!tables || tables.length === 0) { console.log('No tables found with class "node" or "node-unclaimed"'); return; } // Create container for the dropdown and controls var navContainer = document.createElement('div'); navContainer.className = 'koozali-table-nav'; // Set styles with fallbacks for older browsers var navStyles = { 'position': 'sticky', 'top': '0', 'background-color': '#e6f2d5', 'padding': '10px', 'margin-bottom': '20px', 'z-index': '100', 'border-bottom': '1px solid #d5e5c0', 'display': 'flex', 'justify-content': 'space-between', 'align-items': 'center', 'box-shadow': '0 2px 4px rgba(0,0,0,0.1)', 'max-width': '680px' }; // Apply styles with vendor prefixes for (var prop in navStyles) { if (navStyles.hasOwnProperty(prop)) { navContainer.style.setProperty(prop, navStyles[prop]); // Add vendor prefixes for key properties if (prop === 'position' && navStyles[prop] === 'sticky') { navContainer.style.setProperty('-webkit-position', 'sticky'); navContainer.style.setProperty('-moz-position', 'sticky'); navContainer.style.setProperty('-ms-position', 'sticky'); } if (prop === 'display' && navStyles[prop] === 'flex') { navContainer.style.setProperty('-webkit-display', 'flex'); navContainer.style.setProperty('-moz-display', 'flex'); navContainer.style.setProperty('-ms-display', 'flex'); } if (prop === 'justify-content') { navContainer.style.setProperty('-webkit-justify-content', navStyles[prop]); navContainer.style.setProperty('-moz-justify-content', navStyles[prop]); navContainer.style.setProperty('-ms-justify-content', navStyles[prop]); } if (prop === 'align-items') { navContainer.style.setProperty('-webkit-align-items', navStyles[prop]); navContainer.style.setProperty('-moz-align-items', navStyles[prop]); navContainer.style.setProperty('-ms-align-items', navStyles[prop]); } } } // Create the select element var select = document.createElement('select'); select.className = 'koozali-table-select'; // Set select styles var selectStyles = { 'padding': '8px', 'font-size': '16px', 'border': '1px solid #d5e5c0', 'border-radius': '4px', 'background-color': '#f5f9ef', 'cursor': 'pointer', 'margin-right': '10px', 'color': '#333', 'width': '500px', 'max-width': '500px' }; // Apply select styles for (var prop in selectStyles) { if (selectStyles.hasOwnProperty(prop)) { select.style.setProperty(prop, selectStyles[prop]); } } // Add default option var defaultOption = document.createElement('option'); defaultOption.value = ''; defaultOption.textContent = '-- Select a table --'; select.appendChild(defaultOption); // Create "Show All" button var showAllButton = document.createElement('button'); showAllButton.textContent = 'Show All Tables'; showAllButton.className = 'koozali-show-all-button'; // Set button styles var buttonStyles = { 'padding': '8px 12px', 'background-color': '#006600', 'color': 'white', 'border': 'none', 'border-radius': '4px', 'cursor': 'pointer', 'font-weight': 'bold', 'box-shadow': '0 1px 3px rgba(0,0,0,0.1)' }; // Apply button styles for (var prop in buttonStyles) { if (buttonStyles.hasOwnProperty(prop)) { showAllButton.style.setProperty(prop, buttonStyles[prop]); } } // Add hover effect to button (with IE fallback) function setButtonHoverState(isHover) { showAllButton.style.backgroundColor = isHover ? '#008800' : '#006600'; } if (showAllButton.addEventListener) { showAllButton.addEventListener('mouseover', function() { setButtonHoverState(true); }); showAllButton.addEventListener('mouseout', function() { setButtonHoverState(false); }); } else if (showAllButton.attachEvent) { showAllButton.attachEvent('onmouseover', function() { setButtonHoverState(true); }); showAllButton.attachEvent('onmouseout', function() { setButtonHoverState(false); }); } // Process each table and add to dropdown for (var i = 0; i < tables.length; i++) { var table = tables[i]; // Try to get table ID from the first row if it contains "id:" var tableId = 'table-' + i; var tableName = 'Table ' + (i + 1); // Look for the ID in the table header var idCell = table.querySelector('thead td.first'); var idValue = table.querySelector('thead td.second'); if (idCell && idValue && idCell.textContent.trim().toLowerCase() === 'id:') { // Get the ID value (might be in a div with class 'id') var idDiv = idValue.querySelector('.id'); if (idDiv) { tableId = idDiv.textContent.trim(); tableName = tableId; } else if (idValue.textContent.trim()) { tableId = idValue.textContent.trim(); tableName = tableId; } } // Try to get a better name from the description if available var descRows = table.querySelectorAll('tbody tr'); for (var j = 0; j < descRows.length; j++) { var descRow = descRows[j]; var descCell = descRow.querySelector('td.first'); var descValue = descRow.querySelector('td.second'); if (descCell && descValue && descCell.textContent.trim().toLowerCase().includes('description') && descValue.textContent.trim()) { tableName = descValue.textContent.trim() + ' (' + tableName + ')'; break; } } // Create a unique ID for the table if it doesn't have one if (!table.id) { // Clean the ID to make it valid for HTML var safeId = tableId.replace(/[^a-z0-9]/gi, '_').toLowerCase(); table.id = safeId; } // Add option to dropdown var option = document.createElement('option'); option.value = table.id; option.textContent = tableName; select.appendChild(option); // Add wrapper div around the table for visibility control var wrapper = document.createElement('div'); wrapper.className = 'table-visibility-wrapper'; wrapper.id = 'wrapper-' + table.id; // Set transition duration for fade effects wrapper.style.transition = 'opacity 1.5s ease, height 1.5s ease'; wrapper.style.WebkitTransition = 'opacity 1.5s ease, height 1.5s ease'; wrapper.style.MozTransition = 'opacity 1.5s ease, height 1.5s ease'; wrapper.style.OTransition = 'opacity 1.5s ease, height 1.5s ease'; wrapper.style.msTransition = 'opacity 1.5s ease, height 1.5s ease'; // Move the table into the wrapper table.parentNode.insertBefore(wrapper, table); wrapper.appendChild(table); // Set initial state - only first table visible if (i === 0) { // First table is visible wrapper.style.opacity = '1'; wrapper.style.display = 'block'; wrapper.style.height = 'auto'; wrapper.style.overflow = 'visible'; } else { // Other tables are hidden wrapper.style.opacity = '0'; wrapper.style.display = 'none'; wrapper.style.height = '0'; wrapper.style.overflow = 'hidden'; } } // Add the select and button to the container navContainer.appendChild(select); navContainer.appendChild(showAllButton); // Insert the container at the top of the body or after the first heading var heading = document.querySelector('h1, h2'); if (heading && heading.parentNode) { heading.parentNode.insertBefore(navContainer, heading.nextSibling); } else { // If no heading found, insert at the beginning of the main content area or body var mainContent = document.querySelector('.main-content, main, #content, #main'); if (mainContent) { if (mainContent.firstChild) { mainContent.insertBefore(navContainer, mainContent.firstChild); } else { mainContent.appendChild(navContainer); } } else { // Last resort: insert at the beginning of the body var body = document.body; if (body.firstChild) { body.insertBefore(navContainer, body.firstChild); } else { body.appendChild(navContainer); } } } // Handle dropdown change with fade transitions select.addEventListener('change', function() { var selectedId = this.value; // First fade out all tables fadeOutAllTables(function() { // Then show only the selected table or all tables if none selected var wrappers = document.querySelectorAll('.table-visibility-wrapper'); for (var i = 0; i < wrappers.length; i++) { var wrapper = wrappers[i]; if (!selectedId || wrapper.id === 'wrapper-' + selectedId) { // Show and fade in this table fadeInTable(wrapper); } else { // Keep this table hidden wrapper.style.opacity = '0'; wrapper.style.display = 'none'; wrapper.style.height = '0'; wrapper.style.overflow = 'hidden'; } } }); }); // Handle show all button with fade transitions showAllButton.addEventListener('click', function() { // First fade out all tables fadeOutAllTables(function() { // Then fade in all tables var wrappers = document.querySelectorAll('.table-visibility-wrapper'); for (var i = 0; i < wrappers.length; i++) { fadeInTable(wrappers[i]); } // Reset the dropdown selection select.value = ''; }); }); // Function to fade out all tables and then execute a callback function fadeOutAllTables(callback) { var wrappers = document.querySelectorAll('.table-visibility-wrapper'); var visibleTables = 0; var completedFades = 0; // Count visible tables for (var i = 0; i < wrappers.length; i++) { if (getComputedStyle(wrappers[i]).display !== 'none') { visibleTables++; } } // If no tables are visible, just run the callback if (visibleTables === 0) { callback(); return; } // Fade out each visible table for (var i = 0; i < wrappers.length; i++) { var wrapper = wrappers[i]; if (getComputedStyle(wrapper).display !== 'none') { // Start fade out wrapper.style.opacity = '0'; // Use a closure to capture the current wrapper (function(currentWrapper) { // Set a timeout for when the fade completes var fadeTimer = setTimeout(function() { currentWrapper.style.display = 'none'; currentWrapper.style.height = '0'; currentWrapper.style.overflow = 'hidden'; // Increment completed fades counter completedFades++; // If all visible tables have faded out, run the callback if (completedFades >= visibleTables) { callback(); } }, 1500); // Match the CSS transition duration // Store the timer on the element to allow cancellation if needed currentWrapper._fadeTimer = fadeTimer; })(wrapper); } } } // Function to fade in a table function fadeInTable(wrapper) { // Cancel any pending fade out timer if (wrapper._fadeTimer) { clearTimeout(wrapper._fadeTimer); wrapper._fadeTimer = null; } // First make it visible but transparent wrapper.style.opacity = '0'; wrapper.style.display = 'block'; wrapper.style.height = 'auto'; wrapper.style.overflow = 'visible'; // Force a reflow to ensure the browser registers the display change void wrapper.offsetWidth; // Then fade it in setTimeout(function() { wrapper.style.opacity = '1'; }, 50); // Small delay to ensure the browser has processed the display change } // Handle browser compatibility for older browsers function isBrowserIE() { return navigator.userAgent.indexOf('MSIE') !== -1 || navigator.userAgent.indexOf('Trident/') !== -1; } // Apply special handling for IE if (isBrowserIE()) { // For IE, use a step-based fade fadeOutAllTables = function(callback) { var wrappers = document.querySelectorAll('.table-visibility-wrapper'); var visibleTables = 0; var completedFades = 0; // Count visible tables for (var i = 0; i < wrappers.length; i++) { if (wrappers[i].style.display !== 'none') { visibleTables++; } } if (visibleTables === 0) { callback(); return; } // For IE, use a step-based fade for (var i = 0; i < wrappers.length; i++) { var wrapper = wrappers[i]; if (wrapper.style.display !== 'none') { (function(currentWrapper) { var opacity = 1; var fadeInterval = setInterval(function() { opacity -= 0.1; if (opacity <= 0) { clearInterval(fadeInterval); currentWrapper.style.opacity = '0'; currentWrapper.style.display = 'none'; currentWrapper.style.height = '0'; currentWrapper.style.overflow = 'hidden'; completedFades++; if (completedFades >= visibleTables) { callback(); } } else { currentWrapper.style.opacity = opacity.toString(); } }, 150); // 10 steps over 1.5 seconds })(wrapper); } } }; fadeInTable = function(wrapper) { wrapper.style.opacity = '0'; wrapper.style.display = 'block'; wrapper.style.height = 'auto'; wrapper.style.overflow = 'visible'; var opacity = 0; var fadeInterval = setInterval(function() { opacity += 0.1; if (opacity >= 1) { clearInterval(fadeInterval); wrapper.style.opacity = '1'; } else { wrapper.style.opacity = opacity.toString(); } }, 150); // 10 steps over 1.5 seconds }; } });