PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.2.9
Yatra – Travel Booking & Tour Operator Software v3.0.2.9
3.0.16 3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 All 84 releases
yatra / assets / js / login-shortcode.js

login-shortcode.js in Yatra – Travel Booking & Tour Operator Software 3.0.2.9, at assets/js/login-shortcode.js

414 lines 14.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Login Shortcode JavaScript
3 *
4 * Production-optimized login form interactions and functionality
5 *
6 * @package Yatra
7 * @version 1.0.0
8 */
9
10 (function($) {
11 'use strict';
12
13 // Performance: Cache DOM elements
14 let $document = $(document);
15 let $window = $(window);
16
17 // Performance: Debounce function for performance
18 function debounce(func, wait) {
19 let timeout;
20 return function executedFunction(...args) {
21 const later = () => {
22 clearTimeout(timeout);
23 func(...args);
24 };
25 clearTimeout(timeout);
26 timeout = setTimeout(later, wait);
27 };
28 }
29
30 // Performance: Throttle function for scroll events
31 function throttle(func, limit) {
32 let inThrottle;
33 return function() {
34 const args = arguments;
35 const context = this;
36 if (!inThrottle) {
37 func.apply(context, args);
38 inThrottle = true;
39 setTimeout(() => inThrottle = false, limit);
40 }
41 };
42 }
43
44 // Initialize when DOM is ready
45 $document.ready(function() {
46 // Performance: Check if login form exists before initializing
47 if ($('.yatra-login-form').length === 0) {
48 return;
49 }
50
51 // Initialize components
52 initPasswordToggles();
53 initAjaxLogin();
54 initAccessibility();
55 initPerformanceOptimizations();
56 });
57
58 /**
59 * Initialize password toggle functionality with performance optimizations
60 */
61 function initPasswordToggles() {
62 const passwordToggles = $('.yatra-password-toggle');
63
64 // Performance: Use event delegation for better performance
65 $document.on('click', '.yatra-password-toggle', function(e) {
66 e.preventDefault();
67
68 const $toggle = $(this);
69 const targetId = $toggle.data('target');
70 const $input = targetId ? $('#' + targetId) : $toggle.siblings('.yatra-form-input');
71
72 if (!$input.length) {
73 console.warn('Password input not found for toggle');
74 return;
75 }
76
77 const $eyeIcon = $toggle.find('.yatra-eye-icon');
78 const $eyeOffIcon = $toggle.find('.yatra-eye-off-icon');
79
80 try {
81 const isPassword = $input.attr('type') === 'password';
82
83 if (isPassword) {
84 $input.attr('type', 'text');
85 $eyeIcon.hide();
86 $eyeOffIcon.show();
87 $toggle.attr('aria-label', 'Hide password');
88 } else {
89 $input.attr('type', 'password');
90 $eyeIcon.show();
91 $eyeOffIcon.hide();
92 $toggle.attr('aria-label', 'Show password');
93 }
94 } catch (error) {
95 console.error('Error toggling password visibility:', error);
96 }
97 });
98 }
99
100 /**
101 * Enhanced form validation function
102 */
103 function validateLoginForm($username, $password) {
104 const username = $username.val().trim();
105 const password = $password.val().trim();
106
107 if (!username) {
108 return {
109 valid: false,
110 message: yatra_ajax?.strings?.validation_error || 'Please enter your email or username.'
111 };
112 }
113
114 if (!password) {
115 return {
116 valid: false,
117 message: yatra_ajax?.strings?.validation_error || 'Please enter your password.'
118 };
119 }
120
121 if (username.length > 60) {
122 return {
123 valid: false,
124 message: 'Username is too long.'
125 };
126 }
127
128 if (password.length > 72) {
129 return {
130 valid: false,
131 message: 'Password is too long.'
132 };
133 }
134
135 return { valid: true };
136 }
137
138 /**
139 * Initialize accessibility features
140 */
141 function initAccessibility() {
142 // Add ARIA labels and improve keyboard navigation
143 $document.on('keydown', '.yatra-form-input', function(e) {
144 if (e.key === 'Enter') {
145 const $form = $(this).closest('form');
146 if ($form.length) {
147 $form.trigger('submit');
148 }
149 }
150 });
151
152 // Focus management for error messages
153 $document.on('click', '.yatra-form-error', function() {
154 $(this).fadeOut(200, function() {
155 $(this).remove();
156 });
157 });
158 }
159
160 /**
161 * Initialize performance optimizations
162 */
163 function initPerformanceOptimizations() {
164 // Lazy load error styles
165 if (!$('#yatra-login-error-styles').length) {
166 $('<style id="yatra-login-error-styles">')
167 .text('.yatra-form-error { transition: opacity 0.2s ease; }')
168 .appendTo('head');
169 }
170
171 // Preload critical resources
172 if (window.performance && window.performance.getEntriesByType) {
173 // Monitor performance in development
174 if (yatra_ajax?.debug) {
175 setTimeout(() => {
176 const entries = performance.getEntriesByType('navigation');
177 if (entries.length > 0) {
178 console.log('Login form load time:', entries[0].loadEventEnd - entries[0].loadEventStart, 'ms');
179 }
180 }, 1000);
181 }
182 }
183 }
184
185 /**
186 * Initialize AJAX login with production optimizations
187 */
188 function initAjaxLogin() {
189 const $loginForm = $('.yatra-login-form');
190
191 // Only use AJAX if the form has data-ajax="true" attribute
192 if ($loginForm.data('ajax') === true) {
193 // Performance: Use event delegation for better performance
194 $document.on('submit', '.yatra-login-form[data-ajax="true"]', function(e) {
195 e.preventDefault();
196 e.stopPropagation();
197
198 const $form = $(this);
199 const $submitBtn = $form.find('button[type="submit"], input[type="submit"]');
200 const $username = $form.find('[name="log"], [name="username"], [name="email"]');
201 const $password = $form.find('[name="pwd"], [name="password"]');
202
203 // Prevent multiple submissions
204 if ($submitBtn.prop('disabled')) {
205 return false;
206 }
207
208 // Enhanced form validation
209 const validation = validateLoginForm($username, $password);
210 if (!validation.valid) {
211 showFormError($form, validation.message);
212 return false;
213 }
214
215 // Reset previous errors
216 $('.yatra-form-error').remove();
217 // Field-level validation is already handled by validateLoginForm()
218
219 // Show loading state immediately to prevent flicker
220 $submitBtn.prop('disabled', true);
221 // Preserve the original markup once, then toggle the built-in loading spans.
222 if (!$submitBtn.data('yatraOriginalHtml')) {
223 $submitBtn.data('yatraOriginalHtml', $submitBtn.html());
224 }
225 $submitBtn.addClass('yatra-is-loading');
226 $submitBtn.find('.yatra-btn-text').hide();
227 $submitBtn.find('.yatra-btn-loading').show();
228
229 // Prepare AJAX request data
230 const formData = {
231 action: 'yatra_ajax_login',
232 username: $username.val().trim(),
233 password: $password.val().trim(),
234 rememberme: $form.find('[name="rememberme"]').is(':checked') ? 'forever' : '',
235 yatra_login_nonce: $form.find('[name="yatra_login_nonce"]').val(),
236 redirect_to: $form.find('[name="redirect_to"]').val() || '',
237 ajax: true
238 };
239
240 // Perform AJAX request with timeout
241 const ajaxRequest = $.ajax({
242 url: yatra_ajax?.ajax_url || '/wp-admin/admin-ajax.php',
243 type: 'POST',
244 data: formData,
245 timeout: 15000, // 15 second timeout
246 dataType: 'json'
247 });
248
249 // Handle success
250 ajaxRequest.done(function(response) {
251 if (response.success) {
252 // Success: Redirect or show success message
253 if (response.data && (response.data.redirect_url || response.data.redirect)) {
254 window.location.href = response.data.redirect_url || response.data.redirect;
255 } else {
256 showFormSuccess($form, response.data.message || 'Login successful!');
257 setTimeout(() => {
258 window.location.reload();
259 }, 1500);
260 }
261 } else {
262 // Error: Show error message
263 showFormError($form, response.data.message || 'Login failed. Please try again.');
264 }
265 });
266
267 // Handle error
268 ajaxRequest.fail(function(xhr, status, error) {
269 let errorMessage = 'Network error. Please check your connection and try again.';
270
271 if (status === 'timeout') {
272 errorMessage = 'Request timed out. Please try again.';
273 } else if (xhr.responseJSON && xhr.responseJSON.data) {
274 errorMessage = xhr.responseJSON.data.message || errorMessage;
275 }
276
277 showFormError($form, errorMessage);
278 });
279
280 // Always restore button state
281 ajaxRequest.always(function() {
282 $submitBtn.prop('disabled', false);
283 $submitBtn.removeClass('yatra-is-loading');
284 $submitBtn.find('.yatra-btn-loading').hide();
285 $submitBtn.find('.yatra-btn-text').show();
286
287 // If some other code replaced the markup, restore it.
288 const originalHtml = $submitBtn.data('yatraOriginalHtml');
289 if (originalHtml && $submitBtn.find('.yatra-btn-text').length === 0) {
290 $submitBtn.html(originalHtml);
291 }
292 });
293
294 return false;
295 });
296 }
297 }
298
299 /**
300 * Show field-specific error message
301 */
302 function showFieldError($field, message) {
303 // Remove existing field error
304 $field.siblings('.yatra-form-error').fadeOut(200, function() {
305 $(this).remove();
306 });
307
308 // Create and insert error message with fade-in to prevent flicker
309 const $error = $('<div class="yatra-form-error" style="display: none;">' + message + '</div>');
310 $error.css({
311 color: '#dc2626',
312 fontSize: '0.875rem',
313 marginTop: '4px',
314 fontWeight: '500',
315 opacity: 0,
316 transition: 'opacity 0.2s ease'
317 });
318
319 $field.after($error);
320
321 // Fade in error message
322 setTimeout(function() {
323 $error.css({
324 display: 'block',
325 opacity: 1
326 });
327 }, 10);
328
329 // Remove error on focus
330 $field.on('focus', function() {
331 $(this).removeClass('error');
332 $(this).siblings('.yatra-form-error').fadeOut(200, function() {
333 $(this).remove();
334 });
335 });
336 }
337
338 /**
339 * Show form error message
340 */
341 function showFormError($form, message) {
342 // Remove existing error with fade out
343 $form.find('.yatra-form-error').fadeOut(200, function() {
344 $(this).remove();
345 });
346
347 // Create and insert error message at the top of the form with fade-in
348 const $error = $('<div class="yatra-form-error" style="display: none;">' + message + '</div>');
349 $error.css({
350 color: '#dc2626',
351 backgroundColor: '#fef2f2',
352 border: '1px solid #fecaca',
353 borderRadius: '8px',
354 padding: '12px 16px',
355 marginBottom: '20px',
356 fontWeight: '500',
357 opacity: 0,
358 transition: 'opacity 0.2s ease'
359 });
360
361 $form.prepend($error);
362
363 // Fade in error message
364 setTimeout(function() {
365 $error.css({
366 display: 'block',
367 opacity: 1
368 });
369 }, 10);
370
371 // Scroll to error smoothly
372 setTimeout(function() {
373 $('html, body').animate({
374 scrollTop: $form.offset().top - 100
375 }, 300);
376 }, 50);
377 }
378
379 /**
380 * Show form success message
381 */
382 function showFormSuccess($form, message) {
383 // Remove existing messages
384 $form.find('.yatra-form-error, .yatra-form-success').fadeOut(200, function() {
385 $(this).remove();
386 });
387
388 // Create and insert success message
389 const $success = $('<div class="yatra-form-success" style="display: none;">' + message + '</div>');
390 $success.css({
391 color: '#059669',
392 backgroundColor: '#f0fdf4',
393 border: '1px solid #bbf7d0',
394 borderRadius: '8px',
395 padding: '12px 16px',
396 marginBottom: '20px',
397 fontWeight: '500',
398 opacity: 0,
399 transition: 'opacity 0.2s ease'
400 });
401
402 $form.prepend($success);
403
404 // Fade in success message
405 setTimeout(function() {
406 $success.css({
407 display: 'block',
408 opacity: 1
409 });
410 }, 10);
411 }
412
413 })(jQuery);
414