PageSourceSearch

https://elperuano.pe/fenomeno-el-nino/components/resumen/resumen.j…ZNpLKVVURgN_u5f_575qlnQUw8j24igcJsdxYOJwp4w

js elperuano.pe collected 2026-10-01 09:10:18 UTC 9,166 bytes, 222 lines download raw bytes

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.