Infini-Gallery

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

当前为 2023-09-11 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        Infini-Gallery
// @namespace   Violentmonkey Scripts
// @match       *://*.furaffinity.net/*
// @require 	https://greasyfork.org/scripts/475041-furaffinity-custom-settings/code/Furaffinity-Custom-Settings.js
// @grant       none
// @version     1.8.0
// @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
// @homepageURL https://greasyfork.org/de/scripts/462632-infini-gallery
// @supportURL  https://greasyfork.org/de/scripts/462632-infini-gallery/feedback
// @license     MIT
// ==/UserScript==

// jshint esversion: 8

const matchList = ['net/browse', 'net/gallery', 'net/search', 'net/favorites', 'net/scraps', 'net/msg/pms' ];

CustomSettings.Name = "Extension Settings";
CustomSettings.Provider = "Midori's Script Settings";
CustomSettings.HeaderName = `${GM_info.script.name} Settings`;
const showPageSeperatorSetting = new Setting("Page Seperator", "Sets wether a Page Seperator is shown foreach new Page loaded.", SettingTypes.Boolean, "Show Page Seperators", true);
const showDisableButtonSetting = new Setting("Disable Button", "Sets wether the disable Infini Gallery button is shown in each Gallery.", SettingTypes.Boolean, "Show disable Infini Gallery button", true);
CustomSettings.loadSettings();

const isClassicTheme = document.head.querySelector('script[type="text/javascript"][src*="themes/classic"]') != null;

const isSettings = window.location.toString().includes('controls/settings');
let exSettings = JSON.parse(localStorage.getItem("igsettings"));
if (exSettings == null)
  exSettings = false;
addExSettings();
if (isSettings) {
  addExSettingsSidebar();
  if (exSettings)
    createSettings();
}

if (window.parent !== window) return;

let color = "color: blue";
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)
    color = "color: aqua";

if (window.location.toString().includes("?extension")) {
	console.info(`%cSettings: ${GM_info.script.name} v${GM_info.script.version}`, color);
	return;
}

if (!matchList.some(x => window.location.toString().includes(x)))
  return;

console.info(`%cRunning: ${GM_info.script.name} v${GM_info.script.version} ${CustomSettings.toString()}`, color);

const isGallery = window.location.toString().includes('net/gallery');
const isFavorites = window.location.toString().includes('net/favorites');
const isBrowse = window.location.toString().includes('net/browse');
const isScraps = window.location.toString().includes('net/scraps');
const isNotes = window.location.toString().includes('net/msg/pms');

let allowScan = true;
let nextButtons;
let lastNextButton;
let gallery;
let lastLink;
let lastNextPageButton;
let pageCount;

if (!isSettings) {
  if (isClassicTheme) {
    if (isGallery)
      nextButtons = document.querySelectorAll('a[class*="button-link"][href]');
    else if (isFavorites)
      nextButtons = document.querySelectorAll('a[class*="button-link"][href]');
    else if (isBrowse)
      nextButtons = document.querySelectorAll('button[class*="button"][type="submit"]');
    else if (isScraps)
      nextButtons = document.querySelectorAll('a[class*="button-link"][href]');
    else if (isNotes)
      nextButtons = document.querySelectorAll('a[class*="button-link"][href]');
  } else {
    if (isGallery)
      nextButtons = document.querySelectorAll('button[class*="button standard"][type="submit"]');
    else if (isFavorites)
      nextButtons = document.querySelectorAll('a[class*="button mobile-button right"][href]');
    else if (isBrowse)
      nextButtons = document.querySelectorAll('a[class*="button standard"][href]');
    else if (isScraps)
      nextButtons = document.querySelectorAll('form[action][method="get"]');
  }
  if (!nextButtons || nextButtons.length == 0)
    return;

  if (showDisableButtonSetting.value) {
    let navPage;
    if (isClassicTheme)
      navPage = document.querySelector('input[type="button"][class="button toggle_titles"]').parentNode;
    else
      navPage = document.querySelector('userpage-nav-links').querySelector('ul');
    let disableIGButton = document.createElement('button');
    disableIGButton.id = "disableIGButton";
    disableIGButton.type = "button";
    disableIGButton.className = "button standard mobile-fix";
    disableIGButton.textContent = "Disable Infini Gallery";
    if (isClassicTheme)
      disableIGButton.style.marginLeft = "8px";
    else
      disableIGButton.style.marginTop = "8px";
    disableIGButton.style.marginRight = "18px";
    disableIGButton.onclick = function() {
      allowScan = !allowScan;
      if (allowScan) {
        disableIGButton.textContent = "Disable Infini Gallery";
        scan();
      } else
        disableIGButton.textContent = "Enable Infini Gallery";
    };
    navPage.appendChild(disableIGButton);
  }

  lastNextButton = nextButtons[nextButtons.length - 1];
  gallery = document.querySelector('section[id*="gallery"]');
  lastLink = window.location.toString();
  lastNextPageButton = lastNextButton;
  pageCount = 1;
  scan();
}

async function scan() {
  const interval = setInterval(() => {
    if (!allowScan)
      clearInterval(interval);
    if (isElementOnScreen(lastNextButton)) {
      clearInterval(interval);
      loadNextPage();
    }
  }, 100);
}

async function loadNextPage() {
  let figures;
  if (isClassicTheme) {
    if (isGallery)
      figures = await getNextPageFiguresGallery();
    else if (isFavorites)
      figures = await getNextPageFiguresGallery();
    else if (isBrowse)
      figures = await getNextPageFiguresGallery();
    else if (isScraps)
      figures = await getNextPageFiguresGallery();
    else if (isNotes)
      figures = await getNextPageFiguresGallery();
  } else {
    if (isGallery)
      figures = await getNextPageFiguresGallery();
    else if (isFavorites)
      figures = await getNextPageFiguresFavorites();
    else if (isBrowse)
      figures = await getNextPageFiguresGallery();
    else if (isScraps)
      figures = await getNextPageFiguresGallery();
  }
  if (!figures || figures.length == 0) {
    lastNextButton.parentNode.removeChild(lastNextButton);
    return;
  }
  pageCount++;
  if (!isNotes) {
		if (showPageSeperatorSetting.value) {
			let nextPageDescContainer = document.createElement('div');
			nextPageDescContainer.className = 'folder-description';
			nextPageDescContainer.style.marginTop = '6px';
			nextPageDescContainer.style.marginBottom = '6px';
			let nextPageDesc = document.createElement('div');
			nextPageDesc.className = 'container-item-top';
			let nextPageDescText = document.createElement('h3');
			nextPageDescText.textContent = `${GM_info.script.name} Page: ${pageCount}`;
			if (isClassicTheme) {
				nextPageDescText.style.color = "#c4e9ff";
				nextPageDescText.style.fontSize = "18px";
				nextPageDescText.style.lineHeight = "22px";
				nextPageDescText.style.fontWeight = "600";
				nextPageDescText.style.paddingTop = "3px";
				nextPageDescText.style.paddingBottom = "2px";
				nextPageDescText.style.paddingRight = "0";
				nextPageDescText.style.paddingLeft = "0";
				nextPageDescText.style.margin = "0";
			}
			nextPageDesc.appendChild(nextPageDescText);
			nextPageDescContainer.appendChild(nextPageDesc);
			gallery.appendChild(nextPageDescContainer);
		}
    for (const figure of figures)
      gallery.appendChild(figure);
    try { window.updateEmbedded(); } catch {} //Embedded Image Viewer Integration
    try { window.updateFastFavoriter(); } catch {} //Fast Favoriter 2 Integration
  } else {
    let table = document.getElementById("notes-list");
    let tbody = table.querySelector(tbody);
    for (const figure of figures)
      tbody.appendChild(figure);
  }

  await scan();
}

async function getNextPageFiguresGallery() {
  const nextLink = await incrementUrlLastNumber(lastLink);
  console.log(nextLink);
  lastLink = nextLink;
  const nextPage = await getHTML(nextLink);
  const figures = nextPage.querySelectorAll('figure[class*="t"]');
  return figures;
}
async function getNextPageFiguresFavorites() {
  const nextLink = lastNextPageButton.href;
  console.log(nextLink);
  lastLink = nextLink;
  const nextPage = await getHTML(nextLink);
  let currNextPageButton = nextPage.querySelectorAll('a[class="button mobile-button right"][href]');
  lastNextPageButton = currNextPageButton[currNextPageButton.length - 1];
  const figures = nextPage.querySelectorAll('figure[class*="t"]');
  return figures;
}
async function getNextPageFiguresNotes() {
  const nextLink = await incrementUrlLastNumber(lastLink);
  console.log(nextLink);
  lastLink = nextLink;
  const nextPage = await getHTML(nextLink);
  const notes = nextPage.querySelectorAll('tr[class*="note"]');
  return notes;
}

async function incrementUrlLastNumber(url) {
  if (url.endsWith('/?'))
    url = url.slice(0, -1);
  if (url.endsWith('/'))
    url = url.slice(0, -1);

  var segments = url.split('/');
  var lastSegment = segments[segments.length - 1];

  var match = lastSegment.match(/^\d+/);

  if (match) {
    var nextNumber = parseInt(match[0]) + 1;
    return url.replace(/\d+$/, nextNumber);
  } else
    return url + '/2';
}


function isElementOnScreen(element) {
  var rect = element.getBoundingClientRect();
  var windowHeight = (window.innerHeight || document.documentElement.clientHeight) * 2;
  return (rect.top <= windowHeight) && ((rect.top + rect.height) >= 0);
}

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);
  }
}