PageSourceSearch

https://srilankasquash.lk/wp-content/plugins/sls-player-registration/assets/js/registration-form.js?ver=1.0.0

js srilankasquash.lk collected 2026-10-02 14:28:11 UTC 27,599 bytes, 777 lines download raw bytes

1/**
2 * SLS Player Registration Form JavaScript
3 */
4
5jQuery(document).ready(function($) {
6    'use strict';
7    
8    // Form submission handler
9    $('#sls-registration-form').on('submit', function(e) {
10        e.preventDefault();
11        
12        var $form = $(this);
13        var $submitBtn = $form.find('button[type="submit"]');
14        var $messages = $('#form-messages');
15        
16        // Show loading state
17        $form.addClass('loading');
18        $submitBtn.prop('disabled', true).text('Processing...');
19        
20        // Clear previous messages
21        $messages.removeClass('success error info').hide();
22        
23        // Validate form
24        if (!validateForm($form)) {
25            $form.removeClass('loading');
26            $submitBtn.prop('disabled', false).text('Submit Registration');
27            return;
28        }
29        
30        // Prepare form data
31        var formData = new FormData(this);
32        
33        // Convert date of birth from dd/mm/yyyy to Y-m-d format for backend
34        var dobInput = formData.get('date_of_birth');
35        if (dobInput) {
36            var dateObj = parseDDMMYYYY(dobInput);
37            if (dateObj) {
38                var year = dateObj.getFullYear();
39                var month = String(dateObj.getMonth() + 1).padStart(2, '0');
40                var day = String(dateObj.getDate()).padStart(2, '0');
41                formData.set('date_of_birth', year + '-' + month + '-' + day);
42            }
43        }
44        
45        formData.append('action', 'sls_submit_registration');
46        formData.append('nonce', sls_ajax.nonce);
47        
48        // Submit form via AJAX
49        $.ajax({
50            url: sls_ajax.ajax_url,
51            type: 'POST',
52            data: formData,
53            processData: false,
54            contentType: false,
55            success: function(response) {
56                $form.removeClass('loading');
57                $submitBtn.prop('disabled', false).text('Submit Registration');
58                
59                if (response.success) {
60                    if (response.data.confirmation_page) {
61                        // Show confirmation page
62                        showConfirmationPage(response.data.data);
63                    } else if (response.data.redirect_url) {
64                        // Redirect to payment
65                        setTimeout(function() {
66                            window.location.href = response.data.redirect_url;
67                        }, 2000);
68                    } else {
69                        showMessage('success', response.data.message);
70                        // Reset form after successful submission
71                        setTimeout(function() {
72                            $form[0].reset();
73                            $form.find('.photo-preview').empty();
74                        }, 3000);
75                    }
76                } else {
77                    showMessage('error', response.data || 'An error occurred. Please try again.');
78                }
79            },
80            error: function(xhr, status, error) {
81                $form.removeClass('loading');
82                $submitBtn.prop('disabled', false).text('Submit Registration');
83                showMessage('error', 'Network error. Please check your connection and try again.');
84            }
85        });
86    });
87    
88    // Photo upload handler
89    $('#player_photo').on('change', function() {
90        var file = this.files[0];
91        var $preview = $('#photo-preview');
92        
93        if (file) {
94            // Validate file type
95            var allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'];
96            if (!allowedTypes.includes(file.type)) {
97                showMessage('error', 'Please select a valid image file (JPG, PNG, GIF)');
98                this.value = '';
99                return;
100            }
101            
102            // Validate file size (3MB)
103            if (file.size > 3145728) {
104                showMessage('error', 'File size must be less than 3MB');
105                this.value = '';
106                return;
107            }
108            
109            // Show preview
110            var reader = new FileReader();
111            reader.onload = function(e) {
112                $preview.html('<img src="' + e.target.result + '" alt="Photo Preview">');
113            };
114            reader.readAsDataURL(file);
115            
116            // Upload file
117            uploadPhoto(file);
118        } else {
119            $preview.empty();
120        }
121    });
122    
123    // Form reset handler
124    $('button[type="reset"]').on('click', function() {
125        $('#photo-preview').empty();
126        $('#form-messages').hide();
127        clearFormErrors();
128        
129        // Reset reCAPTCHA if enabled
130        if (sls_ajax.recaptcha_site_key && typeof grecaptcha !== 'undefined') {
131            grecaptcha.reset();
132        }
133    });
134    
135    // Real-time validation
136    $('input[required], select[required]').on('blur', function() {
137        validateField($(this));
138    });
139    
140    // Initialize datepicker for date of birth
141    $('#date_of_birth').datepicker({
142        dateFormat: 'dd/mm/yy',
143        changeMonth: true,
144        changeYear: true,
145        yearRange: '1900:' + new Date().getFullYear(),
146        maxDate: 0, // Cannot select future dates
147        onSelect: function(dateText, inst) {
148            clearFieldError($(this));
149            // Trigger age calculation
150            $(this).trigger('change');
151        }
152    });
153    
154    // Validate date of birth on blur
155    $('#date_of_birth').on('blur', function() {
156        var $field = $(this);
157        var value = $field.val().trim();
158        
159        if (value.length > 0) {
160            if (!isValidDDMMYYYY(value)) {
161                showFieldError($field, 'Please enter a valid date in DD/MM/YYYY format');
162            } else {
163                // Check if date is not in the future
164                var dateObj = parseDDMMYYYY(value);
165                if (dateObj) {
166                    var today = new Date();
167                    today.setHours(0, 0, 0, 0);
168                    if (dateObj > today) {
169                        showFieldError($field, 'Date of birth cannot be in the future');
170                    }
171                }
172            }
173        }
174    });
175
176    // Tel input live validation to clear previous errors
177    $('input[type="tel"]').on('input', function() {
178        var $field = $(this);
179        var value = $field.val().trim();
180
181        clearFieldError($field);
182
183        if (!value.length) {
184            return;
185        }
186
187        if (!isValidPhone(value)) {
188            showFieldError($field, 'Please enter a valid phone number');
189        }
190    });
191    
192    // Registration type change handler
193    $('#registration_type').on('change', function() {
194        updateRegistrationAmount();
195    });
196    
197    // Initialize
198    updateRegistrationAmount();
199    
200    /**
201     * Validate entire form
202     */
203    function validateForm($form) {
204        var isValid = true;
205        var $requiredFields = $form.find('input[required], select[required], textarea[required]');
206        
207        $requiredFields.each(function() {
208            if (!validateField($(this))) {
209                isValid = false;
210            }
211        });
212        
213        // Validate email format
214        var $email = $form.find('input[type="email"]');
215        if ($email.length && $email.val()) {
216            if (!isValidEmail($email.val())) {
217                showFieldError($email, 'Please enter a valid email address');
218                isValid = false;
219            }
220        }
221        
222        // Validate phone format
223        $form.find('input[type="tel"]').each(function() {
224            var $phoneField = $(this);
225            var phoneValue = $phoneField.val().trim();
226            if (phoneValue.length && !isValidPhone(phoneValue)) {
227                showFieldError($phoneField, 'Please enter a valid phone number');
228                isValid = false;
229            }
230        });
231        
232        // Validate date of birth format (dd/mm/yyyy)
233        var $dobField = $form.find('#date_of_birth');
234        if ($dobField.length) {
235            var dobValue = $dobField.val().trim();
236            if ($dobField.prop('required') && !dobValue) {
237                showFieldError($dobField, 'Date of birth is required');
238                isValid = false;
239            } else if (dobValue.length > 0) {
240                if (!isValidDDMMYYYY(dobValue)) {
241                    showFieldError($dobField, 'Please enter a valid date in DD/MM/YYYY format');
242                    isValid = false;
243                } else {
244                    // Check if date is not in the future
245                    var dateObj = parseDDMMYYYY(dobValue);
246                    if (dateObj) {
247                        var today = new Date();
248                        today.setHours(0, 0, 0, 0);
249                        if (dateObj > today) {
250                            showFieldError($dobField, 'Date of birth cannot be in the future');
251                            isValid = false;
252                        }
253                    }
254                }
255            }
256        }
257        
258        // Validate reCAPTCHA if enabled
259        if (sls_ajax.recaptcha_site_key) {
260            var recaptchaResponse = grecaptcha.getResponse();
261            if (!recaptchaResponse) {
262                var $recaptcha = $form.find('.g-recaptcha');
263                var $recaptchaError = $form.find('.recaptcha-error');
264                if ($recaptchaError.length) {
265                    $recaptchaError.show();
266                } else {
267                    $recaptcha.after('<span class="recaptcha-error" style="color: red; display: block;">Please complete the reCAPTCHA verification.</span>');
268                }
269                isValid = false;
270            } else {
271                $form.find('.recaptcha-error').hide();
272            }
273        }
274        
275        return isValid;
276    }
277    
278    /**
279     * Validate individual field
280     */
281    function validateField($field) {
282        var value = $field.val().trim();
283        var isValid = true;
284        
285        // Clear previous errors
286        clearFieldError($field);
287        
288        if ($field.prop('required') && !value) {
289            showFieldError($field, 'This field is required');
290            isValid = false;
291        }
292
293        if (isValid && $field.attr('type') === 'tel' && value && !isValidPhone(value)) {
294            showFieldError($field, 'Please enter a valid phone number');
295            isValid = false;
296        }
297        
298        // Validate date of birth format
299        if (isValid && $field.attr('id') === 'date_of_birth' && value) {
300            if (!isValidDDMMYYYY(value)) {
301                showFieldError($field, 'Please enter a valid date in DD/MM/YYYY format');
302                isValid = false;
303            } else {
304                var dateObj = parseDDMMYYYY(value);
305                if (dateObj) {
306                    var today = new Date();
307                    today.setHours(0, 0, 0, 0);
308                    if (dateObj > today) {
309                        showFieldError($field, 'Date of birth cannot be in the future');
310                        isValid = false;
311                    }
312                }
313            }
314        }
315        
316        return isValid;
317    }
318    
319    /**
320     * Show field error
321     */
322    function showFieldError($field, message) {
323        $field.addClass('error');
324        $field.closest('.form-group').addClass('error');
325        
326        var $errorDiv = $field.siblings('.error-message');
327        if ($errorDiv.length === 0) {
328            $errorDiv = $('<div class="error-message"></div>');
329            $field.after($errorDiv);
330        }
331        $errorDiv.text(message);
332    }
333    
334    /**
335     * Clear field error
336     */
337    function clearFieldError($field) {
338        $field.removeClass('error');
339        $field.closest('.form-group').removeClass('error');
340        $field.siblings('.error-message').remove();
341    }
342    
343    /**
344     * Clear all form errors
345     */
346    function clearFormErrors() {
347        $('.form-group').removeClass('error');
348        $('.error-message').remove();
349        $('input, select, textarea').removeClass('error');
350    }
351    
352    /**
353     * Validate email format
354     */
355    function isValidEmail(email) {
356        var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
357        return emailRegex.test(email);
358    }
359    
360    /**
361     * Validate phone format
362     */
363    function isValidPhone(phone) {
364        var trimmed = phone.trim();
365
366        if (!trimmed.length) {
367            return true;
368        }
369
370        var allowedChars = /^\+?[0-9\s().-]+$/;
371        if (!allowedChars.test(trimmed)) {
372            return false;
373        }
374
375        if ((trimmed.match(/\+/g) || []).length > 1) {
376            return false;
377        }
378
379        if (trimmed.indexOf('+') > 0) {
380            return false;
381        }
382
383        var digitCount = trimmed.replace(/\D/g, '').length;
384        return digitCount >= 7 && digitCount <= 15;
385    }
386    
387    /**
388     * Upload photo
389     */
390    function uploadPhoto(file) {
391        var formData = new FormData();
392        formData.append('file', file);
393        formData.append('action', 'sls_upload_player_photo');
394        formData.append('nonce', sls_ajax.nonce);
395        
396        $.ajax({
397            url: sls_ajax.ajax_url,
398            type: 'POST',
399            data: formData,
400            processData: false,
401            contentType: false,
402            success: function(response) {
403                if (response.success) {
404                    // Store photo URL in hidden field
405                    $('<input>').attr({
406                        type: 'hidden',
407                        name: 'photo_url',
408                        value: response.data.url
409                    }).appendTo('#sls-registration-form');
410                    
411                    showMessage('success', 'Photo uploaded successfully');
412                } else {
413                    showMessage('error', response.data || 'Photo upload failed');
414                }
415            },
416            error: function() {
417                showMessage('error', 'Photo upload failed. Please try again.');
418            }
419        });
420    }
421    
422    /**
423     * Update registration amount display
424     */
425    function updateRegistrationAmount() {
426        var $typeSelect = $('#registration_type');
427        var selectedType = $typeSelect.val();
428        var amounts = {
429            'new': 5000,
430            'renewal': 3000,
431            'junior': 2000
432        };
433        
434        if (amounts[selectedType]) {
435            var $amountDisplay = $typeSelect.siblings('.amount-display');
436            if ($amountDisplay.length === 0) {
437                $amountDisplay = $('<div class="amount-display" style="margin-top: 5px; font-weight: bold; color: #2b4079;"></div>');
438                $typeSelect.after($amountDisplay);
439            }
440            $amountDisplay.text('Amount: LKR ' + amounts[selectedType].toLocaleString());
441        }
442    }
443    
444    /**
445     * Show confirmation page
446     */
447    function showConfirmationPage(data) {
448        var confirmationHtml = generateConfirmationHtml(data);
449        
450        // Hide the form and show confirmation
451        $('.sls-registration-container').html(confirmationHtml);
452        
453        // Add event handlers for confirmation buttons
454        $('#back-to-form').on('click', function() {
455            location.reload();
456        });
457
458        $('#terms-conditions').on('change', function() {
459            $('#confirm-registration').prop('disabled', !this.checked);
460        });
461        
462        $('#confirm-registration').on('click', function() {
463            if (!$('#terms-conditions').is(':checked')) {
464                return;
465            }
466            confirmRegistration();
467        });
468    }
469    
470    /**
471     * Generate confirmation HTML
472     */
473    function generateConfirmationHtml(data) {
474        var html = '<div class="sls-confirmation-container">';
475        html += '<div class="sls-confirmation-header">';
476        html += '<h2>Please review and confirm the following.</h2>';
477        html += '</div>';
478        html += '<div class="sls-confirmation-content">';
479        
480        // Personal Details
481        html += '<div class="confirmation-row">';
482        html += '<div class="confirmation-label">First Name:</div>';
483        html += '<div class="confirmation-value">' + (data.first_name || '') + '</div>';
484        html += '</div>';
485        
486        html += '<div class="confirmation-row">';
487        html += '<div class="confirmation-label">Middle Name:</div>';
488        html += '<div class="confirmation-value">' + (data.middle_name || '') + '</div>';
489        html += '</div>';
490        
491        html += '<div class="confirmation-row">';
492        html += '<div class="confirmation-label">Surname:</div>';
493        html += '<div class="confirmation-value">' + (data.surname || '') + '</div>';
494        html += '</div>';
495        
496        // Address Information
497        html += '<div class="confirmation-row">';
498        html += '<div class="confirmation-label">Address 1:</div>';
499        html += '<div class="confirmation-value">' + (data.address_1 || '') + '</div>';
500        html += '</div>';
501        
502        html += '<div class="confirmation-row">';
503        html += '<div class="confirmation-label">Address 2:</div>';
504        html += '<div class="confirmation-value">' + (data.address_2 || '') + '</div>';
505        html += '</div>';
506        
507        html += '<div class="confirmation-row">';
508        html += '<div class="confirmation-label">City:</div>';
509        html += '<div class="confirmation-value">' + (data.city || '') + '</div>';
510        html += '</div>';
511        
512        // Date of Birth (calculate age)
513        if (data.date_of_birth) {
514            var dob = new Date(data.date_of_birth);
515            var now = new Date();
516            var age = now.getFullYear() - dob.getFullYear();
517            var monthDiff = now.getMonth() - dob.getMonth();
518            if (monthDiff < 0 || (monthDiff === 0 && now.getDate() < dob.getDate())) {
519                age--;
520            }
521            var months = (now.getFullYear() - dob.getFullYear()) * 12 + (now.getMonth() - dob.getMonth());
522            if (now.getDate() < dob.getDate()) months--;
523            
524            // Format date for display (convert Y-m-d to dd/mm/yyyy)
525            var dobDate = new Date(data.date_of_birth);
526            var displayDate = String(dobDate.getDate()).padStart(2, '0') + '/' + 
527                            String(dobDate.getMonth() + 1).padStart(2, '0') + '/' + 
528                            dobDate.getFullYear();
529            
530            html += '<div class="confirmation-row">';
531            html += '<div class="confirmation-label">Date of Birth:</div>';
532            html += '<div class="confirmation-value">' + displayDate + ' (' + age + ' years ' + (months % 12) + ' months)</div>';
533            html += '</div>';
534        }
535        
536        // html += '<div class="confirmation-row">';
537        // html += '<div class="confirmation-label">Age Group:</div>';
538        // html += '<div class="confirmation-value">' + (data.age_group || '') + '</div>';
539        // html += '</div>';
540        
541        html += '<div class="confirmation-row">';
542        html += '<div class="confirmation-label">Player Category:</div>';
543        html += '<div class="confirmation-value">' + (data.player_category || '') + '</div>';
544        html += '</div>';
545        
546        html += '<div class="confirmation-row">';
547        html += '<div class="confirmation-label">School / Institution:</div>';
548        html += '<div class="confirmation-value">' + (data.school_institi || '') + '</div>';
549        html += '</div>';
550        
551        html += '<div class="confirmation-row">';
552        html += '<div class="confirmation-label">Coached By:</div>';
553        html += '<div class="confirmation-value">' + (data.coached_by || '') + '</div>';
554        html += '</div>';
555        
556        html += '<div class="confirmation-row">';
557        html += '<div class="confirmation-label">Rankdin ID:</div>';
558        html += '<div class="confirmation-value">' + (data.rankdin_id || '') + '</div>';
559        html += '</div>';
560        
561        html += '<div class="confirmation-row">';
562        html += '<div class="confirmation-label">WSF Spin Number:</div>';
563        html += '<div class="confirmation-value">' + (data.wsfspinnumber || '') + '</div>';
564        html += '</div>';
565        
566        html += '<div class="confirmation-row">';
567        html += '<div class="confirmation-label">Contact Number 1 - Mobile:</div>';
568        html += '<div class="confirmation-value">' + (data.contact_number_1 || '') + '</div>';
569        html += '</div>';
570        
571        html += '<div class="confirmation-row">';
572        html += '<div class="confirmation-label">Contact Number 2 - Mobile:</div>';
573        html += '<div class="confirmation-value">' + (data.contact_number_2 || '') + '</div>';
574        html += '</div>';
575        
576        // html += '<div class="confirmation-row">';
577        // html += '<div class="confirmation-label">Land Line:</div>';
578        // html += '<div class="confirmation-value">' + (data.contact_number_3 || '') + '</div>';
579        // html += '</div>';
580        
581        html += '<div class="confirmation-row">';
582        html += '<div class="confirmation-label">Email:</div>';
583        html += '<div class="confirmation-value">' + (data.email || '') + '</div>';
584        html += '</div>';
585        
586        html += '</div>';
587        html += '<div class="sls-terms-section">';
588        html += '<div class="sls-checkbox-group">';
589        html += '<input type="checkbox" id="terms-conditions" name="terms_conditions">';
590        html += '<label for="terms-conditions">I have read and agreed to the <a href="https://srilankasquash.lk/SRILANKASQUASHPAYMENTPOLICY.pdf" target="_blank" rel="noopener noreferrer">Terms &amp; Conditions</a></label>';
591        html += '</div>';
592        html += '</div>';
593        html += '<div class="confirmation-actions">';
594        html += '<button type="button" id="back-to-form" class="btn btn-back">Back</button>';
595        html += '<button type="button" id="confirm-registration" class="btn btn-confirm" disabled>Confirm</button>';
596        html += '</div>';
597        html += '</div>';
598        
599        return html;
600    }
601    
602    /**
603     * Confirm registration
604     */
605    function confirmRegistration() {
606        var $confirmBtn = $('#confirm-registration');
607        $confirmBtn.prop('disabled', true).text('Processing...');
608        
609        $.ajax({
610            url: sls_ajax.ajax_url,
611            type: 'POST',
612            data: {
613                action: 'sls_confirm_registration',
614                nonce: sls_ajax.nonce
615            },
616            success: function(response) {
617                if (response.success) {
618                    if (response.data.redirect_url) {
619                        // Redirect to payment
620                        window.location.href = response.data.redirect_url;
621                    } else {
622                        showMessage('success', response.data.message);
623                    }
624                } else {
625                    $confirmBtn.prop('disabled', !$('#terms-conditions').is(':checked')).text('Confirm');
626                    showMessage('error', response.data || 'An error occurred. Please try again.');
627                }
628            },
629            error: function() {
630                $confirmBtn.prop('disabled', !$('#terms-conditions').is(':checked')).text('Confirm');
631                showMessage('error', 'Network error. Please try again.');
632            }
633        });
634    }
635    
636    /**
637     * Show message
638     */
639    function showMessage(type, message) {
640        var $messages = $('#form-messages');
641        $messages.removeClass('success error info')
642                 .addClass(type)
643                 .html(message)
644                 .show();
645        
646        // Auto-hide success messages
647        if (type === 'success') {
648            setTimeout(function() {
649                $messages.fadeOut();
650            }, 5000);
651        }
652        
653        // Scroll to message
654        $('html, body').animate({
655            scrollTop: $messages.offset().top - 100
656        }, 500);
657    }
658    
659    /**
660     * Format phone number as user types
661     */
662    $('#phone').on('input', function() {
663        var value = this.value.replace(/\D/g, '');
664        var formattedValue = value.replace(/(\d{3})(\d{3})(\d{4})/, '$1-$2-$3');
665        if (value.length <= 10) {
666            this.value = formattedValue;
667        }
668    });
669    
670    /**
671     * Age group auto-selection based on date of birth
672     */
673    $('#date_of_birth').on('change', function() {
674        var dobValue = $(this).val().trim();
675        if (dobValue && isValidDDMMYYYY(dobValue)) {
676            var dob = parseDDMMYYYY(dobValue);
677            if (dob) {
678                var today = new Date();
679                var age = today.getFullYear() - dob.getFullYear();
680                var monthDiff = today.getMonth() - dob.getMonth();
681                
682                if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < dob.getDate())) {
683                    age--;
684                }
685                
686                var ageGroup = '';
687                if (age < 12) {
688                    ageGroup = 'under-12';
689                } else if (age >= 12 && age <= 15) {
690                    ageGroup = '12-15';
691                } else if (age >= 16 && age <= 18) {
692                    ageGroup = '16-18';
693                } else if (age >= 19 && age <= 25) {
694                    ageGroup = '19-25';
695                } else {
696                    ageGroup = '26-plus';
697                }
698                
699                $('#age_group').val(ageGroup);
700            }
701        }
702    });
703    
704    /**
705     * Validate dd/mm/yyyy format
706     */
707    function isValidDDMMYYYY(value) {
708        if (!value) {
709            return false;
710        }
711        
712        // Check format: dd/mm/yyyy
713        var datePattern = /^(\d{2})\/(\d{2})\/(\d{4})$/;
714        var match = value.match(datePattern);
715        
716        if (!match) {
717            return false;
718        }
719        
720        var day = parseInt(match[1], 10);
721        var month = parseInt(match[2], 10);
722        var year = parseInt(match[3], 10);
723        
724        // Basic range checks
725        if (month < 1 || month > 12) {
726            return false;
727        }
728        if (day < 1 || day > 31) {
729            return false;
730        }
731        if (year < 1900 || year > new Date().getFullYear()) {
732            return false;
733        }
734        
735        // Create date object to validate (handles leap years, etc.)
736        var dateObj = new Date(year, month - 1, day);
737        if (dateObj.getFullYear() !== year || 
738            dateObj.getMonth() !== (month - 1) || 
739            dateObj.getDate() !== day) {
740            return false;
741        }
742        
743        return true;
744    }
745    
746    /**
747     * Parse dd/mm/yyyy string to Date object
748     */
749    function parseDDMMYYYY(value) {
750        if (!value) {
751            return null;
752        }
753        
754        // Check format: dd/mm/yyyy
755        var datePattern = /^(\d{2})\/(\d{2})\/(\d{4})$/;
756        var match = value.match(datePattern);
757        
758        if (!match) {
759            return null;
760        }
761        
762        var day = parseInt(match[1], 10);
763        var month = parseInt(match[2], 10);
764        var year = parseInt(match[3], 10);
765        
766        var dateObj = new Date(year, month - 1, day);
767        
768        // Verify the date is valid
769        if (dateObj.getFullYear() !== year || 
770            dateObj.getMonth() !== (month - 1) || 
771            dateObj.getDate() !== day) {
772            return null;
773        }
774        
775        return dateObj;
776    }
777});

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.