您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Changes 'Continue this thread' links to insert the linked comments into the current page
当前为
// ==UserScript== // @name Reddit - Load 'Continue this thread' inline // @description Changes 'Continue this thread' links to insert the linked comments into the current page // @author James Skinner <[email protected]> (http://github.com/spiralx) // @namespace http://spiralx.org/ // @version 1.9.6 // @icon data:image/png;base64,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 // @match *://*.reddit.com/r/*/comments/* // @grant none // @run-at document-end // @require https://unpkg.com/jquery@3/dist/jquery.min.js // @require https://greasyfork.org/scripts/7602-mutation-observer/code/mutation-observer.js // ==/UserScript== /* jshint asi: true, es6: true, laxbreak: true */ /* global jQuery, MutationSummary */ /* ==== 1.9.6 (2020.08.08) ==== * Reduced size of load more links compared to comment text * Fixed script icon * Removed some unnecessary code ==== 1.9.5 (2018.07.11) ==== * Updated jQuery to v3 and source from unpkg.com * Add downloadURL to update from Gist ==== 1.9.4 (2018.02.11) ==== * Added @icon field in metadata as SVG wasn't displaying on the installed userscript page ==== 1.9.3 (2017.12.03) ==== * Changed base-64 encoded PNG icons to an SVG icon ==== 1.9.2 (2017.10.11) ==== * Gets correct comment ID for links * Changed location in comment HTML to use as its root * Get children of first comment when it is already on the page ==== 1.9.1 (2017.10.11) ==== * Fix broken $target selector ==== 1.9.0 ==== * Catch failed loads, log them to the console and then restore original load link */ ; ($ => { 'use strict' const EXPAND_ICON = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADZklEQVR42tVXv08TURyvIdbYjg0DCQuaLjA4Y5rQ61lo0jASHIQFEnUQ/BGDkqJiAj00YkuPa0Fw4S8gYYDgxEYTdHaDgAMDDQUGBunz+7m79t6112srFPEln6Tpfd/3+3nfX+/7HI7/bQmCcDsQCNwnjIqBwEcAv/EfvtXFaCgUahQFYSwgCD/bu/tZa7/EWh4vseahZRUtj5bU//ANMpAN+XyNF2H4BpR1dIZPvINJ5n6/wxzTjDk+F2FaxyfG3OM7zDuQZNijEiEdf2VcFMVb5NYfbX2TzBnNaIZiOuIWiJkJOSczDHuhA7pqjfMdQQztNz1fMwzDyIyOhAXy33gy5JGmZ2vML3btQ2fVJxdog+d12jCeNyprcE3tMc/YpgkuaddMJmZ4wzOSVklU9ERPT4+Tkuh74eRxzvgsQQFy7OHwS/Ztfd2E4RcjrGHmVCOZ4LzBeQK6YaP86SlpEDeTcZkznmTsWirHJElixUuWZXY9ntXkZvV9M+ZwtD2YYLBhnfFUNshcp5Qx3J43ToYdKcIcEZi3IZDIanJJfR8fDj0xO+6FTyxLFMxQaoXTJ4qMzxO+EIEFGwJyVpNLMcMTCXMovAOKtRfQQNQ650+vmI07FonAog0BJavJzXEkZLMX3O921GZV0l7bu/ssTp+jmJ+R2wkLhK/A7/IEkoeaHOSxTzkjPbkSL8CWqSLQx9FK+dO7Puyp2T5FxoqxsrJSQgCVYCULHS5pz0SgtS8KAr08gVH0dp6A502arZPS8y7o8IyljTAQAdwdsGkkIN1ozcPLZgJv60SA8qD5yTKDzatD4J+HwDIJp3bV9ionEiWwIraxsWEpO0Q6XNFd+yRESViVYYN8St3tiBoMQSGkgMOyZXgz/kuTgzz2xY+0+6FSGV5uI9oubUR6K454B5XLasURy8vI3xk+VqefOl5G/mD42FduXtSu44l6X8eRKgaS1YoDCSqBR8WB5Olq5YEEKxgMtmB88rzatB/JqE8UQHVuP5JtqiMZdNcwlHbtFzxxrqF0tbahlO8NFK8t5IQzelDdWB7jx/KDfMy3qj65VU4gaZC5KFH3+Lb9wwR1TjIoNezB3ooxr2ahbKAMDeQudTG00uKnWRv9h2/60yziu4inWdnQUB8vfpyKfn9vzS+gq7D+AAlDQCI1XwNKAAAAAElFTkSuQmCC' // -------------------------------------------------------------------- const units = (v, s) => `${v}${s}` const pluralise = (w, n) => w + (n !== 1 ? 's' : '') const capitalise = s => typeof s === 'string' && s && s.split(/\s+/g).map(w => w[0].toUpperCase() + w.substr(1).toLowerCase()).join(' ') function* flatten (arr) { for (let x of arr) { if (Array.isArray(x)) { yield* (flatten(x)) } else { yield x } } } // -------------------------------------------------------------------- $.fn.extend({ spinner (options) { options = Object.assign({}, $.fn.spinner.defaults, options) const $spinner = $('<div class="pulsar-horizontal"></div>') .css({ padding: units(options.size * 0.25, 'px'), height: units(options.size, 'px') }) const total_duration = (options.steps + 1) * options.step_duration for (let i = 0; i < options.steps; i++) { const delay = i * options.step_duration $('<div></div>') .css({ width: units(options.size, 'px'), height: units(options.size, 'px'), backgroundColor: options.colour, animationDuration: units(total_duration, 's'), animationDelay: units(delay, 's') }) .appendTo($spinner) } if (options.replace) { this.empty() } return options.mode === 'prepend' ? this.prepend($spinner) : this.append($spinner) }, log (name, ...extras) { const title = [ `%c${name || '$'}%c : %c${this.length}%c ${pluralise('item', this.length)}`, 'font-weight: bold', '', 'color: #05f', '' ] if (this.length > 0 || extras.length > 0) { console.group.apply(console, title) if (this.length > 0) { console.info(this) } extras.forEach(extra => { console.log(extra) }) console.groupEnd() } else { console.info.apply(console, title) } return this } }) $.fn.spinner.defaults = { replace: true, mode: 'append', steps: 3, size: 24, colour: '#28f', step_duration: 0.25 } // -------------------------------------------------------------------- async function getCommentPage (id) { const data = await $.get(postUrl + id) return $('.nestedlisting', data) } // -------------------------------------------------------------------- function addComments ($target, $comments) { $target .empty() .append($comments) .find('.usertext.border .usertext-body') .css('animation', 'fadenewpost 4s ease-out 4s both') } // -------------------------------------------------------------------- function loadComments ($span, $target, ids) { let insertChildren = false if (!Array.isArray(ids)) { ids = [ ids ] insertChildren = true } const urls = ids.map(id => postUrl + id) const original = $span.parent().html() $span.spinner() const pageRequests = urls.map(url => { return $.get(url) .then( data => $('.nestedlisting', data).get(), (xhr, textStatus, errorThrown) => { console.warn(`%c${capitalise(textStatus)}: ${xhr.status} ${xhr.statusText}%c ${url}`, 'font-weight: bold; color: #f4f', '') } ) }) $.when(...pageRequests) .then((...children) => { let $children = $([...flatten(children)]) if (insertChildren) { $children = $children.find('> .thing > .child > .sitetable') $children .find('> .entry > .usertext.border') .removeClass('border') } $target .empty() .append($children) .find('.usertext.border .usertext-body') .css('animation', 'fadenewpost 4s ease-out 4s both') }) .fail((xhr, textStatus, errorThrown) => { $span.parent().html(original) }) } // -------------------------------------------------------------------- function getCommentId (linkElem) { const m = linkElem.pathname.match(/\/([a-z0-9]+)\/?$/) if (!m) { throw new Error(`No comment ID parsed from link URL "${linkElem.href}"`) } return m[1] } // -------------------------------------------------------------------- function processDeepThreadSpans (deepThreadSpans) { const $deepThreadSpans = $(deepThreadSpans) .filter(':not([data-comment-ids])') // console.info(`processDeepThreadSpans: processing ${$deepThreadSpans.length}/${deepThreadSpans.length} deep thread spans`) $deepThreadSpans.each(function () { const $span = $(this) const $target = $span.closest('.child') const $a = $span.children('a') const cid = getCommentId($a[ 0 ]) $span .attr('data-comment-ids', cid) .addClass('expand-inline') async function load () { $span.spinner() const $listing = await getCommentPage(cid) const $children = $listing.find('> .thing > .child > .sitetable') addComments($target, $children) } $a.one('click', event => { load() return false }) }) } // -------------------------------------------------------------------- function processMoreCommentsSpans (moreCommentsSpans) { const $moreCommentsSpans = $(moreCommentsSpans) .filter(':not([data-comment-ids])') // console.info(`processMoreCommentsSpans: processing ${$moreCommentsSpans.length}/${moreCommentsSpans.length} more comment spans`) $moreCommentsSpans.each(function() { const $span = $(this) const $target = $span.closest('.child') const $a = $span.children('a') const onclick = $a.attr('onclick') const cids = onclick.split(', ')[3].slice(1, -1).split(',') $span .attr('data-comment-ids', cids.join(',')) .addClass('expand-inline') async function load () { $span.spinner() const $listings = $(await Promise.all(cids.map(getCommentPage))) addComments($target, $listings) } $a .removeAttr('onclick') .attr('data-onclick', onclick) .one('click', event => { load() return false }) }) } function processMoreCommentsSpans2 (moreCommentsSpans) { $(moreCommentsSpans).addClass('expand-inline') } // -------------------------------------------------------------------- const rootUrl = `https://${location.hostname}/` const postUrl = $('.thing.link > .entry a.comments').prop('href') // console.info(`%cSite:%c ${rootUrl}\n%cPost:%c ${postUrl}`, 'font-weight: bold', '', 'font-weight: bold', '') // -------------------------------------------------------------------- const observer = new MutationSummary({ callback([ deepThreadSpans, moreCommentsSpans ]) { // console.log(`Added ${deepThreadSpans.added.length} deep thread spans and ${moreCommentsSpans.added.length} more comment spans`) processDeepThreadSpans(deepThreadSpans.added) processMoreCommentsSpans2(moreCommentsSpans.added) }, rootNode: document.body, queries: [ { element: 'span.deepthread' }, { element: 'span.morecomments' } ] }) // To process spans in the HTML source processDeepThreadSpans($('span.deepthread')) processMoreCommentsSpans2($('span.morecomments')) // -------------------------------------------------------------------- $(document.body).append(`<style type="text/css"> .expand-inline { display: block; padding: 0; } .expand-inline:after { display: none !important; } .expand-inline a { display: block; background: transparent url(${EXPAND_ICON}) no-repeat center left; padding-left: 40px; height: 32px; line-height: 32px; font-size: 1.2rem !important; font-weight: normal !important; vertical-align: middle; text-align: left; } .expand-inline a:hover { background-color: rgba(0, 105, 255, 0.05); text-decoration: none; } .pulsar-horizontal { display: inline-block; } .pulsar-horizontal > div { display: inline-block; border-radius: 100%; animation-name: pulsing; animation-timing-function: ease-in-out; animation-iteration-count: infinite; animation-fill-mode: both; } @keyframes pulsing { 0%, 100% { transform: scale(0); opacity: 0.5; } 50% { transform: scale(1); opacity: 1; } } @keyframes fadenewpost { 0% { background-color: #ffc; padding-left: 5px; } 100% { background-color: transparent; padding-left: 0; } } </style>`) })(jQuery) jQuery.noConflict(true)