PageSourceSearch

https://netcattest.com/assets/js/livro-manual.js

js netcattest.com collected 2026-10-02 01:37:05 UTC 16,356 bytes, 542 lines download raw bytes

1import * as pdfjsLib from "https://cdn.jsdelivr.net/npm/[email protected]/build/pdf.mjs";
2
3pdfjsLib.GlobalWorkerOptions.workerSrc = "https://cdn.jsdelivr.net/npm/[email protected]/build/pdf.worker.mjs";
4
5console.log(
6    "%cMANUAL DE OPERAÇÕES ANONIMAS",
7    "color:#ffffff;background:#06111f;font-size:28px;font-weight:900;letter-spacing:2px;padding:10px 14px;border:1px solid #38e8ff;border-radius:6px;text-shadow:0 0 12px rgba(56,232,255,.75);",
8);
9
10const pdfUrl = document.body.dataset.pdf;
11const canvas = document.querySelector("#pdf-canvas");
12const contexto = canvas.getContext("2d");
13const area = document.querySelector("#canvas-area");
14const estado = document.querySelector("#estado-pdf");
15const campoPagina = document.querySelector("#pagina-atual");
16const totalPaginas = document.querySelector("#total-paginas");
17const zoomValor = document.querySelector("#zoom-valor");
18const botoes = document.querySelectorAll("[data-acao]");
19const elementosSemSelecao = document.querySelectorAll(".leitor-topo, .visualizador, #pdf-canvas");
20const detalhesSobreManual = document.querySelector("[data-sobre-manual-detalhes]");
21const menuContexto = document.createElement("div");
22let alvoContexto = null;
23
24let pdf = null;
25let paginaAtual = 1;
26let zoom = 0.45;
27let escala = 1;
28let renderTask = null;
29let distanciaPinch = null;
30let zoomPinch = zoom;
31let rafPinch = null;
32
33function limitar(valor, minimo, maximo) {
34    return Math.min(Math.max(valor, minimo), maximo);
35}
36
37function somenteDigitos(valor) {
38    return String(valor).replace(/\D/g, "");
39}
40
41function obterNumeroPagina(valor, paginaReserva = paginaAtual) {
42    const digitos = somenteDigitos(valor);
43
44    if (!digitos) {
45        return limitar(paginaReserva, 1, pdf ? pdf.numPages : paginaReserva);
46    }
47
48    const numero = Number.parseInt(digitos, 10);
49
50    if (!Number.isSafeInteger(numero)) {
51        return limitar(paginaReserva, 1, pdf ? pdf.numPages : paginaReserva);
52    }
53
54    return limitar(numero, 1, pdf ? pdf.numPages : numero);
55}
56
57function atualizarControles() {
58    campoPagina.value = paginaAtual;
59    campoPagina.maxLength = pdf ? String(pdf.numPages).length : "5";
60    totalPaginas.textContent = pdf ? pdf.numPages : "--";
61    zoomValor.textContent = `${Math.round(zoom * 100)}%`;
62}
63
64function obterEscalaAjustada(pagina) {
65    const viewportBase = pagina.getViewport({ scale: 1 });
66    const estilos = getComputedStyle(area);
67    const paddingHorizontal = parseFloat(estilos.paddingLeft) + parseFloat(estilos.paddingRight);
68    const larguraDisponivel = Math.max(area.clientWidth - paddingHorizontal, 320);
69    const escalaBase = larguraDisponivel / viewportBase.width;
70
71    return limitar(escalaBase * zoom, 0.35, 4.5);
72}
73
74async function renderizarPagina(numero) {
75    if (!pdf) {
76        return;
77    }
78
79    if (renderTask) {
80        renderTask.cancel();
81    }
82
83    estado.textContent = "Renderizando página...";
84
85    try {
86        const centroAnterior = area.scrollWidth > area.clientWidth
87            ? (area.scrollLeft + area.clientWidth / 2) / area.scrollWidth
88            : 0.5;
89        const pagina = await pdf.getPage(numero);
90        escala = obterEscalaAjustada(pagina);
91        const viewport = pagina.getViewport({ scale: escala });
92        const proporcao = window.devicePixelRatio || 1;
93
94        canvas.width = Math.floor(viewport.width * proporcao);
95        canvas.height = Math.floor(viewport.height * proporcao);
96        canvas.style.width = `${Math.floor(viewport.width)}px`;
97        canvas.style.height = `${Math.floor(viewport.height)}px`;
98        area.classList.toggle("canvas-area--rolagem-x", viewport.width > area.clientWidth);
99
100        const transform = proporcao !== 1 ? [proporcao, 0, 0, proporcao, 0, 0] : null;
101        renderTask = pagina.render({ canvasContext: contexto, viewport, transform });
102        await renderTask.promise;
103        renderTask = null;
104        area.scrollLeft = Math.max(0, area.scrollWidth * centroAnterior - area.clientWidth / 2);
105        estado.textContent = "";
106        atualizarControles();
107    } catch (erro) {
108        if (erro?.name === "RenderingCancelledException") {
109            return;
110        }
111
112        estado.textContent = "Não foi possível renderizar esta página.";
113    }
114}
115
116function irParaPagina(numero) {
117    if (!pdf) {
118        return;
119    }
120
121    paginaAtual = obterNumeroPagina(numero);
122    renderizarPagina(paginaAtual);
123}
124
125function ajustarZoom(valor) {
126    zoom = limitar(Number((zoom + valor).toFixed(2)), 0.25, 3.2);
127    renderizarPagina(paginaAtual);
128}
129
130function distanciaEntreToques(toques) {
131    const [primeiro, segundo] = toques;
132    return Math.hypot(primeiro.clientX - segundo.clientX, primeiro.clientY - segundo.clientY);
133}
134
135function aplicarZoomPinch(proximoZoom) {
136    zoom = limitar(Number(proximoZoom.toFixed(2)), 0.25, 3.2);
137
138    if (rafPinch) {
139        cancelAnimationFrame(rafPinch);
140    }
141
142    rafPinch = requestAnimationFrame(() => {
143        renderizarPagina(paginaAtual);
144        rafPinch = null;
145    });
146}
147
148function abrirSobreManual() {
149    if (!detalhesSobreManual) {
150        return;
151    }
152
153    detalhesSobreManual.open = true;
154    detalhesSobreManual.scrollIntoView({ behavior: "smooth", block: "start" });
155    detalhesSobreManual.querySelector("summary")?.focus({ preventScroll: true });
156}
157
158function ocultarMenuContexto() {
159    menuContexto.classList.remove("ativo");
160    menuContexto.setAttribute("aria-hidden", "true");
161    alvoContexto = null;
162}
163
164function criarItemContexto(rotulo, acao) {
165    const botao = document.createElement("button");
166    botao.type = "button";
167    botao.textContent = rotulo;
168    botao.dataset.contextoAcao = acao;
169    botao.setAttribute("role", "menuitem");
170
171    return botao;
172}
173
174function criarSeparadorContexto() {
175    const separador = document.createElement("span");
176    separador.className = "menu-contexto__separador";
177    separador.setAttribute("aria-hidden", "true");
178
179    return separador;
180}
181
182function posicionarMenuContexto(evento) {
183    const margem = 10;
184    const retangulo = menuContexto.getBoundingClientRect();
185    const esquerda = Math.min(evento.clientX, window.innerWidth - retangulo.width - margem);
186    const topo = Math.min(evento.clientY, window.innerHeight - retangulo.height - margem);
187
188    menuContexto.style.left = `${Math.max(margem, esquerda)}px`;
189    menuContexto.style.top = `${Math.max(margem, topo)}px`;
190}
191
192function mostrarMenuContexto(evento) {
193    const sobrePdf = evento.target === canvas;
194    const sobreDownload = evento.target.closest("a[download]");
195    const sobreCampoPagina = evento.target === campoPagina;
196    const sobreControle = evento.target.closest(".toolbar");
197
198    evento.preventDefault();
199    alvoContexto = { sobrePdf, sobreDownload, sobreCampoPagina, sobreControle };
200    menuContexto.replaceChildren();
201
202    if (sobrePdf) {
203        menuContexto.append(
204            criarItemContexto("Página anterior", "pagina-anterior"),
205            criarItemContexto("Próxima página", "pagina-proxima"),
206            criarSeparadorContexto(),
207            criarItemContexto("Aumentar zoom", "zoom-mais"),
208            criarItemContexto("Diminuir zoom", "zoom-menos"),
209            criarSeparadorContexto(),
210            criarItemContexto("Salvar página como imagem", "salvar-imagem"),
211            criarItemContexto("Copiar imagem da página", "copiar-imagem"),
212            criarSeparadorContexto(),
213            criarItemContexto("Baixar manual", "baixar"),
214            criarItemContexto("Copiar endereço desta página", "copiar-pagina"),
215        );
216    } else if (sobreDownload) {
217        menuContexto.append(
218            criarItemContexto("Baixar manual", "baixar"),
219            criarItemContexto("Abrir PDF em nova aba", "abrir-pdf"),
220            criarItemContexto("Copiar link do PDF", "copiar-pdf"),
221        );
222    } else if (sobreCampoPagina) {
223        menuContexto.append(
224            criarItemContexto("Focar campo da página", "focar-pagina"),
225            criarItemContexto("Ir para primeira página", "primeira-pagina"),
226            criarItemContexto("Ir para última página", "ultima-pagina"),
227        );
228    } else if (sobreControle) {
229        menuContexto.append(
230            criarItemContexto("Página anterior", "pagina-anterior"),
231            criarItemContexto("Próxima página", "pagina-proxima"),
232            criarSeparadorContexto(),
233            criarItemContexto("Aumentar zoom", "zoom-mais"),
234            criarItemContexto("Diminuir zoom", "zoom-menos"),
235            criarItemContexto("Baixar manual", "baixar"),
236            criarItemContexto("Voltar ao site", "voltar-site"),
237        );
238    } else {
239        menuContexto.append(
240            criarItemContexto("Página anterior", "pagina-anterior"),
241            criarItemContexto("Próxima página", "pagina-proxima"),
242            criarSeparadorContexto(),
243            criarItemContexto("Baixar manual", "baixar"),
244            criarItemContexto("Voltar ao site", "voltar-site"),
245            criarItemContexto("Copiar endereço desta página", "copiar-pagina"),
246        );
247    }
248
249    menuContexto.classList.add("ativo");
250    menuContexto.setAttribute("aria-hidden", "false");
251    posicionarMenuContexto(evento);
252}
253
254async function copiarTexto(texto) {
255    if (!texto) {
256        return;
257    }
258
259    try {
260        await navigator.clipboard.writeText(texto);
261    } catch (erro) {
262        const campo = document.createElement("textarea");
263        campo.value = texto;
264        campo.style.position = "fixed";
265        campo.style.opacity = "0";
266        document.body.appendChild(campo);
267        campo.select();
268        document.execCommand("copy");
269        campo.remove();
270    }
271}
272
273function baixarImagemPagina() {
274    const link = document.createElement("a");
275    link.download = `manual-pagina-${paginaAtual}.png`;
276    link.href = canvas.toDataURL("image/png");
277    link.click();
278}
279
280async function copiarImagemPagina() {
281    if (!("ClipboardItem" in window) || !navigator.clipboard?.write) {
282        copiarTexto(`Página ${paginaAtual} do manual`);
283        return;
284    }
285
286    canvas.toBlob(async (blob) => {
287        if (!blob) {
288            return;
289        }
290
291        await navigator.clipboard.write([
292            new ClipboardItem({ [blob.type]: blob }),
293        ]);
294    }, "image/png");
295}
296
297menuContexto.className = "menu-contexto";
298menuContexto.setAttribute("role", "menu");
299menuContexto.setAttribute("aria-hidden", "true");
300document.body.appendChild(menuContexto);
301
302botoes.forEach((botao) => {
303    botao.addEventListener("click", () => {
304        const acao = botao.dataset.acao;
305
306        if (acao === "anterior") {
307            irParaPagina(paginaAtual - 1);
308        }
309
310        if (acao === "proxima") {
311            irParaPagina(paginaAtual + 1);
312        }
313
314        if (acao === "menos-zoom") {
315            ajustarZoom(-0.1);
316        }
317
318        if (acao === "mais-zoom") {
319            ajustarZoom(0.1);
320        }
321    });
322});
323
324if (window.location.hash === "#sobre-manual") {
325    abrirSobreManual();
326}
327
328menuContexto.addEventListener("click", (evento) => {
329    const item = evento.target.closest("[data-contexto-acao]");
330
331    if (!item) {
332        return;
333    }
334
335    const acao = item.dataset.contextoAcao;
336
337    if (acao === "pagina-anterior") {
338        irParaPagina(paginaAtual - 1);
339    }
340
341    if (acao === "pagina-proxima") {
342        irParaPagina(paginaAtual + 1);
343    }
344
345    if (acao === "zoom-mais") {
346        ajustarZoom(0.1);
347    }
348
349    if (acao === "zoom-menos") {
350        ajustarZoom(-0.1);
351    }
352
353    if (acao === "salvar-imagem") {
354        baixarImagemPagina();
355    }
356
357    if (acao === "copiar-imagem") {
358        copiarImagemPagina();
359    }
360
361    if (acao === "baixar") {
362        document.querySelector("a[download]")?.click();
363    }
364
365    if (acao === "abrir-pdf") {
366        window.open(pdfUrl, "_blank", "noopener");
367    }
368
369    if (acao === "copiar-pdf") {
370        copiarTexto(new URL(pdfUrl, window.location.href).href);
371    }
372
373    if (acao === "focar-pagina") {
374        campoPagina.focus();
375        campoPagina.select();
376    }
377
378    if (acao === "primeira-pagina") {
379        irParaPagina(1);
380    }
381
382    if (acao === "ultima-pagina" && pdf) {
383        irParaPagina(pdf.numPages);
384    }
385
386    if (acao === "copiar-pagina") {
387        copiarTexto(window.location.href);
388    }
389
390    if (acao === "voltar-site") {
391        window.location.href = "/";
392    }
393
394    ocultarMenuContexto();
395});
396
397document.addEventListener("contextmenu", mostrarMenuContexto);
398document.addEventListener("click", ocultarMenuContexto);
399window.addEventListener("blur", ocultarMenuContexto);
400window.addEventListener("resize", ocultarMenuContexto);
401window.addEventListener("scroll", ocultarMenuContexto, { passive: true });
402window.addEventListener("keydown", (evento) => {
403    if (evento.key === "Escape") {
404        ocultarMenuContexto();
405    }
406});
407
408elementosSemSelecao.forEach((elemento) => {
409    elemento.addEventListener("selectstart", (evento) => {
410        if (evento.target === campoPagina) {
411            return;
412        }
413
414        evento.preventDefault();
415    });
416
417    elemento.addEventListener("dragstart", (evento) => {
418        evento.preventDefault();
419    });
420});
421
422campoPagina.addEventListener("beforeinput", (evento) => {
423    if (evento.inputType.startsWith("delete") || !evento.data) {
424        return;
425    }
426
427    if (/\D/.test(evento.data)) {
428        evento.preventDefault();
429    }
430});
431
432campoPagina.addEventListener("input", () => {
433    const valorSeguro = somenteDigitos(campoPagina.value);
434
435    if (campoPagina.value !== valorSeguro) {
436        campoPagina.value = valorSeguro;
437    }
438});
439
440campoPagina.addEventListener("paste", (evento) => {
441    evento.preventDefault();
442
443    const texto = evento.clipboardData?.getData("text") ?? "";
444    const valorSeguro = somenteDigitos(texto).slice(0, Number(campoPagina.maxLength) || 5);
445    const inicio = campoPagina.selectionStart ?? campoPagina.value.length;
446    const fim = campoPagina.selectionEnd ?? campoPagina.value.length;
447
448    campoPagina.setRangeText(valorSeguro, inicio, fim, "end");
449    campoPagina.dispatchEvent(new Event("input", { bubbles: true }));
450});
451
452campoPagina.addEventListener("change", () => {
453    irParaPagina(campoPagina.value);
454});
455
456campoPagina.addEventListener("blur", () => {
457    campoPagina.value = obterNumeroPagina(campoPagina.value);
458});
459
460window.addEventListener("wheel", (evento) => {
461    if (!evento.ctrlKey && !evento.metaKey) {
462        return;
463    }
464
465    evento.preventDefault();
466    ajustarZoom(evento.deltaY > 0 ? -0.1 : 0.1);
467}, { passive: false });
468
469area.addEventListener("touchstart", (evento) => {
470    if (evento.touches.length !== 2) {
471        distanciaPinch = null;
472        return;
473    }
474
475    distanciaPinch = distanciaEntreToques(evento.touches);
476    zoomPinch = zoom;
477}, { passive: true });
478
479area.addEventListener("touchmove", (evento) => {
480    if (evento.touches.length !== 2 || !distanciaPinch) {
481        return;
482    }
483
484    evento.preventDefault();
485    const distanciaAtual = distanciaEntreToques(evento.touches);
486    const fator = distanciaAtual / distanciaPinch;
487    aplicarZoomPinch(zoomPinch * fator);
488}, { passive: false });
489
490area.addEventListener("touchend", (evento) => {
491    if (evento.touches.length < 2) {
492        distanciaPinch = null;
493        zoomPinch = zoom;
494    }
495});
496
497window.addEventListener("keydown", (evento) => {
498    const alvo = evento.target;
499    const editandoTexto = alvo instanceof HTMLInputElement
500        || alvo instanceof HTMLTextAreaElement
501        || alvo instanceof HTMLSelectElement
502        || alvo?.isContentEditable;
503
504    if (editandoTexto) {
505        return;
506    }
507
508    if (evento.key === "ArrowLeft") {
509        irParaPagina(paginaAtual - 1);
510    }
511
512    if (evento.key === "ArrowRight") {
513        irParaPagina(paginaAtual + 1);
514    }
515
516    if ((evento.ctrlKey || evento.metaKey) && (evento.key === "+" || evento.key === "=")) {
517        evento.preventDefault();
518        ajustarZoom(0.1);
519    }
520
521    if ((evento.ctrlKey || evento.metaKey) && evento.key === "-") {
522        evento.preventDefault();
523        ajustarZoom(-0.1);
524    }
525});
526
527window.addEventListener("resize", () => {
528    renderizarPagina(paginaAtual);
529});
530
531async function iniciar() {
532    try {
533        estado.textContent = "Carregando manual...";
534        pdf = await pdfjsLib.getDocument(pdfUrl).promise;
535        atualizarControles();
536        await renderizarPagina(paginaAtual);
537    } catch (erro) {
538        estado.textContent = "Não foi possível carregar o manual.";
539    }
540}
541
542iniciar();

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.