1jQuery(document).ready(function($) { 2 'use strict'; 3 4 // Variables 5 let otpVerified = false; 6 let currentMobile = ''; 7 8 // Send OTP button click 9 $('#send-otp-btn').on('click', function(e) { 10 e.preventDefault(); 11 12 const mobile = $('#mobile').val().trim(); 13 const $mobileField = $('#mobile'); 14 const $mobileGroup = $mobileField.closest('.form-group'); 15 16 // Clear previous error 17 $mobileField.removeClass('error'); 18 $mobileGroup.find('.field-error').remove(); 19 20 if (!mobile) { 21 $mobileField.addClass('error'); 22 $mobileGroup.append('<div class="field-error">Please enter your mobile number.</div>'); 23 return; 24 } 25 26 // Validate Malaysian mobile number format (9-11 digits without +60) 27 if (!/^[0-9]{9,11}$/.test(mobile)) { 28 $mobileField.addClass('error'); 29 $mobileGroup.append('<div class="field-error">Please enter a valid 9-11 digit mobile number (e.g., 123456789).</div>'); 30 return; 31 } 32 33 // Format full mobile number with +60 prefix for backend 34 // Remove leading 0 if present (Malaysian numbers don't have leading 0 after +60) 35 let formattedMobile = mobile; 36 if (mobile.startsWith('0')) { 37 formattedMobile = mobile.substring(1); 38 console.log('HMFL: Removed leading 0 from mobile number:', mobile, '->', formattedMobile); 39 } 40 const fullMobileNumber = '+60' + formattedMobile; 41 42 // Debug logging 43 console.log('HMFL: Original mobile input:', mobile); 44 console.log('HMFL: Formatted mobile for backend:', fullMobileNumber); 45 console.log('HMFL: Mobile number validation passed'); 46 47 // Disable button and show loading text (without transparent effect) 48 const $btn = $(this); 49 const originalText = $btn.text(); 50 $btn.prop('disabled', true).text('Sending...').addClass('sending'); 51 52 // Send AJAX request 53 console.log('HMFL: Sending AJAX request with mobile:', fullMobileNumber); 54 $.ajax({ 55 url: hmfl_ajax.ajax_url, 56 type: 'POST', 57 data: { 58 action: 'hmfl_send_otp', 59 mobile: fullMobileNumber, 60 nonce: hmfl_ajax.nonce 61 }, 62 success: function(response) { 63 console.log('HMFL: AJAX response:', response); 64 if (response.success) { 65 // Show success message below mobile field 66 const $mobileField = $('#mobile'); 67 const $mobileGroup = $mobileField.closest('.form-group'); 68 69 // Clear any previous error 70 $mobileField.removeClass('error'); 71 $mobileGroup.find('.field-error').remove(); 72 73 // Add success message 74 $mobileGroup.append('<div class="field-success">OTP sent successfully!</div>'); 75 76 currentMobile = fullMobileNumber; 77 $('.otp-section').show(); 78 $('#mobile').prop('readonly', true); 79 $('#send-otp-btn').hide(); 80 startOtpTimer(); 81 } else { 82 console.log('HMFL: AJAX error:', response.data.message); 83 // Show error message directly below mobile field 84 const $mobileField = $('#mobile'); 85 const $mobileGroup = $mobileField.closest('.form-group'); 86 87 // Clear any previous success message 88 $mobileField.removeClass('error'); 89 $mobileGroup.find('.field-success').remove(); 90 91 // Add error message below mobile field 92 $mobileGroup.append('<div class="field-error">' + response.data.message + '</div>'); 93 94 // Reset button state 95 $btn.prop('disabled', false).text(originalText).removeClass('sending'); 96 } 97 }, 98 error: function(xhr, status, error) { 99 console.log('HMFL: AJAX error - Status:', status, 'Error:', error, 'Response:', xhr.responseText); 100 // Try to parse error response 101 let errorMessage = hmfl_ajax.messages.error; 102 try { 103 const response = JSON.parse(xhr.responseText); 104 if (response.data && response.data.message) { 105 errorMessage = response.data.message; 106 } 107 } catch (e) { 108 console.log('HMFL: Could not parse error response'); 109 } 110 111 // Show error message directly below mobile field 112 const $mobileField = $('#mobile'); 113 const $mobileGroup = $mobileField.closest('.form-group'); 114 115 // Clear any previous success message 116 $mobileField.removeClass('error'); 117 $mobileGroup.find('.field-success').remove(); 118 119 // Add error message below mobile field 120 $mobileGroup.append('<div class="field-error">' + errorMessage + '</div>'); 121 122 $btn.prop('disabled', false).text(originalText).removeClass('sending'); 123 } 124 }); 125 }); 126 127 // Verify OTP button click 128 $('#verify-otp-btn').on('click', function(e) { 129 e.preventDefault(); 130 131 const otp = $('#otp_code').val().trim(); 132 const $otpField = $('#otp_code'); 133 const $otpGroup = $otpField.closest('.form-group'); 134 135 // Clear previous error 136 $otpField.removeClass('error'); 137 $otpGroup.find('.field-error').remove(); 138 139 if (!otp) { 140 $otpField.addClass('error'); 141 $otpGroup.append('<div class="field-error">Please enter the OTP code.</div>'); 142 return; 143 } 144 145 if (!/^[0-9]{6}$/.test(otp)) { 146 $otpField.addClass('error'); 147 $otpGroup.append('<div class="field-error">
147Please enter a valid 6-digit OTP code.</div>'); 148 return; 149 } 150 151 // Disable button and show loading text (without transparent effect) 152 const $btn = $(this); 153 const originalText = $btn.text(); 154 $btn.prop('disabled', true).text('Verifying...').addClass('verifying'); 155 156 // Send AJAX request 157 $.ajax({ 158 url: hmfl_ajax.ajax_url, 159 type: 'POST', 160 data: { 161 action: 'hmfl_verify_otp', 162 mobile: currentMobile, 163 otp: otp, 164 nonce: hmfl_ajax.nonce 165 }, 166 success: function(response) { 167 if (response.success) { 168 // Show success message below OTP field 169 const $otpField = $('#otp_code'); 170 const $otpGroup = $otpField.closest('.form-group'); 171 172 // Clear any previous error 173 $otpField.removeClass('error'); 174 $otpGroup.find('.field-error').remove(); 175 176 // Add success message 177 $otpGroup.append('<div class="field-success">OTP verified successfully!</div>'); 178 179 otpVerified = true; 180 $('#otp_code').prop('readonly', true); 181 // Keep button disabled and show "Verified" with checkmark 182 $btn.prop('disabled', true).text('â Verified').removeClass('verifying').addClass('verified'); 183 checkFormValidity(); 184 } else { 185 showMessage(response.data.message, 'error'); 186 // Reset button on error 187 $btn.prop('disabled', false).text(originalText).removeClass('verifying'); 188 } 189 }, 190 error: function(xhr, status, error) { 191 console.log('HMFL: OTP verification AJAX error - Status:', status, 'Error:', error, 'Response:', xhr.responseText); 192 // Try to parse error response 193 let errorMessage = hmfl_ajax.messages.error; 194 try { 195 const response = JSON.parse(xhr.responseText); 196 if (response.data && response.data.message) { 197 errorMessage = response.data.message; 198 } 199 } catch (e) { 200 console.log('HMFL: Could not parse OTP verification error response'); 201 } 202 showMessage(errorMessage, 'error'); 203 // Reset button on error 204 $btn.prop('disabled', false).text(originalText).removeClass('verifying'); 205 } 206 }); 207 }); 208 209 // Form submission 210 $('#hmfl-registration-form').on('submit', function(e) { 211 e.preventDefault(); 212 213 // Validate form with field-specific errors 214 if (!validateForm()) { 215 return; 216 } 217 218 // Disable submit button 219 const $submitBtn = $('#submit-registration'); 220 const originalText = $submitBtn.text(); 221 $submitBtn.prop('disabled', true).text('Submitting...').addClass('submitting'); 222 223 // Collect form data 224 const formData = { 225 action: 'hmfl_submit_registration', 226 nonce: hmfl_ajax.nonce, 227 full_name: $('#full_name').val().trim(), 228 gender: $('#gender').val(), 229 year_of_birth: $('#year_of_birth').val(), 230 state: $('#state').val(), 231 email: $('#email').val().trim(), 232 mobile: currentMobile, 233 fpl_team_name: $('#fpl_team_name').val().trim(), 234 consent_1: $('#consent_1').is(':checked'), 235 consent_2: $('#consent_2').is(':checked'), 236 consent_3: $('#consent_3').is(':checked') 237 }; 238 239 // Send AJAX request 240 $.ajax({ 241 url: hmfl_ajax.ajax_url, 242 type: 'POST', 243 data: formData, 244 success: function(response) { 245 if (response.success) {
246 // Reset submit button to normal state immediately 247 $submitBtn.prop('disabled', false).text(originalText).removeClass('submitting'); 248 249 // Show success message with better styling 250 const $messages = $('#hmfl-messages'); 251 $messages.html('<div class="message success">' + response.data.message + '<br><small>Page will reload in 3 seconds...</small></div>'); 252 253 // Reset form completely 254 $('#hmfl-registration-form')[0].reset(); 255 resetForm(); 256 257 // Reload page after 3 seconds to show fresh form 258 setTimeout(function() { 259 window.location.reload(); 260 }, 3000); 261 } else { 262 // Check if it's a mobile-related error 263 const errorMessage = response.data.message; 264 if (errorMessage.includes('mobile') || errorMessage.includes('Mobile')) { 265 // Show mobile-related errors below mobile field 266 const $mobileField = $('#mobile'); 267 const $mobileGroup = $mobileField.closest('.form-group'); 268 269 // Clear any previous messages 270 $mobileField.removeClass('error'); 271 $mobileGroup.find('.field-error, .field-success').remove(); 272 273 // Add error message below mobile field 274 $mobileGroup.append('<div class="field-error">' + errorMessage + '</div>'); 275 } else { 276 // Show other errors in the general message area 277 showMessage(errorMessage, 'error'); 278 } 279 $submitBtn.prop('disabled', false).text(originalText).removeClass('submitting'); 280 } 281 }, 282 error: function(xhr, status, error) { 283 console.log('HMFL: Registration AJAX error - Status:', status, 'Error:', error, 'Response:', xhr.responseText); 284 // Try to parse error response 285 let errorMessage = hmfl_ajax.messages.error; 286 try { 287 const response = JSON.parse(xhr.responseText); 288 if (response.data && response.data.message) { 289 errorMessage = response.data.message; 290 } 291 } catch (e) { 292 console.log('HMFL: Could not parse registration error response'); 293 } 294 295 // Check if it's a mobile-related error 296 if (errorMessage.includes('mobile') || errorMessage.includes('Mobile')) { 297 // Show mobile-related errors below mobile field 298 const $mobileField = $('#mobile'); 299 const $mobileGroup = $mobileField.closest('.form-group'); 300 301 // Clear any previous messages 302 $mobileField.removeClass('error'); 303 $mobileGroup.find('.field-error, .field-success').remove(); 304 305 // Add error message below mobile field 306 $mobileGroup.append('<div class="field-error">' + errorMessage + '</div>'); 307 } else { 308 // Show other errors in the general message area 309 showMessage(errorMessage, 'error'); 310 } 311 $submitBtn.prop('disabled', false).text(originalText).removeClass('submitting'); 312 } 313 }); 314 }); 315 316 // Real-time form validation 317 $('#hmfl-registration-form input, #hmfl-registration-form select').on('change keyup', function() { 318 const $field = $(this); 319 const $fieldGroup = $field.closest('.form-group'); 320 321 // Remove existing error for this field 322 $field.removeClass('error'); 323 $fieldGroup.find('.field-error').remove(); 324 325 // Validate specific field 326 if ($field.attr('id') === 'email') { 327 const email = $field.val(); 328 if (email && !isValidEmail(email)) { 329 $field.addClass('error'); 330 $fieldGroup.append('<div class="field-error">Please enter a valid email address.</div>'); 331 } 332 } else if ($field.attr('id') === 'mobile') { 333 const mobile = $field.val().trim(); 334 if (mobile && !/^[0-9]{9,11}$/.test(mobile)) { 335 $field.addClass('error'); 336 $fieldGroup.append('<div class="field-error">Please enter a valid 9-11 digit mobile number (e.g., 123456789).</div>'); 337 } 338 } else if ($field.attr('id') === 'otp_code') { 339 if (!otpVerified && $field.val()) { 340 $field.addClass('error'); 341 $fieldGroup.append('<div class="field-error">Please verify your mobile number with OTP.</div>'); 342 } 343 } 344 345 checkFormValidity(); 346 }); 347 348 // Checkboxes change 349 $('#consent_1, #consent_2, #consent_3').on('change', function() { 350 const $consent1 = $('#consent_1'); 351 const $consent2 = $('#consent_2'); 352 const $consent3 = $('#consent_3'); 353 const $consentGroup = $consent1.closest('.form-group'); 354 355 // Remove existing error 356 $consent1.removeClass('error'); 357 $consent2.removeClass('error'); 358 $consent3.removeClass('error'); 359 $consentGroup.find('.field-error').remove(); 360 361 // Check if all three consents are checked 362 if (!$consent1.is(':checked') || !$consent2.is(':checked') || !$consent3.is(':checked')) { 363 $consent1.addClass('error'); 364 $consent2.addClass('error'); 365 $consent3.addClass('error'); 366 $consentGroup.append('<div class="field-error">Please agree to all consent terms.</div>'); 367 } 368 369 checkFormValidity(); 370 }); 371 372 // Mobile number input validation 373 $('#mobile').on('input', function() { 374 const value = $(this).val(); 375 // Only allow numbers 376 $(this).val(value.replace(/[^0-9]/g, '')); 377 378 // Enable/disable send OTP button 379 if (value.length >= 9) { 380 $('#send-otp-btn').prop('disabled', false); 381 } else { 382 $('#send-otp-btn').prop('disabled', true); 383 } 384 }); 385 386 // OTP input validation 387 $('#otp_code').on('input', function() { 388 const value = $(this).val(); 389 // Only allow numbers 390 $(this).val(value.replace(/[^0-9]/g, '')); 391 }); 392 393 // Functions 394 function validateForm() { 395 const requiredFields = ['#full_name', '#gender', '#year_of_birth', '#state', '#email', '#fpl_team_name']; 396 let isValid = true; 397 398 // Clear all previous error messages 399 $('.field-error').remove(); 400 401 // Check required fields 402 requiredFields.forEach(function(field) { 403 const $field = $(field); 404 const $fieldGroup = $field.closest('.form-group'); 405 406 if (!$field.val()) { 407 $field.addClass('error'); 408 $fieldGroup.append('<div class="field-error">This field is required.</div>'); 409 isValid = false;
410 } else { 411 $field.removeClass('error'); 412 } 413 }); 414 415 // Check email format 416 const email = $('#email').val(); 417 const $emailField = $('#email'); 418 const $emailGroup = $emailField.closest('.form-group'); 419 420 if (email && !isValidEmail(email)) { 421 $emailField.addClass('error'); 422 $emailGroup.append('<div class="field-error">Please enter a valid email address.</div>'); 423 isValid = false; 424 } else if (email) { 425 $emailField.removeClass('error'); 426 } 427 428 // Check mobile number 429 const mobile = $('#mobile').val().trim(); 430 const $mobileField = $('#mobile'); 431 const $mobileGroup = $mobileField.closest('.form-group'); 432 433 if (!mobile) { 434 $mobileField.addClass('error'); 435 $mobileGroup.append('<div class="field-error">Please enter your mobile number.</div>'); 436 isValid = false; 437 } else if (!/^[0-9]{9,11}$/.test(mobile)) { 438 $mobileField.addClass('error'); 439 $mobileGroup.append('<div class="field-error">Please enter a valid 9-11 digit mobile number (e.g., 123456789).</div>'); 440 isValid = false; 441 } else { 442 $mobileField.removeClass('error'); 443 } 444 445 // Check OTP verification 446 if (!otpVerified) { 447 const $otpField = $('#otp_code'); 448 const $otpGroup = $otpField.closest('.form-group'); 449 $otpField.addClass('error'); 450 $otpGroup.append('<div class="field-error">Please verify your mobile number with OTP.</div>'); 451 isValid = false; 452 } 453 454 // Check consents 455 const $consent1 = $('#consent_1'); 456 const $consent2 = $('#consent_2'); 457 const $consent3 = $('#consent_3'); 458 const $consentGroup = $consent1.closest('.form-group'); 459 460 if (!$consent1.is(':checked') || !$consent2.is(':checked') || !$consent3.is(':checked')) { 461 $consent1.addClass('error'); 462 $consent2.addClass('error'); 463 $consent3.addClass('error'); 464 $consentGroup.append('<div class="field-error">Please agree to all consent terms.</div>'); 465 isValid = false; 466 } else { 467 $consent1.removeClass('error'); 468 $consent2.removeClass('error'); 469 $consent3.removeClass('error'); 470 } 471 472 return isValid; 473 } 474 475 function checkFormValidity() { 476 const requiredFields = ['#full_name', '#gender', '#year_of_birth', '#state', '#email', '#fpl_team_name']; 477 let allFieldsFilled = true; 478 479 requiredFields.forEach(function(field) { 480 if (!$(field).val()) { 481 allFieldsFilled = false; 482 } 483 }); 484 485 const email = $('#email').val(); 486 const emailValid = email && isValidEmail(email); 487 const consentsChecked = $('#consent_1').is(':checked') && $('#consent_2').is(':checked') && $('#consent_3').is(':checked'); 488 489 if (allFieldsFilled && emailValid && consentsChecked && otpVerified) { 490 $('#submit-registration').prop('disabled', false); 491 } else { 492 $('#submit-registration').prop('disabled', true); 493 } 494 } 495 496 function isValidEmail(email) { 497 const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; 498 return emailRegex.test(email); 499 } 500 501 function showMessage(message, type) { 502 const $messages = $('#hmfl-messages'); 503 const messageClass = type === 'success' ? 'success' : 'error'; 504 505 // Clear any existing messages first 506 $messages.empty(); 507 508 // Create the message element 509 const $messageElement = $('<div class="message ' + messageClass + '">' + message + '</div>'); 510 $messages.append($messageElement); 511 512 // For error messages, don't auto-hide them - let user dismiss manually 513 if (type === 'error') { 514 // Add a close button for error messages 515 $messageElement.append('<button type="button" class="message-close" style="float: right; background: none; border: none; font-size: 18px; cursor: pointer; color: inherit;">×</button>'); 516 517 // Handle close button click 518 $messageElement.find('.message-close').on('click', function() { 519 $messageElement.fadeOut(); 520 }); 521
522 // Auto-hide after 10 seconds instead of 5 for errors 523 setTimeout(function() { 524 if ($messageElement.is(':visible')) { 525 $messageElement.fadeOut(); 526 } 527 }, 10000); 528 } else { 529 // Auto-hide success messages after 5 seconds 530 setTimeout(function() { 531 if ($messageElement.is(':visible')) { 532 $messageElement.fadeOut(); 533 } 534 }, 5000); 535 } 536 537 // Scroll to message if it's not visible 538 $('html, body').animate({ 539 scrollTop: $messages.offset().top - 100 540 }, 500); 541 } 542 543 function resetForm() { 544 otpVerified = false; 545 currentMobile = ''; 546 $('.otp-section').hide(); 547 $('#mobile').prop('readonly', false).val(''); 548 $('#send-otp-btn').show().prop('disabled', true).text('Send OTP').removeClass('sending verifying'); 549 $('#otp_code').prop('readonly', false).val(''); 550 $('#verify-otp-btn').prop('disabled', false).text('Verify OTP').removeClass('verified verifying'); 551 $('#submit-registration').prop('disabled', true).text('Submit Registration').removeClass('submitting'); 552 $('.form-group').removeClass('error'); 553 $('.field-error, .field-success').remove(); 554 555 // Clear any existing timers 556 if (window.otpTimer) { 557 clearInterval(window.otpTimer); 558 window.otpTimer = null; 559 } 560 } 561 562 function startOtpTimer() { 563 let timeLeft = 300; // 5 minutes in seconds 564 const $btn = $('#send-otp-btn'); 565 566 // Clear any existing timer 567 if (window.otpTimer) { 568 clearInterval(window.otpTimer); 569 } 570 571 window.otpTimer = setInterval(function() { 572 const minutes = Math.floor(timeLeft / 60); 573 const seconds = timeLeft % 60; 574 575 if (timeLeft <= 0) { 576 clearInterval(window.otpTimer); 577 window.otpTimer = null; 578 showMessage('OTP has expired. Please request a new one.', 'error'); 579 resetOtpSection(); 580 } else { 581 $btn.text('Resend OTP (' + minutes + ':' + (seconds < 10 ? '0' : '') + seconds + ')'); 582 } 583 584 timeLeft--; 585 }, 1000); 586 } 587 588 function resetOtpSection() { 589 $('.otp-section').hide(); 590 $('#mobile').prop('readonly', false); 591 $('#send-otp-btn').show().prop('disabled', false).text('Send OTP').removeClass('sending'); 592 $('#otp_code').val(''); 593 $('#verify-otp-btn').prop('disabled', false).text('Verify OTP').removeClass('verified verifying'); 594 $('.field-error, .field-success').remove(); 595 otpVerified = false; 596 currentMobile = ''; 597 598 // Clear any existing timer 599 if (window.otpTimer) { 600 clearInterval(window.otpTimer); 601 window.otpTimer = null; 602 } 603 } 604});
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.