- // ==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 <spiralx@gmail.com> (http://github.com/spiralx)
- // @namespace http://spiralx.org/
- // @version 2.3.3
- // @license MIT
- // @icon data:image/png;base64,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
- // @match *://*.reddit.com/r/*/comments/*
- // @match *://*.reddit.com/user/*/comments/*
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant GM_setValue
- // @grant GM_registerMenuCommand
- // @grant GM_addStyle
- // @grant GM_addValueChangeListener
- // @grant GM.getValue
- // @grant GM.setValue
- // @grant GM.deleteValue
- // @grant GM.registerMenuCommand
- // @grant GM.addStyle
- // @grant GM.addValueChangeListener
- // @run-at document-end
- // @require https://unpkg.com/jquery@3/dist/jquery.min.js
- // @require https://unpkg.com/mutation-summary@1/dist/umd/mutation-summary.js
- // @require https://greasyfork.org/scripts/371339-gm-webextpref/code/GM_webextPref.js?version=961539
- // ==/UserScript==
-
- /* jshint asi: true, esnext: true, laxbreak: true */
- /* global jQuery, MutationSummary, GM_webextPref */
-
- /*
- ==== 2.3.3 (2022.10.13) ====
- * Actually fix bug supposedly fixed by previous version...
-
- ==== 2.3.2 (2022.08.28) ====
- * Fix bug where clicking on "Continue this thread" after hover loading was triggered would open the comment's page
-
- ==== 2.3.1 (2022.06.26) ====
- * Use GM_webextPref library to support Greasemonkey 4 users
-
- ==== 2.3.0 (2022.05.03) ====
- * Fix centred text in expand links
- * Add configuration for expanding links by moving the mouse over the text "Continue this thread" or "Load more comments"
-
- ==== 2.2.1 (2022.05.02) ====
- * Make expand links a block again so they stretch across whole width
-
- ==== 2.2.0 (2022.05.01) ====
- * Use MonkeyConfig library to provide settings for intersection observer behaviour
- * CHanged styling of expandos and replaced icon with emoji ↘️
-
- ==== 2.1.0 (2022.04.17) ====
- * Use IntersectionObserver to automatically open "Load more comments" when they scroll into view
- * Put above behaviour behind USE_INTERSECTION_OBSERVER feature flag
-
- ==== 2.0.0 (2022.04.02) ====
- * Added MIT license
- * Expand non-top level collapsed comments on load
- * Expand collapsed comments inserted from clicking "Load more comments" or "Continue this thread"
- * Script now also runs on posts made to a user's homepage
- * Remove old code handling "Load more comments" links
- * Tidied up old code and updated to use current JS features
-
- ==== 1.9.7 (2021.11.05) ====
- * Use MutationSummary from unpkg.com instead of Greasyfork
-
- ==== 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
-
- */
-
- ; (async ($, MutationSummary) => {
-
- const config = GM_webextPref({
- navbar: false,
- default: {
- autoExpandWhenVisible: false,
- expandOnMouseOver: false,
- expandOnMouseOverDelay: 500,
- },
- body: [
- {
- key: 'autoExpandWhenVisible',
- label: 'Automatically expand any links when they come into view?',
- type: 'checkbox',
- },
- {
- key: 'expandOnMouseOver',
- label: 'Expand links when you move the mouse over them?',
- type: 'checkbox',
- },
- {
- key: 'expandOnMouseOverDelay',
- label: 'Delay between when you move the mouse over a link and it expands (ms)',
- type: 'number',
- },
- ],
- onSave(newSettings) {
- settings = newSettings
- createOrDestroyIntersectionObserver()
- addOrRemoveMouseoverHandler()
- },
- })
-
- await config.ready()
-
- config.on('change', changedSettings => {
- settings = { ...settings, ...changedSettings }
- createOrDestroyIntersectionObserver()
- addOrRemoveMouseoverHandler()
- })
-
- let settings = config.getAll()
-
- // --------------------------------------------------------------------
-
- $.fn.extend({
- spinner(options) {
- options = {
- replace: true,
- mode: 'append',
- steps: 3,
- size: 24,
- colour: '#28f',
- step_duration: 0.25,
- ...options
- }
-
- const $spinner = $('<div class="pulsar-horizontal"></div>')
- .css({
- padding: `${options.size * 0.25}px`,
- height: `${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: `${options.size}px`,
- height: `${options.size}px`,
- backgroundColor: options.colour,
- animationDuration: `${total_duration}s`,
- animationDelay: `${delay}s`
- })
- .appendTo($spinner)
- }
-
- if (options.replace) {
- this.empty()
- }
-
- return options.mode === 'prepend'
- ? this.prepend($spinner)
- : this.append($spinner)
- },
-
- log(name = '$') {
- const title = [ `%c${name}%c : %c${this.length}%c ${this.length > 1 ? 'items' : 'item'}`, 'font-weight: bold', '', 'color: #05f', '' ]
-
- if (this.length > 0) {
- console.group(...title)
- console.info(this)
- console.groupEnd()
- } else {
- console.info(...title)
- }
-
- return this
- }
- })
-
- // --------------------------------------------------------------------
-
- async function loadAndInsertComments(cid, $span, $target) {
- $target.data('loading', 'true')
- $span.spinner()
-
- const data = await $.get(postUrl + cid)
-
- const $comments = $('.nestedlisting > .thing > .child > .sitetable', data)
-
- $target
- .empty()
- .append($comments)
- .find('.usertext.border .usertext-body')
- .css('animation', 'fadenewpost 4s ease-out 4s both')
- }
-
- // --------------------------------------------------------------------
-
- 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 ])
-
- let first = true
-
- $span
- .attr('data-comment-ids', cid)
- .addClass('expand-inline')
-
- $a
- .wrapInner('<span class="expand-text"></span>')
- .on('click', event => {
- const loading = $target.data('loading')
-
- if (first && !loading) {
- first = false
- loadAndInsertComments(cid, $span, $target)
- }
-
- return false
- })
- })
- }
-
- // --------------------------------------------------------------------
-
- function uncollapseComments($collapsedComments) {
- $collapsedComments
- .removeClass('collapsed')
- .addClass('noncollapsed')
- .find('> .entry .tagline .expand')
- .text('[-]')
- }
-
- function uncollapseAllComments($collapsedComments, depth = 3) {
- // console.log($collapsedComments, depth)
-
- if ($collapsedComments.length > 0 && depth > 0) {
- uncollapseComments($collapsedComments)
-
- requestAnimationFrame(() => {
- uncollapseAllComments($collapsedComments.find('.thing.comment.collapsed'), depth - 1)
- })
- }
- }
-
- // --------------------------------------------------------------------
-
- 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', '')
-
- // --------------------------------------------------------------------
-
- let intersectionObserver = null
-
- function createOrDestroyIntersectionObserver() {
- if (settings.autoExpandWhenVisible && !intersectionObserver) {
- intersectionObserver = new IntersectionObserver(
- (entries, observer) => {
- for (const entry of entries) {
- if (entry.isIntersecting) {
- entry.target.click()
- observer.unobserve(entry.target)
- }
- }
- },
- {
- threshold: 0.5
- }
- )
-
- $('span.morecomments, span.deepthread').each(function() {
- intersectionObserver.observe(this.firstElementChild)
- })
-
- console.log('IntersectionObserver created')
- } else if (!settings.autoExpandWhenVisible && intersectionObserver) {
- intersectionObserver.disconnect()
- intersectionObserver = null
- console.log('IntersectionObserver destroyed')
- }
- }
-
- createOrDestroyIntersectionObserver()
-
- // --------------------------------------------------------------------
-
- function addOrRemoveMouseoverHandler() {
- $('.commentarea').off('mouseenter.spiralx')
-
- if (settings.expandOnMouseOver) {
- const hoveredElems = new WeakMap()
-
- $('.commentarea')
- .on('mouseenter.spiralx', '.expand-text', function() {
- const elem = this
-
- const timeoutId = setTimeout(() => {
- hoveredElems.delete(elem)
- elem.click()
- }, settings.expandOnMouseOverDelay)
-
- hoveredElems.set(elem, timeoutId)
- })
- .on('mouseleave.spiralx', '.expand-text', function() {
- const timeoutId = hoveredElems.get(this)
-
- if (timeoutId) {
- clearTimeout(timeoutId)
- hoveredElems.delete(this)
- }
- })
- }
- }
-
- addOrRemoveMouseoverHandler()
-
- // --------------------------------------------------------------------
-
- function markAsExpand(selectorOrElements, observe = true) {
- const $elems = $(selectorOrElements)
- .addClass('expand-inline')
- .children('a')
- .wrapInner('<span class="expand-text"></span>')
-
- if (intersectionObserver) {
- $elems.each(function() {
- intersectionObserver.observe(this.firstElementChild)
- })
- }
- }
-
- // --------------------------------------------------------------------
-
- // Uncollapse non-top level comments on page load
- uncollapseAllComments($('.thing.comment .thing.comment.collapsed'))
-
- const observer = new MutationSummary({
- callback([ deepThreadSpans, moreCommentsSpans, comments ]) {
- // console.log(`Added ${deepThreadSpans.added.length} deep thread spans and ${moreCommentsSpans.added.length} more comment spans`)
-
- markAsExpand(moreCommentsSpans.added)
-
- processDeepThreadSpans(deepThreadSpans.added)
-
- const $collapsedComments = $(comments.added).filter('.collapsed')
- uncollapseAllComments($collapsedComments)
- },
- rootNode: document.body,
- queries: [
- { element: 'span.deepthread' },
- { element: 'span.morecomments' },
- { element: '.thing.comment' },
- ]
- })
-
- // To process spans in the HTML source
- markAsExpand('span.morecomments', false)
-
- processDeepThreadSpans($('span.deepthread'))
-
- // --------------------------------------------------------------------
-
- $(document.body).append(`<style type="text/css">
- .expand-inline {
- display: block;
- padding: 0;
- }
- .expand-inline:after {
- display: none !important;
- }
- .expand-inline a {
- display: block;
- text-align: left;
- }
- .expand-inline a:before {
- content: "↘️";
- padding-right: 0.4em;
- }
- .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, MutationSummary?.MutationSummary)
-
- jQuery.noConflict(true)