Files
GEEP---Genocide-map/assets/js/geep-maps.js
T

318 lines
9.4 KiB
JavaScript
Raw Normal View History

/**
* 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-<slug>): 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-<slug> selects that map on page load.
* ---------------------------------------------------------------- */
function shareUrl() {
var base = window.location.href.split('#')[0];
if (!current) {
return base;
}
return base + HASH_PREFIX + encodeURIComponent(current.getAttribute('data-slug'));
}
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 =
'<div class="geep-lightbox-backdrop"></div>' +
'<figure class="geep-lightbox-inner">' +
'<div class="geep-lightbox-actions">' +
'<button type="button" class="geep-lightbox-btn geep-lightbox-share">' +
'<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.6" y1="10.6" x2="15.4" y2="6.4"/><line x1="8.6" y1="13.4" x2="15.4" y2="17.6"/></svg>' +
'<span>' + (labels.share || 'Share map') + '</span>' +
'</button>' +
'<button type="button" class="geep-lightbox-btn geep-lightbox-close" aria-label="' + (labels.close || 'Close') + '">&#215;</button>' +
'</div>' +
'<img class="geep-lightbox-img" alt="">' +
'<div class="geep-lightbox-toast" hidden>' + (labels.copied || 'Link copied!') + '</div>' +
'</figure>';
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() {
var url = shareUrl();
var title = current ? current.querySelector('.geep-toggle-title').textContent : document.title;
if (navigator.share) {
navigator.share({ title: title, url: url }).catch(function () {});
return;
}
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);
});
})();