Auto-deploy: 2026-06-05 22:29:04
This commit is contained in:
@@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
// ─── Find Result Container ───────────────────────────────────────────────
|
// ─── Find Result Container ───────────────────────────────────────────────
|
||||||
function getSearchResultsContainer() {
|
function getSearchResultsContainer() {
|
||||||
return document.querySelector('.search-results-container, .reusable-search__entity-result-list, ul.reusable-search__entity-result-list');
|
return document.querySelector('.search-results-container, .reusable-search__entity-result-list, ul.reusable-search__entity-result-list, .search-results__list');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Extract Person Data ─────────────────────────────────────────────────
|
// ─── Extract Person Data ─────────────────────────────────────────────────
|
||||||
@@ -43,30 +43,38 @@
|
|||||||
summary: ''
|
summary: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
// Name
|
// 1. Extract Name (Usually inside a link with /in/)
|
||||||
const nameEl = cardEl.querySelector('.entity-result__title-text a span[dir="ltr"], .entity-result__title-line a span[dir="ltr"], span.entity-result__title-text, .app-aware-link span[dir="ltr"]');
|
const nameEl = cardEl.querySelector('.entity-result__title-text, .search-result__title, span[dir="ltr"]');
|
||||||
if (nameEl) {
|
if (nameEl) {
|
||||||
data.name = nameEl.innerText.trim();
|
data.name = nameEl.innerText.trim().split('\n')[0];
|
||||||
|
} else {
|
||||||
|
const profileLinks = Array.from(cardEl.querySelectorAll('a[href*="/in/"]')).filter(a => a.innerText.trim().length > 0);
|
||||||
|
if (profileLinks.length > 0) {
|
||||||
|
data.name = profileLinks[0].innerText.trim().split('\n')[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Headline
|
// 2. Extract Headline
|
||||||
const headlineEl = cardEl.querySelector('.entity-result__primary-subtitle');
|
const headlineEl = cardEl.querySelector('.entity-result__primary-subtitle, .search-result__truncate, .linked-area');
|
||||||
if (headlineEl) {
|
if (headlineEl) {
|
||||||
data.headline = headlineEl.innerText.trim();
|
data.headline = headlineEl.innerText.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Location
|
// 3. Extract Location
|
||||||
const locationEl = cardEl.querySelector('.entity-result__secondary-subtitle');
|
const locationEl = cardEl.querySelector('.entity-result__secondary-subtitle, .search-result__info');
|
||||||
if (locationEl) {
|
if (locationEl) {
|
||||||
data.location = locationEl.innerText.trim();
|
data.location = locationEl.innerText.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Summary/Snippet
|
// 4. Extract Summary
|
||||||
const summaryEl = cardEl.querySelector('.entity-result__summary');
|
const summaryEl = cardEl.querySelector('.entity-result__summary, .search-result__snippets');
|
||||||
if (summaryEl) {
|
if (summaryEl) {
|
||||||
data.summary = summaryEl.innerText.trim();
|
data.summary = summaryEl.innerText.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clean up "View Profile" texts
|
||||||
|
data.name = data.name.replace(/View .* profile/gi, '').trim();
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,14 +82,20 @@
|
|||||||
function injectScanButton(cardEl) {
|
function injectScanButton(cardEl) {
|
||||||
if (cardEl.querySelector('.lja-scan-person-btn') || cardEl.querySelector('.lja-investor-result')) return;
|
if (cardEl.querySelector('.lja-scan-person-btn') || cardEl.querySelector('.lja-investor-result')) return;
|
||||||
|
|
||||||
// Find a good place to put the button. Usually near the title or actions.
|
|
||||||
const actionArea = cardEl.querySelector('.entity-result__actions') || cardEl.querySelector('.entity-result__item');
|
|
||||||
if (!actionArea) return;
|
|
||||||
|
|
||||||
const btn = document.createElement('button');
|
const btn = document.createElement('button');
|
||||||
btn.className = 'lja-scan-person-btn';
|
btn.className = 'lja-scan-person-btn';
|
||||||
btn.innerHTML = '🔍 Scan Investor';
|
btn.innerHTML = '🔍 Scan Investor';
|
||||||
|
|
||||||
|
// Add some basic styling just in case CSS fails to load or apply correctly
|
||||||
|
btn.style.margin = '10px 0';
|
||||||
|
btn.style.padding = '5px 15px';
|
||||||
|
btn.style.cursor = 'pointer';
|
||||||
|
btn.style.backgroundColor = '#6C63FF';
|
||||||
|
btn.style.color = '#fff';
|
||||||
|
btn.style.border = 'none';
|
||||||
|
btn.style.borderRadius = '5px';
|
||||||
|
btn.style.fontWeight = 'bold';
|
||||||
|
|
||||||
// Create a container for the result
|
// Create a container for the result
|
||||||
const resultContainer = document.createElement('div');
|
const resultContainer = document.createElement('div');
|
||||||
resultContainer.className = 'lja-result-wrapper';
|
resultContainer.className = 'lja-result-wrapper';
|
||||||
@@ -92,16 +106,16 @@
|
|||||||
await scanPerson(cardEl, btn, resultContainer);
|
await scanPerson(cardEl, btn, resultContainer);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Try to append button to actions area if possible, else to the item
|
// Try to append to actions area, if not just append at the end of the card
|
||||||
if (actionArea.classList.contains('entity-result__actions')) {
|
const actionArea = cardEl.querySelector('.entity-result__actions, .search-result__actions');
|
||||||
|
if (actionArea) {
|
||||||
actionArea.prepend(btn);
|
actionArea.prepend(btn);
|
||||||
} else {
|
} else {
|
||||||
const titleLine = cardEl.querySelector('.entity-result__title-line') || actionArea;
|
cardEl.appendChild(btn);
|
||||||
titleLine.appendChild(btn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cardEl.appendChild(resultContainer);
|
cardEl.appendChild(resultContainer);
|
||||||
console.log('[LJA] Injected button for a profile');
|
console.log('[LJA] Injected button for a profile:', extractPersonData(cardEl).name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Scan a Single Person ────────────────────────────────────────────────
|
// ─── Scan a Single Person ────────────────────────────────────────────────
|
||||||
@@ -171,11 +185,11 @@
|
|||||||
const colorClass = isGreen ? 'green' : 'red';
|
const colorClass = isGreen ? 'green' : 'red';
|
||||||
|
|
||||||
resultContainer.innerHTML = `
|
resultContainer.innerHTML = `
|
||||||
<div class="lja-investor-result ${colorClass}" dir="rtl">
|
<div class="lja-investor-result ${colorClass}" dir="rtl" style="margin-top: 10px; padding: 10px; border-radius: 8px; font-weight: bold; font-family: system-ui; background-color: ${isGreen ? '#e6ffe6' : '#ffe6e6'}; color: ${isGreen ? '#006600' : '#cc0000'}; border: 1px solid ${isGreen ? '#00cc00' : '#ff0000'};">
|
||||||
<div class="lja-investor-badge ${colorClass}">
|
<div class="lja-investor-badge">
|
||||||
<span>${badgeIcon}</span> ${badgeText}
|
<span>${badgeIcon}</span> ${badgeText}
|
||||||
</div>
|
</div>
|
||||||
<div class="lja-investor-reason">
|
<div class="lja-investor-reason" style="margin-top: 5px; font-weight: normal; font-size: 14px;">
|
||||||
${resultData.reason}
|
${resultData.reason}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -185,7 +199,7 @@
|
|||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[LJA Search]', e);
|
console.error('[LJA Search]', e);
|
||||||
resultContainer.innerHTML = `<div class="lja-investor-result red">❌ Error: ${e.message}</div>`;
|
resultContainer.innerHTML = `<div class="lja-investor-result red" style="color:red; font-weight:bold;">❌ Error: ${e.message}</div>`;
|
||||||
btnEl.disabled = false;
|
btnEl.disabled = false;
|
||||||
btnEl.innerHTML = '🔍 Scan Investor';
|
btnEl.innerHTML = '🔍 Scan Investor';
|
||||||
}
|
}
|
||||||
@@ -195,17 +209,31 @@
|
|||||||
function injectScanAllButton() {
|
function injectScanAllButton() {
|
||||||
if (document.querySelector('.lja-scan-list-btn')) return;
|
if (document.querySelector('.lja-scan-list-btn')) return;
|
||||||
|
|
||||||
const container = getSearchResultsContainer();
|
let container = getSearchResultsContainer();
|
||||||
if (!container) return;
|
|
||||||
|
|
||||||
const btn = document.createElement('button');
|
const btn = document.createElement('button');
|
||||||
btn.className = 'lja-scan-list-btn';
|
btn.className = 'lja-scan-list-btn';
|
||||||
btn.innerHTML = '✨ Scan All Investors';
|
btn.innerHTML = '✨ Scan All Investors';
|
||||||
btn.title = 'Scan all loaded profiles on this page';
|
btn.title = 'Scan all loaded profiles on this page';
|
||||||
|
|
||||||
|
// Robust styling for the Scan All button
|
||||||
|
btn.style.margin = '20px auto';
|
||||||
|
btn.style.display = 'block';
|
||||||
|
btn.style.padding = '10px 20px';
|
||||||
|
btn.style.cursor = 'pointer';
|
||||||
|
btn.style.backgroundColor = '#6C63FF';
|
||||||
|
btn.style.color = '#fff';
|
||||||
|
btn.style.border = 'none';
|
||||||
|
btn.style.borderRadius = '8px';
|
||||||
|
btn.style.fontWeight = 'bold';
|
||||||
|
btn.style.fontSize = '16px';
|
||||||
|
btn.style.boxShadow = '0 4px 6px rgba(0,0,0,0.1)';
|
||||||
|
|
||||||
btn.addEventListener('click', async () => {
|
btn.addEventListener('click', async () => {
|
||||||
// Allow broader class matches for people cards
|
// Find all cards using our robust selector
|
||||||
const cards = document.querySelectorAll('.reusable-search__result-container, li.search-result__occluded-item, li.search-result');
|
const allLis = Array.from(document.querySelectorAll('li'));
|
||||||
|
const cards = allLis.filter(li => li.querySelector('a[href*="/in/"]'));
|
||||||
|
|
||||||
if (cards.length === 0) {
|
if (cards.length === 0) {
|
||||||
alert('No profiles found to scan.');
|
alert('No profiles found to scan.');
|
||||||
return;
|
return;
|
||||||
@@ -214,15 +242,13 @@
|
|||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
btn.innerHTML = '<span class="lja-spinner"></span> Scanning profiles...';
|
btn.innerHTML = '<span class="lja-spinner"></span> Scanning profiles...';
|
||||||
|
|
||||||
// Scan sequentially to avoid hammering the API
|
// Scan sequentially
|
||||||
for (const card of cards) {
|
for (const card of cards) {
|
||||||
const scanBtn = card.querySelector('.lja-scan-person-btn');
|
const scanBtn = card.querySelector('.lja-scan-person-btn');
|
||||||
if (scanBtn && scanBtn.style.display !== 'none') {
|
if (scanBtn && scanBtn.style.display !== 'none') {
|
||||||
// Scroll to card
|
|
||||||
card.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
card.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||||
await new Promise(r => setTimeout(r, 500)); // Small delay
|
await new Promise(r => setTimeout(r, 500));
|
||||||
scanBtn.click();
|
scanBtn.click();
|
||||||
// Wait for result
|
|
||||||
while (scanBtn.disabled && scanBtn.style.display !== 'none') {
|
while (scanBtn.disabled && scanBtn.style.display !== 'none') {
|
||||||
await new Promise(r => setTimeout(r, 500));
|
await new Promise(r => setTimeout(r, 500));
|
||||||
}
|
}
|
||||||
@@ -234,18 +260,32 @@
|
|||||||
setTimeout(() => { btn.innerHTML = '✨ Scan All Investors'; }, 3000);
|
setTimeout(() => { btn.innerHTML = '✨ Scan All Investors'; }, 3000);
|
||||||
});
|
});
|
||||||
|
|
||||||
container.parentNode.insertBefore(btn, container);
|
if (container && container.parentNode) {
|
||||||
|
container.parentNode.insertBefore(btn, container);
|
||||||
|
} else {
|
||||||
|
// Fallback: Floating button bottom right
|
||||||
|
btn.style.position = 'fixed';
|
||||||
|
btn.style.bottom = '20px';
|
||||||
|
btn.style.right = '20px';
|
||||||
|
btn.style.zIndex = '99999';
|
||||||
|
btn.style.margin = '0';
|
||||||
|
document.body.appendChild(btn);
|
||||||
|
}
|
||||||
console.log('[LJA] Injected Scan All button');
|
console.log('[LJA] Injected Scan All button');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Process Page ────────────────────────────────────────────────────────
|
// ─── Process Page ────────────────────────────────────────────────────────
|
||||||
function processPage() {
|
function processPage() {
|
||||||
// Prevent running outside of search page just in case
|
|
||||||
if (!window.location.href.includes('linkedin.com/search/results/')) return;
|
if (!window.location.href.includes('linkedin.com/search/results/')) return;
|
||||||
|
|
||||||
injectScanAllButton();
|
// Robustly find any <li> that contains a link to a LinkedIn profile
|
||||||
const cards = document.querySelectorAll('.reusable-search__result-container, li.search-result__occluded-item, li.search-result');
|
const allLis = Array.from(document.querySelectorAll('li'));
|
||||||
|
const cards = allLis.filter(li => li.querySelector('a[href*="/in/"]'));
|
||||||
|
|
||||||
|
console.log('[LJA] processPage found cards:', cards.length);
|
||||||
|
|
||||||
if (cards.length > 0) {
|
if (cards.length > 0) {
|
||||||
|
injectScanAllButton();
|
||||||
cards.forEach(injectScanButton);
|
cards.forEach(injectScanButton);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,7 +297,7 @@
|
|||||||
observerTimer = setTimeout(() => {
|
observerTimer = setTimeout(() => {
|
||||||
processPage();
|
processPage();
|
||||||
observerTimer = null;
|
observerTimer = null;
|
||||||
}, 800);
|
}, 1500);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ─── Initialize ──────────────────────────────────────────────────────────
|
// ─── Initialize ──────────────────────────────────────────────────────────
|
||||||
@@ -265,9 +305,6 @@
|
|||||||
console.log('[LJA] Initializing Search Analyzer...');
|
console.log('[LJA] Initializing Search Analyzer...');
|
||||||
processPage();
|
processPage();
|
||||||
observer.observe(document.body, { childList: true, subtree: true });
|
observer.observe(document.body, { childList: true, subtree: true });
|
||||||
|
|
||||||
// Listen for SPA navigation from background script if implemented,
|
|
||||||
// or just rely on MutationObserver for body changes when URL changes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle SPA navigation by checking URL changes
|
// Handle SPA navigation by checking URL changes
|
||||||
@@ -276,7 +313,8 @@
|
|||||||
if (window.location.href !== lastUrl) {
|
if (window.location.href !== lastUrl) {
|
||||||
lastUrl = window.location.href;
|
lastUrl = window.location.href;
|
||||||
if (lastUrl.includes('linkedin.com/search/results/')) {
|
if (lastUrl.includes('linkedin.com/search/results/')) {
|
||||||
setTimeout(processPage, 1500);
|
console.log('[LJA] SPA Navigation detected, re-processing...');
|
||||||
|
setTimeout(processPage, 2000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
@@ -285,6 +323,6 @@
|
|||||||
document.addEventListener('DOMContentLoaded', init);
|
document.addEventListener('DOMContentLoaded', init);
|
||||||
} else {
|
} else {
|
||||||
init();
|
init();
|
||||||
setTimeout(init, 2000); // Fallback for delayed renders
|
setTimeout(init, 3000); // Fallback for delayed renders
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user