1/** 2 * JavaScript para formularios de newsletter 3 * Compatible con la estructura HTML existente del tema 4 * 5 * @package MailchimpNewsletterManager 6 * @since 1.0.0 7 */ 8 9(function($) { 10 'use strict'; 11 12 /** 13 * Clase principal para manejo de formularios de newsletter 14 */ 15 class NewsletterForm { 16 constructor(form) { 17 this.form = $(form); 18 this.emailInput = this.form.find('.newsletter_email'); 19 this.submitButton = this.form.find('.newsletter_submit'); 20 this.errorDiv = this.form.find('.newsletter_error'); 21 this.thanksDiv = this.form.find('.newsletter_thanks_wrapper'); 22 this.formInputs = this.form.find('.newsletter_form_inputs'); 23 24 // Verificar que los elementos existen 25 if (!this.errorDiv.length) { 26 this.errorDiv = $('<div class="newsletter_error" style="margin-top: 15px; min-height: 50px; opacity: 0;"></div>'); 27 this.form.append(this.errorDiv); 28 } 29 if (!this.thanksDiv.length) { 30 this.thanksDiv = $('<div class="newsletter_thanks_wrapper" style="margin-top: 15px; min-height: 50px; opacity: 0;"></div>'); 31 this.form.append(this.thanksDiv); 32 } 33 34 this.init(); 35 } 36 37 init() { 38 this.bindEvents(); 39 this.setupValidation(); 40 } 41 42 bindEvents() { 43 // Validación en tiempo real del email 44 this.emailInput.on('input', () => { 45 this.validateEmail(); 46 }); 47 48 // EnvÃo del formulario 49 this.form.on('submit', (e) => { 50 e.preventDefault(); 51 this.handleSubmit(); 52 }); 53 54 // Limpiar mensajes al escribir 55 this.emailInput.on('input', () => { 56 this.hideMessages(); 57 }); 58 } 59 60 setupValidation() { 61 // Validación inicial 62 this.validateEmail(); 63 } 64 65 validateEmail() { 66 const email = this.emailInput.val(); 67 const isValid = this.isValidEmail(email); 68 69 if (email && !isValid) { 70 this.emailInput.addClass('error'); 71 this.submitButton.prop('disabled', true); 72 } else if (email && isValid) { 73 this.emailInput.removeClass('error'); 74 this.submitButton.prop('disabled', false); 75 } else { 76 this.emailInput.removeClass('error'); 77 this.submitButton.prop('disabled', true); 78 } 79 } 80 81 isValidEmail(email) { 82 const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; 83 return emailRegex.test(email); 84 } 85 86 handleSubmit() { 87 const email = this.emailInput.val(); 88 89 // Validación final 90 if (!email || !this.isValidEmail(email)) { 91 this.showError(mailchimpNewsletter.messages.invalid_email); 92 return; 93 } 94 95 // Mostrar estado de carga 96 this.setLoadingState(true); 97 this.hideMessages(); 98 99 // Preparar datos 100 const formData = { 101 action: 'newsletter_subscribe', 102 nonce: mailchimpNewsletter.nonce, 103 email: email, 104 source: 'wordpress-form' 105 }; 106 107 // Enviar petición AJAX 108 $.ajax({ 109 url: mailchimpNewsletter.ajaxUrl, 110 type: 'POST', 111 data: formData, 112 timeout: 30000, 113 success: (response) => { 114 this.handleResponse(response); 115 }, 116 error: (xhr, status, error) => { 117 this.handleError(status, error); 118 }, 119 complete: () => { 120 this.setLoadingState(false); 121 } 122 }); 123 } 124 125 handleResponse(response) { 126 console.log("=== HANDLERESPONSE EJECUTÃNDOSE ==="); 127 console.log("Response completa:", response); 128 console.log("Response.success:", response.success); 129 130 if (response.success) { 131 console.log("Llamando showSuccess()"); 132 this.showSuccess(); 133 this.resetForm(); 134 this.trackEvent('success', { 135 email: this.emailInput.val(), 136 source: 'wordpress-form' 137 }); 138 } else { 139 console.log("Llamando showError()"); 140 this.showError(response.data.message || mailchimpNewsletter.messages.error); 141 this.trackEvent('error', { 142 email: this.emailInput.val(), 143 error: response.data.message, 144 source: 'wordpress-form' 145 }); 146 } 147 } 148 149 handleError(status, error) { 150 let errorMessage = mailchimpNewsletter.messages.error; 151 152 if (status === 'timeout') { 153 errorMessage = 'La petición tardó demasiado. Inténtalo de nuevo.'; 154 } else if (status === 'abort') { 155 errorMessage = 'La petición fue cancelada.'; 156 } else if (error) { 157 errorMessage = `Error de red: ${error}`; 158 } 159 160 this.showError(errorMessage); 161 this.trackEvent('error', { 162 email: this.emailInput.val(), 163 error: errorMessage, 164 source: 'wordpress-form' 165 }); 166 } 167 168 showError(message) { 169 this.errorDiv.text(message).addClass("show"); 170 this.thanksDiv.removeClass("show"); 171 this.formInputs.addClass('error'); 172 173 // Scroll al error si es necesario 174 this.scrollToError(); 175 } 176 177 showSuccess() { 178 this.errorDiv.removeClass("show"); 179 this.thanksDiv.text(mailchimpNewsletter.messages.success || '¡Gracias por suscribirte!').addClass("show"); 180 this.formInputs.removeClass('error'); 181 } 182 183 hideMessages() { 184 this.errorDiv.removeClass("show"); 185 this.thanksDiv.removeClass("show"); 186 this.formInputs.removeClass('error'); 187 } 188
189 setLoadingState(loading) { 190 if (loading) { 191 this.submitButton 192 .prop('disabled', true) 193 .addClass('loading') 194 .val(mailchimpNewsletter.messages.loading); 195 } else { 196 this.submitButton 197 .prop('disabled', false) 198 .removeClass('loading') 199 .val('Subscribe'); // Texto original del botón 200 } 201 } 202 203 resetForm() { 204 this.form[0].reset(); 205 this.submitButton.prop('disabled', true); 206 this.hideMessages(); 207 } 208 209 scrollToError() { 210 if (this.errorDiv.length && this.errorDiv.is(':visible')) { 211 const errorTop = this.errorDiv.offset().top; 212 const windowTop = $(window).scrollTop(); 213 const windowHeight = $(window).height(); 214 215 if (errorTop < windowTop || errorTop > windowTop + windowHeight) { 216 $('html, body').animate({ 217 scrollTop: errorTop - 100 218 }, 300); 219 } 220 } 221 } 222 223 trackEvent(eventName, parameters = {}) { 224 // Google Analytics 4 225 if (typeof gtag !== 'undefined') { 226 gtag('event', `newsletter_${eventName}`, { 227 event_category: 'engagement', 228 event_label: parameters.source || 'wordpress-form', 229 value: parameters.email ? 1 : 0, 230 ...parameters 231 }); 232 } 233 234 // Facebook Pixel 235 if (typeof fbq !== 'undefined') { 236 if (eventName === 'success') { 237 fbq('track', 'CompleteRegistration', { 238 content_name: 'Newsletter Subscription', 239 content_category: 'Email Marketing', 240 ...parameters 241 }); 242 } else if (eventName === 'error') { 243 fbq('track', 'Lead', { 244 content_name: 'Newsletter Subscription Attempt', 245 content_category: 'Email Marketing', 246 ...parameters 247 }); 248 } 249 } 250 251 // Evento personalizado para otros sistemas de tracking 252 $(document).trigger('newsletter:event', [eventName, parameters]); 253 } 254 } 255 256 /** 257 * Clase para formularios minimalistas 258 */ 259 class MinimalNewsletterForm { 260 constructor(form) { 261 this.form = $(form); 262 this.emailInput = this.form.find('.newsletter_email_minimal'); 263 this.submitButton = this.form.find('.newsletter_submit_minimal'); 264 265 this.init(); 266 } 267 268 init() { 269 this.bindEvents(); 270 } 271 272 bindEvents() { 273 this.form.on('submit', (e) => { 274 e.preventDefault(); 275 this.handleSubmit(); 276 }); 277 } 278 279 handleSubmit() { 280 const email = this.emailInput.val(); 281 282 if (!email || !this.isValidEmail(email)) { 283 this.emailInput.focus(); 284 return; 285 } 286 287 this.setLoadingState(true); 288 289 $.ajax({ 290 url: mailchimpNewsletter.ajaxUrl, 291 type: 'POST', 292 data: { 293 action: 'newsletter_subscribe', 294 nonce: mailchimpNewsletter.nonce, 295 email: email, 296 source: 'minimal-form' 297 }, 298 success: (response) => { 299 if (response.success) { 300 this.showSuccess(); 301 this.resetForm(); 302 } else { 303 this.showError(response.data.message); 304 } 305 }, 306 error: () => { 307 this.showError('Error de conexión'); 308 }, 309 complete: () => { 310 this.setLoadingState(false); 311 } 312 }); 313 } 314 315 isValidEmail(email) { 316 const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; 317 return emailRegex.test(email); 318 } 319
320 setLoadingState(loading) { 321 if (loading) { 322 this.submitButton.prop('disabled', true).text('...'); 323 } else { 324 this.submitButton.prop('disabled', false).text('Subscribe'); 325 } 326 } 327 328 showSuccess() { 329 this.submitButton.text('â Subscribed!'); 330 setTimeout(() => { 331 this.submitButton.text('Subscribe'); 332 }, 3000); 333 } 334 335 showError(message) { 336 const originalText = this.submitButton.text(); 337 this.submitButton.text('Error'); 338 setTimeout(() => { 339 this.submitButton.text(originalText); 340 }, 2000); 341 } 342 343 resetForm() { 344 this.form[0].reset(); 345 } 346 } 347 348 /** 349 * Clase para popups de newsletter 350 */ 351 class NewsletterPopup { 352 constructor(popup) { 353 this.popup = $(popup); 354 this.overlay = this.popup.find('.newsletter_popup_overlay'); 355 this.closeBtn = this.popup.find('.newsletter_popup_close'); 356 this.form = this.popup.find('.newsletter_popup_form'); 357 this.emailInput = this.form.find('.newsletter_popup_email'); 358 this.nameInput = this.form.find('.newsletter_popup_name'); 359 this.submitButton = this.form.find('.newsletter_popup_submit'); 360 361 this.shown = false; 362 this.init(); 363 } 364 365 init() { 366 this.bindEvents(); 367 this.setupTriggers(); 368 } 369 370 bindEvents() { 371 // Cerrar popup 372 this.closeBtn.on('click', () => { 373 this.hide(); 374 }); 375 376 this.overlay.on('click', (e) => { 377 if (e.target === this.overlay[0]) { 378 this.hide(); 379 } 380 }); 381 382 // EnvÃo del formulario 383 this.form.on('submit', (e) => { 384 e.preventDefault(); 385 this.handleSubmit(); 386 }); 387 388 // Cerrar con Escape 389 $(document).on('keydown', (e) => { 390 if (e.key === 'Escape' && this.overlay.is(':visible')) { 391 this.hide(); 392 } 393 }); 394 } 395 396 setupTriggers() { 397 // Verificar si ya se mostró el popup 398 if (localStorage.getItem('newsletter_popup_shown') === 'true') { 399 return; 400 } 401 402 // Trigger por tiempo 403 const delay = parseInt(this.popup.data('delay')) || 5000; 404 setTimeout(() => { 405 this.show(); 406 }, delay); 407 408 // Trigger por scroll 409 const scrollPercent = parseInt(this.popup.data('scroll-percent')) || 50; 410 $(window).on('scroll', () => { 411 const scrollTop = $(window).scrollTop(); 412 const docHeight = $(document).height(); 413 const winHeight = $(window).height(); 414 const scrollPercentActual = (scrollTop / (docHeight - winHeight)) * 100; 415 416 if (scrollPercentActual >= scrollPercent) { 417 this.show(); 418 $(window).off('scroll'); 419 } 420 }); 421 422 // Trigger por exit intent 423 $(document).on('mouseleave', (e) => { 424 if (e.clientY <= 0) { 425 this.show(); 426 $(document).off('mouseleave'); 427 } 428 }); 429 } 430 431 show() { 432 if (this.shown) return; 433 434 this.overlay.fadeIn(300); 435 this.shown = true; 436 localStorage.setItem('newsletter_popup_shown', 'true'); 437 438 // Focus en el primer input 439 setTimeout(() => { 440 this.emailInput.focus(); 441 }, 350); 442 } 443 444 hide() { 445 this.overlay.fadeOut(300); 446 } 447 448 handleSubmit() { 449 const email = this.emailInput.val(); 450 const name = this.nameInput.length ? this.nameInput.val() : ''; 451 452 if (!email || !this.isValidEmail(email)) { 453 this.emailInput.focus(); 454 return; 455 } 456 457 this.setLoadingState(true); 458 459 $.ajax({ 460 url: mailchimpNewsletter.ajaxUrl, 461 type: 'POST', 462 data: { 463 action: 'newsletter_subscribe', 464 nonce: mailchimpNewsletter.nonce, 465 email: email, 466 name: name, 467 source: 'popup-form' 468 }, 469 success: (response) => { 470 if (response.success) { 471 this.showSuccess(); 472 setTimeout(() => { 473 this.hide(); 474 }, 2000); 475 } else { 476 this.showError(response.data.message); 477 } 478 }, 479 error: () => { 480 this.showError('Error de conexión'); 481 }, 482 complete: () => { 483 this.setLoadingState(false); 484 } 485 }); 486 } 487 488 isValidEmail(email) { 489 const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; 490 return emailRegex.test(email); 491 } 492
493 setLoadingState(loading) { 494 if (loading) { 495 this.submitButton.prop('disabled', true).text('Subscribing...'); 496 } else { 497 this.submitButton.prop('disabled', false).text('Subscribe'); 498 } 499 } 500 501 showSuccess() { 502 this.submitButton.text('â Subscribed!'); 503 } 504 505 showError(message) { 506 const originalText = this.submitButton.text(); 507 this.submitButton.text('Error'); 508 setTimeout(() => { 509 this.submitButton.text(originalText); 510 }, 2000); 511 } 512 } 513 514 /** 515 * Utilidades globales 516 */ 517 window.MailchimpNewsletter = { 518 // Suscribir programáticamente 519 subscribe: function(email, name = '', source = 'api') { 520 return $.ajax({ 521 url: mailchimpNewsletter.ajaxUrl, 522 type: 'POST', 523 data: { 524 action: 'newsletter_subscribe', 525 nonce: mailchimpNewsletter.nonce, 526 email: email, 527 name: name, 528 source: source 529 } 530 }); 531 }, 532 533 // Eventos personalizados 534 onSuccess: function(callback) { 535 $(document).on('newsletter:event', function(e, eventName, parameters) { 536 if (eventName === 'success') { 537 callback(parameters); 538 } 539 }); 540 }, 541 542 onError: function(callback) { 543 $(document).on('newsletter:event', function(e, eventName, parameters) { 544 if (eventName === 'error') { 545 callback(parameters); 546 } 547 }); 548 } 549 }; 550 551 /** 552 * Inicialización cuando el DOM está listo 553 */ 554 $(document).ready(function() { 555 // Inicializar formularios estándar 556 $('.newsletter_form').each(function() { 557 new NewsletterForm(this); 558 }); 559 560 // Inicializar formularios minimalistas 561 $('.newsletter_form_minimal').each(function() { 562 new MinimalNewsletterForm(this); 563 }); 564 565 // Inicializar popups 566 $('.newsletter_popup_overlay').each(function() { 567 new NewsletterPopup(this); 568 }); 569 570 // Rate limiting básico 571 let lastSubmission = 0; 572 const minInterval = 2000; // 2 segundos entre envÃos 573 574 $(document).on('submit', '.newsletter_form, .newsletter_form_minimal, .newsletter_popup_form', function(e) { 575 const now = Date.now(); 576 if (now - lastSubmission < minInterval) { 577 e.preventDefault(); 578 return false; 579 } 580 lastSubmission = now; 581 }); 582 }); 583 584 /** 585 * Manejo de errores globales 586 */ 587 window.addEventListener('error', function(e) { 588 console.error('Newsletter Form Error:', e.error); 589 }); 590 591 /** 592 * Manejo de promesas rechazadas 593 */ 594 window.addEventListener('unhandledrejection', function(e) { 595 console.error('Newsletter Form Promise Rejection:', e.reason); 596 }); 597 598})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.