/** * FP Genocide Maps — front-end behavior. * * - Exclusive accordion with one map always open (700 ms slide, Divi's * default speed); opening a map swaps the image with a fade. * - Clicking the map image opens it in a lightbox with a share button. * - Each map has a shareable URL (#map-): visiting it opens that map * and scrolls the section into view. */ (function () { 'use strict'; var DURATION = 700; var HASH_PREFIX = '#map-'; function slideDown(el) { el.classList.add('geep-animating'); el.style.overflow = 'hidden'; el.style.height = '0px'; var target = el.scrollHeight; el.style.transition = 'height ' + DURATION + 'ms ease-in-out'; requestAnimationFrame(function () { el.style.height = target + 'px'; }); window.setTimeout(function () { el.style.transition = ''; el.style.height = ''; el.style.overflow = ''; el.classList.remove('geep-animating'); }, DURATION); } function slideUp(el, done) { el.classList.add('geep-animating'); el.style.overflow = 'hidden'; el.style.height = el.scrollHeight + 'px'; el.style.transition = 'height ' + DURATION + 'ms ease-in-out'; requestAnimationFrame(function () { el.style.height = '0px'; }); window.setTimeout(function () { el.style.transition = ''; el.style.height = ''; el.style.overflow = ''; el.classList.remove('geep-animating'); done(); }, DURATION); } function init(root) { var labels; try { labels = JSON.parse(root.getAttribute('data-labels') || '{}'); } catch (e) { labels = {}; } var images = root.querySelectorAll('.geep-map-img'); // The first map is rendered open by the shortcode. var current = root.querySelector('.geep-toggle--open'); function showMap(key) { // No fallback: a map without its own image leaves the gap empty. var target = null; if (key) { images.forEach(function (img) { if (img.getAttribute('data-map') === key) { target = img; } }); } images.forEach(function (img) { if (img !== target) { img.classList.remove('is-active', 'is-shown'); } }); if (target && !target.classList.contains('is-active')) { target.classList.add('is-active'); requestAnimationFrame(function () { requestAnimationFrame(function () { target.classList.add('is-shown'); }); }); } } function close(toggle) { var content = toggle.querySelector('.geep-toggle-content'); toggle.classList.remove('geep-toggle--open'); slideUp(content, function () { toggle.classList.add('geep-toggle--close'); }); } function open(toggle) { var content = toggle.querySelector('.geep-toggle-content'); toggle.classList.remove('geep-toggle--close'); toggle.classList.add('geep-toggle--open'); slideDown(content); } function select(toggle) { if (toggle === current) { return; } if (current) { close(current); } open(toggle); current = toggle; showMap(toggle.getAttribute('data-map')); } root.querySelectorAll('.geep-toggle').forEach(function (toggle) { var title = toggle.querySelector('.geep-toggle-title'); var content = toggle.querySelector('.geep-toggle-content'); if (!title || !content) { return; } title.addEventListener('click', function () { if (content.classList.contains('geep-animating')) { return; } select(toggle); }); }); /* ------------------------------------------------------------------ * Shareable URLs: #map- selects that map on page load. * ---------------------------------------------------------------- */ function openFromHash() { if (window.location.hash.indexOf(HASH_PREFIX) !== 0) { return; } var slug = decodeURIComponent(window.location.hash.slice(HASH_PREFIX.length)); var target = null; root.querySelectorAll('.geep-toggle').forEach(function (t) { if (t.getAttribute('data-slug') === slug) { target = t; } }); if (!target || target === current) { if (target) { root.scrollIntoView({ behavior: 'smooth', block: 'center' }); } return; } // Switch instantly (no slide) — the page is just loading. if (current) { current.classList.remove('geep-toggle--open'); current.classList.add('geep-toggle--close'); } target.classList.remove('geep-toggle--close'); target.classList.add('geep-toggle--open'); current = target; showMap(target.getAttribute('data-map')); root.scrollIntoView({ behavior: 'smooth', block: 'center' }); } openFromHash(); window.addEventListener('hashchange', openFromHash); /* ------------------------------------------------------------------ * Lightbox with share button. * ---------------------------------------------------------------- */ var lightbox = null; var toastTimer = null; function buildLightbox() { var lb = document.createElement('div'); lb.className = 'geep-lightbox'; lb.setAttribute('role', 'dialog'); lb.setAttribute('aria-modal', 'true'); lb.innerHTML = '
' + '
' + '
' + '' + '' + '
' + '' + '' + '
'; document.body.appendChild(lb); lb.querySelector('.geep-lightbox-backdrop').addEventListener('click', closeLightbox); lb.querySelector('.geep-lightbox-close').addEventListener('click', closeLightbox); lb.querySelector('.geep-lightbox-share').addEventListener('click', share); return lb; } function openLightbox() { var active = root.querySelector('.geep-map-img.is-active'); if (!active) { return; } if (!lightbox) { lightbox = buildLightbox(); } var img = lightbox.querySelector('.geep-lightbox-img'); var key = active.getAttribute('data-map'); var full = active.src; // src attribute holds the full-size file. // Show instantly whatever variant the page already downloaded for // this map (it is in cache), instead of leaving the previous map // on screen while the big file downloads. img.dataset.map = key; img.src = active.currentSrc || full; img.alt = current ? current.querySelector('.geep-toggle-title').textContent : ''; // Then upgrade to the full-size file in the background. if (img.src !== full) { var pre = new Image(); pre.onload = function () { // Only swap if the lightbox still shows this same map. if (lightbox.classList.contains('is-open') && img.dataset.map === key) { img.src = full; } }; pre.src = full; } lightbox.classList.add('is-open'); document.documentElement.classList.add('geep-lightbox-noscroll'); document.addEventListener('keydown', onKeydown); } function closeLightbox() { if (!lightbox) { return; } lightbox.classList.remove('is-open'); document.documentElement.classList.remove('geep-lightbox-noscroll'); document.removeEventListener('keydown', onKeydown); } function onKeydown(e) { if (e.key === 'Escape') { closeLightbox(); } } function toast() { var t = lightbox.querySelector('.geep-lightbox-toast'); t.hidden = false; window.clearTimeout(toastTimer); toastTimer = window.setTimeout(function () { t.hidden = true; }, 2200); } function share() { // Copy the full-size image URL of the map currently on screen. var active = root.querySelector('.geep-map-img.is-active'); if (!active) { return; } var url = active.src; // src attribute holds the full-size file. if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(url).then(toast, function () { fallbackCopy(url); }); } else { fallbackCopy(url); } } function fallbackCopy(url) { var ta = document.createElement('textarea'); ta.value = url; ta.style.position = 'fixed'; ta.style.opacity = '0'; document.body.appendChild(ta); ta.select(); try { document.execCommand('copy'); toast(); } catch (e) {} document.body.removeChild(ta); } var wrap = root.querySelector('.geep-maps-image-wrap'); if (wrap) { wrap.addEventListener('click', openLightbox); wrap.addEventListener('keydown', function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openLightbox(); } }); } } function ready(fn) { if (document.readyState !== 'loading') { fn(); } else { document.addEventListener('DOMContentLoaded', fn); } } ready(function () { document.querySelectorAll('.geep-maps').forEach(init); }); })();