feat: The Other Dude v9.0.1 — full-featured email system
ci: add GitHub Pages deployment workflow for docs site Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
241
docs/website/script.js
Normal file
241
docs/website/script.js
Normal file
@@ -0,0 +1,241 @@
|
||||
/* TOD Documentation Website — Shared JavaScript */
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
/* 1. Scroll Spy (docs page) */
|
||||
/* -------------------------------------------------- */
|
||||
function initScrollSpy() {
|
||||
const sidebar = document.querySelector('.sidebar-nav');
|
||||
if (!sidebar) return;
|
||||
|
||||
const links = Array.from(document.querySelectorAll('.sidebar-link'));
|
||||
const sections = links
|
||||
.map(function (link) {
|
||||
var id = link.getAttribute('data-section');
|
||||
return id ? document.getElementById(id) : null;
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
if (!sections.length) return;
|
||||
|
||||
var current = null;
|
||||
|
||||
var observer = new IntersectionObserver(
|
||||
function (entries) {
|
||||
entries.forEach(function (entry) {
|
||||
if (entry.isIntersecting) {
|
||||
var id = entry.target.id;
|
||||
if (id !== current) {
|
||||
current = id;
|
||||
links.forEach(function (l) {
|
||||
l.classList.toggle(
|
||||
'sidebar-link--active',
|
||||
l.getAttribute('data-section') === id
|
||||
);
|
||||
});
|
||||
|
||||
/* keep active link visible in sidebar */
|
||||
var active = sidebar.querySelector('.sidebar-link--active');
|
||||
if (active) {
|
||||
active.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
{ rootMargin: '-80px 0px -60% 0px', threshold: 0 }
|
||||
);
|
||||
|
||||
sections.forEach(function (s) {
|
||||
observer.observe(s);
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
/* 2. Docs Search */
|
||||
/* -------------------------------------------------- */
|
||||
function initDocsSearch() {
|
||||
var input = document.getElementById('docs-search-input');
|
||||
if (!input) return;
|
||||
|
||||
var content = document.getElementById('docs-content');
|
||||
if (!content) return;
|
||||
|
||||
var sections = Array.from(content.querySelectorAll('section[id]'));
|
||||
var links = Array.from(document.querySelectorAll('.sidebar-link'));
|
||||
|
||||
input.addEventListener('input', function () {
|
||||
var q = input.value.trim().toLowerCase();
|
||||
|
||||
if (!q) {
|
||||
sections.forEach(function (s) { s.style.display = ''; });
|
||||
links.forEach(function (l) { l.style.display = ''; });
|
||||
return;
|
||||
}
|
||||
|
||||
sections.forEach(function (s) {
|
||||
var text = s.textContent.toLowerCase();
|
||||
var match = text.indexOf(q) !== -1;
|
||||
s.style.display = match ? '' : 'none';
|
||||
});
|
||||
|
||||
links.forEach(function (l) {
|
||||
var sectionId = l.getAttribute('data-section');
|
||||
var section = sectionId ? document.getElementById(sectionId) : null;
|
||||
if (section) {
|
||||
l.style.display = section.style.display;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
/* 3. Back to Top */
|
||||
/* -------------------------------------------------- */
|
||||
function initBackToTop() {
|
||||
var btn = document.getElementById('back-to-top');
|
||||
if (!btn) return;
|
||||
|
||||
window.addEventListener('scroll', function () {
|
||||
btn.classList.toggle('back-to-top--visible', window.scrollY > 400);
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
window.scrollToTop = function () {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
};
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
/* 4. Sidebar Toggle (mobile) */
|
||||
/* -------------------------------------------------- */
|
||||
window.toggleSidebar = function () {
|
||||
var sidebar = document.getElementById('docs-sidebar');
|
||||
if (!sidebar) return;
|
||||
sidebar.classList.toggle('docs-sidebar--open');
|
||||
};
|
||||
|
||||
function initSidebarClose() {
|
||||
var sidebar = document.getElementById('docs-sidebar');
|
||||
if (!sidebar) return;
|
||||
|
||||
/* close on outside click */
|
||||
document.addEventListener('click', function (e) {
|
||||
if (
|
||||
sidebar.classList.contains('docs-sidebar--open') &&
|
||||
!sidebar.contains(e.target) &&
|
||||
!e.target.closest('.docs-hamburger')
|
||||
) {
|
||||
sidebar.classList.remove('docs-sidebar--open');
|
||||
}
|
||||
});
|
||||
|
||||
/* close on link click (mobile) */
|
||||
sidebar.addEventListener('click', function (e) {
|
||||
if (e.target.closest('.sidebar-link')) {
|
||||
sidebar.classList.remove('docs-sidebar--open');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
/* 5. Reveal Animation (landing page) */
|
||||
/* -------------------------------------------------- */
|
||||
function initReveal() {
|
||||
var els = document.querySelectorAll('.reveal');
|
||||
if (!els.length) return;
|
||||
|
||||
var observer = new IntersectionObserver(
|
||||
function (entries) {
|
||||
entries.forEach(function (entry) {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('reveal--visible');
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 0.1 }
|
||||
);
|
||||
|
||||
els.forEach(function (el) {
|
||||
observer.observe(el);
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
/* 6. Smooth scroll for anchor links */
|
||||
/* -------------------------------------------------- */
|
||||
function initSmoothScroll() {
|
||||
document.addEventListener('click', function (e) {
|
||||
var link = e.target.closest('a[href^="#"]');
|
||||
if (!link) return;
|
||||
|
||||
var id = link.getAttribute('href').slice(1);
|
||||
var target = document.getElementById(id);
|
||||
if (!target) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
var offset = 80;
|
||||
var top = target.getBoundingClientRect().top + window.pageYOffset - offset;
|
||||
window.scrollTo({ top: top, behavior: 'smooth' });
|
||||
|
||||
/* update URL without jump */
|
||||
history.pushState(null, '', '#' + id);
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
/* 7. Active nav link (landing page) */
|
||||
/* -------------------------------------------------- */
|
||||
function initActiveNav() {
|
||||
var navLinks = document.querySelectorAll('.nav-link[href^="index.html#"]');
|
||||
if (!navLinks.length) return;
|
||||
|
||||
/* only run on landing page */
|
||||
if (document.body.classList.contains('docs-page')) return;
|
||||
|
||||
var sectionIds = [];
|
||||
navLinks.forEach(function (l) {
|
||||
var hash = l.getAttribute('href').split('#')[1];
|
||||
if (hash) sectionIds.push({ id: hash, link: l });
|
||||
});
|
||||
|
||||
if (!sectionIds.length) return;
|
||||
|
||||
var observer = new IntersectionObserver(
|
||||
function (entries) {
|
||||
entries.forEach(function (entry) {
|
||||
if (entry.isIntersecting) {
|
||||
sectionIds.forEach(function (item) {
|
||||
item.link.classList.toggle(
|
||||
'nav-link--active',
|
||||
item.id === entry.target.id
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
{ rootMargin: '-80px 0px -60% 0px', threshold: 0 }
|
||||
);
|
||||
|
||||
sectionIds.forEach(function (item) {
|
||||
var el = document.getElementById(item.id);
|
||||
if (el) observer.observe(el);
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
/* Init on DOMContentLoaded */
|
||||
/* -------------------------------------------------- */
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
initScrollSpy();
|
||||
initDocsSearch();
|
||||
initBackToTop();
|
||||
initSidebarClose();
|
||||
initReveal();
|
||||
initSmoothScroll();
|
||||
initActiveNav();
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user