Changeset 3411134
- Timestamp:
- 12/04/2025 03:28:49 PM (3 months ago)
- Location:
- madnesschat-button/tags/1.0.2
- Files:
-
- 1 added
- 5 edited
-
assets/js/mcnb-frontend-simple.js (modified) (33 diffs)
-
frontend/class-mcnb-frontend.php (modified) (3 diffs)
-
includes/class-mcnb-seo.php (added)
-
includes/class-mcnb-settings.php (modified) (3 diffs)
-
madnesschat-button.php (modified) (5 diffs)
-
readme.txt (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
madnesschat-button/tags/1.0.2/assets/js/mcnb-frontend-simple.js
r3403447 r3411134 1 (function(){ 2 function onReady(fn){ 3 if(document.readyState!=='loading'){ 4 fn(); 5 } else { 6 document.addEventListener('DOMContentLoaded', fn); 7 } 8 } 9 10 function createSvgIcon(iconType){ 11 var svg = document.createElementNS('http://www.w3.org/2000/svg','svg'); 12 svg.setAttribute('viewBox','0 0 24 24'); 13 svg.setAttribute('width','1em'); 14 svg.setAttribute('height','1em'); 15 svg.className = 'mcnb-icon'; 16 svg.setAttribute('aria-hidden','true'); 17 svg.setAttribute('focusable','false'); 18 svg.style.display = 'inline-block'; 19 svg.style.fill = 'currentColor'; 20 svg.style.verticalAlign = 'middle'; 21 22 var path = document.createElementNS('http://www.w3.org/2000/svg','path'); 23 path.setAttribute('fill','currentColor'); 24 25 // Diferentes íconos según el tipo 26 switch(iconType) { 27 case 'whatsapp': 28 path.setAttribute('d','M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.890-5.335 11.893-11.893A11.821 11.821 0 0020.051 3.488'); 29 break; 30 case 'phone': 31 path.setAttribute('d','M20.01 15.38c-1.23 0-2.42-.2-3.53-.56-.35-.12-.74-.03-1.01.24l-1.57 1.97c-2.83-1.35-5.48-3.9-6.89-6.83l1.95-1.66c.27-.28.35-.67.24-1.02-.37-1.11-.56-2.3-.56-3.53 0-.54-.45-.99-.99-.99H4.19C3.65 3 3 3.24 3 3.99 3 13.28 10.73 21 20.01 21c.71 0 .99-.63.99-1.18v-3.45c0-.54-.45-.99-.99-.99z'); 32 break; 33 case 'chat': 34 path.setAttribute('d','M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h4l4 4 4-4h4c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z'); 35 break; 36 case 'message': 37 path.setAttribute('d','M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10h5v-2h-5c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8v1.43c0 .79-.71 1.57-1.5 1.57s-1.5-.78-1.5-1.57V12c0-2.76-2.24-5-5-5s-5 2.24-5 5 2.24 5 5 5c1.38 0 2.64-.56 3.54-1.47.65.89 1.77 1.47 2.96 1.47 1.97 0 3.5-1.53 3.5-3.5V12c0-5.52-4.48-10-10-10zm0 13c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3z'); 38 break; 39 default: 40 // Fallback al ícono de WhatsApp 41 path.setAttribute('d','M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.890-5.335 11.893-11.893A11.821 11.821 0 0020.051 3.488'); 42 } 43 44 svg.appendChild(path); 45 return svg; 46 } 47 1 (function () { 2 function onReady(fn) { 3 if (document.readyState !== 'loading') { 4 fn(); 5 } else { 6 document.addEventListener('DOMContentLoaded', fn); 7 } 8 } 9 10 function createSvgIcon(iconType) { 11 try { 12 // Check for SVG support 13 if (!document.createElementNS) { 14 return createFallbackIcon(); 15 } 16 17 var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 18 if (!svg) { 19 return createFallbackIcon(); 20 } 21 22 svg.setAttribute('viewBox', '0 0 24 24'); 23 svg.setAttribute('width', '1em'); 24 svg.setAttribute('height', '1em'); 25 svg.className = 'mcnb-icon'; 26 svg.setAttribute('aria-hidden', 'true'); 27 svg.setAttribute('focusable', 'false'); 28 svg.style.display = 'inline-block'; 29 svg.style.fill = 'currentColor'; 30 svg.style.verticalAlign = 'middle'; 31 32 var path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); 33 if (!path) { 34 return createFallbackIcon(); 35 } 36 37 path.setAttribute('fill', 'currentColor'); 38 39 // Diferentes íconos según el tipo 40 switch (iconType) { 41 case 'whatsapp': 42 path.setAttribute('d', 'M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.890-5.335 11.893-11.893A11.821 11.821 0 0020.051 3.488'); 43 break; 44 case 'phone': 45 path.setAttribute('d', 'M20.01 15.38c-1.23 0-2.42-.2-3.53-.56-.35-.12-.74-.03-1.01.24l-1.57 1.97c-2.83-1.35-5.48-3.9-6.89-6.83l1.95-1.66c.27-.28.35-.67.24-1.02-.37-1.11-.56-2.3-.56-3.53 0-.54-.45-.99-.99-.99H4.19C3.65 3 3 3.24 3 3.99 3 13.28 10.73 21 20.01 21c.71 0 .99-.63.99-1.18v-3.45c0-.54-.45-.99-.99-.99z'); 46 break; 47 case 'chat': 48 path.setAttribute('d', 'M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h4l4 4 4-4h4c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z'); 49 break; 50 case 'message': 51 path.setAttribute('d', 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10h5v-2h-5c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8v1.43c0 .79-.71 1.57-1.5 1.57s-1.5-.78-1.5-1.57V12c0-2.76-2.24-5-5-5s-5 2.24-5 5 2.24 5 5 5c1.38 0 2.64-.56 3.54-1.47.65.89 1.77 1.47 2.96 1.47 1.97 0 3.5-1.53 3.5-3.5V12c0-5.52-4.48-10-10-10zm0 13c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3z'); 52 break; 53 default: 54 // Fallback al ícono de WhatsApp 55 path.setAttribute('d', 'M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.890-5.335 11.893-11.893A11.821 11.821 0 0020.051 3.488'); 56 } 57 58 svg.appendChild(path); 59 return svg; 60 } catch (e) { 61 // If any error occurs during SVG creation, return fallback 62 return createFallbackIcon(); 63 } 64 } 65 48 66 function createCustomIcon(customIcon) { 49 if (customIcon.match(/\.(svg|png|jpg|jpeg|gif)$/i)) { 50 // Es una URL de imagen 51 var img = document.createElement('img'); 52 img.src = customIcon; 53 img.className = 'mcnb-icon'; 54 img.style.width = '1em'; 55 img.style.height = '1em'; 56 img.style.display = 'inline-block'; 57 img.style.verticalAlign = 'middle'; 58 return img; 59 } else { 60 // Es código HTML personalizado 61 var wrapper = document.createElement('span'); 62 wrapper.className = 'mcnb-icon'; 63 wrapper.innerHTML = customIcon; 64 wrapper.style.display = 'inline-block'; 65 wrapper.style.fontSize = '1em'; 66 wrapper.style.lineHeight = '1'; 67 return wrapper; 68 } 69 } 70 71 function createFallbackIcon(){ 67 try { 68 if (!customIcon) { 69 return createFallbackIcon(); 70 } 71 72 if (customIcon.match(/\.(svg|png|jpg|jpeg|gif)$/i)) { 73 // Es una URL de imagen 74 var img = document.createElement('img'); 75 img.src = customIcon; 76 img.className = 'mcnb-icon'; 77 img.alt = 'WhatsApp'; 78 img.style.width = '1em'; 79 img.style.height = '1em'; 80 img.style.display = 'inline-block'; 81 img.style.verticalAlign = 'middle'; 82 83 // Handle image load error 84 img.onerror = function () { 85 var fallback = createFallbackIcon(); 86 if (img.parentNode) { 87 img.parentNode.replaceChild(fallback, img); 88 } 89 }; 90 91 return img; 92 } else { 93 // Es código HTML personalizado 94 var wrapper = document.createElement('span'); 95 wrapper.className = 'mcnb-icon'; 96 wrapper.innerHTML = customIcon; 97 wrapper.style.display = 'inline-block'; 98 wrapper.style.fontSize = '1em'; 99 wrapper.style.lineHeight = '1'; 100 return wrapper; 101 } 102 } catch (e) { 103 return createFallbackIcon(); 104 } 105 } 106 107 function createFallbackIcon() { 72 108 // Ícono de fallback usando texto 73 109 var icon = document.createElement('span'); … … 81 117 82 118 83 function trackClick() {84 if (!window.mcnbData){ return; }85 119 function trackClick() { 120 if (!window.mcnbData) { return; } 121 86 122 // Verificar consentimiento GDPR antes de hacer tracking (Directriz 7) 87 123 // Solo hacer tracking si: … … 89 125 // 2. (GDPR está deshabilitado O se ha dado consentimiento) 90 126 var cfg = window.mcnbData; 91 if (cfg.analyticsEnabled){127 if (cfg.analyticsEnabled) { 92 128 // Si GDPR está habilitado, verificar consentimiento 93 if (cfg.gdprEnabled && !hasGDPRConsent()){129 if (cfg.gdprEnabled && !hasGDPRConsent()) { 94 130 // No hacer tracking sin consentimiento 95 131 return; … … 99 135 return; 100 136 } 101 137 102 138 var trackingData = { 103 139 action: 'mcnb_track_click', … … 109 145 110 146 var ajaxUrl = getAjaxUrl(); 111 if (!ajaxUrl){147 if (!ajaxUrl) { 112 148 // No se puede hacer tracking sin URL de Ajax válida 113 149 return; … … 115 151 116 152 // Usar jQuery AJAX si está disponible (de WordPress), sino fetch 117 if (window.jQuery && window.jQuery.ajax){153 if (window.jQuery && window.jQuery.ajax) { 118 154 window.jQuery.ajax({ 119 155 url: ajaxUrl, 120 156 method: 'POST', 121 157 data: trackingData, 122 success: function (response){158 success: function (response) { 123 159 // Click tracked successfully 124 160 }, 125 error: function (xhr, status, error){161 error: function (xhr, status, error) { 126 162 // Failed to track click 127 163 } 128 164 }); 129 } else if (window.fetch){165 } else if (window.fetch) { 130 166 fetch(ajaxUrl, { 131 167 method: 'POST', … … 134 170 }, 135 171 body: new URLSearchParams(trackingData) 136 }).then(function (response){172 }).then(function (response) { 137 173 return response.json(); 138 }).then(function (data){174 }).then(function (data) { 139 175 // Click tracked successfully 140 }).catch(function (error){176 }).catch(function (error) { 141 177 // Failed to track click 142 178 }); … … 144 180 } 145 181 146 function getAjaxUrl() {147 if (window.mcnbData && window.mcnbData.ajaxUrl){ return window.mcnbData.ajaxUrl; }148 if (window.ajaxurl){ return window.ajaxurl; }182 function getAjaxUrl() { 183 if (window.mcnbData && window.mcnbData.ajaxUrl) { return window.mcnbData.ajaxUrl; } 184 if (window.ajaxurl) { return window.ajaxurl; } 149 185 // No usar fallback estático - debe ser proporcionado desde PHP via wp_localize_scri 150 186 return null; … … 153 189 function getCurrentDeviceConfig() { 154 190 var cfg = window.mcnbData; 155 191 156 192 // Si responsive no está habilitado, usar configuración básica 157 193 if (!cfg.responsiveEnabled || !cfg.responsiveConfig) { … … 164 200 }; 165 201 } 166 202 167 203 // Determinar dispositivo actual según breakpoints 168 204 var screenWidth = window.innerWidth; 169 205 var deviceType = 'desktop'; // Por defecto 170 206 171 207 if (screenWidth <= 425) { 172 208 deviceType = 'mobile'; … … 178 214 deviceType = 'desktop'; 179 215 } 180 216 181 217 // Dispositivo detectado según breakpoint 182 218 183 219 // Obtener configuración para el dispositivo 184 220 var deviceConfig = cfg.responsiveConfig[deviceType]; … … 195 231 }; 196 232 } 197 233 198 234 return { 199 235 size: parseInt(deviceConfig.size || '56', 10), … … 207 243 } 208 244 209 onReady(function (){210 if (!window.mcnbData){211 return; 212 } 213 245 onReady(function () { 246 if (!window.mcnbData) { 247 return; 248 } 249 214 250 var cfg = window.mcnbData; 215 251 216 252 // A/B Testing no disponible en versión gratuita - siempre usar configuración original 217 253 function getABVariant() { … … 228 264 } 229 265 230 function mount() {266 function mount() { 231 267 var root = document.getElementById('mcnb-root'); 232 if (!root){233 return; 234 } 235 268 if (!root) { 269 return; 270 } 271 236 272 // Limpiar contenido previo 237 273 root.innerHTML = ''; 238 274 239 275 // A/B Testing no disponible en versión gratuita - usar configuración original 240 276 var abVariant = getABVariant(); // Siempre 'A' 241 277 var variantConfig = getVariantConfig(abVariant); // Siempre configuración original 242 278 243 279 // Obtener configuración según responsive 244 280 var currentConfig = getCurrentDeviceConfig(); 245 281 246 282 // Posicionamiento 247 283 root.style.position = 'fixed'; 248 284 root.style.zIndex = '999999'; 249 285 250 286 // Aplicar posicionamiento según la variante A/B 251 287 if (currentConfig.position === 'bottom-right') { … … 269 305 270 306 var btn = document.createElement('a'); 271 btn.href = cfg.link || 'https://wa.me/'; 272 btn.target = '_blank'; 307 btn.href = cfg.link || 'https://wa.me/'; 308 btn.target = '_blank'; 273 309 btn.rel = 'noopener noreferrer'; 274 310 btn.className = 'mcnb-button'; 275 311 276 312 // Aplicar estilos básicos 277 313 var size = currentConfig.size; 278 btn.style.width = size + 'px'; 279 btn.style.height = size + 'px'; 280 btn.style.background = currentConfig.brandColor || '#25D366'; 314 btn.style.width = size + 'px'; 315 btn.style.height = size + 'px'; 316 btn.style.background = currentConfig.brandColor || '#25D366'; 281 317 btn.style.color = cfg.textColor || '#ffffff'; 282 318 btn.style.borderRadius = '50%'; … … 287 323 btn.style.fontSize = Math.round(size * 0.6) + 'px'; 288 324 btn.style.transition = 'all 0.15s ease'; 289 325 290 326 // Aplicar estilo de botón si está configurado 291 if (cfg.buttonStyle === 'square'){327 if (cfg.buttonStyle === 'square') { 292 328 btn.style.borderRadius = '8px'; 293 } else if (cfg.buttonStyle === 'pill'){329 } else if (cfg.buttonStyle === 'pill') { 294 330 btn.style.borderRadius = '999px'; 295 331 btn.style.padding = '0 16px'; 296 } else if (cfg.buttonStyle === 'text_side'){332 } else if (cfg.buttonStyle === 'text_side') { 297 333 btn.style.borderRadius = '999px'; 298 334 btn.style.padding = '12px 20px'; 299 335 btn.style.gap = '8px'; 300 336 } 301 337 302 338 // Aplicar sombra si está habilitada (más visible por defecto) 303 if (cfg.shadowEnabled){339 if (cfg.shadowEnabled) { 304 340 // Sombra más visible y pronunciada por defecto 305 341 var shadowValue = cfg.shadowColor || 'rgba(0,0,0,0.3)'; … … 309 345 btn.style.boxShadow = 'none'; 310 346 } 311 347 312 348 // Aplicar borde si está habilitado 313 if (cfg.borderEnabled){349 if (cfg.borderEnabled) { 314 350 btn.style.border = (cfg.borderWidth || 2) + 'px solid ' + (cfg.borderColor || '#ffffff'); 315 351 } 316 352 317 353 // Aplicar animación 318 if (cfg.animation && cfg.animation !== 'none'){354 if (cfg.animation && cfg.animation !== 'none') { 319 355 btn.style.animation = 'mcnb-' + cfg.animation + ' 2s infinite'; 320 if (cfg.animationDelay){356 if (cfg.animationDelay) { 321 357 btn.style.animationDelay = cfg.animationDelay + 'ms'; 322 358 } 323 359 } 324 360 325 361 // Aplicar efecto hover usando clases CSS (más eficiente y soporta todos los efectos) 326 if (cfg.hoverEffect && cfg.hoverEffect !== 'none'){362 if (cfg.hoverEffect && cfg.hoverEffect !== 'none') { 327 363 btn.classList.add('hover-' + cfg.hoverEffect); 328 364 } 329 365 330 // Agregar ícono según configuración331 try {366 // Agregar ícono según configuración 367 try { 332 368 var icon; 333 369 if (cfg.iconType === 'custom' && cfg.customIcon) { … … 336 372 icon = createSvgIcon(cfg.iconType || 'chat'); 337 373 } 338 374 339 375 if (icon && (icon.appendChild || icon.tagName)) { 340 376 btn.appendChild(icon); … … 342 378 throw new Error('Icon creation failed'); 343 379 } 344 } catch (e) {380 } catch (e) { 345 381 btn.appendChild(createFallbackIcon()); 346 382 } 347 383 348 384 // Añadir tracking de clicks 349 btn.addEventListener('click', function (e) {385 btn.addEventListener('click', function (e) { 350 386 e.preventDefault(); 351 387 352 388 // Verificar GDPR si está habilitado 353 389 if (cfg.gdprEnabled && !hasGDPRConsent()) { … … 355 391 return; 356 392 } 357 358 // Si ya tiene consentimiento o GDPR está deshabilitado, continuar359 360 // Tracking interno361 trackClick();362 393 394 // Si ya tiene consentimiento o GDPR está deshabilitado, continuar 395 396 // Tracking interno 397 trackClick(); 398 363 399 // Abrir WhatsApp 364 400 openWhatsApp(); 365 401 }); 366 402 367 403 // Si es estilo text_side, agregar texto 368 if (cfg.buttonStyle === 'text_side' && cfg.labelText){404 if (cfg.buttonStyle === 'text_side' && cfg.labelText) { 369 405 var textSpan = document.createElement('span'); 370 406 textSpan.textContent = cfg.labelText; … … 375 411 // Etiqueta externa (solo si no es text_side y está habilitada para este dispositivo) 376 412 var label = null; 377 if (currentConfig.showLabel && cfg.buttonStyle !== 'text_side' && cfg.labelText){413 if (currentConfig.showLabel && cfg.buttonStyle !== 'text_side' && cfg.labelText) { 378 414 label = document.createElement('span'); 379 415 label.className = 'mcnb-label'; 380 416 label.textContent = cfg.labelText || 'Chat'; 381 label.style.background = cfg.brandColor || '#25D366'; 417 label.style.background = cfg.brandColor || '#25D366'; 382 418 label.style.color = cfg.textColor || '#ffffff'; 383 419 label.style.padding = '10px 14px'; 384 420 label.style.borderRadius = '999px'; 385 421 label.style.whiteSpace = 'nowrap'; 386 422 387 423 // Aplicar posicionamiento según labelPosition 388 424 var labelPosition = cfg.labelPosition || 'left'; 389 switch (labelPosition) {425 switch (labelPosition) { 390 426 case 'left': 391 427 label.style.marginRight = '10px'; … … 402 438 } 403 439 } 404 440 405 441 // Configurar wrapper y añadir elementos en el orden correcto 406 442 wrapper.style.display = 'flex'; 407 443 wrapper.style.alignItems = 'center'; 408 409 if (label) {444 445 if (label) { 410 446 var labelPosition = cfg.labelPosition || 'left'; 411 412 if (labelPosition === 'left') {447 448 if (labelPosition === 'left') { 413 449 wrapper.style.flexDirection = 'row'; 414 450 wrapper.appendChild(label); 415 451 wrapper.appendChild(btn); 416 } else if (labelPosition === 'right') {452 } else if (labelPosition === 'right') { 417 453 wrapper.style.flexDirection = 'row'; 418 454 wrapper.appendChild(btn); 419 455 wrapper.appendChild(label); 420 } else if (labelPosition === 'top') {456 } else if (labelPosition === 'top') { 421 457 wrapper.style.flexDirection = 'column'; 422 458 wrapper.appendChild(label); 423 459 wrapper.appendChild(btn); 424 } else if (labelPosition === 'bottom') {460 } else if (labelPosition === 'bottom') { 425 461 wrapper.style.flexDirection = 'column'; 426 462 wrapper.appendChild(btn); … … 447 483 function checkAndMount() { 448 484 if (buttonMounted) return; 449 485 450 486 // Verificar si todas las condiciones necesarias están cumplidas 451 487 var delayReady = (cfg.delayMs || 0) === 0 || triggers.delay; 452 488 var scrollReady = (cfg.scrollPercent || 0) === 0 || triggers.scroll; 453 489 var timeReady = (cfg.timeOnPage || 0) === 0 || triggers.time; 454 490 455 491 if (delayReady && scrollReady && timeReady) { 456 492 // Montar el botón sin verificar GDPR (eso se hace en el click) … … 463 499 // 1. Configurar delay 464 500 if (cfg.delayMs && cfg.delayMs > 0) { 465 setTimeout(function () {501 setTimeout(function () { 466 502 triggers.delay = true; 467 503 checkAndMount(); … … 470 506 triggers.delay = true; 471 507 } 472 508 473 509 // 2. Configurar scroll 474 510 if (cfg.scrollPercent && cfg.scrollPercent > 0) { 475 511 // Verificar scroll inicial 476 512 var currentScroll = getScrollPercent(); 477 513 478 514 if (currentScroll >= cfg.scrollPercent) { 479 515 triggers.scroll = true; … … 493 529 triggers.scroll = true; 494 530 } 495 531 496 532 // 3. Configurar tiempo en página 497 533 if (cfg.timeOnPage && cfg.timeOnPage > 0) { 498 setTimeout(function () {534 setTimeout(function () { 499 535 triggers.time = true; 500 536 checkAndMount(); … … 503 539 triggers.time = true; 504 540 } 505 541 506 542 // Verificación inicial 507 543 checkAndMount(); … … 521 557 // Iniciar configuración de disparadores 522 558 setupTriggers(); 523 559 524 560 // Reposicionar en cambio de tamaño de ventana (responsive) 525 if (cfg.responsiveEnabled) {561 if (cfg.responsiveEnabled) { 526 562 var resizeTimeout; 527 window.addEventListener('resize', function () {563 window.addEventListener('resize', function () { 528 564 clearTimeout(resizeTimeout); 529 resizeTimeout = setTimeout(function () {565 resizeTimeout = setTimeout(function () { 530 566 mount(); // Volver a montar con nueva configuración 531 567 }, 250); … … 533 569 } 534 570 }); 535 571 536 572 // Funciones de GDPR 537 573 function hasGDPRConsent() { 538 574 return localStorage.getItem('mcnb_gdpr_consent') === 'true'; 539 575 } 540 576 541 577 function setGDPRConsent(consent) { 542 578 localStorage.setItem('mcnb_gdpr_consent', consent ? 'true' : 'false'); 543 579 } 544 580 545 581 function showGDPRModal() { 546 582 if (!window.mcnbData) return; 547 583 548 584 // Evitar que se abra múltiples veces 549 585 if (document.querySelector('.mcnb-gdpr-modal')) return; 550 586 551 587 var cfg = window.mcnbData; 588 589 // Get customizable styles from config (con valores por defecto) 590 var modalBgColor = cfg.gdprModalBgColor || 'rgba(0,0,0,0.7)'; 591 var modalContentBg = cfg.gdprModalContentBg || '#ffffff'; 592 var modalBorderRadius = cfg.gdprModalBorderRadius || '12px'; 593 var modalTitleColor = cfg.gdprModalTitleColor || '#2c3e50'; 594 var acceptBtnBg = cfg.gdprAcceptBtnBg || cfg.brandColor || '#25D366'; 595 var acceptBtnColor = cfg.gdprAcceptBtnColor || '#ffffff'; 596 var acceptBtnHoverBg = cfg.gdprAcceptBtnHoverBg || '#22c55e'; 597 var declineBtnBg = cfg.gdprDeclineBtnBg || '#e74c3c'; 598 var declineBtnColor = cfg.gdprDeclineBtnColor || '#ffffff'; 599 var declineBtnHoverBg = cfg.gdprDeclineBtnHoverBg || '#dc2626'; 600 var btnBorderRadius = cfg.gdprBtnBorderRadius || '6px'; 601 552 602 var modal = document.createElement('div'); 553 603 modal.className = 'mcnb-gdpr-modal'; 604 modal.setAttribute('role', 'dialog'); 605 modal.setAttribute('aria-modal', 'true'); 606 modal.setAttribute('aria-labelledby', 'mcnb-gdpr-title'); 607 modal.setAttribute('tabindex', '-1'); 554 608 modal.style.cssText = ` 555 609 position: fixed; … … 558 612 width: 100%; 559 613 height: 100%; 560 background: rgba(0,0,0,0.7);614 background: ${modalBgColor}; 561 615 display: flex; 562 616 align-items: center; … … 564 618 z-index: 999999; 565 619 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; 620 animation: mcnb-fade-in 0.25s ease; 566 621 `; 567 622 568 623 var content = document.createElement('div'); 624 content.className = 'mcnb-gdpr-content'; 569 625 content.style.cssText = ` 570 background: white;626 background: ${modalContentBg}; 571 627 padding: 30px; 572 border-radius: 12px;628 border-radius: ${modalBorderRadius}; 573 629 max-width: 400px; 574 630 width: 90%; 575 631 text-align: center; 576 632 box-shadow: 0 10px 30px rgba(0,0,0,0.3); 633 animation: mcnb-slide-up 0.25s ease; 577 634 `; 578 635 579 636 content.innerHTML = ` 580 <h3 style="margin: 0 0 20px 0; color: #2c3e50; font-size: 18px;">${cfg.gdprMessage || 'Usamos chat para atenderte. ¿Aceptas continuar?'}</h3>637 <h3 id="mcnb-gdpr-title" style="margin: 0 0 20px 0; color: ${modalTitleColor}; font-size: 18px;">${cfg.gdprMessage || 'Usamos chat para atenderte. ¿Aceptas continuar?'}</h3> 581 638 <div style="display: flex; gap: 12px; justify-content: center; margin-top: 24px;"> 582 639 <button class="mcnb-gdpr-accept" style=" 583 background: #25D366;584 color: white;640 background: ${acceptBtnBg}; 641 color: ${acceptBtnColor}; 585 642 border: none; 586 643 padding: 12px 24px; 587 border-radius: 6px;644 border-radius: ${btnBorderRadius}; 588 645 font-weight: 600; 589 646 cursor: pointer; 590 647 font-size: 14px; 648 transition: all 0.2s ease; 591 649 ">${cfg.gdprButtonLabel || 'Aceptar y continuar'}</button> 592 650 <button class="mcnb-gdpr-decline" style=" 593 background: #e74c3c;594 color: white;651 background: ${declineBtnBg}; 652 color: ${declineBtnColor}; 595 653 border: none; 596 654 padding: 12px 24px; 597 border-radius: 6px;655 border-radius: ${btnBorderRadius}; 598 656 font-weight: 600; 599 657 cursor: pointer; 600 658 font-size: 14px; 659 transition: all 0.2s ease; 601 660 ">Cancelar</button> 602 661 </div> 603 662 `; 604 663 605 664 modal.appendChild(content); 606 665 document.body.appendChild(modal); 607 608 // Event listeners 609 content.querySelector('.mcnb-gdpr-accept').addEventListener('click', function() { 610 setGDPRConsent(true); 611 document.body.removeChild(modal); 612 613 // Abrir WhatsApp después de aceptar 614 615 // Tracking interno 616 trackClick(); 617 666 667 // Focus trap elements 668 var acceptBtn = content.querySelector('.mcnb-gdpr-accept'); 669 var declineBtn = content.querySelector('.mcnb-gdpr-decline'); 670 var focusableElements = [acceptBtn, declineBtn]; 671 var firstFocusable = focusableElements[0]; 672 var lastFocusable = focusableElements[focusableElements.length - 1]; 673 674 // Set initial focus 675 setTimeout(function () { 676 firstFocusable.focus(); 677 }, 100); 678 679 // Close modal function (ARREGLADO: sin parpadeo) 680 function closeModal() { 681 if (modal.parentNode) { 682 modal.style.pointerEvents = 'none'; 683 content.style.animation = 'mcnb-slide-down 0.3s ease forwards'; 684 modal.style.animation = 'mcnb-fade-out 0.3s ease forwards'; 685 setTimeout(function () { 686 if (modal.parentNode) { 687 document.body.removeChild(modal); 688 } 689 }, 300); 690 } 691 } 692 693 // Accept button 694 acceptBtn.addEventListener('click', function () { 695 setGDPRConsent(true); 696 closeModal(); 697 698 // Tracking interno 699 trackClick(); 700 618 701 // Abrir WhatsApp 619 702 openWhatsApp(); 620 703 }); 621 622 content.querySelector('.mcnb-gdpr-decline').addEventListener('click', function() { 623 document.body.removeChild(modal); 704 705 // Decline button 706 declineBtn.addEventListener('click', function () { 707 closeModal(); 624 708 }); 625 626 // C errar con ESC627 modal.addEventListener(' keydown', function(e) {628 if (e. key === 'Escape') {629 document.body.removeChild(modal);709 710 // Close on backdrop click (click on modal, not content) 711 modal.addEventListener('click', function (e) { 712 if (e.target === modal) { 713 closeModal(); 630 714 } 631 715 }); 632 modal.focus(); 633 } 634 716 717 // Close with ESC key 718 function handleEscape(e) { 719 if (e.key === 'Escape' || e.keyCode === 27) { 720 closeModal(); 721 document.removeEventListener('keydown', handleEscape); 722 } 723 } 724 document.addEventListener('keydown', handleEscape); 725 726 // Focus trap 727 modal.addEventListener('keydown', function (e) { 728 if (e.key === 'Tab' || e.keyCode === 9) { 729 if (e.shiftKey) { 730 // Shift + Tab 731 if (document.activeElement === firstFocusable) { 732 e.preventDefault(); 733 lastFocusable.focus(); 734 } 735 } else { 736 // Tab 737 if (document.activeElement === lastFocusable) { 738 e.preventDefault(); 739 firstFocusable.focus(); 740 } 741 } 742 } 743 }); 744 745 // Add CSS animations if not already present 746 if (!document.getElementById('mcnb-modal-styles')) { 747 var style = document.createElement('style'); 748 style.id = 'mcnb-modal-styles'; 749 style.textContent = ` 750 @keyframes mcnb-fade-in { 751 from { opacity: 0; } 752 to { opacity: 1; } 753 } 754 @keyframes mcnb-fade-out { 755 from { opacity: 1; } 756 to { opacity: 0; } 757 } 758 @keyframes mcnb-slide-up { 759 from { transform: translateY(20px); opacity: 0; } 760 to { transform: translateY(0); opacity: 1; } 761 } 762 @keyframes mcnb-slide-down { 763 from { transform: translateY(0); opacity: 1; } 764 to { transform: translateY(20px); opacity: 0; } 765 } 766 .mcnb-gdpr-accept:hover { 767 background: ${acceptBtnHoverBg} !important; 768 transform: translateY(-1px); 769 box-shadow: 0 4px 12px rgba(0,0,0,0.15); 770 } 771 .mcnb-gdpr-decline:hover { 772 background: ${declineBtnHoverBg} !important; 773 transform: translateY(-1px); 774 box-shadow: 0 4px 12px rgba(0,0,0,0.15); 775 } 776 .mcnb-gdpr-accept:focus, 777 .mcnb-gdpr-decline:focus { 778 outline: 2px solid #3b82f6; 779 outline-offset: 2px; 780 } 781 .mcnb-gdpr-accept:active, 782 .mcnb-gdpr-decline:active { 783 transform: translateY(0); 784 } 785 `; 786 document.head.appendChild(style); 787 } 788 } 789 635 790 // Función para abrir WhatsApp con UTM 636 791 function openWhatsApp() { 637 792 if (!window.mcnbData) return; 638 793 639 794 var cfg = window.mcnbData; 640 795 var url = cfg.link; 641 796 642 797 // Versión gratuita - siempre usar mensaje por defecto 643 798 var message = cfg.defaultMessage; 644 799 645 800 // Si hay mensaje personalizado, agregarlo a la URL 646 801 if (message && message.trim()) { … … 648 803 url += separator + 'text=' + encodeURIComponent(message); 649 804 } 650 805 651 806 // Agregar parámetros UTM si están habilitados 652 807 if (cfg.utmEnabled) { … … 655 810 utmParams.append('utm_medium', cfg.utmMedium || 'cta'); 656 811 utmParams.append('utm_campaign', cfg.utmCampaign || 'support'); 657 812 658 813 // Agregar UTM al mensaje 659 814 var separator = url.includes('?') ? '&' : '?'; 660 815 url += separator + utmParams.toString(); 661 816 } 662 817 663 818 window.open(url, '_blank'); 664 819 } -
madnesschat-button/tags/1.0.2/frontend/class-mcnb-frontend.php
r3403447 r3411134 8 8 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) ); 9 9 add_action( 'wp_footer', array( $this, 'render_markup' ) ); 10 } 11 12 /** 13 * Normalize time format to HH:MM for consistent comparison 14 * 15 * @param string $time Time string in various formats (H:i, H:i:s, etc.) 16 * @return string Normalized time in HH:MM format or empty string if invalid 17 */ 18 private function normalize_time_format( $time ) { 19 if ( empty( $time ) ) { 20 return ''; 21 } 22 23 // Remove any whitespace 24 $time = trim( $time ); 25 26 // Try to parse the time 27 $timestamp = strtotime( $time ); 28 if ( false === $timestamp ) { 29 return ''; 30 } 31 32 // Return in HH:MM format (24-hour) 33 return date( 'H:i', $timestamp ); 10 34 } 11 35 … … 30 54 // Obtener hora local usando wp_date que respeta la zona horaria configurada en WordPress 31 55 $current_time = wp_date( 'H:i' ); 32 $start_time = $options['schedule_start']; 33 $end_time = $options['schedule_end']; 56 $start_time = isset( $options['schedule_start'] ) ? $options['schedule_start'] : ''; 57 $end_time = isset( $options['schedule_end'] ) ? $options['schedule_end'] : ''; 58 59 // Normalizar formato de tiempo a HH:MM para comparación consistente 60 $current_time = $this->normalize_time_format( $current_time ); 61 $start_time = $this->normalize_time_format( $start_time ); 62 $end_time = $this->normalize_time_format( $end_time ); 63 64 // Validar que los tiempos sean válidos 65 if ( empty( $start_time ) || empty( $end_time ) ) { 66 return true; // Si no hay horario válido, mostrar el botón 67 } 34 68 35 69 // Comparar horarios como strings en formato HH:MM … … 100 134 $link = add_query_arg( array_map( 'rawurlencode', $query ), $link ); 101 135 102 // Preparar payload sanitizado para JavaScript 103 $payload = array( 104 'link' => esc_url_raw( $link ), 105 'position' => sanitize_key( $options['position'] ), 106 'offsetX' => max( 0, min( 500, intval( $options['offset_x'] ) ) ), 107 'offsetY' => max( 0, min( 500, intval( $options['offset_y'] ) ) ), 108 'size' => sanitize_key( $options['size'] ), 109 'buttonStyle' => sanitize_key( $options['button_style'] ?? 'circle' ), 110 'showLabel' => ! empty( $options['show_label'] ), 111 'labelText' => wp_strip_all_tags( $options['label_text'] ), 112 'labelPosition' => sanitize_key( $options['label_position'] ?? 'left' ), 113 'brandColor' => sanitize_hex_color( $options['brand_color'] ) ?: '#25D366', 114 'textColor' => sanitize_hex_color( $options['text_color'] ) ?: '#ffffff', 115 'shadowEnabled' => ! empty( $options['shadow_enabled'] ), 116 'shadowColor' => sanitize_text_field( $options['shadow_color'] ?? 'rgba(0,0,0,0.3)' ), 117 'borderEnabled' => ! empty( $options['border_enabled'] ), 118 'borderColor' => sanitize_hex_color( $options['border_color'] ) ?: '#ffffff', 119 'borderWidth' => max( 1, min( 10, intval( $options['border_width'] ?? 2 ) ) ), 120 'animation' => sanitize_key( $options['animation'] ?? 'none' ), 121 'animationDelay' => max( 1000, min( 10000, intval( $options['animation_delay'] ?? 3000 ) ) ), 122 'hoverEffect' => sanitize_key( $options['hover_effect'] ?? 'lift' ), 123 'iconType' => sanitize_key( $options['icon_type'] ?? 'whatsapp' ), 124 'customIcon' => esc_url( $options['custom_icon'] ?? '' ), 125 'delayMs' => max( 0, min( 60000, intval( $options['delay_ms'] ) ) ), 126 'scrollPercent' => max( 0, min( 100, intval( $options['scroll_percent'] ) ) ), 127 'timeOnPage' => max( 0, min( 600, intval( $options['time_on_page'] ) ) ), 128 'gdprEnabled' => ! empty( $options['gdpr_enabled'] ), 129 'gdprMessage' => wp_strip_all_tags( $options['gdpr_message'] ), 130 'gdprButtonLabel' => wp_strip_all_tags( $options['gdpr_button_label'] ), 131 'analyticsEnabled' => ! empty( $options['analytics_enabled'] ), 132 'utmEnabled' => ! empty( $options['utm_enabled'] ), 133 'utmSource' => sanitize_key( $options['utm_source'] ?? 'whatsapp_button' ), 134 'utmMedium' => sanitize_key( $options['utm_medium'] ?? 'cta' ), 135 'utmCampaign' => sanitize_key( $options['utm_campaign'] ?? 'support' ), 136 // A/B Testing deshabilitado en versión gratuita 137 'abTestingEnabled' => false, 138 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 139 'trackingNonce' => wp_create_nonce( 'mcnb_track_click' ), 140 'responsiveEnabled' => ! empty( $options['responsive_enabled'] ), 141 'responsiveConfig' => $options['responsive_config'] ?? array(), 142 ); 136 // Validar y normalizar configuración responsive 137 $responsive_config = array(); 138 if ( ! empty( $options['responsive_enabled'] ) && is_array( $options['responsive_config'] ?? null ) ) { 139 $devices = array( 'mobile', 'tablet', 'laptop', 'desktop' ); 140 foreach ( $devices as $device ) { 141 if ( isset( $options['responsive_config'][ $device ] ) && is_array( $options['responsive_config'][ $device ] ) ) { 142 $device_config = $options['responsive_config'][ $device ]; 143 $responsive_config[ $device ] = array( 144 'size' => sanitize_text_field( $device_config['size'] ?? '56' ), 145 'position' => sanitize_key( $device_config['position'] ?? 'bottom-right' ), 146 'offset_x' => max( 0, min( 500, intval( $device_config['offset_x'] ?? 20 ) ) ), 147 'offset_y' => max( 0, min( 500, intval( $device_config['offset_y'] ?? 20 ) ) ), 148 'show_label' => ! empty( $device_config['show_label'] ), 149 ); 150 } 151 } 152 } 153 154 // Preparar payload sanitizado para JavaScript 155 $payload = array( 156 'link' => esc_url_raw( $link ), 157 'position' => sanitize_key( $options['position'] ), 158 'offsetX' => max( 0, min( 500, intval( $options['offset_x'] ) ) ), 159 'offsetY' => max( 0, min( 500, intval( $options['offset_y'] ) ) ), 160 'size' => sanitize_key( $options['size'] ), 161 'buttonStyle' => sanitize_key( $options['button_style'] ?? 'circle' ), 162 'showLabel' => ! empty( $options['show_label'] ), 163 'labelText' => wp_strip_all_tags( $options['label_text'] ?? '' ), 164 'labelPosition' => sanitize_key( $options['label_position'] ?? 'left' ), 165 'brandColor' => sanitize_hex_color( $options['brand_color'] ) ?: '#25D366', 166 'textColor' => sanitize_hex_color( $options['text_color'] ) ?: '#ffffff', 167 'shadowEnabled' => ! empty( $options['shadow_enabled'] ), 168 'shadowColor' => sanitize_text_field( $options['shadow_color'] ?? 'rgba(0,0,0,0.3)' ), 169 'borderEnabled' => ! empty( $options['border_enabled'] ), 170 'borderColor' => sanitize_hex_color( $options['border_color'] ) ?: '#ffffff', 171 'borderWidth' => max( 1, min( 10, intval( $options['border_width'] ?? 2 ) ) ), 172 'animation' => sanitize_key( $options['animation'] ?? 'none' ), 173 'animationDelay' => max( 1000, min( 10000, intval( $options['animation_delay'] ?? 3000 ) ) ), 174 'hoverEffect' => sanitize_key( $options['hover_effect'] ?? 'lift' ), 175 'iconType' => sanitize_key( $options['icon_type'] ?? 'whatsapp' ), 176 'customIcon' => esc_url( $options['custom_icon'] ?? '' ), 177 'delayMs' => max( 0, min( 60000, intval( $options['delay_ms'] ?? 0 ) ) ), 178 'scrollPercent' => max( 0, min( 100, intval( $options['scroll_percent'] ?? 0 ) ) ), 179 'timeOnPage' => max( 0, min( 600, intval( $options['time_on_page'] ?? 0 ) ) ), 180 'gdprEnabled' => ! empty( $options['gdpr_enabled'] ), 181 'gdprMessage' => wp_strip_all_tags( $options['gdpr_message'] ?? '' ), 182 'gdprButtonLabel' => wp_strip_all_tags( $options['gdpr_button_label'] ?? '' ), 183 // GDPR Modal Styling Options 184 'gdprModalBgColor' => sanitize_text_field( $options['gdpr_modal_bg_color'] ?? 'rgba(0,0,0,0.7)' ), 185 'gdprModalContentBg' => sanitize_hex_color( $options['gdpr_modal_content_bg'] ?? '' ) ?: '#ffffff', 186 'gdprModalBorderRadius' => sanitize_text_field( $options['gdpr_modal_border_radius'] ?? '12px' ), 187 'gdprModalTitleColor' => sanitize_hex_color( $options['gdpr_modal_title_color'] ?? '' ) ?: '#2c3e50', 188 'gdprAcceptBtnBg' => sanitize_hex_color( $options['gdpr_accept_btn_bg'] ?? '' ) ?: '', 189 'gdprAcceptBtnColor' => sanitize_hex_color( $options['gdpr_accept_btn_color'] ?? '' ) ?: '#ffffff', 190 'gdprAcceptBtnHoverBg' => sanitize_hex_color( $options['gdpr_accept_btn_hover_bg'] ?? '' ) ?: '#22c55e', 191 'gdprDeclineBtnBg' => sanitize_hex_color( $options['gdpr_decline_btn_bg'] ?? '' ) ?: '#e74c3c', 192 'gdprDeclineBtnColor' => sanitize_hex_color( $options['gdpr_decline_btn_color'] ?? '' ) ?: '#ffffff', 193 'gdprDeclineBtnHoverBg' => sanitize_hex_color( $options['gdpr_decline_btn_hover_bg'] ?? '' ) ?: '#dc2626', 194 'gdprBtnBorderRadius' => sanitize_text_field( $options['gdpr_btn_border_radius'] ?? '6px' ), 195 'analyticsEnabled' => ! empty( $options['analytics_enabled'] ), 196 'utmEnabled' => ! empty( $options['utm_enabled'] ), 197 'utmSource' => sanitize_key( $options['utm_source'] ?? 'whatsapp_button' ), 198 'utmMedium' => sanitize_key( $options['utm_medium'] ?? 'cta' ), 199 'utmCampaign' => sanitize_key( $options['utm_campaign'] ?? 'support' ), 200 // A/B Testing deshabilitado en versión gratuita 201 'abTestingEnabled' => false, 202 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 203 'trackingNonce' => wp_create_nonce( 'mcnb_track_click' ), 204 'responsiveEnabled' => ! empty( $options['responsive_enabled'] ), 205 'responsiveConfig' => $responsive_config, 206 ); 143 207 144 208 wp_localize_script( 'mcnb-frontend', 'mcnbData', $payload ); -
madnesschat-button/tags/1.0.2/includes/class-mcnb-settings.php
r3403447 r3411134 184 184 $is_privacy_tab = array_key_exists( 'gdpr_message', $input ) || 185 185 array_key_exists( 'gdpr_button_label', $input ) || 186 array_key_exists( 'gdpr_modal_bg_color', $input ) || 187 array_key_exists( 'gdpr_modal_content_bg', $input ) || 186 188 array_key_exists( 'utm_source', $input ) || 187 189 array_key_exists( 'utm_medium', $input ) || … … 206 208 $gdpr_btn = sanitize_text_field( (string) $input['gdpr_button_label'] ); 207 209 $sanitized['gdpr_button_label'] = mb_substr( $gdpr_btn, 0, 50, 'UTF-8' ); 210 } 211 212 // Sanitizar estilos del modal GDPR 213 if ( array_key_exists( 'gdpr_modal_bg_color', $input ) ) { 214 $modal_bg = sanitize_text_field( (string) $input['gdpr_modal_bg_color'] ); 215 // Validar formato de color CSS (hex, rgb, rgba, hsl) 216 if ( preg_match( '/^(#[0-9a-fA-F]{3,6}|rgba?\([0-9,\s\.]+\)|hsla?\([0-9,\s\.%]+\))$/', $modal_bg ) ) { 217 $sanitized['gdpr_modal_bg_color'] = $modal_bg; 218 } else { 219 $sanitized['gdpr_modal_bg_color'] = 'rgba(0,0,0,0.7)'; 220 } 221 } 222 if ( array_key_exists( 'gdpr_modal_content_bg', $input ) ) { 223 $content_bg = sanitize_hex_color( (string) $input['gdpr_modal_content_bg'] ); 224 $sanitized['gdpr_modal_content_bg'] = $content_bg ? $content_bg : '#ffffff'; 225 } 226 if ( array_key_exists( 'gdpr_modal_border_radius', $input ) ) { 227 $border_radius = sanitize_text_field( (string) $input['gdpr_modal_border_radius'] ); 228 // Validar formato CSS (px, em, rem, %) 229 if ( preg_match( '/^\d+(\.\d+)?(px|em|rem|%)$/', $border_radius ) ) { 230 $sanitized['gdpr_modal_border_radius'] = $border_radius; 231 } else { 232 $sanitized['gdpr_modal_border_radius'] = '12px'; 233 } 234 } 235 if ( array_key_exists( 'gdpr_modal_title_color', $input ) ) { 236 $title_color = sanitize_hex_color( (string) $input['gdpr_modal_title_color'] ); 237 $sanitized['gdpr_modal_title_color'] = $title_color ? $title_color : '#2c3e50'; 238 } 239 if ( array_key_exists( 'gdpr_accept_btn_bg', $input ) ) { 240 $accept_bg = sanitize_hex_color( (string) $input['gdpr_accept_btn_bg'] ); 241 $sanitized['gdpr_accept_btn_bg'] = $accept_bg ? $accept_bg : ''; 242 } 243 if ( array_key_exists( 'gdpr_accept_btn_color', $input ) ) { 244 $accept_color = sanitize_hex_color( (string) $input['gdpr_accept_btn_color'] ); 245 $sanitized['gdpr_accept_btn_color'] = $accept_color ? $accept_color : '#ffffff'; 246 } 247 if ( array_key_exists( 'gdpr_accept_btn_hover_bg', $input ) ) { 248 $accept_hover = sanitize_hex_color( (string) $input['gdpr_accept_btn_hover_bg'] ); 249 $sanitized['gdpr_accept_btn_hover_bg'] = $accept_hover ? $accept_hover : '#22c55e'; 250 } 251 if ( array_key_exists( 'gdpr_decline_btn_bg', $input ) ) { 252 $decline_bg = sanitize_hex_color( (string) $input['gdpr_decline_btn_bg'] ); 253 $sanitized['gdpr_decline_btn_bg'] = $decline_bg ? $decline_bg : '#e74c3c'; 254 } 255 if ( array_key_exists( 'gdpr_decline_btn_color', $input ) ) { 256 $decline_color = sanitize_hex_color( (string) $input['gdpr_decline_btn_color'] ); 257 $sanitized['gdpr_decline_btn_color'] = $decline_color ? $decline_color : '#ffffff'; 258 } 259 if ( array_key_exists( 'gdpr_decline_btn_hover_bg', $input ) ) { 260 $decline_hover = sanitize_hex_color( (string) $input['gdpr_decline_btn_hover_bg'] ); 261 $sanitized['gdpr_decline_btn_hover_bg'] = $decline_hover ? $decline_hover : '#dc2626'; 262 } 263 if ( array_key_exists( 'gdpr_btn_border_radius', $input ) ) { 264 $btn_border_radius = sanitize_text_field( (string) $input['gdpr_btn_border_radius'] ); 265 // Validar formato CSS (px, em, rem, %) 266 if ( preg_match( '/^\d+(\.\d+)?(px|em|rem|%)$/', $btn_border_radius ) ) { 267 $sanitized['gdpr_btn_border_radius'] = $btn_border_radius; 268 } else { 269 $sanitized['gdpr_btn_border_radius'] = '6px'; 270 } 208 271 } 209 272 … … 829 892 echo '</div>'; 830 893 echo '</div>'; 894 895 // Estilos del modal GDPR 896 echo '<div class="mcnb-field-row">'; 897 echo '<div class="mcnb-field-col-full">'; 898 echo '<h4 style="margin: 20px 0 10px 0; color: #333; border-top: 1px solid #ddd; padding-top: 15px;">' . esc_html__( 'Estilos del modal', 'madnesschat-button' ) . '</h4>'; 899 echo '</div>'; 900 echo '</div>'; 901 902 // Color de fondo del modal (overlay) 903 echo '<div class="mcnb-field-row">'; 904 echo '<div class="mcnb-field-col">'; 905 echo '<label class="mcnb-field-label">' . esc_html__( 'Color de fondo del modal', 'madnesschat-button' ) . '</label>'; 906 echo '<input type="text" name="mcnb_basic_options[gdpr_modal_bg_color]" value="' . esc_attr( $options['gdpr_modal_bg_color'] ?? 'rgba(0,0,0,0.7)' ) . '" class="mcnb-color" />'; 907 echo '<p class="mcnb-field-hint">' . esc_html__( 'Color del overlay de fondo', 'madnesschat-button' ) . '</p>'; 908 echo '</div>'; 909 // Color de fondo de la card 910 echo '<div class="mcnb-field-col">'; 911 echo '<label class="mcnb-field-label">' . esc_html__( 'Color de fondo de la card', 'madnesschat-button' ) . '</label>'; 912 echo '<input type="text" name="mcnb_basic_options[gdpr_modal_content_bg]" value="' . esc_attr( $options['gdpr_modal_content_bg'] ?? '#ffffff' ) . '" class="mcnb-color" />'; 913 echo '<p class="mcnb-field-hint">' . esc_html__( 'Color de fondo del contenido', 'madnesschat-button' ) . '</p>'; 914 echo '</div>'; 915 echo '</div>'; 916 917 // Color del título y border radius 918 echo '<div class="mcnb-field-row">'; 919 echo '<div class="mcnb-field-col">'; 920 echo '<label class="mcnb-field-label">' . esc_html__( 'Color del título', 'madnesschat-button' ) . '</label>'; 921 echo '<input type="text" name="mcnb_basic_options[gdpr_modal_title_color]" value="' . esc_attr( $options['gdpr_modal_title_color'] ?? '#2c3e50' ) . '" class="mcnb-color" />'; 922 echo '</div>'; 923 echo '<div class="mcnb-field-col">'; 924 echo '<label class="mcnb-field-label">' . esc_html__( 'Border radius del modal', 'madnesschat-button' ) . '</label>'; 925 echo '<input type="text" name="mcnb_basic_options[gdpr_modal_border_radius]" value="' . esc_attr( $options['gdpr_modal_border_radius'] ?? '12px' ) . '" class="regular-text" placeholder="12px" />'; 926 echo '</div>'; 927 echo '</div>'; 928 929 // Botón Aceptar 930 echo '<div class="mcnb-field-row">'; 931 echo '<div class="mcnb-field-col-full">'; 932 echo '<h5 style="margin: 15px 0 10px 0; color: #555;">' . esc_html__( 'Botón Aceptar', 'madnesschat-button' ) . '</h5>'; 933 echo '</div>'; 934 echo '</div>'; 935 echo '<div class="mcnb-field-row">'; 936 echo '<div class="mcnb-field-col">'; 937 echo '<label class="mcnb-field-label">' . esc_html__( 'Color de fondo', 'madnesschat-button' ) . '</label>'; 938 echo '<input type="text" name="mcnb_basic_options[gdpr_accept_btn_bg]" value="' . esc_attr( $options['gdpr_accept_btn_bg'] ?? '' ) . '" class="mcnb-color" />'; 939 echo '<p class="mcnb-field-hint">' . esc_html__( 'Dejar vacío para usar el color del botón principal', 'madnesschat-button' ) . '</p>'; 940 echo '</div>'; 941 echo '<div class="mcnb-field-col">'; 942 echo '<label class="mcnb-field-label">' . esc_html__( 'Color del texto', 'madnesschat-button' ) . '</label>'; 943 echo '<input type="text" name="mcnb_basic_options[gdpr_accept_btn_color]" value="' . esc_attr( $options['gdpr_accept_btn_color'] ?? '#ffffff' ) . '" class="mcnb-color" />'; 944 echo '</div>'; 945 echo '<div class="mcnb-field-col">'; 946 echo '<label class="mcnb-field-label">' . esc_html__( 'Color hover', 'madnesschat-button' ) . '</label>'; 947 echo '<input type="text" name="mcnb_basic_options[gdpr_accept_btn_hover_bg]" value="' . esc_attr( $options['gdpr_accept_btn_hover_bg'] ?? '#22c55e' ) . '" class="mcnb-color" />'; 948 echo '</div>'; 949 echo '</div>'; 950 951 // Botón Cancelar/Declinar 952 echo '<div class="mcnb-field-row">'; 953 echo '<div class="mcnb-field-col-full">'; 954 echo '<h5 style="margin: 15px 0 10px 0; color: #555;">' . esc_html__( 'Botón Cancelar', 'madnesschat-button' ) . '</h5>'; 955 echo '</div>'; 956 echo '</div>'; 957 echo '<div class="mcnb-field-row">'; 958 echo '<div class="mcnb-field-col">'; 959 echo '<label class="mcnb-field-label">' . esc_html__( 'Color de fondo', 'madnesschat-button' ) . '</label>'; 960 echo '<input type="text" name="mcnb_basic_options[gdpr_decline_btn_bg]" value="' . esc_attr( $options['gdpr_decline_btn_bg'] ?? '#e74c3c' ) . '" class="mcnb-color" />'; 961 echo '</div>'; 962 echo '<div class="mcnb-field-col">'; 963 echo '<label class="mcnb-field-label">' . esc_html__( 'Color del texto', 'madnesschat-button' ) . '</label>'; 964 echo '<input type="text" name="mcnb_basic_options[gdpr_decline_btn_color]" value="' . esc_attr( $options['gdpr_decline_btn_color'] ?? '#ffffff' ) . '" class="mcnb-color" />'; 965 echo '</div>'; 966 echo '<div class="mcnb-field-col">'; 967 echo '<label class="mcnb-field-label">' . esc_html__( 'Color hover', 'madnesschat-button' ) . '</label>'; 968 echo '<input type="text" name="mcnb_basic_options[gdpr_decline_btn_hover_bg]" value="' . esc_attr( $options['gdpr_decline_btn_hover_bg'] ?? '#dc2626' ) . '" class="mcnb-color" />'; 969 echo '</div>'; 970 echo '</div>'; 971 972 // Border radius de botones 973 echo '<div class="mcnb-field-row">'; 974 echo '<div class="mcnb-field-col">'; 975 echo '<label class="mcnb-field-label">' . esc_html__( 'Border radius de botones', 'madnesschat-button' ) . '</label>'; 976 echo '<input type="text" name="mcnb_basic_options[gdpr_btn_border_radius]" value="' . esc_attr( $options['gdpr_btn_border_radius'] ?? '6px' ) . '" class="regular-text" placeholder="6px" />'; 977 echo '</div>'; 978 echo '</div>'; 979 831 980 echo '</div>'; 832 981 }, 'mcnb_section_privacy', 'mcnb_privacy' ); -
madnesschat-button/tags/1.0.2/madnesschat-button.php
r3406405 r3411134 3 3 * Plugin Name: Floating Contact Button 4 4 * Description: Easy-to-configure floating WhatsApp button with GDPR compliance, basic analytics, and smart triggers. 5 * Version: 1.0. 65 * Version: 1.0.7 6 6 * Author: madnesscode1 7 7 * Requires at least: 5.6 … … 39 39 } 40 40 41 define( 'MCNB_VERSION', '1.0. 6' );41 define( 'MCNB_VERSION', '1.0.7' ); 42 42 define( 'MCNB_SLUG', 'madnesschat-button' ); 43 43 define( 'MCNB_FILE', __FILE__ ); … … 91 91 'gdpr_message' => __( 'Usamos chat para atenderte. ¿Aceptas continuar?', 'madnesschat-button' ), 92 92 'gdpr_button_label' => __( 'Aceptar y continuar', 'madnesschat-button' ), 93 'gdpr_modal_bg_color' => 'rgba(0,0,0,0.7)', 94 'gdpr_modal_content_bg' => '#ffffff', 95 'gdpr_modal_border_radius' => '12px', 96 'gdpr_modal_title_color' => '#2c3e50', 97 'gdpr_accept_btn_bg' => '', 98 'gdpr_accept_btn_color' => '#ffffff', 99 'gdpr_accept_btn_hover_bg' => '#22c55e', 100 'gdpr_decline_btn_bg' => '#e74c3c', 101 'gdpr_decline_btn_color' => '#ffffff', 102 'gdpr_decline_btn_hover_bg' => '#dc2626', 103 'gdpr_btn_border_radius' => '6px', 93 104 'analytics_enabled' => true, 94 105 'utm_enabled' => true, … … 120 131 require_once MCNB_DIR . 'includes/class-mcnb-settings.php'; 121 132 require_once MCNB_DIR . 'includes/class-mcnb-security.php'; 133 require_once MCNB_DIR . 'includes/class-mcnb-seo.php'; 122 134 // require_once MCNB_DIR . 'includes/class-mcnb-license.php'; // Removido - venta directa sin licencias 123 135 require_once MCNB_DIR . 'includes/class-mcnb-analytics.php'; … … 156 168 // Frontend siempre se carga 157 169 new MCNB_Frontend(); 170 171 // SEO features (schema markup, OG tags) 172 new MCNB_SEO(); 158 173 } ); 159 174 -
madnesschat-button/tags/1.0.2/readme.txt
r3406409 r3411134 5 5 Tested up to: 6.9 6 6 Requires PHP: 7.3 7 Stable tag: 1.0. 67 Stable tag: 1.0.7 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 167 167 168 168 == Changelog == 169 170 = 1.0.7 = 171 * Added customizable styling options for GDPR modal (background colors, button colors, border radius). 172 * Fixed undefined array key warnings in frontend class. 173 * SEO optimization: Changed tag from "widget" to "floating" for better discoverability. 169 174 170 175 = 1.0.6 = … … 212 217 == Upgrade Notice == 213 218 219 = 1.0.7 = 220 New GDPR modal styling options: customize colors, buttons, and border radius for the consent modal. Fixed warnings and improved SEO. Recommended update for all users. 221 214 222 = 1.0.6 = 215 223 Plugin rebranding and visual improvements: renamed to "Floating Contact Button" for WordPress.org compliance, enhanced dashboard visibility, and added frontend screenshots. Recommended update for all users.
Note: See TracChangeset
for help on using the changeset viewer.