您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds listed pages to a Navbox upon request with UI, CSRF token, adaptive speed, duplicate checker, and highlighted links option.
当前为
// ==UserScript== // @name OSRS Wiki Auto-Navbox with UI, Adaptive Speed, Duplicate Checker (Slow Version) // @namespace https://github.com/Nick2bad4u/UserStyles // @version 5.2 // @description Adds listed pages to a Navbox upon request with UI, CSRF token, adaptive speed, duplicate checker, and highlighted links option. // @author Nick2bad4u // @match https://oldschool.runescape.wiki/* // @match https://runescape.wiki/* // @match https://*.runescape.wiki/* // @match https://api.runescape.wiki/* // @match https://classic.runescape.wiki/* // @match *://*.runescape.wiki/* // @grant GM_xmlhttpRequest // @icon https://www.google.com/s2/favicons?sz=64&domain=oldschool.runescape.wiki // @license UnLicense // ==/UserScript== (function () { "use strict"; const versionNumber = "5.1"; let navboxName = ""; let pageLinks = []; let selectedLinks = []; let currentIndex = 0; let csrfToken = ""; let isCancelled = false; let isRunning = false; let requestInterval = 10000; const maxInterval = 20000; const excludedPrefixes = [ "Template:", "File:", "Navbox:", "Module:", "RuneScape:", "Update:", "Exchange:", "RuneScape:", "User:", "Help:", ]; let actionLog = []; // Track actions for summary function addButtonAndProgressBar() { console.log("Adding button and progress bar to the UI."); const container = document.createElement("div"); container.id = "Navbox-ui"; container.style = `position: fixed; bottom: 20px; right: 20px; z-index: 1000; background-color: #2b2b2b; color: #ffffff; padding: 12px; border: 1px solid #595959; border-radius: 8px; font-family: Arial, sans-serif; width: 250px;`; const startButton = document.createElement("button"); startButton.textContent = "Start Categorizing"; startButton.style = `background-color: #4caf50; color: #fff; border: none; padding: 6px 12px; border-radius: 5px; cursor: pointer;`; startButton.onclick = promptnavboxName; container.appendChild(startButton); const cancelButton = document.createElement("button"); cancelButton.textContent = "Cancel"; cancelButton.style = `background-color: #d9534f; color: #fff; border: none; padding: 6px 12px; border-radius: 5px; cursor: pointer; margin-left: 10px;`; cancelButton.onclick = cancelNavbox; container.appendChild(cancelButton); const progressBarContainer = document.createElement("div"); progressBarContainer.style = `width: 100%; margin-top: 10px; background-color: #3d3d3d; height: 20px; border-radius: 5px; position: relative;`; progressBarContainer.id = "progress-bar-container"; const progressBar = document.createElement("div"); progressBar.style = `width: 0%; height: 100%; background-color: #4caf50; border-radius: 5px;`; progressBar.id = "progress-bar"; progressBarContainer.appendChild(progressBar); const progressText = document.createElement("span"); progressText.id = "progress-text"; progressText.style = `position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); font-size: 12px; color: #ffffff; white-space: nowrap; overflow: visible; text-align: center;`; progressBarContainer.appendChild(progressText); container.appendChild(progressBarContainer); document.body.appendChild(container); } function promptnavboxName() { navboxName = prompt("Enter the Navbox name you'd like to add:"); console.log("Navbox name entered:", navboxName); if (!navboxName) { alert("Navbox name is required."); return; } getPageLinks(); if (pageLinks.length === 0) { alert("No pages found to Navbox."); console.log("No pages found after filtering."); return; } displayPageSelectionPopup(); } // Function to check for highlighted text function getHighlightedText() { const selection = globalThis.getSelection(); if (selection.rangeCount > 0) { const container = document.createElement("div"); for (let i = 0; i < selection.rangeCount; i++) { container.appendChild(selection.getRangeAt(i).cloneContents()); } return container.innerHTML; } return ""; } // Modify getPageLinks to consider highlighted text function getPageLinks() { let contextElement = document.querySelector("#mw-content-text"); const highlightedText = getHighlightedText(); if (highlightedText) { // Create a temporary container to process the highlighted text const tempContainer = document.createElement("div"); tempContainer.innerHTML = highlightedText; contextElement = tempContainer; } pageLinks = Array.from( new Set( Array.from(contextElement.querySelectorAll("a")) .map((link) => link.getAttribute("href")) .filter((href) => href && href.startsWith("/w/")) .map((href) => decodeURIComponent(href.replace("/w/", ""))) .filter( (page) => !excludedPrefixes.some((prefix) => page.startsWith(prefix)) && !page.includes("?") && !page.includes("/") && !page.includes("#"), ), ), ); console.log("Filtered unique page links:", pageLinks); } function displayPageSelectionPopup() { console.log("Displaying page selection popup."); const popup = document.createElement("div"); popup.style = `position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 1001; background-color: #2b2b2b; padding: 15px; color: white; border-radius: 8px; max-height: 80vh; overflow-y: auto; border: 1px solid #595959;`; const title = document.createElement("h3"); title.textContent = "Select Pages to Navbox"; title.style = `margin: 0 0 10px; font-family: Arial, sans-serif;`; popup.appendChild(title); const listContainer = document.createElement("div"); listContainer.style = `max-height: 300px; overflow-y: auto;`; // Declare lastChecked outside the event listener to keep track of the last clicked checkbox let lastChecked = null; pageLinks.forEach((link) => { const listItem = document.createElement("div"); const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.checked = true; checkbox.value = link; listItem.appendChild(checkbox); listItem.appendChild(document.createTextNode(` ${link}`)); listContainer.appendChild(listItem); checkbox.addEventListener("click", function (e) { if (e.shiftKey && lastChecked) { let inBetween = false; listContainer .querySelectorAll('input[type="checkbox"]') .forEach((checkbox) => { if (checkbox === this || checkbox === lastChecked) { inBetween = !inBetween; } if (inBetween) { checkbox.checked = this.checked; } }); } lastChecked = this; }); }); popup.appendChild(listContainer); const buttonContainer = document.createElement("div"); buttonContainer.style = `margin-top: 10px; display: flex; justify-content: space-between;`; let allSelected = true; const selectAllButton = document.createElement("button"); selectAllButton.textContent = "Select All"; selectAllButton.style = `padding: 5px 10px; background-color: #5bc0de; border: none; color: white; cursor: pointer; border-radius: 5px;`; selectAllButton.onclick = () => { listContainer .querySelectorAll('input[type="checkbox"]') .forEach((checkbox) => { checkbox.checked = allSelected; }); selectAllButton.textContent = allSelected ? "Deselect All" : "Select All"; allSelected = !allSelected; console.log( allSelected ? "Select All clicked: all checkboxes selected." : "Deselect All clicked: all checkboxes deselected.", ); }; buttonContainer.appendChild(selectAllButton); const confirmButton = document.createElement("button"); confirmButton.textContent = "Confirm Selection"; confirmButton.style = `padding: 5px 10px; background-color: #4caf50; border: none; color: white; cursor: pointer; border-radius: 5px;`; confirmButton.onclick = () => { selectedLinks = Array.from( listContainer.querySelectorAll("input:checked"), ).map((input) => input.value); console.log("Confirmed selected links:", selectedLinks); document.body.removeChild(popup); if (selectedLinks.length > 0) { startNavbox(); } else { alert("No pages selected."); } }; const cancelPopupButton = document.createElement("button"); cancelPopupButton.textContent = "Cancel"; cancelPopupButton.style = `padding: 5px 10px; background-color: #d9534f; border: none; color: white; cursor: pointer; border-radius: 5px;`; cancelPopupButton.onclick = () => { console.log("Popup canceled."); document.body.removeChild(popup); }; buttonContainer.appendChild(confirmButton); buttonContainer.appendChild(cancelPopupButton); popup.appendChild(buttonContainer); document.body.appendChild(popup); } function startNavbox() { console.log("Starting Navbox process."); isCancelled = false; isRunning = true; currentIndex = 0; document.getElementById("progress-bar-container").style.display = "block"; fetchCsrfToken(() => processNextPage()); } function processNextPage() { if (isCancelled || currentIndex >= selectedLinks.length) { console.log( "Navbox ended. Reason:", isCancelled ? "Cancelled" : "Completed", ); isRunning = false; if (!isCancelled) { displayCompletionSummary(); // Show summary popup } resetUI(); return; } const pageTitle = selectedLinks[currentIndex]; updateProgressBar(`Processing: ${pageTitle}`); console.log(`Processing page: ${pageTitle}`); addNavboxToPage(pageTitle, () => { currentIndex++; updateProgressBar(`Processed: ${pageTitle}`); setTimeout(processNextPage, requestInterval); }); } function addNavboxToPage(pageTitle, callback) { const navboxTemplate = `{{${navboxName}}}`; // Fetch page content to check for duplicate const apiUrl = `https://oldschool.runescape.wiki/api.php?action=query&prop=revisions&titles=${encodeURIComponent(pageTitle)}&rvprop=content&format=json`; GM_xmlhttpRequest({ method: "GET", url: apiUrl, onload(response) { const responseJson = JSON.parse(response.responseText); const pageId = Object.keys(responseJson.query.pages)[0]; const page = responseJson.query.pages[pageId]; // If page content is undefined, skip processing this page if (!page.revisions || !page.revisions[0]) { console.log(`Page content not found for '${pageTitle}', skipping.`); actionLog.push(`Skipped: '${pageTitle}' - page content not found.`); callback(); return; } const pageContent = page.revisions[0]["*"]; // Check if the navbox already exists in the content if (pageContent.includes(navboxTemplate)) { console.log( `Page '${pageTitle}' already contains the navbox '${navboxName}', skipping.`, ); actionLog.push( `Skipped: '${pageTitle}' already contains '${navboxName}'`, ); callback(); return; } // Find the index of the first category const firstCategoryIndex = pageContent.indexOf("[[Category:"); // Add the navbox above the first category, or append if no categories const newContent = firstCategoryIndex === -1 ? pageContent + "\n" + navboxTemplate // Append if no categories : pageContent.slice(0, firstCategoryIndex) + navboxTemplate + "\n" + pageContent.slice(firstCategoryIndex); const editUrl = "https://oldschool.runescape.wiki/api.php"; const formData = new URLSearchParams(); formData.append("action", "edit"); formData.append("title", pageTitle); formData.append("text", newContent); formData.append("token", csrfToken); formData.append("format", "json"); GM_xmlhttpRequest({ method: "POST", url: editUrl, headers: { "Content-Type": "application/x-www-form-urlencoded", }, data: formData.toString(), onload(editResponse) { if (editResponse.status === 200) { actionLog.push(`Added: '${pageTitle}' to '${navboxName}'`); console.log( `Successfully added '${pageTitle}' to Navbox '${navboxName}'.`, ); callback(); } else { console.log(`Failed to add '${pageTitle}' to Navbox.`); callback(); } }, }); }, }); } function fetchCsrfToken(callback) { const apiUrl = "https://oldschool.runescape.wiki/api.php?action=query&meta=tokens&type=csrf&format=json"; GM_xmlhttpRequest({ method: "GET", url: apiUrl, onload(response) { const responseJson = JSON.parse(response.responseText); csrfToken = responseJson.query.tokens.csrftoken; console.log("CSRF token fetched:", csrfToken); callback(); }, }); } function updateProgressBar(status) { const progressBar = document.getElementById("progress-bar"); const progressText = document.getElementById("progress-text"); const progress = (currentIndex / selectedLinks.length) * 100; progressBar.style.width = `${progress}%`; progressText.textContent = `${Math.round(progress)}% - ${status}`; } function displayCompletionSummary() { console.log("Displaying completion summary."); const summaryPopup = document.createElement("div"); summaryPopup.style = `position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 1001; background-color: #2b2b2b; padding: 15px; color: white; border-radius: 8px; max-height: 80vh; overflow-y: auto; border: 1px solid #595959;`; const title = document.createElement("h3"); title.textContent = "Navbox Summary"; title.style = `margin: 0 0 10px; font-family: Arial, sans-serif;`; summaryPopup.appendChild(title); const logList = document.createElement("ul"); logList.style = "max-height: 300px; overflow-y: auto;"; actionLog.forEach((entry) => { const listItem = document.createElement("li"); listItem.textContent = entry; logList.appendChild(listItem); }); summaryPopup.appendChild(logList); const closeButton = document.createElement("button"); closeButton.textContent = "Close"; closeButton.style = `margin-top: 10px; padding: 5px 10px; background-color: #4caf50; border: none; color: white; cursor: pointer; border-radius: 5px;`; closeButton.onclick = () => { document.body.removeChild(summaryPopup); actionLog = []; }; summaryPopup.appendChild(closeButton); document.body.appendChild(summaryPopup); } function resetUI() { document.getElementById("progress-bar").style.width = "0%"; document.getElementById("progress-text").textContent = ""; document.getElementById("progress-bar-container").style.display = "none"; isRunning = false; } function cancelNavbox() { console.log("Navbox cancelled by user."); isCancelled = true; } addButtonAndProgressBar(); })();