morphos / js /papers.js
josesalazar2025
Add Spanish explanatory comments across JS modules and PHP proxy
d90e9a6
Raw
History Blame Contribute Delete
10.9 kB
const PROXY_URL = 'api/papers_proxy.php';
const POR_PAGINA = 10;
let todosLosPapers = [];
let paginaActual = 0;
let consultaActual = '';
//Esto nos permite hacer búsquedas en la API de PubMed, sólo recibe texto en inglés
const TERMINOS_EN = {
'Anemia': 'anemia',
'Eritrocitosis': 'erythrocytosis polycythemia',
'Leucocitosis': 'leukocytosis',
'Leucocitosis neutrofílica': 'neutrophilic leukocytosis',
'Leucocitosis linfocítica': 'lymphocytic leukocytosis',
'Leucopenia': 'leukopenia',
'Eosinofilia': 'eosinophilia',
'Neutropenia': 'neutropenia',
'Linfopenia': 'lymphopenia',
'Trombocitopenia': 'thrombocytopenia',
'Trombocitosis': 'thrombocytosis',
'Daño hepatocelular': 'hepatocellular damage liver injury',
'Elevación de ALT aislada': 'ALT elevation liver',
'Patrón colestásico': 'cholestasis',
'Hiperbilirrubinemia': 'hyperbilirubinemia jaundice',
'Azotemia': 'azotemia renal failure',
'Hiperuremia aislada (BUN)': 'elevated BUN prerenal azotemia',
'BUN disminuido': 'low BUN hepatic failure',
'Creatinina elevada (BUN normal)': 'elevated creatinine kidney',
'Hiperglucemia': 'hyperglycemia diabetes mellitus',
'Hipoglucemia': 'hypoglycemia',
'Hiperproteinemia': 'hyperproteinemia',
'Hipoproteinemia / Hipoalbuminemia': 'hypoproteinemia hypoalbuminemia',
'Hipoalbuminemia': 'hypoalbuminemia',
'Hipercalcemia': 'hypercalcemia',
'Hipocalcemia': 'hypocalcemia',
'Hipernatremia': 'hypernatremia',
'Hiponatremia': 'hyponatremia',
'Hiperpotasemia': 'hyperkalemia',
'Hipopotasemia': 'hypokalemia',
'Hiperfosforemia': 'hyperphosphatemia',
'Hipotiroidismo': 'hypothyroidism',
'Hipertiroidismo': 'hyperthyroidism',
'Hiperadrenocorticismo (Cushing)': 'hyperadrenocorticism Cushing',
'Hipoadrenocorticismo (Addison)': 'hypoadrenocorticism Addison',
'Ratio Na:K reducido — sospecha de hipoadrenocorticismo': 'hypoadrenocorticism sodium potassium ratio',
'Cortisol basal bajo — posible hipoadrenocorticismo': 'low basal cortisol hypoadrenocorticism',
'Hiposthenuria': 'hyposthenuria urine specific gravity',
'Isosthenuria': 'isosthenuria urine concentration renal',
'Posible déficit de insulina': 'insulin deficiency hyperglycemia diabetes mellitus',
};
const traducirPatron = (nombre) => {
for (const [es, en] of Object.entries(TERMINOS_EN)) {
if (nombre.startsWith(es)) return en;
}
return nombre.replace(/[áéíóúñ]/g, c => ({á:'a',é:'e',í:'i',ó:'o',ú:'u',ñ:'n'})[c] || c);
};
const construirQuery = (patrones) => {
if (!patrones || patrones.length === 0) return 'veterinary clinical laboratory diagnosis canine feline';
// Limita a 3 terminos para mantener la query enfocada y evitar resultados irrelevantes
const terminos = [...new Set(patrones.map(p => traducirPatron(p.nombre)))].slice(0, 3);
return `${terminos.join(' ')} canine OR feline veterinary`;
};
const buscarPapers = async (query) => {
const respuesta = await fetch(`${PROXY_URL}?query=${encodeURIComponent(query)}`);
if (!respuesta.ok) {
const detalle = await respuesta.json().catch(() => ({}));
throw new Error(detalle.error || `Error ${respuesta.status}`);
}
const datos = await respuesta.json();
return datos.data || [];
};
const renderizarTarjetaPaper = (paper) => {
const titulo = paper.title || 'Sin título';
const autores = paper.authors?.slice(0, 3).map(a => a.name).join(', ') || 'Autores desconocidos';
const masAutores = (paper.authors?.length || 0) > 3 ? ' et al.' : '';
const anio = paper.year || '—';
const revista = paper.journal || '';
const urlDoi = paper.doi ? `https://doi.org/${paper.doi}` : null;
const urlPubmed = paper.pmid ? `https://pubmed.ncbi.nlm.nih.gov/${paper.pmid}/` : null;
const urlPrincipal = urlDoi || urlPubmed;
const articulo = document.createElement('article');
articulo.className = 'paper-tarjeta';
const meta = document.createElement('div');
meta.className = 'paper-meta';
const spanAnio = document.createElement('span');
spanAnio.className = 'paper-anio';
spanAnio.textContent = anio;
meta.append(spanAnio);
if (revista) {
const spanRevista = document.createElement('span');
spanRevista.className = 'paper-revista';
spanRevista.textContent = revista;
meta.append(spanRevista);
}
articulo.append(meta);
const h3 = document.createElement('h3');
h3.className = 'paper-titulo';
if (urlPrincipal) {
const link = document.createElement('a');
link.href = urlPrincipal;
link.target = '_blank';
link.rel = 'noopener noreferrer';
link.textContent = titulo;
h3.append(link);
} else {
h3.textContent = titulo;
}
articulo.append(h3);
const pAutores = document.createElement('p');
pAutores.className = 'paper-autores';
pAutores.textContent = autores + masAutores;
articulo.append(pAutores);
return articulo;
};
const renderizarPaginacion = () => {
const totalPaginas = Math.ceil(todosLosPapers.length / POR_PAGINA);
const contenedor = document.getElementById('papers-paginacion');
if (!contenedor) return;
if (totalPaginas <= 1) {
contenedor.innerHTML = '';
return;
}
// Ventana deslizante de maximo 5 botones centrada en la pagina actual
const inicio = Math.max(0, paginaActual - 2);
const fin = Math.min(totalPaginas, inicio + 5);
let html = `<button class="papers-pag-btn" data-pagina="${paginaActual - 1}" ${paginaActual === 0 ? 'disabled' : ''} aria-label="Página anterior"><img src="assets/icons/anterior.svg" alt="" aria-hidden="true" width="16" height="16"></button>`;
for (let i = inicio; i < fin; i++) {
html += `<button class="papers-pag-btn ${i === paginaActual ? 'activo' : ''}" data-pagina="${i}">${i + 1}</button>`;
}
html += `<button class="papers-pag-btn" data-pagina="${paginaActual + 1}" ${paginaActual >= totalPaginas - 1 ? 'disabled' : ''} aria-label="Página siguiente"><img src="assets/icons/siguiente.svg" alt="" aria-hidden="true" width="16" height="16"></button>`;
contenedor.innerHTML = html;
};
const renderizarPaginaActual = () => {
const lista = document.getElementById('papers-lista');
if (!lista) return;
const inicio = paginaActual * POR_PAGINA;
const pagina = todosLosPapers.slice(inicio, inicio + POR_PAGINA);
lista.replaceChildren();
if (pagina.length === 0) {
const vacio = document.createElement('p');
vacio.className = 'papers-vacio';
vacio.textContent = 'No se encontraron artículos para esta búsqueda.';
lista.append(vacio);
} else {
lista.append(...pagina.map(renderizarTarjetaPaper));
}
renderizarPaginacion();
lista.scrollTop = 0;
};
const irAPagina = (numeroPagina) => {
const totalPaginas = Math.ceil(todosLosPapers.length / POR_PAGINA);
if (numeroPagina < 0 || numeroPagina >= totalPaginas) return;
paginaActual = numeroPagina;
renderizarPaginaActual();
};
const mostrarEstadoCarga = () => {
const lista = document.getElementById('papers-lista');
if (lista) lista.innerHTML = '<p class="papers-cargando">Buscando artículos científicos…</p>';
const paginacion = document.getElementById('papers-paginacion');
if (paginacion) paginacion.innerHTML = '';
};
const mostrarError = (mensaje) => {
const lista = document.getElementById('papers-lista');
if (!lista) return;
const p = document.createElement('p');
p.className = 'papers-error';
p.textContent = mensaje;
lista.replaceChildren(p);
};
export const abrirModalPapers = async (patrones) => {
const modal = document.getElementById('modal-papers');
const overlay = document.getElementById('modal-papers-overlay');
if (!modal || !overlay) return;
const nuevaConsulta = construirQuery(patrones);
modal.removeAttribute('hidden');
overlay.classList.add('activo');
document.body.style.overflow = 'hidden';
requestAnimationFrame(() => modal.classList.add('visible'));
const etiquetaConsulta = document.getElementById('papers-consulta');
if (etiquetaConsulta) etiquetaConsulta.textContent = `"${nuevaConsulta}"`;
// Reutiliza resultados si la consulta no cambio desde la ultima vez
if (nuevaConsulta === consultaActual && todosLosPapers.length > 0) {
renderizarPaginaActual();
return;
}
consultaActual = nuevaConsulta;
todosLosPapers = [];
paginaActual = 0;
mostrarEstadoCarga();
try {
todosLosPapers = await buscarPapers(nuevaConsulta);
renderizarPaginaActual();
} catch (error) {
mostrarError(error.message || 'No se pudo conectar con PubMed. Intenta de nuevo más tarde.');
console.error('Error buscando papers:', error);
}
};
const cerrarModalPapers = () => {
const modal = document.getElementById('modal-papers');
const overlay = document.getElementById('modal-papers-overlay');
if (!modal || !overlay) return;
modal.classList.remove('visible');
overlay.classList.remove('activo');
document.body.style.overflow = '';
setTimeout(() => modal.setAttribute('hidden', ''), 250);
};
export const inicializarModalPapers = () => {
document.getElementById('modal-papers-cerrar')?.addEventListener('click', cerrarModalPapers);
document.getElementById('modal-papers-overlay')?.addEventListener('click', cerrarModalPapers);
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
const modal = document.getElementById('modal-papers');
if (modal && !modal.hidden) cerrarModalPapers();
}
});
document.getElementById('papers-paginacion')?.addEventListener('click', (e) => {
const btn = e.target.closest('.papers-pag-btn');
if (!btn || btn.disabled) return;
irAPagina(parseInt(btn.dataset.pagina, 10));
});
document.getElementById('papers-busqueda-form')?.addEventListener('submit', async (e) => {
e.preventDefault();
const input = document.getElementById('papers-busqueda-input');
const termino = input?.value.trim();
if (!termino) return;
const etiquetaConsulta = document.getElementById('papers-consulta');
if (etiquetaConsulta) etiquetaConsulta.textContent = `"${termino}"`;
consultaActual = termino;
todosLosPapers = [];
paginaActual = 0;
mostrarEstadoCarga();
try {
todosLosPapers = await buscarPapers(termino);
renderizarPaginaActual();
} catch (error) {
mostrarError(error.message || 'No se pudo conectar con PubMed. Intenta de nuevo más tarde.');
console.error('Error buscando papers:', error);
}
});
};