1(function () { 2 "use strict"; 3 4 const TRACK_LABELS = { 5 todos: "Todos", 6 captura_institucional: "Captura institucional", 7 decisoes_impacto: "Decisões de impacto", 8 }; 9 10 const GROUP_LABELS = { 11 todos: "Todos os grupos", 12 corrupcao_judicial: "Corrupção judicial", 13 cnj_disciplinar: "CNJ disciplinar", 14 penduricalhos: "Penduricalhos", 15 chokepoint_stf: "Chokepoint STF", 16 eleitoral_tse: "Eleitoral / TSE", 17 outros_judiciario: "Outros (judiciário)", 18 soltura_hc: "Soltura / HC", 19 progressao_regime: "Progressão de regime", 20 arquivamento: "Arquivamento / nulidade", 21 jurisprudencia_estrutural: "Jurisprudência estrutural", 22 foragidos_impacto: "Foragidos", 23 }; 24 25 const CRIME_LABELS = { 26 todos: "Todos crimes", 27 trafico: "Tráfico", 28 homicidio: "HomicÃdio", 29 estupro: "Crimes sexuais", 30 latrocinio: "LatrocÃnio", 31 foragido: "Foragido", 32 corrupcao: "Corrupção", 33 milicia: "MilÃcia", 34 pcc: "PCC", 35 }; 36 37 const EV_LABELS = { 38 todos: "Toda evidência", 39 "ev-confirmed": "Confirmado", 40 "ev-alleged": "Alegado / pendente", 41 }; 42 43 const ICONS = { 44 penduricalhos: 45 '<svg class="icon icon-stroke card-icon" viewBox="0 0 24 24" aria-hidden="true"><rect x="2" y="7" width="20" height="14" rx="2"/><path d="M16 7V5a2 2 0 00-2-2h-4a2 2 0 00-2 2v2"/><path d="M12 12v4"/><path d="M10 14h4"/></svg>', 46 corrupcao_judicial: 47 '<svg class="icon icon-stroke card-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3v18"/><path d="M5 8h14"/><path d="M7 8l5 5 5-5"/></svg>', 48 cnj_disciplinar: 49 '<svg class="icon icon-stroke card-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>', 50 chokepoint_stf: 51 '<svg class="icon icon-stroke card-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 21h18"/><path d="M5 21V8l7-4 7 4v13"/><path d="M9 21v-6h6v6"/></svg>', 52 eleitoral_tse: 53 '<svg class="icon icon-stroke card-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h11"/></svg>', 54 soltura_hc: 55 '<svg class="icon icon-stroke card-icon" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 019.9-1"/><path d="M12 16v2"/></svg>', 56 progressao_regime: 57 '<svg class="icon icon-stroke card-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14"/><path d="M13 5l7 7-7 7"/></svg>', 58 arquivamento: 59 '<svg class="icon icon-stroke card-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z"/></svg>', 60 jurisprudencia_estrutural: 61 '<svg class="icon icon-stroke card-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 19.5A2.5 2.5 0 016.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 014 19.5v-15A2.5 2.5 0 016.5 2z"/></svg>', 62 foragidos_impacto: 63 '<svg class="icon icon-stroke card-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>', 64 trafico: 65 '<svg class="icon icon-stroke card-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v6l4 2"/></svg>', 66 default: 67 '<svg class="icon icon-stroke card-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 8v4"/><path d="M12 16h.01"/></svg>', 68 }; 69 70 const state = { 71 track: "todos", 72 grupo: "todos", 73 crime: "todos", 74 evidence: "todos", 75 q: "", 76 }; 77 78 let raw = null; 79 let cards = []; 80 81 function esc(s) { 82 return String(s || "").replace(/[&<>"']/g, function (c) { 83 return { 84 "&": "&", 85 "<": "<", 86 ">": ">", 87 '"': """, 88 "'": "'", 89 }[c]; 90 }); 91 } 92 93 function iconFor(card) { 94 if (card.crime_tags && card.crime_tags.indexOf("foragido") !== -1) { 95 return ICONS.foragidos_impacto; 96 } 97 if (card.crime_tags && card.crime_tags.indexOf("trafico") !== -1) { 98 return ICONS.trafico; 99 } 100 return ICONS[card.grupo] || ICONS.default; 101 } 102 103 function fmtNum(n) { 104 if (n == null) return "â"; 105 if (typeof n === "string") return n;
106 return n.toLocaleString("pt-BR"); 107 } 108 109 function hcStjDisplay() { 110 const divs = raw.divergencias_nao_reconciliadas || []; 111 const d = divs.find(function (x) { 112 return x.indicador === "hc_traficantes_stj_2024"; 113 }); 114 if (d && d.valor_a && d.valor_b) { 115 return fmtNum(d.valor_a.numero) + " / " + fmtNum(d.valor_b.numero); 116 } 117 return fmtNum((raw.stats || {}).hc_traficantes_stj_2024); 118 } 119 120 function renderStats() { 121 const s = raw.stats || {}; 122 const el = document.getElementById("statGrid"); 123 const items = [ 124 [raw.total, "casos no painel", ""], 125 [s.captura, "captura institucional", "gold"], 126 [s.decisoes, "decisões de impacto", "teal"], 127 [s.ev_confirmed, "ev-confirmed", ""], 128 [hcStjDisplay(), "HC tráfico STJ (2024, R4)", "red"], 129 [s.foragidos_hc_marco_aurelio_2020, "foragidos via HC (2020)", "red"], 130 ]; 131 el.innerHTML = items 132 .map(function (item) { 133 return ( 134 '<div class="stat-cell"><span class="stat-num ' + 135 (item[2] || "") + 136 '">' + 137 (typeof item[0] === "string" ? esc(item[0]) : fmtNum(item[0])) + 138 '</span><span class="stat-lbl">' + 139 item[1] + 140 "</span></div>" 141 ); 142 }) 143 .join(""); 144 } 145 146 function renderAlertas() { 147 const list = raw.alertas_sistemicos || []; 148 const el = document.getElementById("alertsGrid"); 149 if (!list.length) { 150 el.innerHTML = 151 '<p class="empty-state">Nenhum alerta sistêmico no corpus atual.</p>'; 152 return; 153 } 154 el.innerHTML = list 155 .map(function (a) { 156 const nivel = a.nivel || "medio"; 157 const label = 158 nivel === "alto" 159 ? "Risco alto" 160 : nivel === "baixo" 161 ? "Atenção" 162 : "Risco médio"; 163 return ( 164 '<div class="alert-card ' + 165 esc(nivel) + 166 '"><div class="alert-level">' + 167 esc(label) + 168 '</div><div class="alert-title">' + 169 esc(a.titulo) + 170 '</div><div class="alert-text">' + 171 esc(a.descricao) + 172 "</div></div>" 173 ); 174 }) 175 .join(""); 176 } 177 178 function renderDataTable() { 179 const s = raw.stats || {}; 180 const tbody = document.getElementById("dataTableBody"); 181 const rows = [ 182 ["Casos captura institucional", s.captura, raw.sync_date, "lawfare-timeline", "Extract JusMonitor"], 183 ["Casos decisões de impacto", s.decisoes, "2005â2026", "T-209 JustiçaWatch", "Sidecar reconciliado R1âR7"], 184 ["HC traficantes STJ (R4)", hcStjDisplay(), "2024", "STJ/ConJur à dossiê Seif", "Divergência não reconciliada"], 185 ["HC/RHC tráfico STJ (ConJur)", s.hc_stj_2024_conjur, "2024", "ConJur/STJ", "Até 26/dez/2024"], 186 ["HC tráfico privilegiado STJ", s.hc_stj_2024_privilegiado, "2024", "ConJur/STJ", "Aplicação do §4º"], 187 ["Concessões HC/RHC STJ (total)", s.hc_stj_2024_concessoes_total, "2024", "ConJur/STJ", "Base do percentual 49,1%"], 188 ["HC tráfico STF", s.hc_trafico_stf_2024, "2024", "Fontes públicas", "Agregado"], 189 ["Taxa reincidência BR", s.taxa_reincidencia_br, "â", "Fontes públicas", "Indicador contextual"], 190 ["População carcerária", s.populacao_carceraria, "â", "Fontes públicas", "Contexto"], 191 ["Foragidos HC Marco Aurélio", s.foragidos_hc_marco_aurelio_2020, "2020", "Estadão", "Investigação"], 192 ["Pendentes enriquecimento", raw.excluidos_pendente_enriquecimento, raw.sync_date, "corpus", "Lacuna de dados"], 193 ["Conflitos resolvidos (R3)", (raw.conflitos_resolvidos || []).length, raw.sync_date, "curadoria", "Ver envelope unified"], 194 ["Divergências abertas (R4)", (raw.divergencias_nao_reconciliadas || []).length, raw.sync_date, "curadoria", "Dois valores exibidos"], 195 ]; 196 tbody.innerHTML = rows 197 .map(function (r) { 198 const val = typeof r[1] === "string" ? r[1] : fmtNum(r[1]); 199 return ( 200 "<tr><td>" + 201 esc(r[0]) + 202 '</td><td class="num-up">' + 203 esc(val) + 204 "</td><td>" + 205 esc(r[2]) + 206 "</td><td>" + 207 esc(r[3]) + 208 "</td><td>" + 209 esc(r[4]) + 210 "</td></tr>" 211 ); 212 }) 213 .join(""); 214 } 215 216 function renderDivergencias() { 217 const el = document.getElementById("divergenciasBox"); 218 if (!el) return; 219 const list = raw.divergencias_nao_reconciliadas || []; 220 if (!list.length) { 221 el.innerHTML = 222 "<p>Nenhuma divergência numérica aberta no envelope atual.</p>"; 223 return; 224 } 225 el.innerHTML = list 226 .map(function (d) { 227 const a = d.valor_a || {}; 228 const b = d.valor_b || {}; 229 return ( 230 "<p><strong>" + 231 esc(d.indicador || "indicador") + 232 "</strong> (" + 233 esc(d.regra_aplicada || "R4") + 234 "): " + 235 esc(fmtNum(a.numero)) + 236 " (" + 237 esc(a.fonte || "fonte A") + 238 ") vs " + 239 esc(fmtNum(b.numero)) + 240 " (" + 241 esc(b.fonte || "fonte B") + 242 "). " + 243 esc(d.status || "") + 244 "</p>" 245 ); 246 }) 247 .join(""); 248 } 249 250 function chip(label, count, attrs, active) { 251 const extra = Object.keys(attrs) 252 .map(function (k) { 253 return k + '="' + esc(attrs[k]) + '"'; 254 }) 255 .join(" "); 256 return ( 257 '<button type="button" class="chip' + 258 (active ? " active" : "") + 259 '" ' + 260 extra + 261 ">" + 262 esc(label) + 263 (count != null ? " (" + count + ")" : "") + 264 "</button>" 265 ); 266 } 267 268 function renderFilters() { 269 const tracks = document.getElementById("filterTrack"); 270 const groups = document.getElementById("filterGroup"); 271 const crimes = document.getElementById("filterCrime"); 272 const evidence = document.getElementById("filterEvidence"); 273 274 const trackCounts = { todos: cards.length }; 275 const groupCounts = {}; 276 const crimeCounts = {}; 277 const evCounts = { todos: cards.length }; 278 279 cards.forEach(function (c) { 280 trackCounts[c.track] = (trackCounts[c.track] || 0) + 1; 281 groupCounts[c.grupo] = (groupCounts[c.grupo] || 0) + 1; 282 evCounts[c.evidence_status] = (evCounts[c.evidence_status] || 0) + 1;
283 (c.crime_tags || []).forEach(function (t) { 284 crimeCounts[t] = (crimeCounts[t] || 0) + 1; 285 }); 286 }); 287 288 tracks.innerHTML = 289 '<span class="filters-label">Trilha:</span>' + 290 Object.keys(TRACK_LABELS) 291 .map(function (k) { 292 if (k !== "todos" && !trackCounts[k]) return ""; 293 return chip(TRACK_LABELS[k], trackCounts[k] || 0, { "data-track": k }, state.track === k); 294 }) 295 .join(""); 296 297 const groupOrder = [ 298 "todos", 299 "penduricalhos", 300 "corrupcao_judicial", 301 "cnj_disciplinar", 302 "chokepoint_stf", 303 "eleitoral_tse", 304 "soltura_hc", 305 "progressao_regime", 306 "arquivamento", 307 "jurisprudencia_estrutural", 308 "foragidos_impacto", 309 "outros_judiciario", 310 ]; 311 groups.innerHTML = 312 '<span class="filters-label">Grupo:</span>' + 313 groupOrder 314 .map(function (k) { 315 if (k !== "todos" && !groupCounts[k]) return ""; 316 const count = k === "todos" ? cards.length : groupCounts[k]; 317 return chip(GROUP_LABELS[k], count, { "data-grp": k }, state.grupo === k); 318 }) 319 .join(""); 320 321 const crimeKeys = ["todos"].concat(Object.keys(CRIME_LABELS).filter(function (k) { 322 return k !== "todos" && crimeCounts[k]; 323 })); 324 crimes.innerHTML = 325 '<span class="filters-label">Crime:</span>' + 326 crimeKeys 327 .map(function (k) { 328 const count = k === "todos" ? null : crimeCounts[k]; 329 return chip(CRIME_LABELS[k], count, { "data-crime": k }, state.crime === k); 330 }) 331 .join(""); 332 333 evidence.innerHTML = 334 '<span class="filters-label">Evidência:</span>' + 335 Object.keys(EV_LABELS) 336 .map(function (k) { 337 if (k !== "todos" && !evCounts[k]) return ""; 338 return chip(EV_LABELS[k], evCounts[k] || 0, { "data-ev": k }, state.evidence === k); 339 }) 340 .join(""); 341 } 342 343 function matches(c) { 344 if (state.track !== "todos" && c.track !== state.track) return false; 345 if (state.grupo !== "todos" && c.grupo !== state.grupo) return false; 346 if (state.evidence !== "todos" && c.evidence_status !== state.evidence) return false; 347 if (state.crime !== "todos") { 348 if (!(c.crime_tags || []).includes(state.crime) && !(c.tags || []).includes(state.crime)) { 349 return false; 350 } 351 } 352 if (!state.q) return true; 353 const hay = [ 354 c.titulo, 355 c.descricao, 356 (c.tags || []).join(" "), 357 (c.instituicoes || []).join(" "), 358 (c.crime_tags || []).join(" "), 359 c.ref || "", 360 c.tribunal || "", 361 c.tipo_decisao || "", 362 c.lacuna_investigativa || "", 363 c.id != null ? String(c.id) : "", 364 ] 365 .join(" ") 366 .toLowerCase(); 367 return hay.indexOf(state.q) !== -1; 368 } 369 370 function cardHTML(c) { 371 const fontesHTML = 372 c.fontes && c.fontes.length 373 ? '<div class="card-sources">' + 374 c.fontes 375 .slice(0, 4) 376 .map(function (u) { 377 return ( 378 '<a href="' + 379 esc(u) + 380 '" target="_blank" rel="noopener">â ' + 381 esc(u) + 382 "</a>" 383 ); 384 }) 385 .join("") + 386 "</div>" 387 : ""; 388 const tagsHTML = (c.tags || []) 389 .slice(0, 6) 390 .map(function (t) { 391 return '<span class="tag">' + esc(t) + "</span>"; 392 }) 393 .join(""); 394 const valorHTML = c.valor_envolvido 395 ? '<div class="card-valor">' + esc(c.valor_envolvido) + "</div>" 396 : ""; 397 const refHTML = c.ref 398 ? '<div class="card-ref">' + esc(c.ref) + (c.tribunal ? " · " + esc(c.tribunal) : "") + "</div>" 399 : ""; 400 const lacunaHTML = c.lacuna_investigativa 401 ? '<div class="card-ref">Lacuna: ' + esc(c.lacuna_investigativa) + "</div>" 402 : ""; 403 const evClass = c.evidence_status === "ev-confirmed" ? "confirmed" : "alleged"; 404 const trackLabel = 405 c.track === "decisoes_impacto" ? "Decisões" : "Captura"; 406 407 return ( 408 '<article class="card" data-track="' + 409 esc(c.track) + 410 '">' + 411 '<div class="card-top">' + 412 '<span class="card-date">' + 413 iconFor(c) + 414 esc(c.data) + 415 "</span>" + 416 '<div class="card-badges">' + 417 '<span class="badge badge-track-' + 418 esc(c.track) + 419 '">' + 420 trackLabel + 421 "</span>" + 422 '<span class="badge badge-grp-' + 423 esc(c.grupo) + 424 '">' + 425 esc(GROUP_LABELS[c.grupo] || c.grupo) + 426 "</span>" + 427 '<span class="badge badge-ev ' + 428 evClass + 429 '">' + 430 esc(c.evidence_status) + 431 "</span>" + 432 "</div></div>" + 433 "<h3>" + 434 esc(c.titulo) + 435 "</h3>" + 436 "<p>" + 437 esc(c.descricao) + 438 "</p>" + 439 valorHTML + 440 refHTML + 441 lacunaHTML + 442 '<div class="card-meta">' + 443 tagsHTML + 444 "</div>" + 445 fontesHTML + 446 "</article>" 447 ); 448 } 449 450 function renderFeed() { 451 const list = cards.filter(matches); 452 document.getElementById("countRow").innerHTML = 453 "<b>" + list.length + "</b>
453 de " + cards.length + " casos exibidos"; 454 const container = document.getElementById("cardList"); 455 container.innerHTML = list.length 456 ? list.map(cardHTML).join("") 457 : '<div class="empty-state">Nenhum caso corresponde aos filtros atuais.</div>'; 458 } 459 460 function bindFilters() { 461 document.getElementById("filterTrack").addEventListener("click", function (e) { 462 const btn = e.target.closest("[data-track]"); 463 if (!btn) return; 464 state.track = btn.getAttribute("data-track"); 465 renderFilters(); 466 renderFeed(); 467 }); 468 document.getElementById("filterGroup").addEventListener("click", function (e) { 469 const btn = e.target.closest("[data-grp]"); 470 if (!btn) return; 471 state.grupo = btn.getAttribute("data-grp"); 472 renderFilters(); 473 renderFeed(); 474 }); 475 document.getElementById("filterCrime").addEventListener("click", function (e) { 476 const btn = e.target.closest("[data-crime]"); 477 if (!btn) return; 478 state.crime = btn.getAttribute("data-crime"); 479 renderFilters(); 480 renderFeed(); 481 }); 482 document.getElementById("filterEvidence").addEventListener("click", function (e) { 483 const btn = e.target.closest("[data-ev]"); 484 if (!btn) return; 485 state.evidence = btn.getAttribute("data-ev"); 486 renderFilters(); 487 renderFeed(); 488 }); 489 document.getElementById("search").addEventListener("input", function (e) { 490 state.q = e.target.value.trim().toLowerCase(); 491 renderFeed(); 492 }); 493 } 494 495 function renderEcosystem(hubs) { 496 const el = document.getElementById("ecoGrid"); 497 if (!el) return; 498 if (!hubs || !hubs.length) { 499 el.innerHTML = '<div class="eco-empty">Ecossistema indisponÃvel.</div>'; 500 return; 501 } 502 el.innerHTML = hubs 503 .map(function (h) { 504 const current = !!h.current; 505 const title = esc(h.title || ""); 506 const desc = esc(h.description || ""); 507 const href = esc(h.href || "#"); 508 const badge = current ? '<span class="eco-badge">este hub</span>' : ""; 509 if (current) { 510 return ( 511 '<div class="eco-card is-current" aria-current="page">' + 512 '<div class="eco-card-title">' + 513 title + 514 badge + 515 "</div>" + 516 '<p class="eco-card-desc">' + 517 desc + 518 "</p></div>" 519 ); 520 } 521 return ( 522 '<a class="eco-card" href="' + 523 href + 524 '" target="_blank" rel="noopener noreferrer">' + 525 '<div class="eco-card-title">' + 526 title + 527 "</div>" + 528 '<p class="eco-card-desc">' + 529 desc + 530 "</p></a>" 531 ); 532 }) 533 .join(""); 534 } 535 536 function boot(data) { 537 raw = data; 538 cards = data.cards || []; 539 const syncText = data.sync_date || "â"; 540 document.getElementById("syncDate").textContent = syncText; 541 const footerSync = document.getElementById("footer-sync-date"); 542 if (footerSync) footerSync.textContent = syncText; 543 document.getElementById("lacunaText").textContent = 544 (data.excluidos_pendente_enriquecimento || 0) + 545 " entradas do corpus de captura correspondem ao escopo temático mas carecem de descrição/fonte estruturada â excluÃdas do painel até enriquecimento."; 546 renderStats(); 547 renderAlertas(); 548 renderDataTable(); 549 renderDivergencias(); 550 renderFilters(); 551 renderFeed(); 552 bindFilters(); 553 } 554 555 const gotop = document.getElementById("gotop"); 556 window.addEventListener("scroll", function () { 557 gotop.classList.toggle("show", window.scrollY > 500); 558 }); 559 gotop.addEventListener("click", function () { 560 window.scrollTo({ top: 0, behavior: "smooth" }); 561 }); 562 563 fetch("data/unified.json") 564 .then(function (r) { 565 if (!r.ok) throw new Error("HTTP " + r.status); 566 return r.json(); 567 }) 568 .then(boot) 569 .catch(function (err) { 570 document.getElementById("cardList").innerHTML = 571 '<div class="error-state">Falha ao carregar data/unified.json â ' + 572 esc(err.message) + 573 "</div>"; 574 });
575 576 fetch("data/ecosystem.json") 577 .then(function (r) { 578 if (!r.ok) throw new Error("HTTP " + r.status); 579 return r.json(); 580 }) 581 .then(renderEcosystem) 582 .catch(function () { 583 const el = document.getElementById("ecoGrid"); 584 if (el) el.innerHTML = '<div class="eco-empty">Falha ao carregar data/ecosystem.json.</div>'; 585 }); 586})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.