305 lines
8.7 KiB
JavaScript
305 lines
8.7 KiB
JavaScript
|
|
/**
|
||
|
|
* DWN Window Manager - Website JavaScript
|
||
|
|
* Vanilla JS for interactivity
|
||
|
|
*/
|
||
|
|
|
||
|
|
// Mobile Navigation Toggle
|
||
|
|
function toggleNav() {
|
||
|
|
const navLinks = document.querySelector('.nav-links');
|
||
|
|
const toggle = document.querySelector('.nav-toggle');
|
||
|
|
|
||
|
|
navLinks.classList.toggle('active');
|
||
|
|
toggle.classList.toggle('active');
|
||
|
|
}
|
||
|
|
|
||
|
|
// Close mobile nav when clicking outside
|
||
|
|
document.addEventListener('click', function(e) {
|
||
|
|
const nav = document.querySelector('nav');
|
||
|
|
const navLinks = document.querySelector('.nav-links');
|
||
|
|
|
||
|
|
if (navLinks && navLinks.classList.contains('active')) {
|
||
|
|
if (!nav.contains(e.target)) {
|
||
|
|
navLinks.classList.remove('active');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Close mobile nav when clicking a link
|
||
|
|
document.querySelectorAll('.nav-links a').forEach(link => {
|
||
|
|
link.addEventListener('click', () => {
|
||
|
|
const navLinks = document.querySelector('.nav-links');
|
||
|
|
if (navLinks) {
|
||
|
|
navLinks.classList.remove('active');
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// Tab functionality
|
||
|
|
function showTab(tabId) {
|
||
|
|
// Hide all tab contents
|
||
|
|
document.querySelectorAll('.tab-content').forEach(content => {
|
||
|
|
content.classList.remove('active');
|
||
|
|
});
|
||
|
|
|
||
|
|
// Deactivate all tabs
|
||
|
|
document.querySelectorAll('.tab').forEach(tab => {
|
||
|
|
tab.classList.remove('active');
|
||
|
|
});
|
||
|
|
|
||
|
|
// Show selected tab content
|
||
|
|
const selectedContent = document.getElementById(tabId);
|
||
|
|
if (selectedContent) {
|
||
|
|
selectedContent.classList.add('active');
|
||
|
|
}
|
||
|
|
|
||
|
|
// Activate clicked tab
|
||
|
|
event.target.classList.add('active');
|
||
|
|
}
|
||
|
|
|
||
|
|
// FAQ Accordion
|
||
|
|
function toggleFaq(button) {
|
||
|
|
const faqItem = button.closest('.faq-item');
|
||
|
|
const isActive = faqItem.classList.contains('active');
|
||
|
|
|
||
|
|
// Close all FAQ items
|
||
|
|
document.querySelectorAll('.faq-item').forEach(item => {
|
||
|
|
item.classList.remove('active');
|
||
|
|
});
|
||
|
|
|
||
|
|
// Open clicked item if it wasn't already open
|
||
|
|
if (!isActive) {
|
||
|
|
faqItem.classList.add('active');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Copy to clipboard functionality
|
||
|
|
function copyCode(button) {
|
||
|
|
const codeBlock = button.closest('.code-header').nextElementSibling;
|
||
|
|
const code = codeBlock.querySelector('code');
|
||
|
|
|
||
|
|
if (code) {
|
||
|
|
const text = code.textContent;
|
||
|
|
|
||
|
|
navigator.clipboard.writeText(text).then(() => {
|
||
|
|
// Show feedback
|
||
|
|
const originalText = button.textContent;
|
||
|
|
button.textContent = 'Copied!';
|
||
|
|
button.style.background = 'var(--success)';
|
||
|
|
|
||
|
|
setTimeout(() => {
|
||
|
|
button.textContent = originalText;
|
||
|
|
button.style.background = '';
|
||
|
|
}, 2000);
|
||
|
|
}).catch(err => {
|
||
|
|
console.error('Failed to copy:', err);
|
||
|
|
button.textContent = 'Error';
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Shortcut search/filter functionality
|
||
|
|
function filterShortcuts() {
|
||
|
|
const searchInput = document.getElementById('shortcut-search');
|
||
|
|
if (!searchInput) return;
|
||
|
|
|
||
|
|
const filter = searchInput.value.toLowerCase();
|
||
|
|
const tables = document.querySelectorAll('.shortcuts-table');
|
||
|
|
|
||
|
|
tables.forEach(table => {
|
||
|
|
const rows = table.querySelectorAll('tbody tr');
|
||
|
|
let visibleCount = 0;
|
||
|
|
|
||
|
|
rows.forEach(row => {
|
||
|
|
const text = row.textContent.toLowerCase();
|
||
|
|
if (text.includes(filter)) {
|
||
|
|
row.style.display = '';
|
||
|
|
visibleCount++;
|
||
|
|
} else {
|
||
|
|
row.style.display = 'none';
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Show/hide the entire table section if no matches
|
||
|
|
const section = table.closest('.table-wrapper');
|
||
|
|
const header = section ? section.previousElementSibling : null;
|
||
|
|
|
||
|
|
if (section && header && header.tagName === 'H2') {
|
||
|
|
if (visibleCount === 0 && filter !== '') {
|
||
|
|
section.style.display = 'none';
|
||
|
|
header.style.display = 'none';
|
||
|
|
} else {
|
||
|
|
section.style.display = '';
|
||
|
|
header.style.display = '';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// Smooth scroll for anchor links
|
||
|
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||
|
|
anchor.addEventListener('click', function(e) {
|
||
|
|
const href = this.getAttribute('href');
|
||
|
|
if (href === '#') return;
|
||
|
|
|
||
|
|
e.preventDefault();
|
||
|
|
const target = document.querySelector(href);
|
||
|
|
|
||
|
|
if (target) {
|
||
|
|
const headerOffset = 80; // Account for fixed header
|
||
|
|
const elementPosition = target.getBoundingClientRect().top;
|
||
|
|
const offsetPosition = elementPosition + window.pageYOffset - headerOffset;
|
||
|
|
|
||
|
|
window.scrollTo({
|
||
|
|
top: offsetPosition,
|
||
|
|
behavior: 'smooth'
|
||
|
|
});
|
||
|
|
|
||
|
|
// Update URL without scrolling
|
||
|
|
history.pushState(null, null, href);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// Highlight active section in docs sidebar
|
||
|
|
function updateActiveSection() {
|
||
|
|
const sidebar = document.querySelector('.docs-sidebar');
|
||
|
|
if (!sidebar) return;
|
||
|
|
|
||
|
|
const sections = document.querySelectorAll('h2[id], h3[id]');
|
||
|
|
const links = sidebar.querySelectorAll('a[href^="#"]');
|
||
|
|
|
||
|
|
let currentSection = '';
|
||
|
|
const scrollPos = window.scrollY + 100;
|
||
|
|
|
||
|
|
sections.forEach(section => {
|
||
|
|
if (section.offsetTop <= scrollPos) {
|
||
|
|
currentSection = section.id;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
links.forEach(link => {
|
||
|
|
link.classList.remove('active');
|
||
|
|
if (link.getAttribute('href') === '#' + currentSection) {
|
||
|
|
link.classList.add('active');
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// Throttle scroll events
|
||
|
|
let scrollTimeout;
|
||
|
|
window.addEventListener('scroll', () => {
|
||
|
|
if (scrollTimeout) return;
|
||
|
|
|
||
|
|
scrollTimeout = setTimeout(() => {
|
||
|
|
updateActiveSection();
|
||
|
|
scrollTimeout = null;
|
||
|
|
}, 100);
|
||
|
|
});
|
||
|
|
|
||
|
|
// Header background on scroll
|
||
|
|
function updateHeaderBackground() {
|
||
|
|
const header = document.querySelector('header');
|
||
|
|
if (!header) return;
|
||
|
|
|
||
|
|
if (window.scrollY > 50) {
|
||
|
|
header.style.background = 'rgba(26, 26, 46, 0.98)';
|
||
|
|
} else {
|
||
|
|
header.style.background = 'rgba(26, 26, 46, 0.95)';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
window.addEventListener('scroll', updateHeaderBackground);
|
||
|
|
|
||
|
|
// Animate elements on scroll (intersection observer)
|
||
|
|
function initScrollAnimations() {
|
||
|
|
const observer = new IntersectionObserver((entries) => {
|
||
|
|
entries.forEach(entry => {
|
||
|
|
if (entry.isIntersecting) {
|
||
|
|
entry.target.classList.add('fade-in');
|
||
|
|
observer.unobserve(entry.target);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}, {
|
||
|
|
threshold: 0.1,
|
||
|
|
rootMargin: '0px 0px -50px 0px'
|
||
|
|
});
|
||
|
|
|
||
|
|
// Observe feature cards and other elements
|
||
|
|
document.querySelectorAll('.feature-card, .card, .comparison-card, .testimonial').forEach(el => {
|
||
|
|
el.style.opacity = '0';
|
||
|
|
observer.observe(el);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// Keyboard shortcut for search (/)
|
||
|
|
document.addEventListener('keydown', (e) => {
|
||
|
|
if (e.key === '/' && !['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName)) {
|
||
|
|
const searchInput = document.getElementById('shortcut-search');
|
||
|
|
if (searchInput) {
|
||
|
|
e.preventDefault();
|
||
|
|
searchInput.focus();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Escape to close search/nav
|
||
|
|
if (e.key === 'Escape') {
|
||
|
|
const searchInput = document.getElementById('shortcut-search');
|
||
|
|
if (searchInput && document.activeElement === searchInput) {
|
||
|
|
searchInput.blur();
|
||
|
|
searchInput.value = '';
|
||
|
|
filterShortcuts();
|
||
|
|
}
|
||
|
|
|
||
|
|
const navLinks = document.querySelector('.nav-links');
|
||
|
|
if (navLinks) {
|
||
|
|
navLinks.classList.remove('active');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// External link handling - open in new tab
|
||
|
|
document.querySelectorAll('a[href^="http"]').forEach(link => {
|
||
|
|
if (!link.hostname.includes(window.location.hostname)) {
|
||
|
|
link.setAttribute('target', '_blank');
|
||
|
|
link.setAttribute('rel', 'noopener noreferrer');
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Initialize on DOM ready
|
||
|
|
document.addEventListener('DOMContentLoaded', () => {
|
||
|
|
initScrollAnimations();
|
||
|
|
updateActiveSection();
|
||
|
|
updateHeaderBackground();
|
||
|
|
|
||
|
|
// Set current year in footer if needed
|
||
|
|
const yearSpan = document.querySelector('.current-year');
|
||
|
|
if (yearSpan) {
|
||
|
|
yearSpan.textContent = new Date().getFullYear();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Print-friendly handling
|
||
|
|
window.addEventListener('beforeprint', () => {
|
||
|
|
// Expand all FAQs for printing
|
||
|
|
document.querySelectorAll('.faq-item').forEach(item => {
|
||
|
|
item.classList.add('active');
|
||
|
|
});
|
||
|
|
|
||
|
|
// Show all tab contents
|
||
|
|
document.querySelectorAll('.tab-content').forEach(content => {
|
||
|
|
content.style.display = 'block';
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
window.addEventListener('afterprint', () => {
|
||
|
|
// Restore FAQ state
|
||
|
|
document.querySelectorAll('.faq-item').forEach(item => {
|
||
|
|
item.classList.remove('active');
|
||
|
|
});
|
||
|
|
|
||
|
|
// Restore tab state
|
||
|
|
document.querySelectorAll('.tab-content').forEach(content => {
|
||
|
|
content.style.display = '';
|
||
|
|
});
|
||
|
|
});
|