Automatically fills VFS Global visa application forms with saved data
Size
14.8 KB
Version
1.1.2
Created
Dec 8, 2025
Updated
4 days ago
1// ==UserScript==
2// @name VFS Global Form Auto-Filler
3// @description Automatically fills VFS Global visa application forms with saved data
4// @version 1.1.2
5// @match https://*.visa.vfsglobal.com/*
6// @icon https://liftassets.vfsglobal.com/_angular/assets/images/global/vfs.ico
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 console.log('VFS Global Form Auto-Filler initialized');
12
13 // Debounce function to prevent excessive calls
14 function debounce(func, wait) {
15 let timeout;
16 return function executedFunction(...args) {
17 const later = () => {
18 clearTimeout(timeout);
19 func(...args);
20 };
21 clearTimeout(timeout);
22 timeout = setTimeout(later, wait);
23 };
24 }
25
26 // Add custom styles for the UI
27 TM_addStyle(`
28 .vfs-autofill-container {
29 position: fixed;
30 top: 20px;
31 right: 20px;
32 z-index: 10000;
33 background: white;
34 border: 2px solid #ff6b35;
35 border-radius: 8px;
36 padding: 15px;
37 box-shadow: 0 4px 12px rgba(0,0,0,0.15);
38 font-family: Arial, sans-serif;
39 max-width: 300px;
40 }
41
42 .vfs-autofill-header {
43 font-size: 16px;
44 font-weight: bold;
45 color: #333;
46 margin-bottom: 10px;
47 display: flex;
48 justify-content: space-between;
49 align-items: center;
50 }
51
52 .vfs-close-btn {
53 background: none;
54 border: none;
55 font-size: 20px;
56 cursor: pointer;
57 color: #666;
58 padding: 0;
59 width: 24px;
60 height: 24px;
61 line-height: 20px;
62 }
63
64 .vfs-close-btn:hover {
65 color: #ff6b35;
66 }
67
68 .vfs-autofill-btn {
69 background: #ff6b35;
70 color: white;
71 border: none;
72 padding: 10px 20px;
73 border-radius: 5px;
74 cursor: pointer;
75 font-size: 14px;
76 font-weight: bold;
77 width: 100%;
78 margin-bottom: 10px;
79 transition: background 0.3s;
80 }
81
82 .vfs-autofill-btn:hover {
83 background: #e55a2b;
84 }
85
86 .vfs-autofill-btn:disabled {
87 background: #ccc;
88 cursor: not-allowed;
89 }
90
91 .vfs-settings-btn {
92 background: white;
93 color: #ff6b35;
94 border: 2px solid #ff6b35;
95 padding: 8px 16px;
96 border-radius: 5px;
97 cursor: pointer;
98 font-size: 13px;
99 width: 100%;
100 transition: all 0.3s;
101 }
102
103 .vfs-settings-btn:hover {
104 background: #ff6b35;
105 color: white;
106 }
107
108 .vfs-form-group {
109 margin-bottom: 12px;
110 }
111
112 .vfs-form-group label {
113 display: block;
114 font-size: 13px;
115 color: #333;
116 margin-bottom: 5px;
117 font-weight: 500;
118 }
119
120 .vfs-form-group input,
121 .vfs-form-group select {
122 width: 100%;
123 padding: 8px;
124 border: 1px solid #ddd;
125 border-radius: 4px;
126 font-size: 13px;
127 box-sizing: border-box;
128 }
129
130 .vfs-form-group input:focus,
131 .vfs-form-group select:focus {
132 outline: none;
133 border-color: #ff6b35;
134 }
135
136 .vfs-save-btn {
137 background: #4CAF50;
138 color: white;
139 border: none;
140 padding: 10px 20px;
141 border-radius: 5px;
142 cursor: pointer;
143 font-size: 14px;
144 font-weight: bold;
145 width: 100%;
146 margin-top: 10px;
147 transition: background 0.3s;
148 }
149
150 .vfs-save-btn:hover {
151 background: #45a049;
152 }
153
154 .vfs-status-message {
155 padding: 8px;
156 border-radius: 4px;
157 font-size: 12px;
158 margin-top: 10px;
159 text-align: center;
160 }
161
162 .vfs-status-success {
163 background: #d4edda;
164 color: #155724;
165 border: 1px solid #c3e6cb;
166 }
167
168 .vfs-status-error {
169 background: #f8d7da;
170 color: #721c24;
171 border: 1px solid #f5c6cb;
172 }
173
174 .vfs-floating-btn {
175 position: fixed;
176 bottom: 30px;
177 right: 30px;
178 z-index: 9999;
179 background: #ff6b35;
180 color: white;
181 border: none;
182 padding: 15px 25px;
183 border-radius: 50px;
184 cursor: pointer;
185 font-size: 14px;
186 font-weight: bold;
187 box-shadow: 0 4px 12px rgba(255, 107, 53, 0.4);
188 transition: all 0.3s;
189 }
190
191 .vfs-floating-btn:hover {
192 background: #e55a2b;
193 transform: translateY(-2px);
194 box-shadow: 0 6px 16px rgba(255, 107, 53, 0.5);
195 }
196 `);
197
198 // Main initialization
199 async function init() {
200 console.log('Initializing VFS Form Auto-Filler...');
201
202 // Wait for page to be fully loaded
203 if (document.readyState === 'loading') {
204 document.addEventListener('DOMContentLoaded', createUI);
205 } else {
206 createUI();
207 }
208 }
209
210 // Create the floating button
211 function createUI() {
212 console.log('Creating UI...');
213
214 // Remove existing button if any
215 const existingBtn = document.querySelector('.vfs-floating-btn');
216 if (existingBtn) {
217 existingBtn.remove();
218 }
219
220 // Create floating button
221 const floatingBtn = document.createElement('button');
222 floatingBtn.className = 'vfs-floating-btn';
223 floatingBtn.textContent = '🚀 Auto-Fill Form';
224 floatingBtn.addEventListener('click', showPanel);
225 document.body.appendChild(floatingBtn);
226
227 console.log('Floating button created');
228 }
229
230 // Show the control panel
231 async function showPanel() {
232 console.log('Showing panel...');
233
234 // Remove existing panel if any
235 const existingPanel = document.querySelector('.vfs-autofill-container');
236 if (existingPanel) {
237 existingPanel.remove();
238 return;
239 }
240
241 // Load saved data
242 const savedData = await GM.getValue('vfs_form_data', {});
243 console.log('Loaded saved data:', savedData);
244
245 // Create panel
246 const panel = document.createElement('div');
247 panel.className = 'vfs-autofill-container';
248 panel.innerHTML = `
249 <div class="vfs-autofill-header">
250 <span>VFS Auto-Filler</span>
251 <button class="vfs-close-btn" id="vfs-close">×</button>
252 </div>
253 <button class="vfs-autofill-btn" id="vfs-fill-btn">Fill Form Now</button>
254 <button class="vfs-settings-btn" id="vfs-settings-btn">⚙️ Edit Saved Data</button>
255 <div id="vfs-settings-panel" style="display: none; margin-top: 15px; border-top: 1px solid #ddd; padding-top: 15px;">
256 <div class="vfs-form-group">
257 <label>First Name:</label>
258 <input type="text" id="vfs-firstname" value="${savedData.firstName || ''}" placeholder="Enter first name">
259 </div>
260 <div class="vfs-form-group">
261 <label>Last Name:</label>
262 <input type="text" id="vfs-lastname" value="${savedData.lastName || ''}" placeholder="Enter last name">
263 </div>
264 <div class="vfs-form-group">
265 <label>Passport Number:</label>
266 <input type="text" id="vfs-passport" value="${savedData.passportNumber || ''}" placeholder="Enter passport number">
267 </div>
268 <div class="vfs-form-group">
269 <label>Nationality:</label>
270 <input type="text" id="vfs-nationality" value="${savedData.nationality || ''}" placeholder="e.g., Algeria">
271 </div>
272 <button class="vfs-save-btn" id="vfs-save-btn">💾 Save Data</button>
273 </div>
274 <div id="vfs-status"></div>
275 `;
276
277 document.body.appendChild(panel);
278
279 // Add event listeners
280 document.getElementById('vfs-close').addEventListener('click', () => panel.remove());
281 document.getElementById('vfs-fill-btn').addEventListener('click', fillForm);
282 document.getElementById('vfs-settings-btn').addEventListener('click', toggleSettings);
283 document.getElementById('vfs-save-btn').addEventListener('click', saveData);
284
285 console.log('Panel created and displayed');
286 }
287
288 // Toggle settings panel
289 function toggleSettings() {
290 const settingsPanel = document.getElementById('vfs-settings-panel');
291 if (settingsPanel.style.display === 'none') {
292 settingsPanel.style.display = 'block';
293 } else {
294 settingsPanel.style.display = 'none';
295 }
296 }
297
298 // Save user data
299 async function saveData() {
300 console.log('Saving data...');
301
302 const firstName = document.getElementById('vfs-firstname').value.trim();
303 const lastName = document.getElementById('vfs-lastname').value.trim();
304 const passportNumber = document.getElementById('vfs-passport').value.trim();
305 const nationality = document.getElementById('vfs-nationality').value.trim();
306
307 if (!firstName || !lastName || !passportNumber || !nationality) {
308 showStatus('Please fill all fields', 'error');
309 return;
310 }
311
312 const data = {
313 firstName: firstName,
314 lastName: lastName,
315 passportNumber: passportNumber,
316 nationality: nationality
317 };
318
319 await GM.setValue('vfs_form_data', data);
320 console.log('Data saved:', data);
321 showStatus('Data saved successfully!', 'success');
322 }
323
324 // Fill the form
325 async function fillForm() {
326 console.log('Filling form...');
327
328 const savedData = await GM.getValue('vfs_form_data', {});
329
330 if (!savedData.firstName || !savedData.lastName || !savedData.passportNumber || !savedData.nationality) {
331 showStatus('Please save your data first', 'error');
332 return;
333 }
334
335 try {
336 // Fill First Name
337 const firstNameInput = document.querySelector('input[placeholder*="first name" i]');
338 if (firstNameInput) {
339 firstNameInput.value = savedData.firstName.toUpperCase();
340 firstNameInput.dispatchEvent(new Event('input', { bubbles: true }));
341 firstNameInput.dispatchEvent(new Event('change', { bubbles: true }));
342 console.log('First name filled:', savedData.firstName);
343 } else {
344 console.error('First name input not found');
345 }
346
347 // Fill Last Name
348 const lastNameInput = document.querySelector('input[placeholder*="last name" i]');
349 if (lastNameInput) {
350 lastNameInput.value = savedData.lastName.toUpperCase();
351 lastNameInput.dispatchEvent(new Event('input', { bubbles: true }));
352 lastNameInput.dispatchEvent(new Event('change', { bubbles: true }));
353 console.log('Last name filled:', savedData.lastName);
354 } else {
355 console.error('Last name input not found');
356 }
357
358 // Fill Passport Number
359 const passportInput = document.querySelector('input[placeholder*="passport number" i]:not([type="password"])');
360 if (passportInput) {
361 passportInput.value = savedData.passportNumber.toUpperCase();
362 passportInput.dispatchEvent(new Event('input', { bubbles: true }));
363 passportInput.dispatchEvent(new Event('change', { bubbles: true }));
364 console.log('Passport number filled');
365 } else {
366 console.error('Passport number input not found');
367 }
368
369 // Fill Confirm Passport Number
370 const confirmPassportInput = document.querySelector('input[type="password"][placeholder*="*"]');
371 if (confirmPassportInput) {
372 confirmPassportInput.value = savedData.passportNumber.toUpperCase();
373 confirmPassportInput.dispatchEvent(new Event('input', { bubbles: true }));
374 confirmPassportInput.dispatchEvent(new Event('change', { bubbles: true }));
375 console.log('Confirm passport number filled');
376 } else {
377 console.error('Confirm passport number input not found');
378 }
379
380 // Fill Nationality dropdown
381 setTimeout(async () => {
382 const nationalitySelect = document.querySelector('mat-select[aria-labelledby*="mat-select-value"]');
383 if (nationalitySelect) {
384 nationalitySelect.click();
385 console.log('Nationality dropdown clicked');
386
387 // Wait for dropdown to open
388 setTimeout(() => {
389 const options = document.querySelectorAll('mat-option');
390 console.log('Found options:', options.length);
391
392 for (const option of options) {
393 const optionText = option.textContent.trim().toLowerCase();
394 if (optionText.includes(savedData.nationality.toLowerCase())) {
395 option.click();
396 console.log('Nationality selected:', optionText);
397 showStatus('Form filled successfully!', 'success');
398 return;
399 }
400 }
401
402 console.error('Nationality not found in dropdown');
403 showStatus('Nationality not found in dropdown', 'error');
404 }, 500);
405 } else {
406 console.error('Nationality dropdown not found');
407 showStatus('Form filled (nationality dropdown not found)', 'success');
408 }
409 }, 300);
410
411 } catch (error) {
412 console.error('Error filling form:', error);
413 showStatus('Error filling form', 'error');
414 }
415 }
416
417 // Show status message
418 function showStatus(message, type) {
419 const statusDiv = document.getElementById('vfs-status');
420 if (!statusDiv) return;
421
422 statusDiv.textContent = message;
423 statusDiv.className = `vfs-status-message vfs-status-${type}`;
424
425 setTimeout(() => {
426 statusDiv.textContent = '';
427 statusDiv.className = '';
428 }, 3000);
429 }
430
431 // Start the extension
432 init();
433
434})();