// ==UserScript==
// @name 4chan Image Resizer
// @namespace https://gf.qytechs.cn/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
// ==/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("<END> \n[classObserver] Restarting...");
} else {
console.log("<END>\n[Error] BAD INPUT Dimensions: " + img.width + "x" + img.height);
return;
}
}
}
// Read the file
reader.readAsDataURL(file);
} else {
console.log("<END>\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("<START>\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]}