4chan Image Resizer

Automatically downscales uploaded pre-submit images. Requires 4chan X.

当前为 2019-10-30 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name 4chan Image Resizer
  3. // @namespace https://greasyfork.org/en/users/393416
  4. // @version 1.0
  5. // @description Automatically downscales uploaded pre-submit images. Requires 4chan X.
  6. // @author greenronia
  7. // @match *://boards.4chan.org/*
  8. // @match *://boards.4channel.org/*
  9. // @grant none
  10. // ==/UserScript==
  11. console.log("[ImageResizer] Initialized");
  12. //Checking if QuickReply dialogue is open.
  13. document.addEventListener('QRDialogCreation', function(listenForQRDC) {
  14. var checkBox = document.getElementById("imgResize");
  15. //Checking if the check box already exists
  16. if (!checkBox) {
  17. appendCheckBox();
  18. }
  19. else {
  20. console.log("[ImageResizer][Error] Check box already exists");
  21. }
  22. //Listening for clicks on check box
  23. document.getElementById("imgResize").addEventListener("click", checkState);
  24. checkState(1);
  25. console.log("[QRFile] Listening...");
  26. //QRFile | Listening for QRFile, in response to: QRGetFile | Request File
  27. document.addEventListener('QRFile', function(GetFile) {
  28. console.log("[QRFile] File served: " + GetFile.detail);
  29.  
  30. const file = GetFile.detail;
  31. //Initialize an instance of a FileReader
  32. const reader = new FileReader();
  33.  
  34. //console.log("Type: " + file.type);
  35. //Checking if file is JPG or PNG
  36. if (file.type == "image/jpeg" || file.type == "image/png") {
  37. console.log("Correct FileType: " + file.type);
  38. reader.onload = function(resize) {
  39. var img = new Image();
  40. img.src = reader.result;
  41. img.onload = function() {
  42. //Accepted image dimensions
  43. //(img.height == 1080)
  44. if (img.width == 1920) { //ADJUST HERE
  45. console.log("INPUT Dimensions OK: " + img.width + "x" + img.height);
  46. var canvas = document.createElement("canvas");
  47. //Target image dimensions. Don't try to upscale images!
  48. var MAX_WIDTH = 1280; //ADJUST HERE
  49. var MAX_HEIGHT = 720; //ADJUST HERE
  50. var width = img.width;
  51. var height = img.height;
  52. //Calculating dimensions
  53. if (width > height) {
  54. if (width > MAX_WIDTH) {
  55. height *= MAX_WIDTH / width;
  56. width = MAX_WIDTH;
  57. }
  58. } else {
  59. if (height > MAX_HEIGHT) {
  60. width *= MAX_HEIGHT / height;
  61. height = MAX_HEIGHT;
  62. }
  63. }
  64. console.log("OUTPUT Dimesnions: " + width + "x" + height);
  65. // resize the canvas to the new dimensions
  66. canvas.width = width;
  67. canvas.height = height;
  68. // scale & draw the image onto the canvas
  69. var ctx = canvas.getContext("2d");
  70. ctx.drawImage(img, 0, 0, width, height);
  71. //DEBUG Show image
  72. //document.body.appendChild(canvas)
  73.  
  74. //Converts dataURI to blob
  75. function dataURItoBlob(dataURI) {
  76. // convert base64/URLEncoded data component to raw binary data held in a string
  77. var byteString;
  78. if (dataURI.split(',')[0].indexOf('base64') >= 0) { byteString = atob(dataURI.split(',')[1]); }
  79. else { byteString = unescape(dataURI.split(',')[1]); }
  80.  
  81. // separate out the mime component
  82. var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
  83.  
  84. // write the bytes of the string to a typed array
  85. var ia = new Uint8Array(byteString.length);
  86. for (var i = 0; i < byteString.length; i++) {
  87. ia[i] = byteString.charCodeAt(i);
  88. }
  89.  
  90. return new Blob([ia], {
  91. type: mimeString
  92. });
  93. }
  94. //canvas to dataURL | jpeg quality (0-1)
  95. var dataURL = canvas.toDataURL('image/jpeg', 0.92); //ADJUST HERE
  96. //dataURL to blob
  97. var blob = dataURItoBlob(dataURL);
  98. //Stop classObserver | prevent trigger loop
  99. classObserver.disconnect();
  100. console.log("[classObserver] Stopping...");
  101. //QRSetFile | Set the resized image to upload
  102. var detail = {
  103. file: blob,
  104. name: 'autoResized'
  105. };
  106. var event = new CustomEvent('QRSetFile', {
  107. bubbles: true,
  108. detail: detail
  109. });
  110. document.dispatchEvent(event);
  111. console.log("[QRSetFile] File Sent");
  112. //Notification
  113. var FSInfo = "Original size: (" + formatBytes(file.size) + ", " + img.width + "x" + img.height + ") \n New size: (" + formatBytes(blob.size)+ ", " + width + "x" + height +")";
  114. var msgDetail = {type: 'info', content: FSInfo};
  115. var msgEvent = new CustomEvent('CreateNotification', {bubbles: true, detail: msgDetail});
  116. document.dispatchEvent(msgEvent);
  117. //Restart classObserver
  118. classObserver.observe(targetNode, observerOptions);
  119. console.log("<END> \n[classObserver] Restarting...");
  120.  
  121. } else {
  122. console.log("<END>\n[Error] BAD INPUT Dimensions: " + img.width + "x" + img.height);
  123. return;
  124. }
  125. }
  126. }
  127. // Read the file
  128. reader.readAsDataURL(file);
  129. } else {
  130. console.log("<END>\n [Error] Invalid FileType: " + file.type);
  131. }
  132. }, false);
  133. //Observing if a file is uploaded or not | checking if div (with id: "file-n-submit") has class named: "has-file"
  134. function callback(mutationList, observer) {
  135. if (document.getElementById("file-n-submit").classList.contains("has-file") === true && checkState(2) === true) {
  136. console.log("<START>\n[classObserver] File detected")
  137. //QRGetFile | Request File
  138. console.log("[QRGetFile] Requesting file...");
  139. document.dispatchEvent(new CustomEvent('QRGetFile'));
  140.  
  141. } else if (checkState(2) === false) {
  142. console.log("[classObserver] ImageResizer is disabled");
  143. return;
  144. }
  145. else {
  146. console.log("[classObserver] No file");
  147. }
  148. }
  149. //MutationObserver. Checks if div (with id "file-n-submit") has its class attribute changed
  150. const targetNode = document.getElementById('file-n-submit');
  151. var observerOptions = {
  152. attributes: true
  153. };
  154. var classObserver = new MutationObserver(callback);
  155. console.log("[classObserver] Starting...");
  156. classObserver.observe(targetNode, observerOptions);
  157. }, false);
  158. //Add a label with a check box for ImageResize in QR, AFTER label with an id "autohide"
  159. function appendCheckBox() {
  160. var labelElem = document.createElement("label");
  161. var inputElem = document.createElement("input");
  162. inputElem.type = "checkbox";
  163. inputElem.id = "imgResize";
  164. inputElem.title = "image-resize";
  165. var reference = document.getElementById('autohide');
  166. reference.parentNode.parentNode.insertBefore(labelElem, parent.nextSibling)
  167. labelElem.appendChild(inputElem);
  168. labelElem.innerHTML += "Resize";
  169. //Checked by default
  170. document.getElementById("imgResize").checked = true; //ADJUST HERE
  171. }
  172. //Check box state
  173. function checkState(caller) {
  174. var state = document.getElementById("imgResize").checked;
  175. if (state === true) {
  176. if (caller != 2) console.log("[ImageResizer] Enabled");
  177. return true;
  178. } else {
  179. if (caller != 2) console.log("[ImageResizer] Disabled");
  180. return false;
  181. };
  182. }
  183. //Bloat
  184. function formatBytes(a,b){if(0==a)return"0 Bytes";var c=1024,d=b||2,e=["Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"],f=Math.floor(Math.log(a)/Math.log(c));return parseFloat((a/Math.pow(c,f)).toFixed(d))+" "+e[f]}