added overlay function for tarot cards

This commit is contained in:
2026-03-08 03:52:25 -07:00
parent 84b340d7d1
commit 78abb582dd
17 changed files with 4050 additions and 1175 deletions

View File

@@ -3,79 +3,21 @@
(function () {
"use strict";
// ----- shared utilities (mirrored from ui-quiz.js since they aren't exported) -----
const quizPluginHelpers = window.QuizPluginHelpers || {};
const {
normalizeOption,
normalizeKey,
toUniqueOptionList,
makeTemplate
} = quizPluginHelpers;
function normalizeOption(value) {
return String(value || "").trim();
}
function normalizeKey(value) {
return normalizeOption(value).toLowerCase();
}
function toUniqueOptionList(values) {
const seen = new Set();
const unique = [];
(values || []).forEach((value) => {
const formatted = normalizeOption(value);
if (!formatted) return;
const key = normalizeKey(formatted);
if (seen.has(key)) return;
seen.add(key);
unique.push(formatted);
});
return unique;
}
function shuffle(list) {
const clone = list.slice();
for (let i = clone.length - 1; i > 0; i -= 1) {
const j = Math.floor(Math.random() * (i + 1));
[clone[i], clone[j]] = [clone[j], clone[i]];
}
return clone;
}
function buildOptions(correctValue, poolValues) {
const correct = normalizeOption(correctValue);
if (!correct) return null;
const uniquePool = toUniqueOptionList(poolValues || []);
if (!uniquePool.some((v) => normalizeKey(v) === normalizeKey(correct))) {
uniquePool.push(correct);
}
const distractors = uniquePool.filter((v) => normalizeKey(v) !== normalizeKey(correct));
if (distractors.length < 3) return null;
const selected = shuffle(distractors).slice(0, 3);
const options = shuffle([correct, ...selected]);
const correctIndex = options.findIndex((v) => normalizeKey(v) === normalizeKey(correct));
if (correctIndex < 0 || options.length < 4) return null;
return { options, correctIndex };
}
/**
* Build a validated quiz question template.
* Returns null if there aren't enough distractors for a 4-choice question.
*/
function makeTemplate(key, categoryId, category, prompt, answer, pool) {
const correctStr = normalizeOption(answer);
const promptStr = normalizeOption(prompt);
if (!key || !categoryId || !promptStr || !correctStr) return null;
const uniquePool = toUniqueOptionList(pool || []);
if (!uniquePool.some((v) => normalizeKey(v) === normalizeKey(correctStr))) {
uniquePool.push(correctStr);
}
const distractorCount = uniquePool.filter((v) => normalizeKey(v) !== normalizeKey(correctStr)).length;
if (distractorCount < 3) return null;
return {
key,
categoryId,
category,
promptByDifficulty: promptStr,
answerByDifficulty: correctStr,
poolByDifficulty: uniquePool
};
if (
typeof normalizeOption !== "function"
|| typeof normalizeKey !== "function"
|| typeof toUniqueOptionList !== "function"
|| typeof makeTemplate !== "function"
) {
throw new Error("QuizPluginHelpers must load before quiz-calendars.js");
}
function ordinal(n) {