2026-07-17 12:19:41 +02:00
|
|
|
/**
|
|
|
|
|
* 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 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">' +
|
2026-07-17 18:02:33 +02:00
|
|
|
'<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"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>' +
|
|
|
|
|
'<span>' + (labels.copy || 'Copy map URL') + '</span>' +
|
2026-07-17 12:19:41 +02:00
|
|
|
'</button>' +
|
|
|
|
|
'<button type="button" class="geep-lightbox-btn geep-lightbox-close" aria-label="' + (labels.close || 'Close') + '">×</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() {
|
2026-07-17 18:02:33 +02:00
|
|
|
// Copy the full-size image URL of the map currently on screen.
|
|
|
|
|
var active = root.querySelector('.geep-map-img.is-active');
|
|
|
|
|
if (!active) {
|
2026-07-17 12:19:41 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2026-07-17 18:02:33 +02:00
|
|
|
var url = active.src; // src attribute holds the full-size file.
|
2026-07-17 12:19:41 +02:00
|
|
|
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);
|
|
|
|
|
});
|
|
|
|
|
})();
|