PageSourceSearch

https://pixelstudioperu.com/wp-content/plugins/personajes-grupos-plugin/js/script.js

js pixelstudioperu.com collected 2026-10-07 03:46:03 UTC 37,119 bytes, 834 lines download raw bytes

1// Flag para indicar si la página está completamente inicializada
2let isPageInitialized = false; 
3let currentTabId = 'personajes'; // Almacena el ID del tab actualmente activo
4let isFichaSidebarActive = false; // Nueva variable para rastrear el estado de ficha-sidebar
5let isSearchBarVisible = false; // Nuevo flag para el estado de visibilidad
6
7// Actualiza el estado de los botones del tabs-container en función de isFichaSidebarActive
8function actualizarEstadoBotonesTabs() {
9    const buttons = document.querySelectorAll('.tabs-container .tab-button');
10    buttons.forEach(button => {
11        if (isFichaSidebarActive) {
12            button.classList.remove('active');
13            //console.log(`Removiendo clase 'active' de botón: ${button.innerText} debido a isFichaSidebarActive=true`);
14        } else if (button.getAttribute('onclick') === `mostrarTab('${currentTabId}')`) {
15            button.classList.add('active');
16            //console.log(`Restaurando clase 'active' en botón: ${button.innerText}`);
17        }
18    });
19}
20function resetFichaSidebar() {
21    //console.log("Resetando vista de ficha...");
22
23    // Restablece la bandera de la ficha activa
24    isFichaSidebarActive = false;
25
26    // Muestra el contenedor de búsqueda si estaba oculto
27    const searchBarContainer = document.querySelector('.search-bar-container');
28    if (searchBarContainer) {
29        //searchBarContainer.style.display = 'flex'; // Asegura que sea visible
30        searchBarContainer.classList.remove('hidden-opacity');
31        //console.log("Contenedor de búsqueda restaurado.");
32    }
33
34    // Asegúrate de que todos los tabs estén en un estado coherente
35    actualizarEstadoBotonesTabs();
36
37    // Limpia el contenedor de la ficha si es necesario
38    const fichaContainer = document.querySelector('.single-view');
39    if (fichaContainer) {
40        fichaContainer.innerHTML = ''; // Limpia el contenido de la ficha
41        fichaContainer.classList.remove('single-view'); // Restablece clases
42        //console.log("Contenido de la ficha limpiado.");
43    }
44
45    //console.log("Vista de ficha reseteada correctamente.");
46}
47function mostrarTab(tabId) {
48    // Si está activa la ficha, reseteamos el estado y continuamos
49    if (isFichaSidebarActive) {
50        //console.log("Saliendo de la vista de ficha para mostrar el tab:", tabId);
51        resetFichaSidebar(); // Nueva función para reiniciar el estado
52    }
53
54    currentTabId = tabId;
55
56    const tabs = document.querySelectorAll('.tab-content');
57    const buttons = document.querySelectorAll('.tab-button');
58    const personajesContainer = document.querySelector('.personajes-container');
59    const gruposContainer = document.querySelector('.grupos-container');
60    const activeContainer = tabId === 'personajes' ? personajesContainer : gruposContainer;
61    const inactiveContainer = tabId === 'personajes' ? gruposContainer : personajesContainer;
62
63    // Mostrar el preloader (si es necesario)
64    mostrarPreloader();
65
66    // Limpia el contenedor inactivo y oculta su contenido
67    if (inactiveContainer) {
68        inactiveContainer.innerHTML = '';
69        inactiveContainer.classList.remove('single-view');
70        inactiveContainer.style.display = 'none';
71        const inactiveButton = document.querySelector(
72            `.cargar-mas-btn[data-type="${tabId === 'personajes' ? 'grupos' : 'personajes'}"]`
73        );
74        if (inactiveButton) {
75            inactiveButton.setAttribute('data-offset', 0);
76            inactiveButton.style.display = 'none';
77        }
78    }
79
80    // Ocultar el botón "Cargar Más" del tab activo inicialmente
81    const cargarMasBtn = document.querySelector(`.cargar-mas-btn[data-type="${tabId}"]`);
82    if (cargarMasBtn) {
83        cargarMasBtn.style.display = 'none';
84    }
85
86    // Limpia y muestra el contenedor activo
87    if (activeContainer) {
88        activeContainer.innerHTML = '';
89        activeContainer.classList.remove('single-view');
90        cargarListado(tabId); // Cargar contenido mediante AJAX
91        activeContainer.style.display = 'flex';
92    }
93
94    // Actualiza el estado de los botones del contenedor de tabs
95    actualizarEstadoBotonesTabs();
96
97    // Actualiza el campo de búsqueda
98    const searchInput = document.getElementById('search-input');
99    if (searchInput) {
100        searchInput.value = ''; // Limpiar el campo de búsqueda
101        searchInput.blur(); // Quitar el foco del campo
102        searchInput.placeholder =
103            tabId === 'personajes' ? 'Buscar personajes.(min. 3 letras.)' : 'Buscar grupos.(min. 3 letras.)';
104    }
105
106    // Limpia el mensaje de búsqueda
107    const searchMessageContainer = document.getElementById('search-message');
108    if (searchMessageContainer) {
109        searchMessageContainer.textContent = '';
110    }
111
112    // Cambia la visibilidad de los tabs
113    tabs.forEach((tab) => {
114        tab.style.display = tab.id === tabId ? 'block' : 'none';
115    });
116
117    buttons.forEach((button) => {
118        button.classList.remove('active');
119    });
120
121    const activeButton = document.querySelector(`button[onclick="mostrarTab('${tabId}')"]`);
122    if (activeButton) {
123        activeButton.classList.add('active');
124    }
125
126    // Ajustar el botón "Cargar Más" después de mostrar el tab
127    setTimeout(ajustarLineasCargarMas, 300); // Retardo para asegurar que el contenedor esté visible
128}
129
130// Observa cambios en el botón con clase "active" y restaura si es necesario
131// Observa cambios en el botón con clase "active" y asegura que la barra de búsqueda esté visible
132function observarActive(tabId) {
133    const button = document.querySelector(`button[onclick="mostrarTab('${tabId}')"]`);
134    if (!button) return;
135
136    const searchBarContainer = document.querySelector('.search-bar-container');
137    const observer = new MutationObserver((mutations) => {
138        mutations.forEach((mutation) => {
139            // Verificar si isFichaSidebarActive está activo
140            if (isFichaSidebarActive) {
141                return; // Si isFichaSidebarActive es true, no ejecuta el resto del bloque
142            }
143
144            // Detectar cambios en los atributos del botón
145            if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
146                const isButtonActive = button.classList.contains('active');
147
148                if (!isButtonActive && currentTabId === tabId) {
149                    // Restaurar la clase "active" si se pierde
150                    //console.warn(`Restaurando clase "active" a ${button.innerText}`);
151                    button.classList.add('active');
152                }
153
154                // Asegurar que la barra de búsqueda esté visible si el botón tiene la clase "active"
155                if (isButtonActive) {
156                    if (searchBarContainer && searchBarContainer.style.display !== 'flex') {
157                        //console.log('Asegurando que la barra de búsqueda esté visible.');
158                        searchBarContainer.style.display = 'flex';
159                    }
160                }
161            }
162        });
163    });
164
165    // Configurar el observador para detectar cambios en los atributos del botón
166    observer.observe(button, { attributes: true });
167}
168
169
170
171// Función para mostrar el preloader y ocultar el search-bar-container
172function mostrarPreloader() {
173    const preloader = document.getElementById('preloader');
174    const searchBarContainer = document.querySelector('.search-bar-container');
175
176    if (preloader) {
177        //console.log("Mostrando preloader");
178        preloader.classList.remove('hidden-opacity');
179        preloader.style.display = 'flex';
180    }
181
182    if (!isFichaSidebarActive && isSearchBarVisible) {
183        //searchBarContainer.classList.add('fading-out');
184        setTimeout(() => {
185            searchBarContainer.style.display = 'none';
186            //searchBarContainer.classList.remove('fading-out');
187            isSearchBarVisible = false; // Actualiza el estado
188        }, 300);
189    }
190}
191
192// Función para ocultar el preloader y mostrar el search-bar-container (si es apropiado)
193function ocultarPreloader() {
194    const preloader = document.getElementById('preloader');
195    const searchBarContainer = document.querySelector('.search-bar-container');
196
197    if (!preloader) return;
198
199    //console.log("Ocultando preloader");
200
201    // Ocultar el preloader con animación
202    preloader.classList.add('hidden-opacity');
203    setTimeout(() => {
204        preloader.style.display = 'none'; // Oculta completamente el preloader tras la animación
205
206        // Limpia hidden-opacity de elementos secundarios
207        document.querySelectorAll('.hidden-opacity').forEach(element => {
208            if (!element.classList.contains('search-bar-container') || !isFichaSidebarActive) {
209                element.classList.remove('hidden-opacity');
210                element.classList.add('fading-in');
211                //console.log(`Removiendo hidden-opacity de:`, element);
212            }
213        });
214
215        // Verifica y maneja la visibilidad del search-bar-container
216        if (searchBarContainer) {
217            if (isFichaSidebarActive) {
218                // Oculta el search-bar-container suavemente si está activo el sidebar
219                if (!searchBarContainer.classList.contains('fading-out')) {
220                    searchBarContainer.classList.remove('fading-in');
221                    searchBarContainer.classList.add('fading-out');
222                    setTimeout(() => {
223                        //searchBarContainer.style.display = 'none';
224                        searchBarContainer.classList.remove('fading-out');
225                        isSearchBarVisible = false; // Actualiza el estado
226                        //console.log("Manteniendo search-bar-container oculto debido a isFichaSidebarActive=true");
227                    }, 300);
228                }
229            } else {
230                // Muestra el search-bar-container suavemente si no está activo el sidebar
231                if (!isSearchBarVisible && !searchBarContainer.classList.contains('fading-in')) {
232                    searchBarContainer.style.display = 'flex';
233                    searchBarContainer.style.opacity = '0';
234                    searchBarContainer.classList.remove('fading-out');
235                    searchBarContainer.classList.add('fading-in');
236                    setTimeout(() => {
237                        searchBarContainer.style.opacity = '1';
238                        searchBarContainer.classList.remove('fading-in');
239                        isSearchBarVisible = true; // Actualiza el estado
240                        //console.log("Mostrando search-bar-container con fade-in ya que isFichaSidebarActive=false");
241                    }, 300);
242                }
243            }
244        }
245
246        // Actualiza el estado de los botones del tabs-container
247        actualizarEstadoBotonesTabs();
248    }, 500); // Tiempo de espera tras la animación del preloader
249}
250// Función para cargar el listado de un tipo específico
251function cargarListado(postType) {
252    mostrarPreloader(); // Mostrar el preloader al iniciar la carga del listado
253
254    fetch(pg_ajax.ajax_url, {
255        method: 'POST',
256        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
257        body: new URLSearchParams({
258            action: 'cargar_mas_items',
259            tipo: postType,
260            offset: 0 // Reinicia desde el principio
261        })
262    })
263    .then(response => response.json())
264    .then(data => {
265        ocultarPreloader(); // Ocultar el preloader al cargar
266        var container = document.querySelector(`.${postType}-container`);
267
268         if (!container) {
269            //console.error("Contenedor no encontrado para el tipo:", postType);
270            return;
271        }
272
273        container.classList.remove('single-view'); // Quitar clase de vista individual
274        container.innerHTML = ''; // Limpiar el contenedor antes de insertar nuevos elementos
275
276        // Crear un elemento temporal para analizar el HTML recibido
277        var tempDiv = document.createElement('div');
278        tempDiv.innerHTML = data.html; // HTML de la respuesta
279
280        // Filtrar y añadir solo los elementos `.personaje-card` o `.grupo-card` al contenedor
281        var newCards = tempDiv.querySelectorAll('.personaje-card, .grupo-card');
282        newCards.forEach(function(item) {
283            item.style.opacity = 0; // Iniciar en 0 para el fadeIn
284            container.appendChild(item);
285            fadeIn(item);
286        });
287
288         //console.log(`${newCards.length} tarjetas cargadas.`);
289             
290        // Reactivar el botón "Cargar Más" con efecto `fadeIn` y actualizar `data-total` si no está definido
291        var cargarMasBtn = document.querySelector(`.cargar-mas-btn[data-type="${postType}"]`);
292        if (cargarMasBtn) {
293            cargarMasBtn.style.display = 'flex'; // Mostrarlo antes de aplicar fadeIn
294            fadeIn(cargarMasBtn); // Aplicar el efecto fadeIn al botón en la carga inicial
295            cargarMasBtn.setAttribute('data-offset', newCards.length);
296
297            // Configurar `data-total` solo si aún no está configurado (para la carga inicial)
298            if (!cargarMasBtn.getAttribute('data-total')) {
299                cargarMasBtn.setAttribute('data-total', data.total_posts);
300            }
301        }
302    })
303     .catch(error => {
304        ocultarPreloader(); // Ocultar preloader en caso de error
305        //console.error('Error al cargar el listado:', error);
306    });
307}
308// Función para hacer fade in
309function fadeIn(element, callback) {
310    element.classList.remove('hidden-opacity'); // Remover la clase antes de iniciar el fade
311    element.style.opacity = 0;
312    element.style.display = 'flex';
313    setTimeout(() => {
314        element.style.opacity = 1;
315        if (callback) callback();
316    }, 300); // Duración de la transición
317}
318// Función para hacer fade out
319function fadeOut(element, callback) {
320    element.style.opacity = 1;
321    const tick = function() {
322        element.style.opacity = +element.style.opacity - 0.05;
323        if (+element.style.opacity > 0) {
324            requestAnimationFrame(tick);
325        } else {
326            element.style.display = 'none';
327            if (typeof callback === "function") callback();
328        }
329    };
330    tick();
331}
332
333
334
335// Manejador de clic para el botón "Cargar Más"
336document.addEventListener('click', function(e) {
337     if (e.target.classList.contains('cargar-mas-btn')) {
338        mostrarPreloader(); // Mostrar preloader al hacer clic en "Cargar Más"
339
340        const button = e.target;
341        const postType = button.getAttribute('data-type'); // Detectar si es personajes o grupos
342        let offset = parseInt(button.getAttribute('data-offset'), 10);
343        const totalPosts = parseInt(button.getAttribute('data-total'), 10) || 0;
344
345        //console.log(`Botón "Cargar Más" clickeado para tipo: ${postType}`);
346        //console.log(`Offset inicial: ${offset}, Total de posts: ${totalPosts}`);
347
348        // Cambiar el texto del botón a "Cargando..." y deshabilitar el botón
349        const originalText = button.textContent;
350        button.textContent = 'CARGANDO...';
351        button.disabled = true;
352
353        // Realizar la solicitud Ajax
354        fetch(pg_ajax.ajax_url, {
355            method: 'POST',
356            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
357            body: new URLSearchParams({
358                action: 'cargar_mas_items',
359                tipo: postType,
360                offset: offset,
361            }),
362        })
363            .then(response => response.json())
364            .then(data => {
365                ocultarPreloader(); // Ocultar preloader después de la carga
366
367                const container = document.querySelector(`#${postType} .${postType}-container`);
368                if (!container) {
369                    //console.error(`Contenedor no encontrado para el tipo: ${postType}`);
370                    return;
371                }
372
373                // Crear un elemento temporal para analizar el HTML recibido
374                const tempDiv = document.createElement('div');
375                tempDiv.innerHTML = data.html;
376
377                // Filtrar y añadir solo los elementos `.personaje-card` o `.grupo-card` al contenedor
378                const newCards = tempDiv.querySelectorAll('.personaje-card, .grupo-card');
379                //console.log(`Nuevas tarjetas encontradas: ${newCards.length}`);
380
381                newCards.forEach(function (item) {
382                    item.style.opacity = 0; // Inicialmente oculto
383                    container.appendChild(item); // Agregar al contenedor
384                    fadeIn(item); // Aplicar efecto de fadeIn
385                });
386
387                // Actualizar el offset en el botón
388                offset += newCards.length;
389                button.setAttribute('data-offset', offset);
390
391                // Restaurar el texto y habilitar el botón
392                button.textContent = originalText;
393                button.disabled = false;
394
395                // Comprobar si el offset alcanzó o superó el total de posts para ocultar el botón
396                if (offset >= totalPosts || newCards.length === 0) {
397                    //console.log(`Offset (${offset}) es mayor o igual al total de posts (${totalPosts}), ocultando botón.`);
398                    button.style.display = 'none';
399                } else {
400                    //console.log('Aún hay más contenido para cargar, manteniendo el botón visible.');
401                }
402            })
403            .catch(error => {
404                //console.error('Error en la solicitud Ajax:', error);
405                ocultarPreloader(); // Ocultar el preloader en caso de error
406                button.textContent = originalText; // Restaurar el texto original en caso de error
407                button.disabled = false; // Rehabilitar el botón
408            });
409    }
410
411    // Manejador de clic para el botón "Ver Ficha" o enlace de organización en el listado
412    if (e.target.classList.contains('ver-ficha-btn') || e.target.classList.contains('ficha-enlace')) {
413        e.preventDefault(); // Evitar recarga en caso de ser enlace
414        // Obtener el slug desde el atributo del botón
415        const slug = e.target.getAttribute('data-slug');
416        if (!slug) {
417            //console.error('Slug no encontrado en el botón.');
418            return;
419        }
420        const container = e.target.closest('.personajes-container') || e.target.closest('.grupos-container');
421        if (!container) {
422            //console.error('No se encontró el contenedor.');
423            return;
424        }
425        mostrarPreloader(); // Mostrar el preloader antes de cargar la vista individual
426
427        // Ocultar el botón "Cargar Más" inmediatamente para la vista single
428        const postType = container.classList.contains('personajes-container') ? 'personajes' : 'grupos';
429        const cargarMasBtn = document.querySelector(`.cargar-mas-btn[data-type="${postType}"]`);
430        if (cargarMasBtn) {
431            //console.log(`Ocultando el botón 'Cargar Más' en la vista individual para el tipo: ${postType}`);
432            cargarMasBtn.style.display = 'none';
433        }
434
435        // Iniciar scroll hacia arriba hacia el elemento tabs-container
436        const tabsContainer = document.querySelector('.tabs-container');
437        if (tabsContainer) {
438            const tabsPosition = tabsContainer.getBoundingClientRect().top + window.scrollY; // Calcula su posición relativa
439            window.scrollTo({
440                top: tabsPosition - 20,
441                behavior: 'smooth'
442            });
443        }
444        // Iniciar la transición de salida del listado actual
445        fadeOut(container, function() {
446            // Ajuste para que el contenido sea visible
447            fetch(pg_ajax.ajax_url, {
448                method: 'POST',
449                headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
450                body: new URLSearchParams({
451                    action: 'cargar_vista_individual',
452                    slug: slug // Usar el slug
453                })
454            })
455            .then(response => response.text())
456            .then(data => {
457                ocultarPreloader(); // Oculta el preloader
458
459                // Inyecta el HTML de la ficha individual
460                container.innerHTML = data;
461                container.classList.add('single-view');
462                //console.log(`Ficha individual inyectada en el DOM para el slug: ${slug}`);
463
464                // Actualizar la URL eliminando el parámetro `tab`
465                const newUrl = new URL(window.location.href);
466                newUrl.searchParams.set('ficha', slug);
467                newUrl.searchParams.delete('tab'); // Eliminar `tab` siempre
468                window.history.pushState({}, '', newUrl);
469                //console.log(`Ficha individual cargada y URL actualizada: ${newUrl}`);
470
471                 // Selección y visualización de cada card específico dentro de ficha-sidebar
472                const fichaSidebar = container.querySelector('.ficha-sidebar');
473                const personajesButton = document.querySelector('button[onclick="mostrarTab(\'personajes\')"]');
474                const gruposButton = document.querySelector('button[onclick="mostrarTab(\'grupos\')"]');
475                
476                if (fichaSidebar) {
477                     // Remueve hidden-opacity y aplica fadeIn para asegurar visibilidad
478                    const fichaSidebar = container.querySelector('.ficha-sidebar .personaje-card, .ficha-sidebar .grupo-card');
479                    if (fichaSidebar) {
480                        fichaSidebar.classList.remove('hidden-opacity');
481                        fadeIn(fichaSidebar); // Aplica el fadeIn inmediatamente
482                    }
483
484                    // Ocultar el search-bar-container usando clase 'hidden-opacity'
485                    const searchBarContainer = document.querySelector('.search-bar-container');
486                    if (searchBarContainer) {
487                        searchBarContainer.classList.add('hidden-opacity'); // Oculta con clase
488                        //console.log("search-bar-container ocultado al mostrar ficha-sidebar.");
489                    }
490                    // Establecemos que ficha-sidebar está activo
491                    isFichaSidebarActive = true;
492                    actualizarEstadoBotonesTabs(); // Actualiza el estado de los botones después de activar ficha-sidebar
493                    //console.log("isFichaSidebarActive set to true");
494                } else {
495                    // Mostrar el search-bar-container solo si ficha-sidebar no está activo
496                    const searchBarContainer = document.querySelector('.search-bar-container');
497                     if (searchBarContainer && isFichaSidebarActive) {
498                        searchBarContainer.classList.remove('hidden-opacity'); // Muestra quitando la clase
499                        //console.log("search-bar-container mostrado nuevamente ya que ficha-sidebar no está presente.");
500                        isFichaSidebarActive = false; // Restablece el estado
501                        actualizarEstadoBotonesTabs(); // Actualiza el estado de los botones después de desactivar ficha-sidebar
502                        //console.log("isFichaSidebarActive set to false");
503                    }
504                }
505
506                fadeIn(container); // Aplica fadeIn al contenedor principal también
507            })
508            .catch(error => {
509                //console.error('Error al cargar la ficha:', error);
510                ocultarPreloader(); // Oculta el preloader en caso de error
511            });
512        });
513    }
514});
515
516// Aplicar fadeIn a los primeros elementos al cargar la página
517// Mostrar el preloader al cargar la página
518document.addEventListener('DOMContentLoaded', function () {
519    mostrarPreloader();
520
521    // Detectar si la URL incluye el parámetro "ficha"
522    const urlParams = new URLSearchParams(window.location.search);
523    const fichaSlug = urlParams.get('ficha');
524
525    if (fichaSlug) {
526        //console.log(`Cargando ficha directamente desde la URL: ${fichaSlug}`);
527        cargarFichaPorSlug(fichaSlug, true);
528        return; // Evita que mostrarTab se ejecute
529    }
530
531
532    // Asegurar que los tabs estén siempre visibles
533    const tabsContainer = document.querySelector('.tabs-container');
534    if (tabsContainer) {
535        tabsContainer.classList.add('hidden-opacity'); // Asegurar que empieza oculto
536        //tabsContainer.style.display = 'flex'; // Garantizar que los tabs sean visibles siempre
537        //fadeIn(tabsContainer); // Aplicar fadeIn inicial
538        //tabsContainer.classList.add('fading-in');
539    }
540
541    if (fichaSlug) {
542        //console.log(`Cargando contenido dinámico para la ficha: ${fichaSlug}`);
543  cargarFichaPorSlug(fichaSlug, true);
544        return; // Evita que mostrarTab se ejecute
545        // Mostrar preloader y proceder con la carga del contenido
546        mostrarPreloader();
547
548        // Ocultar el search-bar al cargar ficha directamente (mantener tabs visibles)
549        const searchBarContainer = document.querySelector('.search-bar-container');
550        if (searchBarContainer) {
551            searchBarContainer.style.display = 'none'; // Ocultar el search-bar
552        }
553
554        // Realizar la solicitud para cargar el contenido dinámico
555        fetch(pg_ajax.ajax_url, {
556            method: 'POST',
557            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
558            body: new URLSearchParams({
559                action: 'cargar_vista_individual',
560                slug: fichaSlug,
561            }),
562        })
563        .then(response => response.text())
564        .then(data => {
565            ocultarPreloader(); // Ocultar preloader al finalizar
566
567            const container = document.querySelector('.personajes-container, .grupos-container');
568            if (container) {
569                container.innerHTML = data;
570                container.classList.add('single-view');
571                fadeIn(container); // Aplicar fadeIn al contenido principal
572
573                // Mostrar el card del sidebar con fadeIn
574                const fichaSidebar = container.querySelector('.ficha-sidebar .personaje-card, .ficha-sidebar .grupo-card');
575                if (fichaSidebar) {
576                    fichaSidebar.classList.remove('hidden-opacity');
577                    fadeIn(fichaSidebar); // Asegurar que el card del sidebar aparezca
578                    //console.log(`Ficha individual cargada correctamente para el slug: ${fichaSlug}`);
579                } else {
580                    //console.warn('No se encontró el card en el sidebar.');
581                }
582            } else {
583                //console.error('Contenedor principal no encontrado para la vista dinámica.');
584            }
585        })
586        .catch(error => {
587            ocultarPreloader(); // Ocultar el preloader en caso de error
588            //console.error('Error al cargar contenido dinámico:', error);
589        });
590
591        // Evitar ejecutar el resto de lógica estándar en la carga inicial
592        return;
593    }
594
595    // Lógica estándar para la carga de tabs y listados
596    window.addEventListener('load', function () {
597        ocultarPreloader(); // Oculta el preloader y configura `isPageInitialized`
598        isPageInitialized = true;
599
600        // Llamada para ajustar el botón "Cargar Más"
601        observarActive(currentTabId); // Observa el tab actual
602        ajustarLineasCargarMas();
603        //console.log("Página completamente cargada. Scroll desbloqueado.");
604
605        // Retrasamos la carga del searchBarContainer hasta que el preloader esté completamente oculto
606        setTimeout(() => {
607            const searchBarContainer = document.querySelector('.search-bar-container');
608            fadeIn(searchBarContainer);
609        }, 200); // Ajusta el retardo para que ocurra después de ocultar el preloader
610    });
611
612    // Aplicar fadeIn para tabs y listados iniciales
613    if (tabsContainer) {
614        //console.log("Aplicando fadeIn a .tabs-container.");
615        fadeIn(tabsContainer);
616        //tabsContainer.classList.add('fading-in');
617    }
618
619    const initialCards = document.querySelectorAll('.personajes-container .personaje-card, .grupos-container .grupo-card');
620    initialCards.forEach(function (card) {
621        fadeIn(card);
622    });
623
624    const cargarMasBtns = document.querySelectorAll('.cargar-mas-btn');
625    cargarMasBtns.forEach(function (btn) {
626        fadeIn(btn);
627    });
628});
629
630// Función para cargar ficha por slug
631function cargarFichaPorSlug(slug, desdeUrl = false) {
632    isFichaSidebarActive = true; // Asegura que el estado esté activo para evitar conflictos
633
634    mostrarPreloader();
635
636    fetch(pg_ajax.ajax_url, {
637        method: 'POST',
638        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
639        body: new URLSearchParams({
640            action: 'cargar_vista_individual',
641            slug: slug
642        })
643    })
644    .then(response => response.text())
645    .then(data => {
646        ocultarPreloader();
647
648        const container = document.querySelector('.personajes-container') || document.querySelector('.grupos-container');
649        if (!container) {
650            //console.error('No se encontró el contenedor para mostrar la ficha.');
651            return;
652        }
653
654        container.innerHTML = data;
655        container.classList.add('single-view');
656        //console.log(`Ficha cargada correctamente para el slug: ${slug}`);
657
658        const buttons = document.querySelectorAll('.tabs-container .tab-button');
659        buttons.forEach(button => button.classList.remove('active'));
660
661        const searchBarContainer = document.querySelector('.search-bar-container');
662        if (searchBarContainer) searchBarContainer.style.display = 'none';
663
664        const newUrl = new URL(window.location.href);
665        newUrl.searchParams.set('ficha', slug);
666        newUrl.searchParams.delete('tab');
667        window.history.replaceState({}, '', newUrl);
668
669        actualizarEstadoBotonesTabs();
670    })
671    .catch(error => {
672        //console.error('Error al cargar la ficha:', error);
673        ocultarPreloader();
674    });
675}
676
677
678
679// Función para ajustar las líneas del botón "Cargar Más" cuando esté visible
680function ajustarLineasCargarMas() {
681    const tabsContainer = document.querySelector('.tabs-container');
682    const cargarMasBtn = document.querySelector(`.cargar-mas-btn[data-type="${currentTabId}"]`);
683
684    if (!tabsContainer || !cargarMasBtn) {
685        //console.warn("No se encontró .tabs-container o el botón 'Cargar Más' para el tab activo");
686        return;
687    }
688
689    const ajustarSiBotonVisible = () => {
690        const tabsContainerWidth = tabsContainer.offsetWidth;
691        const buttonWidth = cargarMasBtn.offsetWidth;
692
693        if (buttonWidth > 0) {
694            const lineWidth = (tabsContainerWidth - buttonWidth - 20) / 2;
695            //console.log("Ajustando lineWidth:", lineWidth);
696
697            cargarMasBtn.style.setProperty('--line-width', `${lineWidth}px`);
698            cargarMasBtn.classList.add('lines-visible'); // Hacer visibles las pseudo-elementos
699        } else {
700            //console.log("Esperando a que el botón esté visible...");
701            setTimeout(ajustarSiBotonVisible, 100);
702        }
703    };
704
705    ajustarSiBotonVisible();
706}
707
708// Llamada de ajuste de líneas al redimensionar ventana
709window.addEventListener('resize', ajustarLineasCargarMas);
710
711
712// Filtrado de búsqueda
713// Evento al ingresar texto en el campo de búsqueda
714const searchInput = document.getElementById('search-input');
715searchInput.addEventListener('input', function() {
716    const searchTerm = this.value.trim();
717    const searchMessageContainer = document.getElementById('search-message'); // Contenedor del mensaje de búsqueda
718    const cargarMasBtn = document.querySelector(`.cargar-mas-btn[data-type="${currentTabId}"]`); // Botón "Cargar Más" del tab actual
719
720    //console.log("Término de búsqueda ingresado:", searchTerm);
721
722    // Si el campo de búsqueda está vacío, recargar el listado completo y limpiar el mensaje
723    if (searchTerm === '') {
724        //console.log("Campo de búsqueda vacío, recargando listado completo para:", currentTabId);
725
726        // Limpiar mensaje y asegurar que permanezca vacío
727        if (searchMessageContainer) {
728            searchMessageContainer.textContent = ''; // Limpiar el mensaje de coincidencias
729        }
730        
731        cargarListado(currentTabId); // Recarga el listado correspondiente a la pestaña actual
732        
733        // Restaurar el botón "Cargar Más"
734        if (cargarMasBtn) {
735            cargarMasBtn.style.display = 'flex';
736        }
737        
738        return; // Detener aquí para evitar ejecutar la búsqueda vacía
739    }
740
741    // Ejecutar búsqueda cuando el término tiene 3 o más caracteres
742    if (searchTerm.length >= 3) {
743        //console.log("Ejecutando búsqueda para:", searchTerm);
744
745        // Ocultar el botón "Cargar Más" al iniciar la búsqueda
746        if (cargarMasBtn) {
747            cargarMasBtn.style.display = 'none';
748        }
749
750        // Agregar la clase "loading" a searchMessageContainer para mostrar el spinner
751        searchMessageContainer.textContent = 'Buscando...'; // Texto temporal para claridad
752        searchMessageContainer.classList.add('loading');
753
754        fetch(pg_ajax.ajax_url, {
755            method: 'POST',
756            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
757            body: new URLSearchParams({
758                action: 'filtrar_items',
759                term: searchTerm,
760                tipo: currentTabId // Envía el tab actual ('personajes' o 'grupos')
761            })
762        })
763        .then(response => response.json())
764        .then(data => {
765             // Ocultar el spinner y limpiar el mensaje temporal
766            searchMessageContainer.classList.remove('loading');
767            searchMessageContainer.textContent = ''; // Limpiar antes de agregar el nuevo mensaje
768
769            var container = document.querySelector(`.${currentTabId}-container`);
770            container.innerHTML = ''; // Limpiar el contenedor antes de insertar nuevos elementos
771
772            // Verificar el número de coincidencias y actualizar el mensaje solo si el campo aún tiene texto
773            if (searchInput.value.trim() !== '') { // Validación adicional
774                const numberOfResults = (data.html.match(/class="personaje-card|class="grupo-card/g) || []).length;
775                if (numberOfResults > 1) {
776                    searchMessageContainer.textContent = `Se encontraron ${numberOfResults} coincidencias.`;
777                } else if (numberOfResults === 1) {
778                    searchMessageContainer.textContent = 'Se encontró 1 coincidencia.';
779                } else {
780                    searchMessageContainer.textContent = 'No se encontraron coincidencias.';
781                }
782            } else {
783                // Si el campo está vacío, asegurar que searchMessageContainer esté en blanco
784                searchMessageContainer.textContent = '';
785            }
786
787            if (data.html) {
788                //console.log("Resultados de búsqueda encontrados.");
789
790                // Crear un contenedor temporal para procesar el HTML
791                const tempDiv = document.createElement('div');
792                tempDiv.innerHTML = data.html;
793
794                // Seleccionar los nuevos elementos y aplicar fadeIn
795                const newCards = tempDiv.querySelectorAll('.personaje-card, .grupo-card');
796                newCards.forEach(function(card) {
797                    card.style.opacity = 0; // Iniciar en 0 para el fadeIn
798                    container.appendChild(card); // Agregar al contenedor principal
799                    fadeIn(card); // Aplicar el efecto fadeIn
800                });
801            } else {
802                //console.log("No se encontraron coincidencias.");
803                container.innerHTML = '<p class="no-results-message">No se encontraron coincidencias.</p>';
804            }
805        })
806        .catch(error => {
807            searchMessageContainer.classList.remove('loading');
808            searchMessageContainer.textContent = ''; // Limpia el mensaje en caso de error
809            ocultarPreloader(); // Ocultar preloader en caso de error
810            //console.error("Error en la solicitud AJAX de búsqueda:", error);
811        });
812    }
813});
814
815// Limpiar el campo de búsqueda y quitar el foco al presionar Escape
816searchInput.addEventListener('keydown', function(event) {
817    if (event.key === 'Escape') {
818        this.value = ''; // Borrar el contenido del campo
819        this.blur(); // Quitar el foco del campo
820
821        const searchMessageContainer = document.getElementById('search-message');
822        if (searchMessageContainer) {
823            searchMessageContainer.textContent = ''; // Limpiar mensaje de coincidencias
824        }
825
826        cargarListado(currentTabId); // Recargar el listado completo de la pestaña actual
827
828        // Restaurar el botón "Cargar Más"
829        const cargarMasBtn = document.querySelector(`.cargar-mas-btn[data-type="${currentTabId}"]`);
830        if (cargarMasBtn) {
831            cargarMasBtn.style.display = 'flex';
832        }
833    }
834});

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.