Auto-deploy: 2026-05-25 23:27:04
This commit is contained in:
101
filler.js
Normal file
101
filler.js
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
// filler.js — Script executed on the active tab to auto-fill form fields.
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
// Retrieve the profile data passed from the popup/scripting context
|
||||||
|
const profile = window.__autofillProfileData;
|
||||||
|
if (!profile) {
|
||||||
|
console.error("Autofill profile data not found.");
|
||||||
|
return "Error: Profile data not found";
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Starting auto-fill with profile:", profile);
|
||||||
|
|
||||||
|
// Helper to find associated label text for an input
|
||||||
|
function getLabelText(input) {
|
||||||
|
let labelText = "";
|
||||||
|
|
||||||
|
// 1. Check aria-label or placeholder
|
||||||
|
if (input.getAttribute("aria-label")) {
|
||||||
|
labelText += " " + input.getAttribute("aria-label");
|
||||||
|
}
|
||||||
|
if (input.getAttribute("placeholder")) {
|
||||||
|
labelText += " " + input.getAttribute("placeholder");
|
||||||
|
}
|
||||||
|
if (input.getAttribute("id")) {
|
||||||
|
const label = document.querySelector(`label[for="${input.getAttribute("id")}"]`);
|
||||||
|
if (label) {
|
||||||
|
labelText += " " + label.innerText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Check parent label
|
||||||
|
let parent = input.parentElement;
|
||||||
|
while (parent && parent !== document.body) {
|
||||||
|
if (parent.tagName === "LABEL") {
|
||||||
|
labelText += " " + parent.innerText;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
parent = parent.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Check name or id attributes
|
||||||
|
if (input.name) labelText += " " + input.name;
|
||||||
|
if (input.id) labelText += " " + input.id;
|
||||||
|
|
||||||
|
return labelText.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all input, textarea, and select elements
|
||||||
|
const inputs = document.querySelectorAll("input, textarea, select");
|
||||||
|
let filledCount = 0;
|
||||||
|
|
||||||
|
inputs.forEach(input => {
|
||||||
|
// Skip hidden or read-only elements
|
||||||
|
if (input.type === "hidden" || input.disabled || input.readOnly) return;
|
||||||
|
|
||||||
|
const label = getLabelText(input);
|
||||||
|
|
||||||
|
let valToSet = null;
|
||||||
|
|
||||||
|
// Matching logic based on common field names
|
||||||
|
if (label.includes("email") || label.includes("mail")) {
|
||||||
|
valToSet = profile.email;
|
||||||
|
} else if (label.includes("phone") || label.includes("tel") || label.includes("mobile") || label.includes("contact")) {
|
||||||
|
valToSet = profile.phone;
|
||||||
|
} else if (label.includes("linkedin")) {
|
||||||
|
valToSet = profile.linkedin;
|
||||||
|
} else if (label.includes("github")) {
|
||||||
|
valToSet = profile.github;
|
||||||
|
} else if (label.includes("first name") || label.includes("firstname")) {
|
||||||
|
valToSet = profile.firstName;
|
||||||
|
} else if (label.includes("last name") || label.includes("lastname")) {
|
||||||
|
valToSet = profile.lastName;
|
||||||
|
} else if (label.includes("full name") || label.includes("fullname") || (label.includes("name") && !label.includes("company") && !label.includes("school") && !label.includes("employer"))) {
|
||||||
|
valToSet = profile.fullName;
|
||||||
|
} else if (label.includes("website") || label.includes("portfolio") || label.includes("personal link")) {
|
||||||
|
valToSet = profile.portfolio || profile.linkedin;
|
||||||
|
} else if (label.includes("cover letter") || label.includes("describe") || label.includes("introduce yourself")) {
|
||||||
|
valToSet = profile.summary || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valToSet !== null && valToSet !== undefined) {
|
||||||
|
// Set the value
|
||||||
|
input.value = valToSet;
|
||||||
|
|
||||||
|
// Highlight the filled field briefly
|
||||||
|
const originalBg = input.style.backgroundColor;
|
||||||
|
input.style.backgroundColor = "rgba(0, 214, 126, 0.2)";
|
||||||
|
input.style.transition = "background-color 0.5s ease";
|
||||||
|
setTimeout(() => {
|
||||||
|
input.style.backgroundColor = originalBg;
|
||||||
|
}, 1500);
|
||||||
|
|
||||||
|
// Trigger change/input events so any framework (React/Angular/Vue) registers the value
|
||||||
|
input.dispatchEvent(new Event("input", { bubbles: true }));
|
||||||
|
input.dispatchEvent(new Event("change", { bubbles: true }));
|
||||||
|
filledCount++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return `Filled ${filledCount} fields successfully!`;
|
||||||
|
})();
|
||||||
14
popup.html
14
popup.html
@@ -414,6 +414,20 @@
|
|||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
|
||||||
|
<!-- QUICK ACTIONS -->
|
||||||
|
<div class="section" style="background: linear-gradient(135deg, rgba(108, 99, 255, 0.15) 0%, rgba(155, 93, 229, 0.15) 100%); border-color: var(--accent);">
|
||||||
|
<div class="section-header">
|
||||||
|
<span class="section-title">⚡ Quick Actions</span>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary" id="autofill-btn">
|
||||||
|
✨ Auto-fill Form on Current Page
|
||||||
|
</button>
|
||||||
|
<p style="font-size: 11px; color: var(--text-secondary); margin-top: 8px; text-align: center;">
|
||||||
|
Fills fields on the current page using your profile details.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- API KEY SECTION -->
|
<!-- API KEY SECTION -->
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
|
|||||||
93
popup.js
93
popup.js
@@ -169,6 +169,99 @@ document.getElementById('clear-all-btn').addEventListener('click', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ─── Autofill Functionality ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function parseProfileText(text) {
|
||||||
|
const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/;
|
||||||
|
const phoneRegex = /\+?[0-9]{1,4}[ \t.-]?[0-9]{3,4}[ \t.-]?[0-9]{3,4}/;
|
||||||
|
const linkedinRegex = /(https?:\/\/)?(www\.)?linkedin\.com\/in\/[a-zA-Z0-9_-]+/;
|
||||||
|
const githubRegex = /(https?:\/\/)?(www\.)?github\.com\/[a-zA-Z0-9_-]+/;
|
||||||
|
|
||||||
|
const emailMatch = text.match(emailRegex);
|
||||||
|
const phoneMatch = text.match(phoneRegex);
|
||||||
|
const linkedinMatch = text.match(linkedinRegex);
|
||||||
|
const githubMatch = text.match(githubRegex);
|
||||||
|
|
||||||
|
// Extract Name (first line usually)
|
||||||
|
const lines = text.split('\n').map(l => l.trim()).filter(Boolean);
|
||||||
|
let fullName = "Hamza Ayed";
|
||||||
|
if (lines.length > 0) {
|
||||||
|
const firstLine = lines[0].replace(/—.*/, '').replace(/:.*/, '').trim();
|
||||||
|
fullName = firstLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nameParts = fullName.split(' ');
|
||||||
|
const firstName = nameParts[0] || "";
|
||||||
|
const lastName = nameParts.slice(1).join(' ') || "";
|
||||||
|
|
||||||
|
// Extract Summary
|
||||||
|
let summary = "";
|
||||||
|
const summaryIdx = text.toLowerCase().indexOf("summary:");
|
||||||
|
if (summaryIdx !== -1) {
|
||||||
|
const skillsIdx = text.toLowerCase().indexOf("core skills:", summaryIdx);
|
||||||
|
if (skillsIdx !== -1) {
|
||||||
|
summary = text.substring(summaryIdx + 8, skillsIdx).trim();
|
||||||
|
} else {
|
||||||
|
summary = text.substring(summaryIdx + 8, summaryIdx + 500).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
fullName,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
email: emailMatch ? emailMatch[0] : "",
|
||||||
|
phone: phoneMatch ? phoneMatch[0] : "",
|
||||||
|
linkedin: linkedinMatch ? linkedinMatch[0] : "",
|
||||||
|
github: githubMatch ? githubMatch[0] : "",
|
||||||
|
summary,
|
||||||
|
cvText: text
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('autofill-btn').addEventListener('click', async () => {
|
||||||
|
const userProfile = document.getElementById('profile-textarea').value.trim();
|
||||||
|
if (!userProfile) {
|
||||||
|
showToast('⚠️ Please enter profile info first', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const profileData = parseProfileText(userProfile);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||||
|
if (!tab) {
|
||||||
|
showToast('❌ No active tab found', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the profile data on the tab window context
|
||||||
|
await chrome.scripting.executeScript({
|
||||||
|
target: { tabId: tab.id },
|
||||||
|
func: (data) => {
|
||||||
|
window.__autofillProfileData = data;
|
||||||
|
},
|
||||||
|
args: [profileData]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Execute the filler script
|
||||||
|
const results = await chrome.scripting.executeScript({
|
||||||
|
target: { tabId: tab.id },
|
||||||
|
files: ['filler.js']
|
||||||
|
});
|
||||||
|
|
||||||
|
if (results && results[0]) {
|
||||||
|
showToast('✨ ' + results[0].result, 'success');
|
||||||
|
} else {
|
||||||
|
showToast('✨ Autofill completed!', 'success');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
showToast('❌ Error: ' + e.message, 'error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// ─── Init ────────────────────────────────────────────────────────────────────
|
// ─── Init ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user