(function () {
"use strict";
let initialized = false;
let activeTarotSpread = null;
let activeTarotSpreadDraw = [];
let config = {
ensureTarotSection: null,
getReferenceData: () => null,
getMagickDataset: () => null,
getActiveSection: () => "home",
setActiveSection: null
};
const THREE_CARD_POSITIONS = [
{ pos: "past", label: "Past" },
{ pos: "present", label: "Present" },
{ pos: "future", label: "Future" }
];
const CELTIC_CROSS_POSITIONS = [
{ pos: "crown", label: "Crown" },
{ pos: "out", label: "Outcome" },
{ pos: "past", label: "Recent Past" },
{ pos: "present", label: "Present" },
{ pos: "near-fut", label: "Near Future" },
{ pos: "hope", label: "Hopes & Fears" },
{ pos: "chall", label: "Challenge" },
{ pos: "env", label: "Environment" },
{ pos: "found", label: "Foundation" },
{ pos: "self", label: "Self" }
];
function getElements() {
return {
openTarotCardsEl: document.getElementById("open-tarot-cards"),
openTarotSpreadEl: document.getElementById("open-tarot-spread"),
tarotBrowseViewEl: document.getElementById("tarot-browse-view"),
tarotSpreadViewEl: document.getElementById("tarot-spread-view"),
tarotSpreadBackEl: document.getElementById("tarot-spread-back"),
tarotSpreadBtnThreeEl: document.getElementById("tarot-spread-btn-three"),
tarotSpreadBtnCelticEl: document.getElementById("tarot-spread-btn-celtic"),
tarotSpreadRevealAllEl: document.getElementById("tarot-spread-reveal-all"),
tarotSpreadRedrawEl: document.getElementById("tarot-spread-redraw"),
tarotSpreadMeaningsEl: document.getElementById("tarot-spread-meanings"),
tarotSpreadBoardEl: document.getElementById("tarot-spread-board")
};
}
function ensureTarotBrowseData() {
const referenceData = typeof config.getReferenceData === "function" ? config.getReferenceData() : null;
const magickDataset = typeof config.getMagickDataset === "function" ? config.getMagickDataset() : null;
if (typeof config.ensureTarotSection === "function" && referenceData) {
config.ensureTarotSection(referenceData, magickDataset);
}
}
function normalizeTarotSpread(value) {
return value === "celtic-cross" ? "celtic-cross" : "three-card";
}
function drawNFromDeck(n) {
const allCards = window.TarotSectionUi?.getCards?.() || [];
if (!allCards.length) return [];
const shuffled = [...allCards];
for (let index = shuffled.length - 1; index > 0; index -= 1) {
const swapIndex = Math.floor(Math.random() * (index + 1));
[shuffled[index], shuffled[swapIndex]] = [shuffled[swapIndex], shuffled[index]];
}
return shuffled.slice(0, n).map((card) => ({
...card,
reversed: Math.random() < 0.3
}));
}
function escapeHtml(value) {
return String(value || "")
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/\"/g, """)
.replace(/'/g, "'");
}
function getSpreadPositions(spreadId) {
return spreadId === "celtic-cross" ? CELTIC_CROSS_POSITIONS : THREE_CARD_POSITIONS;
}
function regenerateTarotSpreadDraw() {
const normalizedSpread = normalizeTarotSpread(activeTarotSpread);
const positions = getSpreadPositions(normalizedSpread);
const cards = drawNFromDeck(positions.length);
activeTarotSpreadDraw = positions.map((position, index) => ({
position,
card: cards[index] || null,
revealed: false
}));
}
function renderTarotSpreadMeanings() {
const { tarotSpreadMeaningsEl } = getElements();
if (!tarotSpreadMeaningsEl) {
return;
}
if (!activeTarotSpreadDraw.length || activeTarotSpreadDraw.some((entry) => !entry.card)) {
tarotSpreadMeaningsEl.innerHTML = "";
return;
}
const revealedEntries = activeTarotSpreadDraw.filter((entry) => entry.card && entry.revealed);
if (!revealedEntries.length) {
tarotSpreadMeaningsEl.innerHTML = '
Cards are face down. Click a card to reveal its meaning.
';
return;
}
const hiddenCount = activeTarotSpreadDraw.length - revealedEntries.length;
const hiddenHintMarkup = hiddenCount > 0
? `${hiddenCount} card${hiddenCount === 1 ? "" : "s"} still face down.
`
: "";
tarotSpreadMeaningsEl.innerHTML = revealedEntries.map((entry) => {
const positionLabel = escapeHtml(entry.position.label).toUpperCase();
const card = entry.card;
const cardName = escapeHtml(card.name || "Unknown Card");
const meaningText = escapeHtml(card.reversed ? (card.meanings?.reversed || card.summary || "--") : (card.meanings?.upright || card.summary || "--"));
const keywords = Array.isArray(card.keywords)
? card.keywords.map((keyword) => String(keyword || "").trim()).filter(Boolean)
: [];
const keywordMarkup = keywords.length
? `Keywords: ${escapeHtml(keywords.join(", "))}
`
: "";
const orientationMarkup = card.reversed
? ' (Reversed)'
: "";
return ``
+ `
${positionLabel}: ${cardName}${orientationMarkup}
`
+ `
${meaningText}
`
+ keywordMarkup
+ `
`;
}).join("") + hiddenHintMarkup;
}
function renderTarotSpread() {
const { tarotSpreadBoardEl, tarotSpreadMeaningsEl, tarotSpreadRevealAllEl } = getElements();
if (!tarotSpreadBoardEl) {
return;
}
const normalizedSpread = normalizeTarotSpread(activeTarotSpread);
const isCeltic = normalizedSpread === "celtic-cross";
const cardBackImageSrc = String(window.TarotCardImages?.resolveTarotCardBackImage?.() || "").trim();
if (!activeTarotSpreadDraw.length) {
regenerateTarotSpreadDraw();
}
tarotSpreadBoardEl.className = `tarot-spread-board tarot-spread-board--${isCeltic ? "celtic" : "three"}`;
if (!activeTarotSpreadDraw.length || activeTarotSpreadDraw.some((entry) => !entry.card)) {
tarotSpreadBoardEl.innerHTML = 'Tarot deck not loaded yet - open Cards first, then return to Spread.
';
if (tarotSpreadMeaningsEl) {
tarotSpreadMeaningsEl.innerHTML = "";
}
if (tarotSpreadRevealAllEl) {
tarotSpreadRevealAllEl.disabled = true;
tarotSpreadRevealAllEl.textContent = "Reveal All";
}
return;
}
if (tarotSpreadRevealAllEl) {
const totalCards = activeTarotSpreadDraw.length;
const revealedCount = activeTarotSpreadDraw.reduce((count, entry) => (
count + (entry?.card && entry.revealed ? 1 : 0)
), 0);
tarotSpreadRevealAllEl.disabled = revealedCount >= totalCards;
tarotSpreadRevealAllEl.textContent = revealedCount >= totalCards
? "All Revealed"
: `Reveal All (${totalCards - revealedCount})`;
}
renderTarotSpreadMeanings();
tarotSpreadBoardEl.innerHTML = activeTarotSpreadDraw.map((entry, index) => {
const position = entry.position;
const card = entry.card;
const imgSrc = window.TarotCardImages?.resolveTarotCardImage?.(card.name);
const isRevealed = Boolean(entry.revealed);
const cardBackAttr = cardBackImageSrc
? ` data-card-back-src="${escapeHtml(cardBackImageSrc)}"`
: "";
const reversed = card.reversed;
const wrapClass = [
"spread-card-wrap",
isRevealed ? "is-revealed" : "is-facedown",
(isRevealed && reversed) ? "is-reversed" : ""
].filter(Boolean).join(" ");
let faceMarkup = "";
if (isRevealed) {
faceMarkup = imgSrc
? `
`
: `${escapeHtml(card.name)}
`;
} else if (cardBackImageSrc) {
faceMarkup = '
';
} else {
faceMarkup = 'CARD BACK
';
}
const reversedTag = isRevealed && reversed
? 'Reversed'
: "";
const buttonAriaLabel = isRevealed
? `Open ${escapeHtml(card.name)} for ${escapeHtml(position.label)} in fullscreen`
: `Reveal ${escapeHtml(position.label)} card`;
return ``
+ `
${escapeHtml(position.label)}
`
+ `
`
+ (reversedTag ? `
${reversedTag}
` : "")
+ `
`;
}).join("");
}
function applyViewState() {
const {
openTarotCardsEl,
openTarotSpreadEl,
tarotBrowseViewEl,
tarotSpreadViewEl,
tarotSpreadBtnThreeEl,
tarotSpreadBtnCelticEl
} = getElements();
const isSpreadOpen = activeTarotSpread !== null;
const isCeltic = activeTarotSpread === "celtic-cross";
const isTarotActive = typeof config.getActiveSection === "function" && config.getActiveSection() === "tarot";
if (tarotBrowseViewEl) tarotBrowseViewEl.hidden = isSpreadOpen;
if (tarotSpreadViewEl) tarotSpreadViewEl.hidden = !isSpreadOpen;
if (tarotSpreadBtnThreeEl) tarotSpreadBtnThreeEl.classList.toggle("is-active", isSpreadOpen && !isCeltic);
if (tarotSpreadBtnCelticEl) tarotSpreadBtnCelticEl.classList.toggle("is-active", isSpreadOpen && isCeltic);
if (openTarotCardsEl) openTarotCardsEl.classList.toggle("is-active", isTarotActive && !isSpreadOpen);
if (openTarotSpreadEl) openTarotSpreadEl.classList.toggle("is-active", isTarotActive && isSpreadOpen);
}
function showCardsView() {
activeTarotSpread = null;
activeTarotSpreadDraw = [];
applyViewState();
ensureTarotBrowseData();
const detailPanelEl = document.querySelector("#tarot-browse-view .tarot-detail-panel");
if (detailPanelEl instanceof HTMLElement) {
detailPanelEl.scrollTop = 0;
}
}
function showTarotSpreadView(spreadId = "three-card") {
activeTarotSpread = normalizeTarotSpread(spreadId);
regenerateTarotSpreadDraw();
applyViewState();
ensureTarotBrowseData();
renderTarotSpread();
}
function setSpread(spreadId, openTarotSection = false) {
if (openTarotSection && typeof config.setActiveSection === "function") {
config.setActiveSection("tarot");
}
showTarotSpreadView(spreadId);
}
function revealAll() {
if (!activeTarotSpreadDraw.length) {
regenerateTarotSpreadDraw();
}
activeTarotSpreadDraw.forEach((entry) => {
if (entry?.card) {
entry.revealed = true;
}
});
renderTarotSpread();
}
function handleBoardClick(event) {
const target = event.target;
if (!(target instanceof Node)) {
return;
}
const button = target instanceof Element
? target.closest(".spread-card-wrap[data-spread-index]")
: null;
if (!(button instanceof HTMLButtonElement)) {
return;
}
const spreadIndex = Number(button.dataset.spreadIndex);
if (!Number.isInteger(spreadIndex) || spreadIndex < 0 || spreadIndex >= activeTarotSpreadDraw.length) {
return;
}
const spreadEntry = activeTarotSpreadDraw[spreadIndex];
if (!spreadEntry?.card) {
return;
}
if (!spreadEntry.revealed) {
spreadEntry.revealed = true;
renderTarotSpread();
return;
}
const imageSrc = window.TarotCardImages?.resolveTarotCardImage?.(spreadEntry.card.name);
if (imageSrc) {
window.TarotUiLightbox?.open?.(imageSrc, `${spreadEntry.card.name} (${spreadEntry.position?.label || "Spread"})`);
}
}
function bindEvents() {
const {
openTarotCardsEl,
openTarotSpreadEl,
tarotSpreadBackEl,
tarotSpreadBtnThreeEl,
tarotSpreadBtnCelticEl,
tarotSpreadRevealAllEl,
tarotSpreadRedrawEl,
tarotSpreadBoardEl
} = getElements();
if (openTarotCardsEl) {
openTarotCardsEl.addEventListener("click", () => {
if (typeof config.setActiveSection === "function") {
config.setActiveSection("tarot");
}
showCardsView();
});
}
if (openTarotSpreadEl) {
openTarotSpreadEl.addEventListener("click", () => {
setSpread("three-card", true);
});
}
if (tarotSpreadBackEl) {
tarotSpreadBackEl.addEventListener("click", () => {
showCardsView();
});
}
if (tarotSpreadBtnThreeEl) {
tarotSpreadBtnThreeEl.addEventListener("click", () => {
showTarotSpreadView("three-card");
});
}
if (tarotSpreadBtnCelticEl) {
tarotSpreadBtnCelticEl.addEventListener("click", () => {
showTarotSpreadView("celtic-cross");
});
}
if (tarotSpreadRedrawEl) {
tarotSpreadRedrawEl.addEventListener("click", () => {
regenerateTarotSpreadDraw();
renderTarotSpread();
});
}
if (tarotSpreadRevealAllEl) {
tarotSpreadRevealAllEl.addEventListener("click", revealAll);
}
if (tarotSpreadBoardEl) {
tarotSpreadBoardEl.addEventListener("click", handleBoardClick);
}
}
function handleSectionActivated() {
ensureTarotBrowseData();
applyViewState();
if (activeTarotSpread !== null) {
renderTarotSpread();
}
}
function init(nextConfig = {}) {
config = {
...config,
...nextConfig
};
if (initialized) {
applyViewState();
return;
}
bindEvents();
applyViewState();
initialized = true;
}
window.TarotSpreadUi = {
...(window.TarotSpreadUi || {}),
init,
applyViewState,
showCardsView,
showTarotSpreadView,
setSpread,
handleSectionActivated,
renderTarotSpread,
isSpreadOpen() {
return activeTarotSpread !== null;
}
};
})();