CoolFace
Apppublic

mvpweb/formfinder-scraper-wizard

sourceHugging Faceupdated 11mo agoView on Hugging Face
0likes
script.js317 linesDownload Raw Back to root
1document.addEventListener('DOMContentLoaded', function() {2    const scrapeBtn = document.getElementById('scrape-btn');3    const exportBtn = document.getElementById('export-btn');4    const resultsContainer = document.getElementById('results-container');5    const resultsBody = document.getElementById('results-body');6    const loadingIndicator = document.getElementById('loading');7// Initialize mock data array8let mockResults = [];9 10// Generate mock data for 100 sites when needed11function generateMockData() {12    mockResults = Array.from({length: 100}, (_, i) => {13const mockResults = Array.from({length: 100}, (_, i) => {14        const hasForm = Math.random() > 0.3; // 70% chance of having a form15        const cities = ['miami', 'orlando', 'tampa', 'jacksonville', 'fortlauderdale', 'pensacola', 'sarasota', 'naples', 'daytona', 'gainesville'];16        const dealerships = ['autos', 'motors', 'cars', 'dealers', 'auto', 'usedcars', 'preowned', 'luxury', 'trucks', 'imports'];17        const tlds = ['com', 'org', 'net', 'io', 'co'];18        const paths = ['contact', 'contact-us', 'get-in-touch', 'support', 'help'];19        20        const city = cities[Math.floor(Math.random() * cities.length)];21        const dealer = dealerships[Math.floor(Math.random() * dealerships.length)];22        const tld = tlds[Math.floor(Math.random() * tlds.length)];23        const path = paths[Math.floor(Math.random() * paths.length)];24        const url = `https://${city}${dealer}.${tld}/${path}`;25        // Add some "used car dealers in florida" specific keywords to some URLs26        if (i % 3 === 0) {27            const floridaKeywords = ['used-car-dealers-florida', 'florida-used-cars', 'best-used-cars-florida', 'used-auto-florida'];28            const keyword = floridaKeywords[Math.floor(Math.random() * floridaKeywords.length)];29            url = `https://${keyword.replace(/-/g, '')}.${tld}/${path}`;30        }31        if (!hasForm) {32            return { url, hasForm: false };33        }34const methods = ['POST', 'GET'];35        const actions = ['/submit', '/contact', '/process', '/send', '/form-handler'];36        const fieldTypes = ['text', 'email', 'tel', 'textarea', 'select'];37        const fieldLabels = ['Name', 'Email', 'Phone', 'Message', 'Subject', 'Company', 'Inquiry'];38        39        const numFields = Math.floor(Math.random() * 5) + 3; // 3-7 fields40        const fields = Array.from({length: numFields}, (_, i) => {41            const type = fieldTypes[Math.floor(Math.random() * fieldTypes.length)];42            const label = fieldLabels[Math.floor(Math.random() * fieldLabels.length)];43            return {44                name: label.toLowerCase().replace(' ', '_') + (i > 0 ? i : ''),45                type,46                label47            };48        });49        50        return {51            url,52            hasForm: true,53            formDetails: {54                method: methods[Math.floor(Math.random() * methods.length)],55                action: actions[Math.floor(Math.random() * actions.length)],56                fields57            }58        };59    });60 61    // Pagination variables62    let currentPage = 1;63    const resultsPerPage = 10;64    // Start scraping button click handler65    scrapeBtn.addEventListener('click', async function() {66        const keywords = document.getElementById('keywords').value.trim();67        const maxResults = document.getElementById('max-results').value;68        69        if (!keywords) {70            alert('Please enter at least one keyword');71            return;72        }73 74        // Show loading indicator75        loadingIndicator.classList.remove('hidden');76        resultsContainer.classList.add('hidden');77        scrapeBtn.disabled = true;78        scrapeBtn.classList.add('opacity-75');79 80        try {81            // Using ScraperAPI as an example (you would need to sign up for an API key)82            const apiKey = 'YOUR_SCRAPERAPI_KEY'; // Replace with your actual API key83            const searchQuery = encodeURIComponent(keywords);84            const apiUrl = `https://api.scraperapi.com/structured/google/search?api_key=${apiKey}&q=${searchQuery}&num=${maxResults}`;85 86            const response = await fetch(apiUrl);87            const data = await response.json();88 89            // Process results into our format90            const processedResults = data.organic_results.map(result => ({91                url: result.link,92                hasForm: Math.random() > 0.3, // Randomly assign form presence for demo93                formDetails: {94                    method: 'POST',95                    action: '/submit',96                    fields: [97                        { name: 'name', type: 'text', label: 'Name' },98                        { name: 'email', type: 'email', label: 'Email' },99                        { name: 'message', type: 'textarea', label: 'Message' }100                    ]101                }102            }));103 104            currentPage = 1;105            displayResults(processedResults);106        } catch (error) {107            console.error('Scraping failed:', error);108            alert('Scraping failed. Please try again later.');109            // Fall back to mock data if API fails110            generateMockData();111            currentPage = 1;112            displayResults(mockResults);113} finally {114            loadingIndicator.classList.add('hidden');115            resultsContainer.classList.remove('hidden');116            scrapeBtn.disabled = false;117            scrapeBtn.classList.remove('opacity-75');118        }119});120    // Export button click handler121    exportBtn.addEventListener('click', function() {122        // Convert results to CSV123        const rows = mockResults.map(result => {124            return [125                `"${result.url}"`,126                result.hasForm ? 'Yes' : 'No',127                result.hasForm ? result.formDetails.method : '',128                result.hasForm ? result.formDetails.action : '',129                result.hasForm ? result.formDetails.fields.length : ''130            ].join(',');131        });132        133        const csvContent = [134            ['URL', 'Has Form', 'Method', 'Action', 'Field Count'].join(','),135            ...rows136        ].join('\n');137        138        // Create download link139        const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });140        const url = URL.createObjectURL(blob);141        const link = document.createElement('a');142        link.setAttribute('href', url);143        link.setAttribute('download', 'formfinder_results.csv');144        link.style.visibility = 'hidden';145        document.body.appendChild(link);146        link.click();147        document.body.removeChild(link);148});149    // Display results in the table with pagination150    function displayResults(results) {151        resultsBody.innerHTML = '';152        153        // Calculate pagination154        const startIndex = (currentPage - 1) * resultsPerPage;155        const endIndex = Math.min(startIndex + resultsPerPage, results.length);156        const paginatedResults = results.slice(startIndex, endIndex);157        158        // Display current page results159        paginatedResults.forEach((result, index) => {160const row = document.createElement('tr');161            row.className = 'result-row';162            row.style.animationDelay = `${index * 0.1}s`;163            164            row.innerHTML = `165                <td class="px-6 py-4 whitespace-nowrap">166                    <div class="text-sm font-medium text-gray-900 truncate max-w-xs">${result.url}</div>167                </td>168                <td class="px-6 py-4 whitespace-nowrap">169                    <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full 170                        ${result.hasForm ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}">171                        ${result.hasForm ? 'Yes' : 'No'}172                    </span>173                </td>174                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">175                    <button class="view-form-btn text-blue-600 hover:text-blue-900 mr-3" data-url="${result.url}">176                        <i data-feather="eye" class="w-4 h-4"></i>177                    </button>178<button class="text-blue-600 hover:text-blue-900">179                        <i data-feather="external-link" class="w-4 h-4"></i>180                    </button>181                </td>182            `;183            resultsBody.appendChild(row);184        });185        186        // Add pagination controls187        const totalPages = Math.ceil(results.length / resultsPerPage);188        const paginationContainer = document.createElement('div');189        paginationContainer.className = 'flex justify-between items-center mt-4';190        191        paginationContainer.innerHTML = `192            <div class="text-sm text-gray-500">193                Showing ${startIndex + 1}-${endIndex} of ${results.length} results194            </div>195            <div class="flex space-x-2">196                <button id="prev-page" class="px-3 py-1 border rounded ${currentPage === 1 ? 'opacity-50 cursor-not-allowed' : 'hover:bg-gray-50'}">197                    Previous198                </button>199                <div class="flex items-center">200                    Page ${currentPage} of ${totalPages}201                </div>202                <button id="next-page" class="px-3 py-1 border rounded ${currentPage === totalPages ? 'opacity-50 cursor-not-allowed' : 'hover:bg-gray-50'}">203                    Next204                </button>205            </div>206        `;207        208        resultsContainer.appendChild(paginationContainer);209        feather.replace();210        211        // Pagination event listeners212        document.getElementById('prev-page')?.addEventListener('click', () => {213            if (currentPage > 1) {214                currentPage--;215                displayResults(results);216                window.scrollTo({ top: 0, behavior: 'smooth' });217            }218        });219        220        document.getElementById('next-page')?.addEventListener('click', () => {221            if (currentPage < totalPages) {222                currentPage++;223                displayResults(results);224                window.scrollTo({ top: 0, behavior: 'smooth' });225            }226        });227// Add click handlers for view form buttons228        document.querySelectorAll('.view-form-btn').forEach(btn => {229            btn.addEventListener('click', function() {230                const url = this.getAttribute('data-url');231                const result = mockResults.find(r => r.url === url);232                if (result && result.hasForm) {233                    showFormDetails(result.formDetails, url);234                }235            });236        });237    }238 239    // Show form details in modal240    function showFormDetails(formDetails, url) {241        const modal = document.createElement('div');242        modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50';243        modal.innerHTML = `244            <div class="bg-white rounded-lg shadow-xl max-w-2xl w-full max-h-[80vh] overflow-y-auto">245                <div class="p-6">246                    <div class="flex justify-between items-start mb-4">247                        <h3 class="text-lg font-medium text-gray-900">Form Details: ${url}</h3>248                        <button class="close-modal text-gray-400 hover:text-gray-500">249                            <i data-feather="x"></i>250                        </button>251                    </div>252                    <div class="space-y-4">253                        <div>254                            <h4 class="text-sm font-medium text-gray-700 mb-2">Form Attributes</h4>255                            <div class="grid grid-cols-2 gap-4">256                                <div>257                                    <p class="text-sm text-gray-500">Method</p>258                                    <p class="text-sm font-medium text-gray-900">${formDetails.method}</p>259                                </div>260                                <div>261                                    <p class="text-sm text-gray-500">Action</p>262                                    <p class="text-sm font-medium text-gray-900">${formDetails.action}</p>263                                </div>264                            </div>265                        </div>266                        <div>267                            <h4 class="text-sm font-medium text-gray-700 mb-2">Form Fields</h4>268                            <div class="border border-gray-200 rounded-lg overflow-hidden">269                                <table class="min-w-full divide-y divide-gray-200">270                                    <thead class="bg-gray-50">271                                        <tr>272                                            <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>273                                            <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Type</th>274                                            <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Label</th>275                                        </tr>276                                    </thead>277                                    <tbody class="bg-white divide-y divide-gray-200">278                                        ${formDetails.fields.map(field => `279                                            <tr>280                                                <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">${field.name}</td>281                                                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${field.type}</td>282                                                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${field.label}</td>283                                            </tr>284                                        `).join('')}285                                    </tbody>286                                </table>287                            </div>288                        </div>289                    </div>290                </div>291            </div>292        `;293        294        document.body.appendChild(modal);295        feather.replace();296        297        // Close modal handler298        modal.querySelector('.close-modal').addEventListener('click', () => {299            modal.remove();300        });301        302        // Close when clicking outside303        modal.addEventListener('click', (e) => {304            if (e.target === modal) {305                modal.remove();306            }307        });308    }309// Add pulse animation to scrape button on hover310    scrapeBtn.addEventListener('mouseenter', function() {311        this.classList.add('pulse');312    });313    314    scrapeBtn.addEventListener('mouseleave', function() {315        this.classList.remove('pulse');316    });317});