Reddit - Load 'Continue this thread' inline

Changes 'Continue this thread' links to insert the linked comments into the current page

当前为 2022-04-01 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Reddit - Load 'Continue this thread' inline
  3. // @description Changes 'Continue this thread' links to insert the linked comments into the current page
  4. // @author James Skinner <spiralx@gmail.com> (http://github.com/spiralx)
  5. // @namespace http://spiralx.org/
  6. // @version 2.0.0
  7. // @license MIT
  8. // @icon data:image/png;base64,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
  9. // @match *://*.reddit.com/r/*/comments/*
  10. // @match *://*.reddit.com/user/*/comments/*
  11. // @grant none
  12. // @run-at document-end
  13. // @require https://unpkg.com/jquery@3/dist/jquery.min.js
  14. // @require https://unpkg.com/mutation-summary@1/dist/umd/mutation-summary.js
  15. // ==/UserScript==
  16.  
  17. /* jshint asi: true, esnext: true, laxbreak: true */
  18. /* global jQuery, MutationSummary */
  19.  
  20. /*
  21.  
  22. ==== 2.0.0 (2022.04.02) ====
  23. * Added MIT license
  24. * Expand non-top level collapsed comments on load
  25. * Expand collapsed comments inserted from clicking "Load more comments" or "Continue this thread"
  26. * Script now also runs on posts made to a user's homepage
  27. * Remove old code handling "Load more comments" links
  28. * Tidied up old code and updated to use current JS features
  29.  
  30. ==== 1.9.7 (2021.11.05) ====
  31. * Use MutationSummary from unpkg.com instead of Greasyfork
  32.  
  33. ==== 1.9.6 (2020.08.08) ====
  34. * Reduced size of load more links compared to comment text
  35. * Fixed script icon
  36. * Removed some unnecessary code
  37.  
  38. ==== 1.9.5 (2018.07.11) ====
  39. * Updated jQuery to v3 and source from unpkg.com
  40. * Add downloadURL to update from Gist
  41.  
  42. ==== 1.9.4 (2018.02.11) ====
  43. * Added @icon field in metadata as SVG wasn't displaying on the installed userscript page
  44.  
  45. ==== 1.9.3 (2017.12.03) ====
  46. * Changed base-64 encoded PNG icons to an SVG icon
  47.  
  48. ==== 1.9.2 (2017.10.11) ====
  49. * Gets correct comment ID for links
  50. * Changed location in comment HTML to use as its root
  51. * Get children of first comment when it is already on the page
  52.  
  53. ==== 1.9.1 (2017.10.11) ====
  54. * Fix broken $target selector
  55.  
  56. ==== 1.9.0 ====
  57. * Catch failed loads, log them to the console and then restore original load link
  58.  
  59. */
  60.  
  61. ; (($, MutationSummary) => {
  62.  
  63. $.fn.extend({
  64. spinner(options) {
  65. options = {
  66. replace: true,
  67. mode: 'append',
  68. steps: 3,
  69. size: 24,
  70. colour: '#28f',
  71. step_duration: 0.25,
  72. ...options
  73. }
  74.  
  75. const $spinner = $('<div class="pulsar-horizontal"></div>')
  76. .css({
  77. padding: `${options.size * 0.25}px`,
  78. height: `${options.size}px`
  79. })
  80.  
  81. const total_duration = (options.steps + 1) * options.step_duration
  82.  
  83. for (let i = 0; i < options.steps; i++) {
  84. const delay = i * options.step_duration
  85.  
  86. $('<div></div>')
  87. .css({
  88. width: `${options.size}px`,
  89. height: `${options.size}px`,
  90. backgroundColor: options.colour,
  91. animationDuration: `${total_duration}s`,
  92. animationDelay: `${delay}s`
  93. })
  94. .appendTo($spinner)
  95. }
  96.  
  97. if (options.replace) {
  98. this.empty()
  99. }
  100.  
  101. return options.mode === 'prepend'
  102. ? this.prepend($spinner)
  103. : this.append($spinner)
  104. },
  105.  
  106. log(name = '$') {
  107. const title = [ `%c${name}%c : %c${this.length}%c ${this.length > 1 ? 'items' : 'item'}`, 'font-weight: bold', '', 'color: #05f', '' ]
  108.  
  109. if (this.length > 0) {
  110. console.group(...title)
  111. console.info(this)
  112. console.groupEnd()
  113. } else {
  114. console.info(...title)
  115. }
  116.  
  117. return this
  118. }
  119. })
  120.  
  121. // --------------------------------------------------------------------
  122.  
  123. async function loadAndInsertComments(cid, $target) {
  124. const data = await $.get(postUrl + cid)
  125.  
  126. const $comments = $('.nestedlisting > .thing > .child > .sitetable', data)
  127.  
  128. $target
  129. .empty()
  130. .append($comments)
  131. .find('.usertext.border .usertext-body')
  132. .css('animation', 'fadenewpost 4s ease-out 4s both')
  133. }
  134.  
  135. // --------------------------------------------------------------------
  136.  
  137. function getCommentId(linkElem) {
  138. const m = linkElem.pathname.match(/\/([a-z0-9]+)\/?$/)
  139. if (!m) {
  140. throw new Error(`No comment ID parsed from link URL "${linkElem.href}"`)
  141. }
  142. return m[1]
  143. }
  144.  
  145. // --------------------------------------------------------------------
  146.  
  147. function processDeepThreadSpans(deepThreadSpans) {
  148. const $deepThreadSpans = $(deepThreadSpans)
  149. .filter(':not([data-comment-ids])')
  150.  
  151. // console.info(`processDeepThreadSpans: processing ${$deepThreadSpans.length}/${deepThreadSpans.length} deep thread spans`)
  152.  
  153. $deepThreadSpans.each(function () {
  154. const $span = $(this)
  155. const $target = $span.closest('.child')
  156.  
  157. const $a = $span.children('a')
  158. const cid = getCommentId($a[ 0 ])
  159.  
  160. $span
  161. .attr('data-comment-ids', cid)
  162. .addClass('expand-inline')
  163.  
  164. $a.one('click', event => {
  165. $span.spinner()
  166.  
  167. loadAndInsertComments(cid, $target)
  168.  
  169. return false
  170. })
  171. })
  172. }
  173.  
  174. // --------------------------------------------------------------------
  175.  
  176. function uncollapseComments($collapsedComments) {
  177. $collapsedComments
  178. .removeClass('collapsed')
  179. .addClass('noncollapsed')
  180. .find('> .entry .tagline .expand')
  181. .text('[-]')
  182. }
  183.  
  184. function uncollapseAllComments($collapsedComments, depth = 3) {
  185. // console.log($collapsedComments, depth)
  186.  
  187. if ($collapsedComments.length > 0 && depth > 0) {
  188. uncollapseComments($collapsedComments)
  189.  
  190. requestAnimationFrame(() => {
  191. uncollapseAllComments($collapsedComments.find('.thing.comment.collapsed'), depth - 1)
  192. })
  193. }
  194. }
  195.  
  196. // --------------------------------------------------------------------
  197.  
  198. const rootUrl = `https://${location.hostname}/`
  199. const postUrl = $('.thing.link > .entry a.comments').prop('href')
  200.  
  201. // console.info(`%cSite:%c ${rootUrl}\n%cPost:%c ${postUrl}`, 'font-weight: bold', '', 'font-weight: bold', '')
  202.  
  203. // --------------------------------------------------------------------
  204.  
  205. // Uncollapse non-top level comments on page load
  206. uncollapseAllComments($('.thing.comment .thing.comment.collapsed'))
  207.  
  208. const observer = new MutationSummary({
  209. callback([ deepThreadSpans, moreCommentsSpans, comments ]) {
  210. // console.log(`Added ${deepThreadSpans.added.length} deep thread spans and ${moreCommentsSpans.added.length} more comment spans`)
  211.  
  212. $(moreCommentsSpans).addClass('expand-inline')
  213. processDeepThreadSpans(deepThreadSpans.added)
  214.  
  215. const $collapsedComments = $(comments.added).filter('.collapsed')
  216. uncollapseAllComments($collapsedComments)
  217. },
  218. rootNode: document.body,
  219. queries: [
  220. { element: 'span.deepthread' },
  221. { element: 'span.morecomments' },
  222. { element: '.thing.comment' },
  223. ]
  224. })
  225.  
  226. // To process spans in the HTML source
  227. $('span.morecomments').addClass('expand-inline')
  228. processDeepThreadSpans($('span.deepthread'))
  229.  
  230. // --------------------------------------------------------------------
  231.  
  232. 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'
  233.  
  234. $(document.body).append(`<style type="text/css">
  235. .expand-inline {
  236. display: block;
  237. padding: 0;
  238. }
  239. .expand-inline:after {
  240. display: none !important;
  241. }
  242. .expand-inline a {
  243. display: block;
  244. background: transparent url(${EXPAND_ICON}) no-repeat center left;
  245. padding-left: 40px;
  246. height: 32px;
  247. line-height: 32px;
  248. font-size: 1.2rem !important;
  249. font-weight: normal !important;
  250. vertical-align: middle;
  251. text-align: left;
  252. }
  253. .expand-inline a:hover {
  254. background-color: rgba(0, 105, 255, 0.05);
  255. text-decoration: none;
  256. }
  257. .pulsar-horizontal {
  258. display: inline-block;
  259. }
  260. .pulsar-horizontal > div {
  261. display: inline-block;
  262. border-radius: 100%;
  263. animation-name: pulsing;
  264. animation-timing-function: ease-in-out;
  265. animation-iteration-count: infinite;
  266. animation-fill-mode: both;
  267. }
  268. @keyframes pulsing {
  269. 0%, 100% {
  270. transform: scale(0);
  271. opacity: 0.5;
  272. }
  273. 50% {
  274. transform: scale(1);
  275. opacity: 1;
  276. }
  277. }
  278. @keyframes fadenewpost {
  279. 0% {
  280. background-color: #ffc;
  281. padding-left: 5px;
  282. }
  283. 100% {
  284. background-color: transparent;
  285. padding-left: 0;
  286. }
  287. }
  288. </style>`)
  289.  
  290. })(jQuery, MutationSummary?.MutationSummary)
  291.  
  292. jQuery.noConflict(true)