(function(){
"use strict";
// ── Data source config ──────────────────────────────────────────
// Production (WordPress): this endpoint scans /wp-content/uploads/qa-data/
// and returns { "categories": [...] }. Locally, that route 404s, so we
// fall back to a plain qa-data/ folder with an index.json file listing
// the category JSON filenames to fetch individually.
var CURRENT_LANG = /^\/es(\/|$)/.test(location.pathname) ? "es" : "en";
var DATA_CONFIG = {
wpEndpoint: "/wp-json/veriskey/v1/qa?lang=" + CURRENT_LANG,
localIndex: "qa-data/index.json",
localBase: "qa-data/"
};
var categories = [];
var currentPath = [];
// "General" (company-wide trust questions not tied to any one product)
// is just another category JSON file, like any product category — see
// qa-data/general.json. It sorts first by giving it a low "order" value
// in that file, same mechanism as everything else.
function byOrder(a, b){ return (a.order || 0) - (b.order || 0); }
function escapeHtml(str){
return String(str == null ? "" : str).replace(/[&"']/g, function(ch){
return ({ "&": "&", "": ">", '"': """, "'": "'" })[ch];
});
}
var Preguntas Frecuentes_ICON_SVG = '';
// Named per-question icons. A qa's "icon" field picks one of these by name;
// unrecognized or missing names fall back to the generic Preguntas Frecuentes_ICON_SVG above.
var QA_ICONS = {
shield: '',
cart: '',
devices: '',
key: '',
support: '',
layout: '',
badge: ''
};
// Tabs/cards need a short label even when a category's full "title" is a
// long product name. Authors can set an explicit "shortTitle" in the JSON;
// if absent, derive one by stripping a trailing "(...)" qualifier and
// capping word count, so a long title never blows up the pill layout.
function deriveShortTitle(title){
var stripped = title.replace(/\s*\([^)]*\)\s*$/, "").trim();
var words = stripped.split(/\s+/);
if (words.length <= 4) return stripped;
return words.slice(0, 4).join(" ");
}
// ── Normalize + validate a category/subcategory node ────────────
function normalizeNode(node){
if (!node || typeof node !== "object") return null;
if (typeof node.id !== "string" || !node.id) return null;
if (typeof node.title !== "string" || !node.title) return null;
var qas = Array.isArray(node.qas) ? node.qas : [];
var cleanQas = [];
for (var i = 0; i < qas.length; i++) {
var q = qas[i];
if (q && typeof q.question === "string" && typeof q.answer === "string") {
cleanQas.push({
id: typeof q.id === "string" ? q.id : q.question,
question: q.question,
answer: q.answer,
icon: typeof q.icon === "string" ? q.icon : "",
popular: q.popular === true
});
}
}
var subs = Array.isArray(node.subcategories) ? node.subcategories : [];
var cleanSubs = [];
for (var j = 0; j < subs.length; j++) {
var normalized = normalizeNode(subs[j]);
if (normalized) cleanSubs.push(normalized);
}
return {
id: node.id,
title: node.title,
shortTitle: typeof node.shortTitle === "string" && node.shortTitle ? node.shortTitle : deriveShortTitle(node.title),
icon: typeof node.icon === "string" ? node.icon : "",
order: typeof node.order === "number" ? node.order : 0,
description: typeof node.description === "string" ? node.description : "",
qas: cleanQas,
subcategories: cleanSubs
};
}
// ── Load categories: try WP endpoint first, fall back to local ──
function loadCategories(){
return fetch(DATA_CONFIG.wpEndpoint, { headers: { Accept: "application/json" } })
.then(function(res){
if (!res.ok) throw new Error("wp endpoint unavailable");
return res.json();
})
.then(function(data){
if (!data || !Array.isArray(data.categories)) throw new Error("unexpected wp payload");
return data.categories;
})
.catch(function(){
return loadLocalCategories();
})
.then(function(rawCategories){
var result = [];
for (var i = 0; i < rawCategories.length; i++) {
var normalized = normalizeNode(rawCategories[i]);
if (normalized) result.push(normalized);
else console.warn("[Preguntas Frecuentes] Skipped malformed category at index " + i);
}
return result;
});
}
function loadLocalCategories(){
return fetch(DATA_CONFIG.localIndex)
.then(function(res){
if (!res.ok) throw new Error("local index missing");
return res.json();
})
.then(function(filenames){
if (!Array.isArray(filenames)) return [];
var fetches = filenames.map(function(filename){
return fetch(DATA_CONFIG.localBase + filename)
.then(function(res){
if (!res.ok) throw new Error("fetch failed: " + filename);
return res.json();
})
.catch(function(err){
console.warn("[Preguntas Frecuentes] Skipped malformed file " + filename + ":", err && err.message);
return null;
});
});
return Promise.all(fetches);
})
.then(function(results){
return results.filter(function(r){ return r !== null; });
})
.catch(function(err){
console.warn("[Preguntas Frecuentes] Could not load local qa-data:", err && err.message);
return [];
});
}
// ── Path / hash helpers ──────────────────────────────────────────
function parseHash(){
var raw = location.hash.replace(/^#/, "");
if (!raw) return [];
return raw.split("/").filter(Boolean).map(function(s){
try { return decodeURIComponent(s); } catch (e) { return s; }
});
}
function hashForPath(path){
return "#" + path.map(encodeURIComponent).join("/");
}
function findChain(path){
if (!path.length) return null;
var level = categories;
var chain = [];
for (var i = 0; i < path.length; i++) {
var node = null;
for (var j = 0; j 0);
renderSubcats(subcatsEl, node.subcategories);
}
function renderTabs(container, activeId){
var sorted = categories.slice().sort(byOrder);
container.innerHTML = sorted.map(function(cat){
var cls = "tab" + (cat.id === activeId ? " active" : "");
return '";
}).join("");
var btns = container.querySelectorAll(".tab");
btns.forEach(function(btn){
btn.addEventListener("click", function(){
setPath([btn.getAttribute("data-id")]);
});
});
}
function renderBreadcrumb(container, chain){
if (chain.length <= 1) {
container.style.display = "none";
container.innerHTML = "";
return;
}
container.style.display = "flex";
var parts = [];
for (var i = 0; i < chain.length; i++) {
var isLast = i === chain.length - 1;
if (isLast) {
parts.push('' + escapeHtml(chain[i].title) + "");
} else {
parts.push('");
parts.push('›');
}
}
container.innerHTML = parts.join("");
var crumbBtns = container.querySelectorAll("button[data-idx]");
crumbBtns.forEach(function(btn){
btn.addEventListener("click", function(){
var idx = parseInt(btn.getAttribute("data-idx"), 10);
setPath(currentPath.slice(0, idx + 1));
});
});
}
function renderFaqList(container, qas, hasSubcategories){
var sorted = qas.slice().sort(function(a, b){
return (b.popular ? 1 : 0) - (a.popular ? 1 : 0);
});
if (!sorted.length) {
container.innerHTML = hasSubcategories ? "" : '
No questions in this section yet.
';
return;
}
// When a category also has subcategories, label its own direct questions
// "General" so it reads as "the default section" rather than orphaned
// questions floating above the subcategory cards.
var heading = hasSubcategories ? 'General' : "";
container.innerHTML = heading + sorted.map(function(qa, i){
var openCls = i === 0 ? " open" : "";
var icon = (qa.icon && QA_ICONS[qa.icon]) || Preguntas Frecuentes_ICON_SVG;
return (
'
' +
'" +
'
' + qa.answer + "
" +
"
"
);
}).join("");
var items = container.querySelectorAll(".faq-item");
items.forEach(function(item){
var btn = item.querySelector(".faq-q");
var ans = item.querySelector(".faq-a");
btn.addEventListener("click", function(){
var isOpen = item.classList.toggle("open");
ans.style.maxHeight = isOpen ? ans.scrollHeight + "px" : null;
});
if (item.classList.contains("open")) {
ans.style.maxHeight = ans.scrollHeight + "px";
}
});
}
function renderSubcats(container, subcats){
if (!subcats.length) {
container.style.display = "none";
container.innerHTML = "";
return;
}
container.style.display = "grid";
var sorted = subcats.slice().sort(byOrder);
container.innerHTML = sorted.map(function(sc){
var icon = sc.icon ? escapeHtml(sc.icon) : "📁";
var desc = sc.description ? '' + escapeHtml(sc.description) + "" : "";
return (
'"
);
}).join("");
var cards = container.querySelectorAll(".subcat-card");
cards.forEach(function(btn){
btn.addEventListener("click", function(){
setPath(currentPath.concat([btn.getAttribute("data-id")]));
});
});
}
// ── Init ──────────────────────────────────────────────────────────
loadCategories().then(function(loaded){
categories = loaded;
var initial = parseHash();
currentPath = initial.length ? initial : [];
render();
});
})();