PageSourceSearch

https://xpathlabs.ai/auth-shared.js

js xpathlabs.ai collected 2026-10-05 13:20:30 UTC 6,139 bytes, 196 lines download raw bytes

1/**
2 * Shared Auth Page JavaScript
3 * Used by: reset-password.html, accept-invite.html
4 */
5
6(function() {
7    'use strict';
8
9    /**
10     * Password validation rules
11     */
12    const PASSWORD_RULES = {
13        length: { test: (p) => p.length >= 8, label: '8+ characters' },
14        upper: { test: (p) => /[A-Z]/.test(p), label: 'Uppercase' },
15        lower: { test: (p) => /[a-z]/.test(p), label: 'Lowercase' },
16        number: { test: (p) => /[0-9]/.test(p), label: 'Number' }
17    };
18
19    /**
20     * Update a single requirement indicator
21     */
22    function updateRequirement(req, met) {
23        const el = document.querySelector(`[data-req="${req}"]`);
24        if (!el) return;
25
26        const icon = el.querySelector('i');
27        if (met) {
28            el.classList.add('met');
29            if (icon) {
30                icon.classList.remove('fa-circle');
31                icon.classList.add('fa-check');
32            }
33        } else {
34            el.classList.remove('met');
35            if (icon) {
36                icon.classList.remove('fa-check');
37                icon.classList.add('fa-circle');
38            }
39        }
40    }
41
42    /**
43     * Validate password against all rules
44     */
45    function validatePassword(password) {
46        const results = {};
47        for (const [key, rule] of Object.entries(PASSWORD_RULES)) {
48            results[key] = rule.test(password);
49            updateRequirement(key, results[key]);
50        }
51        return results;
52    }
53
54    /**
55     * Check if password meets all requirements
56     */
57    function isPasswordValid(password) {
58        return Object.values(PASSWORD_RULES).every(rule => rule.test(password));
59    }
60
61    /**
62     * Setup password toggle functionality
63     */
64    function setupPasswordToggle() {
65        document.querySelectorAll('.toggle-password').forEach(button => {
66            // Prevent adding duplicate listeners
67            if (button.dataset.toggleSetup) return;
68            button.dataset.toggleSetup = 'true';
69
70            button.addEventListener('click', function() {
71                const input = this.parentElement.querySelector('input');
72                const icon = this.querySelector('i');
73
74                if (input.type === 'password') {
75                    input.type = 'text';
76                    icon.classList.remove('fa-eye');
77                    icon.classList.add('fa-eye-slash');
78                } else {
79                    input.type = 'password';
80                    icon.classList.remove('fa-eye-slash');
81                    icon.classList.add('fa-eye');
82                }
83            });
84        });
85    }
86
87    /**
88     * Setup password validation on input
89     */
90    function setupPasswordValidation(passwordId, confirmId, errorId) {
91        const passwordInput = document.getElementById(passwordId);
92        const confirmInput = document.getElementById(confirmId);
93        const errorEl = errorId ? document.getElementById(errorId) : null;
94
95        if (passwordInput) {
96            passwordInput.addEventListener('input', function() {
97                validatePassword(this.value);
98            });
99        }
100
101        if (confirmInput && passwordInput) {
102            confirmInput.addEventListener('input', function() {
103                const password = passwordInput.value;
104                const confirm = this.value;
105
106                if (confirm && password !== confirm) {
107                    if (errorEl) errorEl.classList.remove('hidden');
108                    confirmInput.classList.add('error');
109                } else {
110                    if (errorEl) errorEl.classList.add('hidden');
111                    confirmInput.classList.remove('error');
112                }
113            });
114        }
115
116        // Setup toggle buttons
117        setupPasswordToggle();
118    }
119
120    /**
121     * Set button loading state
122     */
123    function setButtonLoading(button, loading, text, iconClass) {
124        if (loading) {
125            button.disabled = true;
126            button.innerHTML = `<div class="loading-spinner"></div><span>${text || 'Loading...'}</span>`;
127        } else {
128            button.disabled = false;
129            const icon = iconClass ? `<i class="fas ${iconClass}"></i>` : '';
130            button.innerHTML = `${icon}<span>${text || 'Submit'}</span>`;
131        }
132    }
133
134    /**
135     * Show error message
136     */
137    function showError(container, message) {
138        const errorEl = container.querySelector('.auth-error') || createMessageEl(container, 'error');
139        errorEl.querySelector('span').textContent = message;
140        errorEl.style.display = 'flex';
141
142        const successEl = container.querySelector('.auth-success');
143        if (successEl) successEl.style.display = 'none';
144    }
145
146    /**
147     * Show success message
148     */
149    function showSuccess(container, message) {
150        const successEl = container.querySelector('.auth-success') || createMessageEl(container, 'success');
151        successEl.querySelector('span').textContent = message;
152        successEl.style.display = 'flex';
153
154        const errorEl = container.querySelector('.auth-error');
155        if (errorEl) errorEl.style.display = 'none';
156    }
157
158    /**
159     * Create message element if it doesn't exist
160     */
161    function createMessageEl(container, type) {
162        const el = document.createElement('div');
163        el.className = `auth-${type}`;
164        el.innerHTML = `
165            <i class="fas fa-${type === 'success' ? 'check-circle' : 'exclamation-circle'}"></i>
166            <span></span>
167        `;
168        el.style.display = 'none';
169
170        const form = container.querySelector('form');
171        if (form) {
172            form.parentNode.insertBefore(el, form);
173        } else {
174            container.appendChild(el);
175        }
176
177        return el;
178    }
179
180    // Export to window
181    window.AuthShared = {
182        PASSWORD_RULES,
183        validatePassword,
184        isPasswordValid,
185        setupPasswordValidation,
186        setupPasswordToggle,
187        setButtonLoading,
188        showError,
189        showSuccess
190    };
191
192    // Auto-setup on DOMContentLoaded
193    document.addEventListener('DOMContentLoaded', function() {
194        setupPasswordToggle();
195    });
196})();

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.