// ==UserScript==
// @name FaceCheck WebP URL Extractor
// @namespace http://tampermonkey.net/
// @version 3.1.0
// @description Extracts WebP image URLs from FaceCheck results for both mobile and desktop
// @author vin31_ modified by Nthompson096, perplexity.ai and 0wn3dg0d
// @match https://facecheck.id/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
// Detect mobile device
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
// Helper to check if on results page
const isResultsPage = () => /https:\/\/facecheck\.id\/(?:[a-z]{2})?\#.+/.test(window.location.href);
// Helper function to determine rating and color based on confidence score
const getRating = (confidence) => {
if (confidence >= 90) return { rating: 'Certain Match', color: isMobile ? 'green' : '#4caf50' };
if (confidence >= 83) return { rating: 'Confident Match', color: isMobile ? 'yellow' : '#ffeb3b' };
if (confidence >= 70) return { rating: 'Uncertain Match', color: isMobile ? 'orange' : '#ff9800' };
if (confidence >= 50) return { rating: 'Weak Match', color: isMobile ? 'red' : '#f44336' };
return { rating: 'No Match', color: isMobile ? 'white' : '#9e9e9e' };
};
// Extract WebP URL from a single image element
const extractWebPUrl = (fimg) => {
const bgImage = window.getComputedStyle(fimg).backgroundImage;
// Extract the data URL directly without decoding Base64
const urlMatch = bgImage.match(/url\("([^"]+)"\)/);
if (!urlMatch) return null;
// Get the full data URL (including base64 part)
const dataUrl = urlMatch[1];
// Get domain from the image icon in the HTML
const domainSpan = fimg.querySelector('.topdiv span');
const domain = domainSpan ? domainSpan.textContent : 'Unknown Domain';
// Get confidence score
const distSpan = fimg.querySelector('.dist');
const confidence = distSpan ? parseInt(distSpan.textContent) : 0;
const { rating, color } = getRating(confidence);
return {
url: dataUrl,
domain,
confidence,
rating,
color,
isBase64: dataUrl.includes('base64')
};
};
// Shared URL extraction function (works for both mobile and desktop)
const extractUrls = (fimg) => {
const parentAnchor = fimg.closest('a');
const groupId = parentAnchor ? parentAnchor.getAttribute('data-grp') : null;
const results = [];
// If it's a group, collect all elements of the group
if (groupId) {
const groupElements = document.querySelectorAll(`a[data-grp="${groupId}"]`);
groupElements.forEach(groupElement => {
const groupFimg = groupElement.querySelector('.facediv') || groupElement.querySelector('[id^="fimg"]');
if (!groupFimg) return;
const result = extractWebPUrl(groupFimg);
if (result) results.push(result);
});
} else {
// If it's a standalone element
const result = extractWebPUrl(fimg);
if (result) results.push(result);
}
return results.sort((a, b) => b.confidence - a.confidence);
};
// MOBILE FUNCTIONALITY
if (isMobile) {
// Mobile-specific styles for overlays
const mobileStyles = `
.mobile-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(to top, rgba(0,0,0,0.9) 0%, rgba(0,0,0,0.7) 50%, transparent 100%);
color: white;
padding: 12px 8px 8px 8px;
font-size: 14px;
line-height: 1.4;
z-index: 1000;
border-radius: 0 0 8px 8px;
pointer-events: none;
transform: translateY(100%);
transition: transform 0.3s ease;
}
.mobile-overlay.visible {
transform: translateY(0);
}
.mobile-overlay a {
color: #00FFFF;
text-decoration: none;
display: block;
margin-bottom: 4px;
font-weight: bold;
pointer-events: all;
padding: 6px 8px;
border-radius: 4px;
background: rgba(0,0,0,0.8);
font-size: 14px;
}
.mobile-overlay a:active {
background: rgba(0,255,255,0.2);
}
.mobile-overlay .rating {
font-size: 12px;
font-weight: normal;
}
.fimg-container {
position: relative;
overflow: hidden;
}
.mobile-info-panel {
position: fixed;
bottom: 10px;
left: 10px;
right: 10px;
background: rgba(0,0,0,0.95);
color: white;
padding: 15px;
border-radius: 8px;
z-index: 9999;
font-size: 16px;
line-height: 1.5;
max-height: 70vh;
overflow-y: auto;
transform: translateY(120%);
transition: transform 0.3s ease;
border: 1px solid rgba(0,255,255,0.3);
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
}
.mobile-info-panel.visible {
transform: translateY(0);
}
.mobile-info-panel .close-btn {
position: absolute;
top: 8px;
right: 12px;
background: none;
border: none;
color: #00FFFF;
font-size: 20px;
cursor: pointer;
padding: 0;
width: 24px;
height: 24px;
}
.mobile-info-panel a {
color: #00FFFF;
text-decoration: none;
display: block;
margin: 12px 0;
padding: 10px 12px;
background: rgba(0,255,255,0.1);
border-radius: 6px;
border: 1px solid rgba(0,255,255,0.2);
word-break: break-all;
font-size: 16px;
}
.mobile-info-panel a:active {
background: rgba(0,255,255,0.3);
}
.mobile-info-panel .url-item {
margin-bottom: 16px;
}
.mobile-info-panel .confidence {
font-size: 14px;
margin-top: 6px;
}
.mobile-overlay .click-hint {
font-size: 12px;
color: #aaa;
margin-top: 4px;
font-style: italic;
}
.data-url-display {
font-size: 10px;
color: #888;
margin-top: 4px;
word-break: break-all;
max-height: 60px;
overflow-y: auto;
}
`;
// Inject mobile styles
const mobileStyleSheet = document.createElement("style");
mobileStyleSheet.type = "text/css";
mobileStyleSheet.innerText = mobileStyles;
document.head.appendChild(mobileStyleSheet);
// Create overlay for mobile images
const createMobileOverlay = (fimg, results) => {
// Make sure the parent container has relative positioning
const container = fimg.parentElement;
if (!container.classList.contains('fimg-container')) {
container.classList.add('fimg-container');
}
const overlay = document.createElement("div");
overlay.classList.add("mobile-overlay");
// Show domain, confidence, and click hint
const topResult = results[0];
const shortUrl = topResult.url.length > 50 ?
topResult.url.substring(0, 50) + '...' :
topResult.url;
overlay.innerHTML = `
${topResult.domain} (${topResult.confidence}%) - ${topResult.rating}
${shortUrl}
Tap for more URLs
`;
container.appendChild(overlay);
// Show overlay with animation after a short delay
setTimeout(() => {
overlay.classList.add("visible");
}, 100);
return overlay;
};
// Create floating info panel that shows when tapping on overlay info
const createInfoPanel = () => {
const panel = document.createElement("div");
panel.classList.add("mobile-info-panel");
panel.innerHTML = `
`;
document.body.appendChild(panel);
// Close button functionality
panel.querySelector('.close-btn').addEventListener('click', (e) => {
e.stopPropagation();
panel.classList.remove('visible');
});
// Close when clicking outside
document.addEventListener('click', (e) => {
if (!panel.contains(e.target) && panel.classList.contains('visible')) {
panel.classList.remove('visible');
}
});
return panel;
};
const infoPanel = createInfoPanel();
// Add click handler to overlays to show detailed info
const addOverlayClickHandler = (overlay, results) => {
overlay.style.pointerEvents = 'all';
overlay.style.cursor = 'pointer';
overlay.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
const content = results.map((result, index) => `
${index + 1}. ${result.domain} (${result.confidence}%) - ${result.rating}
${result.url}
`).join('');
infoPanel.querySelector('#panel-content').innerHTML = content;
// Add copy functionality
infoPanel.querySelectorAll('.copy-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const url = e.target.getAttribute('data-url');
navigator.clipboard.writeText(url).then(() => {
e.target.textContent = 'Copied!';
setTimeout(() => {
e.target.textContent = 'Copy Data URL';
}, 2000);
});
});
});
infoPanel.classList.add('visible');
});
};
// Process all mobile images
const processMobileImages = () => {
const fimgElements = document.querySelectorAll('[id^="fimg"]');
fimgElements.forEach(fimg => {
// Skip if already processed
if (fimg.parentElement.querySelector('.mobile-overlay')) return;
const results = extractUrls(fimg);
if (results.length > 0) {
const overlay = createMobileOverlay(fimg, results);
addOverlayClickHandler(overlay, results);
}
});
};
// Start processing mobile images
const mobileCheckInterval = setInterval(() => {
if (isResultsPage() && document.querySelector('[id^="fimg"]')) {
processMobileImages();
// Continue checking for new images that might load dynamically
setTimeout(() => {
processMobileImages();
}, 2000);
}
}, 1000);
} else {
// DESKTOP FUNCTIONALITY
// CSS Variables for easy theme management
const desktopStyles = `
:root {
--popup-bg: #1e1e1e;
--popup-color: #00ffff;
--popup-opacity: 0.95;
--popup-border: 1px solid rgba(0, 255, 255, 0.2);
--popup-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
--popup-radius: 8px; /* Reduced from 12px */
--popup-padding: 12px; /* Reduced from 16px */
--popup-width: 350px; /* Reduced from 400px */
--popup-max-height: 400px; /* Reduced from 500px */
--popup-transition: opacity 0.2s ease, transform 0.2s ease; /* Faster */
}
.popup {
position: fixed;
background: var(--popup-bg);
color: var(--popup-color);
opacity: 0;
border: var(--popup-border);
box-shadow: var(--popup-shadow);
border-radius: var(--popup-radius);
padding: var(--popup-padding);
width: var(--popup-width);
max-height: var(--popup-max-height);
overflow-y: auto;
pointer-events: auto;
transition: var(--popup-transition);
transform: translateY(-10px);
backdrop-filter: blur(10px);
z-index: 9999;
}
.popup.visible {
opacity: var(--popup-opacity);
transform: translateY(0);
}
.popup ul {
list-style: none;
padding: 0;
margin: 0;
}
.popup li {
margin: 12px 0;
padding: 10px;
background: rgba(0,255,255,0.05);
border-radius: 6px;
}
.popup .data-url {
font-size: 11px;
color: #aaa;
word-break: break-all;
margin: 8px 0;
padding: 8px;
background: rgba(0,0,0,0.3);
border-radius: 4px;
max-height: 80px;
overflow-y: auto;
}
.popup .copy-btn {
background: #00FFFF;
color: black;
border: none;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
margin-top: 8px;
}
.popup .copy-btn:hover {
background: #00cccc;
}
`;
// Inject desktop styles
const desktopStyleSheet = document.createElement("style");
desktopStyleSheet.type = "text/css";
desktopStyleSheet.innerText = desktopStyles;
document.head.appendChild(desktopStyleSheet);
// Create and style the popup window
const createPopup = () => {
const popup = document.createElement("div");
popup.classList.add("popup");
document.body.appendChild(popup);
return popup;
};
// Function to display results in the popup window
const displayResultsDesktop = (results, popup, fimg) => {
const rect = fimg.getBoundingClientRect();
// Get viewport dimensions
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
// Position directly below the image
let leftPosition = rect.left;
let topPosition = rect.bottom - 5; // Just below the image
// Adjust if popup would go offscreen to the right
const popupWidth = 400; // Should match --popup-width CSS variable
if (leftPosition + popupWidth > viewportWidth - 10) {
// Try placing on left side instead
if (rect.left - popupWidth > 10) {
leftPosition = rect.left - popupWidth - 5;
} else {
// If neither side works, position at viewport edge
leftPosition = viewportWidth - popupWidth - 10;
}
}
// Adjust if popup would go offscreen vertically
const popupHeight = Math.min(500, results.length * 100); // Estimate height
if (topPosition + popupHeight > viewportHeight - 10) {
topPosition = viewportHeight - popupHeight - 10;
}
if (topPosition < 10) {
topPosition = 10;
}
popup.style.left = `${leftPosition}px`;
popup.style.top = `${topPosition}px`;
const resultsList = results.map((result, index) => `
${index + 1}. ${result.domain} (${result.confidence}%) - ${result.rating}
${result.url}
`).join('');
popup.innerHTML = ``;
popup.classList.add('visible');
// Add copy functionality
popup.querySelectorAll('.copy-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const url = e.target.getAttribute('data-url');
navigator.clipboard.writeText(url).then(() => {
e.target.textContent = 'Copied!';
setTimeout(() => {
e.target.textContent = 'Copy Data URL';
}, 2000);
});
});
});
};
// Create the popup window
const popup = createPopup();
// Track which elements have listeners attached
const processedFimgs = new WeakSet();
let hoverTimeout;
let isPopupHovered = false;
// Add event listeners for all fimg elements
const addHoverListeners = () => {
const fimgElements = document.querySelectorAll('[id^="fimg"]');
fimgElements.forEach(fimg => {
if (processedFimgs.has(fimg)) return;
processedFimgs.add(fimg);
fimg.addEventListener('mouseenter', () => {
if (isPopupHovered) return;
clearTimeout(hoverTimeout);
const results = extractUrls(fimg);
if (results.length > 0) {
displayResultsDesktop(results, popup, fimg);
}
});
fimg.addEventListener('mouseleave', () => {
if (isPopupHovered) return;
hoverTimeout = setTimeout(() => {
popup.classList.remove('visible');
}, 300);
});
});
// Event handler for the popup
popup.addEventListener('mouseenter', () => {
isPopupHovered = true;
clearTimeout(hoverTimeout);
});
popup.addEventListener('mouseleave', () => {
isPopupHovered = false;
popup.classList.remove('visible');
});
};
// Start adding event listeners after the page loads
const desktopCheckInterval = setInterval(() => {
if (isResultsPage() && document.querySelector('[id^="fimg"]')) {
addHoverListeners();
}
}, 1000);
}
})();