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.