PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.83
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.83
51.1.84 51.1.85 51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 51.1.46 All 39 releases
← All changes | includes/widgets/Login_Register_Form/script.js +915 -0 51.1.251.1.83 View file →
@@ -1,0 +1,915 @@
1 +(function($) {
2 + 'use strict';
3 +
4 + // Initialize when Elementor frontend is ready
5 + $(window).on('elementor/frontend/init', function() {
6 + elementorFrontend.hooks.addAction('frontend/element_ready/king-addons-login-register-form.default', function($scope) {
7 + initLoginRegisterForm($scope);
8 + });
9 + });
10 +
11 + // Initialize for non-Elementor pages
12 + $(document).ready(function() {
13 + if (typeof elementorFrontend === 'undefined') {
14 + $('.king-addons-login-register-form-wrapper').each(function() {
15 + initLoginRegisterForm($(this));
16 + });
17 + }
18 + });
19 +
20 + function initLoginRegisterForm($scope) {
21 + const $wrapper = $scope.find('.king-addons-login-register-form-wrapper');
22 + if (!$wrapper.length) return;
23 +
24 + const $loginForm = $wrapper.find('.king-addons-login-form');
25 + const $registerForm = $wrapper.find('.king-addons-register-form');
26 + const widgetId = $wrapper.data('widget-id');
27 + const isAjaxEnabled = $wrapper.data('ajax') === true;
28 +
29 + // Get widget settings from data attributes
30 + const widgetSettings = {
31 + recaptcha_score_threshold: $wrapper.data('recaptcha-threshold') || '0.5',
32 + redirect_after_login: $wrapper.data('redirect-login') || '',
33 + redirect_after_register: $wrapper.data('redirect-register') || '',
34 + terms_required: $wrapper.data('terms-required') || 'no',
35 + enable_user_email: $wrapper.data('enable-user-email') || 'yes',
36 + user_email_subject: $wrapper.data('user-email-subject') || '',
37 + user_email_content: $wrapper.data('user-email-content') || '',
38 + enable_admin_email: $wrapper.data('enable-admin-email') || 'no',
39 + admin_email_address: $wrapper.data('admin-email-address') || '',
40 + admin_email_subject: $wrapper.data('admin-email-subject') || '',
41 + admin_email_content: $wrapper.data('admin-email-content') || '',
42 + enable_mailchimp_integration: $wrapper.data('enable-mailchimp') || 'no',
43 + auto_login_after_register: $wrapper.data('auto-login') || 'yes'
44 + };
45 +
46 + // Form toggle functionality
47 + initFormToggle($wrapper, $loginForm, $registerForm);
48 +
49 + // AJAX form submission
50 + if (isAjaxEnabled) {
51 + const $lostPasswordForm = $wrapper.find('.king-addons-lost-password-form');
52 + initAjaxForms($wrapper, $loginForm, $registerForm, $lostPasswordForm, widgetId, widgetSettings);
53 + }
54 +
55 + // Form validation
56 + initFormValidation($loginForm, $registerForm);
57 +
58 + // Password visibility toggle
59 + initPasswordToggle($wrapper);
60 +
61 + // Social login
62 + initSocialLogin($wrapper);
63 +
64 + // reCAPTCHA initialization
65 + initRecaptcha($wrapper);
66 + }
67 +
68 + function initFormToggle($wrapper, $loginForm, $registerForm) {
69 + $wrapper.on('click', '.king-addons-form-toggle', function(e) {
70 + e.preventDefault();
71 +
72 + const toggleType = $(this).data('toggle');
73 + const $lostPasswordForm = $wrapper.find('.king-addons-lost-password-form');
74 +
75 + let $targetForm;
76 + if (toggleType === 'login') {
77 + $targetForm = $loginForm;
78 + } else if (toggleType === 'register') {
79 + $targetForm = $registerForm;
80 + } else if (toggleType === 'lostpassword') {
81 + $targetForm = $lostPasswordForm;
82 + }
83 +
84 + if (!$targetForm) return;
85 +
86 + // Clear any existing messages
87 + clearMessages($wrapper);
88 +
89 + // Hide all forms
90 + $loginForm.hide();
91 + $registerForm.hide();
92 + $lostPasswordForm.hide();
93 +
94 + // Show target form
95 + $targetForm.show();
96 + });
97 + }
98 +
99 + function initAjaxForms($wrapper, $loginForm, $registerForm, $lostPasswordForm, widgetId, widgetSettings) {
100 + // Login form submission
101 + $loginForm.find('form').on('submit', function(e) {
102 + e.preventDefault();
103 + handleLoginSubmission($(this), $wrapper, widgetId, widgetSettings);
104 + });
105 +
106 + // Register form submission
107 + $registerForm.find('form').on('submit', function(e) {
108 + e.preventDefault();
109 + handleRegisterSubmission($(this), $wrapper, widgetId, widgetSettings);
110 + });
111 +
112 + // Lost password form submission
113 + if ($lostPasswordForm.length) {
114 + $lostPasswordForm.find('form').on('submit', function(e) {
115 + e.preventDefault();
116 + handleLostPasswordSubmission($(this), $wrapper, widgetId);
117 + });
118 + }
119 + }
120 +
121 + function handleLoginSubmission($form, $wrapper, widgetId, widgetSettings) {
122 + const $messageContainer = $form.closest('.king-addons-login-register-form').find('.king-addons-form-message');
123 + const $submitButton = $form.find('.king-addons-login-button');
124 + const postId = $wrapper.data('post-id') || 0;
125 +
126 + // Clear previous messages
127 + clearMessages($wrapper);
128 +
129 + // Check if variables are defined
130 + if (typeof king_addons_login_register_vars === 'undefined') {
131 + // console.error('King Addons Login Register: AJAX variables not loaded');
132 + showMessage($messageContainer, 'Configuration error. Please refresh the page.', 'error');
133 + return;
134 + }
135 +
136 + // Get form data
137 + const formData = {
138 + action: 'king_addons_user_login',
139 + nonce: king_addons_login_register_vars.login_nonce,
140 + username: $form.find('[name="username"]').val(),
141 + password: $form.find('[name="password"]').val(),
142 + remember: $form.find('[name="remember"]').is(':checked') ? 1 : 0,
143 + widget_id: widgetId,
144 + post_id: postId,
145 + recaptcha_score_threshold: widgetSettings.recaptcha_score_threshold,
146 + redirect_after_login: widgetSettings.redirect_after_login
147 + };
148 +
149 + // Add reCAPTCHA response if present
150 + const recaptchaResponse = $form.find('[name="g-recaptcha-response"]').val();
151 + if (recaptchaResponse) {
152 + formData['g-recaptcha-response'] = recaptchaResponse;
153 + }
154 +
155 + // console.log('Submitting login form with data:', formData);
156 +
157 + // Validate required fields
158 + if (!formData.username || !formData.password) {
159 + showMessage($messageContainer, 'Please fill in all required fields.', 'error');
160 + return;
161 + }
162 +
163 + // Show loading state
164 + setLoadingState($form, true);
165 + $submitButton.prop('disabled', true);
166 +
167 + // Submit via AJAX
168 + $.ajax({
169 + url: king_addons_login_register_vars.ajax_url,
170 + type: 'POST',
171 + data: formData,
172 + success: function(response) {
173 + if (response.success) {
174 + showMessage($messageContainer, response.data.message, 'success');
175 +
176 + // Redirect if URL provided
177 + if (response.data.redirect) {
178 + setTimeout(function() {
179 + window.location.href = response.data.redirect;
180 + }, 1500);
181 + } else {
182 + // Reload page
183 + setTimeout(function() {
184 + location.reload();
185 + }, 1500);
186 + }
187 + } else {
188 + showMessage($messageContainer, response.data.message || 'An error occurred. Please try again.', 'error');
189 + }
190 + },
191 + error: function(xhr, status, error) {
192 + // console.error('Login AJAX Error:', error);
193 + // console.error('Login XHR response:', xhr.responseText);
194 + // console.error('Login XHR status code:', xhr.status);
195 +
196 + let errorMessage = 'Network error. Please check your connection and try again.';
197 +
198 + // Try to parse error response
199 + try {
200 + const response = JSON.parse(xhr.responseText);
201 + if (response.data && response.data.message) {
202 + errorMessage = response.data.message;
203 + }
204 + } catch (e) {
205 + // Use default error message
206 + }
207 +
208 + showMessage($messageContainer, errorMessage, 'error');
209 + },
210 + complete: function() {
211 + setLoadingState($form, false);
212 + $submitButton.prop('disabled', false);
213 + }
214 + });
215 + }
216 +
217 + function handleRegisterSubmission($form, $wrapper, widgetId, widgetSettings) {
218 + const $messageContainer = $form.closest('.king-addons-login-register-form').find('.king-addons-form-message');
219 + const $submitButton = $form.find('.king-addons-register-button');
220 + const postId = $wrapper.data('post-id') || 0;
221 +
222 + // Clear previous messages
223 + clearMessages($wrapper);
224 +
225 + // Check if variables are defined
226 + if (typeof king_addons_login_register_vars === 'undefined') {
227 + // console.error('King Addons Login Register: AJAX variables not loaded');
228 + showMessage($messageContainer, 'Configuration error. Please refresh the page.', 'error');
229 + return;
230 + }
231 +
232 + // Get form data
233 + const formData = {
234 + action: 'king_addons_user_register',
235 + nonce: king_addons_login_register_vars.register_nonce,
236 + email: $form.find('[name="email"]').val(),
237 + username: $form.find('[name="username"]').val(),
238 + password: $form.find('[name="password"]').val(),
239 + confirm_password: $form.find('[name="confirm_password"]').val(),
240 + widget_id: widgetId,
241 + post_id: postId,
242 + recaptcha_score_threshold: widgetSettings.recaptcha_score_threshold,
243 + redirect_after_register: widgetSettings.redirect_after_register,
244 + terms_required: widgetSettings.terms_required,
245 + enable_user_email: widgetSettings.enable_user_email,
246 + user_email_subject: widgetSettings.user_email_subject,
247 + user_email_content: widgetSettings.user_email_content,
248 + enable_admin_email: widgetSettings.enable_admin_email,
249 + admin_email_address: widgetSettings.admin_email_address,
250 + admin_email_subject: widgetSettings.admin_email_subject,
251 + admin_email_content: widgetSettings.admin_email_content,
252 + enable_mailchimp_integration: widgetSettings.enable_mailchimp_integration,
253 + auto_login_after_register: widgetSettings.auto_login_after_register
254 + };
255 +
256 + // Add additional fields if they exist
257 + const firstName = $form.find('[name="first_name"]').val();
258 + const lastName = $form.find('[name="last_name"]').val();
259 + const website = $form.find('[name="website"]').val();
260 + const phone = $form.find('[name="phone"]').val();
261 + const userRole = $form.find('[name="user_role"]').val();
262 + const termsConditions = $form.find('[name="terms_conditions"]').is(':checked');
263 +
264 + if (firstName) formData.first_name = firstName;
265 + if (lastName) formData.last_name = lastName;
266 + if (website) formData.website = website;
267 + if (phone) formData.phone = phone;
268 + if (userRole) formData.user_role = userRole;
269 + if (termsConditions) formData.terms_conditions = 1;
270 +
271 + // Collect custom fields from repeater
272 + const customFields = {};
273 + const customFieldLabels = {};
274 +
275 + $form.find('[name^="custom_field_"]').each(function() {
276 + const fieldName = $(this).attr('name');
277 + const fieldType = $(this).attr('type');
278 + const fieldLabel = $(this).closest('.king-addons-form-field').data('field-label') || fieldName.replace('custom_field_', 'Field ');
279 + let fieldValue;
280 +
281 + if (fieldType === 'checkbox') {
282 + // For checkboxes, collect all checked values
283 + const checkedBoxes = $form.find('[name="' + fieldName + '"]:checked');
284 + if (checkedBoxes.length > 0) {
285 + const values = [];
286 + checkedBoxes.each(function() {
287 + values.push($(this).val());
288 + });
289 + fieldValue = values.length === 1 ? values[0] : values;
290 + }
291 + } else if (fieldType === 'radio') {
292 + // For radio buttons, get the checked value
293 + fieldValue = $form.find('[name="' + fieldName + '"]:checked').val();
294 + } else {
295 + // For other input types (text, email, etc.)
296 + fieldValue = $(this).val();
297 + }
298 +
299 + if (fieldValue && fieldValue !== '') {
300 + customFields[fieldName] = fieldValue;
301 + customFieldLabels[fieldName] = fieldLabel;
302 + }
303 + });
304 +
305 + // Also handle textarea and select fields
306 + $form.find('textarea[name^="custom_field_"], select[name^="custom_field_"]').each(function() {
307 + const fieldName = $(this).attr('name');
308 + const fieldValue = $(this).val();
309 + const fieldLabel = $(this).closest('.king-addons-form-field').data('field-label') || fieldName.replace('custom_field_', 'Field ');
310 +
311 + if (fieldValue && fieldValue !== '') {
312 + customFields[fieldName] = fieldValue;
313 + customFieldLabels[fieldName] = fieldLabel;
314 + }
315 + });
316 +
317 + // Add custom fields to form data if any exist
318 + if (Object.keys(customFields).length > 0) {
319 + formData.custom_fields = customFields;
320 + formData.custom_field_labels = customFieldLabels;
321 + // console.log('Found custom fields:', customFields);
322 + // console.log('Field labels:', customFieldLabels);
323 + } else {
324 + // console.log('No custom fields found');
325 + }
326 +
327 + // Add reCAPTCHA response if present
328 + const recaptchaResponse = $form.find('[name="g-recaptcha-response"]').val();
329 + if (recaptchaResponse) {
330 + formData['g-recaptcha-response'] = recaptchaResponse;
331 + }
332 +
333 + // console.log('Submitting registration form with data:', formData);
334 +
335 + // Client-side validation
336 + const validationResult = validateRegistrationForm(formData);
337 + if (!validationResult.valid) {
338 + showMessage($messageContainer, validationResult.message, 'error');
339 + return;
340 + }
341 +
342 + // Show loading state
343 + setLoadingState($form, true);
344 + $submitButton.prop('disabled', true);
345 +
346 + // Prepare FormData for file uploads
347 + let ajaxData = formData;
348 + let ajaxOptions = {
349 + url: king_addons_login_register_vars.ajax_url,
350 + type: 'POST',
351 + data: ajaxData,
352 + };
353 +
354 + // Check if there are file inputs
355 + const $fileInputs = $form.find('input[type="file"]');
356 + if ($fileInputs.length > 0) {
357 + // Use FormData for file uploads
358 + const formDataObj = new FormData();
359 +
360 + // Add all form data
361 + for (const key in formData) {
362 + if (formData.hasOwnProperty(key)) {
363 + if (typeof formData[key] === 'object' && formData[key] !== null) {
364 + formDataObj.append(key, JSON.stringify(formData[key]));
365 + } else {
366 + formDataObj.append(key, formData[key]);
367 + }
368 + }
369 + }
370 +
371 + // Add files
372 + $fileInputs.each(function() {
373 + const $input = $(this);
374 + const files = $input[0].files;
375 + if (files.length > 0) {
376 + for (let i = 0; i < files.length; i++) {
377 + formDataObj.append($input.attr('name'), files[i]);
378 + }
379 + }
380 + });
381 +
382 + ajaxOptions.data = formDataObj;
383 + ajaxOptions.processData = false;
384 + ajaxOptions.contentType = false;
385 + }
386 +
387 + // Submit via AJAX
388 + $.ajax(ajaxOptions).done(function(response) {
389 + if (response.success) {
390 + showMessage($messageContainer, response.data.message, 'success');
391 +
392 + // Redirect if URL provided
393 + if (response.data.redirect) {
394 + setTimeout(function() {
395 + window.location.href = response.data.redirect;
396 + }, 1500);
397 + } else {
398 + // Reload page or switch to login form
399 + setTimeout(function() {
400 + location.reload();
401 + }, 1500);
402 + }
403 + } else {
404 + showMessage($messageContainer, response.data.message || 'Registration failed. Please try again.', 'error');
405 + }
406 + }).fail(function(xhr, status, error) {
407 + // console.error('Registration AJAX Error:', error);
408 + // console.error('Registration XHR response:', xhr.responseText);
409 + // console.error('Registration XHR status code:', xhr.status);
410 +
411 + let errorMessage = 'Network error. Please check your connection and try again.';
412 +
413 + // Try to parse error response
414 + try {
415 + const response = JSON.parse(xhr.responseText);
416 + if (response.data && response.data.message) {
417 + errorMessage = response.data.message;
418 + }
419 + } catch (e) {
420 + // Use default error message
421 + }
422 +
423 + showMessage($messageContainer, errorMessage, 'error');
424 + }).always(function() {
425 + setLoadingState($form, false);
426 + $submitButton.prop('disabled', false);
427 + });
428 + }
429 +
430 + function handleLostPasswordSubmission($form, $wrapper, widgetId) {
431 + const $messageContainer = $form.closest('.king-addons-lost-password-form').find('.king-addons-form-message');
432 + const $submitButton = $form.find('.king-addons-lostpassword-button');
433 +
434 + // Clear previous messages
435 + clearMessages($wrapper);
436 +
437 + // Check if variables are defined
438 + if (typeof king_addons_login_register_vars === 'undefined') {
439 + // console.error('King Addons Login Register: AJAX variables not loaded');
440 + showMessage($messageContainer, 'Configuration error. Please refresh the page.', 'error');
441 + return;
442 + }
443 +
444 + // Get form data
445 + const formData = {
446 + action: 'king_addons_user_lostpassword',
447 + nonce: king_addons_login_register_vars.lostpassword_nonce,
448 + user_login: $form.find('[name="user_login"]').val(),
449 + widget_id: widgetId
450 + };
451 +
452 + // Validate required fields
453 + if (!formData.user_login) {
454 + showMessage($messageContainer, 'Please enter your email address.', 'error');
455 + return;
456 + }
457 +
458 + // Show loading state
459 + setLoadingState($form, true);
460 + $submitButton.prop('disabled', true);
461 +
462 + // Submit via AJAX
463 + $.ajax({
464 + url: king_addons_login_register_vars.ajax_url,
465 + type: 'POST',
466 + data: formData,
467 + timeout: 30000,
468 + success: function(response) {
469 + if (response.success) {
470 + showMessage($messageContainer, response.data.message, 'success');
471 + $form[0].reset(); // Clear the form
472 + } else {
473 + showMessage($messageContainer, response.data.message || 'Lost password request failed. Please try again.', 'error');
474 + }
475 + },
476 + error: function(xhr, status, error) {
477 + // console.error('Lost Password AJAX Error:', error);
478 + // console.error('Lost Password XHR response:', xhr.responseText);
479 + // console.error('Lost Password XHR status code:', xhr.status);
480 +
481 + let errorMessage = 'Network error. Please check your connection and try again.';
482 +
483 + // Try to parse error response
484 + try {
485 + const response = JSON.parse(xhr.responseText);
486 + if (response.data && response.data.message) {
487 + errorMessage = response.data.message;
488 + }
489 + } catch (e) {
490 + // Use default error message
491 + }
492 +
493 + showMessage($messageContainer, errorMessage, 'error');
494 + },
495 + complete: function() {
496 + setLoadingState($form, false);
497 + $submitButton.prop('disabled', false);
498 + }
499 + });
500 + }
501 +
502 + function validateRegistrationForm(data) {
503 + // Check required fields
504 + if (!data.email || !data.username || !data.password || !data.confirm_password) {
505 + return {
506 + valid: false,
507 + message: 'Please fill in all required fields.'
508 + };
509 + }
510 +
511 + // Validate email format
512 + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
513 + if (!emailRegex.test(data.email)) {
514 + return {
515 + valid: false,
516 + message: 'Please enter a valid email address.'
517 + };
518 + }
519 +
520 + // Validate username (basic validation)
521 + if (data.username.length < 3) {
522 + return {
523 + valid: false,
524 + message: 'Username must be at least 3 characters long.'
525 + };
526 + }
527 +
528 + // Check username characters
529 + const usernameRegex = /^[a-zA-Z0-9._-]+$/;
530 + if (!usernameRegex.test(data.username)) {
531 + return {
532 + valid: false,
533 + message: 'Username can only contain letters, numbers, periods, hyphens, and underscores.'
534 + };
535 + }
536 +
537 + // Validate password length
538 + if (data.password.length < 6) {
539 + return {
540 + valid: false,
541 + message: 'Password must be at least 6 characters long.'
542 + };
543 + }
544 +
545 + // Enhanced password strength validation (updated for security)
546 + if (data.password.length < 8) {
547 + return {
548 + valid: false,
549 + message: 'Password must be at least 8 characters long.'
550 + };
551 + }
552 +
553 + // Check for basic password strength
554 + const hasLower = /[a-z]/.test(data.password);
555 + const hasUpper = /[A-Z]/.test(data.password);
556 + const hasNumbers = /\d/.test(data.password);
557 + const hasSpecial = /[!@#$%^&*(),.?":{}|<>]/.test(data.password);
558 +
559 + let strengthScore = 0;
560 + if (hasLower) strengthScore++;
561 + if (hasUpper) strengthScore++;
562 + if (hasNumbers) strengthScore++;
563 + if (hasSpecial) strengthScore++;
564 +
565 + if (strengthScore < 3) {
566 + return {
567 + valid: false,
568 + message: 'Password must contain uppercase, lowercase, numbers, and special characters for security.'
569 + };
570 + }
571 +
572 + // Check against common passwords
573 + const commonPasswords = ['password', '123456', '123456789', 'qwerty', 'abc123', 'password123'];
574 + if (commonPasswords.includes(data.password.toLowerCase())) {
575 + return {
576 + valid: false,
577 + message: 'Please choose a more unique password. This password is too common.'
578 + };
579 + }
580 +
581 + // Check password confirmation
582 + if (data.password !== data.confirm_password) {
583 + return {
584 + valid: false,
585 + message: 'Passwords do not match.'
586 + };
587 + }
588 +
589 + // Check terms and conditions if required
590 + const $termsCheckbox = $('.king-addons-register-form [name="terms_conditions"]');
591 + if ($termsCheckbox.length && $termsCheckbox.prop('required') && !$termsCheckbox.is(':checked')) {
592 + return {
593 + valid: false,
594 + message: 'Please accept the Terms & Conditions.'
595 + };
596 + }
597 +
598 + return { valid: true };
599 + }
600 +
601 + function initFormValidation($loginForm, $registerForm) {
602 + // Real-time validation for registration form
603 + $registerForm.find('[name="confirm_password"]').on('blur keyup', function() {
604 + const password = $registerForm.find('[name="password"]').val();
605 + const confirmPassword = $(this).val();
606 + const $field = $(this).closest('.king-addons-form-field');
607 +
608 + // Remove existing validation classes
609 + $field.removeClass('field-error field-success');
610 +
611 + if (confirmPassword && password && password !== confirmPassword) {
612 + $field.addClass('field-error');
613 + showFieldError($field, 'Passwords do not match.');
614 + } else if (confirmPassword && password && password === confirmPassword) {
615 + $field.addClass('field-success');
616 + hideFieldError($field);
617 + } else {
618 + hideFieldError($field);
619 + }
620 + });
621 +
622 + // Email validation
623 + $registerForm.find('[name="email"]').on('blur', function() {
624 + const email = $(this).val();
625 + const $field = $(this).closest('.king-addons-form-field');
626 + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
627 +
628 + $field.removeClass('field-error field-success');
629 +
630 + if (email && !emailRegex.test(email)) {
631 + $field.addClass('field-error');
632 + showFieldError($field, 'Please enter a valid email address.');
633 + } else if (email) {
634 + $field.addClass('field-success');
635 + hideFieldError($field);
636 + } else {
637 + hideFieldError($field);
638 + }
639 + });
640 +
641 + // Username validation
642 + $registerForm.find('[name="username"]').on('blur', function() {
643 + const username = $(this).val();
644 + const $field = $(this).closest('.king-addons-form-field');
645 + const usernameRegex = /^[a-zA-Z0-9._-]+$/;
646 +
647 + $field.removeClass('field-error field-success');
648 +
649 + if (username && (username.length < 3 || !usernameRegex.test(username))) {
650 + $field.addClass('field-error');
651 + const message = username.length < 3
652 + ? 'Username must be at least 3 characters long.'
653 + : 'Username can only contain letters, numbers, periods, hyphens, and underscores.';
654 + showFieldError($field, message);
655 + } else if (username) {
656 + $field.addClass('field-success');
657 + hideFieldError($field);
658 + } else {
659 + hideFieldError($field);
660 + }
661 + });
662 + }
663 +
664 + function showFieldError($field, message) {
665 + hideFieldError($field);
666 + $field.append('<span class="field-error-message" style="color: #f44336; font-size: 12px; margin-top: 5px; display: block;">' + message + '</span>');
667 + }
668 +
669 + function hideFieldError($field) {
670 + $field.find('.field-error-message').remove();
671 + }
672 +
673 + function initPasswordToggle($wrapper) {
674 + $wrapper.on('click', '.king-addons-password-toggle', function(e) {
675 + e.preventDefault();
676 +
677 + const $button = $(this);
678 + const $input = $button.siblings('input[type="password"], input[type="text"]');
679 + const $icon = $button.find('.king-addons-password-toggle-icon');
680 +
681 + if ($input.attr('type') === 'password') {
682 + $input.attr('type', 'text');
683 + $icon.text('🙈');
684 + $button.attr('aria-label', 'Hide password');
685 + } else {
686 + $input.attr('type', 'password');
687 + $icon.text('👁');
688 + $button.attr('aria-label', 'Show password');
689 + }
690 + });
691 + }
692 +
693 + function showMessage($container, message, type) {
694 + $container.removeClass('success error').addClass(type + ' show').text(message);
695 +
696 + // Auto-hide success messages after 5 seconds
697 + if (type === 'success') {
698 + setTimeout(function() {
699 + $container.removeClass('show');
700 + }, 5000);
701 + }
702 + }
703 +
704 + function clearMessages($wrapper) {
705 + $wrapper.find('.king-addons-form-message').removeClass('success error show').text('');
706 + }
707 +
708 + function setLoadingState($form, loading) {
709 + if (loading) {
710 + $form.addClass('king-addons-form-loading');
711 + } else {
712 + $form.removeClass('king-addons-form-loading');
713 + }
714 + }
715 +
716 + function initSocialLogin($wrapper) {
717 + // Google login
718 + $wrapper.find('.king-addons-google-login').on('click', function(e) {
719 + e.preventDefault();
720 + const $button = $(this);
721 + const clientId = $button.data('client-id');
722 +
723 + if (!clientId) {
724 + showSocialLoginError('Google Client ID not configured. Please check widget settings.');
725 + return;
726 + }
727 +
728 + // Check if Google API is loaded
729 + if (typeof google === 'undefined' || !google.accounts) {
730 + showSocialLoginError('Google API not loaded. Please check your internet connection.');
731 + return;
732 + }
733 +
734 + // Initialize Google One Tap
735 + google.accounts.id.initialize({
736 + client_id: clientId,
737 + callback: handleGoogleResponse,
738 + auto_select: false,
739 + cancel_on_tap_outside: true
740 + });
741 +
742 + // Show Google login prompt
743 + google.accounts.id.prompt();
744 + });
745 +
746 + // Facebook login
747 + $wrapper.find('.king-addons-facebook-login').on('click', function(e) {
748 + e.preventDefault();
749 + const $button = $(this);
750 + const appId = $button.data('app-id');
751 +
752 + if (!appId) {
753 + showSocialLoginError('Facebook App ID not configured. Please check widget settings.');
754 + return;
755 + }
756 +
757 + // Check if Facebook SDK is loaded
758 + if (typeof FB === 'undefined') {
759 + showSocialLoginError('Facebook SDK not loaded. Please check your internet connection.');
760 + return;
761 + }
762 +
763 + // Facebook login
764 + FB.login(function(response) {
765 + if (response.authResponse) {
766 + handleFacebookResponse(response.authResponse);
767 + } else {
768 + showSocialLoginError('Facebook login was cancelled or failed.');
769 + }
770 + }, {scope: 'email,public_profile'});
771 + });
772 +
773 + function handleGoogleResponse(response) {
774 + const $wrapper = $('.king-addons-login-register-form-wrapper');
775 + const widgetId = $wrapper.data('widget-id');
776 + const $messageContainer = $wrapper.find('.king-addons-form-message');
777 +
778 + // Show loading
779 + showMessage($messageContainer, 'Authenticating with Google...', 'info');
780 +
781 + $.ajax({
782 + url: king_addons_login_register_vars.ajax_url,
783 + type: 'POST',
784 + data: {
785 + action: 'king_addons_google_login',
786 + nonce: king_addons_login_register_vars.social_login_nonce,
787 + google_token: response.credential,
788 + widget_id: widgetId,
789 + post_id: $wrapper.data('post-id') || 0
790 + },
791 + success: function(ajaxResponse) {
792 + if (ajaxResponse.success) {
793 + showMessage($messageContainer, ajaxResponse.data.message, 'success');
794 + if (ajaxResponse.data.redirect) {
795 + setTimeout(() => {
796 + window.location.href = ajaxResponse.data.redirect;
797 + }, 1500);
798 + } else {
799 + setTimeout(() => {
800 + location.reload();
801 + }, 1500);
802 + }
803 + } else {
804 + showMessage($messageContainer, ajaxResponse.data.message, 'error');
805 + }
806 + },
807 + error: function() {
808 + showMessage($messageContainer, 'Google login failed. Please try again.', 'error');
809 + }
810 + });
811 + }
812 +
813 + function handleFacebookResponse(authResponse) {
814 + const $wrapper = $('.king-addons-login-register-form-wrapper');
815 + const widgetId = $wrapper.data('widget-id');
816 + const $messageContainer = $wrapper.find('.king-addons-form-message');
817 +
818 + // Show loading
819 + showMessage($messageContainer, 'Authenticating with Facebook...', 'info');
820 +
821 + $.ajax({
822 + url: king_addons_login_register_vars.ajax_url,
823 + type: 'POST',
824 + data: {
825 + action: 'king_addons_facebook_login',
826 + nonce: king_addons_login_register_vars.social_login_nonce,
827 + facebook_token: authResponse.accessToken,
828 + widget_id: widgetId,
829 + post_id: $wrapper.data('post-id') || 0
830 + },
831 + success: function(ajaxResponse) {
832 + if (ajaxResponse.success) {
833 + showMessage($messageContainer, ajaxResponse.data.message, 'success');
834 + if (ajaxResponse.data.redirect) {
835 + setTimeout(() => {
836 + window.location.href = ajaxResponse.data.redirect;
837 + }, 1500);
838 + } else {
839 + setTimeout(() => {
840 + location.reload();
841 + }, 1500);
842 + }
843 + } else {
844 + showMessage($messageContainer, ajaxResponse.data.message, 'error');
845 + }
846 + },
847 + error: function() {
848 + showMessage($messageContainer, 'Facebook login failed. Please try again.', 'error');
849 + }
850 + });
851 + }
852 +
853 + function showSocialLoginError(message) {
854 + const $wrapper = $('.king-addons-login-register-form-wrapper');
855 + const $messageContainer = $wrapper.find('.king-addons-form-message');
856 + showMessage($messageContainer, message, 'error');
857 + }
858 + }
859 +
860 + function initRecaptcha($wrapper) {
861 + // Initialize reCAPTCHA v2
862 + $wrapper.find('.king-addons-recaptcha-v2').each(function() {
863 + const $recaptcha = $(this);
864 + const sitekey = $recaptcha.data('sitekey');
865 + const theme = $recaptcha.data('theme') || 'light';
866 + const size = $recaptcha.data('size') || 'normal';
867 +
868 + if (typeof grecaptcha !== 'undefined' && sitekey) {
869 + grecaptcha.ready(function() {
870 + grecaptcha.render($recaptcha[0], {
871 + 'sitekey': sitekey,
872 + 'theme': theme,
873 + 'size': size
874 + });
875 + });
876 + }
877 + });
878 +
879 + // For reCAPTCHA v3, we'll handle it in form submission
880 + $wrapper.find('.king-addons-recaptcha-v3').each(function() {
881 + const $recaptcha = $(this);
882 + const sitekey = $recaptcha.data('sitekey');
883 + const action = $recaptcha.data('action');
884 +
885 + if (typeof grecaptcha !== 'undefined' && sitekey) {
886 + // v3 tokens are generated on form submission
887 + $recaptcha.closest('form').on('submit', function(e) {
888 + const $form = $(this);
889 +
890 + if ($recaptcha.val()) {
891 + // Token already exists, proceed
892 + return;
893 + }
894 +
895 + e.preventDefault();
896 +
897 + grecaptcha.ready(function() {
898 + grecaptcha.execute(sitekey, {action: action}).then(function(token) {
899 + $recaptcha.val(token);
900 + $form.trigger('submit');
901 + });
902 + });
903 + });
904 + }
905 + });
906 + }
907 +
908 + // Expose functions globally for potential external use
909 + window.KingAddonsLoginRegister = {
910 + initForm: initLoginRegisterForm,
911 + showMessage: showMessage,
912 + clearMessages: clearMessages
913 + };
914 +
915 +})(jQuery);