450 lines
17 KiB
JavaScript
450 lines
17 KiB
JavaScript
/**
|
|
* 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
|
|
};
|
|
}
|
|
});
|