Reddit expand media and comments

Shows pictures and some videos right after the link, loads and expands comment threads.

目前為 2018-08-05 提交的版本,檢視 最新版本

// ==UserScript==
// @name           Reddit expand media and comments
// @description    Shows pictures and some videos right after the link, loads and expands comment threads.
// @version        0.0.3
// @author         wOxxOm
// @namespace      wOxxOm.scripts
// @license        MIT License
// @match          *://*.reddit.com/*
// @grant          GM_addStyle
// @grant          GM_xmlhttpRequest
// @connect        imgur.com
// @connect        gfycat.com
// @connect        streamable.com
// ==/UserScript==

const CLASS = 'reddit-inline-media';
const MORE_SELECTOR = '[id^="moreComments-"] p';
const RULES = [
  {r:/^https?:\/\/imgur\.com\/a\/.+/i, q:'link[rel="image_src"]'},
  {r:/^https?:\/\/streamable\.com\/.+/i, q:'video'},
  {r:/^https?:\/\/gfycat\.com\/.+/i, q:'#webmSource'},
  {r:/\.gifv$/i, s:'.mp4'},
  {r:/\.(jpe?g|png|gif|webm|mp4)$/i},
];

GM_addStyle(`
  .${CLASS} {
    width: 100%;
    display: block;
  }
  .${CLASS}:hover {
    outline: 2px solid #3bbb62;
  }
`);

const isChrome = navigator.userAgent.includes('Chrom');

new MutationObserver(onMutation)
  .observe(document.body, {subtree: true, childList: true});

onMutation([{
  addedNodes: [document.body]
}]);

const scrollObserver = new IntersectionObserver(expandComments, {
  rootMargin: window.innerHeight + 'px',
});

function onMutation(mutations) {
  const items = [];
  let someElementsAdded = false;
  for (var i = 0, m; (m = mutations[i++]);) {
    for (var j = 0, added = m.addedNodes, node; (node = added[j++]);) {
      if (node.nodeType !== 1) continue; // Node.ELEMENT_NODE
      someElementsAdded = true;
      if (node.localName === 'a') {
        const data = preprocess(node);
        if (data) items.push(data);
        continue;
      }
      if (!node.children[0]) continue;
      var aa = node.getElementsByTagName('a');
      for (var k = 0, a; (a = aa[k++]);) {
        const data = preprocess(a);
        if (data) items.push(data);
      }
    }
  }
  if (someElementsAdded) debounce(observeShowMore);
  if (items.length) setTimeout(process, 0, items);
}

function preprocess(a) {
  let url = a.href;
  for (const {r, s, q} of RULES) {
    if (typeof r === 'string') {
      if (!url.includes(r)) continue;
    } else {
      if (!r.test(url)) continue;
      if (s) url = url.replace(r, s);
    }
    return {a, url, q};
  }
}

function process(items) {
  for (const item of items) {
    const {a, url, q} = item;
    if (!/^https?:\/\/\S+?\.{3}$/.test(a.textContent) &&
        !a.closest('[data-test-id="post-content"], .scrollerItem') &&
        !a.closest(`img[src="${url}"] + * a[href="${url}"]`)) {
      q ? expandRemote(item) : expand(item);
    }
  }
}

function expandRemote({a, url, q}) {
  GM_xmlhttpRequest({
    url,
    method: 'GET',
    onload: r => {
      const doc = new DOMParser().parseFromString(r.response, 'text/html');
      const el = doc && doc.querySelector(q);
      if (el) expand({a, url: el.href || el.src});
    },
  });
}

function expand({a, url = a.href}) {
  const isVideo = /(webm|gifv|mp4)(\?.*)?$/i.test(url);
  const el = document.createElement(isVideo ? 'video' : 'img');
  el.src = url;
  el.className = CLASS;
  a.insertAdjacentElement('afterend', el);
  if (isVideo) {
    el.controls = true;
    el.preload = 'metadata';
    if (isChrome) el.addEventListener('click', playOnClick);
  }
  return el;
}

function observeShowMore() {
  const more = document.querySelector(MORE_SELECTOR);
  if (!more) return;
  for (const el of document.querySelectorAll(MORE_SELECTOR)) {
    scrollObserver.observe(el);
  }
}

function expandComments(entries) {
  for (const e of entries) {
    if (!e.isIntersecting) continue;
    e.target.dispatchEvent(new MouseEvent('click', {bubbles: true}));
  }
}

function playOnClick(event, el, wasPaused) {
  if (!el) {
    setTimeout(playOnClick, 0, event, this, this.paused);
  } else if (el.paused === wasPaused) {
    wasPaused ? el.play() : el.pause();
  }
}

function debounce(fn, timeout = 0, ...args) {
  clearTimeout(fn.__timeout);
  fn.__timeout = setTimeout(fn, timeout, ...args);
}

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址