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

This commit is contained in:
Translator 2025-10-04 09:05:12 +00:00
parent 8c441ed615
commit 0d36f271d0
2 changed files with 238 additions and 201 deletions

View File

@ -7,14 +7,14 @@
> [!TIP] > [!TIP]
> Estos son los **valores del Proyecto HackTricks**: > Estos son los **valores del Proyecto HackTricks**:
> >
> - Dar acceso **GRATIS** a recursos **educativos de hacking** a **TODO** Internet. > - Dar **FREE** acceso a recursos **EDUCATIONAL hacking** a **TODO** Internet.
> - Hacking trata sobre aprender, y el aprendizaje debería ser lo más libre posible. > - El hacking trata sobre aprender, y el aprendizaje debería ser lo más gratuito posible.
> - El propósito de este libro es servir como un **recurso educativo** integral. > - El propósito de este libro es servir como un **recurso educativo** integral.
> - **ALMACENAR** increíbles **hacking** técnicas que la comunidad publica dando a los **AUTORES** **ORIGINALES** todos los **créditos**. > - **STORE** increíbles técnicas de **hacking** que la comunidad publica dando a los **AUTORES** **ORIGINALES** todos los **créditos**.
> - **No queremos el crédito de otras personas**, solo queremos almacenar trucos geniales para todos. > - **No queremos el crédito de otras personas**, solo queremos almacenar trucos geniales para todos.
> - También escribimos **nuestras propias investigaciones** en HackTricks. > - También escribimos **nuestras propias investigaciones** en HackTricks.
> - En varios casos solo escribiremos **en HackTricks un resumen de las partes importantes** de la técnica y **animaremos al lector a visitar el post original** para más detalles. > - En varios casos solo escribiremos **en HackTricks un resumen de las partes importantes** de la técnica y **animaremos al lector a visitar la publicación original** para más detalles.
> - **ORGANIZAR** todas las técnicas de **hacking** en el libro para que sea **MÁS ACCESIBLE** > - **ORGANIZE** todas las técnicas de **hacking** en el libro para que sea **MÁS ACCESIBLE**
> - El equipo de HackTricks ha dedicado miles de horas de forma gratuita **solo para organizar el contenido** para que la gente pueda **aprender más rápido** > - El equipo de HackTricks ha dedicado miles de horas de forma gratuita **solo para organizar el contenido** para que la gente pueda **aprender más rápido**
<figure><img src="../images/hack tricks gif.gif" alt="" width="375"><figcaption></figcaption></figure> <figure><img src="../images/hack tricks gif.gif" alt="" width="375"><figcaption></figcaption></figure>
@ -25,32 +25,32 @@
> >
> - **Muchas gracias por estos recursos, ¿cómo puedo agradecerles?** > - **Muchas gracias por estos recursos, ¿cómo puedo agradecerles?**
Puedes agradecer públicamente a los equipos de HackTricks por reunir todos estos recursos públicamente en un tweet mencionando [**@hacktricks_live**](https://twitter.com/hacktricks_live).\ Puedes agradecer públicamente al equipo de HackTricks por reunir todos estos recursos públicamente en un tweet mencionando [**@hacktricks_live**](https://twitter.com/hacktricks_live).\
If you are specially grateful you can also [**sponsor the project here**](https://github.com/sponsors/carlospolop).\ Si estás especialmente agradecido también puedes [**patrocinar el proyecto aquí**](https://github.com/sponsors/carlospolop).\
Y no olvides **dar una estrella en los proyectos de Github!** (Encuentra los enlaces abajo). Y no olvides **dar una estrella a los proyectos de Github!** (¡Encuentra los enlaces abajo!).
> [!TIP] > [!TIP]
> >
> - **¿Cómo puedo contribuir al proyecto?** > - **¿Cómo puedo contribuir al proyecto?**
Puedes **compartir nuevos consejos y trucos con la comunidad o corregir bugs** que encuentres en los libros enviando un **Pull Request** a las páginas respectivas de Github: Puedes **compartir nuevos consejos y trucos con la comunidad o corregir errores** que encuentres en los libros enviando un **Pull Request** a las páginas correspondientes de Github:
- [https://github.com/carlospolop/hacktricks](https://github.com/carlospolop/hacktricks) - [https://github.com/carlospolop/hacktricks](https://github.com/carlospolop/hacktricks)
- [https://github.com/carlospolop/hacktricks-cloud](https://github.com/carlospolop/hacktricks-cloud) - [https://github.com/carlospolop/hacktricks-cloud](https://github.com/carlospolop/hacktricks-cloud)
¡No olvides **dar una estrella en los proyectos de Github!** No olvides **dar una estrella a los proyectos de Github!**
> [!TIP] > [!TIP]
> >
> - **¿Puedo copiar contenido de HackTricks y ponerlo en mi blog?** > - **¿Puedo copiar contenido de HackTricks y ponerlo en mi blog?**
Sí, puedes, pero **no olvides mencionar el/los enlace(s) específicos** de donde se tomó el contenido. Sí, puedes, pero **no olvides mencionar los enlaces específicos** de donde se tomó el contenido.
> [!TIP] > [!TIP]
> >
> - **¿Cómo puedo citar una página de HackTricks?** > - **¿Cómo puedo referenciar una página de HackTricks?**
Con tal de que aparezca el enlace **de** la(s) página(s) de donde obtuviste la información, es suficiente.\ Mientras aparezca el enlace **de** la(s) página(s) de donde obtuviste la información, es suficiente.\
Si necesitas un bibtex puedes usar algo como: Si necesitas un bibtex puedes usar algo como:
```latex ```latex
@misc{hacktricks-bibtexing, @misc{hacktricks-bibtexing,
@ -62,82 +62,82 @@ url = {\url{https://book.hacktricks.wiki/specific-page}},
``` ```
> [!WARNING] > [!WARNING]
> >
> - **Can I copy all HackTricks in my blog?** > - **¿Puedo copiar todo el contenido de HackTricks en mi blog?**
**Preferiría que no**. Eso **no va a beneficiar a nadie** ya que todo el **contenido ya está disponible públicamente** en los libros oficiales de HackTricks de forma gratuita. **Preferiría que no**. Eso **no beneficiará a nadie** ya que todo el **contenido ya está disponible públicamente** en los libros oficiales de HackTricks de forma gratuita.
Si temes que vaya a desaparecer, simplemente haz fork en Github o descárgalo; como dije, ya es gratuito. Si temes que desaparezca, simplemente hazle fork en Github o descárgalo; como dije, ya es gratuito.
> [!WARNING] > [!WARNING]
> >
> - **Why do you have sponsors? Are HackTricks books for commercial purposes?** > - **¿Por qué tienen sponsors? ¿Los libros de HackTricks son con fines comerciales?**
El primer **valor** de **HackTricks** es ofrecer recursos educativos de hacking **GRATIS** para **TODO** el mundo. El equipo de HackTricks ha **dedicado miles de horas** para ofrecer este contenido, de nuevo, de forma **GRATIS**. El primer **valor** de **HackTricks** es ofrecer recursos educativos de hacking **GRATIS** para **TODO** el mundo. El equipo de HackTricks ha **dedicado miles de horas** para ofrecer este contenido, de nuevo, **GRATIS**.
Si piensas que los libros de HackTricks están hechos con **fines comerciales** estás **COMPLETAMENTE EQUIVOCADO**. Si piensas que los libros de HackTricks están hechos con **fines comerciales** estás **COMPLETAMENTE EQUIVOCADO**.
Tenemos sponsors porque, aunque todo el contenido sea GRATIS, queremos **ofrecer a la comunidad la posibilidad de valorar nuestro trabajo** si lo desean. Por eso ofrecemos a la gente la opción de donar a HackTricks a través de [**Github sponsors**](https://github.com/sponsors/carlospolop), y a **empresas relevantes de ciberseguridad** patrocinar HackTricks y **tener algunos anuncios** en el libro, colocando los **anuncios** siempre en lugares donde sean **visibles** pero **no interfieran en el proceso de aprendizaje** si alguien se concentra en el contenido. Tenemos patrocinadores porque, aunque todo el contenido sea GRATIS, queremos **ofrecer a la comunidad la posibilidad de valorar nuestro trabajo** si así lo desean. Por ello, ofrecemos a las personas la opción de donar a HackTricks a través de [**Github sponsors**](https://github.com/sponsors/carlospolop), y a **empresas relevantes de ciberseguridad** patrocinar HackTricks y **poner algunos anuncios** en el libro, siendo los **anuncios** siempre colocados en lugares donde sean **visibles** pero **no interfieran en el proceso de aprendizaje** si alguien se concentra en el contenido.
No encontrarás HackTricks lleno de anuncios molestos como otros blogs con mucho menos contenido que HackTricks, porque HackTricks no está hecho con fines comerciales. No encontrarás a HackTricks lleno de anuncios molestos como otros blogs con mucho menos contenido que HackTricks, porque HackTricks no está hecho con fines comerciales.
> [!CAUTION] > [!CAUTION]
> >
> - **What should I do if some HackTricks page is based on my blog post but it isn't referenced?** > - **¿Qué debo hacer si alguna página de HackTricks está basada en mi entrada de blog pero no está referenciada?**
**Lo sentimos mucho. Esto no debería haber ocurrido**. Por favor, háznoslo saber mediante Github issues, Twitter, Discord... el enlace de la página de HackTricks con el contenido y el enlace de tu blog y **lo revisaremos y lo añadiremos lo antes posible**. **Lo sentimos mucho. Esto no debería haber ocurrido**. Por favor, háznoslo saber vía Github issues, Twitter, Discord... el enlace de la página de HackTricks con el contenido y el enlace de tu blog y **lo comprobaremos y lo añadiremos lo antes posible**.
> [!CAUTION] > [!CAUTION]
> >
> - **What should I do if there is content from my blog in HackTricks and I don't want it there?** > - **¿Qué debo hacer si hay contenido de mi blog en HackTricks y no quiero que esté ahí?**
Ten en cuenta que tener enlaces a tu página en HackTricks: Ten en cuenta que tener enlaces a tu página en HackTricks:
- Mejora tu **SEO** - Mejora tu **SEO**
- El contenido se **traduce a más de 15 idiomas**, permitiendo que más personas accedan a este contenido - El contenido se **traduce a más de 15 idiomas**, permitiendo que más gente acceda a este contenido
- **HackTricks anima** a la gente a **visitar tu página** (varias personas nos han mencionado que desde que alguna de sus páginas está en HackTricks reciben más visitas) - **HackTricks anima** a la gente a **visitar tu página** (varias personas nos han comentado que desde que alguna de sus páginas está en HackTricks reciben más visitas)
Sin embargo, si aún quieres que el contenido de tu blog sea eliminado de HackTricks, solo háznoslo saber y definitivamente **eliminaremos todo enlace a tu blog**, y cualquier contenido basado en él. Sin embargo, si aún deseas que el contenido de tu blog sea eliminado de HackTricks, simplemente avísanos y **definitivamente eliminaremos todo enlace a tu blog**, y cualquier contenido basado en él.
> [!CAUTION] > [!CAUTION]
> >
> - **What should I do if I find copy-pasted content in HackTricks?** > - **¿Qué debo hacer si encuentro contenido copiado y pegado en HackTricks?**
Siempre **damos a los autores originales todos los créditos**. Si encuentras una página con contenido copiado y pegado sin la fuente original referenciada, háznoslo saber y nosotros o bien **la eliminaremos**, **añadiremos el enlace antes del texto**, o **la reescribiremos añadiendo el enlace**. Siempre **damos todo el crédito a los autores originales**. Si encuentras una página con contenido copiado sin referencia a la fuente original, háznoslo saber y nosotros **lo eliminaremos**, **añadiremos el enlace antes del texto**, o **lo reescribiremos añadiendo el enlace**.
## LICENCIA ## LICENCIA
Copyright © Todos los derechos reservados, salvo que se especifique lo contrario. Copyright © Todos los derechos reservados salvo que se especifique lo contrario.
#### Resumen de la licencia: #### Resumen de la licencia:
- Atribución: Eres libre de: - Atribución: Eres libre de:
- Compartir — copiar y redistribuir el material en cualquier medio o formato. - Compartir — copiar y redistribuir el material en cualquier medio o formato.
- Adaptar — remixar, transformar y crear obras derivadas del material. - Adaptar — remezclar, transformar y elaborar a partir del material.
#### Términos adicionales: #### Términos adicionales:
- Contenido de terceros: Algunas partes de este blog/libro pueden incluir contenido de otras fuentes, como extractos de otros blogs o publicaciones. El uso de dicho contenido se realiza bajo los principios de fair use o con permiso explícito de los respectivos titulares de derechos de autor. Por favor, consulta las fuentes originales para información específica sobre licencias respecto al contenido de terceros. - Contenido de terceros: Algunas partes de este blog/libro pueden incluir contenido de otras fuentes, como extractos de otros blogs o publicaciones. El uso de dicho contenido se realiza bajo los principios del uso legítimo (fair use) o con permiso explícito de los titulares de derechos de autor correspondientes. Por favor, consulta las fuentes originales para información específica sobre la licencia del contenido de terceros.
- Autoría: El contenido original creado por HackTricks está sujeto a los términos de esta licencia. Se recomienda atribuir este trabajo al autor cuando lo compartas o adaptes. - Autoría: El contenido original creado por HackTricks está sujeto a los términos de esta licencia. Se recomienda atribuir esta obra al autor al compartirla o adaptarla.
#### Exenciones: #### Exenciones:
- Uso comercial: Para consultas sobre el uso comercial de este contenido, por favor contáctame. - Uso comercial: Para consultas sobre el uso comercial de este contenido, por favor contáctame.
Esta licencia no concede ningún derecho sobre marcas comerciales o branding en relación con el contenido. Todas las marcas comerciales y el branding que aparecen en este blog/libro son propiedad de sus respectivos dueños. Esta licencia no concede ningún derecho sobre marcas registradas o identidad de marca en relación con el contenido. Todas las marcas y elementos de branding mostrados en este blog/libro son propiedad de sus respectivos titulares.
**Al acceder o usar HackTricks, aceptas cumplir con los términos de esta licencia. Si no estás de acuerdo con estos términos, por favor, no accedas a este sitio web.** **Al acceder o usar HackTricks, aceptas cumplir los términos de esta licencia. Si no estás de acuerdo con estos términos, por favor, no accedas a este sitio web.**
## **Descargo de responsabilidad** ## **Descargo de responsabilidad**
> [!CAUTION] > [!CAUTION]
> Este libro, 'HackTricks,' está destinado únicamente a fines educativos e informativos. El contenido de este libro se proporciona 'tal cual', y los autores y editores no hacen representaciones ni garantías de ningún tipo, expresas o implícitas, sobre la totalidad, exactitud, fiabilidad, idoneidad o disponibilidad de la información, productos, servicios o gráficos relacionados contenidos en este libro. Cualquier confianza que deposites en dicha información será, por tanto, estrictamente bajo tu propia responsabilidad. > Este libro, 'HackTricks,' está destinado únicamente a fines educativos e informativos. El contenido de este libro se proporciona 'tal cual', y los autores y editores no hacen declaraciones ni garantías de ningún tipo, expresas o implícitas, sobre la integridad, exactitud, fiabilidad, idoneidad o disponibilidad de la información, productos, servicios o gráficos relacionados contenidos en este libro. Cualquier confianza que deposites en dicha información es, por tanto, estrictamente bajo tu propia responsabilidad.
> >
> En ningún caso los autores y editores serán responsables de ninguna pérdida o daño, incluyendo sin limitación, pérdidas o daños indirectos o consecuentes, o cualquier pérdida o daño que surja de la pérdida de datos o beneficios derivados de, o en conexión con, el uso de este libro. > Los autores y editores no serán en ningún caso responsables por cualquier pérdida o daño, incluyendo, sin limitación, pérdidas o daños indirectos o consecuentes, o cualquier pérdida o daño que surja de la pérdida de datos o beneficios derivados de, o en relación con, el uso de este libro.
> >
> Además, las técnicas y consejos descritos en este libro se proporcionan únicamente con fines educativos e informativos, y no deben utilizarse para actividades ilegales o maliciosas. Los autores y editores no aprueban ni apoyan ninguna actividad ilegal o poco ética, y cualquier uso de la información contenida en este libro corre por cuenta y riesgo del usuario. > Además, las técnicas y consejos descritos en este libro se proporcionan únicamente con fines educativos e informativos, y no deben utilizarse para actividades ilegales o maliciosas. Los autores y editores no avalan ni apoyan actividades ilegales o poco éticas, y cualquier uso de la información contenida en este libro es bajo la propia responsabilidad y criterio del usuario.
> >
> El usuario es el único responsable de cualquier acción tomada basándose en la información contenida en este libro, y siempre debe buscar asesoramiento y asistencia profesional al intentar implementar cualquiera de las técnicas o consejos descritos en el mismo. > El usuario es el único responsable de cualquier acción tomada con base en la información contenida en este libro, y siempre debe buscar asesoramiento y asistencia profesional al intentar implementar cualquiera de las técnicas o consejos aquí descritos.
> >
> Al usar este libro, el usuario acepta liberar a los autores y editores de cualquier y toda responsabilidad por daños, pérdidas o perjuicios que puedan resultar del uso de este libro o de cualquiera de las informaciones contenidas en el mismo. > Al usar este libro, el usuario acepta eximir a los autores y editores de toda responsabilidad por cualquier daño, pérdida o perjuicio que pueda resultar del uso de este libro o de cualquiera de las informaciones contenidas en él.
{{#include ../banners/hacktricks-training.md}} {{#include ../banners/hacktricks-training.md}}

View File

@ -6,34 +6,63 @@
*/ */
(() => { (() => {
"use strict"; "use strict";
/* ───────────── 0. helpers (main thread) ───────────── */ /* ───────────── 0. helpers (main thread) ───────────── */
const clear = el => { while (el.firstChild) el.removeChild(el.firstChild); }; const clear = el => { while (el.firstChild) el.removeChild(el.firstChild); };
/* ───────────── 1. WebWorker code ─────────────────── */ /* ───────────── 1. WebWorker code ─────────────────── */
const workerCode = ` const workerCode = `
self.window = self; self.window = self;
self.search = self.search || {}; self.search = self.search || {};
const abs = p => location.origin + p; const abs = p => location.origin + p;
/* 1 — elasticlunr */ /* 1 — elasticlunr */
try { importScripts('https://cdn.jsdelivr.net/npm/elasticlunr@0.9.5/elasticlunr.min.js'); } try { importScripts('https://cdn.jsdelivr.net/npm/elasticlunr@0.9.5/elasticlunr.min.js'); }
catch { importScripts(abs('/elasticlunr.min.js')); } catch { importScripts(abs('/elasticlunr.min.js')); }
/* 2 — load a single index (remote → local) */ /* 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;
}
}
/* 3 — load a single index (remote → local) */
async function loadIndex(remote, local, isCloud=false){ async function loadIndex(remote, local, isCloud=false){
let rawLoaded = false; let rawLoaded = false;
if(remote){ if(remote){
/* Try ONLY compressed version from GitHub (remote already includes .js.gz) */
try { try {
const r = await fetch(remote,{mode:'cors'}); const r = await fetch(remote,{mode:'cors'});
if (!r.ok) throw new Error('HTTP '+r.status); if (r.ok) {
importScripts(URL.createObjectURL(new Blob([await r.text()],{type:'application/javascript'}))); const compressed = await r.arrayBuffer();
rawLoaded = true; const text = await decompressGzip(compressed);
} catch(e){ console.warn('remote',remote,'failed →',e); } 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){ 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); } catch(e){ console.error('local',local,'failed →',e); }
} }
if(!rawLoaded) return null; /* give up on this index */ if(!rawLoaded) return null; /* give up on this index */
@ -62,150 +91,158 @@
return local ? loadIndex(null, local, isCloud) : null; return local ? loadIndex(null, local, isCloud) : null;
} }
(async () => { let built = [];
const htmlLang = (document.documentElement.lang || 'en').toLowerCase(); const MAX = 30, opts = {bool:'AND', expand:true};
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';
const mainTags = Array.from(new Set([\`searchindex-\${lang}\`, 'searchindex-en', 'searchindex-master'])); self.onmessage = async ({data}) => {
const cloudTags = Array.from(new Set([\`searchindex-\${lang}\`, 'searchindex-en', 'searchindex-master'])); if(data.type === 'init'){
const lang = data.lang || 'en';
const searchindexBase = 'https://raw.githubusercontent.com/HackTricks-wiki/hacktricks-searchindex/master';
const MAIN_REMOTE_SOURCES = mainTags.map(tag => \`\${mainReleaseBase}/\${tag}/searchindex.js\`); /* Remote sources are .js.gz (compressed), local fallback is .js (uncompressed) */
const CLOUD_REMOTE_SOURCES = cloudTags.map(tag => \`\${cloudReleaseBase}/\${tag}/searchindex.js\`); 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 indices = []; const MAIN_REMOTE_SOURCES = mainFilenames.map(function(filename) { return searchindexBase + '/' + filename; });
const main = await loadWithFallback(MAIN_REMOTE_SOURCES , '/searchindex.js', false); if(main) indices.push(main); const CLOUD_REMOTE_SOURCES = cloudFilenames.map(function(filename) { return searchindexBase + '/' + filename; });
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; } 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 */ /* build index objects */
const built = indices.map(d => ({ built = indices.map(d => ({
idx : elasticlunr.Index.load(d.json), idx : elasticlunr.Index.load(d.json),
urls: d.urls, urls: d.urls,
cloud: d.cloud, cloud: d.cloud,
base: d.cloud ? 'https://cloud.hacktricks.wiki/' : '' base: d.cloud ? 'https://cloud.hacktricks.wiki/' : ''
})); }));
postMessage({ready:true}); postMessage({ready:true});
const MAX = 30, opts = {bool:'AND', expand:true}; return;
}
self.onmessage = ({data:q}) => { const q = data.query || data;
if(!q){ postMessage([]); return; } if(!q){ postMessage([]); return; }
const all = []; const all = [];
for(const s of built){ for(const s of built){
const res = s.idx.search(q,opts); const res = s.idx.search(q,opts);
if(!res.length) continue; if(!res.length) continue;
const max = res[0].score || 1; const max = res[0].score || 1;
res.forEach(r => { res.forEach(r => {
const doc = s.idx.documentStore.getDoc(r.ref); const doc = s.idx.documentStore.getDoc(r.ref);
all.push({ all.push({
norm : r.score / max, norm : r.score / max,
title: doc.title, title: doc.title,
body : doc.body, body : doc.body,
breadcrumbs: doc.breadcrumbs, breadcrumbs: doc.breadcrumbs,
url : s.base + s.urls[r.ref], url : s.base + s.urls[r.ref],
cloud: s.cloud cloud: s.cloud
});
}); });
}); }
} all.sort((a,b)=>b.norm-a.norm);
all.sort((a,b)=>b.norm-a.norm); postMessage(all.slice(0,MAX));
postMessage(all.slice(0,MAX)); };
}; `;
})();
`;
/* ───────────── 2. spawn worker ───────────── */ /* ───────────── 2. spawn worker ───────────── */
const worker = new Worker(URL.createObjectURL(new Blob([workerCode],{type:'application/javascript'}))); const worker = new Worker(URL.createObjectURL(new Blob([workerCode],{type:'application/javascript'})));
/* ───────────── 3. DOM refs ─────────────── */ /* ───────────── 2.1. initialize worker with language ───────────── */
const wrap = document.getElementById('search-wrapper'); const htmlLang = (document.documentElement.lang || 'en').toLowerCase();
const bar = document.getElementById('searchbar'); const lang = htmlLang.split('-')[0];
const list = document.getElementById('searchresults'); worker.postMessage({type: 'init', lang: lang});
const listOut = document.getElementById('searchresults-outer');
const header = document.getElementById('searchresults-header');
const icon = document.getElementById('search-toggle');
const READY_ICON = icon.innerHTML; /* ───────────── 3. DOM refs ─────────────── */
icon.textContent = '⏳'; const wrap = document.getElementById('search-wrapper');
icon.setAttribute('aria-label','Loading search …'); const bar = document.getElementById('searchbar');
icon.setAttribute('title','Search is loading, please wait...'); const list = document.getElementById('searchresults');
const listOut = document.getElementById('searchresults-outer');
const header = document.getElementById('searchresults-header');
const icon = document.getElementById('search-toggle');
const HOT=83, ESC=27, DOWN=40, UP=38, ENTER=13; const READY_ICON = icon.innerHTML;
let debounce, teaserCount=0; icon.textContent = '⏳';
icon.setAttribute('aria-label','Loading search …');
icon.setAttribute('title','Search is loading, please wait...');
/* ───────────── 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 HOT=83, ESC=27, DOWN=40, UP=38, ENTER=13;
const stem=w=>elasticlunr.stemmer(w.toLowerCase()); let debounce, teaserCount=0;
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){ /* ───────────── helpers (teaser, metric) ───────────── */
const teaser=makeTeaser(escapeHTML(d.body),terms); const escapeHTML = (()=>{const M={'&':'&amp;','<':'&lt;','>':'&gt;','"':'&#34;','\'':'&#39;'};return s=>s.replace(/[&<>'"]/g,c=>M[c]);})();
teaserCount++; const URL_MARK='highlight';
const enc=encodeURIComponent(terms.join(' ')).replace(/'/g,'%27'); function metric(c,t){return c?`${c} search result${c>1?'s':''} for '${t}':`:`No search results for '${t}'.`;}
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 makeTeaser(body,terms){
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'));}} const stem=w=>elasticlunr.stemmer(w.toLowerCase());
function blur(){const t=document.createElement('input'); t.style.cssText='position:absolute;opacity:0;'; icon.appendChild(t); t.focus(); t.remove();} const T=terms.map(stem),W_S=40,W_F=8,W_N=2,WIN=30;
const W=[],sents=body.toLowerCase().split('. ');
icon.addEventListener('click',()=>showUI(wrap.classList.contains('hidden'))); 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++;});
document.addEventListener('keydown',e=>{ if(!W.length) return body;
if(e.altKey||e.ctrlKey||e.metaKey||e.shiftKey) return; const win=Math.min(W.length,WIN);
const f=/^(?:input|select|textarea)$/i.test(e.target.nodeName); const sums=[W.slice(0,win).reduce((a,[,wt])=>a+wt,0)];
if(e.keyCode===HOT && !f){e.preventDefault(); showUI(true);} else if(e.keyCode===ESC){e.preventDefault(); showUI(false); blur();} for(let k=1;k<=W.length-win;k++) sums[k]=sums[k-1]-W[k-1][1]+W[k+win-1][1];
else if(e.keyCode===DOWN && document.activeElement===bar){e.preventDefault(); const first=list.firstElementChild; if(first){blur(); first.classList.add('focus');}} const best=found?sums.lastIndexOf(Math.max(...sums)):0;
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);}} 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('');
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 docs=data, q=bar.value.trim(), terms=q.split(/\s+/).filter(Boolean);
header.textContent=metric(docs.length,q); function format(d,terms){
clear(list); const teaser=makeTeaser(escapeHTML(d.body),terms);
docs.forEach(d=>{const li=document.createElement('li'); li.innerHTML=format(d,terms); list.appendChild(li);}); teaserCount++;
listOut.classList.toggle('hidden',!docs.length); 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);
};
})();