1(function($) { 2 'use strict'; 3 4 // Password strength criteria 5 const PASSWORD_CRITERIA = { 6 minLength: 8, 7 hasUpperCase: /[A-Z]/, 8 hasLowerCase: /[a-z]/, 9 hasNumbers: /[0-9]/, 10 hasSpecialChar: /[^A-Za-z0-9]/, 11 maxLength: 60 12 }; 13 14 class RegistrationForm { 15 constructor() { 16 this.$form = $('#cobra-register-form'); 17 this.$password = $('#password'); 18 this.$confirmPassword = $('#confirm_password'); 19 this.$username = $('#username'); 20 this.$email = $('#email'); 21 this.$submitButton = this.$form.find('button[type="submit"]'); 22 23 if (this.$form.length) { 24 this.initializeHandlers(); 25 } 26 } 27 28 initializeHandlers() { 29 // Password strength checker 30 this.$password.on('input', () => this.checkPasswordStrength()); 31 32 // Password confirmation checker 33 this.$confirmPassword.on('input', () => this.checkPasswordMatch()); 34 35 // Username availability checker 36 this.$username.on('blur', () => this.checkUsernameAvailability()); 37 38 // Email availability checker 39 this.$email.on('blur', () => this.checkEmailAvailability()); 40 41 // Password visibility toggle 42 // $('.toggle-password').on('click', (e) => this.togglePasswordVisibility(e)); 43 44 // Form submission 45 this.$form.on('submit', (e) => this.handleSubmit(e)); 46 } 47 48 checkPasswordStrength() { 49 const password = this.$password.val(); 50 const $strengthBar = $('.strength-bar'); 51 const $strengthText = $('.strength-text'); 52 53 if (!password) { 54 this.updateStrengthIndicator($strengthBar, $strengthText, 0, ''); 55 return; 56 } 57 58 let score = 0; 59 let feedback = []; 60 61 // Check length 62 if (password.length >= PASSWORD_CRITERIA.minLength) { 63 score++; 64 } else { 65 feedback.push(cobraAIRegister.i18n.passwordTooShort); 66 } 67 if (password.length < PASSWORD_CRITERIA.maxLength) { 68 score++; 69 } else { 70 feedback.push(cobraAIRegister.i18n.passwordTooLong); 71 } 72 73 // Check uppercase 74 if (PASSWORD_CRITERIA.hasUpperCase.test(password)) score++; 75 else feedback.push(cobraAIRegister.i18n.passwordNeedsUpper); 76 77 // Check lowercase 78 if (PASSWORD_CRITERIA.hasLowerCase.test(password)) score++; 79 else feedback.push(cobraAIRegister.i18n.passwordNeedsLower); 80 81 // Check numbers 82 if (PASSWORD_CRITERIA.hasNumbers.test(password)) score++; 83 else feedback.push(cobraAIRegister.i18n.passwordNeedsNumber); 84 85 // Check special characters 86 if (PASSWORD_CRITERIA.hasSpecialChar.test(password)) score++; 87 else feedback.push(cobraAIRegister.i18n.passwordNeedsSpecial); 88 89 // Calculate percentage 90 const strengthPercentage = (score / 5) * 100; 91 92 // Get strength text 93 let strengthText = ''; 94 if (score < 2) strengthText = cobraAIRegister.i18n.veryWeak; 95 else if (score < 3) strengthText = cobraAIRegister.i18n.weak; 96 else if (score < 4) strengthText = cobraAIRegister.i18n.medium; 97 else if (score < 5) strengthText = cobraAIRegister.i18n.strong; 98 else strengthText = cobraAIRegister.i18n.veryStrong; 99 100 // Add feedback if password is weak 101 if (score < 3 && feedback.length) { 102 strengthText += ': ' + feedback.join(', '); 103 } 104 105 this.updateStrengthIndicator($strengthBar, $strengthText, strengthPercentage, strengthText); 106 } 107 108 updateStrengthIndicator($bar, $text, percentage, message) { 109 // Update strength bar 110 $bar.css({ 111 'width': `${percentage}%`,
112 'background-color': this.getStrengthColor(percentage) 113 }); 114 115 // Update strength text 116 $text.text(message) 117 .css('color', this.getStrengthColor(percentage)); 118 } 119 120 getStrengthColor(percentage) { 121 if (percentage <= 20) return '#dc3232'; // Very weak - Red 122 if (percentage <= 40) return '#dc3232'; // Weak - Red 123 if (percentage <= 60) return '#dba617'; // Medium - Yellow 124 if (percentage <= 80) return '#7ad03a'; // Strong - Light green 125 return '#00a32a'; // Very strong - Green 126 } 127 128 checkPasswordMatch() { 129 const password = this.$password.val(); 130 const confirmPassword = this.$confirmPassword.val(); 131 const $feedback = this.$confirmPassword.siblings('.password-match-feedback'); 132 133 if (!confirmPassword) { 134 this.removePasswordMatchFeedback($feedback); 135 return; 136 } 137 138 if (password === confirmPassword) { 139 this.showPasswordMatchSuccess($feedback); 140 } else { 141 this.showPasswordMatchError($feedback); 142 } 143 } 144 145 removePasswordMatchFeedback($feedback) { 146 $feedback.remove(); 147 } 148 149 showPasswordMatchSuccess($feedback) { 150 this.updatePasswordMatchFeedback($feedback, cobraAIRegister.i18n.passwordsMatch, 'success'); 151 } 152 153 showPasswordMatchError($feedback) { 154 this.updatePasswordMatchFeedback($feedback, cobraAIRegister.i18n.passwordsMismatch, 'error'); 155 } 156 157 updatePasswordMatchFeedback($feedback, message, type) { 158 if ($feedback.length) { 159 $feedback.text(message).attr('class', `password-match-feedback ${type}`); 160 } else { 161 this.$confirmPassword.after( 162 `<span class="password-match-feedback ${type}">${message}</span>` 163 ); 164 } 165 } 166 167 checkUsernameAvailability() { 168 const username = this.$username.val(); 169 if (!username) return; 170 171 this.checkAvailability('username', username, this.$username); 172 } 173 174 checkEmailAvailability() { 175 const email = this.$email.val(); 176 if (!email) return; 177 178 this.checkAvailability('email', email, this.$email); 179 } 180 181 checkAvailability(type, value, $field) { 182 $.ajax({
183 url: cobraAIRegister.ajax_url, 184 type: 'POST', 185 data: { 186 action: 'cobra_check_availability', 187 nonce: cobraAIRegister.nonce, 188 type: type, 189 value: value 190 }, 191 beforeSend: () => { 192 $field.addClass('checking'); 193 this.removeAvailabilityFeedback($field); 194 }, 195 success: (response) => { 196 if (response.success) { 197 this.showAvailabilitySuccess($field); 198 } else { 199 this.showAvailabilityError($field, response.data.message); 200 } 201 }, 202 error: () => { 203 this.showAvailabilityError($field, cobraAIRegister.i18n.checkFailed); 204 }, 205 complete: () => { 206 $field.removeClass('checking'); 207 } 208 }); 209 } 210 211 removeAvailabilityFeedback($field) { 212 $field.siblings('.availability-feedback').remove(); 213 } 214 215 showAvailabilitySuccess($field) { 216 this.updateAvailabilityFeedback( 217 $field, 218 cobraAIRegister.i18n.available, 219 'success' 220 ); 221 } 222 223 showAvailabilityError($field, message) { 224 this.updateAvailabilityFeedback($field, message, 'error'); 225 } 226 227 updateAvailabilityFeedback($field, message, type) { 228 const $feedback = $field.siblings('.availability-feedback'); 229 230 if ($feedback.length) { 231 $feedback.text(message).attr('class', `availability-feedback ${type}`); 232 } else { 233 $field.after(`<span class="availability-feedback ${type}">${message}</span>`); 234 } 235 } 236 237 // togglePasswordVisibility(event) { 238 // const $button = $(event.currentTarget); 239 // const $input = $button.siblings('input'); 240 // const $icon = $button.find('.dashicons'); 241 242 // if ($input.attr('type') === 'password') { 243 // $input.attr('type', 'text'); 244 // $icon.removeClass('dashicons-visibility').addClass('dashicons-hidden'); 245 // $button.attr('aria-label', cobraAIRegister.i18n.hidePassword); 246 // } else { 247 // $input.attr('type', 'password'); 248 // $icon.removeClass('dashicons-hidden').addClass('dashicons-visibility'); 249 // $button.attr('aria-label', cobraAIRegister.i18n.showPassword); 250 // } 251 // } 252 253 handleSubmit(event) { 254 if (!this.validateForm()) { 255 event.preventDefault(); 256 return; 257 } 258 259 this.$submitButton.prop('disabled', true).addClass('loading'); 260 } 261 262 validateForm() { 263 let isValid = true; 264 265 // Clear previous errors AND password match feedback 266 $('.cobra-form-error').remove(); 267 $('.password-match-feedback').remove(); 268 269 // Check required fields 270 this.$form.find('[required]').each((index, element) => { 271 const $field = $(element); 272 if (!$field.val()) { 273 this.showFieldError($field, cobraAIRegister.i18n.fieldRequired); 274 isValid = false; 275 } 276 }); 277 278 // Check password strength 279 if (this.$password.val() && !this.isPasswordStrengthAcceptable()) { 280 const message = this.passwordMissing && this.passwordMissing.length > 0 281 ? cobraAIRegister.i18n.passwordTooWeak + ': ' + this.passwordMissing.join(', ') 282 : cobraAIRegister.i18n.passwordTooWeak; 283 this.showFieldError(this.$password, message); 284 isValid = false; 285 } 286 287 // Check password match 288 if (this.$password.val() !== this.$confirmPassword.val()) {
289 this.showFieldError(this.$confirmPassword, cobraAIRegister.i18n.passwordsMismatch); 290 isValid = false; 291 } 292 293 return isValid; 294 } 295 296 isPasswordStrengthAcceptable() { 297 const password = this.$password.val(); 298 const missing = []; 299 300 // Only check minimum length 301 if (password.length < PASSWORD_CRITERIA.minLength) { 302 missing.push(cobraAIRegister.i18n.passwordTooShort); 303 } 304 305 // Store missing requirements for error message 306 this.passwordMissing = missing; 307 308 // Password is acceptable if it meets minimum length 309 return password.length >= PASSWORD_CRITERIA.minLength; 310 } 311 312 showFieldError($field, message) { 313 if (!$field.siblings('.cobra-form-error').length) { 314 $field.after(`<span class="cobra-form-error">${message}</span>`); 315 } 316 } 317 } 318 319 // Initialize when document is ready 320 $(document).ready(() => { 321 new RegistrationForm(); 322 323 324 }); 325 326 // $('.toggle-password').on('click', function() { 327 // const $button = $(this); 328 // const $input = $button.siblings('input'); 329 // const $icon = $button.find('.dashicons'); 330 331 // if ($input.attr('type') === 'password') { 332 // $input.attr('type', 'text'); 333 // $icon.removeClass('dashicons-visibility').addClass('dashicons-hidden'); 334 // $button.attr('aria-label', 'Hide password'); 335 // } else { 336 // $input.attr('type', 'password'); 337 // $icon.removeClass('dashicons-hidden').addClass('dashicons-visibility'); 338 // $button.attr('aria-label', 'Show password'); 339 // } 340 // }); 341})(jQuery); 342jQuery(document).ready(function($) { 343 $('.toggle-password').on('click', function() { 344 const $button = $(this); 345 const $input = $button.parent().find('input'); 346 const $icon = $button.find('.eye-icon'); 347 348 349 350 if ($input.attr('type') === 'password') { 351 $input.attr('type', 'text'); 352 $icon.removeClass('fa-eye').addClass('fa-eye-slash'); 353 $button.attr('aria-label', 'Hide password'); 354 355 } else { 356 $input.attr('type', 'password'); 357 $icon.removeClass('fa-eye-slash').addClass('fa-eye'); 358 $button.attr('aria-label', 'Show password'); 359 360 } 361 362 }); 363});
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.