Translated ['', 'src/welcome/hacktricks-values-and-faq.md'] to pt

This commit is contained in:
Translator 2025-10-04 09:04:39 +00:00
parent 6c0b0f7629
commit e6ac246259
2 changed files with 254 additions and 217 deletions

View File

@ -7,15 +7,15 @@
> [!TIP]
> Estes são os **valores do Projeto HackTricks**:
>
> - Dar acesso **GRATUITO** a recursos **educacionais de hacking** para **toda** a Internet.
> - Dar acesso **GRÁTIS** a recursos de **hacking EDUCACIONAL** para **TODOS** na Internet.
> - Hacking é sobre aprender, e aprender deve ser o mais gratuito possível.
> - O objetivo deste livro é servir como um **recurso educacional** abrangente.
> - **ARMAZENAR** técnicas incríveis de **hacking** que a comunidade publica, dando aos **AUTORES ORIGINAIS** todos os **créditos**.
> - **Não queremos os créditos de outras pessoas**, nós apenas queremos armazenar truques legais para todos.
> - Também escrevemos **nossas próprias pesquisas** no HackTricks.
> - Em vários casos vamos apenas escrever **no HackTricks um resumo das partes importantes** da técnica e **encorajar o leitor a visitar o post original** para mais detalhes.
> - **ORGANIZAR** todas as técnicas de hacking no livro para que seja **MAIS ACESSÍVEL**
> - A equipe do HackTricks dedicou milhares de horas gratuitamente **apenas para organizar o conteúdo** para que as pessoas possam **aprender mais rápido**
> - **ARMAZENAR** técnicas incríveis de **hacking** que a comunidade publica, dando aos **AUTORES** **ORIGINAIS** todos os **créditos**.
> - **Não queremos os créditos de outras pessoas**, apenas queremos armazenar truques legais para todos.
> - Também escrevemos as **nossas próprias pesquisas** no HackTricks.
> - Em vários casos escreveremos apenas **no HackTricks um resumo das partes importantes** da técnica e iremos **encorajar o leitor a visitar o post original** para mais detalhes.
> - **ORGANIZAR** todas as técnicas de **hacking** no livro para que seja **MAIS ACESSÍVEL**
> - A equipe HackTricks dedicou milhares de horas gratuitamente **apenas para organizar o conteúdo** para que as pessoas possam **aprender mais rápido**
<figure><img src="../images/hack tricks gif.gif" alt="" width="375"><figcaption></figcaption></figure>
@ -33,10 +33,10 @@ E não esqueça de **dar uma estrela nos projetos do Github!** (Encontre os link
>
> - **Como posso contribuir para o projeto?**
Você pode **compartilhar novas dicas e truques com a comunidade ou corrigir bugs** que encontrar nos livros enviando um **Pull Request** para as páginas correspondentes do Github:
Você pode **compartilhar novas dicas e truques com a comunidade ou corrigir bugs** que encontrar nos livros enviando um **Pull Request** para as páginas correspondentes no Github:
- https://github.com/carlospolop/hacktricks
- https://github.com/carlospolop/hacktricks-cloud
- [https://github.com/carlospolop/hacktricks](https://github.com/carlospolop/hacktricks)
- [https://github.com/carlospolop/hacktricks-cloud](https://github.com/carlospolop/hacktricks-cloud)
Não esqueça de **dar uma estrela nos projetos do Github!**
@ -48,9 +48,9 @@ Sim, você pode, mas **não esqueça de mencionar o(s) link(s) específico(s)**
> [!TIP]
>
> - **Como posso citar uma página do HackTricks?**
> - **Como posso referenciar uma página do HackTricks?**
Desde que o link **da(s)** página(s) de onde você obteve a informação apareça, isso é suficiente.\
Contanto que o link da(s) página(s) de onde você retirou a informação apareça, isso é suficiente.\
Se você precisar de um bibtex, pode usar algo como:
```latex
@misc{hacktricks-bibtexing,
@ -62,47 +62,47 @@ url = {\url{https://book.hacktricks.wiki/specific-page}},
```
> [!WARNING]
>
> - **Posso copiar todo o HackTricks no meu blog?**
**Prefiro que não**. Isso **não vai beneficiar ninguém**, já que todo o **conteúdo já está publicamente disponível** nos livros oficiais do HackTricks gratuitamente.
Se você teme que vá desaparecer, apenas faça um fork no Github ou baixe-o; como eu disse, já é gratuito.
> - **Posso copiar todo o conteúdo do HackTricks no meu blog?**
>
> **Eu preferiria que não**. Isso **não vai beneficiar ninguém**, pois todo o **conteúdo já está publicamente disponível** nos livros oficiais do HackTricks, de forma gratuita.
>
> Se você teme que ele vá desaparecer, apenas faça um fork no Github ou baixe-o; como eu disse, já é gratuito.
>
> [!WARNING]
>
> - **Por que vocês têm patrocinadores? Os livros do HackTricks são para fins comerciais?**
O primeiro **valor** do **HackTricks** é oferecer recursos educacionais de hacking **GRATUITOS** para **TODAS** as pessoas. A equipe do HackTricks dedicou **milhares de horas** para oferecer este conteúdo, novamente, **GRATUITAMENTE**.
Se você acha que os livros do HackTricks são feitos para **fins comerciais**, você está **COMPLETAMENTE ERRADO**.
Temos patrocinadores porque, mesmo que todo o conteúdo seja GRATUITO, queremos **oferecer à comunidade a possibilidade de apreciar nosso trabalho** se desejarem. Portanto, oferecemos às pessoas a opção de doar para o HackTricks via [**Github sponsors**](https://github.com/sponsors/carlospolop), e **empresas relevantes de cibersegurança** podem patrocinar o HackTricks e **ter alguns anúncios** no livro, sendo os **anúncios** sempre colocados em locais onde fiquem **visíveis** mas **não atrapalhem o processo de aprendizagem** caso alguém foque no conteúdo.
Você não encontrará o HackTricks cheio de anúncios irritantes como outros blogs com bem menos conteúdo que o HackTricks, porque o HackTricks não é feito para fins comerciais.
> - **Por que vocês têm patrocinadores? Os livros HackTricks têm fins comerciais?**
>
> O primeiro **valor** do **HackTricks** é oferecer recursos educacionais de hacking **GRATUITOS** para **TODO** o mundo. A equipe do HackTricks dedicou **milhares de horas** para oferecer este conteúdo, novamente, **GRATUITAMENTE**.
>
> Se você acha que os livros HackTricks foram feitos para **fins comerciais**, você está **COMPLETAMENTE ERRADO**.
>
> Temos patrocinadores porque, mesmo que todo o conteúdo seja **GRATUITO**, queremos **oferecer à comunidade a possibilidade de valorizar nosso trabalho** caso desejem. Portanto, oferecemos às pessoas a opção de doar para HackTricks via [**Github sponsors**](https://github.com/sponsors/carlospolop), e empresas relevantes de cybersecurity podem patrocinar o HackTricks e **ter alguns anúncios** no livro, sendo os **anúncios** sempre colocados em locais onde sejam **visíveis** mas **não atrapalhem o processo de aprendizagem** caso alguém foque no conteúdo.
>
> Você não encontrará o HackTricks cheio de anúncios irritantes como outros blogs com muito menos conteúdo que o HackTricks, porque o HackTricks não foi feito para fins comerciais.
>
> [!CAUTION]
>
> - **O que devo fazer se alguma página do HackTricks estiver baseada no meu post do blog, mas não estiver referenciada?**
**Lamentamos muito. Isso não deveria ter acontecido**. Por favor, nos avise via issues no Github, Twitter, Discord... o link da página do HackTricks com o conteúdo e o link do seu blog e **verificaremos e adicionaremos a referência o mais rápido possível**.
> - **O que devo fazer se alguma página do HackTricks estiver baseada no meu post do blog, mas não for referenciada?**
>
> **Lamentamos muito. Isso não deveria ter acontecido**. Por favor, avise-nos via Github issues, Twitter, Discord... envie o link da página do HackTricks com o conteúdo e o link do seu blog e **verificaremos e adicionaremos a referência o mais rápido possível**.
>
> [!CAUTION]
>
> - **O que devo fazer se houver conteúdo do meu blog no HackTricks e eu não quiser que ele esteja lá?**
Observe que ter links para sua página no HackTricks:
- Melhora seu **SEO**
- O conteúdo é **traduzido para mais de 15 idiomas**, tornando possível que mais pessoas acessem esse conteúdo
- **HackTricks incentiva** as pessoas a **verem sua página** (várias pessoas nos disseram que desde que alguma página delas apareceu no HackTricks passaram a receber mais visitas)
No entanto, se você ainda quer que o conteúdo do seu blog seja removido do HackTricks, apenas nos informe e nós **removeremos definitivamente todo link para seu blog**, e qualquer conteúdo baseado nele.
>
> Observe que ter links para sua página no HackTricks:
>
> - Melhora o seu **SEO**
> - O conteúdo é **traduzido para mais de 15 idiomas**, possibilitando que mais pessoas acessem esse conteúdo
> - **HackTricks incentiva** as pessoas a **visitar sua página** (várias pessoas nos disseram que, desde que alguma página loro apareceu no HackTricks, elas receberam mais visitas)
>
> No entanto, se você ainda quiser que o conteúdo do seu blog seja removido do HackTricks, apenas nos avise e definitivamente **removeremos todo link para o seu blog**, e qualquer conteúdo baseado nele.
>
> [!CAUTION]
>
> - **O que devo fazer se encontrar conteúdo copiado e colado no HackTricks?**
Sempre **damos todos os créditos aos autores originais**. Se você encontrar uma página com conteúdo copiado sem a fonte original referenciada, nos avise e nós iremos **removê-la**, **adicionar o link antes do texto**, ou **reescrevê-la adicionando o link**.
>
> Nós sempre **damos todo o crédito aos autores originais**. Se você encontrar uma página com conteúdo copiado sem a fonte original referenciada, nos avise e nós iremos **removê-la**, **adicionar o link antes do texto**, ou **reescrevê-la adicionando o link**.
## LICENÇA
@ -110,34 +110,34 @@ Copyright © Todos os direitos reservados, salvo indicação em contrário.
#### Resumo da Licença:
- Atribuição: Você tem permissão para:
- Atribuição: Você é livre para:
- Compartilhar — copiar e redistribuir o material em qualquer meio ou formato.
- Adaptar — remixar, transformar e criar obras derivadas do material.
- Adaptar — remixar, transformar e criar a partir do material.
#### Termos Adicionais:
- Conteúdo de Terceiros: Algumas partes deste blog/livro podem incluir conteúdo de outras fontes, como trechos de outros blogs ou publicações. O uso de tal conteúdo é feito sob os princípios de fair use ou com permissão explícita dos respectivos detentores de direitos autorais. Por favor, consulte as fontes originais para informações de licenciamento específicas sobre conteúdo de terceiros.
- Autoria: O conteúdo original produzido pelo HackTricks está sujeito aos termos desta licença. Recomenda-se atribuir este trabalho ao autor ao compartilhá-lo ou adaptá-lo.
- Conteúdo de Terceiros: Algumas partes deste blog/livro podem incluir conteúdo de outras fontes, como trechos de outros blogs ou publicações. O uso de tal conteúdo é feito sob os princípios de uso justo (fair use) ou com permissão explícita dos respectivos detentores de direitos autorais. Por favor, consulte as fontes originais para informações específicas de licenciamento sobre conteúdo de terceiros.
- Autoria: O conteúdo original criado pelo HackTricks está sujeito aos termos desta licença. Recomenda-se atribuir este trabalho ao autor ao compartilhar ou adaptar o conteúdo.
#### Isenções:
- Uso Comercial: Para consultas relacionadas ao uso comercial deste conteúdo, por favor entre em contato comigo.
- Uso Comercial: Para consultas sobre uso comercial deste conteúdo, por favor entre em contato comigo.
Esta licença não concede quaisquer direitos de marca registrada ou de branding em relação ao conteúdo. Todas as marcas registradas e identidades visuais apresentadas neste blog/livro são propriedade de seus respectivos proprietários.
Esta licença não concede quaisquer direitos sobre marcas registradas ou branding em relação ao conteúdo. Todas as marcas e identidades visuais apresentadas neste blog/livro são propriedade de seus respectivos proprietários.
**Ao acessar ou usar o HackTricks, você concorda em cumprir os termos desta licença. Se você não concorda com estes termos, por favor, não acesse este site.**
**Ao acessar ou usar o HackTricks, você concorda em obedecer aos termos desta licença. Se você não concorda com estes termos, por favor, não acesse este website.**
## **Aviso Legal**
> [!CAUTION]
> Este livro, 'HackTricks', destina-se apenas a fins educacionais e informativos. O conteúdo deste livro é fornecido "no estado em que se encontra" e os autores e editores não fazem declarações ou garantias de qualquer tipo, expressas ou implícitas, sobre a integridade, exatidão, confiabilidade, adequação ou disponibilidade das informações, produtos, serviços ou gráficos relacionados contidos neste livro. Qualquer confiança que você deposite em tais informações é, portanto, estritamente por sua conta e risco.
> Este livro, 'HackTricks', destina-se apenas a fins educacionais e informativos. O conteúdo deste livro é fornecido no estado em que se encontra ('as is'), e os autores e editores não fazem representações ou garantias de qualquer tipo, expressas ou implícitas, sobre a completude, exatidão, confiabilidade, adequação ou disponibilidade das informações, produtos, serviços ou gráficos relacionados contidos neste livro. Qualquer confiança que você deposite em tais informações é, portanto, estritamente por sua conta e risco.
>
> Os autores e editores não serão em nenhum caso responsáveis por qualquer perda ou dano, incluindo, sem limitação, perda ou dano indireto ou consequente, ou qualquer perda ou dano decorrente de perda de dados ou lucros decorrentes de, ou em conexão com, o uso deste livro.
> Os autores e editores não serão, em hipótese alguma, responsáveis por qualquer perda ou dano, incluindo, sem limitação, perda ou dano indireto ou consequente, ou qualquer perda ou dano decorrente de perda de dados ou lucros resultantes do uso deste livro.
>
> Além disso, as técnicas e dicas descritas neste livro são fornecidas apenas para fins educacionais e informativos, e não devem ser usadas para atividades ilegais ou maliciosas. Os autores e editores não endossam ou apoiam quaisquer atividades ilegais ou antiéticas, e qualquer uso das informações contidas neste livro é por conta e risco do usuário.
> Além disso, as técnicas e dicas descritas neste livro são fornecidas apenas para fins educacionais e informativos, e não devem ser usadas para atividades ilegais ou maliciosas. Os autores e editores não endossam nem apoiam quaisquer atividades ilegais ou antiéticas, e qualquer uso da informação contida neste livro é de responsabilidade e risco exclusivo do usuário.
>
> O usuário é o único responsável por quaisquer ações tomadas com base nas informações contidas neste livro, e deve sempre buscar aconselhamento e assistência profissional ao tentar implementar quaisquer das técnicas ou dicas aqui descritas.
> O usuário é o único responsável por quaisquer ações tomadas com base nas informações contidas neste livro, e deve sempre procurar aconselhamento e assistência profissional ao tentar implementar quaisquer técnicas ou dicas aqui descritas.
>
> Ao usar este livro, o usuário concorda em isentar os autores e editores de qualquer e toda responsabilidade por quaisquer danos, perdas ou prejuízos que possam resultar do uso deste livro ou de qualquer informação nele contida.
> Ao usar este livro, o usuário concorda em isentar os autores e editores de qualquer e toda responsabilidade por quaisquer danos, perdas ou prejuízos que possam resultar do uso deste livro ou de qualquer informação contida nele.
{{#include ../banners/hacktricks-training.md}}

View File

@ -6,34 +6,63 @@
*/
(() => {
"use strict";
"use strict";
/* ───────────── 0. helpers (main thread) ───────────── */
const clear = el => { while (el.firstChild) el.removeChild(el.firstChild); };
/* ───────────── 1. WebWorker code ─────────────────── */
const workerCode = `
self.window = self;
self.search = self.search || {};
const abs = p => location.origin + p;
/* 1 — elasticlunr */
try { importScripts('https://cdn.jsdelivr.net/npm/elasticlunr@0.9.5/elasticlunr.min.js'); }
catch { importScripts(abs('/elasticlunr.min.js')); }
/* 2 — decompress gzip data */
async function decompressGzip(arrayBuffer){
if(typeof DecompressionStream !== 'undefined'){
/* Modern browsers: use native DecompressionStream */
const stream = new Response(arrayBuffer).body.pipeThrough(new DecompressionStream('gzip'));
const decompressed = await new Response(stream).arrayBuffer();
return new TextDecoder().decode(decompressed);
} else {
/* Fallback: use pako library */
if(typeof pako === 'undefined'){
try { importScripts('https://cdn.jsdelivr.net/npm/pako@2.1.0/dist/pako.min.js'); }
catch(e){ throw new Error('pako library required for decompression: '+e); }
}
const uint8Array = new Uint8Array(arrayBuffer);
const decompressed = pako.ungzip(uint8Array, {to: 'string'});
return decompressed;
}
}
/* ───────────── 0. helpers (main thread) ───────────── */
const clear = el => { while (el.firstChild) el.removeChild(el.firstChild); };
/* ───────────── 1. WebWorker code ─────────────────── */
const workerCode = `
self.window = self;
self.search = self.search || {};
const abs = p => location.origin + p;
/* 1 — elasticlunr */
try { importScripts('https://cdn.jsdelivr.net/npm/elasticlunr@0.9.5/elasticlunr.min.js'); }
catch { importScripts(abs('/elasticlunr.min.js')); }
/* 2 — load a single index (remote → local) */
/* 3 — load a single index (remote → local) */
async function loadIndex(remote, local, isCloud=false){
let rawLoaded = false;
if(remote){
/* Try ONLY compressed version from GitHub (remote already includes .js.gz) */
try {
const r = await fetch(remote,{mode:'cors'});
if (!r.ok) throw new Error('HTTP '+r.status);
importScripts(URL.createObjectURL(new Blob([await r.text()],{type:'application/javascript'})));
rawLoaded = true;
} catch(e){ console.warn('remote',remote,'failed →',e); }
if (r.ok) {
const compressed = await r.arrayBuffer();
const text = await decompressGzip(compressed);
importScripts(URL.createObjectURL(new Blob([text],{type:'application/javascript'})));
rawLoaded = true;
console.log('Loaded compressed from GitHub:',remote);
}
} catch(e){ console.warn('compressed GitHub',remote,'failed →',e); }
}
/* If remote (GitHub) failed, fall back to local uncompressed file */
if(!rawLoaded && local){
try { importScripts(abs(local)); rawLoaded = true; }
try {
importScripts(abs(local));
rawLoaded = true;
console.log('Loaded local fallback:',local);
}
catch(e){ console.error('local',local,'failed →',e); }
}
if(!rawLoaded) return null; /* give up on this index */
@ -61,151 +90,159 @@
return local ? loadIndex(null, local, isCloud) : null;
}
let built = [];
const MAX = 30, opts = {bool:'AND', expand:true};
self.onmessage = async ({data}) => {
if(data.type === 'init'){
const lang = data.lang || 'en';
const searchindexBase = 'https://raw.githubusercontent.com/HackTricks-wiki/hacktricks-searchindex/master';
(async () => {
const htmlLang = (document.documentElement.lang || 'en').toLowerCase();
const lang = htmlLang.split('-')[0];
const mainReleaseBase = 'https://github.com/HackTricks-wiki/hacktricks/releases/download';
const cloudReleaseBase = 'https://github.com/HackTricks-wiki/hacktricks-cloud/releases/download';
/* Remote sources are .js.gz (compressed), local fallback is .js (uncompressed) */
const mainFilenames = Array.from(new Set(['searchindex-' + lang + '.js.gz', 'searchindex-en.js.gz']));
const cloudFilenames = Array.from(new Set(['searchindex-cloud-' + lang + '.js.gz', 'searchindex-cloud-en.js.gz']));
const mainTags = Array.from(new Set([\`searchindex-\${lang}\`, 'searchindex-en', 'searchindex-master']));
const cloudTags = Array.from(new Set([\`searchindex-\${lang}\`, 'searchindex-en', 'searchindex-master']));
const MAIN_REMOTE_SOURCES = mainFilenames.map(function(filename) { return searchindexBase + '/' + filename; });
const CLOUD_REMOTE_SOURCES = cloudFilenames.map(function(filename) { return searchindexBase + '/' + filename; });
const MAIN_REMOTE_SOURCES = mainTags.map(tag => \`\${mainReleaseBase}/\${tag}/searchindex.js\`);
const CLOUD_REMOTE_SOURCES = cloudTags.map(tag => \`\${cloudReleaseBase}/\${tag}/searchindex.js\`);
const indices = [];
const main = await loadWithFallback(MAIN_REMOTE_SOURCES , '/searchindex.js', false); if(main) indices.push(main);
const cloud= await loadWithFallback(CLOUD_REMOTE_SOURCES, '/searchindex-cloud.js', true ); if(cloud) indices.push(cloud);
if(!indices.length){ postMessage({ready:false, error:'no-index'}); return; }
/* build index objects */
const built = indices.map(d => ({
idx : elasticlunr.Index.load(d.json),
urls: d.urls,
cloud: d.cloud,
base: d.cloud ? 'https://cloud.hacktricks.wiki/' : ''
}));
postMessage({ready:true});
const MAX = 30, opts = {bool:'AND', expand:true};
self.onmessage = ({data:q}) => {
if(!q){ postMessage([]); return; }
const all = [];
for(const s of built){
const res = s.idx.search(q,opts);
if(!res.length) continue;
const max = res[0].score || 1;
res.forEach(r => {
const doc = s.idx.documentStore.getDoc(r.ref);
all.push({
norm : r.score / max,
title: doc.title,
body : doc.body,
breadcrumbs: doc.breadcrumbs,
url : s.base + s.urls[r.ref],
cloud: s.cloud
const indices = [];
const main = await loadWithFallback(MAIN_REMOTE_SOURCES , '/searchindex-book.js', false); if(main) indices.push(main);
const cloud= await loadWithFallback(CLOUD_REMOTE_SOURCES, '/searchindex.js', true ); if(cloud) indices.push(cloud);
if(!indices.length){ postMessage({ready:false, error:'no-index'}); return; }
/* build index objects */
built = indices.map(d => ({
idx : elasticlunr.Index.load(d.json),
urls: d.urls,
cloud: d.cloud,
base: d.cloud ? 'https://cloud.hacktricks.wiki/' : ''
}));
postMessage({ready:true});
return;
}
const q = data.query || data;
if(!q){ postMessage([]); return; }
const all = [];
for(const s of built){
const res = s.idx.search(q,opts);
if(!res.length) continue;
const max = res[0].score || 1;
res.forEach(r => {
const doc = s.idx.documentStore.getDoc(r.ref);
all.push({
norm : r.score / max,
title: doc.title,
body : doc.body,
breadcrumbs: doc.breadcrumbs,
url : s.base + s.urls[r.ref],
cloud: s.cloud
});
});
});
}
all.sort((a,b)=>b.norm-a.norm);
postMessage(all.slice(0,MAX));
};
})();
`;
}
all.sort((a,b)=>b.norm-a.norm);
postMessage(all.slice(0,MAX));
};
`;
/* ───────────── 2. spawn worker ───────────── */
const worker = new Worker(URL.createObjectURL(new Blob([workerCode],{type:'application/javascript'})));
/* ───────────── 2.1. initialize worker with language ───────────── */
const htmlLang = (document.documentElement.lang || 'en').toLowerCase();
const lang = htmlLang.split('-')[0];
worker.postMessage({type: 'init', lang: lang});
/* ───────────── 3. DOM refs ─────────────── */
const wrap = document.getElementById('search-wrapper');
const bar = document.getElementById('searchbar');
const list = document.getElementById('searchresults');
const listOut = document.getElementById('searchresults-outer');
const header = document.getElementById('searchresults-header');
const icon = document.getElementById('search-toggle');
const READY_ICON = icon.innerHTML;
icon.textContent = '⏳';
icon.setAttribute('aria-label','Loading search …');
icon.setAttribute('title','Search is loading, please wait...');
/* ───────────── 2. spawn worker ───────────── */
const worker = new Worker(URL.createObjectURL(new Blob([workerCode],{type:'application/javascript'})));
/* ───────────── 3. DOM refs ─────────────── */
const wrap = document.getElementById('search-wrapper');
const bar = document.getElementById('searchbar');
const list = document.getElementById('searchresults');
const listOut = document.getElementById('searchresults-outer');
const header = document.getElementById('searchresults-header');
const icon = document.getElementById('search-toggle');
const READY_ICON = icon.innerHTML;
icon.textContent = '⏳';
icon.setAttribute('aria-label','Loading search …');
icon.setAttribute('title','Search is loading, please wait...');
const HOT=83, ESC=27, DOWN=40, UP=38, ENTER=13;
let debounce, teaserCount=0;
/* ───────────── helpers (teaser, metric) ───────────── */
const escapeHTML = (()=>{const M={'&':'&amp;','<':'&lt;','>':'&gt;','"':'&#34;','\'':'&#39;'};return s=>s.replace(/[&<>'"]/g,c=>M[c]);})();
const URL_MARK='highlight';
function metric(c,t){return c?`${c} search result${c>1?'s':''} for '${t}':`:`No search results for '${t}'.`;}
function makeTeaser(body,terms){
const stem=w=>elasticlunr.stemmer(w.toLowerCase());
const T=terms.map(stem),W_S=40,W_F=8,W_N=2,WIN=30;
const W=[],sents=body.toLowerCase().split('. ');
let i=0,v=W_F,found=false;
sents.forEach(s=>{v=W_F; s.split(' ').forEach(w=>{ if(w){ if(T.some(t=>stem(w).startsWith(t))){v=W_S;found=true;} W.push([w,v,i]); v=W_N;} i+=w.length+1; }); i++;});
if(!W.length) return body;
const win=Math.min(W.length,WIN);
const sums=[W.slice(0,win).reduce((a,[,wt])=>a+wt,0)];
for(let k=1;k<=W.length-win;k++) sums[k]=sums[k-1]-W[k-1][1]+W[k+win-1][1];
const best=found?sums.lastIndexOf(Math.max(...sums)):0;
const out=[]; i=W[best][2];
for(let k=best;k<best+win;k++){const [w,wt,pos]=W[k]; if(i<pos){out.push(body.substring(i,pos)); i=pos;} if(wt===W_S) out.push('<em>'); out.push(body.substr(pos,w.length)); if(wt===W_S) out.push('</em>'); i=pos+w.length;}
return out.join('');
}
function format(d,terms){
const teaser=makeTeaser(escapeHTML(d.body),terms);
teaserCount++;
const enc=encodeURIComponent(terms.join(' ')).replace(/'/g,'%27');
const parts=d.url.split('#'); if(parts.length===1) parts.push('');
const abs=d.url.startsWith('http');
const href=`${abs?'':path_to_root}${parts[0]}?${URL_MARK}=${enc}#${parts[1]}`;
const style=d.cloud?" style=\"color:#1e88e5\"":"";
const isCloud=d.cloud?" [Cloud]":" [Book]";
return `<a href="${href}" aria-details="teaser_${teaserCount}"${style}>`+
`${d.breadcrumbs}${isCloud}<span class="teaser" id="teaser_${teaserCount}" aria-label="Search Result Teaser">${teaser}</span></a>`;
}
/* ───────────── UI control ───────────── */
function showUI(s){wrap.classList.toggle('hidden',!s); icon.setAttribute('aria-expanded',s); if(s){window.scrollTo(0,0); bar.focus(); bar.select();} else {listOut.classList.add('hidden'); [...list.children].forEach(li=>li.classList.remove('focus'));}}
function blur(){const t=document.createElement('input'); t.style.cssText='position:absolute;opacity:0;'; icon.appendChild(t); t.focus(); t.remove();}
icon.addEventListener('click',()=>showUI(wrap.classList.contains('hidden')));
document.addEventListener('keydown',e=>{
if(e.altKey||e.ctrlKey||e.metaKey||e.shiftKey) return;
const f=/^(?:input|select|textarea)$/i.test(e.target.nodeName);
if(e.keyCode===HOT && !f){e.preventDefault(); showUI(true);} else if(e.keyCode===ESC){e.preventDefault(); showUI(false); blur();}
else if(e.keyCode===DOWN && document.activeElement===bar){e.preventDefault(); const first=list.firstElementChild; if(first){blur(); first.classList.add('focus');}}
else if([DOWN,UP,ENTER].includes(e.keyCode) && document.activeElement!==bar){const cur=list.querySelector('li.focus'); if(!cur) return; e.preventDefault(); if(e.keyCode===DOWN){const nxt=cur.nextElementSibling; if(nxt){cur.classList.remove('focus'); nxt.classList.add('focus');}} else if(e.keyCode===UP){const prv=cur.previousElementSibling; cur.classList.remove('focus'); if(prv){prv.classList.add('focus');} else {bar.focus();}} else {const a=cur.querySelector('a'); if(a) window.location.assign(a.href);}}
});
bar.addEventListener('input',e=>{ clearTimeout(debounce); debounce=setTimeout(()=>worker.postMessage(e.target.value.trim()),120); });
/* ───────────── worker messages ───────────── */
worker.onmessage = ({data}) => {
if(data && data.ready!==undefined){
if(data.ready){
icon.innerHTML=READY_ICON;
icon.setAttribute('aria-label','Open search (S)');
icon.removeAttribute('title');
}
else {
icon.textContent='❌';
icon.setAttribute('aria-label','Search unavailable');
icon.setAttribute('title','Search is unavailable');
}
return;
const HOT=83, ESC=27, DOWN=40, UP=38, ENTER=13;
let debounce, teaserCount=0;
/* ───────────── helpers (teaser, metric) ───────────── */
const escapeHTML = (()=>{const M={'&':'&amp;','<':'&lt;','>':'&gt;','"':'&#34;','\'':'&#39;'};return s=>s.replace(/[&<>'"]/g,c=>M[c]);})();
const URL_MARK='highlight';
function metric(c,t){return c?`${c} search result${c>1?'s':''} for '${t}':`:`No search results for '${t}'.`;}
function makeTeaser(body,terms){
const stem=w=>elasticlunr.stemmer(w.toLowerCase());
const T=terms.map(stem),W_S=40,W_F=8,W_N=2,WIN=30;
const W=[],sents=body.toLowerCase().split('. ');
let i=0,v=W_F,found=false;
sents.forEach(s=>{v=W_F; s.split(' ').forEach(w=>{ if(w){ if(T.some(t=>stem(w).startsWith(t))){v=W_S;found=true;} W.push([w,v,i]); v=W_N;} i+=w.length+1; }); i++;});
if(!W.length) return body;
const win=Math.min(W.length,WIN);
const sums=[W.slice(0,win).reduce((a,[,wt])=>a+wt,0)];
for(let k=1;k<=W.length-win;k++) sums[k]=sums[k-1]-W[k-1][1]+W[k+win-1][1];
const best=found?sums.lastIndexOf(Math.max(...sums)):0;
const out=[]; i=W[best][2];
for(let k=best;k<best+win;k++){const [w,wt,pos]=W[k]; if(i<pos){out.push(body.substring(i,pos)); i=pos;} if(wt===W_S) out.push('<em>'); out.push(body.substr(pos,w.length)); if(wt===W_S) out.push('</em>'); i=pos+w.length;}
return out.join('');
}
const docs=data, q=bar.value.trim(), terms=q.split(/\s+/).filter(Boolean);
header.textContent=metric(docs.length,q);
clear(list);
docs.forEach(d=>{const li=document.createElement('li'); li.innerHTML=format(d,terms); list.appendChild(li);});
listOut.classList.toggle('hidden',!docs.length);
};
})();
function format(d,terms){
const teaser=makeTeaser(escapeHTML(d.body),terms);
teaserCount++;
const enc=encodeURIComponent(terms.join(' ')).replace(/'/g,'%27');
const parts=d.url.split('#'); if(parts.length===1) parts.push('');
const abs=d.url.startsWith('http');
const href=`${abs?'':path_to_root}${parts[0]}?${URL_MARK}=${enc}#${parts[1]}`;
const style=d.cloud?" style=\"color:#1e88e5\"":"";
const isCloud=d.cloud?" [Cloud]":" [Book]";
return `<a href="${href}" aria-details="teaser_${teaserCount}"${style}>`+
`${d.breadcrumbs}${isCloud}<span class="teaser" id="teaser_${teaserCount}" aria-label="Search Result Teaser">${teaser}</span></a>`;
}
/* ───────────── UI control ───────────── */
function showUI(s){wrap.classList.toggle('hidden',!s); icon.setAttribute('aria-expanded',s); if(s){window.scrollTo(0,0); bar.focus(); bar.select();} else {listOut.classList.add('hidden'); [...list.children].forEach(li=>li.classList.remove('focus'));}}
function blur(){const t=document.createElement('input'); t.style.cssText='position:absolute;opacity:0;'; icon.appendChild(t); t.focus(); t.remove();}
icon.addEventListener('click',()=>showUI(wrap.classList.contains('hidden')));
document.addEventListener('keydown',e=>{
if(e.altKey||e.ctrlKey||e.metaKey||e.shiftKey) return;
const f=/^(?:input|select|textarea)$/i.test(e.target.nodeName);
if(e.keyCode===HOT && !f){e.preventDefault(); showUI(true);} else if(e.keyCode===ESC){e.preventDefault(); showUI(false); blur();}
else if(e.keyCode===DOWN && document.activeElement===bar){e.preventDefault(); const first=list.firstElementChild; if(first){blur(); first.classList.add('focus');}}
else if([DOWN,UP,ENTER].includes(e.keyCode) && document.activeElement!==bar){const cur=list.querySelector('li.focus'); if(!cur) return; e.preventDefault(); if(e.keyCode===DOWN){const nxt=cur.nextElementSibling; if(nxt){cur.classList.remove('focus'); nxt.classList.add('focus');}} else if(e.keyCode===UP){const prv=cur.previousElementSibling; cur.classList.remove('focus'); if(prv){prv.classList.add('focus');} else {bar.focus();}} else {const a=cur.querySelector('a'); if(a) window.location.assign(a.href);}}
});
bar.addEventListener('input',e=>{ clearTimeout(debounce); debounce=setTimeout(()=>worker.postMessage({query: e.target.value.trim()}),120); });
/* ───────────── worker messages ───────────── */
worker.onmessage = ({data}) => {
if(data && data.ready!==undefined){
if(data.ready){
icon.innerHTML=READY_ICON;
icon.setAttribute('aria-label','Open search (S)');
icon.removeAttribute('title');
}
else {
icon.textContent='❌';
icon.setAttribute('aria-label','Search unavailable');
icon.setAttribute('title','Search is unavailable');
}
return;
}
const docs=data, q=bar.value.trim(), terms=q.split(/\s+/).filter(Boolean);
header.textContent=metric(docs.length,q);
clear(list);
docs.forEach(d=>{const li=document.createElement('li'); li.innerHTML=format(d,terms); list.appendChild(li);});
listOut.classList.toggle('hidden',!docs.length);
};
})();