4chan browser

Button in catalog that shows all the images in all posts.

  1. // ==UserScript==
  2. // @name 4chan browser
  3. // @namespace e343367e7d795211da74d4a886e28f9c
  4. // @description Button in catalog that shows all the images in all posts.
  5. // @version 1.0.1
  6. // @include http://boards.4chan.org/*/catalog
  7. // @include https://boards.4chan.org/*/catalog
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (function () {
  12. const width = 200;
  13. const height = 200;
  14. const queries = {
  15. thread: '.thread',
  16. subject: '.teaser',
  17. image: '.fileThumb'
  18. };
  19. const button = document.createElement('button');
  20. button.innerHTML = 'Browse images';
  21. document.body.insertBefore(button, document.body.firstChild);
  22. button.addEventListener('click', () => {
  23. const window = open();
  24. const document = window.document;
  25. document.body.style.paddingTop = '20px';
  26. document.body.style.background = '#F1F1F1';
  27. navigation(window, document, threads(document));
  28. });
  29. function threads(newDocument) {
  30. const play = makePlay(newDocument);
  31. const tags = Array.from(document.querySelectorAll(queries.thread));
  32. return tags.map(tag => new Thread(newDocument, tag, play));
  33. }
  34. class Thread {
  35. constructor(document, tag, play) {
  36. let subject = tag.querySelector(queries.subject);
  37. if (subject) {
  38. subject = subject.innerHTML;
  39. } else {
  40. subject = 'Thread';
  41. }
  42. const a = document.createElement('a');
  43. a.href = tag.querySelector('a').href;
  44. a.innerHTML = subject;
  45. const p = document.createElement('p');
  46. p.appendChild(a);
  47. const loading = document.createTextNode('Loading');
  48. this.div = document.createElement('div');
  49. this.div.appendChild(p);
  50. this.div.appendChild(loading);
  51. this.request = new Promise((resolve, reject) => {
  52. const request = new XMLHttpRequest();
  53. request.open('GET', a.href);
  54. request.responseType = 'document';
  55. request.addEventListener('load', () => {
  56. this.div.removeChild(loading);
  57. let images = queries.image;
  58. images = request.response.querySelectorAll(images);
  59. images = Array.from(images);
  60. images = images.filter(image => image.href);
  61. this.thumbnails = images.map(image => {
  62. return new Thumbnail(document, image, this.div, play);
  63. });
  64. resolve();
  65. });
  66. request.send();
  67. });
  68. }
  69. load() {
  70. this.request.then(() => {
  71. for (let thumbnail of this.thumbnails) {
  72. thumbnail.load();
  73. }
  74. });
  75. }
  76. }
  77. class Thumbnail {
  78. constructor(document, image, container, play) {
  79. this.image = image;
  80. this.img = document.createElement('img');
  81. this.img.style.display = 'block';
  82. this.img.style.margin = 'auto';
  83. this.img.addEventListener('error', () => {
  84. container.removeChild(this.div);
  85. });
  86. this.img.addEventListener('load', () => {
  87. if (this.img.naturalWidth > this.img.naturalHeight) {
  88. this.img.width = width;
  89. } else {
  90. this.img.height = height;
  91. }
  92. });
  93. const a = document.createElement('a');
  94. a.style.display = 'block';
  95. a.style.position = 'relative';
  96. a.href = image.href;
  97. a.target = '_blank';
  98. a.appendChild(this.img);
  99.  
  100. this.div = document.createElement('div');
  101. this.div.style.width = width + 'px';
  102. this.div.style.height = height + 'px';
  103. this.div.style.padding = '10px';
  104. this.div.style.margin = '10px';
  105. this.div.style.borderRadius = '4px';
  106. this.div.style.display = 'inline-block';
  107. this.div.style.background = 'white';
  108. this.div.style.border = '1px solid #D8D8D8';
  109. this.div.appendChild(a);
  110.  
  111. if (image.href.match(/.webm/)) {
  112. const canvas = document.createElement('canvas');
  113. canvas.width = width;
  114. canvas.height = height;
  115. canvas.style.position = 'absolute';
  116. canvas.style.top = '0px';
  117. canvas.getContext('2d').drawImage(play, 0, 0);
  118. a.appendChild(canvas);
  119. }
  120. container.appendChild(this.div);
  121. }
  122. load() {
  123. this.img.src = this.image.querySelector('img').src;
  124. }
  125. }
  126. function navigation(window, document, threads) {
  127. let i = 0;
  128. const next = document.createElement('button');
  129. next.innerHTML = 'next';
  130. next.style.position = 'fixed';
  131. next.style.right = '0px';
  132. next.style.top = '0px';
  133. next.style.zIndex = '1';
  134. next.addEventListener('click', function () {
  135. if (i == 0) {
  136. document.body.appendChild(previous);
  137. }
  138.  
  139. document.body.removeChild(threads[i].div);
  140. i += 1;
  141. document.body.appendChild(threads[i].div);
  142. threads[i].load();
  143.  
  144. if (i == threads.length - 1) {
  145. document.body.removeChild(next);
  146. }
  147.  
  148. window.scrollTo(0, 0);
  149. });
  150. const previous = document.createElement('button');
  151. previous.innerHTML = 'previous';
  152. previous.style.position = 'fixed';
  153. previous.style.left = '0px';
  154. previous.style.top = '0px';
  155. previous.style.zIndex = '1';
  156. previous.addEventListener('click', function () {
  157. if (i == threads.length - 1) {
  158. document.body.appendChild(next);
  159. }
  160.  
  161. document.body.removeChild(threads[i].div);
  162. i -= 1;
  163. document.body.appendChild(threads[i].div);
  164. threads[i].load();
  165.  
  166. if (i == 0) {
  167. document.body.removeChild(previous);
  168. }
  169.  
  170. window.scrollTo(0, 0);
  171. });
  172. document.body.appendChild(threads[i].div);
  173. document.body.appendChild(next);
  174. threads[i].load();
  175. window.addEventListener('keydown', function (event) {
  176. if (event.keyCode == 37 && i > 0) {
  177. previous.click();
  178. }
  179.  
  180. if (event.keyCode == 39 && i < threads.length - 1) {
  181. next.click();
  182. }
  183. });
  184. }
  185. function makePlay(document) {
  186. const canvas = document.createElement('canvas');
  187. canvas.width = width;
  188. canvas.height = height;
  189.  
  190. const context = canvas.getContext('2d');
  191. context.fillStyle = 'hsla(0, 0%, 80%, 0.25)';
  192. context.strokeStyle = 'hsla(0, 0%, 100%, 0.5)';
  193. context.lineWidth = 2;
  194. context.moveTo(width / 3, height / 3);
  195. context.lineTo(width / 3 * 2, height / 2);
  196. context.lineTo(width / 3, height / 3 * 2);
  197. context.closePath();
  198. context.fill();
  199. context.stroke();
  200.  
  201. return canvas;
  202. }
  203. })();