Batch Open Thread Pages

One‐click batch opener for all pages in a forum thread

As of 2025-07-06. See the latest version.

// ==UserScript==
// @name         Batch Open Thread Pages
// @namespace    https://github.com/your-username/tampermonkey-scripts
// @version      1
// @description  One‐click batch opener for all pages in a forum thread
// @author       TheSina
// @license MIT 
// @match        *://*.*/threads/*
// @grant        GM.openInTab
// @grant        GM.addStyle
// @grant        GM.getValue
// @grant        GM.setValue
// @grant        GM.registerMenuCommand
// ==/UserScript==
/* jshint esversion: 8 */
(function() {
  'use strict';

  const CONFIRM_KEY = 'openAllSuppressConfirm';
  const MAX_TABS    = 50;

  GM.addStyle(`
    #openAllBtn {
      background: #1976d2;
      color: #fff;
      border: none;
      padding: 8px 12px;
      border-radius: 4px;
      cursor: pointer;
      font-size: 12px;
      margin-left: 10px;
      font-weight: bold;
    }
    #openAllBtn:hover { background: #1565c0; }
  `);

  GM.registerMenuCommand('Toggle Confirm Open-All', async () => {
    const on = await GM.getValue(CONFIRM_KEY, true);
    await GM.setValue(CONFIRM_KEY, !on);
    alert(`Confirmation is now ${!on ? 'ON' : 'OFF'}`);
  });

  function getPageInfo() {
    const links = Array.from(document.querySelectorAll('.pageNav-page a[href*="/page-"]'));
    let total = 1;
    if (links.length) {
      total = links
        .map(a => parseInt((a.href.match(/page-(\d+)/) || [])[1] || '1', 10))
        .reduce((max, v) => Math.max(max, v), 1);
    }
    const currEl = document.querySelector('.pageNav-page--current a') ||
                  document.querySelector('.pageNavSimple-el--current');
    const current = currEl ? parseInt(currEl.textContent.trim(), 10) || 1 : 1;
    return { current, total };
  }

  function generateUrls(total) {
    const base = new URL(location);
    base.hash = '';
    base.pathname = base.pathname.replace(/\/page-\d+/, '');
    return Array.from({ length: total }, (_, i) => {
      const u = new URL(base);
      if (i > 0) u.pathname = `${base.pathname.replace(/\/$/, '')}/page-${i+1}`;
      return u.href;
    });
  }

  async function openAllPages() {
    const { total } = getPageInfo();
    if (total <= 1) return alert('Only one page detected.');
    if (total > MAX_TABS && !confirm(`Open ${total} tabs?`)) return;
    if (await GM.getValue(CONFIRM_KEY, true) && !confirm(`Open all ${total} pages?`)) return;

    // Fire them all off in parallel:
    generateUrls(total)
      .reverse()
      .forEach((url, i) => GM.openInTab(url, { active: i === 0 }));
  }

  function addButton() {
    const nav = document.querySelector('.pageNavWrapper');
    if (!nav || nav.querySelector('#openAllBtn')) return;
    const btn = document.createElement('button');
    btn.id       = 'openAllBtn';
    btn.textContent = '📄 Open All';
    btn.onclick = openAllPages;
    nav.appendChild(btn);
  }

  document.addEventListener('DOMContentLoaded', addButton);
  new MutationObserver(addButton).observe(document.body, { childList: true, subtree: true });
})();