/** * Behaviour for the theme's design pages (About, Contact, FAQ, blog list and * post) — the parts of "new front/js/main.js" these pages use. Header/footer * behaviour stays in chrome.js. */ (function () { 'use strict'; var cfg = window.hohoPages || {}; var page = document.querySelector('main.hoho-page'); if (!page) return; var each = function (list, fn) { Array.prototype.forEach.call(list, fn); }; /* ---------- testimonials marquee: duplicate for a seamless loop ---------- */ var marquee = page.querySelector('[data-marquee] .marquee__track'); if (marquee) { each(Array.prototype.slice.call(marquee.children), function (node) { var copy = node.cloneNode(true); copy.setAttribute('aria-hidden', 'true'); marquee.appendChild(copy); }); } /* ---------- FAQ accordion ---------- */ each(page.querySelectorAll('[data-accordion]'), function (accordion) { accordion.addEventListener('click', function (e) { var btn = e.target.closest('.acc__q'); if (!btn) return; var open = btn.getAttribute('aria-expanded') === 'true'; each(accordion.querySelectorAll('.acc__q'), function (other) { other.setAttribute('aria-expanded', 'false'); }); btn.setAttribute('aria-expanded', String(!open)); }); }); /* ---------- FAQ page: category nav follows the scroll ---------- */ var catLinks = Array.prototype.slice.call(page.querySelectorAll('.catnav__link')); if (catLinks.length && 'IntersectionObserver' in window) { var setCat = function (id) { catLinks.forEach(function (a) { var on = a.getAttribute('href') === '#' + id; a.classList.toggle('is-active', on); if (on) a.setAttribute('aria-current', 'true'); else a.removeAttribute('aria-current'); }); }; var catObserver = new IntersectionObserver(function (entries) { entries.forEach(function (entry) { if (entry.isIntersecting) setCat(entry.target.id); }); }, { rootMargin: '-30% 0px -60% 0px' }); catLinks.forEach(function (a) { var target = document.querySelector(a.getAttribute('href')); if (target) catObserver.observe(target); }); } /* ---------- blog list: sort applies on change ---------- */ each(page.querySelectorAll('select[data-autosubmit]'), function (select) { select.addEventListener('change', function () { select.form.submit(); }); }); /* ---------- single article: share links, copy link, more reviews ---------- */ var post = document.getElementById('post'); if (post) { var pageUrl = encodeURIComponent(window.location.href.split('#')[0]); var shareTargets = { telegram: 'https://t.me/share/url?url=', facebook: 'https://www.facebook.com/sharer/sharer.php?u=', whatsapp: 'https://wa.me/?text=' }; each(post.querySelectorAll('[data-share]'), function (a) { var base = shareTargets[a.getAttribute('data-share')]; if (base) a.href = base + pageUrl; }); var copyBtn = post.querySelector('[data-copy-link]'); if (copyBtn && navigator.clipboard) { var copyLabel = copyBtn.textContent; copyBtn.addEventListener('click', function () { navigator.clipboard.writeText(window.location.href.split('#')[0]).then(function () { copyBtn.textContent = cfg.copied || 'Link copied'; copyBtn.classList.add('is-done'); window.setTimeout(function () { copyBtn.textContent = copyLabel; copyBtn.classList.remove('is-done'); }, 2000); }, function () {}); }); } var reviewList = document.getElementById('reviewList'); var reviewMore = document.getElementById('reviewMore'); if (reviewList && reviewMore) { var moreLabel = reviewMore.querySelector('span'); reviewMore.addEventListener('click', function () { var open = reviewList.classList.toggle('is-collapsed') === false; reviewMore.setAttribute('aria-expanded', String(open)); moreLabel.textContent = open ? cfg.fewer : cfg.more; }); } } /* ---------- contact form: validate, then send without leaving the page ---------- */ var ticketForm = document.getElementById('ticketForm'); var ticketStatus = document.getElementById('ticketStatus'); if (ticketForm && ticketStatus && window.fetch && window.FormData) { var submitBtn = ticketForm.querySelector('[type="submit"]'); ticketForm.addEventListener('submit', function (e) { e.preventDefault(); var bad = null; each(ticketForm.querySelectorAll('.rinput'), function (field) { var ok = field.value.trim() !== '' && field.checkValidity(); field.setAttribute('aria-invalid', String(!ok)); if (!ok && !bad) bad = field; }); ticketStatus.classList.toggle('is-error', !!bad); if (bad) { ticketStatus.textContent = cfg.invalid; bad.focus(); return; } ticketStatus.textContent = cfg.sending; submitBtn.disabled = true; fetch(ticketForm.getAttribute('data-ajax'), { method: 'POST', credentials: 'same-origin', body: new FormData(ticketForm) }) .then(function (r) { return r.json(); }) .then(function (res) { var ok = res && res.success; ticketStatus.classList.toggle('is-error', !ok); ticketStatus.textContent = (res && res.data && res.data.message) || cfg.failed; if (ok) ticketForm.reset(); }) .catch(function () { ticketStatus.classList.add('is-error'); ticketStatus.textContent = cfg.failed; }) .then(function () { submitBtn.disabled = false; }); }); } /* ---------- scroll reveal: sections fade/rise in, cards cascade after ---------- */ var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; if (!reduceMotion && 'IntersectionObserver' in window) { var sections = Array.prototype.slice.call(page.querySelectorAll(':scope > section, :scope > article, :scope > div.section')); var cardSelector = '.dcard, .acard, .titem, .rcard, .quote, .statcard'; var groupCounts = new Map(); sections.forEach(function (section) { section.classList.add('reveal'); each(section.querySelectorAll(cardSelector), function (card) { var group = card.parentElement; var i = groupCounts.get(group) || 0; groupCounts.set(group, i + 1); card.classList.add('reveal-item'); card.style.animationDelay = (Math.min(i, 7) * 0.07) + 's'; }); }); var revealObserver = new IntersectionObserver(function (entries, obs) { entries.forEach(function (entry) { if (!entry.isIntersecting) return; entry.target.classList.add('is-visible'); each(entry.target.querySelectorAll('.reveal-item'), function (item) { item.classList.add('is-visible'); }); obs.unobserve(entry.target); }); }, { threshold: 0.15, rootMargin: '0px 0px -8% 0px' }); sections.forEach(function (section) { revealObserver.observe(section); }); } })();