Kbin Collapsible Comments

On the KBin website, support collapsing and expanding comments

  1. // ==UserScript==
  2. // @name Kbin Collapsible Comments
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.8
  5. // @description On the KBin website, support collapsing and expanding comments
  6. // @author CodingAndCoffee
  7. // @match https://kbin.social/m/*
  8. // @match https://fedia.io/m/*
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=kbin.social
  10. // @grant none
  11. // @license MIT
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. const COLLAPSE_PARENTS_BY_DEFAULT = false;
  18.  
  19. const isMobileUser = function () {
  20. if (navigator.userAgent.match(/Android/i)
  21. || navigator.userAgent.match(/webOS/i)
  22. || navigator.userAgent.match(/iPhone/i)
  23. || navigator.userAgent.match(/iPad/i)
  24. || navigator.userAgent.match(/iPod/i)
  25. || navigator.userAgent.match(/BlackBerry/i)
  26. || navigator.userAgent.match(/Windows Phone/i)) {
  27. return true;
  28. } else {
  29. return false;
  30. }
  31. };
  32.  
  33. const getNumericId = function (comment) {
  34. return comment.id.split("-").reverse()[0];
  35. };
  36.  
  37. const getComment = function (numericId) {
  38. return document.querySelector('#comments blockquote#entry-comment-' + numericId);
  39. };
  40.  
  41. const getChildrenOf = function (numericId) {
  42. return document.querySelectorAll('#comments blockquote[data-subject-parent-value="' + numericId + '"]');
  43. }
  44.  
  45. const getDescendentsOf = function (numericId) {
  46. var parent = getComment(numericId);
  47. var children = getChildrenOf(numericId);
  48.  
  49. var descendents = [];
  50. children.forEach(function (child) {
  51. descendents.push(child);
  52. var childDescendents = getDescendentsOf(getNumericId(child));
  53. childDescendents.forEach (function (cd) {
  54. descendents.push(cd);
  55. });
  56. });
  57. return descendents;
  58. };
  59.  
  60. const makeCollapseLabel = function (isVisible, childrenCount) {
  61. var upDown = (isVisible ? '<i class="fa-solid fa-chevron-up"></i>' : '<i class="fa-solid fa-chevron-down"></i>');
  62. if (!isMobileUser()) {
  63. var label = (isVisible ? ' hide ' : ' show ')
  64. return (childrenCount > 0 ?
  65. (label + ' [' + childrenCount + '] ' + upDown) :
  66. (label + upDown)
  67. );
  68. } else {
  69. return upDown;
  70. }
  71. };
  72.  
  73. window.toggleChildren = function (numericId) {
  74. var parent = getComment(numericId);
  75.  
  76. // get visibility status
  77. var childrenVisible = (parent.dataset['childrenVisible'] === 'true');
  78. var toggledStatus = !childrenVisible;
  79.  
  80. // update dataset
  81. parent.setAttribute('data-children-visible', toggledStatus);
  82. if (!COLLAPSE_PARENTS_BY_DEFAULT) {
  83. var figure = parent.querySelector('figure');
  84. var footer = parent.querySelector('footer');
  85. var content = parent.querySelector('.content');
  86. var more = parent.querySelector('.more');
  87. if (toggledStatus) {
  88. content.style.display = '';
  89. footer.style.display = '';
  90. figure.style.display = '';
  91. parent.style.height = '';
  92. if (more) { more.style.display = ''; }
  93. } else {
  94. content.style.display = 'none';
  95. footer.style.display = 'none';
  96. figure.style.display = 'none';
  97. parent.style.height = '43px';
  98. parent.style.paddingTop = '0.53rem';
  99. if (more) { more.style.display = 'none'; }
  100. }
  101. }
  102.  
  103. // toggle visibility of the descendents
  104. var descendents = getDescendentsOf(numericId);
  105. descendents.forEach(function(c) {
  106. c.style.display = (toggledStatus ? 'grid' : 'none');
  107. });
  108.  
  109. // update the link text
  110. var childrenCount = parent.dataset['childrenCount'];
  111. var button = document.querySelector('#comment-'+numericId+'-collapse-button');
  112. console.debug(button);
  113. button.innerHTML = makeCollapseLabel(toggledStatus, childrenCount);
  114. };
  115.  
  116. const comments = document.querySelectorAll('#comments blockquote.comment');
  117. comments.forEach(function (comment) {
  118. var numericId = getNumericId(comment);
  119. var children = getChildrenOf(numericId);
  120. var childrenCount = children.length;
  121. // add some metadata
  122. comment.setAttribute('data-children-visible', true);
  123. comment.setAttribute('data-children-count', childrenCount);
  124.  
  125. var header = comment.querySelector('header');
  126. header.style.height = '40px';
  127. header.style.textWrap = 'nowrap';
  128. header.style.textOverflowX = 'ellipsis';
  129. header.style.overflowX = 'hidden';
  130. header.style.display = 'inline-flex';
  131. var content = comment.querySelector('.content');
  132. var footer = comment.querySelector('footer');
  133. var timeAgo = comment.querySelector('.timeago');
  134. timeAgo.style.overflow = 'hidden';
  135.  
  136. var elements = [header];
  137. if (isMobileUser()) {
  138. elements.push(content);
  139. }
  140. var toggleFn = function(ev) {
  141. ev.stopPropagation();
  142. window.toggleChildren(numericId);
  143. return false;
  144. };
  145. elements.forEach(function (it) {
  146. if (it) {
  147. it.addEventListener('click', toggleFn);
  148. it.style.cursor = 'pointer';
  149. }
  150. });
  151.  
  152. // Create the collapse/expand button
  153. var button = document.createElement("a");
  154. button.id = 'comment-'+numericId+'-collapse-button';
  155. button.innerHTML = makeCollapseLabel(true, childrenCount);
  156. button.style.cursor = "pointer";
  157. button.style.marginLeft = "0.5rem";
  158. header.appendChild(button);
  159. });
  160.  
  161. if (COLLAPSE_PARENTS_BY_DEFAULT) {
  162. comments.forEach(function (comment) {
  163. var numericId = getNumericId(comment);
  164.  
  165. var isTopLevel = (typeof comment.dataset['subject-parent-value'] !== 'string');
  166. if (isTopLevel) {
  167. window.toggleChildren(numericId);
  168. }
  169. });
  170. }
  171. })();