Reddit Show Hidden Emotes

Show empty emotes (links with no text or image)

  1. // ==UserScript==
  2. // @name Reddit Show Hidden Emotes
  3. // @namespace reddit_show_hidden_emotes
  4. // @description Show empty emotes (links with no text or image)
  5. // @include http://*.reddit.com/*
  6. // @include https://*.reddit.com/*
  7. // @match http://*.reddit.com/*
  8. // @match https://*.reddit.com/*
  9. // @version 1.4.2
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. // This work is licensed under the Creative Commons Attribution 2.0 Generic License.
  14. // To view a copy of this license, visit http://creativecommons.org/licenses/by/2.0/.
  15.  
  16. whitespaceRegex = new RegExp("^[\\s]*$")
  17. redditTrimRegex = new RegExp("^http://(.*)\.reddit\.com")
  18. spaceEmoteRegex = new RegExp("^http://(.*)\.reddit\.com/sp$")
  19. minecraftRegex = new RegExp("^http://(.*)\.reddit\.com/r/minecraft#")
  20. // This regex is needed because Chrome returns "" for pseudo elements rather than "none"
  21. noneOrBlank = new RegExp("^(?:none)?$")
  22.  
  23. function addStyle(newStyle) {
  24. var head = document.getElementsByTagName("head")[0];
  25. var cssNode = document.createElement('style')
  26. cssNode.type = 'text/css'
  27. cssNode.appendChild(document.createTextNode(newStyle))
  28. head.appendChild(cssNode)
  29. }
  30.  
  31. emoteStyle = ".reddit_show_hidden_emotes_span {\n" +
  32. "color: gray; word-wrap: break-word; background-color: white; " +
  33. "border-color: gray; border-style: solid; border-width: 1px; border-radius: 5px; " +
  34. "padding-left: 4px; padding-right: 4px; " +
  35. "display: block; clear: none; float: left; " +
  36. "cursor: pointer; " +
  37. "}\n"
  38.  
  39. addStyle(emoteStyle)
  40.  
  41. function expandEmotes(postDiv) {
  42. var innerLinks = Array.prototype.slice.call(postDiv.getElementsByTagName("a"));
  43. for (var j in innerLinks) {
  44. if (innerLinks[j].innerHTML.match(whitespaceRegex) ) {
  45. // No link text
  46. // The ":after" query is needed here because the F7U12 CSS is freaky
  47. if (
  48. (window.getComputedStyle(innerLinks[j]).getPropertyValue('background-image') == "none") &&
  49. (window.getComputedStyle(innerLinks[j], ':after').getPropertyValue('background-image').match(noneOrBlank) != null) &&
  50. (innerLinks[j].className.indexOf("hiddenEmoteExpanded") == -1) &&
  51. (innerLinks[j].href.match(spaceEmoteRegex) == null) && // Don't expand the r/mlp space pseudo-emote
  52. (innerLinks[j].href.match(minecraftRegex) == null) // r/minecraft funk
  53. ) {
  54. // No emote image
  55. var theDiv = document.createElement("span")
  56. theDiv.setAttribute("title", innerLinks[j].title)
  57. theDiv.setAttribute("class", "reddit_show_hidden_emotes_span")
  58. linkText = unescape(innerLinks[j].href.replace(redditTrimRegex, ""))
  59. theDiv.appendChild(document.createTextNode(linkText))
  60. innerLinks[j].parentNode.insertBefore(theDiv, innerLinks[j].nextSibling)
  61. // Add a class to the link so Super Reddit Alt-Text Display can put it's text in the right spot
  62. innerLinks[j].className = innerLinks[j].className + " hiddenEmoteExpanded"
  63. }
  64. }
  65. }
  66. }
  67.  
  68. function expandChildMDs(elm) {
  69. // Find all user created content sections and expand emotes in them
  70. var mdElements = elm.getElementsByClassName("md")
  71. for (var i in mdElements) {
  72. if (mdElements[i].tagName == 'DIV') {
  73. expandEmotes(mdElements[i]);
  74. }
  75. }
  76. }
  77.  
  78. // hasClass, stolen from the Reddit Enhancement Suite
  79. function hasClass(ele,cls) {
  80. if ((typeof(ele) == 'undefined') || (ele == null)) {
  81. console.log(arguments.callee.caller);
  82. return false;
  83. }
  84. return ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
  85. }
  86.  
  87. // Add a listener for the DOMNodeInserted event so we can expand emotes in new comments
  88. // created by either a reply or by clicking "load more comments" in large threads.
  89. function handleInsertion( event ) {
  90. // The actual even we see will be the insertion of the outer "thing" div
  91. if ((event.target.tagName == 'DIV') && (hasClass(event.target, "thing"))) {
  92. var mdElements = event.target.getElementsByClassName("md")
  93. for (var i in mdElements) {
  94. if (mdElements[i].tagName == 'DIV') {
  95. expandEmotes(mdElements[i]);
  96. }
  97. }
  98. }
  99. else if ((event.target.tagName == 'FORM') && (hasClass(event.target, "usertext"))) {
  100. var mdElements = event.target.getElementsByClassName("md")
  101. for (var i in mdElements) {
  102. if (mdElements[i].tagName == 'DIV') {
  103. expandEmotes(mdElements[i]);
  104. }
  105. }
  106. }
  107. }
  108.  
  109. // Add a listener for the DOMNodeInserted event so we can expand emotes on the RES dashboard
  110. // This gets it's own listener because watching sitetable causes unnecessary rescans on normal submission pages.
  111. function handleSitetableInsertion( event ) {
  112. if ((event.target.tagName == 'DIV') && (hasClass(event.target, "sitetable")) && (hasClass(event.target, "linklisting"))) {
  113. // Insertion event for RES dashboard widgets
  114. // Note: This causes rescans on some other pages too
  115. //GM_log("Scanning DIV.sitetable.linklisting")
  116. expandChildMDs(event.target)
  117. }
  118. else if ((event.target.tagName == 'DIV') && (hasClass(event.target, "parent"))) {
  119. // Insertion event fow "show parent" on the RES dashboard
  120. //GM_log("Scanning DIV.parent")
  121. expandChildMDs(event.target)
  122. }
  123. }
  124.  
  125. // Initial expansion of static content
  126. expandChildMDs(document)
  127.  
  128. document.body.addEventListener('DOMNodeInserted', handleInsertion, false);
  129.  
  130. if (RegExp("^https?://(?:www.)?reddit.com/r/dashboard", "i").exec(window.location)) {
  131. // Add the listener for sitetable if we're on the RES dashboard
  132. document.body.addEventListener('DOMNodeInserted', handleSitetableInsertion, false);
  133. }
  134. else if (RegExp("^https?://(?:www.)?reddit.com/user/", "i").exec(window.location) && document.getElementsByClassName("neverEndingReddit")) {
  135. // Add this listener for RES never ending reddit on user pages
  136. document.body.addEventListener('DOMNodeInserted', handleSitetableInsertion, false);
  137. }
  138.  
  139. // Interacting with the other emote adding scripts causes timing issues, so instead trying to
  140. // sync with them we just wait a good long time then remove our expansions from things they've styled.
  141. function cleanupRestyledEmotes() {
  142. var emoteExpandElements = Array.prototype.slice.call(document.getElementsByClassName("hiddenEmoteExpanded"))
  143. for (var i in emoteExpandElements) {
  144. if (
  145. (window.getComputedStyle(emoteExpandElements[i]).getPropertyValue('background-image') != "none") ||
  146. (window.getComputedStyle(emoteExpandElements[i], ':after').getPropertyValue('background-image').match(noneOrBlank) == null)
  147. ) {
  148. var nes = emoteExpandElements[i].nextElementSibling
  149. while(nes) {
  150. if (nes.className.indexOf("reddit_show_hidden_emotes_span") != -1) {
  151. nes.parentNode.removeChild(nes)
  152. nes = null
  153. }
  154. else {
  155. nes = nes.nextElementSibling
  156. }
  157. }
  158. }
  159. }
  160. }
  161.  
  162. window.setTimeout(cleanupRestyledEmotes,2000)