function _availableTargets(metadata, type, source) {
return Object.keys(metadata[type]?.sources?.[source]?.targets ?? {});
}
function _availableSources(metadata, type, target) {
const sources = metadata[type]?.sources ?? {};
return Object.keys(sources).filter(source =>
Object.keys(sources[source]?.targets ?? {}).includes(target)
);
}
function availableTargets(metadata, type, source) {
switch (type) {
case "main":
case "ipa":
case "glossary":
return _availableTargets(metadata, type, source);
case "ipa-merged":
console.warn(`availableTargets called for ipa-merged with source=${source}`);
default:
return null;
}
}
function availableSources(metadata, type, target) {
switch (type) {
case "main":
case "ipa":
case "glossary":
return _availableSources(metadata, type, target);
case "ipa-merged":
console.warn(`availableSources called for ipa-merged with target=${target}`);
default:
return null;
}
}
function filterDropdown(box, allowed) {
const set = new Set(allowed);
box.querySelectorAll("div[data-value]").forEach(div => {
div.dataset.matchesFilter = set.has(div.dataset.value) ? "1" : "0";
div.style.display = (div.dataset.matchesFilter === "1" && div.dataset.matchesSearch !== "0") ? "" : "none";
});
}
function buildUrl(type, source, target) {
const BASE_URL =
"https://huggingface.co/datasets/daxida/wty-release/resolve/main/latest/dict";
switch (type) {
case "main":
return `${BASE_URL}/${source}/${target}/wty-${source}-${target}.zip`;
case "ipa":
return `${BASE_URL}/${source}/${target}/wty-${source}-${target}-ipa.zip`;
case "ipa-merged":
return `${BASE_URL}/all/${target}/wty-${target}-ipa.zip`;
case "glossary":
return `${BASE_URL}/${source}/${target}/wty-${source}-${target}-gloss.zip`;
default:
return null;
}
}
function setupCombobox(box) {
if (!box) return;
const search = box.querySelector("input:not([type=hidden])");
const dropdown = box.querySelector(".dl-source-dropdown, .dl-target-dropdown");
const hidden = box.querySelector("input[type=hidden]");
const items = Array.from(dropdown.querySelectorAll("option"));
dropdown.innerHTML = "";
items.forEach((opt) => {
const div = document.createElement("div");
div.textContent = opt.textContent;
div.dataset.value = opt.value;
div.addEventListener("mousedown", () => {
search.value = opt.textContent;
hidden.value = opt.value;
hidden.dataset.label = opt.textContent;
dropdown.style.display = "none";
hidden.dispatchEvent(new Event("change", { bubbles: true }));
});
dropdown.appendChild(div);
});
function clearSelection() {
hidden.value = "";
hidden.dispatchEvent(new Event("change", { bubbles: true }));
}
search.addEventListener("focus", () => (dropdown.style.display = "block"));
search.addEventListener("blur", () =>
setTimeout(() => (dropdown.style.display = "none"), 150),
);
search.addEventListener("input", () => {
const q = search.value.toLowerCase();
dropdown.style.display = "block";
dropdown.querySelectorAll("div").forEach((div) => {
div.dataset.matchesSearch = div.textContent.toLowerCase().includes(q) ? "1" : "0";
div.style.display = (div.dataset.matchesFilter !== "0" && div.dataset.matchesSearch === "1") ? "" : "none";
});
if (hidden.value && search.value !== hidden.dataset.label) {
clearSelection();
}
if (!q) clearSelection();
});
search.addEventListener("keydown", (e) => {
if (e.key !== "Enter") return;
e.preventDefault();
const firstVisible = Array.from(
dropdown.querySelectorAll("div")
).find(div => div.style.display !== "none");
if (!firstVisible) return;
search.value = firstVisible.textContent;
hidden.value = firstVisible.dataset.value;
dropdown.style.display = "none";
hidden.dispatchEvent(new Event("change", { bubbles: true }));
});
}
function setupRow(row, metadata) {
const type = row.dataset.type;
const sourceHidden = row.querySelector(".dl-source");
const targetHidden = row.querySelector(".dl-target");
const btn = row.querySelector(".dl-btn");
const info = row.querySelector(".dl-info");
row.querySelectorAll(".dl-source-combobox, .dl-target-combobox").forEach(
setupCombobox,
);
function update() {
const source = sourceHidden?.value;
const target = targetHidden?.value;
if (source) {
const allowedTargets = availableTargets(metadata, type, source);
filterDropdown(row.querySelector(".dl-target-combobox"), allowedTargets);
}
if (target && type !== "ipa-merged") {
const allowedSources = availableSources(metadata, type, target);
filterDropdown(row.querySelector(".dl-source-combobox"), allowedSources);
}
if (!target || (sourceHidden && !source)) {
btn.disabled = true;
info.textContent = "Select the language(s)";
return;
}
const url = buildUrl(type, source, target);
if (!url) return;
const downloadUrl = `${url}?download=true`;
btn.disabled = false;
info.innerHTML = "";
const copyBtn = document.createElement("button");
copyBtn.type = "button";
copyBtn.textContent = "📋 Copy URL";
copyBtn.className = "copy-url-btn";
copyBtn.onclick = async () => {
try {
await navigator.clipboard.writeText(downloadUrl);
copyBtn.textContent = "✅ Copied!";
setTimeout(() => (copyBtn.textContent = "📋 Copy URL"), 1500);
} catch {
copyBtn.textContent = "❌ Failed";
}
};
info.appendChild(copyBtn);
btn.onclick = () => {
window.location.href = downloadUrl;
};
}
sourceHidden?.addEventListener("change", update);
targetHidden?.addEventListener("change", update);
update();
}
const REPO_NAME = "wiktionary-to-yomitan";
const BRANCH = "gh-pages"; const base = document.querySelector('base')?.href || `https://yomidevs.github.io/${REPO_NAME}/`;
const metadataPromise = fetch(`${base}release_metadata.json`)
.then(res => res.json())
.then(json => json["dicts"]);
document$.subscribe(function () {
metadataPromise.then((metadata) => {
const table = document.querySelector(".download-table");
if (!table) return;
table.classList.add("loaded");
table.querySelectorAll("tr[data-type]").forEach(row => {
setupRow(row, metadata);
});
});
});