您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds subreddit icons to Reddit tabs and prefixes the tab title with r/SubredditName for easier tab identification and navigation.
// ==UserScript== // @name Reddit Tab Icons & Title Prefix // @namespace https://github.com/sinazadeh/userscripts // @version 1.3.6 // @description Adds subreddit icons to Reddit tabs and prefixes the tab title with r/SubredditName for easier tab identification and navigation. // @author TheSina // @match https://*.reddit.com/* // @exclude https://*.reddit.com/account/* // @grant none // @license MIT // ==/UserScript== /* jshint esversion: 8 */ (function () { 'use strict'; // --- Configuration --- const DEBUG = false; const SPECIAL_SUBS = new Set(['all', 'friends', 'popular']); const SUBRE_PATH = /^\/r\/([^\/]+)/; const CACHE_TTL = 24 * 3600 * 1000; // 24h const DEBOUNCE_ICON = 50; // ms const DEBOUNCE_TITLE = 50; // ms const POLL_INTERVAL = 2000; // ms // --- Logging --- const LOG = (...args) => { if (DEBUG) console.log('[RTU]', ...args); }; // --- State --- let iconEls = [], originalIcon = null, iconCache = new Map(), lastHref = location.href; // --- Init --- function init() { // grab/create favicon element iconEls = Array.from(document.querySelectorAll('link[rel*="icon"]')); if (!iconEls.length) { const link = document.createElement('link'); link.rel = 'shortcut icon'; document.head.appendChild(link); iconEls = [link]; } originalIcon = iconEls[0].href; // Hook SPA nav & polling hookHistory(); window.addEventListener('popstate', onNav); setInterval(() => { if (location.href !== lastHref) { lastHref = location.href; onNav(); } }, POLL_INTERVAL); // **Watch the <title> node** for any changes const titleNode = document.head.querySelector('title'); if (titleNode) { new MutationObserver(debounce(updateTitle, DEBOUNCE_TITLE)).observe( titleNode, {childList: true, characterData: true, subtree: true}, ); } onNav(); } // --- Helpers --- function getSub() { const m = SUBRE_PATH.exec(location.pathname); return m ? m[1] : null; } function setFavicon(url) { iconEls.forEach(el => (el.href = url)); } function resetFavicon() { setFavicon(originalIcon); } // Fast + background favicon const updateFavicon = debounce(async () => { const sub = getSub(); if (!sub || SPECIAL_SUBS.has(sub)) { resetFavicon(); return; } // 1) quick switch via /favicon.ico const quick = `https://www.reddit.com/r/${sub}/favicon.ico`; setFavicon(quick); LOG('Quick icon:', quick); // 2) cached? const now = Date.now(), cached = iconCache.get(sub); if (cached && now - cached.ts < CACHE_TTL) { setFavicon(cached.url); return; } // 3) fetch “proper” community_icon try { const res = await fetch( `https://www.reddit.com/r/${sub}/about.json?raw_json=1`, ); const js = await res.json(); const d = js.data || {}; const url = decodeHTML(d.community_icon) || d.icon_img || d.header_img || quick; iconCache.set(sub, {url, ts: now}); setFavicon(url); LOG('Fetched icon:', url); } catch (e) { LOG('Fetch failed, keeping quick icon'); } }, DEBOUNCE_ICON); function decodeHTML(str) { const ta = decodeHTML._ta || (decodeHTML._ta = document.createElement('textarea')); ta.innerHTML = str || ''; return ta.value; } // Update the document.title with no gaps function updateTitle() { const sub = getSub(); if (!sub) return; const prefix = `r/${sub} - `; const cur = document.title; // strip old prefix if present const raw = cur.startsWith(prefix) ? cur.slice(prefix.length) : cur; const next = prefix + raw; if (cur !== next) { document.title = next; LOG('Title set to', next); } } function onNav() { updateFavicon(); updateTitle(); } function hookHistory() { const push = history.pushState, rep = history.replaceState; history.pushState = function () { push.apply(this, arguments); onNav(); }; history.replaceState = function () { rep.apply(this, arguments); onNav(); }; } // --- debounce util --- function debounce(fn, ms) { let t; return function (...a) { clearTimeout(t); t = setTimeout(() => fn.apply(this, a), ms); }; } // --- Start --- if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址