4chan embedded quote fixer

Puts embedded quotes in end of post rather than beginning, can also click on checkboxes in posts to remove them, allows for expand all/collapse all via arrows in post header

Verzia zo dňa 16.11.2018. Pozri najnovšiu verziu.

  1. // ==UserScript==
  2. // @name 4chan embedded quote fixer
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2
  5. // @description Puts embedded quotes in end of post rather than beginning, can also click on checkboxes in posts to remove them, allows for expand all/collapse all via arrows in post header
  6. // @author You
  7. // @match http://boards.4chan.org/*/thread/*
  8. // @match https://boards.4chan.org/*/thread/*
  9. // @grant none
  10. // ==/UserScript==
  11. //window.onload(function() {
  12. setTimeout(function() {
  13. let quotesList = Array.from(document.getElementsByClassName("quotelink")).filter(node => window.getComputedStyle(node).getPropertyValue("font-size") === "10.6667px");
  14.  
  15. //add preview inlined to class of embedded quote, wrap whole thing in mutation observer
  16. //on click, add html, gray out link (add linkfade to classlist)
  17. //on another click, find embedded post 1 layer deep, remove(), and then change link back to normal
  18.  
  19. quotesList.forEach(node => {
  20. node.addEventListener("click", qEV);
  21. });
  22.  
  23. Array.from(document.getElementsByTagName("span")).filter(el => {
  24. try {
  25. if (el.children.length > 0 && el.children[0].classList.contains("quotelink")) return true;
  26. } catch(error) {
  27. //console.log("custom error in span EL", el);
  28. return false;
  29. }
  30. }).forEach(el => el.addEventListener("click", qSpanClick));
  31.  
  32. addRemoveCapability();
  33. addCollapseAndExpand();
  34. }, 4000);
  35. //});
  36.  
  37.  
  38. function addCollapseAndExpand() {
  39. Array.from(document.querySelectorAll(".post.reply")).forEach(postNode => {
  40. if (Array.from(postNode.children[1].children).filter(node => node.classList.contains("expandSpan")).length === 0) {
  41. let expandSpan = document.createElement("span");
  42. expandSpan.classList.add("expandSpan");
  43. expandSpan.innerText = "[↓]";
  44. expandSpan.title = "expand all 'quoted by' posts";
  45. expandSpan.style.color = "#1019d2e6";
  46. expandSpan.style.fontSize = "11px";
  47. expandSpan.style.paddingLeft = "5px";
  48. expandSpan.style.paddingRight = "5px";
  49. expandSpan.style.whiteSpace = "nowrap";
  50. let collapseSpan = document.createElement("span");
  51. collapseSpan.classList.add("collapseSpan");
  52. collapseSpan.style.color = "#1019d2e6";
  53. collapseSpan.innerText = "[↑]";
  54. collapseSpan.title = "collapse all 'quoted by' posts";
  55. collapseSpan.style.fontSize = "11px";
  56. collapseSpan.style.paddingRight = "0px";
  57. collapseSpan.style.whiteSpace = "nowrap";
  58. postNode.children[1].insertBefore(expandSpan, postNode.querySelector(".postMenuBtn"));
  59. postNode.children[1].insertBefore(collapseSpan, postNode.querySelector(".postMenuBtn"));
  60. }
  61. let expand = postNode.querySelector(".expandSpan");
  62. let collapse = postNode.querySelector(".collapseSpan");
  63. expand.removeEventListener("click", expandSpanEL);
  64. expand.addEventListener("click", expandSpanEL);
  65. collapse.removeEventListener("click", collapseSpanEL);
  66. collapse.addEventListener("click", collapseSpanEL);
  67. });
  68. }
  69.  
  70. function expandSpanEL(e) {
  71. Array.from(Array.from(e.target.parentNode.children).filter(node => node.classList.contains("backlink"))[0].children).forEach(node => {if (!node.children[0].classList.contains("linkfade")) node.children[0].click();});
  72. }
  73.  
  74. function collapseSpanEL(e) {
  75. Array.from(Array.from(e.target.parentNode.children).filter(node => node.classList.contains("backlink"))[0].children).forEach(node => {if (node.children[0].classList.contains("linkfade")) node.children[0].click();});
  76. }
  77.  
  78. function qSpanClick(e) {
  79. e.preventDefault();
  80. }
  81.  
  82. function addRemoveCapability() {
  83. //turns all delete checkboxes to simply post removal buttons
  84. Array.from(document.getElementsByClassName("postInfo desktop")).forEach(el => el.children[0].addEventListener("click", postRemove));
  85. }
  86.  
  87. function postRemove(e) {
  88. e.preventDefault();
  89. e.target.parentNode.parentNode.parentNode.remove();
  90. }
  91.  
  92. function qEV(e, node=e.target) {
  93. //console.log("quote clicked.");
  94. e.preventDefault();
  95.  
  96. if (!node.classList.contains("linkfade")) {
  97. //if not grayed out, first time clicking, adding node
  98. node.classList.add("linkfade");
  99.  
  100. //finding post and constructing new post
  101. let postsArr = Array.from(document.getElementsByClassName("postNum desktop")).filter(el => el.children[1].innerText === node.innerText.slice(2));
  102. //console.log("postsAArr", postsArr);
  103. let post = postsArr[0].parentNode.parentNode.parentNode;
  104. //console.log("particular post from postArr", post.outerHTML);
  105. let newPost = post.cloneNode(true);
  106. //newPost.outerHTML = post.outerHTML;
  107. newPost.classList.add("inlined");
  108. newPost.children[1].classList.remove("highlight");
  109. newPost.style.display = "";
  110. newPost.style.marginTop = "10px";
  111. newPost.children[1].style.border = "1px solid #a1b7c899";
  112. newPost.children[0].style.display = "none";
  113. //console.log("newPost before adding", newPost.outerHTML, "oldPost", post.outerHTML, "boolean", newPost.outerHTML === post.outerHTML);
  114.  
  115. //adding to parent post
  116. if (node.parentNode.parentNode.parentNode.parentNode.children[2].classList.contains("file")) {
  117. node.parentNode.parentNode.parentNode.parentNode.children[3].appendChild(newPost);
  118. setTimeout(function() {node.parentNode.parentNode.parentNode.parentNode.children[3].children[0].remove();});
  119. } else {
  120. node.parentNode.parentNode.parentNode.parentNode.children[2].appendChild(newPost);
  121. setTimeout(function() {node.parentNode.parentNode.parentNode.parentNode.children[2].children[0].remove();});
  122. }
  123. //console.log("node just added", newPost);
  124. setTimeout(function(){newPost.style.display = "";}, 50);
  125.  
  126. //resetting event listeners
  127. resetQLEV();
  128. } else {
  129. //grayed out, second time clicking, removing node
  130. node.classList.remove("linkfade");
  131. let idx = node.parentNode.parentNode.parentNode.parentNode.children[2].classList.contains("file") ? 3 : 2;
  132. Array.from(node.parentNode.parentNode.parentNode.parentNode.children[idx].children).forEach(el => {if (el.classList.contains("inlined")
  133. && node.innerText.slice(2) === el.children[1].children[1].children[3].children[1].innerText) el.remove();});
  134. }
  135. }
  136.  
  137. function resetQLEV() {
  138. let newQuotesList = Array.from(document.getElementsByClassName("quotelink")).filter(node => window.getComputedStyle(node).getPropertyValue("font-size") === "10.6667px");
  139. newQuotesList.forEach(node => node.removeEventListener("click", qEV));
  140. newQuotesList.forEach(node => node.addEventListener("click", qEV));
  141. let newSpanList = Array.from(document.getElementsByTagName("span")).filter(el => {
  142. try {
  143. if (el.children.length > 0 && el.children[0].classList.contains("quotelink")) return true;
  144. } catch(error) {
  145. //console.log("custom error in span EL", el);
  146. return false;
  147. }
  148. });
  149. newSpanList.forEach(el => el.removeEventListener("click", qSpanClick));
  150. newSpanList.forEach(el => el.addEventListener("click", qSpanClick));
  151. Array.from(document.getElementsByClassName("postInfo desktop")).forEach(el => el.children[0].removeEventListener("click", postRemove));
  152. addRemoveCapability();
  153. setTimeout(function() {addCollapseAndExpand();}, 100);
  154. }
  155.  
  156. let observer = new MutationObserver(resetQLEV);
  157. observer.observe(document.getElementsByClassName("thread")[0], {childList: true});