anagram and dictionary added
This commit is contained in:
@@ -372,6 +372,12 @@
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadWordAnagrams(text) {
|
||||||
|
return fetchJson(buildApiUrl("/api/v1/words/anagrams", {
|
||||||
|
text
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
async function loadDeckOptions(forceRefresh = false) {
|
async function loadDeckOptions(forceRefresh = false) {
|
||||||
if (!forceRefresh && deckOptionsCache) {
|
if (!forceRefresh && deckOptionsCache) {
|
||||||
return deckOptionsCache;
|
return deckOptionsCache;
|
||||||
@@ -502,6 +508,7 @@
|
|||||||
loadDeckManifest,
|
loadDeckManifest,
|
||||||
loadDeckOptions,
|
loadDeckOptions,
|
||||||
loadGematriaWordsByValue,
|
loadGematriaWordsByValue,
|
||||||
|
loadWordAnagrams,
|
||||||
loadQuizCategories,
|
loadQuizCategories,
|
||||||
loadQuizTemplates,
|
loadQuizTemplates,
|
||||||
loadTarotCards,
|
loadTarotCards,
|
||||||
|
|||||||
@@ -2326,7 +2326,17 @@
|
|||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.alpha-gematria-toggle {
|
.alpha-tool-mode-group {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alpha-tool-mode-option {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
@@ -2335,6 +2345,16 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
padding: 6px 10px;
|
||||||
|
border: 1px solid #3f3f46;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #101018;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alpha-tool-mode-option:has(input:checked) {
|
||||||
|
border-color: #6366f1;
|
||||||
|
background: #1c1b35;
|
||||||
|
color: #eef2ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.alpha-gematria-controls {
|
.alpha-gematria-controls {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
inputEl: null,
|
inputEl: null,
|
||||||
resultEl: null,
|
resultEl: null,
|
||||||
breakdownEl: null,
|
breakdownEl: null,
|
||||||
modeToggleEl: null,
|
modeEls: [],
|
||||||
matchesEl: null,
|
matchesEl: null,
|
||||||
inputLabelEl: null,
|
inputLabelEl: null,
|
||||||
cipherLabelEl: null
|
cipherLabelEl: null
|
||||||
@@ -23,10 +23,13 @@
|
|||||||
activeCipherId: "",
|
activeCipherId: "",
|
||||||
forwardInputText: "",
|
forwardInputText: "",
|
||||||
reverseInputText: "",
|
reverseInputText: "",
|
||||||
|
anagramInputText: "",
|
||||||
activeMode: "forward",
|
activeMode: "forward",
|
||||||
scriptCharMap: new Map(),
|
scriptCharMap: new Map(),
|
||||||
reverseLookupCache: new Map(),
|
reverseLookupCache: new Map(),
|
||||||
reverseRequestId: 0
|
anagramLookupCache: new Map(),
|
||||||
|
reverseRequestId: 0,
|
||||||
|
anagramRequestId: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
function getAlphabets() {
|
function getAlphabets() {
|
||||||
@@ -43,7 +46,7 @@
|
|||||||
inputEl: null,
|
inputEl: null,
|
||||||
resultEl: null,
|
resultEl: null,
|
||||||
breakdownEl: null,
|
breakdownEl: null,
|
||||||
modeToggleEl: null,
|
modeEls: [],
|
||||||
matchesEl: null,
|
matchesEl: null,
|
||||||
inputLabelEl: null,
|
inputLabelEl: null,
|
||||||
cipherLabelEl: null
|
cipherLabelEl: null
|
||||||
@@ -54,10 +57,20 @@
|
|||||||
return state.activeMode === "reverse";
|
return state.activeMode === "reverse";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isAnagramMode() {
|
||||||
|
return state.activeMode === "anagram";
|
||||||
|
}
|
||||||
|
|
||||||
function getCurrentInputText() {
|
function getCurrentInputText() {
|
||||||
return isReverseMode()
|
if (isReverseMode()) {
|
||||||
? state.reverseInputText
|
return state.reverseInputText;
|
||||||
: state.forwardInputText;
|
}
|
||||||
|
|
||||||
|
if (isAnagramMode()) {
|
||||||
|
return state.anagramInputText;
|
||||||
|
}
|
||||||
|
|
||||||
|
return state.forwardInputText;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatCount(value) {
|
function formatCount(value) {
|
||||||
@@ -295,39 +308,50 @@
|
|||||||
matchesEl.hidden = true;
|
matchesEl.hidden = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getModeElements(modeEls) {
|
||||||
|
return Array.isArray(modeEls)
|
||||||
|
? modeEls.filter((element) => element instanceof HTMLInputElement)
|
||||||
|
: [];
|
||||||
|
}
|
||||||
|
|
||||||
function updateModeUi() {
|
function updateModeUi() {
|
||||||
const {
|
const {
|
||||||
cipherEl,
|
cipherEl,
|
||||||
inputEl,
|
inputEl,
|
||||||
modeToggleEl,
|
modeEls,
|
||||||
matchesEl,
|
matchesEl,
|
||||||
inputLabelEl,
|
inputLabelEl,
|
||||||
cipherLabelEl
|
cipherLabelEl
|
||||||
} = getElements();
|
} = getElements();
|
||||||
|
|
||||||
const reverseMode = isReverseMode();
|
const reverseMode = isReverseMode();
|
||||||
|
const anagramMode = isAnagramMode();
|
||||||
|
const radioEls = getModeElements(modeEls);
|
||||||
|
|
||||||
if (modeToggleEl) {
|
radioEls.forEach((element) => {
|
||||||
modeToggleEl.checked = reverseMode;
|
element.checked = String(element.value || "") === state.activeMode;
|
||||||
}
|
});
|
||||||
|
|
||||||
if (inputLabelEl) {
|
if (inputLabelEl) {
|
||||||
inputLabelEl.textContent = reverseMode ? "Value" : "Text";
|
inputLabelEl.textContent = reverseMode ? "Value" : (anagramMode ? "Letters" : "Text");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cipherLabelEl) {
|
if (cipherLabelEl) {
|
||||||
cipherLabelEl.textContent = reverseMode ? "Cipher (disabled in reverse mode)" : "Cipher";
|
cipherLabelEl.textContent = (reverseMode || anagramMode) ? "Cipher (not used in this mode)" : "Cipher";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cipherEl) {
|
if (cipherEl) {
|
||||||
cipherEl.disabled = reverseMode;
|
const disableCipher = reverseMode || anagramMode;
|
||||||
cipherEl.closest(".alpha-gematria-field")?.classList.toggle("is-disabled", reverseMode);
|
cipherEl.disabled = disableCipher;
|
||||||
|
cipherEl.closest(".alpha-gematria-field")?.classList.toggle("is-disabled", disableCipher);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputEl) {
|
if (inputEl) {
|
||||||
inputEl.placeholder = reverseMode ? "Enter a whole number, e.g. 33" : "Type or paste text";
|
inputEl.placeholder = reverseMode
|
||||||
|
? "Enter a whole number, e.g. 33"
|
||||||
|
: (anagramMode ? "Type letters or a word, e.g. listen" : "Type or paste text");
|
||||||
inputEl.inputMode = reverseMode ? "numeric" : "text";
|
inputEl.inputMode = reverseMode ? "numeric" : "text";
|
||||||
inputEl.spellcheck = !reverseMode;
|
inputEl.spellcheck = !(reverseMode || anagramMode);
|
||||||
|
|
||||||
const nextValue = getCurrentInputText();
|
const nextValue = getCurrentInputText();
|
||||||
if (inputEl.value !== nextValue) {
|
if (inputEl.value !== nextValue) {
|
||||||
@@ -335,7 +359,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!reverseMode) {
|
if (!reverseMode && !anagramMode) {
|
||||||
clearReverseMatches(matchesEl);
|
clearReverseMatches(matchesEl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -369,6 +393,17 @@
|
|||||||
return payload;
|
return payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadAnagramLookup(text) {
|
||||||
|
const cacheKey = String(text || "").trim().toLowerCase();
|
||||||
|
if (state.anagramLookupCache.has(cacheKey)) {
|
||||||
|
return state.anagramLookupCache.get(cacheKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = await window.TarotDataService?.loadWordAnagrams?.(text);
|
||||||
|
state.anagramLookupCache.set(cacheKey, payload);
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
function renderReverseLookupMatches(payload, numericValue) {
|
function renderReverseLookupMatches(payload, numericValue) {
|
||||||
const { resultEl, breakdownEl, matchesEl } = getElements();
|
const { resultEl, breakdownEl, matchesEl } = getElements();
|
||||||
if (!resultEl || !breakdownEl || !matchesEl) {
|
if (!resultEl || !breakdownEl || !matchesEl) {
|
||||||
@@ -489,6 +524,93 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderAnagramMatches(payload) {
|
||||||
|
const { resultEl, breakdownEl, matchesEl } = getElements();
|
||||||
|
if (!resultEl || !breakdownEl || !matchesEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const matches = Array.isArray(payload?.matches) ? payload.matches : [];
|
||||||
|
const count = Number(payload?.count);
|
||||||
|
const displayCount = Number.isFinite(count) ? count : matches.length;
|
||||||
|
const visibleMatches = matches.slice(0, 120);
|
||||||
|
const letterCount = Number(payload?.letterCount);
|
||||||
|
|
||||||
|
resultEl.textContent = `Anagrams: ${formatCount(displayCount)}`;
|
||||||
|
|
||||||
|
if (!displayCount) {
|
||||||
|
breakdownEl.textContent = "No exact dictionary anagrams matched these letters.";
|
||||||
|
matchesEl.hidden = false;
|
||||||
|
setMatchesMessage(matchesEl, "Try another letter set or a different spelling.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
breakdownEl.textContent = `Found ${formatCount(displayCount)} anagrams for ${formatCount(letterCount)} letters.${displayCount > visibleMatches.length ? ` Showing first ${formatCount(visibleMatches.length)}.` : ""}`;
|
||||||
|
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
visibleMatches.forEach((match) => {
|
||||||
|
const cardEl = document.createElement("article");
|
||||||
|
cardEl.className = "alpha-gematria-match";
|
||||||
|
|
||||||
|
const wordEl = document.createElement("div");
|
||||||
|
wordEl.className = "alpha-gematria-match-word";
|
||||||
|
wordEl.textContent = String(match?.word || "--");
|
||||||
|
cardEl.appendChild(wordEl);
|
||||||
|
|
||||||
|
const definition = String(match?.definition || "").trim();
|
||||||
|
if (definition) {
|
||||||
|
const definitionEl = document.createElement("div");
|
||||||
|
definitionEl.className = "alpha-gematria-match-definition";
|
||||||
|
definitionEl.textContent = definition;
|
||||||
|
cardEl.appendChild(definitionEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment.appendChild(cardEl);
|
||||||
|
});
|
||||||
|
|
||||||
|
matchesEl.replaceChildren(fragment);
|
||||||
|
matchesEl.hidden = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderAnagramResult() {
|
||||||
|
const { resultEl, breakdownEl, matchesEl } = getElements();
|
||||||
|
if (!resultEl || !breakdownEl || !matchesEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawText = state.anagramInputText;
|
||||||
|
if (!String(rawText || "").trim()) {
|
||||||
|
resultEl.textContent = "Anagrams: --";
|
||||||
|
breakdownEl.textContent = "Enter letters to search for exact dictionary anagrams.";
|
||||||
|
matchesEl.hidden = false;
|
||||||
|
setMatchesMessage(matchesEl, "Anagram Maker finds exact word anagrams from the API word index.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestId = state.anagramRequestId + 1;
|
||||||
|
state.anagramRequestId = requestId;
|
||||||
|
resultEl.textContent = "Anagrams: --";
|
||||||
|
breakdownEl.textContent = "Searching anagram index...";
|
||||||
|
matchesEl.hidden = false;
|
||||||
|
setMatchesMessage(matchesEl, "Loading anagrams...");
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payload = await loadAnagramLookup(rawText);
|
||||||
|
if (requestId !== state.anagramRequestId || !isAnagramMode()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
renderAnagramMatches(payload);
|
||||||
|
} catch {
|
||||||
|
if (requestId !== state.anagramRequestId || !isAnagramMode()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resultEl.textContent = "Anagrams: --";
|
||||||
|
breakdownEl.textContent = "Anagram lookup is unavailable right now.";
|
||||||
|
matchesEl.hidden = false;
|
||||||
|
setMatchesMessage(matchesEl, "Unable to load anagrams from the API.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function computeGematria(text, cipher, baseAlphabet) {
|
function computeGematria(text, cipher, baseAlphabet) {
|
||||||
const normalizedInput = normalizeGematriaText(text);
|
const normalizedInput = normalizeGematriaText(text);
|
||||||
const scriptMap = state.scriptCharMap instanceof Map
|
const scriptMap = state.scriptCharMap instanceof Map
|
||||||
@@ -569,11 +691,16 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isAnagramMode()) {
|
||||||
|
void renderAnagramResult();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
renderForwardGematriaResult();
|
renderForwardGematriaResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
function bindGematriaListeners() {
|
function bindGematriaListeners() {
|
||||||
const { cipherEl, inputEl, modeToggleEl } = getElements();
|
const { cipherEl, inputEl, modeEls } = getElements();
|
||||||
if (state.listenersBound || !cipherEl || !inputEl) {
|
if (state.listenersBound || !cipherEl || !inputEl) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -586,16 +713,24 @@
|
|||||||
inputEl.addEventListener("input", () => {
|
inputEl.addEventListener("input", () => {
|
||||||
if (isReverseMode()) {
|
if (isReverseMode()) {
|
||||||
state.reverseInputText = inputEl.value || "";
|
state.reverseInputText = inputEl.value || "";
|
||||||
|
} else if (isAnagramMode()) {
|
||||||
|
state.anagramInputText = inputEl.value || "";
|
||||||
} else {
|
} else {
|
||||||
state.forwardInputText = inputEl.value || "";
|
state.forwardInputText = inputEl.value || "";
|
||||||
}
|
}
|
||||||
renderGematriaResult();
|
renderGematriaResult();
|
||||||
});
|
});
|
||||||
|
|
||||||
modeToggleEl?.addEventListener("change", () => {
|
getModeElements(modeEls).forEach((modeEl) => {
|
||||||
state.activeMode = modeToggleEl.checked ? "reverse" : "forward";
|
modeEl.addEventListener("change", () => {
|
||||||
updateModeUi();
|
if (!modeEl.checked) {
|
||||||
renderGematriaResult();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.activeMode = String(modeEl.value || "forward").trim() || "forward";
|
||||||
|
updateModeUi();
|
||||||
|
renderGematriaResult();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
state.listenersBound = true;
|
state.listenersBound = true;
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
let tabAll, tabHebrew, tabGreek, tabEnglish, tabArabic, tabEnochian;
|
let tabAll, tabHebrew, tabGreek, tabEnglish, tabArabic, tabEnochian;
|
||||||
let searchInputEl, searchClearEl, typeFilterEl;
|
let searchInputEl, searchClearEl, typeFilterEl;
|
||||||
let gematriaCipherEl, gematriaInputEl, gematriaResultEl, gematriaBreakdownEl;
|
let gematriaCipherEl, gematriaInputEl, gematriaResultEl, gematriaBreakdownEl;
|
||||||
let gematriaReverseEl, gematriaMatchesEl, gematriaInputLabelEl, gematriaCipherLabelEl;
|
let gematriaModeEls, gematriaMatchesEl, gematriaInputLabelEl, gematriaCipherLabelEl;
|
||||||
|
|
||||||
function getElements() {
|
function getElements() {
|
||||||
listEl = document.getElementById("alpha-letter-list");
|
listEl = document.getElementById("alpha-letter-list");
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
gematriaInputEl = document.getElementById("alpha-gematria-input");
|
gematriaInputEl = document.getElementById("alpha-gematria-input");
|
||||||
gematriaResultEl = document.getElementById("alpha-gematria-result");
|
gematriaResultEl = document.getElementById("alpha-gematria-result");
|
||||||
gematriaBreakdownEl = document.getElementById("alpha-gematria-breakdown");
|
gematriaBreakdownEl = document.getElementById("alpha-gematria-breakdown");
|
||||||
gematriaReverseEl = document.getElementById("alpha-gematria-reverse");
|
gematriaModeEls = Array.from(document.querySelectorAll("input[name='alpha-tool-mode']"));
|
||||||
gematriaMatchesEl = document.getElementById("alpha-gematria-matches");
|
gematriaMatchesEl = document.getElementById("alpha-gematria-matches");
|
||||||
gematriaInputLabelEl = document.getElementById("alpha-gematria-input-label");
|
gematriaInputLabelEl = document.getElementById("alpha-gematria-input-label");
|
||||||
gematriaCipherLabelEl = document.getElementById("alpha-gematria-cipher-label");
|
gematriaCipherLabelEl = document.getElementById("alpha-gematria-cipher-label");
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
inputEl: gematriaInputEl,
|
inputEl: gematriaInputEl,
|
||||||
resultEl: gematriaResultEl,
|
resultEl: gematriaResultEl,
|
||||||
breakdownEl: gematriaBreakdownEl,
|
breakdownEl: gematriaBreakdownEl,
|
||||||
modeToggleEl: gematriaReverseEl,
|
modeEls: gematriaModeEls,
|
||||||
matchesEl: gematriaMatchesEl,
|
matchesEl: gematriaMatchesEl,
|
||||||
inputLabelEl: gematriaInputLabelEl,
|
inputLabelEl: gematriaInputLabelEl,
|
||||||
cipherLabelEl: gematriaCipherLabelEl
|
cipherLabelEl: gematriaCipherLabelEl
|
||||||
|
|||||||
18
index.html
18
index.html
@@ -629,10 +629,20 @@
|
|||||||
<div class="planet-meta-card alpha-gematria-card">
|
<div class="planet-meta-card alpha-gematria-card">
|
||||||
<strong>Gematria Lookup</strong>
|
<strong>Gematria Lookup</strong>
|
||||||
<div class="alpha-gematria-toolbar">
|
<div class="alpha-gematria-toolbar">
|
||||||
<label class="alpha-gematria-toggle" for="alpha-gematria-reverse">
|
<fieldset class="alpha-tool-mode-group" aria-label="Alphabet tool mode">
|
||||||
<input id="alpha-gematria-reverse" type="checkbox">
|
<label class="alpha-tool-mode-option" for="alpha-tool-mode-gematria">
|
||||||
<span>Reverse lookup</span>
|
<input id="alpha-tool-mode-gematria" name="alpha-tool-mode" type="radio" value="forward" checked>
|
||||||
</label>
|
<span>Gematria</span>
|
||||||
|
</label>
|
||||||
|
<label class="alpha-tool-mode-option" for="alpha-tool-mode-reverse">
|
||||||
|
<input id="alpha-tool-mode-reverse" name="alpha-tool-mode" type="radio" value="reverse">
|
||||||
|
<span>Reverse Lookup</span>
|
||||||
|
</label>
|
||||||
|
<label class="alpha-tool-mode-option" for="alpha-tool-mode-anagram">
|
||||||
|
<input id="alpha-tool-mode-anagram" name="alpha-tool-mode" type="radio" value="anagram">
|
||||||
|
<span>Anagram Maker</span>
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
<div class="alpha-gematria-controls">
|
<div class="alpha-gematria-controls">
|
||||||
<label class="alpha-gematria-field alpha-gematria-field-cipher" for="alpha-gematria-cipher">
|
<label class="alpha-gematria-field alpha-gematria-field-cipher" for="alpha-gematria-cipher">
|
||||||
|
|||||||
Reference in New Issue
Block a user