mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 19:01:57 -04:00
Auto update
This commit is contained in:
parent
e7391dbbfe
commit
f4679d3f6e
125 changed files with 17882 additions and 48238 deletions
374
script.js
Normal file
374
script.js
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
// Theme Switcher Logic
|
||||
(function() {
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
const prefersLight = window.matchMedia('(prefers-color-scheme: light)').matches;
|
||||
|
||||
if (savedTheme === 'light' || (!savedTheme && prefersLight)) {
|
||||
document.documentElement.classList.add('light-theme');
|
||||
}
|
||||
})();
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Theme Switcher Event Listeners
|
||||
const themeToggles = document.querySelectorAll('.theme-toggle');
|
||||
|
||||
function updateThemeImages(isLight) {
|
||||
const updateUrl = (url) => {
|
||||
if (!url) return url;
|
||||
const isTarget = url.includes('desk-') || url.includes('phone-');
|
||||
if (!isTarget) return url;
|
||||
|
||||
if (isLight) {
|
||||
if (!url.includes('-light.png')) {
|
||||
return url.replace('.png', '-light.png');
|
||||
}
|
||||
} else {
|
||||
if (url.includes('-light.png')) {
|
||||
return url.replace('-light.png', '.png');
|
||||
}
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
document.querySelectorAll('img').forEach(img => {
|
||||
const src = img.getAttribute('src');
|
||||
if (src) {
|
||||
const newSrc = updateUrl(src);
|
||||
if (newSrc !== src) img.setAttribute('src', newSrc);
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('.ui-shot').forEach(shot => {
|
||||
const largeSrc = shot.getAttribute('data-large');
|
||||
if (largeSrc) {
|
||||
const newSrc = updateUrl(largeSrc);
|
||||
if (newSrc !== largeSrc) shot.setAttribute('data-large', newSrc);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Initial call to set correct images based on current theme
|
||||
updateThemeImages(document.documentElement.classList.contains('light-theme'));
|
||||
|
||||
themeToggles.forEach(toggle => {
|
||||
toggle.addEventListener('click', () => {
|
||||
document.documentElement.classList.add('theme-transitioning');
|
||||
document.documentElement.classList.toggle('light-theme');
|
||||
|
||||
// Force reflow to apply styles immediately without transition
|
||||
void document.documentElement.offsetHeight;
|
||||
|
||||
setTimeout(() => {
|
||||
document.documentElement.classList.remove('theme-transitioning');
|
||||
}, 0);
|
||||
|
||||
const isLight = document.documentElement.classList.contains('light-theme');
|
||||
localStorage.setItem('theme', isLight ? 'light' : 'dark');
|
||||
updateThemeImages(isLight);
|
||||
});
|
||||
});
|
||||
|
||||
// Generate TOC for FAQ page
|
||||
const faqTocList = document.getElementById('faq-toc-list');
|
||||
const faqContent = document.getElementById('faq-content');
|
||||
|
||||
if (faqTocList && faqContent) {
|
||||
const sections = faqContent.querySelectorAll('.faq-section');
|
||||
sections.forEach(section => {
|
||||
const h2 = section.querySelector('h2');
|
||||
if (h2) {
|
||||
// Create H2 Link
|
||||
const li = document.createElement('li');
|
||||
const a = document.createElement('a');
|
||||
a.setAttribute('href', '#' + h2.id);
|
||||
a.textContent = h2.textContent;
|
||||
li.appendChild(a);
|
||||
faqTocList.appendChild(li);
|
||||
|
||||
// Create H3 Links (Questions)
|
||||
const questions = section.querySelectorAll('.faq-question');
|
||||
if (questions.length > 0) {
|
||||
const subUl = document.createElement('ul');
|
||||
questions.forEach(q => {
|
||||
const subLi = document.createElement('li');
|
||||
const subA = document.createElement('a');
|
||||
subA.setAttribute('href', '#' + q.id);
|
||||
subA.textContent = q.textContent;
|
||||
subLi.appendChild(subA);
|
||||
subUl.appendChild(subLi);
|
||||
});
|
||||
li.appendChild(subUl);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Generate TOC for Manual page
|
||||
const manualTocList = document.getElementById('manual-toc-list');
|
||||
const manualContainer = document.getElementById('manual-container');
|
||||
|
||||
if (manualTocList && manualContainer) {
|
||||
// Query specific manual title classes
|
||||
const headers = manualContainer.querySelectorAll('.manual-title-level-1, .manual-title-level-2, .manual-title-level-3');
|
||||
|
||||
let currentLevel1 = null;
|
||||
let currentLevel2Ul = null;
|
||||
|
||||
headers.forEach(header => {
|
||||
// Determine level based on class name
|
||||
let level = 1;
|
||||
if (header.classList.contains('manual-title-level-2')) level = 2;
|
||||
if (header.classList.contains('manual-title-level-3')) level = 3;
|
||||
|
||||
if (level === 1) {
|
||||
const li = document.createElement('li');
|
||||
const a = document.createElement('a');
|
||||
a.setAttribute('href', '#' + header.id);
|
||||
a.textContent = header.textContent;
|
||||
li.appendChild(a);
|
||||
manualTocList.appendChild(li);
|
||||
|
||||
currentLevel1 = li;
|
||||
currentLevel2Ul = null;
|
||||
}
|
||||
else if (level === 2 && currentLevel1) {
|
||||
if (!currentLevel2Ul) {
|
||||
currentLevel2Ul = document.createElement('ul');
|
||||
currentLevel1.appendChild(currentLevel2Ul);
|
||||
}
|
||||
const li = document.createElement('li');
|
||||
const a = document.createElement('a');
|
||||
a.setAttribute('href', '#' + header.id);
|
||||
a.textContent = header.textContent;
|
||||
li.appendChild(a);
|
||||
currentLevel2Ul.appendChild(li);
|
||||
}
|
||||
// Add level 3 handling if needed later
|
||||
});
|
||||
}
|
||||
|
||||
// Mobile menu closes when a link is clicked
|
||||
const links = document.querySelectorAll('nav a');
|
||||
const checkbox = document.getElementById('menu-toggle');
|
||||
|
||||
links.forEach(link => {
|
||||
link.addEventListener('click', () => {
|
||||
if (checkbox) checkbox.checked = false;
|
||||
});
|
||||
});
|
||||
|
||||
// Simple smooth scroll for anchor links
|
||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||
anchor.addEventListener('click', function (e) {
|
||||
const href = this.getAttribute('href');
|
||||
// Only handle valid hash links
|
||||
if (href && href.startsWith('#') && href.length > 1) {
|
||||
const id = href.substring(1);
|
||||
// Use getElementById which is more robust than querySelector for IDs with special chars
|
||||
const target = document.getElementById(id);
|
||||
|
||||
if (target) {
|
||||
e.preventDefault();
|
||||
target.scrollIntoView({
|
||||
behavior: 'smooth'
|
||||
});
|
||||
// Update URL hash without jumping
|
||||
history.pushState(null, null, href);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// FAQ Search functionality for .faq-item structure
|
||||
const faqSearch = document.getElementById('faq-search');
|
||||
if (faqSearch) {
|
||||
faqSearch.addEventListener('input', (e) => {
|
||||
const term = e.target.value.toLowerCase();
|
||||
const container = document.getElementById('faq-content');
|
||||
const tocList = document.getElementById('faq-toc-list');
|
||||
|
||||
if (!container) return;
|
||||
|
||||
const items = container.querySelectorAll('.faq-item');
|
||||
|
||||
items.forEach(item => {
|
||||
const questionHeader = item.querySelector('.faq-question');
|
||||
const question = questionHeader.textContent.toLowerCase();
|
||||
const answer = item.querySelector('.faq-answer').textContent.toLowerCase();
|
||||
|
||||
const isMatch = question.includes(term) || answer.includes(term);
|
||||
|
||||
// Toggle display of the entire item in main content
|
||||
item.style.display = isMatch ? '' : 'none';
|
||||
|
||||
// Toggle display of TOC item corresponding to this question
|
||||
if (tocList && questionHeader.id) {
|
||||
const tocLink = tocList.querySelector(`a[href="#${CSS.escape(questionHeader.id)}"]`);
|
||||
if (tocLink) {
|
||||
const tocItem = tocLink.parentElement;
|
||||
tocItem.style.display = isMatch ? '' : 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-hide sections if all their items are hidden
|
||||
const sections = container.querySelectorAll('.faq-section');
|
||||
sections.forEach(section => {
|
||||
const visibleItems = section.querySelectorAll('.faq-item:not([style*="display: none"])');
|
||||
const isSectionVisible = visibleItems.length > 0;
|
||||
|
||||
section.style.display = isSectionVisible ? '' : 'none';
|
||||
|
||||
// Toggle display of TOC section
|
||||
const sectionHeader = section.querySelector('h2');
|
||||
if (tocList && sectionHeader && sectionHeader.id) {
|
||||
const tocLink = tocList.querySelector(`a[href="#${CSS.escape(sectionHeader.id)}"]`);
|
||||
if (tocLink) {
|
||||
// The section link is directly inside the top-level li of the TOC
|
||||
const tocSectionItem = tocLink.parentElement;
|
||||
tocSectionItem.style.display = isSectionVisible ? '' : 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Language Selector functionality
|
||||
const languageSelector = document.getElementById('language-selector');
|
||||
if (languageSelector) {
|
||||
// Automatically select the option matching the current file name
|
||||
const currentFile = window.location.pathname.split('/').pop() || 'manual.html';
|
||||
const options = Array.from(languageSelector.options);
|
||||
const matchingOption = options.find(opt => opt.value === currentFile);
|
||||
if (matchingOption) {
|
||||
languageSelector.value = currentFile;
|
||||
}
|
||||
|
||||
// Redirect on change
|
||||
languageSelector.addEventListener('change', (e) => {
|
||||
const selectedPage = e.target.value;
|
||||
if (selectedPage) {
|
||||
window.location.href = selectedPage;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Manual Section Selector functionality
|
||||
const manualSelector = document.getElementById('manual-selector');
|
||||
if (manualSelector) {
|
||||
const currentFile = window.location.pathname.split('/').pop() || 'manual.html';
|
||||
const options = Array.from(manualSelector.options);
|
||||
const matchingOption = options.find(opt => opt.value === currentFile);
|
||||
|
||||
if (matchingOption) {
|
||||
manualSelector.value = currentFile;
|
||||
} else {
|
||||
// Default to first option (CLI) if current file doesn't match specific sections
|
||||
// This handles cases like manual-it.html where we want "CLI" selected
|
||||
if (currentFile.includes('manual') && !currentFile.includes('daemon') && !currentFile.includes('log')) {
|
||||
const cliOption = options.find(opt => opt.value === 'manual.html');
|
||||
if (cliOption) manualSelector.value = 'manual.html';
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect on change
|
||||
manualSelector.addEventListener('change', (e) => {
|
||||
const selectedPage = e.target.value;
|
||||
if (selectedPage) {
|
||||
window.location.href = selectedPage;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// UI Gallery Preview on Click
|
||||
const shots = document.querySelectorAll('.ui-shot');
|
||||
const overlay = document.getElementById('ui-preview-overlay');
|
||||
const previewImg = document.getElementById('ui-preview-img');
|
||||
|
||||
if (overlay && previewImg) {
|
||||
shots.forEach(shot => {
|
||||
shot.addEventListener('click', () => {
|
||||
const largeSrc = shot.getAttribute('data-large');
|
||||
if (largeSrc) {
|
||||
previewImg.src = largeSrc;
|
||||
overlay.classList.add('active');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Close when clicking on the overlay
|
||||
overlay.addEventListener('click', () => {
|
||||
overlay.classList.remove('active');
|
||||
});
|
||||
}
|
||||
|
||||
// Global Popover Logic for Compare Table
|
||||
const infoBtns = document.querySelectorAll('.info-btn');
|
||||
const globalPopover = document.getElementById('global-popover');
|
||||
const globalPopoverContent = document.getElementById('global-popover-content');
|
||||
const popoverOverlay = document.getElementById('popover-overlay');
|
||||
|
||||
if (infoBtns.length > 0 && globalPopover && popoverOverlay) {
|
||||
|
||||
function closePopover() {
|
||||
globalPopover.classList.remove('active');
|
||||
popoverOverlay.classList.remove('active');
|
||||
globalPopover.style.opacity = ''; // reset just in case
|
||||
}
|
||||
|
||||
popoverOverlay.addEventListener('click', closePopover);
|
||||
|
||||
infoBtns.forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
// Get content from the hidden sibling div
|
||||
const contentDiv = btn.parentElement.querySelector('.info-content');
|
||||
if (!contentDiv) return;
|
||||
|
||||
globalPopoverContent.innerHTML = contentDiv.innerHTML;
|
||||
|
||||
// Measure the popover by making it transparent but blocked
|
||||
globalPopover.style.opacity = '0';
|
||||
globalPopover.classList.add('active');
|
||||
|
||||
const popWidth = globalPopover.offsetWidth;
|
||||
const popHeight = globalPopover.offsetHeight;
|
||||
|
||||
globalPopover.classList.remove('active');
|
||||
globalPopover.style.opacity = '';
|
||||
|
||||
// Position the popover
|
||||
const rect = btn.getBoundingClientRect();
|
||||
const scrollX = window.pageXOffset || document.documentElement.scrollLeft;
|
||||
const scrollY = window.pageYOffset || document.documentElement.scrollTop;
|
||||
|
||||
// Calculate position (centered above the button by default)
|
||||
let top = rect.top + scrollY - popHeight - 15;
|
||||
let left = rect.left + scrollX - (popWidth / 2) + (rect.width / 2);
|
||||
|
||||
// Simple collision detection
|
||||
if (rect.top - popHeight - 20 < 0) {
|
||||
// flip to bottom
|
||||
top = rect.bottom + scrollY + 15;
|
||||
}
|
||||
|
||||
// ensure left is on screen
|
||||
if (left < 10) left = 10;
|
||||
if (left + popWidth > window.innerWidth) {
|
||||
left = window.innerWidth - popWidth - 10;
|
||||
}
|
||||
|
||||
globalPopover.style.top = `${top}px`;
|
||||
globalPopover.style.left = `${left}px`;
|
||||
|
||||
globalPopover.classList.add('active');
|
||||
popoverOverlay.classList.add('active');
|
||||
});
|
||||
});
|
||||
|
||||
// Close on resize to prevent weird positioning
|
||||
window.addEventListener('resize', closePopover);
|
||||
}
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue