358 lines
11 KiB
JavaScript
358 lines
11 KiB
JavaScript
|
|
/* ui-quiz-bank-builtins.js — Built-in quiz template generation */
|
||
|
|
(function () {
|
||
|
|
"use strict";
|
||
|
|
|
||
|
|
const quizQuestionBankBuiltInDomains = window.QuizQuestionBankBuiltInDomains || {};
|
||
|
|
|
||
|
|
if (typeof quizQuestionBankBuiltInDomains.appendBuiltInQuestionBankDomains !== "function") {
|
||
|
|
throw new Error("QuizQuestionBankBuiltInDomains module must load before ui-quiz-bank-builtins.js");
|
||
|
|
}
|
||
|
|
|
||
|
|
function buildBuiltInQuestionBank(context) {
|
||
|
|
const {
|
||
|
|
referenceData,
|
||
|
|
magickDataset,
|
||
|
|
helpers
|
||
|
|
} = context || {};
|
||
|
|
|
||
|
|
const {
|
||
|
|
toTitleCase,
|
||
|
|
normalizeOption,
|
||
|
|
toUniqueOptionList,
|
||
|
|
createQuestionTemplate
|
||
|
|
} = helpers || {};
|
||
|
|
|
||
|
|
if (
|
||
|
|
typeof toTitleCase !== "function"
|
||
|
|
|| typeof normalizeOption !== "function"
|
||
|
|
|| typeof toUniqueOptionList !== "function"
|
||
|
|
|| typeof createQuestionTemplate !== "function"
|
||
|
|
) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
const grouped = magickDataset?.grouped || {};
|
||
|
|
const alphabets = grouped.alphabets || {};
|
||
|
|
const englishLetters = Array.isArray(alphabets?.english) ? alphabets.english : [];
|
||
|
|
const hebrewLetters = Array.isArray(alphabets?.hebrew) ? alphabets.hebrew : [];
|
||
|
|
const kabbalahTree = grouped?.kabbalah?.["kabbalah-tree"] || {};
|
||
|
|
const treePaths = Array.isArray(kabbalahTree?.paths) ? kabbalahTree.paths : [];
|
||
|
|
const treeSephiroth = Array.isArray(kabbalahTree?.sephiroth) ? kabbalahTree.sephiroth : [];
|
||
|
|
const sephirotById = grouped?.kabbalah?.sephirot && typeof grouped.kabbalah.sephirot === "object"
|
||
|
|
? grouped.kabbalah.sephirot
|
||
|
|
: {};
|
||
|
|
const cube = grouped?.kabbalah?.cube && typeof grouped.kabbalah.cube === "object"
|
||
|
|
? grouped.kabbalah.cube
|
||
|
|
: {};
|
||
|
|
const cubeWalls = Array.isArray(cube?.walls) ? cube.walls : [];
|
||
|
|
const cubeEdges = Array.isArray(cube?.edges) ? cube.edges : [];
|
||
|
|
const cubeCenter = cube?.center && typeof cube.center === "object" ? cube.center : null;
|
||
|
|
const playingCardsData = grouped?.["playing-cards-52"];
|
||
|
|
const playingCards = Array.isArray(playingCardsData)
|
||
|
|
? playingCardsData
|
||
|
|
: (Array.isArray(playingCardsData?.entries) ? playingCardsData.entries : []);
|
||
|
|
const signs = Array.isArray(referenceData?.signs) ? referenceData.signs : [];
|
||
|
|
const planetsById = referenceData?.planets && typeof referenceData.planets === "object"
|
||
|
|
? referenceData.planets
|
||
|
|
: {};
|
||
|
|
const planets = Object.values(planetsById);
|
||
|
|
const decansBySign = referenceData?.decansBySign && typeof referenceData.decansBySign === "object"
|
||
|
|
? referenceData.decansBySign
|
||
|
|
: {};
|
||
|
|
|
||
|
|
const normalizeId = (value) => String(value || "").trim().toLowerCase();
|
||
|
|
|
||
|
|
const toRomanNumeral = (value) => {
|
||
|
|
const numeric = Number(value);
|
||
|
|
if (!Number.isFinite(numeric) || numeric <= 0) {
|
||
|
|
return String(value || "");
|
||
|
|
}
|
||
|
|
|
||
|
|
const intValue = Math.trunc(numeric);
|
||
|
|
const lookup = [
|
||
|
|
[1000, "M"],
|
||
|
|
[900, "CM"],
|
||
|
|
[500, "D"],
|
||
|
|
[400, "CD"],
|
||
|
|
[100, "C"],
|
||
|
|
[90, "XC"],
|
||
|
|
[50, "L"],
|
||
|
|
[40, "XL"],
|
||
|
|
[10, "X"],
|
||
|
|
[9, "IX"],
|
||
|
|
[5, "V"],
|
||
|
|
[4, "IV"],
|
||
|
|
[1, "I"]
|
||
|
|
];
|
||
|
|
|
||
|
|
let current = intValue;
|
||
|
|
let result = "";
|
||
|
|
lookup.forEach(([size, symbol]) => {
|
||
|
|
while (current >= size) {
|
||
|
|
result += symbol;
|
||
|
|
current -= size;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
return result || String(intValue);
|
||
|
|
};
|
||
|
|
|
||
|
|
const labelFromId = (value) => {
|
||
|
|
const id = String(value || "").trim();
|
||
|
|
if (!id) {
|
||
|
|
return "";
|
||
|
|
}
|
||
|
|
return id
|
||
|
|
.replace(/[_-]+/g, " ")
|
||
|
|
.replace(/\s+/g, " ")
|
||
|
|
.trim()
|
||
|
|
.split(" ")
|
||
|
|
.map((part) => part ? part.charAt(0).toUpperCase() + part.slice(1) : "")
|
||
|
|
.join(" ");
|
||
|
|
};
|
||
|
|
|
||
|
|
const getPlanetLabelById = (planetId) => {
|
||
|
|
const normalized = normalizeId(planetId);
|
||
|
|
if (!normalized) {
|
||
|
|
return "";
|
||
|
|
}
|
||
|
|
|
||
|
|
const directPlanet = planetsById[normalized];
|
||
|
|
if (directPlanet?.name) {
|
||
|
|
return directPlanet.name;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (normalized === "primum-mobile") {
|
||
|
|
return "Primum Mobile";
|
||
|
|
}
|
||
|
|
if (normalized === "olam-yesodot") {
|
||
|
|
return "Earth / Elements";
|
||
|
|
}
|
||
|
|
|
||
|
|
return labelFromId(normalized);
|
||
|
|
};
|
||
|
|
|
||
|
|
const hebrewById = new Map(
|
||
|
|
hebrewLetters
|
||
|
|
.filter((entry) => entry?.hebrewLetterId)
|
||
|
|
.map((entry) => [normalizeId(entry.hebrewLetterId), entry])
|
||
|
|
);
|
||
|
|
|
||
|
|
const formatHebrewLetterLabel = (entry, fallbackId = "") => {
|
||
|
|
if (entry?.name && entry?.char) {
|
||
|
|
return `${entry.name} (${entry.char})`;
|
||
|
|
}
|
||
|
|
if (entry?.name) {
|
||
|
|
return entry.name;
|
||
|
|
}
|
||
|
|
if (entry?.char) {
|
||
|
|
return entry.char;
|
||
|
|
}
|
||
|
|
return labelFromId(fallbackId);
|
||
|
|
};
|
||
|
|
|
||
|
|
const sephiraNameByNumber = new Map(
|
||
|
|
treeSephiroth
|
||
|
|
.filter((entry) => Number.isFinite(Number(entry?.number)) && entry?.name)
|
||
|
|
.map((entry) => [Math.trunc(Number(entry.number)), String(entry.name)])
|
||
|
|
);
|
||
|
|
|
||
|
|
const sephiraNameById = new Map(
|
||
|
|
treeSephiroth
|
||
|
|
.filter((entry) => entry?.sephiraId && entry?.name)
|
||
|
|
.map((entry) => [normalizeId(entry.sephiraId), String(entry.name)])
|
||
|
|
);
|
||
|
|
|
||
|
|
const getSephiraName = (numberValue, idValue) => {
|
||
|
|
const numberKey = Number(numberValue);
|
||
|
|
if (Number.isFinite(numberKey)) {
|
||
|
|
const byNumber = sephiraNameByNumber.get(Math.trunc(numberKey));
|
||
|
|
if (byNumber) {
|
||
|
|
return byNumber;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const byId = sephiraNameById.get(normalizeId(idValue));
|
||
|
|
if (byId) {
|
||
|
|
return byId;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (Number.isFinite(numberKey)) {
|
||
|
|
return `Sephira ${Math.trunc(numberKey)}`;
|
||
|
|
}
|
||
|
|
|
||
|
|
return labelFromId(idValue);
|
||
|
|
};
|
||
|
|
|
||
|
|
const formatPathLetter = (path) => {
|
||
|
|
const transliteration = String(path?.hebrewLetter?.transliteration || "").trim();
|
||
|
|
const glyph = String(path?.hebrewLetter?.char || "").trim();
|
||
|
|
|
||
|
|
if (transliteration && glyph) {
|
||
|
|
return `${transliteration} (${glyph})`;
|
||
|
|
}
|
||
|
|
if (transliteration) {
|
||
|
|
return transliteration;
|
||
|
|
}
|
||
|
|
if (glyph) {
|
||
|
|
return glyph;
|
||
|
|
}
|
||
|
|
return "";
|
||
|
|
};
|
||
|
|
|
||
|
|
const flattenDecans = Object.values(decansBySign)
|
||
|
|
.flatMap((entries) => (Array.isArray(entries) ? entries : []));
|
||
|
|
|
||
|
|
const signNameById = new Map(
|
||
|
|
signs
|
||
|
|
.filter((entry) => entry?.id && entry?.name)
|
||
|
|
.map((entry) => [normalizeId(entry.id), String(entry.name)])
|
||
|
|
);
|
||
|
|
|
||
|
|
const formatDecanLabel = (decan) => {
|
||
|
|
const signName = signNameById.get(normalizeId(decan?.signId)) || labelFromId(decan?.signId);
|
||
|
|
const index = Number(decan?.index);
|
||
|
|
if (!signName || !Number.isFinite(index)) {
|
||
|
|
return "";
|
||
|
|
}
|
||
|
|
return `${signName} Decan ${toRomanNumeral(index)}`;
|
||
|
|
};
|
||
|
|
|
||
|
|
const bank = [];
|
||
|
|
|
||
|
|
const englishGematriaPool = englishLetters
|
||
|
|
.map((item) => (Number.isFinite(Number(item?.pythagorean)) ? String(item.pythagorean) : ""))
|
||
|
|
.filter(Boolean);
|
||
|
|
|
||
|
|
const hebrewNumerologyPool = hebrewLetters
|
||
|
|
.map((item) => (Number.isFinite(Number(item?.numerology)) ? String(item.numerology) : ""))
|
||
|
|
.filter(Boolean);
|
||
|
|
|
||
|
|
const hebrewNameAndCharPool = hebrewLetters
|
||
|
|
.filter((item) => item?.name && item?.char)
|
||
|
|
.map((item) => `${item.name} (${item.char})`);
|
||
|
|
|
||
|
|
const hebrewCharPool = hebrewLetters
|
||
|
|
.map((item) => item?.char)
|
||
|
|
.filter(Boolean);
|
||
|
|
|
||
|
|
const planetNamePool = planets
|
||
|
|
.map((planet) => planet?.name)
|
||
|
|
.filter(Boolean);
|
||
|
|
|
||
|
|
const planetWeekdayPool = planets
|
||
|
|
.map((planet) => planet?.weekday)
|
||
|
|
.filter(Boolean);
|
||
|
|
|
||
|
|
const zodiacElementPool = signs
|
||
|
|
.map((sign) => toTitleCase(sign?.element))
|
||
|
|
.filter(Boolean);
|
||
|
|
|
||
|
|
const zodiacTarotPool = signs
|
||
|
|
.map((sign) => sign?.tarot?.majorArcana)
|
||
|
|
.filter(Boolean);
|
||
|
|
|
||
|
|
const pathNumberPool = toUniqueOptionList(
|
||
|
|
treePaths
|
||
|
|
.map((path) => {
|
||
|
|
const pathNo = Number(path?.pathNumber);
|
||
|
|
return Number.isFinite(pathNo) ? String(Math.trunc(pathNo)) : "";
|
||
|
|
})
|
||
|
|
);
|
||
|
|
|
||
|
|
const pathLetterPool = toUniqueOptionList(treePaths.map((path) => formatPathLetter(path)));
|
||
|
|
const pathTarotPool = toUniqueOptionList(treePaths.map((path) => normalizeOption(path?.tarot?.card)));
|
||
|
|
const sephirotPlanetPool = toUniqueOptionList(
|
||
|
|
Object.values(sephirotById).map((entry) => getPlanetLabelById(entry?.planetId))
|
||
|
|
);
|
||
|
|
|
||
|
|
const decanLabelPool = toUniqueOptionList(flattenDecans.map((decan) => formatDecanLabel(decan)));
|
||
|
|
const decanRulerPool = toUniqueOptionList(
|
||
|
|
flattenDecans.map((decan) => getPlanetLabelById(decan?.rulerPlanetId))
|
||
|
|
);
|
||
|
|
|
||
|
|
const cubeWallLabelPool = toUniqueOptionList(
|
||
|
|
cubeWalls.map((wall) => `${String(wall?.name || labelFromId(wall?.id)).trim()} Wall`)
|
||
|
|
);
|
||
|
|
|
||
|
|
const cubeEdgeLabelPool = toUniqueOptionList(
|
||
|
|
cubeEdges.map((edge) => `${String(edge?.name || labelFromId(edge?.id)).trim()} Edge`)
|
||
|
|
);
|
||
|
|
|
||
|
|
const cubeLocationPool = toUniqueOptionList([
|
||
|
|
...cubeWallLabelPool,
|
||
|
|
...cubeEdgeLabelPool,
|
||
|
|
"Center"
|
||
|
|
]);
|
||
|
|
|
||
|
|
const cubeHebrewLetterPool = toUniqueOptionList([
|
||
|
|
...cubeWalls.map((wall) => {
|
||
|
|
const hebrew = hebrewById.get(normalizeId(wall?.hebrewLetterId));
|
||
|
|
return formatHebrewLetterLabel(hebrew, wall?.hebrewLetterId);
|
||
|
|
}),
|
||
|
|
...cubeEdges.map((edge) => {
|
||
|
|
const hebrew = hebrewById.get(normalizeId(edge?.hebrewLetterId));
|
||
|
|
return formatHebrewLetterLabel(hebrew, edge?.hebrewLetterId);
|
||
|
|
}),
|
||
|
|
formatHebrewLetterLabel(hebrewById.get(normalizeId(cubeCenter?.hebrewLetterId)), cubeCenter?.hebrewLetterId)
|
||
|
|
]);
|
||
|
|
|
||
|
|
const playingTarotPool = toUniqueOptionList(
|
||
|
|
playingCards.map((entry) => normalizeOption(entry?.tarotCard))
|
||
|
|
);
|
||
|
|
|
||
|
|
quizQuestionBankBuiltInDomains.appendBuiltInQuestionBankDomains({
|
||
|
|
bank,
|
||
|
|
englishLetters,
|
||
|
|
hebrewLetters,
|
||
|
|
hebrewById,
|
||
|
|
signs,
|
||
|
|
planets,
|
||
|
|
planetsById,
|
||
|
|
treePaths,
|
||
|
|
sephirotById,
|
||
|
|
flattenDecans,
|
||
|
|
cubeWalls,
|
||
|
|
cubeEdges,
|
||
|
|
cubeCenter,
|
||
|
|
playingCards,
|
||
|
|
pools: {
|
||
|
|
englishGematriaPool,
|
||
|
|
hebrewNumerologyPool,
|
||
|
|
hebrewNameAndCharPool,
|
||
|
|
hebrewCharPool,
|
||
|
|
planetNamePool,
|
||
|
|
planetWeekdayPool,
|
||
|
|
zodiacElementPool,
|
||
|
|
zodiacTarotPool,
|
||
|
|
pathNumberPool,
|
||
|
|
pathLetterPool,
|
||
|
|
pathTarotPool,
|
||
|
|
sephirotPlanetPool,
|
||
|
|
decanLabelPool,
|
||
|
|
decanRulerPool,
|
||
|
|
cubeLocationPool,
|
||
|
|
cubeHebrewLetterPool,
|
||
|
|
playingTarotPool
|
||
|
|
},
|
||
|
|
helpers: {
|
||
|
|
createQuestionTemplate,
|
||
|
|
normalizeId,
|
||
|
|
normalizeOption,
|
||
|
|
toTitleCase,
|
||
|
|
formatHebrewLetterLabel,
|
||
|
|
getPlanetLabelById,
|
||
|
|
getSephiraName,
|
||
|
|
formatPathLetter,
|
||
|
|
formatDecanLabel,
|
||
|
|
labelFromId
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
return bank;
|
||
|
|
}
|
||
|
|
|
||
|
|
window.QuizQuestionBankBuiltins = {
|
||
|
|
buildBuiltInQuestionBank
|
||
|
|
};
|
||
|
|
})();
|