// ==UserScript== // @name 4chan Image Resizer // @namespace https://greasyfork.org/en/users/393416 // @version 2.0 // @description Automatically downscales images based on custom presets. Requires 4chan X. // @author greenronia // @match *://boards.4chan.org/* // @match *://boards.4channel.org/* // @grant none // @icon https://i.imgur.com/lezZDKH.png // @downloadURL none // ==/UserScript== //*********************************// // // // "it just werks" // // // //*********************************// // // //----------DEBUG MODE-------------// var DEBUG = false;//console // //---------------------------------// if(DEBUG) console.log("[ImageResizer] Initialized"); //CSS var style = document.createElement("style"); style.innerHTML = '' + '.settingsOverlay { background: rgba(0,0,0,0.8); display: none; height: 100%; left: 0; position: fixed; top: 0; width: 100%; z-index: 7; } \n' + '#imgResizeMenu { position: fixed; top: 20%; left: 50%; width: 620px; margin-left: -15%; padding: 2em; overflow: hidden; }\n' + '#imgResizeMenu h3 { text-align: center; }\n' + '#imgResizeMenu a { cursor: pointer; }' + '#imgResizeMenu label { text-decoration-line: underline; }' + '.settingsOverlay input[type=number] { -moz-appearance: textfield; text-align: right; }\n' + '.resizer-settings { padding-bottom: 5px }\n' + '#errMsg { color: red; text-align: center; }\n' + '#ruleTable { border-collapse: collapse; }\n' + '#ruleTable td, th { padding: 8px; text-align: left; border-bottom: 1pt solid; }\n' + '#inputContainer { text-align: center; padding-top: 30px; }\n' + '#inputContainer button { margin-top: 20px; }\n' + '.menuBtns { margin-left: 1em; }\n' + '.downscale-menu-off { display: none; }\n' + '.downscale-menu-on { display: block; }'; var styleRef = document.querySelector("script"); styleRef.parentNode.insertBefore(style, styleRef); //Load settings getSettings(); getPresets(); function getSettings() { if (JSON.parse(localStorage.getItem("downscale-settings"))) { var settings = JSON.parse(localStorage.getItem("downscale-settings")); } else { settings = { enabled:true, notify:true, convert:false, jpegQuality:0.92 }; } return settings; } function getPresets() { if (JSON.parse(localStorage.getItem("downscale-presets"))) { var presets = JSON.parse(localStorage.getItem("downscale-presets")); } else { presets = []; } return presets; } //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(); } //Listening for clicks on check box document.getElementById("imgResize").addEventListener("click", checkState); checkState(1); if(DEBUG) console.log("[QRFile] Listening..."); //QRFile | Listening for QRFile, in response to: QRGetFile | Request File document.addEventListener('QRFile', function(GetFile) { if(DEBUG) 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 the file is JPG or PNG if (file.type == "image/jpeg" || file.type == "image/png") { if(DEBUG) console.log("Correct FileType: " + file.type); var presets = getPresets(); if (presets.length > 0 || getSettings().convert) { reader.onload = function(resize) { var img = new Image(); img.src = reader.result; img.onload = function() { if(DEBUG) console.log(""); if(DEBUG) console.log(presets); var matchCount = 0; var rule = []; var presetCount = presets.length; for (var i = 0; i < presetCount; i++) { //unpack rules rule[i] = presets[i].split(":"); //check for matching file type if (rule[i][0] != 0) { switch (parseInt(rule[i][0])) { case 1: rule[i][0] = "image/png"; break; case 2: rule[i][0] = "image/jpeg"; } if (rule[i][0] != file.type) continue; } if(DEBUG) console.log("FileType accepted: " + file.type); //check for matching dimensions if (rule[i][1] == img.width && rule[i][2] == img.height) { var MAX_WIDTH = parseInt(rule[i][3]); var MAX_HEIGHT = parseInt(rule[i][4]); matchCount++; if(DEBUG) console.log("INPUT Dimensions OK: " + img.width + "x" + img.height); if(DEBUG) console.log("Preset '" + i + "' matched: " + rule[i]); break; } } //failsafe if (!getSettings().convert && (matchCount == 0 || matchCount > 1)) { if(DEBUG) console.log("Image didn't match any presets.\n"); return; } if (getSettings().convert) { if(DEBUG) console.log("[Converter] Enabled"); if (file.type == "image/png") { MAX_WIDTH = img.width; MAX_HEIGHT = img.height; if(DEBUG) console.log("[Converter] Converting PNG to JPEG"); } else if (matchCount == 0 || matchCount > 1) { if(DEBUG) console.log("[Converter] Image format isn't PNG.\n"); return; } } if(DEBUG) console.log(""); var canvas = document.createElement("canvas"); var width = img.width; var height = img.height; //Calculating dimensions/aspect ratio 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; } } if(DEBUG) 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', parseFloat(getSettings().jpegQuality)); if(DEBUG) console.log("Image quality set to: " + getSettings().jpegQuality); //dataURL to blob var blob = dataURItoBlob(dataURL); //Stop classObserver | prevent trigger loop classObserver.disconnect(); if(DEBUG) 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); if(DEBUG) 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 +")"; if (getSettings().notify) { var msgDetail = {type: 'info', content: FSInfo, lifetime: 5}; var msgEvent = new CustomEvent('CreateNotification', {bubbles: true, detail: msgDetail}); document.dispatchEvent(msgEvent); } //Restart classObserver classObserver.observe(targetNode, observerOptions); if(DEBUG) console.log(" \n[classObserver] Restarting..."); } } // Read the file reader.readAsDataURL(file); } else { if(DEBUG) console.log("[Error] Presets not found.\n"); } } else { if(DEBUG) console.log("[Error] Invalid FileType: " + file.type + "\n"); } }, false); //Observing if a file was 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) { if(DEBUG) console.log("\n[classObserver] File detected") //QRGetFile | Request File if(DEBUG) console.log("[QRGetFile] Requesting file..."); document.dispatchEvent(new CustomEvent('QRGetFile')); } else if (checkState(2) === false) { if(DEBUG) console.log("[classObserver] ImageResizer is disabled"); return; } else { if(DEBUG) 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); if(DEBUG) 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 label = document.createElement("label"); var input = document.createElement("input"); input.type = "checkbox"; input.id = "imgResize"; input.title = "image-resize"; var reference = document.getElementById("autohide"); reference.parentNode.parentNode.insertBefore(label, parent.nextSibling) label.appendChild(input); label.innerHTML += "Resize"; //Checked by default document.getElementById("imgResize").checked = getSettings().enabled; } //Check box state function checkState(caller) { var state = document.getElementById("imgResize").checked; if (state === true) { if (caller != 2) if(DEBUG) console.log("[ImageResizer] Enabled"); return true; } else { if (caller != 2) if(DEBUG) console.log("[ImageResizer] Disabled"); return false; }; } //Clears error message

function clearErr() { document.getElementById("errMsg").innerHTML = ""; }; //Checks for any logic errors function basicCheck(edit, rulePos) { var inWidth = parseInt(document.getElementById("inWidth").value); var inHeight = parseInt(document.getElementById("inHeight").value); var outWidth = parseInt(document.getElementById("outWidth").value); var outHeight = parseInt(document.getElementById("outHeight").value); var imgType = parseInt(document.getElementById("imgType").value); if (outWidth <= 0 || outHeight <= 0) { document.getElementById("errMsg").innerHTML = "Invalid output dimensions"; return} else if (inWidth < outWidth || inHeight < outHeight) { document.getElementById("errMsg").innerHTML = "Cannot upscale images"; return} else finalCheck(edit, imgType, inWidth, inHeight, outWidth, outHeight, rulePos); return; } //Checks for any rule overlaps ([0] - Image type, [1] - Input width, [2] - Input height, [3] - Output width, [4] - Output height) function finalCheck(edit, imgType, inWidth, inHeight, outWidth, outHeight, rulePos) { var e = document.getElementById("imgType"); var format = e.options[e.selectedIndex].text; var presetString = imgType + ":" + inWidth + ":" + inHeight + ":" + outWidth + ":" + outHeight; var presets = getPresets(); if (presets.length > 0) { var rule = []; var presetCount = presets.length; for (var i = 0; i < presetCount; i++) { if (edit && i === rulePos) continue; rule[i] = presets[i].split(":"); if (presetString == presets[i]) { document.getElementById("errMsg").innerHTML = "Exact preset already exists"; return } else if ((inWidth == rule[i][1] && inHeight == rule[i][2]) && (imgType == rule[i][0] || rule[i][0] == 0)) { document.getElementById("errMsg").innerHTML = "Preset with the same input dimensions for " + format + " format already exists"; return } } } //save preset clearErr(); if (edit) presets[rulePos] = presetString; else presets.push(presetString); localStorage.setItem("downscale-presets", JSON.stringify(presets)); //rebuild list document.getElementById("ruleTable").tBodies.item(0).innerHTML = ""; printList(); //hide / display document.getElementById("ruleInput").remove(); document.getElementById("addRule").style.display = "block"; return; } //Check if possible to calculate output WIDTH function aspectCheckH() { var inWidth = document.getElementById("inWidth").value; var inHeight = document.getElementById("inHeight").value; var outWidth = document.getElementById("outWidth").value; var outHeight = document.getElementById("outHeight").value; if (outHeight > 0) { if (parseInt(inHeight) >= parseInt(outHeight)) { calcAspect("width", inWidth, inHeight, outHeight); clearErr(); } else { console.log("bad dimensions/upscaling inHeight" + inHeight + " > outHeight" + outHeight); document.getElementById("errMsg").innerHTML = "Cannot upscale images"; } } } //Check if possible to calculate output HEIGHT function aspectCheckW() { var inWidth = document.getElementById("inWidth").value; var inHeight = document.getElementById("inHeight").value; var outWidth = document.getElementById("outWidth").value; var outHeight = document.getElementById("outHeight").value; if (outWidth > 0) { if (parseInt(inWidth) >= parseInt(outWidth)) { calcAspect("height", inWidth, inHeight, outWidth); clearErr(); } else { console.log("bad dimensions/upscaling inHeight" + inWidth + " > outHeight" + outWidth); document.getElementById("errMsg").innerHTML = "Cannot upscale images"; } } } //Aspect ratio calculation (finds the other output dimension based on given exact input dimensions) function calcAspect(dimension, w, h, output) { if (dimension == "width") { var width = output / h * w; document.getElementById("outWidth").value = Math.round(width); } if (dimension == "height") { var height = output / w * h; document.getElementById("outHeight").value = Math.round(height); } } //Print preset list function printList() { var presets = getPresets(); var list = document.getElementById("imgResizeList"); var table = document.getElementById("ruleTable"); if (presets.length > 0) { var rule = []; var presetCount = presets.length; for (let i = 0; i < presetCount; i++) { rule[i] = presets[i].split(":"); switch (parseInt(rule[i][0])) { case 0: rule[i][0] = "PNG/JPEG"; break; case 1: rule[i][0] = "PNG"; break; case 2: rule[i][0] = "JPEG"; } let delRow = document.createElement("a"); let editRow = document.createElement("a"); delRow.innerHTML = "delete"; editRow.innerHTML = "edit"; //delete a rule and rebuild the list delRow.onclick = function() { if (document.getElementById("inputContainer")) document.getElementById("inputContainer").innerHTML = ""; console.log(rule); presets.splice(delRow.parentElement.parentElement.sectionRowIndex, 1); localStorage.setItem("downscale-presets", JSON.stringify(presets)); document.getElementById("ruleTable").tBodies.item(0).innerHTML = ""; printList(); clearErr(); document.getElementById("addRule").style.display = "block"; }; editRow.onclick = function() { inputUI(true, rule[i], i); clearErr(); }; var row = table.tBodies.item(0).insertRow(-1); row.insertCell(0).innerHTML = rule[i][0]; row.insertCell(1).innerHTML = '[ ' + rule[i][1] + ' x ' + rule[i][2] + ' ]'; row.insertCell(2).innerHTML = '→'; row.insertCell(3).innerHTML = '[ ' + rule[i][3] + ' x ' + rule[i][4] + ' ]'; row.insertCell(4).appendChild(editRow); row.insertCell(5).appendChild(delRow); } } } function inputUI(edit, rule, rulePos) { if (document.getElementById("inputContainer")) document.getElementById("inputContainer").innerHTML = ""; document.getElementById("addRule").style.display = "none"; var inputDiv = document.getElementById("inputContainer"); var input = document.createElement("div"); var discardRuleBtn = document.createElement("button"); discardRuleBtn.innerHTML = "Discard"; var saveRuleBtn = document.createElement("button"); saveRuleBtn.innerHTML = "Save"; input.id = "ruleInput"; //Rules form input.innerHTML = '' + '' + ' ' + '' + ' x ' + '' + ' ' + '  →   x ' + '
'; inputDiv.appendChild(input); var inWidth = document.getElementById("inWidth"); var inHeight = document.getElementById("inHeight"); var outWidth = document.getElementById("outWidth"); var outHeight = document.getElementById("outHeight"); if (edit) { switch (rule[0]) { case "PNG/JPEG": document.getElementById("imgType").selectedIndex = 0; break; case "PNG": document.getElementById("imgType").selectedIndex = 1; break; case "JPEG": document.getElementById("imgType").selectedIndex = 2; } inWidth.value = rule[1]; inHeight.value = rule[2]; outWidth.value = rule[3]; outHeight.value = rule[4]; } //Listen for user input on target dimension input fields to automatically calculate aspect ratio outWidth.addEventListener("input", aspectCheckW); outHeight.addEventListener("input", aspectCheckH); inWidth.onkeypress = function() {outHeight.value = 0; outWidth.value = 0; return isNumber(event)}; inHeight.onkeypress = function() {outHeight.value = 0; outWidth.value = 0; return isNumber(event) }; outWidth.onkeypress = function() { return isNumber(event) }; outHeight.onkeypress = function() { return isNumber(event) }; input.appendChild(saveRuleBtn); input.appendChild(discardRuleBtn); discardRuleBtn.onclick = function(){ document.getElementById(input.id).remove(); document.getElementById("addRule").style.display = "block"; clearErr();}; saveRuleBtn.onclick = function() { if (edit) basicCheck(true, rulePos); else basicCheck(false); }; } //******************************// // /!\ very lazy menu ahead /!\ // //******************************// function appendSettings() { //Button-------------------------------------------------------- var span = document.createElement("span"); var button = document.createElement("a"); button.id = "imgResizeSettings"; button.className += "fa fa-cog"; button.style = "cursor: pointer;"; button.title = "Image Resizer Settings"; var ref = document.getElementById('shortcut-settings'); ref.insertBefore(span, parent.nextSibling); span.appendChild(button); //Overlay | imgResizeOverlay------------------------------------ var overlay = document.createElement("div"); overlay.id = "imgResizeOverlay"; overlay.classList.add("settingsOverlay"); document.body.appendChild(overlay); //Settings menu links | imgResizeMenu--------------------------- var menu = document.createElement("div"); menu.id = "imgResizeMenu"; menu.classList.add("dialog"); overlay.appendChild(menu); var close = document.createElement("a"); close.className += "close fa fa-times"; close.style = "float: right;"; close.title = "Close"; menu.insertAdjacentElement('afterbegin', close); //Settings var settingsBtn = document.createElement("a"); settingsBtn.innerHTML += "Settings"; settingsBtn.classList.add("menuBtns"); settingsBtn.style = "font-weight: bold;"; settingsBtn.onclick = function() { settingsDiv.className = "downscale-menu-on"; presetsDiv.className = "downscale-menu-off"; helpDiv.className = "downscale-menu-off"; settingsBtn.style = "font-weight: bold;"; presetsBtn.style = ""; helpBtn.style = ""; } menu.appendChild(settingsBtn); //Presets var presetsBtn = document.createElement("a"); presetsBtn.innerHTML += "Presets"; presetsBtn.classList.add("menuBtns"); presetsBtn.onclick = function() { settingsDiv.className = "downscale-menu-off"; presetsDiv.className = "downscale-menu-on"; helpDiv.className = "downscale-menu-off"; settingsBtn.style = ""; presetsBtn.style = "font-weight: bold;"; helpBtn.style = ""; } menu.appendChild(presetsBtn); //Help var helpBtn = document.createElement("a"); helpBtn.innerHTML += "About"; helpBtn.classList.add("menuBtns"); helpBtn.onclick = function() { settingsDiv.className = "downscale-menu-off"; presetsDiv.className = "downscale-menu-off"; helpDiv.className = "downscale-menu-on"; settingsBtn.style = ""; presetsBtn.style = ""; helpBtn.style = "font-weight: bold;"; } menu.appendChild(helpBtn); menu.appendChild(document.createElement("hr")); //Content divs| imgResizeContent------------------------------------ var content = document.createElement("div"); content.id = "imgResizeContent"; menu.appendChild(content); content.innerHTML = ""; var errMsg = document.createElement("p"); errMsg.id = "errMsg"; //Settings var settingsDiv = document.createElement("div"); settingsDiv.id = "settingsDiv"; settingsDiv.classList.add("downscale-menu-on"); content.appendChild(settingsDiv); //Presets var presetsDiv = document.createElement("div"); presetsDiv.id = "presetsDiv"; presetsDiv.classList.add("downscale-menu-off"); content.appendChild(presetsDiv); //Help var helpDiv = document.createElement("div"); helpDiv.id = "heplDiv"; helpDiv.classList.add("downscale-menu-off"); content.appendChild(helpDiv); //Enable Resizer------------------------------------------------ //Title var title = document.createElement("h3"); title.innerHTML = "Image Resizer Settings"; settingsDiv.appendChild(title); var enableDiv = document.createElement("div"); enableDiv.classList.add("resizer-settings"); enableDiv.innerHTML = '' + '' + ': ' + 'Automatically downscales images using given presets.'; settingsDiv.appendChild(enableDiv); var enableSet = document.getElementById('enableSet'); enableSet.checked = getSettings().enabled; enableSet.oninput = function() { var settings = getSettings(); settings.enabled = enableSet.checked; document.getElementById("imgResize").checked = enableSet.checked; localStorage.setItem("downscale-settings", JSON.stringify(settings)); } //Display notifications----------------------------------------- var notifySetDiv = document.createElement("div"); notifySetDiv.classList.add("resizer-settings"); notifySetDiv.innerHTML = '' + '' + ': ' + 'Displays a notification when an image is downscaled.'; settingsDiv.appendChild(notifySetDiv); var notifySet = document.getElementById('displaySet'); notifySet.checked = getSettings().notify; notifySet.oninput = function() { var settings = getSettings(); settings.notify = notifySet.checked; localStorage.setItem("downscale-settings", JSON.stringify(settings)); } //Convert all PNGs to JPEGs------------------------------------- var convertSetDiv = document.createElement("div"); convertSetDiv.classList.add("resizer-settings"); convertSetDiv.innerHTML = '' + '' + ': ' + 'Automatically converts all submitted PNGs to JPEGs. Presets apply as normal.'; settingsDiv.appendChild(convertSetDiv); var convertSet = document.getElementById('convertSet'); convertSet.checked = getSettings().convert; convertSet.oninput = function() { var settings = getSettings(); settings.convert = convertSet.checked; localStorage.setItem("downscale-settings", JSON.stringify(settings)); } //Set JPEG quality---------------------------------------------- //RegExp ^(0(\.\d{1,2})?|1(\.0+)?)$ //Only one number (0 or 1) before decimal, and up tp 2 numbers after decimal, if there is 0 before decimal (between 0 and 9) //e.g. 0.92 true, 1.92 false var qualitySetDiv = document.createElement("div"); qualitySetDiv.classList.add("resizer-settings"); qualitySetDiv.innerHTML = '' + '' + ': ' + 'A number between 0 and 1 indicating the output image quality. Recommended 0.92.'; settingsDiv.appendChild(qualitySetDiv); var inputField = document.getElementById('imgQuality'); inputField.value = getSettings().jpegQuality; inputField.onkeypress = function() { return isDecimalNumber(event) }; //Check input field validity inputField.oninput = function() { var inputField = document.getElementById('imgQuality'); var r = new RegExp(/^(0(\.\d{1,2})?|1(\.0+)?)$/); if(r.test(document.getElementById('imgQuality').value)) { inputField.setCustomValidity(""); var settings = getSettings(); settings.jpegQuality = inputField.value; localStorage.setItem("downscale-settings", JSON.stringify(settings)); } else inputField.setCustomValidity("Set the value between 1 and 0 up to 2 numbers after the decimal point."); } //Preset table | ruleTable---------------------------------------- var table = document.createElement("table"); var thead = document.createElement("thead"); var tbody = document.createElement("tbody"); var presetsTitle = document.createElement("h3"); presetsTitle.innerHTML = "Presets"; presetsDiv.appendChild(presetsTitle); table.appendChild(thead); table.appendChild(tbody); table.id = "ruleTable"; var row = thead.insertRow(0); row.insertCell(0).outerHTML = "Format"; row.insertCell(1).outerHTML = "Input"; row.insertCell(2).outerHTML = ""; row.insertCell(3).outerHTML = "Output"; row.insertCell(4).outerHTML = ""; row.insertCell(5).outerHTML = ""; presetsDiv.appendChild(table); //Input container | inputContainer------------------------------ var inputDiv = document.createElement("div"); inputDiv.id = "inputContainer"; presetsDiv.appendChild(inputDiv); var addRuleBtn = document.createElement("button"); addRuleBtn.id = "addRule"; addRuleBtn.innerHTML = "New Preset"; printList(); presetsDiv.appendChild(addRuleBtn); presetsDiv.appendChild(errMsg); button.onclick = function(){ overlay.style.display = "block" }; close.onclick = function(){ overlay.style.display = "none" }; addRuleBtn.onclick = function(){ inputUI(false) }; //Help---------------------------------------------------------- var helpTitle = document.createElement("h3"); helpTitle.innerHTML = "About"; helpDiv.appendChild(helpTitle); var rant = document.createElement("p"); rant.innerHTML = '4chan Image Resizer can\'t upscale automatically downscales images based on custom presets. Originally developed to downscale anime/vidya screenshots "on the fly".

' + 'To get started, you first have to create a preset by choosing an input image format and entering input and output dimensions (pixels). Then just submit (drag & drop) an image to a quick reply form. ' + '
If it meets any of the presets input requirements, the image will be automatically downscaled to specified dimensions as a JPEG. ' + '

Note that output dimensions are constrained by input dimensions aspect ratio. ' + '
Also note that setting JPEG output quality to 1 may result in filesizes larger than that of the original image, and should be considered as a placebo. '; helpDiv.appendChild(rant); } appendSettings(); //Bloat function isDecimalNumber(e){var h=e.which?e.which:e.keyCode;return!(46!=h&&h>31&&(h<48||h>57))} function isNumber(e){var i=(e=e||window.event).which?e.which:e.keyCode;return!(i>31&&(i<48||i>57))} 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]}