// ==UserScript== // @name 4chan Image Resizer // @namespace https://greasyfork.org/en/users/393416 // @version 1.0 // @description Automatically downscales uploaded pre-submit images. Requires 4chan X. // @author greenronia // @match *://boards.4chan.org/* // @match *://boards.4channel.org/* // @grant none // @downloadURL none // ==/UserScript== console.log("[ImageResizer] Initialized"); //Checking if QuickReply dialogue is open. document.addEventListener('QRDialogCreation', function(listenForQRDC) { var checkBox = document.getElementById("imgResize"); //Checking if the check box already exists if (!checkBox) { appendCheckBox(); } else { console.log("[ImageResizer][Error] Check box already exists"); } //Listening for clicks on check box document.getElementById("imgResize").addEventListener("click", checkState); checkState(1); console.log("[QRFile] Listening..."); //QRFile | Listening for QRFile, in response to: QRGetFile | Request File document.addEventListener('QRFile', function(GetFile) { console.log("[QRFile] File served: " + GetFile.detail); const file = GetFile.detail; //Initialize an instance of a FileReader const reader = new FileReader(); //console.log("Type: " + file.type); //Checking if file is JPG or PNG if (file.type == "image/jpeg" || file.type == "image/png") { console.log("Correct FileType: " + file.type); reader.onload = function(resize) { var img = new Image(); img.src = reader.result; img.onload = function() { //Accepted image dimensions //(img.height == 1080) if (img.width == 1920) { //ADJUST HERE console.log("INPUT Dimensions OK: " + img.width + "x" + img.height); var canvas = document.createElement("canvas"); //Target image dimensions. Don't try to upscale images! var MAX_WIDTH = 1280; //ADJUST HERE var MAX_HEIGHT = 720; //ADJUST HERE var width = img.width; var height = img.height; //Calculating dimensions if (width > height) { if (width > MAX_WIDTH) { height *= MAX_WIDTH / width; width = MAX_WIDTH; } } else { if (height > MAX_HEIGHT) { width *= MAX_HEIGHT / height; height = MAX_HEIGHT; } } console.log("OUTPUT Dimesnions: " + width + "x" + height); // resize the canvas to the new dimensions canvas.width = width; canvas.height = height; // scale & draw the image onto the canvas var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0, width, height); //DEBUG Show image //document.body.appendChild(canvas) //Converts dataURI to blob function dataURItoBlob(dataURI) { // convert base64/URLEncoded data component to raw binary data held in a string var byteString; if (dataURI.split(',')[0].indexOf('base64') >= 0) { byteString = atob(dataURI.split(',')[1]); } else { byteString = unescape(dataURI.split(',')[1]); } // separate out the mime component var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; // write the bytes of the string to a typed array var ia = new Uint8Array(byteString.length); for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } return new Blob([ia], { type: mimeString }); } //canvas to dataURL | jpeg quality (0-1) var dataURL = canvas.toDataURL('image/jpeg', 0.92); //ADJUST HERE //dataURL to blob var blob = dataURItoBlob(dataURL); //Stop classObserver | prevent trigger loop classObserver.disconnect(); console.log("[classObserver] Stopping..."); //QRSetFile | Set the resized image to upload var detail = { file: blob, name: 'autoResized' }; var event = new CustomEvent('QRSetFile', { bubbles: true, detail: detail }); document.dispatchEvent(event); console.log("[QRSetFile] File Sent"); //Notification var FSInfo = "Original size: (" + formatBytes(file.size) + ", " + img.width + "x" + img.height + ") \n New size: (" + formatBytes(blob.size)+ ", " + width + "x" + height +")"; var msgDetail = {type: 'info', content: FSInfo}; var msgEvent = new CustomEvent('CreateNotification', {bubbles: true, detail: msgDetail}); document.dispatchEvent(msgEvent); //Restart classObserver classObserver.observe(targetNode, observerOptions); console.log(" \n[classObserver] Restarting..."); } else { console.log("\n[Error] BAD INPUT Dimensions: " + img.width + "x" + img.height); return; } } } // Read the file reader.readAsDataURL(file); } else { console.log("\n [Error] Invalid FileType: " + file.type); } }, false); //Observing if a file is uploaded or not | checking if div (with id: "file-n-submit") has class named: "has-file" function callback(mutationList, observer) { if (document.getElementById("file-n-submit").classList.contains("has-file") === true && checkState(2) === true) { console.log("\n[classObserver] File detected") //QRGetFile | Request File console.log("[QRGetFile] Requesting file..."); document.dispatchEvent(new CustomEvent('QRGetFile')); } else if (checkState(2) === false) { console.log("[classObserver] ImageResizer is disabled"); return; } else { console.log("[classObserver] No file"); } } //MutationObserver. Checks if div (with id "file-n-submit") has its class attribute changed const targetNode = document.getElementById('file-n-submit'); var observerOptions = { attributes: true }; var classObserver = new MutationObserver(callback); console.log("[classObserver] Starting..."); classObserver.observe(targetNode, observerOptions); }, false); //Add a label with a check box for ImageResize in QR, AFTER label with an id "autohide" function appendCheckBox() { var labelElem = document.createElement("label"); var inputElem = document.createElement("input"); inputElem.type = "checkbox"; inputElem.id = "imgResize"; inputElem.title = "image-resize"; var reference = document.getElementById('autohide'); reference.parentNode.parentNode.insertBefore(labelElem, parent.nextSibling) labelElem.appendChild(inputElem); labelElem.innerHTML += "Resize"; //Checked by default document.getElementById("imgResize").checked = true; //ADJUST HERE } //Check box state function checkState(caller) { var state = document.getElementById("imgResize").checked; if (state === true) { if (caller != 2) console.log("[ImageResizer] Enabled"); return true; } else { if (caller != 2) console.log("[ImageResizer] Disabled"); return false; }; } //Bloat 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]}