PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.5.1
Yatra – Travel Booking & Tour Operator Software v3.0.5.1
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 2.0.10 All 83 releases
yatra / assets / js / login-shortcode.js

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

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