feat: árvore em todas as peças, com países e ramo Assuntos

This commit is contained in:
2026-09-30 19:33:02 +02:00
parent 19c32f346f
commit b176c726e4
+31 -7
View File
@@ -1495,6 +1495,19 @@ function parsePlace(raw) {
const uf = CITY_UF.get(k); const uf = CITY_UF.get(k);
return uf ? { state: UF[uf], city: raw.trim().replace(/\.$/, '') } : null; return uf ? { state: UF[uf], city: raw.trim().replace(/\.$/, '') } : null;
} }
// places outside Brazil hang from their country instead of "Brasil"
const COUNTRIES = new Map(['Alemanha', 'França', 'Paraguai', 'Argentina', 'Uruguai', 'Bolívia', 'Peru', 'Chile', 'Colômbia', 'Venezuela',
'Portugal', 'Espanha', 'Itália', 'Inglaterra', 'Reino Unido', 'Estados Unidos', 'Suíça', 'Bélgica', 'Holanda', 'Países Baixos',
'Áustria', 'Rússia', 'Japão', 'China', 'Índia', 'Egito', 'África do Sul', 'Angola', 'Moçambique', 'Cabo Verde', 'Canadá', 'México',
'Cuba', 'Equador', 'Suriname', 'Guiana', 'Guiana Francesa', 'Dinamarca', 'Suécia', 'Noruega', 'Polônia', 'Grécia', 'Turquia',
'Escócia', 'Irlanda'].map(n => [deburr(n), n]));
function parseForeign(raw) {
const t = raw.trim().replace(/\.$/, '');
if (COUNTRIES.has(deburr(t))) return { country: COUNTRIES.get(deburr(t)) };
const m = t.match(/^(.+?)\s*(?:\(|,\s*)([^(),]+)\)?$/);
if (m && COUNTRIES.has(deburr(m[2]))) return { country: COUNTRIES.get(deburr(m[2])), city: m[1].trim() };
return null;
}
const NO_PLACE = /^\[?s\.?\s?l\.?\]?$|^\[?s\.?\s?n\.?\]?$/i; const NO_PLACE = /^\[?s\.?\s?l\.?\]?$|^\[?s\.?\s?n\.?\]?$/i;
// every place-bearing tag of a piece, PT side only // every place-bearing tag of a piece, PT side only
@@ -1540,6 +1553,8 @@ function subjectTree(meta) {
if (p.sub) put(path.concat(p.sub)).place = true; if (p.sub) put(path.concat(p.sub)).place = true;
hasPlace = true; return; hasPlace = true; return;
} }
const fp = parseForeign(tag);
if (fp) { place([fp.country].concat(fp.city ? [fp.city] : [])); return; }
// "Espírito Santo (Estado) - Colônias": a state carrying a topic // "Espírito Santo (Estado) - Colônias": a state carrying a topic
const parts = tag.split(/\s+-\s+/); const parts = tag.split(/\s+-\s+/);
const sm = parts[0].match(/^(.+?)\s*\(Estado\)$/i); const sm = parts[0].match(/^(.+?)\s*\(Estado\)$/i);
@@ -1559,14 +1574,27 @@ function subjectTree(meta) {
} }
others.push(tag); others.push(tag);
}); });
// a lone "Brasil" is no tree — it reads better as a chip with the rest
if (hasPlace && root.kids.get('Brasil').kids.size === 0) { root.kids.clear(); others.push('Brasil'); }
return { root, others }; return { root, others };
} }
// "Assuntos": everything that is not a place, "A - B - C" becoming a path so pieces
// sharing a prefix share a branch. Shooting conditions (Externa, Diurna…) are noise here.
const TOMADA = /^(?:externa|interna|diurna|noturna|horizontal|vertical)$/i;
function themeTree(others) {
const root = { kids: new Map() };
others.filter(t => !TOMADA.test(t.trim())).forEach(tag => {
tag.split(/\s+-\s+/).reduce((n, label) => {
if (!n.kids.has(label)) n.kids.set(label, { kids: new Map(), place: false });
return n.kids.get(label);
}, root);
});
return root;
}
function renderSubjectTree(el, meta) { function renderSubjectTree(el, meta) {
el.replaceChildren(); el.replaceChildren();
const { root, others } = subjectTree(meta); const { root, others } = subjectTree(meta);
const themes = themeTree(others);
const draw = (node, label, into) => { const draw = (node, label, into) => {
const d = document.createElement('div'); const d = document.createElement('div');
d.className = 'tn ' + (node.place ? 'place' : 'topic'); d.className = 'tn ' + (node.place ? 'place' : 'topic');
@@ -1576,11 +1604,7 @@ function renderSubjectTree(el, meta) {
into.appendChild(d); into.appendChild(d);
}; };
root.kids.forEach((kid, name) => draw(kid, name, el)); root.kids.forEach((kid, name) => draw(kid, name, el));
if (others.length) { if (themes.kids.size) draw(Object.assign(themes, { place: true }), 'Assuntos', el);
const o = document.createElement('div'); o.className = 'to';
others.forEach(t => { const c = document.createElement('span'); c.textContent = t; o.appendChild(c); });
el.appendChild(o);
}
} }
// estado / cidade / tópico facets of one piece, as sets of plain keys: // estado / cidade / tópico facets of one piece, as sets of plain keys: