// ==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();
}
})();