// storymals-secure.js - VERSIÓN 1.6.0 - SOPORTE COMPLETO PARA iOS (function(){ window.storymals = window.storymals || {}; storymals.setToken = function(t){ try { localStorage.setItem('storymals_token', t); } catch(e) { console.error(e); } }; storymals.getToken = function(){ try { return localStorage.getItem('storymals_token'); } catch(e){ return null; } }; storymals.clearToken = function(){ try { localStorage.removeItem('storymals_token'); } catch(e){} }; let isInstalling = false; let installTimeout = null; let countdownInterval = null; // ========== DETECCIÓN DE DISPOSITIVO ========== const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; const isStandalone = window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone || document.referrer.includes('android-app://'); const hasKey = window.location.search.includes('key=') || window.location.search.includes('sm_token='); // ========== SERVICE WORKER ========== if ('serviceWorker' in navigator) { const isCuentosPage = window.location.href.includes('/cuento/') || window.location.href.includes('/biblioteca/') || window.location.href.includes('/categoria/') || document.querySelector('.cuento-wrapper') || document.querySelector('.cuentos-miniaturas'); const isPwaPage = window.location.search.includes('storymals_pwa=1') || document.body.classList.contains('storymals-pwa-page'); if (isPwaPage && !isCuentosPage) { console.log('🟢 Registrando Service Worker'); window.addEventListener('load', () => { navigator.serviceWorker.register('/wp-content/plugins/storymals-secure/service-worker.js') .then(reg => console.log('✅ ServiceWorker registrado')) .catch(err => console.log('❌ ServiceWorker error:', err)); }); } else { navigator.serviceWorker.getRegistrations().then(registrations => { for (let reg of registrations) { if (reg.active && reg.active.scriptURL.includes('storymals-secure')) { console.log('🔴 Desregistrando Service Worker'); reg.unregister(); } } }); } } // ========== FUNCIONES PARA iOS ========== function storymals_initIOS() { if (!isIOS) return; // Solo para iOS console.log('🍎 Inicializando funciones para iOS'); // Si está en modo standalone, no hacer nada if (isStandalone) { console.log('✅ iOS en modo standalone (PWA instalada)'); document.cookie = 'storymals_standalone=1; path=/; max-age=3600'; hideAllPwaBanners(); return; } // Si tiene key y no está en standalone, mostrar guía de instalación if (hasKey) { console.log('📱 iOS detectado con key - mostrando guía de instalación'); document.cookie = 'storymals_standalone=0; path=/; max-age=300'; localStorage.setItem('storymals_ios_pending_url', window.location.href); localStorage.setItem('storymals_ios_timestamp', Date.now()); // Intentar abrir en PWA si ya está instalada (por si acaso) setTimeout(function() { var pwaUrl = window.location.href.replace(/^https?:\/\//, 'storymals://'); var iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = pwaUrl; document.body.appendChild(iframe); }, 300); // Mostrar overlay después de 1.5 segundos setTimeout(function() { if (!document.getElementById('storymals-ios-overlay')) { showIOSInstallOverlay(); } }, 1500); } } function showIOSInstallOverlay() { var overlay = document.createElement('div'); overlay.id = 'storymals-ios-overlay'; overlay.style.cssText = 'position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.85); z-index:9999999; display:flex; align-items:center; justify-content:center; flex-direction:column; padding:20px;'; overlay.innerHTML = `
📚

¡Instalar StoryMals!

Para una mejor experiencia, instalá StoryMals en tu iPhone/iPad.

1. Tocá el botón Compartir

2. Seleccioná "Añadir a pantalla de inicio"

3. Tocá "Añadir"

¿Ya tenés la app instalada? Abrir ahora

`; document.body.appendChild(overlay); } function showIOSRedirectOverlay() { var overlay = document.createElement('div'); overlay.id = 'storymals-ios-redirect-overlay'; overlay.style.cssText = 'position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.85); z-index:9999999; display:flex; align-items:center; justify-content:center; flex-direction:column; padding:20px;'; overlay.innerHTML = `
📚

¡Abrir en StoryMals!

Ya tenés la app instalada. Abrila para continuar tu aventura.

`; document.body.appendChild(overlay); // Intentar abrir automáticamente después de 1 segundo setTimeout(function() { window.location.href = window.location.href.replace(/^https?:\/\//, 'storymals://'); }, 1000); } // ========== BANNERS PWA ========== let deferredPrompt; window.addEventListener('beforeinstallprompt', (e) => { console.log('✅ beforeinstallprompt event fired'); deferredPrompt = e; if (!isInstalling && !isIOS) { showAllPwaBanners(); } }); window.addEventListener('load', () => { if (isStandalone) { hideAllPwaBanners(); } else if (isIOS && !isInstalling) { // En iOS solo mostrar banners si no está instalado if (!isStandalone) { showAllPwaBanners(); } } storymals_initModernBanners(); storymals_initIOS(); // Si es iOS y tiene key, mostrar redirección después de 3 segundos if (isIOS && hasKey && !isStandalone) { setTimeout(function() { if (!document.getElementById('storymals-ios-redirect-overlay') && !document.getElementById('storymals-ios-overlay')) { // Verificar si ya está instalado (por si acaso) var checkPWA = window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone; if (checkPWA) { showIOSRedirectOverlay(); } } }, 3000); } }); function storymals_isPWAInstalled() { return window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone || document.referrer.includes('android-app://'); } function storymals_hideBannersIfInstalled() { if (storymals_isPWAInstalled()) { hideAllPwaBanners(); hideModernBanners(); const bannerModern = document.getElementById('storymals-pwa-banner-modern'); const notification = document.getElementById('storymals-pwa-notification'); const legacyBanner = document.getElementById('storymals-pwa-banner'); if (bannerModern) bannerModern.style.display = 'none'; if (notification) notification.style.display = 'none'; if (legacyBanner) legacyBanner.style.display = 'none'; } } function showAllPwaBanners() { if (storymals_isPWAInstalled() || isInstalling) { hideAllPwaBanners(); return; } // En iOS solo mostrar si no está instalado if (isIOS && isStandalone) { hideAllPwaBanners(); return; } showPwaBanner(); showModernBanners(); } function hideAllPwaBanners() { hidePwaBanner(); hideModernBanners(); const bannerModern = document.getElementById('storymals-pwa-banner-modern'); const notification = document.getElementById('storymals-pwa-notification'); if (bannerModern) bannerModern.style.display = 'none'; if (notification) notification.style.display = 'none'; } function showPwaBanner() { if (storymals_isPWAInstalled() || isInstalling) { hidePwaBanner(); return; } if (isIOS && isStandalone) { hidePwaBanner(); return; } var b = document.getElementById('storymals-pwa-banner'); if(b) { applyBannerStyles(); b.style.display = 'block'; const autoHide = getOption('storymals_pwa_auto_hide', 0); if (autoHide > 0) { setTimeout(() => hidePwaBanner(), autoHide * 1000); } } } function hidePwaBanner() { var b = document.getElementById('storymals-pwa-banner'); if(b) b.style.display = 'none'; } function showModernBanners() { if (storymals_isPWAInstalled() || isInstalling) { hideModernBanners(); return; } if (isIOS && isStandalone) { hideModernBanners(); return; } const banner = document.getElementById('storymals-pwa-banner-modern'); const notification = document.getElementById('storymals-pwa-notification'); if (banner && !localStorage.getItem('storymals_banner_dismissed')) { banner.style.display = 'flex'; } if (notification && !localStorage.getItem('storymals_notification_dismissed')) { notification.style.display = 'flex'; } } function hideModernBanners() { const banner = document.getElementById('storymals-pwa-banner-modern'); const notification = document.getElementById('storymals-pwa-notification'); if (banner) banner.style.display = 'none'; if (notification) notification.style.display = 'none'; } function applyBannerStyles() { var b = document.getElementById('storymals-pwa-banner'); if(!b) return; const bgColor = getOption('storymals_pwa_bg_color', '#ffffff'); const textColor = getOption('storymals_pwa_text_color', '#000000'); const btnColor = getOption('storymals_pwa_btn_color', '#7c3aed'); const btnTextColor = getOption('storymals_pwa_btn_text_color', '#ffffff'); const position = getOption('storymals_pwa_banner_position', 'bottom'); b.style.backgroundColor = bgColor; b.style.color = textColor; const button = b.querySelector('button'); if(button) { button.style.backgroundColor = btnColor; button.style.color = btnTextColor; button.id = 'storymals-install-btn'; } b.style.position = 'fixed'; b.style.left = '0'; b.style.right = '0'; b.style.zIndex = '2147483647'; if (position === 'top') { b.style.top = '0'; } else if (position === 'center') { b.style.top = '50%'; b.style.transform = 'translateY(-50%)'; } else { b.style.bottom = '0'; } } function getOption(option, defaultValue) { try { const element = document.getElementById('storymals-pwa-settings'); if (element) { const settings = JSON.parse(element.textContent); return settings[option] || defaultValue; } } catch(e) {} return defaultValue; } function storymals_dismissBanner() { const banner = document.getElementById('storymals-pwa-banner-modern'); if(banner) { banner.style.display = 'none'; localStorage.setItem('storymals_banner_dismissed', 'true'); localStorage.setItem('storymals_banner_dismissed_date', new Date().toISOString()); } } function storymals_dismissNotification() { const notification = document.getElementById('storymals-pwa-notification'); if(notification) { notification.style.display = 'none'; localStorage.setItem('storymals_notification_dismissed', 'true'); localStorage.setItem('storymals_notification_dismissed_date', new Date().toISOString()); } } function storymals_initModernBanners() { storymals_hideBannersIfInstalled(); if (storymals_isPWAInstalled() || isInstalling) return; if (isIOS && isStandalone) return; const banner = document.getElementById('storymals-pwa-banner-modern'); if(banner) { const autoShow = banner.getAttribute('data-auto-show') === 'true'; const delay = parseInt(banner.getAttribute('data-delay')) * 1000; const position = banner.getAttribute('data-position'); const dismissed = localStorage.getItem('storymals_banner_dismissed'); const dismissedDate = localStorage.getItem('storymals_banner_dismissed_date'); if(dismissed && dismissedDate) { const diffDays = Math.ceil((new Date() - new Date(dismissedDate)) / (1000 * 60 * 60 * 24)); if(diffDays > 30) { localStorage.removeItem('storymals_banner_dismissed'); localStorage.removeItem('storymals_banner_dismissed_date'); } } if(autoShow && !dismissed) { setTimeout(() => { if(deferredPrompt && !storymals_isPWAInstalled() && !isInstalling) { banner.style.display = 'flex'; applyBannerPosition(banner, position); } }, delay); } } const notification = document.getElementById('storymals-pwa-notification'); if(notification) { const autoShow = notification.getAttribute('data-auto-show') === 'true'; const delay = parseInt(notification.getAttribute('data-delay')) * 1000; const position = notification.getAttribute('data-position'); const dismissed = localStorage.getItem('storymals_notification_dismissed'); const dismissedDate = localStorage.getItem('storymals_notification_dismissed_date'); if(dismissed && dismissedDate) { const diffDays = Math.ceil((new Date() - new Date(dismissedDate)) / (1000 * 60 * 60 * 24)); if(diffDays > 30) { localStorage.removeItem('storymals_notification_dismissed'); localStorage.removeItem('storymals_notification_dismissed_date'); } } if(autoShow && !dismissed) { setTimeout(() => { if(deferredPrompt && !storymals_isPWAInstalled() && !isInstalling) { notification.style.display = 'flex'; applyBannerPosition(notification, position); } }, delay); } } } function applyBannerPosition(element, position) { element.style.position = 'fixed'; element.style.left = '0'; element.style.right = '0'; element.style.zIndex = '2147483647'; if(position === 'top') { element.style.top = '0'; element.style.bottom = 'auto'; } else { element.style.bottom = '0'; element.style.top = 'auto'; } } // ========== MODAL INSTALACIÓN EN PROGRESO ========== let installingMessageDiv = null; function showInstallingMessage() { if (document.getElementById('storymals-installing-message')) return; hideAllPwaBanners(); storymals_hideBannersIfInstalled(); const overlay = document.createElement('div'); overlay.id = 'storymals-installing-message'; overlay.style.cssText = 'position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.8); z-index:2147483647; margin:0; padding:0;'; const card = document.createElement('div'); card.style.cssText = 'position:absolute; bottom:50px; left:50%; transform:translateX(-50%); background:#2d3748; padding:30px; border-radius:16px; max-width:90%; width:350px; text-align:center; color:white; font-family:sans-serif; box-sizing:border-box;'; const spinner = document.createElement('div'); spinner.className = 'storymals-loading-spinner'; spinner.style.cssText = 'margin:0 auto 20px auto; width:48px; height:48px; border:5px solid #f3f3f3; border-top:5px solid #ce846b; border-radius:50%; animation: spin 1s linear infinite;'; card.appendChild(spinner); const title = document.createElement('h3'); title.textContent = 'Instalando StoryMals...'; title.style.cssText = 'color:#fff; margin-bottom:20px; font-size:1.2rem;'; card.appendChild(title); const spacer1 = document.createElement('div'); spacer1.style.cssText = 'height: 20px;'; card.appendChild(spacer1); const desc = document.createElement('h2'); desc.textContent = 'Esto puede tomar unos segundos. Por favor, espera.'; desc.style.cssText = 'color:#cbd5e1; font-size:14px; margin-bottom:15px;'; card.appendChild(desc); const spacerDesc = document.createElement('div'); spacerDesc.style.cssText = 'height: 20px;'; card.appendChild(spacerDesc); const countdownContainer = document.createElement('div'); countdownContainer.style.cssText = 'margin:15px 0; text-align:center;'; const countdownText = document.createElement('span'); countdownText.id = 'storymals-countdown-text'; countdownText.textContent = '30'; countdownText.style.cssText = 'color:#fff; font-size:18px; font-weight:bold; font-family:"Fredoka", sans-serif;'; const countdownLabel = document.createElement('span'); countdownLabel.textContent = ' segundos restantes'; countdownLabel.style.cssText = 'color:#fff; font-size:14px; font-family:"Fredoka", sans-serif;'; countdownContainer.appendChild(countdownText); countdownContainer.appendChild(countdownLabel); card.appendChild(countdownContainer); const progressContainer = document.createElement('div'); progressContainer.style.cssText = 'width:100%; background-color:#4a5568; border-radius:10px; overflow:hidden; margin-top:10px;'; const progressBar = document.createElement('div'); progressBar.id = 'storymals-progress-bar'; progressBar.style.cssText = 'width:0%; height:8px; background-color:#ce846b; transition:width 0.2s linear;'; progressContainer.appendChild(progressBar); card.appendChild(progressContainer); overlay.appendChild(card); document.body.appendChild(overlay); installingMessageDiv = overlay; let remainingSeconds = 30; if (countdownInterval) clearInterval(countdownInterval); countdownInterval = setInterval(() => { remainingSeconds--; if (remainingSeconds >= 0) { countdownText.textContent = remainingSeconds; const percent = ((30 - remainingSeconds) / 30) * 100; progressBar.style.width = percent + '%'; } if (remainingSeconds <= 0) { clearInterval(countdownInterval); countdownInterval = null; } }, 1000); if (!document.querySelector('#storymals-spin-style')) { const style = document.createElement('style'); style.id = 'storymals-spin-style'; style.textContent = '@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }'; document.head.appendChild(style); } } function hideInstallingMessage() { if (countdownInterval) { clearInterval(countdownInterval); countdownInterval = null; } if (installingMessageDiv) { installingMessageDiv.remove(); installingMessageDiv = null; } } // ========== MODAL INSTALACIÓN EXITOSA ========== function showInstallSuccessModal() { if (document.getElementById('storymals-install-modal')) return; hideAllPwaBanners(); storymals_hideBannersIfInstalled(); const overlay = document.createElement('div'); overlay.id = 'storymals-install-modal'; overlay.style.cssText = 'position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.9); z-index:2147483647; margin:0; padding:0;'; const card = document.createElement('div'); card.style.cssText = 'position:absolute; bottom:50px; left:50%; transform:translateX(-50%); background:#2d3748; padding:30px; border-radius:16px; max-width:90%; width:320px; text-align:center; color:white; font-family:sans-serif; box-sizing:border-box;'; const checkIcon = document.createElement('div'); checkIcon.textContent = '✓'; checkIcon.style.cssText = 'margin:0 auto 20px auto; width:48px; height:48px; background:#4ade80; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:32px; font-weight:bold; color:#fff;'; card.appendChild(checkIcon); const title = document.createElement('h2'); title.textContent = '¡Instalación exitosa!'; title.style.cssText = 'color:#fff; margin-bottom:10px; font-size:1.5rem;'; card.appendChild(title); const spacerTitle = document.createElement('div'); spacerTitle.style.cssText = 'height: 20px;'; card.appendChild(spacerTitle); const message = document.createElement('h3'); message.textContent = 'Acerca tu muñeco StoryMals al dispositivo o escanea el código QR'; message.style.cssText = 'color:#e2e8f0; font-size:14px; margin-bottom:20px;'; card.appendChild(message); const hint = document.createElement('h3'); hint.innerHTML = 'Para descubrir cuentos mágicos y vivir divertidas aventuras.'; hint.style.cssText = 'color:#cbd5e1; font-size:14px; margin-bottom:0;'; card.appendChild(hint); const spacer2 = document.createElement('div'); spacer2.style.cssText = 'height: 20px;'; card.appendChild(spacer2); overlay.appendChild(card); document.body.appendChild(overlay); } // ========== INSTALACIÓN PWA ========== window.storymals_installPWA = function(){ if (storymals_isPWAInstalled()) { return; } // iOS: mostrar instrucciones if (isIOS) { if (!document.getElementById('storymals-ios-overlay')) { showIOSInstallOverlay(); } return; } if(deferredPrompt && !isInstalling){ deferredPrompt.prompt(); deferredPrompt.userChoice.then((choiceResult) => { if (choiceResult.outcome === 'accepted') { console.log('✅ Usuario aceptó la instalación PWA'); isInstalling = true; hideAllPwaBanners(); storymals_hideBannersIfInstalled(); showInstallingMessage(); installTimeout = setTimeout(() => { console.log('⏰ Mostrando modal de éxito después de 30 segundos'); hideInstallingMessage(); showInstallSuccessModal(); isInstalling = false; installTimeout = null; }, 30000); try { fetch(storymals_ajax.ajax_url + '?action=storymals_pwa_installed', { method: 'POST', credentials: 'same-origin' }); } catch(e) {} } else { console.log('❌ Usuario canceló la instalación'); } deferredPrompt = null; }); } else { alert('Para instalar StoryMals:\n\n1. Abre el menú de tu navegador (tres puntos)\n2. Selecciona "Añadir a pantalla de inicio"\n3. Confirma la instalación'); } }; window.addEventListener('beforeunload', function() { if (installTimeout) clearTimeout(installTimeout); if (countdownInterval) clearInterval(countdownInterval); }); // ========== FUNCIÓN PARA ABRIR KEY (MODIFICADA PARA iOS) ========== storymals.openKey = async function(key){ try { const resp = await fetch(storymals_ajax.ajax_url + '?action=' + storymals_ajax.open_action, { method:'POST', credentials:'same-origin', headers:{'Content-Type':'application/json'}, body: JSON.stringify({key:key}) }); const data = await resp.json(); if(!data.success){ if(resp.status === 403 || data.data === 'login_required' || data.data === 'forbidden'){ alert('Por favor, iniciá sesión con tu cuenta (Google o Email).'); window.location.href = '/'; return; } alert('Error: ' + (data.data || 'Llavero inválido')); return; } if(data.data.token) { storymals.setToken(data.data.token); // Si es iOS y tiene redirección especial if (data.data.is_ios && data.data.ios_redirect) { window.location.href = data.data.ios_redirect; return; } if(data.data.url) { const url = new URL(data.data.url, window.location.origin); url.searchParams.set('sm_token', data.data.token); window.location.href = url.toString(); } } } catch(e) { console.error(e); alert('Error de conexión.'); } }; // ========== VALIDACIÓN DE TOKEN ========== storymals.validateTokenOnPage = async function(){ let token = storymals.getToken(); if(!token) return {ok:false,reason:'no_token'}; try { const resp = await fetch(storymals_ajax.ajax_url + '?action=' + storymals_ajax.validate_action, { method:'POST', credentials:'same-origin', headers:{'Content-Type':'application/json'}, body: JSON.stringify({token: token, url: window.location.pathname}) }); const data = await resp.json(); if(data.success && data.data && data.data.renewed && data.data.new_token) { storymals.setToken(data.data.new_token); } return data; } catch(e){ console.error(e); return {ok:false,reason:'network'}; } }; // ========== EXPORTAR FUNCIONES GLOBALES ========== window.storymals_dismissBanner = storymals_dismissBanner; window.storymals_dismissNotification = storymals_dismissNotification; // ========== INICIALIZACIÓN ========== window.addEventListener('DOMContentLoaded', function(){ var auto = document.body.getAttribute('data-storymals-validate'); if(auto === '1'){ storymals.validateTokenOnPage().then(function(res){ if(res && !res.success){ console.warn('Token inválido o expirado', res); } }); } storymals_hideBannersIfInstalled(); storymals_initModernBanners(); storymals_initIOS(); }); })();