<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web Scraping Tool</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
            background-color: #e0e0e0;
            display: flex;
            justify-content: center;
            align-items: flex-start;
            flex-direction: column;
            min-height: 100vh;
            width: 100%;
            box-sizing: border-box;
        }

        h1 {
            color: #333;
            text-align: center;
            width: 100%;
            margin: 20px 0;
        }

        .table-container {
            width: 100%;
            max-width: 1200px;
            margin: 0 auto;
            background-color: #e0e0e0;
        }

        table {
            width: 100%;
            border-collapse: collapse;
            box-shadow: inset 1px 1px 2px #d1d1d1, inset -1px -1px 2px #ffffff;
            border-radius: 12px;
            overflow: hidden;
        }

        th,
        td {
            padding: 12px;
            text-align: left;
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
        }

        th {
            background-color: #e0e0e0;
            box-shadow: inset 1px 1px 2px #d1d1d1, inset -1px -1px 2px #ffffff;
            color: #555;
        }

        td {
            background-color: #e0e0e0;
            color: #333;
        }

        input[type="text"] {
            width: calc(100% - 16px);
            padding: 8px;
            border: none;
            border-radius: 8px;
            box-shadow: inset 2px 2px 5px #bebebe, inset -2px -2px 5px #ffffff;
            background-color: #e0e0e0;
        }

        button {
            padding: 10px 15px;
            border: none;
            border-radius: 8px;
            background-color: #e0e0e0;
            box-shadow: 5px 5px 10px #bebebe, -5px -5px 10px #ffffff;
            cursor: pointer;
            font-size: 14px;
            margin-right: 5px;
        }

        button:hover {
            box-shadow: inset 2px 2px 5px #bebebe, inset -2px -2px 5px #ffffff;
        }

        .debug-panel,
        .result-panel {
            position: fixed;
            top: 8%;
            left: 50%;
            transform: translateX(-50%);
            width: 80%;
            max-height: 400px;
            overflow-y: auto;
            background-color: #e0e0e0;
            box-shadow: 8px 8px 20px #bebebe, -8px -8px 20px #ffffff;
            border-radius: 10px;
            padding: 20px;
            display: none;
            z-index: 1000;
        }

        .close-btn {
            float: right;
            cursor: pointer;
            font-weight: bold;
            color: #333;
        }

        .overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.3);
            display: none;
            z-index: 999;
        }

        .auto-fetch-switch-container,
        .regex-toggle-container {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
            width: 100%;
            max-width: 1200px;
            box-sizing: border-box;
        }

        .switch-label-container {
            display: flex;
            align-items: center;
        }

        .auto-fetch-switch-label,
        .regex-switch-label {
            margin-right: 10px;
            color: #333;
            font-weight: bold;
        }

        .auto-fetch-switch,
        .regex-switch {
            position: relative;
            display: inline-block;
            width: 50px;
            height: 25px;
        }

        .auto-fetch-switch input,
        .regex-switch input {
            display: none;
        }

        .slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: #ccc;
            transition: .4s;
            border-radius: 34px;
        }

        .slider:before {
            position: absolute;
            content: "";
            height: 20px;
            width: 20px;
            left: 3px;
            bottom: 2px;
            background-color: white;
            transition: .4s;
            border-radius: 50%;
        }

        input:checked+.slider {
            background-color: #2196F3;
        }

        input:checked+.slider:before {
            transform: translateX(24px);
        }

        .request-access-link {
            color: #2196F3;
            font-weight: bold;
            text-decoration: none;
        }

        .result-link {
            color: #2196F3;
            cursor: pointer;
            text-decoration: underline;
        }
    </style>
</head>

<body>

    <h1>Web Scraping Tool</h1>
    <div class="table-container">
        <div class="auto-fetch-switch-container">
            <div class="switch-label-container">
                <span class="auto-fetch-switch-label">Auto Fetch on Load</span>
                <label class="auto-fetch-switch">
                    <input type="checkbox" id="auto-fetch-switch" onchange="toggleAutoFetch(this.checked)">
                    <span class="slider"></span>
                </label>
            </div>
            <a href="https://cors-anywhere.herokuapp.com/corsdemo" class="request-access-link" target="_blank">Not working? Request access here</a>
        </div>
        <table>
            <thead>
                <tr>
                    <th>Name</th>
                    <th>URL</th>
                    <th>Selector/Regex</th>
                    <th>Use regex?</th>
                    <th>Results</th>
                    <th>Actions</th>
                </tr>
            </thead>
            <tbody id="scrape-table-body">
                <tr>
                    <td><input type="text" id="new-name" placeholder="Enter name"></td>
                    <td><input type="text" id="new-url" placeholder="Enter URL"></td>
                    <td><input type="text" id="new-selector" placeholder="Enter Selector or Regex"></td>
                    <td>
                        <label class="regex-switch">
                            <input type="checkbox" id="new-regex">
                            <span class="slider"></span>
                        </label>
                    </td>
                    <td colspan="2"><button onclick="addNewRecord()">✚</button></td>
                </tr>
            </tbody>
        </table>
    </div>

    <div id="overlay" class="overlay" onclick="closePanels()"></div>
    <div id="debug-panel" class="debug-panel">
        <span class="close-btn" onclick="toggleDebugPanel(null, true)">×</span>
        <pre id="debug-content"></pre>
    </div>
    <div id="result-panel" class="result-panel">
        <span class="close-btn" onclick="toggleResultPanel(null, true)">×</span>
        <pre id="result-content"></pre>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <script>
        const recordsKey = 'scrapeRecords';
        const autoFetchKey = 'autoFetch';
        const maxResultLength = 50;

        document.addEventListener('DOMContentLoaded', () => {
            const autoFetchState = localStorage.getItem(autoFetchKey);
            const autoFetch = (autoFetchState === 'true');
            const autoFetchSwitch = document.getElementById('auto-fetch-switch');
            autoFetchSwitch.checked = autoFetch;
            loadRecords(autoFetch);
        });

        function toggleAutoFetch(isEnabled) {
            localStorage.setItem(autoFetchKey, isEnabled);
            if (isEnabled) {
                executeAllRecords();
            }
        }

        function loadRecords(autoFetch) {
            const records = JSON.parse(localStorage.getItem(recordsKey)) || [];
            records.forEach((record, index) => {
                addTableRow(record, index);
                if (autoFetch) {
                    executeRecord(index);
                }
            });
        }

        function addTableRow({ name, url, selector, isRegex }, index) {
            const tableBody = document.getElementById('scrape-table-body');
            const row = document.createElement('tr');

            row.innerHTML = `
                <td><input type="text" value="${name}" onchange="updateRecord(${index}, 'name', this.value)" onkeydown="checkEnter(event, ${index})"></td>
                <td><input type="text" value="${url}" onchange="updateRecord(${index}, 'url', this.value)" onkeydown="checkEnter(event, ${index})"></td>
                <td><input type="text" value="${escapeHtml(selector)}" onchange="updateRecord(${index}, 'selector', this.value)" onkeydown="checkEnter(event, ${index})"></td>
                <td>
                    <label class="regex-switch">
                        <input type="checkbox" id="regex-${index}" onchange="updateRecord(${index}, 'isRegex', this.checked)" ${isRegex ? 'checked' : ''}>
                        <span class="slider"></span>
                    </label>
                </td>
                <td id="result-${index}"></td>
                <td>
                    <button onclick="executeRecord(${index})">▶️</button>
                    <button onclick="removeRecord(${index})">🗑️</button>
                    <button onclick="toggleDebugPanel(${index}, false)">ℹ️</button>
                </td>
            `;
            tableBody.insertBefore(row, tableBody.lastElementChild);
        }

        function checkEnter(event, index) {
            if (event.key === 'Enter') {
                executeRecord(index);
            }
        }

        function addNewRecord() {
            const name = document.getElementById('new-name').value;
            const url = document.getElementById('new-url').value;
            const selector = document.getElementById('new-selector').value;
            const isRegex = document.getElementById('new-regex').checked;

            if (!name || !url || !selector) {
                alert('All fields are required!');
                return;
            }

            const record = { name, url, selector, isRegex, response: '', timestamp: '' };
            let records = JSON.parse(localStorage.getItem(recordsKey)) || [];
            records.push(record);
            localStorage.setItem(recordsKey, JSON.stringify(records));

            const index = records.length - 1;
            addTableRow(record, index);
            executeRecord(index);

            document.getElementById('new-name').value = '';
            document.getElementById('new-url').value = '';
            document.getElementById('new-selector').value = '';
            document.getElementById('new-regex').checked = false;
        }

        function executeRecord(index) {
            const records = JSON.parse(localStorage.getItem(recordsKey)) || [];
            const { url, selector, isRegex } = records[index];
            document.getElementById(`result-${index}`).textContent = '⏳';
            executeScraping(url, selector, isRegex, index);
        }

        function executeScraping(url, selector, isRegex, index) {
            const proxy = 'https://cors-anywhere.herokuapp.com/';
            const options = {
                headers: {
                    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
                    'Accept-Language': 'en-US,en;q=0.9'
                }
            };

            axios.get(proxy + url, options)
                .then(response => {
                    let resultText = 'No matches';
                    if (isRegex) {
                        try {
                            const regex = new RegExp(selector);
                            const match = regex.exec(response.data);
                            resultText = match ? match[0] : 'No matches';
                        } catch (e) {
                            console.error('Invalid regex:', e);
                            resultText = 'Invalid regex';
                        }
                    } else {
                        const parser = new DOMParser();
                        const doc = parser.parseFromString(response.data, 'text/html');
                        const resultElement = doc.querySelector(selector);
                        resultText = resultElement ? resultElement.textContent.trim() : 'No matches';
                    }

                    const timestamp = new Date().toISOString();
                    updateResult(index, response.data, timestamp, resultText);
                })
                .catch(error => {
                    console.error(`Error fetching the URL: ${url}`, error);
                    const timestamp = new Date().toISOString();
                    updateResult(index, `Error: ${error.message}`, timestamp, 'Error occurred');
                });
        }

        function updateResult(index, response, timestamp, resultText) {
            const records = JSON.parse(localStorage.getItem(recordsKey)) || [];
            records[index].response = response;
            records[index].timestamp = timestamp;
            localStorage.setItem(recordsKey, JSON.stringify(records));

            let displayResult = resultText;
            if (resultText.length > maxResultLength) {
                displayResult = `${resultText.substring(0, maxResultLength)}... <span class="result-link" onclick="toggleResultPanel(${index}, false)">Read more</span>`;
            }

            document.getElementById(`result-${index}`).innerHTML = displayResult;
        }

        function updateRecord(index, field, value) {
            const records = JSON.parse(localStorage.getItem(recordsKey)) || [];
            records[index][field] = value;
            localStorage.setItem(recordsKey, JSON.stringify(records));
        }

        function removeRecord(index) {
            let records = JSON.parse(localStorage.getItem(recordsKey)) || [];
            records.splice(index, 1);
            localStorage.setItem(recordsKey, JSON.stringify(records));
            document.location.reload();
        }

        function toggleDebugPanel(index, close = false) {
            const debugPanel = document.getElementById('debug-panel');
            const debugContent = document.getElementById('debug-content');
            const overlay = document.getElementById('overlay');
            const records = JSON.parse(localStorage.getItem(recordsKey)) || [];

            if (close || index === null) {
                debugPanel.style.display = 'none';
                overlay.style.display = 'none';
                debugContent.innerHTML = '';
                return;
            }

            const timestamp = records[index].timestamp || 'No timestamp';
            const response = records[index].response || 'No response';
            const debugText = `Timestamp: ${timestamp}\n\nRaw Response:\n${response}`;
            debugPanel.style.display = 'block';
            overlay.style.display = 'block';
            debugContent.textContent = debugText;
        }

        function toggleResultPanel(index, close = false) {
            const resultPanel = document.getElementById('result-panel');
            const resultContent = document.getElementById('result-content');
            const overlay = document.getElementById('overlay');
            const records = JSON.parse(localStorage.getItem(recordsKey)) || [];

            if (close || index === null) {
                resultPanel.style.display = 'none';
                overlay.style.display = 'none';
                resultContent.innerHTML = '';
                return;
            }

            const resultText = records[index].response || 'No Result Data';
            resultPanel.style.display = 'block';
            overlay.style.display = 'block';
            resultContent.textContent = resultText;
        }

        function closePanels() {
            toggleDebugPanel(null, true);
            toggleResultPanel(null, true);
        }

        function escapeHtml(text) {
            const map = {
                '&': '&amp;',
                '<': '&lt;',
                '>': '&gt;',
                '"': '&quot;',
                "'": '&#039;'
            };
            return text.replace(/[&<>"']/g, function (m) { return map[m]; });
        }

        function executeAllRecords() {
            const records = JSON.parse(localStorage.getItem(recordsKey)) || [];
            records.forEach((_, index) => {
                executeRecord(index);
            });
        }
    </script>
</body>

</html>

this is the full code of the a scraping tool, as you notice some information is save to the cookie. add the UI and function that help to export and import these information. the import export button can stay at the top right corner for easy access