1// Componente: Resumen 2(function () { 3 'use strict'; 4 5 var api = FELN.api; 6 var bus = FELN.bus; 7 8 var CUANTAS = 3; 9 10 var el = {}; 11 var peticion = 0; 12 13 // imagen: portada de la norma o foto de la noticia 14 /* El pie: la fecha PRIMERO y con etiqueta propia, y detras el contexto. 15 * 16 * El orden no es cosmetico. El pie lleva -webkit-line-clamp: 1, asi que lo 17 * que no cabe en una linea desaparece -- y la fecha iba al final, detras de 18 * noventa caracteres de sumilla. Es decir: se pintaba siempre y no se veia 19 * nunca. Delante, cabe siempre. 20 * 21 * Y va en su propio <time>: es un dato, no parte de la frase, y asi se le 22 * puede dar peso propio sin tocar el resto. */ 23 function pie(fecha, contexto) { 24 var trozos = []; 25 26 if (fecha) trozos.push('<time class="resumen-item-fecha">' + api.escapeHtml(fecha) + '</time>'); 27 if (contexto) trozos.push(api.escapeHtml(contexto)); 28 29 return trozos.join(' · '); 30 } 31 32 // subtitulo llega como HTML ya escapado por pie(); los dos que llaman lo usan. 33 function fila(titulo, subtitulo, url, imagen, ga) { 34 var miniatura = imagen 35 ? '<img class="resumen-miniatura" src="' + api.escapeHtml(imagen) + '" alt="" loading="lazy"' + 36 ' data-respaldo="1" />' 37 : ''; 38 39 var cuerpo = miniatura + 40 '<span class="resumen-item-texto">' + 41 '<span class="resumen-item-titulo">' + api.escapeHtml(titulo) + '</span>' + 42 (subtitulo ? '<span class="resumen-item-pie">' + subtitulo + '</span>' : '') + 43 '</span>'; 44 45 var marca = ga 46 ? ' data-ga="' + api.escapeHtml(ga.tipo) + '" data-ga-titulo="' + api.escapeHtml(ga.titulo || '') + '"' 47 : ''; 48 49 return '<li class="resumen-item">' + 50 (url 51 ? '<a href="' + api.escapeHtml(url) + '" target="_blank" rel="noopener noreferrer"' + marca + '>' + cuerpo + '</a>' 52 : '<span class="resumen-item-cuerpo">' + cuerpo + '</span>') + 53 '</li>'; 54 } 55 56 function vacio(texto) { 57 return '<li class="resumen-vacio">' + texto + '</li>'; 58 } 59 60 // Filas de espera con la misma estructura que las reales: miniatura, titular de dos lÃneas y pie 61 function esqueleto() { 62 var filas = ''; 63 for (var i = 0; i < CUANTAS; i++) { 64 filas += 65 '<li class="resumen-item">' + 66 '<span class="resumen-item-cuerpo">' + 67 '<span class="resumen-miniatura esq"></span>' + 68 '<span class="resumen-item-texto">' + 69 '<span class="resumen-item-titulo esq">Decreto supremo que declara ' + 70 'el estado de emergencia</span>' + 71 '<span class="resumen-item-pie esq">Departamento · 12 de agosto de 2026</span>' + 72 '</span>' + 73 '</span>' + 74 '</li>'; 75 } 76 return filas; 77 } 78 79 function esperar(bloque) { 80 bloque.raiz.setAttribute('data-cargando', ''); 81 bloque.lista.innerHTML = esqueleto(); 82 } 83 84 function listo(bloque, html) { 85 bloque.raiz.removeAttribute('data-cargando'); 86 bloque.lista.innerHTML = html; 87 } 88 89 /* Texto del enlace de salida: "Ver las 47" / "Ver la única" / "Ver todas". 90 * 91 * Con "deEsteSitio" se antepone cuantas son del territorio elegido: 92 * "Ver las 16 de aqui, de 207". Solo cuando hay territorio y el numero 93 * aporta algo: si coincide con el total, decirlo dos veces sobra. */ 94 function rotuloTotal(destino, total, deEsteSitio) { 95 if (typeof total !== 'number' || total <= 0) { 96 destino.textContent = 'todas'; 97 return; 98 } 99 100 var cuantas = total === 1 ? 'la única' : 'las ' + total.toLocaleString('es-PE'); 101 102 /* Con territorio manda SU cifra, sola, como en normas. 103 * 104 * Se probo "las 4 de aqui, de 208" y sobraba: la comparacion es 105 * informacion que nadie pidio en un enlace de tres palabras. 106 * 107 * OJO con lo que esto promete. La seccion de noticias no descarta por 108 * territorio, reordena: al entrar se ven las 208 con las 4 delante. El 109 * rotulo dice cuantas son de aqui, no cuantas se veran. Si algun dia se 110 * decide que el territorio filtre de verdad, esto ya encaja; mientras, 111 * es la unica parte del portal donde el numero del enlace y el de la 112 * pagina de destino no coinciden. */ 113 if (typeof deEsteSitio === 'number' && deEsteSitio > 0 && deEsteSitio < total) { 114 destino.textContent = deEsteSitio === 1 115 ? 'la única de aquÃ'
116 : 'las ' + deEsteSitio.toLocaleString('es-PE'); 117 return; 118 } 119 120 destino.textContent = cuantas; 121 } 122 123 async function cargarNormas(mia) { 124 try { 125 var data = await api.getJson('/api/normas' + api.buildQuery(FELN.filtrosBase({ 126 tipoDispositivo: FELN.state.tipoDispositivo || null, 127 pagina: 1, 128 tamanoPagina: CUANTAS 129 }))); 130 if (mia !== peticion) return; 131 132 rotuloTotal(el.totalNormas, data.total); 133 listo(el.bloqueNormas, data.items.length 134 ? data.items.map(function (n) { 135 /* Publicacion primero, emision de respaldo: es el mismo 136 criterio que la tabla de normas, y lo que importa aqui es 137 cuando SALIO en el Diario Oficial. */ 138 var f = FELN.normaCard.fechaLegible(n.fechaPublicacion || n.fechaEmision); 139 140 // Un extracto de la sumilla (n.descripcion), no el ambito. 141 var sumilla = (n.descripcion || '').trim(); 142 if (sumilla.length > 90) sumilla = sumilla.slice(0, 90).replace(/\s+\S*$/, '') + 'â¦'; 143 144 return fila(n.titulo, pie(f, sumilla), n.linkHTML || n.urlPDF, n.urlPortada, 145 { tipo: 'norma', titulo: n.titulo }); 146 }).join('') 147 : vacio('No hay normas para este filtro.')); 148 } catch (e) { 149 if (mia !== peticion) return; 150 console.warn('[resumen] normas no disponibles:', e); 151 rotuloTotal(el.totalNormas, null); 152 listo(el.bloqueNormas, vacio('No se pudieron cargar.')); 153 } 154 } 155 156 async function cargarNoticias(mia) { 157 try { 158 var data = await api.getJson('/api/noticias' + api.buildQuery(FELN.filtrosNoticias({ 159 pagina: 1, 160 tamanoPagina: CUANTAS 161 }))); 162 if (mia !== peticion) return; 163 164 /* El territorio NO descarta noticias: las sube. 165 * 166 * Por eso el total no se movia al cambiar de ubigeo y parecia que 167 * el filtro no hacia nada. El numero era correcto; lo que faltaba 168 * era decir cuantas son de ese sitio, que el API ya devuelve en 169 * "coincidencias" y aqui se estaba tirando. 170 * 171 * Se ensenan las dos cifras porque las dos importan: la del 172 * territorio es lo que se pregunto, y el total es lo que se va a 173 * encontrar al entrar -- primero las suyas, y detras el resto. */ 174 rotuloTotal(el.totalNoticias, data.total, data.coincidencias); 175 176 listo(el.bloqueNoticias, data.items.length 177 ? data.items.map(function (n) { 178 return fila(n.titulo, pie(FELN.normaCard.fechaLegible(n.fechaPublicacion), ''), 179 n.urlFuente, n.urlImagen, { tipo: 'noticia', titulo: n.titulo }); 180 }).join('') 181 : vacio('No hay noticias en este periodo.')); 182 } catch (e) { 183 if (mia !== peticion) return; 184 console.warn('[resumen] noticias no disponibles:', e); 185 rotuloTotal(el.totalNoticias, null); 186 listo(el.bloqueNoticias, vacio('No se pudieron cargar.')); 187 } 188 } 189 190 function refrescar() { 191 var mia = ++peticion; 192 193 esperar(el.bloqueNormas); 194 esperar(el.bloqueNoticias); 195 196 return Promise.all([cargarNormas(mia), cargarNoticias(mia)]); 197 } 198 199 FELN.componente('resumen', { 200 init(root) { 201 el.totalNormas = root.querySelector('[data-ref="total-normas"]'); 202 el.totalNoticias = root.querySelector('[data-ref="total-noticias"]'); 203 204 el.bloqueNormas = { 205 raiz: root.querySelector('[data-ref="bloque-normas"]'), 206 lista: root.querySelector('[data-ref="normas"]') 207 }; 208 el.bloqueNoticias = { 209 raiz: root.querySelector('[data-ref="bloque-noticias"]'), 210 lista: root.querySelector('[data-ref="noticias"]') 211 }; 212 213 ['filtros:cambio', 'fecha:cambio', 'dispositivo:cambio'].forEach(function (evento) { 214 bus.on(evento, refrescar); 215 }); 216 217 bus.on('novedades:hay', refrescar); 218 }, 219 220 cargar: refrescar 221 }); 222})();
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.