您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Convert HTML tables to Markdown/XWiki/CSV format
// ==UserScript== // @name HTML Table to Markdown/XWiki/CSV Converter // @namespace tungxd301 // @version 1.1 // @description Convert HTML tables to Markdown/XWiki/CSV format // @author Tung Dinh // @match *://*/* // @run-at document-end // @grant GM_setClipboard // @license Tung Dinh // ==/UserScript== (function () { 'use strict'; // Configuration options const config = { waitForDOMTimeout: 3000, toastDuration: 3000, // Duration of toast message display (in milliseconds) maxAllowedTotalPaginatedPages: 100, childList: true, // Watch for changes to the child nodes (elements added or removed) subtree: true, // Watch for changes in the whole subtree, not just the immediate children }; const a = document.querySelector('[href*="&page="]'); const loadingContainer = document.createElement('div'); loadingContainer.id = 'loading-container'; // Create the loading circle element const loadingCircle = document.createElement('div'); loadingCircle.id = 'loading-circle'; loadingContainer.appendChild(loadingCircle); // Create the loading progress element const loadingProgress = document.createElement('div'); loadingProgress.id = 'loading-progress'; loadingContainer.appendChild(loadingProgress); // Function to convert an HTML table to Markdown function tableToMarkdown(table) { let markdown = '|'; // Iterate through table headers table.querySelectorAll('th').forEach(header => { markdown += header.textContent.trim() + '|'; }); markdown += '\n|'; // Add line separator below headers table.querySelectorAll('th').forEach(() => { markdown += ' --- |'; }); // Iterate through table rows table.querySelectorAll('tr').forEach(row => { row.querySelectorAll('td').forEach(cell => { markdown += cell.textContent.trim() + '|'; }); markdown += '\n|'; }); return markdown.slice(0, -1); } // Function to convert an HTML table to XWiki syntax function tableToXWiki(table) { let xwikiSyntax = ''; // Iterate through table rows table.querySelectorAll('tr').forEach(row => { row.querySelectorAll('th, td').forEach(cell => { // Determine cell type (header or data) const cellType = cell.tagName === 'TH' ? 'th' : 'td'; // Append cell content to XWiki syntax with proper formatting xwikiSyntax += `|${cell.textContent.trim()}`; if (cellType === 'th') { xwikiSyntax += ' (header)'; } }); // Add a new row xwikiSyntax += '\n'; }); return xwikiSyntax.trim(); } // Function to convert an HTML table to CSV format function tableToCSV(table) { const rows = table.querySelectorAll('tr'); let csv = ''; for (let i = 0; i < rows.length; i++) { const row = rows[i].querySelectorAll('th, td'); for (let j = 0; j < row.length; j++) { csv += '"' + row[j].textContent.trim() + '"'; if (j < row.length - 1) { csv += ','; } } csv += '\n'; } return csv; } // Function to initiate the download function downloadCSV(table, tableIndex) { const csvContent = tableToCSV(table); const blob = new Blob([csvContent], {type: 'text/csv'}); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `table_${tableIndex}.csv`; link.click(); URL.revokeObjectURL(url); } async function downloadCSVAllPages(tableIndex) { showToast("Downloading CSV...Hang tight!"); let page = 1; let mergedTable = document.createElement('table'); let currentTables = await fetchTableAllPages(page); while (page < config.maxAllowedTotalPaginatedPages && currentTables != null && currentTables.length > 0 && currentTables.length <= tableIndex + 1) { updateLoadingProgress(page); let table = currentTables[tableIndex]; // Loop through each row in the table var rows = table.querySelectorAll('tr'); rows.forEach(function (row, rowIndex) { // Clone the row from the original table var clonedRow = row.cloneNode(true); // If it's not the first table, and it's the first row (header row), skip it if (page > 1 && rowIndex === 0) { return; } // Append the cloned row to the merged table mergedTable.appendChild(clonedRow); }); currentTables = await fetchTableAllPages(++page); } downloadCSV(mergedTable, tableIndex); loadingContainer.remove(); } async function fetchTableAllPages(page) { let splitPage = a.href.split('page=') || ''; let pagePart = splitPage[1]; let basePage = getFirstDigits(pagePart); let targetPagePart = pagePart.replace(basePage, page); let url = splitPage[0] + 'page=' + targetPagePart; return await fetch(url) .then(response => response.text()) .then(html => { const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); return doc.querySelectorAll('table'); }) .catch(error => { return []; }); } function getFirstDigits(inputString) { const firstDigits = inputString.match(/\d+/); if (firstDigits !== null) { return firstDigits[0]; } else { return -1; } } // Function to display a toast message function showToast(message) { const toast = document.createElement('div'); toast.textContent = message; toast.style.cssText = ` position: fixed; bottom: 10px; left: 50%; transform: translateX(-50%); background-color: #333; color: #fff; padding: 10px 20px; border-radius: 5px; `; document.body.appendChild(toast); setTimeout(() => { toast.remove(); }, config.toastDuration); // Display for 3 seconds } // Function to update the loading indicator with data points function updateLoadingProgress(dataPointsLoaded) { loadingProgress.textContent = `Downloading page ${dataPointsLoaded}`; } // Function to copy all tables to clipboard function copyAllTablesToClipboard() { let clipboard = ''; let allMarkdown = ''; let allXWiki = ''; // Find and process all HTML tables on the page document.querySelectorAll('table').forEach((table, tableIndex) => { downloadCSV(table, tableIndex); const markdown = tableToMarkdown(table); allMarkdown += markdown + '\n---\n'; // Add a separator between tables const xwiki = tableToXWiki(table); allXWiki += xwiki + '\n----\n'; // Add a separator between tables }); clipboard += 'Markdown Tables: \n\n' + allMarkdown + '\n'; clipboard += 'XWiki Tables: \n\n' + allXWiki + '\n'; GM_setClipboard(clipboard); // Display a toast notification showToast('Markdown/XWiki table copied to clipboard!'); } // Add a keyboard shortcut to copy all tables (e.g., Ctrl + Shift + C) window.addEventListener('keydown', event => { if (event.ctrlKey && event.shiftKey && event.key === 'C') { copyAllTablesToClipboard(); event.preventDefault(); } }); // Create a Mutation Observer to watch for changes in the DOM const observer = new MutationObserver(function(mutations) { // Check if the tables you're looking for are now available in the DOM if (document.querySelectorAll('table').length > 0) { // Disconnect the observer to stop watching for changes observer.disconnect(); setTimeout(() => { processTables(); }, config.waitForDOMTimeout); } }); // Start observing changes in the DOM observer.observe(document.body, { childList: true, subtree: true }); function processTables() { // Find and process all HTML tables on the page document.querySelectorAll('table').forEach((table, tableIndex) => { const computedStyle = window.getComputedStyle(table); if (computedStyle.display === 'none') { return; } const markdown = tableToMarkdown(table); const xwiki = tableToXWiki(table); // Create a markdown button element const markdownButton = document.createElement('button'); markdownButton.className = 'clipboard-button'; // Add a CSS class for styling markdownButton.innerHTML = '<i class="fa fa-clipboard" aria-hidden="true"></i> Copy Markdown Table to Clipboard'; // Create a xwiki button element const xwikiButton = document.createElement('button'); xwikiButton.className = 'clipboard-button'; // Add a CSS class for styling xwikiButton.innerHTML = '<i class="fa fa-clipboard" aria-hidden="true"></i> Copy XWiki Table to Clipboard'; // Create a csv button element const csvButton = document.createElement('button'); csvButton.className = 'clipboard-button'; // Add a CSS class for styling csvButton.innerHTML = '<i class="fa fa-clipboard" aria-hidden="true"></i> Download CSV'; // Create a csv button element const csvAllButton = document.createElement('button'); csvAllButton.className = 'clipboard-button'; // Add a CSS class for styling csvAllButton.innerHTML = '<i class="fa fa-clipboard" aria-hidden="true"></i> Download CSV All Pages'; // Add a click event listener to the button markdownButton.addEventListener('click', () => { GM_setClipboard(markdown); showToast('Markdown table copied to clipboard!'); }); // Add a click event listener to the button xwikiButton.addEventListener('click', () => { GM_setClipboard(xwiki); showToast('XWiki table copied to clipboard!'); }); // Add a click event listener to the button csvButton.addEventListener('click', () => { if (table) { downloadCSV(table, tableIndex); } else { showToast('No table found on this page.'); } }); // Add a click event listener to the button csvAllButton.addEventListener('click', () => { if (table) { document.body.appendChild(loadingContainer); downloadCSVAllPages(tableIndex); } else { showToast('No table found on this page.'); } }); // Append the button under the table const container = document.createElement('div'); container.appendChild(markdownButton); container.appendChild(xwikiButton); container.appendChild(csvButton); if (a != null) { container.appendChild(csvAllButton); } table.parentNode.insertBefore(container, table.nextSibling); }); } // Add custom CSS styles for your UI elements (customize as needed) const styles = ` .markdown-table-container { margin-bottom: 20px; border: 1px solid #ccc; padding: 10px; } .clipboard-button { display: inline-block; padding: 8px 16px; font-size: 12px; font-weight: bold; text-align: center; text-decoration: none; border: none; border-radius: 5px; cursor: pointer; transition: background-color 0.3s, color 0.3s; margin-top: 10px; margin-right: 10px; margin-bottom: 10px; } .clipboard-button:hover { background-color: #0056b3; /* Hover background color */ color: #fff; /* Hover text color */ } .clipboard-button i { margin-right: 5px; } #loading-container { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); display: flex; flex-direction: column; align-items: center; } #loading-circle { border: 4px solid transparent; border-top: 4px solid #007BFF; /* Loading circle color */ border-radius: 50%; width: 40px; height: 40px; animation: spin 1s linear infinite; margin-bottom: 10px; } #loading-progress { font-size: 12px; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .toast { position: fixed; bottom: 20px; right: 20px; background-color: #333; color: #fff; padding: 10px; border-radius: 4px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); z-index: 9999; }`; // Create a <style> element and add the custom styles const styleElement = document.createElement('style'); styleElement.textContent = styles; document.head.appendChild(styleElement); })();