☰

xmegadrive infinite scroll

infinte scroll

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴、Greasemonkey 油猴子 或 Violentmonkey 暴力猴,才能安装此脚本。

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

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴 或 Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         xmegadrive infinite scroll
// @namespace    http://tampermonkey.net/
// @version      1.8
// @description  infinte scroll
// @author       You
// @match        https://www.xmegadrive.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=tampermonkey.net
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';

  // ---------- SETTINGS ----------
  const SHOW_STATUS = false; // true = green status label (bottom-left)
  const SHOW_PAGE = true;    // true = cyan "X / Y" label (bottom-right)
  const SHOW_TOP = true;     // true = "Top" button (bottom-right)

  // ---------- BLOCK 0: labels ----------
    const labels = document.createElement('div');
  labels.style.cssText = 'position:fixed;right:8px;bottom:8px;z-index:99999;display:flex;flex-direction:column;gap:4px;align-items:flex-end;';
  document.body.appendChild(labels);
  const mkTag = (color) => {
    const t = document.createElement('div');
    t.style.cssText = 'background:#000;font:15px monospace;padding:4px 8px;opacity:.7;color:' + color;
    return t;
  };
  const tag = mkTag('#0f0');
  const pageTag = mkTag('#0ff');
  if (SHOW_STATUS) labels.appendChild(tag);
  const say = (t) => (tag.textContent = 'IS: ' + t);
  say('script running');

  // ---------- BLOCK 1: wait for the list, then start ----------
  let tries = 0;
  const wait = setInterval(() => {
    tries++;
    const first = document.querySelector('li.next a[data-action="ajax"]');
    if (first && document.getElementById(first.dataset.blockId + '_items')) {
      clearInterval(wait);
      try { init(first); } catch (e) { say('error: ' + e.message); }
    } else if (tries >= 20) {
      clearInterval(wait);
      say(first ? 'no list box for ' + first.dataset.blockId : 'no Next link on this page');
    }
  }, 300);

  function init(first) {
    // ---------- BLOCK 2: list + page numbers ----------
    const blockId = first.dataset.blockId;
    const getBox = () => {
      const c = document.getElementById(blockId + '_items');
      return c ? (c.querySelector('form') || c) : null;
    };
    const pageRe = /(?:^|;)from[^:;]*:(\d+)/;
    const num = (a) => +((pageRe.exec(a?.dataset.parameters || '') || [])[1]);
    let page = num(first);
    const lastPage = num(document.querySelector('li.last a[data-block-id="' + blockId + '"]')) || Infinity;
    const template = first.dataset.parameters;
    const seen = new Set([...getBox().querySelectorAll('.item > a')].map((a) => a.href));
    let loading = false, done = false;

    // mark the items already on the page with their page number
    getBox().querySelectorAll('.item').forEach((el) => (el.dataset.isPage = page - 1));
    if (SHOW_PAGE) labels.appendChild(pageTag);
    if (SHOW_TOP) {
      const topBtn = mkTag('#fff');
      topBtn.textContent = '↑';
      topBtn.style.cssText += ';cursor:pointer;font-size:40px;width:50px;height:50px;padding:0;display:flex;align-items:center;justify-content:center;';
      topBtn.onclick = () => scrollTo({ top: 0, behavior: 'smooth' });
      labels.prepend(topBtn);
    }
    say('ready, next page ' + page + '/' + lastPage);

    // ---------- BLOCK 3: load the next page ----------
    async function loadMore() {
      if (loading || done) return;
      loading = true;
      say('loading page ' + page + '...');
      try {
        const qs = ['mode=async', 'function=get_block', 'block_id=' + blockId];
        for (const pair of template.split(';')) {
          const i = pair.indexOf(':');
          if (i < 1) continue;
          const k = pair.slice(0, i);
          const v = /^from/.test(k) ? page : pair.slice(i + 1);
          k.split('+').forEach((name) => qs.push(name + '=' + encodeURIComponent(v)));
        }
        qs.push('_=' + Date.now());

        const res = await fetch(location.pathname + '?' + qs.join('&'), {
          credentials: 'same-origin',
          headers: { 'X-Requested-With': 'XMLHttpRequest' },
        });
        const doc = new DOMParser().parseFromString(await res.text(), 'text/html');
        const items = doc.querySelectorAll('.item');

        const box = getBox();
        if (!box) { say('list not found'); loading = false; return; }
        const bottom = box.querySelector('.bottom');

        let added = 0;
        items.forEach((el) => {
          const href = el.querySelector('a')?.href;
          if (!href || seen.has(href)) return;
          seen.add(href);
          const img = el.querySelector('img[data-original]');
          if (img) img.src = img.dataset.original;
          const node = document.importNode(el, true);
          node.dataset.isPage = page;
          box.insertBefore(node, bottom);
          added++;
        });

        say('p' + page + ' +' + added + ' | in list: ' + box.querySelectorAll('.item').length);
        page++;
        if (!items.length || !added || page > lastPage) done = true;
      } catch (e) {
        say('error: ' + e.message);
      }
      loading = false;
    }

    // ---------- BLOCK 4: scroll trigger ----------
    const check = () => {
      const el = document.documentElement;
      if (el.scrollHeight - (scrollY + innerHeight) < 1200) loadMore();
    };
    window.addEventListener('scroll', check, { passive: true });
    setInterval(check, 500);

    // ---------- BLOCK 5: "Page X / Y" label ----------
    function showPage() {
      if (!SHOW_PAGE) return;
      const box = getBox();
      if (!box) return;
      const items = box.querySelectorAll('.item');
      let cur = items[items.length - 1];
      for (const el of items) {
        if (el.getBoundingClientRect().bottom > innerHeight / 2) { cur = el; break; }
      }
      pageTag.textContent = 'Page ' + (cur?.dataset.isPage || '?') + ' / ' + (lastPage === Infinity ? '?' : lastPage);
    }
    showPage();
    setInterval(showPage, 300);
  }
})();