Image URL Scraper with Copy Checkboxes

Adds checkboxes to images for easy URL copying

Size

8.1 KB

Version

1.0.1

Created

Feb 26, 2026

Updated

24 days ago

1// ==UserScript==
2// @name		Image URL Scraper with Copy Checkboxes
3// @description		Adds checkboxes to images for easy URL copying
4// @version		1.0.1
5// @match		https://*.app.base44.com/*
6// @icon		https://app.base44.com/logo_v3.png
7// ==/UserScript==
8(function() {
9    'use strict';
10
11    console.log('Image URL Scraper extension loaded');
12
13    // Add custom styles for checkboxes
14    TM_addStyle(`
15        .image-copy-wrapper {
16            position: relative;
17            display: inline-block;
18        }
19        
20        .image-copy-checkbox-container {
21            position: absolute;
22            top: 5px;
23            left: 5px;
24            z-index: 1000;
25            background: rgba(255, 255, 255, 0.95);
26            border: 2px solid #4CAF50;
27            border-radius: 6px;
28            padding: 6px 10px;
29            display: flex;
30            align-items: center;
31            gap: 6px;
32            box-shadow: 0 2px 8px rgba(0,0,0,0.2);
33            cursor: pointer;
34            transition: all 0.2s ease;
35        }
36        
37        .image-copy-checkbox-container:hover {
38            background: rgba(255, 255, 255, 1);
39            transform: scale(1.05);
40            box-shadow: 0 3px 12px rgba(0,0,0,0.3);
41        }
42        
43        .image-copy-checkbox {
44            width: 18px;
45            height: 18px;
46            cursor: pointer;
47            accent-color: #4CAF50;
48        }
49        
50        .image-copy-label {
51            font-size: 12px;
52            font-weight: 600;
53            color: #333;
54            user-select: none;
55            cursor: pointer;
56        }
57        
58        .image-copy-notification {
59            position: fixed;
60            top: 20px;
61            right: 20px;
62            background: #4CAF50;
63            color: white;
64            padding: 12px 20px;
65            border-radius: 8px;
66            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
67            z-index: 10000;
68            font-weight: 600;
69            animation: slideIn 0.3s ease;
70        }
71        
72        @keyframes slideIn {
73            from {
74                transform: translateX(400px);
75                opacity: 0;
76            }
77            to {
78                transform: translateX(0);
79                opacity: 1;
80            }
81        }
82    `);
83
84    // Function to show notification
85    function showNotification(message) {
86        const notification = document.createElement('div');
87        notification.className = 'image-copy-notification';
88        notification.textContent = message;
89        document.body.appendChild(notification);
90        
91        setTimeout(() => {
92            notification.remove();
93        }, 2000);
94    }
95
96    // Function to copy URL to clipboard
97    async function copyImageUrl(imageUrl, checkbox) {
98        try {
99            await GM.setClipboard(imageUrl);
100            console.log('Copied image URL:', imageUrl);
101            showNotification('✓ Image URL copied!');
102            
103            // Keep checkbox checked for visual feedback
104            setTimeout(() => {
105                checkbox.checked = false;
106            }, 1000);
107        } catch (error) {
108            console.error('Failed to copy URL:', error);
109            showNotification('✗ Failed to copy URL');
110            checkbox.checked = false;
111        }
112    }
113
114    // Function to add checkbox to an image
115    function addCheckboxToImage(img) {
116        // Skip if already processed or if it's a tracking pixel
117        if (img.hasAttribute('data-copy-checkbox-added') || 
118            img.width === 0 || 
119            img.height === 0 ||
120            img.width < 20 || 
121            img.height < 20) {
122            return;
123        }
124        
125        img.setAttribute('data-copy-checkbox-added', 'true');
126        
127        // Get the image URL
128        const imageUrl = img.src;
129        if (!imageUrl || imageUrl === '') {
130            return;
131        }
132        
133        // Create wrapper if image doesn't have one
134        const parent = img.parentElement;
135        let wrapper;
136        
137        if (parent.classList.contains('image-copy-wrapper')) {
138            wrapper = parent;
139        } else {
140            wrapper = document.createElement('div');
141            wrapper.className = 'image-copy-wrapper';
142            
143            // Copy relevant styles from image
144            const imgStyles = window.getComputedStyle(img);
145            if (imgStyles.display === 'block') {
146                wrapper.style.display = 'block';
147            }
148            
149            parent.insertBefore(wrapper, img);
150            wrapper.appendChild(img);
151        }
152        
153        // Create checkbox container
154        const checkboxContainer = document.createElement('div');
155        checkboxContainer.className = 'image-copy-checkbox-container';
156        
157        // Create checkbox
158        const checkbox = document.createElement('input');
159        checkbox.type = 'checkbox';
160        checkbox.className = 'image-copy-checkbox';
161        
162        // Create label
163        const label = document.createElement('span');
164        label.className = 'image-copy-label';
165        label.textContent = 'Copy';
166        
167        // Add click handler
168        const handleClick = (e) => {
169            e.stopPropagation();
170            if (checkbox.checked) {
171                copyImageUrl(imageUrl, checkbox);
172            }
173        };
174        
175        checkbox.addEventListener('change', handleClick);
176        checkboxContainer.addEventListener('click', (e) => {
177            e.stopPropagation();
178            checkbox.checked = !checkbox.checked;
179            if (checkbox.checked) {
180                copyImageUrl(imageUrl, checkbox);
181            }
182        });
183        
184        // Assemble elements
185        checkboxContainer.appendChild(checkbox);
186        checkboxContainer.appendChild(label);
187        wrapper.appendChild(checkboxContainer);
188        
189        console.log('Added checkbox to image:', imageUrl);
190    }
191
192    // Function to process all images on the page
193    function processAllImages() {
194        const images = document.querySelectorAll('img');
195        console.log(`Found ${images.length} images on the page`);
196        
197        images.forEach((img) => {
198            // Wait for image to load before processing
199            if (img.complete) {
200                addCheckboxToImage(img);
201            } else {
202                img.addEventListener('load', () => addCheckboxToImage(img), { once: true });
203            }
204        });
205    }
206
207    // Debounce function to avoid excessive processing
208    function debounce(func, wait) {
209        let timeout;
210        return function executedFunction(...args) {
211            const later = () => {
212                clearTimeout(timeout);
213                func(...args);
214            };
215            clearTimeout(timeout);
216            timeout = setTimeout(later, wait);
217        };
218    }
219
220    // Initialize the extension
221    function init() {
222        console.log('Initializing Image URL Scraper...');
223        
224        // Process existing images
225        processAllImages();
226        
227        // Watch for new images being added to the page
228        const debouncedProcess = debounce(processAllImages, 500);
229        
230        const observer = new MutationObserver((mutations) => {
231            let hasNewImages = false;
232            
233            mutations.forEach((mutation) => {
234                mutation.addedNodes.forEach((node) => {
235                    if (node.nodeType === 1) { // Element node
236                        if (node.tagName === 'IMG') {
237                            hasNewImages = true;
238                        } else if (node.querySelectorAll) {
239                            const imgs = node.querySelectorAll('img');
240                            if (imgs.length > 0) {
241                                hasNewImages = true;
242                            }
243                        }
244                    }
245                });
246            });
247            
248            if (hasNewImages) {
249                debouncedProcess();
250            }
251        });
252        
253        observer.observe(document.body, {
254            childList: true,
255            subtree: true
256        });
257        
258        console.log('Image URL Scraper initialized successfully');
259    }
260
261    // Start when DOM is ready
262    if (document.readyState === 'loading') {
263        document.addEventListener('DOMContentLoaded', init);
264    } else {
265        init();
266    }
267})();