Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
File Manager
/
portableapps
/
personal
/
themestrap
:
theme.js
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
// Themestrap theme: dark mode, sticky header, menu, tabs, slider and lightbox. No dependencies. // The first part runs straight away from the head so the right colors show before the page paints. (function () { var d = document.documentElement, k = 'tsblog-theme', s = null, mq = matchMedia('(prefers-color-scheme: dark)'); try { s = localStorage.getItem(k); } catch (e) {} function isDark() { var c = d.getAttribute('data-theme'); return c ? c === 'dark' : mq.matches; } // data-bs-theme and the "dark" class are for Bootstrap and Themestrap components (PluginAlert styles from html.dark) function sync() { d.classList.toggle('dark', isDark()); d.setAttribute('data-bs-theme', isDark() ? 'dark' : 'light'); } if (s) d.setAttribute('data-theme', s); sync(); if (mq.addEventListener) mq.addEventListener('change', sync); document.addEventListener('click', function (e) { var b = e.target.closest && e.target.closest('[data-theme-toggle]'); if (!b) return; var next = isDark() ? 'light' : 'dark'; d.setAttribute('data-theme', next); sync(); try { localStorage.setItem(k, next); } catch (e2) {} }); })(); document.addEventListener('DOMContentLoaded', function () { var $$ = function (sel, root) { return Array.prototype.slice.call((root || document).querySelectorAll(sel)); }; // Header gets a shadow and shrinks after a little scrolling var header = document.getElementById('header'); if (header) { var stuck = false; var onScroll = function () { var now = (window.pageYOffset || document.documentElement.scrollTop) > 70; if (now !== stuck) { stuck = now; header.classList.toggle('is-stuck', now); } }; window.addEventListener('scroll', onScroll, { passive: true }); onScroll(); } // Menu button on small screens var burger = document.querySelector('[data-nav-toggle]'), nav = document.getElementById('tsx-nav'); if (burger && nav) { burger.addEventListener('click', function () { var open = nav.classList.toggle('is-open'); burger.setAttribute('aria-expanded', open ? 'true' : 'false'); }); document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && nav.classList.contains('is-open')) { nav.classList.remove('is-open'); burger.setAttribute('aria-expanded', 'false'); burger.focus(); } }); } // Tabs in the sidebar (arrow keys move between them) $$('[data-tabs]').forEach(function (box) { var tabs = $$('[role=tab]', box); function show(tab) { tabs.forEach(function (t) { var on = t === tab; t.setAttribute('aria-selected', on ? 'true' : 'false'); t.tabIndex = on ? 0 : -1; var pane = document.getElementById(t.getAttribute('aria-controls')); if (pane) pane.hidden = !on; }); } tabs.forEach(function (t, i) { t.addEventListener('click', function () { show(t); }); t.addEventListener('keydown', function (e) { var j = e.key === 'ArrowRight' ? i + 1 : e.key === 'ArrowLeft' ? i - 1 : -1; if (j < 0) return; e.preventDefault(); var n = tabs[(j + tabs.length) % tabs.length]; show(n); n.focus(); }); }); }); // Slider. Without this script the pictures still scroll sideways and snap into place. var arrow = function (dir) { return '<svg viewBox="0 0 24 24" width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="' + (dir < 0 ? 'm14.5 5-7 7 7 7' : 'm9.5 5 7 7-7 7') + '"/></svg>'; }; $$('[data-slider]').forEach(function (box) { var track = box.querySelector('[data-track]'), slides = $$('.tsx-slide', track); if (slides.length < 2) return; var prev = document.createElement('button'), next = document.createElement('button'), dots = document.createElement('div'); prev.type = next.type = 'button'; prev.className = 'tsx-nav-btn tsx-prev'; next.className = 'tsx-nav-btn tsx-next'; prev.setAttribute('aria-label', 'Previous picture'); next.setAttribute('aria-label', 'Next picture'); prev.innerHTML = arrow(-1); next.innerHTML = arrow(1); dots.className = 'tsx-dots'; slides.forEach(function (_, i) { var b = document.createElement('button'); b.type = 'button'; b.setAttribute('aria-label', 'Picture ' + (i + 1)); b.addEventListener('click', function () { go(i); }); dots.appendChild(b); }); box.appendChild(prev); box.appendChild(next); box.appendChild(dots); function index() { return Math.round(track.scrollLeft / Math.max(1, track.clientWidth)); } function go(i) { i = Math.max(0, Math.min(slides.length - 1, i)); track.scrollTo({ left: i * track.clientWidth }); } function sync() { var i = index(); prev.disabled = i <= 0; next.disabled = i >= slides.length - 1; $$('button', dots).forEach(function (b, n) { if (n === i) b.setAttribute('aria-current', 'true'); else b.removeAttribute('aria-current'); }); } prev.addEventListener('click', function () { go(index() - 1); }); next.addEventListener('click', function () { go(index() + 1); }); track.addEventListener('scroll', function () { window.requestAnimationFrame(sync); }, { passive: true }); window.addEventListener('resize', function () { track.style.scrollBehavior = 'auto'; go(index()); track.style.scrollBehavior = ''; sync(); }); box.tabIndex = 0; box.addEventListener('keydown', function (e) { if (e.target !== box) return; if (e.key === 'ArrowLeft') { e.preventDefault(); go(index() - 1); } if (e.key === 'ArrowRight') { e.preventDefault(); go(index() + 1); } }); sync(); }); // Lightbox for galleries var lb = null, items = [], at = 0, opener = null; function build() { lb = document.createElement('div'); lb.className = 'tsx-lb'; lb.hidden = true; lb.setAttribute('role', 'dialog'); lb.setAttribute('aria-modal', 'true'); lb.setAttribute('aria-label', 'Picture viewer'); lb.innerHTML = '<img alt=""><button type="button" class="lb-close" aria-label="Close">×</button>' + '<button type="button" class="lb-prev" aria-label="Previous picture">' + arrow(-1) + '</button>' + '<button type="button" class="lb-next" aria-label="Next picture">' + arrow(1) + '</button><div class="lb-count" aria-live="polite"></div>'; document.body.appendChild(lb); lb.addEventListener('click', function (e) { if (e.target.closest('.lb-close') || e.target === lb) close(); else if (e.target.closest('.lb-prev')) step(-1); else if (e.target.closest('.lb-next')) step(1); }); } function show(i) { at = (i + items.length) % items.length; lb.querySelector('img').src = items[at].href; lb.querySelector('.lb-count').textContent = items.length > 1 ? (at + 1) + ' / ' + items.length : ''; lb.querySelector('.lb-prev').hidden = lb.querySelector('.lb-next').hidden = items.length < 2; } function step(n) { show(at + n); } function open(list, i, from) { if (!lb) build(); items = list; opener = from; show(i); lb.hidden = false; document.documentElement.style.overflow = 'hidden'; lb.querySelector('.lb-close').focus(); } function close() { if (!lb || lb.hidden) return; lb.hidden = true; lb.querySelector('img').removeAttribute('src'); document.documentElement.style.overflow = ''; if (opener) opener.focus(); } document.addEventListener('keydown', function (e) { if (!lb || lb.hidden) return; if (e.key === 'Escape') close(); else if (e.key === 'ArrowLeft') step(-1); else if (e.key === 'ArrowRight') step(1); else if (e.key === 'Tab') { // Keep focus inside the viewer var f = $$('button', lb).filter(function (b) { return !b.hidden; }), first = f[0], last = f[f.length - 1]; if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } } }); $$('[data-lightbox]').forEach(function (g) { var links = $$('a', g); links.forEach(function (a, i) { a.addEventListener('click', function (e) { if (e.ctrlKey || e.metaKey || e.shiftKey || e.button) return; e.preventDefault(); open(links, i, a); }); }); }); });