您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Gives you the option to load all the subsequent comic pages on a FurAffinity comic page automatically. Even for pages without given Links
当前为
// ==UserScript== // @name Webcomic Autoloader 2.0 // @namespace Violentmonkey Scripts // @match *://*.furaffinity.net/* // @grant none // @version 1.4 // @author Midori Dragon // @description Gives you the option to load all the subsequent comic pages on a FurAffinity comic page automatically. Even for pages without given Links // @icon https://www.furaffinity.net/themes/beta/img/banners/fa_logo.png?v2 // @homepageURL https://gf.qytechs.cn/de/scripts/457759-furaffinity-webcomic-autoloader-2-0 // @supportURL https://gf.qytechs.cn/de/scripts/457759-furaffinity-webcomic-autoloader-2-0/feedback // @license MIT // ==/UserScript== // jshint esversion: 8 //User Options: let showSearchButton = JSON.parse(localStorage.getItem("wasetting_01")); if (showSearchButton == null) showSearchButton = true; let loadingSpinSpeed = +localStorage.getItem("wasetting_02"); if (loadingSpinSpeed == null || loadingSpinSpeed == 0) loadingSpinSpeed = 100; const matchList = ['net/view' ]; const isSettings = window.location.toString().includes('controls/settings'); let exSettings = JSON.parse(localStorage.getItem("wasettings")); if (exSettings == null) exSettings = false; addExSettings(); if (isSettings) { addExSettingsSidebar(); if (exSettings) createSettings(); } if (window.parent !== window) return; if (!matchList.some(x => window.location.toString().includes(x))) return; console.info('%cRunning: Webcomic Autoloader', 'color: blue'); let lightboxPresent = false; let currLighboxNo = -1; let imgCount = 1; let rootHolder = document.getElementById("submissionImg"); rootHolder.setAttribute('imgno', 0); rootHolder.onclick = function() { lightboxPresent = true; currLighboxNo = 0; window.addEventListener('keydown', handleArrowKeys); let lightbox = document.body.querySelector('div[class="lightbox lightbox-submission"]'); lightbox.addEventListener('click', function() { lightboxPresent = false; currLighboxNo = -1; window.removeEventListener('keydown', handleArrowKeys); }); }; let counter = 5; let showLinks = false; let startImg = window.location.href; while (startImg.endsWith("/")) startImg = startImg.substring(0, startImg.length - 1); let pageCounter = 0; let openedLinks = [document.location.toString()]; let rotation; function insertAfter(newElement, referenceElement) { referenceElement.parentNode.insertBefore(newElement, referenceElement.nextSibling); } function insertBreakAfter(referenceElement) { let br = document.createElement("br"); insertAfter(br, referenceElement); } function getNextLink(doc) { let links = doc.querySelectorAll('a[href]:not([class*="button standard mobile-fix"]), :not([class])'); let link; for (const elem of links) { if (elem.href && elem.textContent.toLowerCase().includes("next")) { try { let currImgCalc = elem.href; while (currImgCalc.endsWith("/")) currImgCalc = currImgCalc.substring(0, currImgCalc.length - 1); currImgCalc = currImgCalc.substring(currImgCalc.lastIndexOf('/'), currImgCalc.length); let startImgCalc = startImg.substring(startImg.lastIndexOf('/'), startImg.length); if (currImgCalc != startImgCalc && currImgCalc != "/#" && !openedLinks.includes(elem.href)) { link = elem.href; openedLinks.push(link); pageCounter++; } } catch (ex) { console.error(ex); } } } return link; } function loadNextPage(nextLink) { if(nextLink) { if (nextLink.includes("http:")) nextLink = nextLink.replace(/^http:/, "https:"); let request = new XMLHttpRequest(); request.open('GET', nextLink, true); request.onload = function() { if (this.status >= 200 && this.status < 400) { // Success! parser = new DOMParser(); let nextPage = parser.parseFromString(this.response, "text/html"); if (nextPage && nextPage.getElementById("submissionImg")) { let nl = getNextLink(nextPage); imgCount++; let img = nextPage.getElementById("submissionImg"); img.setAttribute('imgno', imgCount - 1); img.onclick = function() { doLightBox(img); }; rootHolder.parentNode.insertBefore(img, rootHolder.nextSibling); rootHolder = rootHolder.nextSibling; insertBreakAfter(rootHolder); rootHolder = rootHolder.nextSibling; if (showLinks) { let lnk = document.createElement('a'); let lnkURL = nextLink; lnk.innerHTML = lnkURL; lnk.href = lnkURL; insertAfter(lnk, rootHolder); rootHolder = rootHolder.nextSibling; insertBreakAfter(rootHolder); rootHolder = rootHolder.nextSibling; } else { let br = document.createElement('br'); insertAfter(br, rootHolder); rootHolder = rootHolder.nextSibling; } } loadNextPage(nl); } else { //We reached our target server, but it returned an error console.log("none"); } }; request.onerror = function() { //There was a connection error of some sort console.log("error"); }; request.send(); } } function loadPages(links, i) { if (i == links.length) return; let nextLink = links[i]; if(nextLink) { let request = new XMLHttpRequest(); request.open('GET', nextLink, true); request.onload = function() { if (this.status >= 200 && this.status < 400) { // Success! parser = new DOMParser(); let nextPage = parser.parseFromString(this.response, "text/html"); if (nextPage && nextPage.getElementById("submissionImg")) { imgCount++; let img = nextPage.getElementById("submissionImg"); img.setAttribute('imgno', imgCount - 1); img.onclick = function() { doLightBox(img); }; rootHolder.parentNode.insertBefore(img, rootHolder.nextSibling); rootHolder = rootHolder.nextSibling; insertBreakAfter(rootHolder); rootHolder = rootHolder.nextSibling; if (showLinks) { let lnk = document.createElement('a'); let lnkURL = nextLink; lnk.innerHTML = lnkURL; lnk.href = lnkURL; insertAfter(lnk, rootHolder); rootHolder = rootHolder.nextSibling; insertBreakAfter(rootHolder); rootHolder = rootHolder.nextSibling; } else { let br = document.createElement('br'); insertAfter(br, rootHolder); rootHolder = rootHolder.nextSibling; } } i++; loadPages(links, i); } else { //We reached our target server, but it returned an error console.log("none"); } }; request.onerror = function() { //There was a connection error of some sort console.log("error"); }; request.send(); } } function doLightBox(img) { let lightbox = document.createElement('div'); lightbox.className = 'lightbox lightbox-submission'; lightbox.onclick = function() { document.body.removeChild(lightbox); lightboxPresent = false; currLighboxNo = -1; window.removeEventListener('keydown', handleArrowKeys); }; let lightboxImg = img.cloneNode(false); lightboxImg.onclick = function(){}; lightbox.appendChild(lightboxImg); document.body.appendChild(lightbox); lightboxPresent = true; currLighboxNo = +img.getAttribute('imgno'); window.addEventListener('keydown', handleArrowKeys); } function navigateLightboxLeft() { if (currLighboxNo > 0) { currLighboxNo--; let lightbox = document.body.querySelector('div[class="lightbox lightbox-submission"]'); let lightboxImg = lightbox.querySelector('img'); let nextImg = document.querySelector('img[imgno="' + currLighboxNo + '"]'); lightboxImg.src = nextImg.src; } } function navigateLightboxRight() { if (currLighboxNo < imgCount - 1) { currLighboxNo++; let lightbox = document.body.querySelector('div[class="lightbox lightbox-submission"]'); let lightboxImg = lightbox.querySelector('img'); let nextImg = document.querySelector('img[imgno="' + currLighboxNo + '"]'); lightboxImg.src = nextImg.src; } } function handleArrowKeys(event) { if (event.keyCode === 37) { // left arrow navigateLightboxLeft(); } else if (event.keyCode === 39) { // right arrow navigateLightboxRight(); } } function startAutoloader() { let ab = document.getElementById("autoloaderButton"); ab.parentNode.removeChild(ab); let checkbox = document.getElementById("linksCheckbox"); checkbox.parentNode.removeChild(checkbox); let label = document.getElementById("showLinksLabel"); label.parentNode.removeChild(label); insertBreakAfter(rootHolder); rootHolder = rootHolder.nextSibling; loadNextPage(secondPage); } async function searchNextSimularPage() { let ab = document.getElementById("morePagesSearch"); if (ab) rotation = rotateText(ab, true); let checkbox = document.getElementById("linksCheckbox"); if (checkbox) checkbox.parentNode.removeChild(checkbox); let label = document.getElementById("showLinksLabel"); if (label) label.parentNode.removeChild(label); if (checkbox) { insertBreakAfter(rootHolder); rootHolder = rootHolder.nextSibling; } const submissionPage = document.getElementById("submission_page"); const container = submissionPage.querySelector('div[class="submission-id-sub-container"]'); let currTitle = container.querySelector('div[class="submission-title"]').querySelector('p').textContent; currTitle = generalizeString(currTitle, true, true, true, true, true); const author = container.querySelector('a'); let galleryLink = author.href.substring(0, author.href.length - 1); let currentSubmissionId = window.location.toString().substring(0, window.location.toString().length - 1); currentSubmissionId = "sid-" + currentSubmissionId.substring(currentSubmissionId.lastIndexOf("/") + 1); galleryLink = galleryLink.substring(galleryLink.lastIndexOf("/") + 1); galleryLink = "https://www.furaffinity.net/gallery/" + galleryLink; let figures = []; let currentFigureIndex = -1; let j = 0; while (currentFigureIndex == -1) { j++; gallery = await getHTML(galleryLink + "/" + j); let figuresNew = gallery.getElementsByTagName("figure"); if (!figuresNew || figuresNew.length == 0) { rotation.stop(); ab.value = "Nothing found... Search again"; return false; } figuresNew = Array.from(figuresNew); figures = figures.concat(figuresNew); currentFigureIndex = figuresNew.findIndex(figure => figure.id == currentSubmissionId); //console.log("j: " + j + " | index: " + currentFigureIndex); } currentFigureIndex = figures.findIndex(figure => figure.id == currentSubmissionId); //console.log("total index: " + currentFigureIndex); if (currentFigureIndex == 0) { rotation.stop(); ab.value = "Nothing found... Search again"; return false; } figures = figures.slice(0, currentFigureIndex); let pages = []; for (let i = figures.length - 1; i >= 0; i--) { const titleElem = figures[i].querySelector('figcaption').querySelector('a'); const title = generalizeString(titleElem.getAttribute("title"), true, true, true, true, true); if (title.includes(currTitle)) pages.push(titleElem.href); } if (pages.length == 0) { rotation.stop(); ab.value = "Nothing found... Search again"; return false; } else loadPages(pages, 0); ab.parentNode.removeChild(ab); return true; } function generalizeString(inputString, textToNumbers, removeSpecialChars, removeNumbers, removeSpaces, removeRoman) { let outputString = inputString.toLowerCase(); if (removeRoman) { const roman = [ "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX"]; //Checks only up to 20 outputString = outputString.replace(new RegExp(`(?:^|[^a-zA-Z])(${roman.join("|") })(?:[^a-zA-Z]|$)`, "g"), ""); } if (textToNumbers) { const numbers = { zero: 0, one: 1, two: 2, three: 3, four: 4, five: 5, six: 6, seven: 7, eight: 8, nine: 9, ten: 10, eleven: 11, twelve: 12, thirteen: 13, fourteen: 14, fifteen: 15, sixteen: 16, seventeen: 17, eighteen: 18, nineteen: 19, twenty: 20, thirty: 30, forty: 40, fifty: 50, sixty: 60, seventy: 70, eighty: 80, ninety: 90, hundred: 100 }; outputString = outputString.replace(new RegExp(Object.keys(numbers).join("|"), "gi"), match => numbers[match.toLowerCase()]); } if (removeSpecialChars) outputString = outputString.replace(/[^a-zA-Z0-9 ]/g, ""); if (removeNumbers) outputString = outputString.replace(/[^a-zA-Z ]/g, ""); if (removeSpaces) outputString = outputString.replace(/\s/g, ""); return outputString; } function rotateText(element, isRotating) { const characters = [ "◜", "◠", "◝", "◞", "◡", "◟" ]; let index = 0; let intervalId; function updateText() { element.value = characters[index]; index = (index + 1) % characters.length; } function startRotation() { intervalId = setInterval(updateText, loadingSpinSpeed); } function stopRotation() { clearInterval(intervalId); } if (isRotating) { startRotation(); } return { start: startRotation, stop: stopRotation } } function setShowLinks() { if (showLinks) showLinks = false; else showLinks = true; let checkbox = document.getElementById("linksCheckbox"); checkbox.checked = showLinks; } async function getHTML(url) { try { const response = await fetch(url); const html = await response.text(); const parser = new DOMParser(); const doc = parser.parseFromString(html, "text/html"); return doc; } catch (error) { console.error(error); } } let secondPage = getNextLink(document); if(secondPage) { let img = document.getElementById("submissionImg"); insertBreakAfter(rootHolder); rootHolder = rootHolder.nextSibling; pageCounter++; let label = document.createElement('a'); label.id = "showLinksLabel"; label.innerHTML = "Show Links"; label.style.cursor = "pointer"; label.style.marginBottom = "30px"; label.style.marginLeft = "5px"; label.onclick = setShowLinks; insertAfter(label, rootHolder); let checkbox = document.createElement('input'); checkbox.value = "Show Links"; checkbox.type = "checkbox"; checkbox.id = "linksCheckbox"; checkbox.className = "checkbox standard mobile-fix"; checkbox.style.cursor = "pointer"; checkbox.style.marginBottom = "30px"; checkbox.style.marginLeft = "20px"; checkbox.onclick = setShowLinks; insertAfter(checkbox, rootHolder); let button = document.createElement('input'); button.value = "Enable Comic Autoloader"; button.type = "button"; button.id = "autoloaderButton"; button.className = "button standard mobile-fix"; button.style.marginTop = "10px"; button.style.marginBottom = "20px"; button.onclick = startAutoloader; insertAfter(button, rootHolder); } else if (showSearchButton) { let img = document.getElementById("submissionImg"); insertBreakAfter(rootHolder); rootHolder = rootHolder.nextSibling; let label = document.createElement('a'); label.id = "showLinksLabel"; label.innerHTML = "Show Links"; label.style.cursor = "pointer"; label.style.marginBottom = "30px"; label.style.marginLeft = "5px"; label.onclick = setShowLinks; insertAfter(label, rootHolder); let checkbox = document.createElement('input'); checkbox.value = "Show Links"; checkbox.type = "checkbox"; checkbox.id = "linksCheckbox"; checkbox.className = "checkbox standard mobile-fix"; checkbox.style.cursor = "pointer"; checkbox.style.marginBottom = "30px"; checkbox.style.marginLeft = "20px"; checkbox.onclick = setShowLinks; insertAfter(checkbox, rootHolder); let button = document.createElement('input'); button.value = "Search for more Pages in Series"; button.type = "button"; button.id = "morePagesSearch"; button.className = "button standard mobile-fix"; button.style.marginTop = "10px"; button.style.marginBottom = "20px"; button.onclick = searchNextSimularPage; insertAfter(button, rootHolder); } async function addExSettings() { const settings = document.querySelector('ul[class="navhideonmobile"]').querySelector('a[href="/controls/settings/"]').parentNode; if (document.getElementById("extension_settings")) { document.getElementById('midori_settings').addEventListener('click', function() { localStorage.setItem("wasettings", true.toString()); }); return; } let exSettingsHeader = document.createElement("h3"); exSettingsHeader.id = "extension_settings"; exSettingsHeader.textContent = "Extension Settings"; settings.appendChild(exSettingsHeader); let wasettings = document.createElement("a"); wasettings.id = "midori_settings"; wasettings.textContent = "Midori's Script Settings"; wasettings.style.cursor = "pointer"; wasettings.onclick = function() { localStorage.setItem("wasettings", true.toString()); window.location = "https://www.furaffinity.net/controls/settings"; } settings.appendChild(wasettings); } async function addExSettingsSidebar() { const settings = document.getElementById('controlpanelnav'); if (document.getElementById("extension_settings_side")) { document.getElementById('midori_settings_side').addEventListener('click', function() { localStorage.setItem("wasettings", true.toString()); }); return; } let exSettingsHeader = document.createElement("h3"); exSettingsHeader.id = "extension_settings_side"; exSettingsHeader.textContent = "Extension Settings"; settings.appendChild(exSettingsHeader); let wasettings = document.createElement("a"); wasettings.id = "midori_settings_side"; wasettings.textContent = "Midori's Script Settings"; wasettings.style.cursor = "pointer"; wasettings.onclick = function() { localStorage.setItem("wasettings", true.toString()); window.location = "https://www.furaffinity.net/controls/settings"; } settings.appendChild(wasettings); } async function createSettings() { localStorage.setItem("wasettings", false.toString()); const columnPage = document.getElementById("columnpage"); let content = columnPage.querySelector('div[class="content"]'); for (const section of content.querySelectorAll('section:not([class="exsettings"])')) section.parentNode.removeChild(section); let section = document.createElement("section"); section.className = 'exsettings'; let headerContainer = document.createElement("div"); headerContainer.className = "section-header"; let header = document.createElement("h2"); header.textContent = "Webcomic Autoloader Settings"; headerContainer.appendChild(header); section.appendChild(headerContainer); let bodyContainer = document.createElement("div"); bodyContainer.className = "section-body"; let Item1 = document.createElement("div"); Item1.className = "control-panel-item-container"; let Item1Name = document.createElement("div"); Item1Name.className = "control-panel-item-name"; let Item1NameText = document.createElement("h4"); Item1NameText.textContent = "Simular Search Button"; Item1Name.appendChild(Item1NameText); Item1.appendChild(Item1Name); let Item1Desc = document.createElement("div"); Item1Desc.className = "control-panel-item-description"; let Item1DescText = document.createTextNode("Sets wether the search for simular Pages button is shown."); Item1Desc.appendChild(Item1DescText); Item1.appendChild(Item1Desc); let Item1Option = document.createElement("div"); Item1Option.className = "control-panel-item-options"; let Item1OptionContainer = document.createElement("div"); let Item1OptionElem1 = document.createElement("input"); Item1OptionElem1.id = 'wasettings_01'; Item1OptionElem1.type = "checkbox"; Item1OptionElem1.style.cursor = 'pointer'; Item1OptionElem1.style.marginRight = '4px'; Item1OptionElem1.addEventListener('change', function() { showSearchButton = Item1OptionElem1.checked; localStorage.setItem("wasetting_01", showSearchButton.toString()); }); Item1OptionContainer.appendChild(Item1OptionElem1); let Item1OptionElem2 = document.createTextNode('Show Search Button'); Item1OptionContainer.appendChild(Item1OptionElem2); Item1Option.appendChild(Item1OptionContainer); Item1.appendChild(Item1Option); bodyContainer.appendChild(Item1); let Item2 = document.createElement("div"); Item2.className = "control-panel-item-container"; let Item2Name = document.createElement("div"); Item2Name.className = "control-panel-item-name"; let Item2NameText = document.createElement("h4"); Item2NameText.textContent = "Loading Animation"; Item2Name.appendChild(Item2NameText); Item2.appendChild(Item2Name); let Item2Desc = document.createElement("div"); Item2Desc.className = "control-panel-item-description"; let Item2DescText = document.createTextNode("Sets the spinning speed of the loading animation in milliseconds."); Item2Desc.appendChild(Item2DescText); Item2.appendChild(Item2Desc); let Item2Option = document.createElement("div"); Item2Option.className = "control-panel-item-options"; let Item2OptionContainer = document.createElement("div"); let Item2OptionElem1 = document.createElement("input"); Item2OptionElem1.id = 'wasettings_02'; Item2OptionElem1.type = "text"; Item2OptionElem1.className = 'textbox'; Item2OptionElem1.addEventListener('input', function() { this.value = this.value.replace(/[^0-9]/g, ''); loadingSpinSpeed = +this.value; localStorage.setItem("wasetting_02", loadingSpinSpeed.toString()); }); Item2OptionContainer.appendChild(Item2OptionElem1); Item2Option.appendChild(Item2OptionContainer); Item2.appendChild(Item2Option); bodyContainer.appendChild(Item2); section.appendChild(bodyContainer); content.appendChild(section); fillSettings(); } async function fillSettings() { let setting1 = document.getElementById('wasettings_01'); setting1.checked = showSearchButton; let setting2 = document.getElementById('wasettings_02'); setting2.value = loadingSpinSpeed.toString(); }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址