FA Infini-Gallery

Makes so that the gallery continues loading the next page when you reach its bottom

目前為 2025-01-12 提交的版本,檢視 最新版本

// ==UserScript==
// @name        FA Infini-Gallery
// @namespace   Violentmonkey Scripts
// @match       *://*.furaffinity.net/*
// @require     https://update.gf.qytechs.cn/scripts/475041/1267274/Furaffinity-Custom-Settings.js
// @require     https://update.gf.qytechs.cn/scripts/485827/1326313/Furaffinity-Match-List.js
// @require	https://update.gf.qytechs.cn/scripts/483952/1519487/Furaffinity-Request-Helper.js
// @grant       GM_info
// @version     2.0.1
// @author      Midori Dragon
// @description Makes so that the gallery continues loading the next page when you reach its bottom
// @icon        https://www.furaffinity.net/themes/beta/img/banners/fa_logo.png?v2
// @license     MIT
// ==/UserScript==
// jshint esversion: 8
(() => {
    "use strict";
    var __webpack_require__ = {
        d: (exports, definition) => {
            for (var key in definition) __webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key) && Object.defineProperty(exports, key, {
                enumerable: !0,
                get: definition[key]
            });
        },
        o: (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
    };
    function createSeparatorElem(pageNo) {
        const nextPageDescContainer = document.createElement("div");
        nextPageDescContainer.className = "folder-description", nextPageDescContainer.style.marginTop = "6px", 
        nextPageDescContainer.style.marginBottom = "6px";
        const nextPageDesc = document.createElement("div");
        nextPageDesc.className = "container-item-top";
        const nextPageDescText = document.createElement("h3"), pageString = pageSeparatorTextSetting.value.replace(/%page%/g, pageNo);
        return nextPageDescText.textContent = pageString, nextPageDesc.appendChild(nextPageDescText), 
        nextPageDescContainer.appendChild(nextPageDesc), nextPageDescContainer;
    }
    function getFiguresFromPage(page) {
        return page.querySelectorAll('figure[class*="t"]');
    }
    function getUserNameFromUrl(url) {
        return url.includes("?") && (url = url.substring(0, url.indexOf("?"))), (url = trimEnd(url, "/")).substring(url.lastIndexOf("/") + 1);
    }
    function trimEnd(string, toRemove) {
        return string.endsWith(toRemove) && (string = string.slice(0, -1)), string;
    }
    __webpack_require__.d({}, {
        I4: () => pageSeparatorTextSetting,
        uL: () => requestHelper,
        kG: () => showPageSeparatorSetting
    });
    class BrowsePage {
        constructor(pageNo) {
            this.pageNo = pageNo, this.gallery = document.querySelector('section[id*="gallery"]');
        }
        async getPage() {
            return await requestHelper.UserRequests.SearchRequests.Browse.getPage(this.pageNo, this.getBrowseOptions());
        }
        getBrowseOptions() {
            const browseOptions = requestHelper.UserRequests.SearchRequests.Browse.newBrowseOptions, sideBar = document.getElementById("sidebar-options"), optionContainers = sideBar.querySelectorAll('div[class*="browse-search-flex-item"]');
            for (const optionContainer of Array.from(optionContainers)) try {
                const optionName = trimEnd(optionContainer.querySelector("strong").textContent.toLowerCase(), ":"), optionValue = optionContainer.querySelector("option[selected]").getAttribute("value");
                browseOptions[optionName] = optionValue;
            } catch {}
            const checkBoxes = sideBar.querySelectorAll('input[type="checkbox"]');
            for (const checkbox of Array.from(checkBoxes)) switch (checkbox.getAttribute("name")) {
              case "rating_general":
                browseOptions.ratingGeneral = checkbox.hasAttribute("checked");
                break;

              case "rating_mature":
                browseOptions.ratingMature = checkbox.hasAttribute("checked");
                break;

              case "rating_adult":
                browseOptions.ratingAdult = checkbox.hasAttribute("checked");
            }
            return browseOptions;
        }
        async loadPage() {
            const figures = getFiguresFromPage(await this.getPage());
            if (!figures || 0 === figures.length) throw new Error("No figures found");
            if (!0 === showPageSeparatorSetting.value) {
                const separator = createSeparatorElem(this.pageNo);
                this.gallery.appendChild(separator);
            }
            for (const figure of Array.from(figures)) this.gallery.appendChild(figure);
            window.dispatchEvent(new CustomEvent("updateEmbeddedEvent"));
        }
    }
    class FavoritesPage {
        constructor(pageNo) {
            this.pageNo = pageNo, this.gallery = document.querySelector('section[id*="gallery"]');
        }
        async getPage() {
            const username = getUserNameFromUrl(window.location.toString());
            return await requestHelper.UserRequests.GalleryRequests.Favorites.getPage(username, this.pageNo);
        }
        async loadPage() {
            const figures = getFiguresFromPage(await this.getPage());
            if (!figures || 0 === figures.length) throw new Error("No figures found");
            if (!0 === showPageSeparatorSetting.value) {
                const separator = createSeparatorElem(this.pageNo);
                this.gallery.appendChild(separator);
            }
            for (const figure of Array.from(figures)) this.gallery.appendChild(figure);
            window.dispatchEvent(new CustomEvent("updateEmbeddedEvent"));
        }
    }
    class GalleryPage {
        constructor(pageNo) {
            this.pageNo = pageNo, this.gallery = document.querySelector('section[id*="gallery"]'), 
            this.isInFolder = window.location.toString().includes("/folder/");
        }
        async getPage() {
            const username = getUserNameFromUrl(window.location.toString());
            let page;
            if (!0 === this.isInFolder) {
                let folderId;
                page = await requestHelper.UserRequests.GalleryRequests.Gallery.getPageInFolder(username, folderId, this.pageNo);
            } else page = await requestHelper.UserRequests.GalleryRequests.Gallery.getPage(username, this.pageNo);
            return page;
        }
        async loadPage() {
            const figures = getFiguresFromPage(await this.getPage());
            if (!figures || 0 === figures.length) throw new Error("No figures found");
            if (!0 === showPageSeparatorSetting.value) {
                const separator = createSeparatorElem(this.pageNo);
                this.gallery.appendChild(separator);
            }
            for (const figure of Array.from(figures)) this.gallery.appendChild(figure);
            window.dispatchEvent(new CustomEvent("updateEmbeddedEvent"));
        }
    }
    class ScrapsPage {
        constructor(pageNo) {
            this.pageNo = pageNo, this.gallery = document.querySelector('section[id*="gallery"]');
        }
        async getPage() {
            const username = getUserNameFromUrl(window.location.toString());
            return await requestHelper.UserRequests.GalleryRequests.Scraps.getPage(username, this.pageNo);
        }
        async loadPage() {
            const figures = getFiguresFromPage(await this.getPage());
            if (!figures || 0 === figures.length) throw new Error("No figures found");
            if (!0 === showPageSeparatorSetting.value) {
                const separator = createSeparatorElem(this.pageNo);
                this.gallery.appendChild(separator);
            }
            for (const figure of Array.from(figures)) this.gallery.appendChild(figure);
            window.dispatchEvent(new CustomEvent("updateEmbeddedEvent"));
        }
    }
    class SearchPage {
        constructor(pageNo) {
            this.pageNo = pageNo, this.gallery = document.querySelector('section[id*="gallery"]');
        }
        async getPage() {
            return await requestHelper.UserRequests.SearchRequests.Search.getPage(this.pageNo, this.getSearchOptions());
        }
        getSearchOptions() {
            const searchOptions = requestHelper.UserRequests.SearchRequests.Search.newSearchOptions, input = document.getElementById("q");
            searchOptions.input = input.getAttribute("value");
            const searchContainer = document.getElementById("search-advanced"), options = searchContainer.querySelectorAll("option[selected]");
            for (const option of Array.from(options)) {
                const name = option.parentNode instanceof HTMLSelectElement ? option.parentNode.getAttribute("name") : null, value = option.getAttribute("value");
                switch (name) {
                  case "order-by":
                    searchOptions.orderBy = value;
                    break;

                  case "order-direction":
                    searchOptions.orderDirection = value;
                }
            }
            const radioButtons = searchContainer.querySelectorAll('input[type="radio"][checked]');
            for (const radioButton of Array.from(radioButtons)) {
                const name = radioButton.getAttribute("name"), value = radioButton.getAttribute("value");
                switch (name) {
                  case "range":
                    searchOptions.range = value;
                    break;

                  case "mode":
                    searchOptions.matching = value;
                }
                if ("manual" == value) {
                    const rangeFrom = searchContainer.querySelector('input[type="date"][name="range_from"]');
                    searchOptions.rangeFrom = rangeFrom.getAttribute("value");
                    const rangeTo = searchContainer.querySelector('input[type="date"][name="range_to"]');
                    searchOptions.rangeTo = rangeTo.getAttribute("value");
                }
            }
            const checkBoxes = searchContainer.querySelectorAll('input[type="checkbox"]');
            for (const checkBox of Array.from(checkBoxes)) switch (checkBox.getAttribute("name")) {
              case "rating-general":
                searchOptions.ratingGeneral = checkBox.hasAttribute("checked");
                break;

              case "rating-mature":
                searchOptions.ratingMature = checkBox.hasAttribute("checked");
                break;

              case "rating-adult":
                searchOptions.ratingAdult = checkBox.hasAttribute("checked");
                break;

              case "type-art":
                searchOptions.typeArt = checkBox.hasAttribute("checked");
                break;

              case "type-music":
                searchOptions.typeMusic = checkBox.hasAttribute("checked");
                break;

              case "type-flash":
                searchOptions.typeFlash = checkBox.hasAttribute("checked");
                break;

              case "type-story":
                searchOptions.typeStory = checkBox.hasAttribute("checked");
                break;

              case "type-photo":
                searchOptions.typePhotos = checkBox.hasAttribute("checked");
                break;

              case "type-poetry":
                searchOptions.typePoetry = checkBox.hasAttribute("checked");
            }
            return searchOptions;
        }
        async loadPage() {
            const figures = getFiguresFromPage(await this.getPage());
            if (!figures || 0 === figures.length) throw new Error("No figures found");
            if (!0 === showPageSeparatorSetting.value) {
                const separator = createSeparatorElem(this.pageNo);
                this.gallery.appendChild(separator);
            }
            for (const figure of Array.from(figures)) this.gallery.appendChild(figure);
            window.dispatchEvent(new CustomEvent("updateEmbeddedEvent"));
        }
    }
    class GalleryManager {
        constructor() {
            if (this.pageNo = 1, this.isGallery = window.location.toString().includes("net/gallery"), 
            this.isFavorites = window.location.toString().includes("net/favorites"), this.isScraps = window.location.toString().includes("net/scraps"), 
            this.isBrowse = window.location.toString().includes("net/browse"), !0 === this.isBrowse) {
                const pageOption = document.getElementById("manual-page");
                pageOption instanceof HTMLInputElement && (this.pageNo = parseInt(pageOption.value));
            }
            this.isSearch = window.location.toString().includes("net/search");
        }
        async loadNextPage() {
            let nextPage;
            this.pageNo++, !0 === this.isGallery ? nextPage = new GalleryPage(this.pageNo) : !0 === this.isFavorites ? nextPage = new FavoritesPage(this.pageNo) : !0 === this.isScraps ? nextPage = new ScrapsPage(this.pageNo) : !0 === this.isBrowse ? nextPage = new BrowsePage(this.pageNo) : !0 === this.isSearch && (nextPage = new SearchPage(this.pageNo)), 
            await nextPage.loadPage();
        }
    }
    class InfiniGallery {
        constructor() {
            this.scanElem = document.getElementById("footer"), this.galleryManager = new GalleryManager;
        }
        startScrollDetection() {
            this.scanInterval = setInterval((() => {
                (function(element) {
                    const rect = element.getBoundingClientRect(), windowHeight = 2 * (window.innerHeight || document.documentElement.clientHeight);
                    return rect.top <= windowHeight && rect.top + rect.height >= 0;
                })(this.scanElem) && (this.stopScrollDetection(), this.loadNextPage());
            }), 100);
        }
        stopScrollDetection() {
            clearInterval(this.scanInterval);
        }
        async loadNextPage() {
            try {
                await this.galleryManager.loadNextPage(), this.startScrollDetection();
            } catch {
                this.stopScrollDetection();
            }
        }
    }
    CustomSettings.name = "Extension Settings", CustomSettings.provider = "Midori's Script Settings", 
    CustomSettings.headerName = `${GM_info.script.name} Settings`;
    const showPageSeparatorSetting = CustomSettings.newSetting("Page Separator", "Wether a Page Separator is shown foreach new Page loaded.", SettingTypes.Boolean, "Show Page Separators", !0), pageSeparatorTextSetting = CustomSettings.newSetting("Page Separator Text", "The Text that is displayed when a new Infini-Gallery Page is loaded (if shown). Number of Page gets inserted instead of: %page% .", SettingTypes.Text, "", "Infini-Gallery Page: %page%");
    CustomSettings.loadSettings();
    const matchList = new MatchList(CustomSettings);
    matchList.matches = [ "net/gallery", "net/favorites", "net/scraps", "net/browse", "net/search" ], 
    matchList.runInIFrame = !1;
    const requestHelper = new FARequestHelper(2);
    if (matchList.hasMatch()) {
        (new InfiniGallery).startScrollDetection();
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址