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