// 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(); }); })(); Storymals – Storymals

Fastest And Most Lightweight WP Theme

Create Amazing Business Websites

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Suspendisse et justo. Praesent mattis commodo augue. Morbi fringilla congue libero, ac malesuada vulputate pharetra.

How can we help you?

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Suspendisse et justo. Praesent mattis commodo augue.​

Portfolio

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Suspendisse et justo. Praesent mattis commodo augue.​

Our Customers

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Suspendisse et justo. Praesent mattis commodo augue.​

"Amazing Designs and Quality Work!"

Nam at congue diam. Etiam erat lectus, finibus eget commodo quis, tincidunt eget leo. Nullam quis vulputate orci, ac accumsan quam. Morbi fringilla congue libero, ac malesuada vulputate pharetra.
John Doe
CEO, Acme Inc.

Meet Our Leadership

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Suspendisse et justo. Praesent mattis commodo augue.​

Joanne Williams

Founder

Fred Buster

Director OPS

Lisa Hoffman​

Director HR

Would you like to start a project with us?

Etiam erat lectus, finibus eget commodo quis, tincidunt eget leo. Nullam quis vulputate orci, ac accumsan quam. Morbi fringilla congue libero.