您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Automatically downscales images based on custom presets and more. Requires 4chan X.
当前为
- // ==UserScript==
- // @name 4chan Image Resizer
- // @namespace https://greasyfork.org/en/users/393416
- // @version 2.2
- // @description Automatically downscales images based on custom presets and more. Requires 4chan X.
- // @author greenronia
- // @match *://boards.4chan.org/*
- // @match *://boards.4channel.org/*
- // @require https://cdnjs.cloudflare.com/ajax/libs/spark-md5/3.0.0/spark-md5.js
- // @grant none
- // @icon https://i.imgur.com/hQp5BTf.png
- // ==/UserScript==
- //
- //Using SparkMD5 to generate image hashes - https://github.com/satazor/js-spark-md5
- //
- //----------DEBUG MODE-------------//
- var DEBUG = false;//console //
- //---------------------------------//
- const version = 2.2;
- //---------------------------------//
- if(DEBUG) console.log("[ImageResizer] Initialized");
- //CSS
- var style = document.createElement("style");
- style.innerHTML = '' +
- '.centerImg { margin: 0; position: absolute; top: 50%; left: 50%; -ms-transform: translate(-50%, -50%); transform: translate(-50%, -50%); max-width: 100%; max-height: 100vh; height: auto; cursor: pointer; }\n' +
- '.settingsOverlay { background: rgba(0,0,0,0.8); display: none; height: 100%; left: 0; position: fixed; top: 0; width: 100%; z-index: 777; } \n' +
- '#pvOverlay { background: rgba(0,0,0,0.9); height: 100%; left: 0; position: fixed; top: 0; width: 100%; z-index: 777; text-align: center;} \n' +
- '#pvHeader { position: fixed; height: 35px; width: 100%; opacity: 0; -webkit-transition: opacity 0.5s ease-in-out;}\n' +
- '#pvHeader:hover { opacity: 0.8; -webkit-transition: none; }\n' +
- '.pvOpct { opacity: 0.7 !important; } \n' +
- '#imgResizeMenu { position: fixed; top: 20%; left: 35%; width: 30%; min-width: 620px; padding: 2em; overflow: hidden; z-index: 8;}\n' +
- '#imgResizeMenu h3 { text-align: center; }\n' +
- '#imgResizeMenu a { cursor: pointer; }\n' +
- '#imgResizeMenu label { text-decoration-line: underline; }\n' +
- '.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' +
- '#QCTable { border-collapse: collapse; }\n' +
- '#QCTable td, th { padding: 8px; text-align: center; border-bottom: 1pt solid; }\n' +
- '#QCTable p { margin: auto; max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n' +
- '#inputContainer { text-align: center; padding-top: 1em; }\n' +
- '#inputContainer button { margin-top: 20px; }\n' +
- '.menuBtns { margin-left: 1em; }\n' +
- '#sideMenu { position: absolute; display: none; padding: 5px 0px 5px 0px; width: 101px; margin-left: -106px; margin-top: -2px;}\n' +
- '.sideMenuElement { background: inherit; display: block; cursor: pointer; padding: 2px 10px 2px 10px; text-align: left;}\n' +
- '.downscale-menu-off { display: none; }\n' +
- '.downscale-menu-on { display: block !important; }';
- var styleRef = document.querySelector("script");
- styleRef.parentNode.insertBefore(style, styleRef);
- //Load settings
- getSettings();
- getPresets();
- getQCList();
- //Update downscale-settings object v2.2
- (function () {
- if (getSettings().shortcut == null) {
- var settings = getSettings();
- settings.shortcut = true;
- localStorage.setItem("downscale-settings", JSON.stringify(settings));
- var info = '4chan Image Resizer updated to version ' + version + '.\nMore info on "About" tab.';
- var msgDetail = {type: 'info', content: info, lifetime: 10};
- var msgEvent = new CustomEvent('CreateNotification', {bubbles: true, detail: msgDetail});
- document.dispatchEvent(msgEvent);
- }
- })();
- 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};//, shortcut:true };
- localStorage.setItem("downscale-settings", JSON.stringify(settings));
- }
- return settings;
- }
- function getPresets() {
- if (JSON.parse(localStorage.getItem("downscale-presets"))) {
- var presets = JSON.parse(localStorage.getItem("downscale-presets"));
- }
- else {
- presets = [];
- }
- return presets;
- }
- function getQCList() {
- if (JSON.parse(localStorage.getItem("downscale-qclist"))) {
- var QCList = JSON.parse(localStorage.getItem("downscale-qclist"));
- }
- else {
- QCList = [];
- }
- return QCList;
- }
- //Checking if QuickReply dialog is open.
- document.addEventListener('QRDialogCreation', function(listenForQRDC) {
- var checkBox = document.getElementById("imgResize");
- var sideMenu = document.getElementById("sideMenuArrow");
- //Checking if the "resize" check box and "side menu" already exist
- if (!sideMenu) {
- appendSideMenu();
- }
- 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);
- //Remove Remember option upon adding a (new) file.
- removeRemOption();
- const file = GetFile.detail;
- //Initialize an instance of a FileReader
- const reader = new FileReader();
- //Checking if the file is JPG or PNG
- if (file.type == "image/jpeg" || file.type == "image/png") {
- if(DEBUG) console.log("Acceptable File type: " + file.type);
- //Check if resizer already completed its task (to determine priority)
- var complete = false;
- var presets = getPresets();
- var QCList = getQCList();
- reader.onload = function(f) {
- var img = new Image();
- img.src = reader.result;
- img.onload = function() {
- //Base64 MD5 hash of an image
- var imgMD5 = SparkMD5.hash(img.src);
- if(DEBUG) console.log("<FILTER START>");
- if(DEBUG) if(getSettings().convert) console.log("[PNGConverter] Enabled"); else console.log("[PNGConverter] Disabled");
- if(DEBUG) console.log("INPUT Dimensions: " + img.width + "x" + img.height);
- if(DEBUG) console.log("INPUT File size: " + formatBytes(file.size));
- //THE priority list
- if (getQCList().length > 0) checkMD5(img, imgMD5);
- if (presets.length > 0 && !complete) checkPresets(img);
- if (getSettings().convert && !complete) checkPNG(img);
- if (!complete) {
- //Add QC button
- removeQCOption();
- quickConvert(img, file, imgMD5);
- //Remove/Add preview buton
- removePreviewOption();
- appendPreviewBtn(img.src, file.size, img.width, img.height, file.name);
- }
- return;
- }
- return;
- }
- function checkMD5(img, imgMD5) {
- if(DEBUG) console.log("[quickConvert] Checking for matching MD5: " + imgMD5);
- var filterCount = QCList.length;
- var matchFound = false;
- for (var i = 0; i < filterCount; i++) {
- //unpack md5 hash
- var filterMD5 = QCList[i].split(":").pop();
- if (filterMD5 == imgMD5) {
- if(DEBUG) console.log("[quickConvert] Match found.");
- matchFound = true;
- resizer(img.width, img.height, img);
- break;
- }
- }
- if(DEBUG) if (!matchFound)console.log("[quickConvert] No matching MD5 found.");
- return;
- }
- function checkPresets(img) {
- 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;
- }
- //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("Preset '" + i + "' matched: " + rule[i]);
- break;
- }
- }
- //failsafe
- if (matchCount == 0 || matchCount > 1) {
- if(DEBUG) console.log("Image didn't match any presets.\n------<END>------");
- return;
- }
- else {
- resizer(MAX_WIDTH, MAX_HEIGHT, img);
- return;
- }
- }
- //PNG -> JPEG
- function checkPNG(img) {
- if (file.type == "image/png") {
- var MAX_WIDTH = img.width;
- var MAX_HEIGHT = img.height;
- if(DEBUG) console.log("[PNGConverter] Converting PNG to JPEG");
- resizer(MAX_WIDTH, MAX_HEIGHT, img);
- }
- else {
- if(DEBUG) console.log("[PNGConverter] Image format isn't PNG.\n------<END>------");
- return;
- }
- }
- //The main resize function
- function resizer(MAX_WIDTH, MAX_HEIGHT, img, imgMD5) {
- if(DEBUG && !imgMD5) console.log("<FILTER END>");
- removePreviewOption();
- var canvas = document.createElement("canvas");
- //Input dimensions
- 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;
- }
- }
- // 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);
- //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;
- if (imgMD5) dataURL = canvas.toDataURL('image/jpeg', 92);
- else dataURL = canvas.toDataURL('image/jpeg', parseFloat(getSettings().jpegQuality));
- //dataURL to blob
- var blob = dataURItoBlob(dataURL);
- //Stop classObserver | prevent trigger loop
- classObserver.disconnect();
- if(DEBUG) console.log("[classObserver] Stopping...");
- setFile(blob, img, width, height, imgMD5);
- appendPreviewBtn(dataURL, blob.size, width, height, file.name);
- }
- //Set the new file to QR form
- function setFile(blob, img, width, height, imgMD5) {
- var detail = {
- file: blob,
- name: file.name
- };
- var event = new CustomEvent('QRSetFile', {
- bubbles: true,
- detail: detail
- });
- document.dispatchEvent(event);
- if (imgMD5) rememberQC(img, file, imgMD5, blob.size);
- if(DEBUG) console.log("[QRSetFile] File Sent");
- if(DEBUG) console.log("OUTPUT Dimesnions: " + Math.round(width) + "x" + Math.round(height));
- if(DEBUG) console.log("OUTPUT Filesize: " + formatBytes(blob.size));
- if(DEBUG) console.log("JPEG Quality: " + getSettings().jpegQuality);
- //Notification
- var FSInfo = "Original size: (" + formatBytes(file.size) + ", " + img.width + "x" + img.height + ") \n New size: (" + formatBytes(blob.size)+ ", " + Math.round(width) + "x" + Math.round(height) +")";
- if (getSettings().notify) {
- var msgDetail = {type: 'info', content: FSInfo, lifetime: 5};
- var msgEvent = new CustomEvent('CreateNotification', {bubbles: true, detail: msgDetail});
- document.dispatchEvent(msgEvent);
- }
- //Remove Quick Convert option after conversion
- removeQCOption();
- //Restart classObserver
- classObserver.observe(targetNode, observerOptions);
- //Preset priority
- complete = true;
- if(DEBUG) console.log("------<END>------\n[classObserver] Restarting...");
- }
- //Quick Convert (QC) image from Side Menu
- function quickConvert(img, file, imgMD5) {
- //Convert options container (future use)
- var container = document.createElement("div");
- container.id = "qcDiv";
- //Convert button
- var convert = document.createElement("a");
- convert.id = "quickConvert";
- convert.classList.add("sideMenuElement");
- convert.classList.add("entry");
- convert.innerHTML = "Quick Convert";
- convert.title = "Convert image to JPEG";
- //CSS on hover
- convert.onmouseover = function(){this.classList.toggle("focused")};
- convert.onmouseout = function(){this.classList.toggle("focused")};
- var hr = document.createElement("hr");
- hr.style.borderColor = getHRColor();
- //Call resizer
- convert.addEventListener('click', function(){
- if(DEBUG) console.log("[quickConvert] Manually calling Resizer...");
- resizer(img.width, img.height, img, imgMD5);
- },);
- var parent = document.getElementById("sideMenu");
- parent.appendChild(container);
- container.appendChild(hr);
- container.appendChild(convert);
- }
- //Remember button
- function rememberQC (img, file, imgMD5, newSize) {
- var container = document.createElement("div");
- container.id = "remDiv";
- var remember = document.createElement("a");
- remember.id = "rememberMD5";
- remember.classList.add("sideMenuElement");
- remember.classList.add("entry");
- remember.innerHTML = "Remember";
- remember.style.fontWeight = "bold";
- remember.title = "Always convert this image."
- //CSS on hover
- remember.onmouseover = function(){this.classList.toggle("focused")};
- remember.onmouseout = function(){this.classList.toggle("focused")};
- var hr = document.createElement("hr");
- hr.style.borderColor = getHRColor();
- remember.onclick = function(){ saveImgMD5(img, file, imgMD5, newSize) };
- var parent = document.getElementById("sideMenu");
- parent.appendChild(container);
- container.appendChild(hr);
- container.appendChild(remember);
- }
- //Preview Image button
- function appendPreviewBtn(img, pvSize, pvWidth, pvHeight, pvName) {
- var existCheck = document.getElementById("previewImg");
- if (!existCheck) {
- var preview = document.createElement("a");
- preview.id = "previewImg";
- preview.classList.add("sideMenuElement");
- preview.classList.add("entry");
- preview.innerHTML = "Preview Image";
- //CSS on hover
- preview.onmouseover = function(){this.classList.toggle("focused")};
- preview.onmouseout = function(){this.classList.toggle("focused")};
- preview.onclick = function(){ showImage(img, pvSize, pvWidth, pvHeight, pvName) };
- var parent = document.getElementById("sideMenu");
- parent.appendChild(preview);
- }
- else {
- existCheck.onclick = function(){ showImage(img, pvSize, pvWidth, pvHeight, pvName) };
- }
- return;
- }
- //Read the file
- reader.readAsDataURL(file);
- } else {
- removeQCOption();
- if(DEBUG) console.log("[Error] Invalid FileType: " + file.type + "\n------<END>------");
- }
- }, 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("------<START>------\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 {
- //Remove Side menu options upon removing a file.
- removeQCOption();
- removeRemOption();
- removePreviewOption();
- 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);
- //*************************************************************************************//
- //END OF THE MAIN PROCESS
- //*************************************************************************************//
- //Add a label with a check box for ImageResize + Setting button in Side Menu
- function appendCheckBox() {
- var settingsButton = document.createElement("a");
- var label = document.createElement("label");
- var input = document.createElement("input");
- input.type = "checkbox";
- input.id = "imgResize";
- label.id = "imgResizeLabel";
- input.title = "Enable Image Resizer";
- input.style = "margin-left: 0";
- settingsButton.classList.add("sideMenuElement");
- settingsButton.classList.add("entry");
- label.classList.add("sideMenuElement");
- //CSS on hover
- label.classList.add("entry");
- var parent = document.getElementById("sideMenu");
- parent.appendChild(label);
- label.appendChild(input);
- label.title = "Enable Image Resizer";
- label.innerHTML += " Enabled";
- settingsButton.title = "Image Resizer Settings";
- settingsButton.innerHTML = "Settings";
- parent.appendChild(settingsButton);
- //CSS on hover
- label.onmouseover = function(){this.classList.toggle("focused")};
- label.onmouseout = function(){this.classList.toggle("focused")};
- settingsButton.onmouseover = function(){this.classList.toggle("focused")};
- settingsButton.onmouseout = function(){this.classList.toggle("focused")};
- //Open settings menu
- settingsButton.onclick = function(){ document.getElementById("imgResizeOverlay").style.display = "block" };
- //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");
- //remove side menu options upon disabling ImageResizer
- removeQCOption(); removeRemOption(); removePreviewOption();
- return false;
- }
- }
- //Clears error messages <p>
- function clearErr() { document.getElementById("errMsg").innerHTML = ""; }
- //Checks for any logic errors (upscaling)
- 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 = "inline";
- 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 {
- 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 {
- 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);
- }
- }
- //Populate Presets 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 = "";
- presets.splice(delRow.parentElement.parentElement.sectionRowIndex, 1);
- localStorage.setItem("downscale-presets", JSON.stringify(presets));
- table.tBodies.item(0).innerHTML = "";
- printList();
- clearErr();
- document.getElementById("addRule").style.display = "inline";
- };
- editRow.onclick = function() { inputUI(true, rule[i], i); clearErr(); };
- //Array contents: [0] - Image type, [1] - Input width, [2] - Input height, [3] - Output width, [4] - Output height
- 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);
- }
- }
- }
- //Input field
- 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 = "Cancel";
- var saveRuleBtn = document.createElement("button");
- saveRuleBtn.innerHTML = "Save";
- input.id = "ruleInput";
- //Rules form
- input.innerHTML = '' +
- '' +
- '<select id="imgType" name="imgType" title="Input Format">' +
- '<option value="0">PNG/JPEG</option>' +
- '<option value="1">PNG</option>' +
- '<option value="2">JPEG</option>' +
- '</select> ' +
- '' +
- '<input type="number" id="inWidth" title="Input Width" size="2" min="0" value="0" onfocus="this.select();"></input> x ' +
- '' +
- '<input type="number" id="inHeight" title="Input Height" size="2" min="0" value="0" onfocus="this.select();"></input> ' +
- '  →   <input type="number" id="outWidth" title="Output Width" size="2" min="0" value="0" onfocus="this.select();"></input> x ' +
- '<input type="number" id="outHeight" title="Output Height" size="2" min="0" value="0" onfocus="this.select();"></input><br>';
- 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 = "inline"; clearErr();};
- saveRuleBtn.onclick = function() { if (edit) basicCheck(true, rulePos); else basicCheck(false); };
- }
- //Populate Quick Convert List table
- function printQCList() {
- var QCList = getQCList();
- var list = document.getElementById("QCList");
- var table = document.getElementById("QCTable");
- var filterCount = QCList.length;
- if (filterCount > 0) {
- var QCFilter = [];
- for (let i = 0; i < filterCount; i++) {
- QCFilter[i] = QCList[i].split(":");
- let delRow = document.createElement("a");
- delRow.innerHTML = "delete";
- delRow.onclick = function() {
- QCList.splice(delRow.parentElement.parentElement.sectionRowIndex, 1);
- localStorage.setItem("downscale-qclist", JSON.stringify(QCList));
- table.tBodies.item(0).innerHTML = "";
- printQCList();
- };
- //QCList Array: [0] - Filetype, [1] - Image Width, [2] - Image Height, [3] - Original Filesize, [4] - New Filesize, [5] - Filename, [6] - Image Base64 MD5 Hash
- var row = table.tBodies.item(0).insertRow(-1);
- row.insertCell(0).innerHTML = QCFilter[i][0];
- row.insertCell(1).innerHTML = '[ ' + QCFilter[i][1] + ' x ' + QCFilter[i][2] + ' ]';
- row.insertCell(2).innerHTML = QCFilter[i][3];
- row.insertCell(3).innerHTML = '→';
- row.insertCell(4).innerHTML = QCFilter[i][4];
- row.insertCell(5).innerHTML = '<p title = "' + QCFilter[i][5] +'">' + QCFilter[i][5] + '</p>';
- row.insertCell(6).appendChild(delRow);
- }
- }
- }
- //*************************************************************************************//
- // MENUS //
- //*************************************************************************************//
- 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";
- QCListDiv.className = "downscale-menu-off";
- helpDiv.className = "downscale-menu-off";
- settingsBtn.style = "font-weight: bold;";
- presetsBtn.style = "";
- QCListBtn.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";
- QCListDiv.className = "downscale-menu-off";
- helpDiv.className = "downscale-menu-off";
- settingsBtn.style = "";
- presetsBtn.style = "font-weight: bold;";
- QCListBtn.style = "";
- helpBtn.style = "";
- };
- menu.appendChild(presetsBtn);
- //Quick Convert List
- var QCListBtn = document.createElement("a");
- QCListBtn.innerHTML += "Quick Convert";
- QCListBtn.classList.add("menuBtns");
- QCListBtn.onclick = function() {
- settingsDiv.className = "downscale-menu-off";
- presetsDiv.className = "downscale-menu-off";
- QCListDiv.className = "downscale-menu-on";
- helpDiv.className = "downscale-menu-off";
- settingsBtn.style = "";
- presetsBtn.style = "";
- QCListBtn.style = "font-weight: bold;";
- helpBtn.style = "";
- };
- menu.appendChild(QCListBtn);
- //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";
- QCListDiv.className = "downscale-menu-off";
- helpDiv.className = "downscale-menu-on";
- settingsBtn.style = "";
- presetsBtn.style = "";
- QCListBtn.style = "";
- helpBtn.style = "font-weight: bold;";
- };
- menu.appendChild(helpBtn);
- var hr = document.createElement("hr");
- hr.style.borderColor = getHRColor();
- menu.appendChild(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");
- presetsDiv.style.textAlign = "center";
- content.appendChild(presetsDiv);
- //Quick Convert List
- var QCListDiv = document.createElement("div");
- QCListDiv.id = "QCListDiv";
- QCListDiv.classList.add("downscale-menu-off");
- content.appendChild(QCListDiv);
- //Help
- var helpDiv = document.createElement("div");
- helpDiv.id = "heplDiv";
- helpDiv.classList.add("downscale-menu-off");
- content.appendChild(helpDiv);
- //--------------------------------------------------------------
- var title = document.createElement("h3");
- title.innerHTML = "Image Resizer Settings";
- settingsDiv.appendChild(title);
- //Enable Resizer------------------------------------------------
- var enableDiv = document.createElement("div");
- enableDiv.classList.add("resizer-settings");
- enableDiv.innerHTML = '' +
- '<input type="checkbox" id="enableSet" title="" size="1"></input>' +
- '<label for="enableSet">Enable Resizer</label>: ' +
- 'Enable 4chan Image Resizer.';
- settingsDiv.appendChild(enableDiv);
- var enableSet = document.getElementById("enableSet");
- enableSet.checked = getSettings().enabled;
- enableSet.oninput = function() {
- //remove side menu options upon disabling ImageResizer
- if (!enableSet.checked) { removeQCOption(); removeRemOption(); removePreviewOption(); }
- var settings = getSettings();
- settings.enabled = enableSet.checked;
- document.getElementById("imgResize").checked = enableSet.checked;
- localStorage.setItem("downscale-settings", JSON.stringify(settings));
- };
- //Enable Shortcut-----------------------------------------------
- var shortcutDiv = document.createElement("div");
- shortcutDiv.classList.add("resizer-settings");
- shortcutDiv.innerHTML = '' +
- '<input type="checkbox" id="shortcutSet" title="" size="1"></input>' +
- '<label for="shortcutSet">Enable Shortcut</label>: ' +
- 'Enable "Quick Convert" shortcut. <kbd>Ctrl</kbd> + <kbd>Q</kbd>';
- settingsDiv.appendChild(shortcutDiv);
- var shortcutSet = document.getElementById("shortcutSet");
- shortcutSet.checked = getSettings().shortcut;
- shortcutSet.oninput = function() {
- var settings = getSettings();
- settings.shortcut = shortcutSet.checked;
- localStorage.setItem("downscale-settings", JSON.stringify(settings));
- };
- //Display notifications-----------------------------------------
- var notifySetDiv = document.createElement("div");
- notifySetDiv.classList.add("resizer-settings");
- notifySetDiv.innerHTML = '' +
- '<input type="checkbox" id="displaySet" title="" size="1"></input>' +
- '<label for="displaySet">Display Notifications</label>: ' +
- 'Display 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 = '' +
- '<input type="checkbox" id="convertSet" title="" size="1"></input>' +
- '<label for="convertSet">Convert All PNGs</label>: ' +
- 'Automatically convert all added 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 a 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 = '' +
- '<input type="text" id="imgQuality" title="JPEG Quality" size="1"></input>' +
- '<label for="imgQuality">JPEG Quality</label>: ' +
- 'A number between 0 and 1 indicating the output image quality.';
- 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 tableWrapper = document.createElement("div");
- tableWrapper.style.overflowY = "auto";
- tableWrapper.style.maxHeight = "220px";
- 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 = "<th>Format</th>";
- row.insertCell(1).outerHTML = "<th>Input</th>";
- row.insertCell(2).outerHTML = "<th></th>";
- row.insertCell(3).outerHTML = "<th>Output</th>";
- row.insertCell(4).outerHTML = "<th></th>";
- row.insertCell(5).outerHTML = "<th></th>";
- presetsDiv.appendChild(tableWrapper);
- tableWrapper.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"; };
- window.addEventListener('click', function(closeSettingsMenu) {
- if (closeSettingsMenu.target == overlay) overlay.style.display = "none";
- });
- addRuleBtn.onclick = function(){ inputUI(false); };
- //import/export buttons
- var bottomPresets = document.createElement("div");
- bottomPresets.style = "float: left;";
- var separator1 = document.createElement("span");
- separator1.innerHTML = " | ";
- var importPresets = document.createElement("a");
- var exportPresets = document.createElement("a");
- importPresets.innerHTML = "Import";
- exportPresets.innerHTML = "Export";
- importPresets.classList.add("menuBtns");
- bottomPresets.innerHTML += '<input id="importPresetsFile-input" type="file" accept=".json" style="display: none;" />'; //file-input
- importPresets.onclick = function(){
- document.getElementById('importPresetsFile-input').click();
- };
- exportPresets.onclick = function(){ downloadObjectAsJson(getPresets(), "4chan Image Resizer v" + version + " Presets List - " + Date.now()); }; //call file exporter
- bottomPresets.appendChild(importPresets);
- bottomPresets.appendChild(separator1);
- bottomPresets.appendChild(exportPresets);
- presetsDiv.appendChild(bottomPresets);
- //import
- document.getElementById('importPresetsFile-input').addEventListener('change', function() {
- var jsonPresetsFile = new FileReader();
- jsonPresetsFile.onload = function() {
- var originalPresets = getPresets();
- var duplicateCount1 = 0;
- var tempDuplicateCount1 = 0;
- //parse raw text
- var importedPresets = JSON.parse(jsonPresetsFile.result);
- //check if array
- if (Array.isArray(importedPresets)) {
- for (let i = 0; i < importedPresets.length; i++) {
- var line1 = importedPresets[i].split(':');
- if (line1.length != 5) {
- if(DEBUG) console.log("[Error] Imported array does not match the required length (5)");
- if(DEBUG) console.log(line1);
- alert("Error: Array length mismatch.\nThis file is either outdated or invalid.");
- return;
- }
- else {
- //check for duplicate entries
- for (let j = 0; j < originalPresets.length; j++) {
- var tempLine = line1[0] + ":" + line1[1] + ":" + line1[2] + ":" + line1[3] + ":" + line1[4];
- if (tempLine == originalPresets[j]) {
- tempDuplicateCount1++;
- break;
- }
- }
- //if not a dupe, push to the original array
- if (tempDuplicateCount1 == 0) {
- originalPresets.push(importedPresets[i]);
- }
- //count all duplicate entries
- else {
- duplicateCount1 += tempDuplicateCount1;
- tempDuplicateCount1 = 0;
- }
- }
- }
- //add the final result to local storage
- localStorage.setItem("downscale-presets", JSON.stringify(originalPresets));
- //rebuild list
- document.getElementById("ruleTable").tBodies.item(0).innerHTML = "";
- printList();
- var newEntries1 = importedPresets.length - duplicateCount1;
- alert("Succesfully imported " + importedPresets.length + " entries.\nDuplicate entries skipped: " + duplicateCount1 + "\nNew entries added: " + newEntries1);
- }
- else {
- alert("Error: Invalid data type.");
- if(DEBUG) console.log("[Error] Imported data object is not an array.")
- }
- }
- jsonPresetsFile.readAsText(this.files[0]);
- });
- //Quick Convert table | QCTable----------------------------------
- var QCTableWrapper = document.createElement("div");
- QCTableWrapper.style.overflowY = "auto";
- QCTableWrapper.style.maxHeight = "220px";
- var QCTable = document.createElement("table");
- var QCThead = document.createElement("thead");
- var QCTbody = document.createElement("tbody");
- var QCTitle = document.createElement("h3");
- QCTitle.innerHTML = "Quick Convert List";
- QCListDiv.appendChild(QCTitle);
- QCListDiv.innerHTML += "<p style='text-align: center;'>Images on this list will be automatically converted to JPEG with a quality setting of 92.</p>";
- QCTable.appendChild(QCThead);
- QCTable.appendChild(QCTbody);
- QCTable.id = "QCTable";
- var QCRow = QCThead.insertRow(0);
- QCRow.insertCell(0).outerHTML = "<th>Format</th>";
- QCRow.insertCell(1).outerHTML = "<th>Dimensions</th>";
- QCRow.insertCell(2).outerHTML = "<th>Original Size</th>";
- QCRow.insertCell(3).outerHTML = "<th></th>";
- QCRow.insertCell(4).outerHTML = "<th>New Size</th>";
- QCRow.insertCell(5).outerHTML = "<th>Filename</th>";
- QCRow.insertCell(6).outerHTML = "<th></th>";
- QCListDiv.appendChild(QCTableWrapper);
- QCTableWrapper.appendChild(QCTable);
- //import/export buttons
- var bottomQCL = document.createElement("div");
- bottomQCL.style = "padding-top: 1em;";
- var separator2 = document.createElement("span");
- separator2.innerHTML = " | ";
- var importQCList = document.createElement("a");
- var exportQCList = document.createElement("a");
- importQCList.innerHTML = "Import";
- exportQCList.innerHTML = "Export";
- importQCList.classList.add("menuBtns");
- bottomQCL.innerHTML += '<input id="importQCLFile-input" type="file" accept=".json" style="display: none;" />'; //file-input
- importQCList.onclick = function(){
- document.getElementById('importQCLFile-input').click();
- };
- exportQCList.onclick = function(){ downloadObjectAsJson(getQCList(), "4chan Image Resizer v" + version + " Quick Convert List - " + Date.now()); }; //call file exporter
- bottomQCL.appendChild(importQCList);
- bottomQCL.appendChild(separator2);
- bottomQCL.appendChild(exportQCList);
- QCListDiv.appendChild(bottomQCL);
- //import
- document.getElementById('importQCLFile-input').addEventListener('change', function() {
- var jsonFile = new FileReader();
- jsonFile.onload = function() {
- var originalQCL = getQCList();
- var duplicateCount2 = 0;
- var tempDuplicateCount2 = 0;
- //parse raw text
- var importedQCL = JSON.parse(jsonFile.result);
- //check if array
- if (Array.isArray(importedQCL)) {
- for (let i = 0; i < importedQCL.length; i++) {
- var line = importedQCL[i].split(':');
- if (line.length != 7 || line[6].length != 32) {
- if(DEBUG) console.log("[Error] Imported array does not match the required length (7) or contains an invalid MD5 hash.");
- if(DEBUG) console.log(line);
- alert("Error: Array length mismatch.\nThis file is either outdated or invalid.");
- return;
- }
- else {
- //check for duplicate MD5 hashes
- for (let j = 0; j < originalQCL.length; j++) {
- var originalLine2 = originalQCL[j].split(':');
- if (line[6] == originalLine2[6]) {
- tempDuplicateCount2++;
- break;
- }
- }
- //if not a dupe, push to the original array
- if (tempDuplicateCount2 == 0) {
- originalQCL.push(importedQCL[i]);
- }
- //count all duplicate entries
- else {
- duplicateCount2 += tempDuplicateCount2;
- tempDuplicateCount2 = 0;
- }
- }
- }
- //add the final result to local storage
- localStorage.setItem("downscale-qclist", JSON.stringify(originalQCL));
- //rebuild list
- document.getElementById("QCTable").tBodies.item(0).innerHTML = "";
- printQCList();
- var newEntries2 = importedQCL.length - duplicateCount2;
- alert("Succesfully imported " + importedQCL.length + " entries.\nDuplicate entries skipped: " + duplicateCount2 + "\nNew entries added: " + newEntries2);
- }
- else {
- alert("Error: Invalid data type.");
- if(DEBUG) console.log("[Error] Imported data object is not an array.")
- }
- }
- jsonFile.readAsText(this.files[0]);
- });
- //delete all QCL entries
- var delAll = document.createElement("a");
- var emptyArray = [];
- delAll.innerHTML = "Delete All";
- delAll.style = "float: right; margin-right: 1em;";
- delAll.onclick = function(){
- if (confirm(" WARNING!\nAre you sure you want to DELETE ALL entries from the \"Quick Convert List\"?")) {
- localStorage.setItem("downscale-qclist", JSON.stringify(emptyArray));
- document.getElementById("QCTable").tBodies.item(0).innerHTML = "";
- }
- };
- bottomQCL.appendChild(delAll);
- //INITIAL PRINT OF QUICK CONVERT LIST
- printQCList();
- //Help----------------------------------------------------------
- var helpTitle = document.createElement("h3");
- helpTitle.innerHTML = "About";
- helpDiv.appendChild(helpTitle);
- var rant = document.createElement("p");
- rant.innerHTML = '<strong>4chan Image <span style="text-decoration-line: line-through;">Resizer</span></strong> <s>Downscaler</s> automatically downscales images based on custom presets. Originally developed to downscale anime/vidya screenshots "on the fly".<br><br>' +
- '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 add an image to a quick reply form. ' +
- '<br>If it meets any of the presets input requirements, the image will be automatically downscaled to specified dimensions as a <strong>JPEG</strong>. ' +
- '<br><br><strong>Note</strong> that output dimensions are constrained by input dimensions <strong>aspect ratio</strong>. ' +
- '<br><strong>Also note</strong> that <strong>setting JPEG output quality to 1</strong> may result in filesizes larger than that of the original image, and should be considered as a placebo.' +
- '<br><br><strong> "Quick Convert"</strong> allows you to quickly convert images (PNG/JPEG) to JPEG at a quality of 92.' +
- '<br>This is very useful when an image exceeds 4chan image size limit of <strong>4 MB</strong>.' +
- '<br>It works well on super high resolution images (+3000px), sometimes drastically cutting the filesize without any noticeble quality loss.' +
- ' However, <strong>it is not recommended to use it on grayscale PNG images</strong>, i.e. manga pages, because most of the time <strong>it will result in larger than original filesizes</strong>.' +
- '<br>Once you are satisfied with the <strong>"Quick Convert"</strong> results, you can click <strong>"Remember"</strong> on the side menu to add the image MD5 hash to the <strong>"Quick Convert List"</strong>, which will always automatically convert this image for you.' +
- '<br><br><span style="font-weight:bold; color: red;">*NEW*</span><br> Added <strong>Import/Export</strong> feature for both lists. <strong>Import</strong> works by merging list entries instead of overwriting them, so you can export/import items between domains without any worry.' +
- '<br>Added a <kbd>Ctrl</kbd> + <kbd>Q</kbd> keyboard shortcut for <strong>"Quick Convert"</strong> to actually make it quick. Press again to <strong>"Remember"</strong> the image.' +
- '<br><br><div style="float: right;" >[ <a href="https://greasyfork.org/en/scripts/391758-4chan-image-resizer" target="_blank">version ' + version + '</a> ]</div>';
- helpDiv.appendChild(rant);
- }
- //Only when QR form is open.
- function appendSideMenu() {
- //Arrow | sideMenuArrow----------------------------------------------------------
- var arrow = document.createElement("a");
- arrow.id = "sideMenuArrow";
- arrow.title = "Side Menu";
- arrow.style.cursor = "pointer";
- arrow.innerHTML = "◀";
- var arrowRef = document.getElementById("autohide");
- arrowRef.parentNode.insertAdjacentElement("beforebegin", arrow);
- arrow.onclick = function(){ sideMenu.classList.toggle("downscale-menu-on"); };
- //Side Menu | sideMenu----------------------------------------------------------
- var sideMenu = document.createElement("div");
- sideMenu.id = "sideMenu";
- sideMenu.classList.add("dialog");
- var sideMenuRef = document.getElementById("qr");
- sideMenuRef.insertAdjacentElement("afterbegin", sideMenu);
- //Close side menu dialog by clicking anywhere but here:
- window.addEventListener('click', function(event) {
- var getSideMenu = document.getElementById("sideMenu");
- if (!event.target.matches('#sideMenuArrow') &&
- !event.target.matches('#sideMenu') &&
- !event.target.matches('#imgResize') &&
- !event.target.matches('#quickConvert') &&
- !event.target.matches('#imgResizeLabel')) {
- if (getSideMenu.classList.contains('downscale-menu-on')) getSideMenu.classList.remove('downscale-menu-on');
- }
- });
- }
- appendSettings();
- //*************************************************************************************//
- //END OF MENUs //
- //*************************************************************************************//
- //Saves image details to local storage
- function saveImgMD5 (img, file, imgMD5, newSize) {
- removeRemOption();
- var QCList = getQCList();
- //"file/jpeg" -> "JPEG"
- var filetype = file.type.split("/").pop().toUpperCase();
- //remove filetype
- var filename = file.name.split(".").slice(0,-1).join(".");
- //replace seperators
- filename = filename.replace(/:/g,"_");
- var orig_filesize = formatBytes(file.size);
- var new_filesize = formatBytes(newSize);
- //QCList Array [0] - Filetype, [1] - Image Width, [2] - Image Height, [3] - Original Filesize, [4] - New Filesize, [5] - Filename, [6] - Image Base64 MD5 Hash
- var QCString = filetype + ":" + img.width + ":" + img.height + ":" + orig_filesize + ":" + new_filesize + ":" + filename + ":" + imgMD5;
- QCList.push(QCString);
- localStorage.setItem("downscale-qclist", JSON.stringify(QCList));
- //Show notification
- var info = file.name + '\nAdded to the "Quick Convert List"';
- var msgDetail = {type: 'info', content: info, lifetime: 5};
- var msgEvent = new CustomEvent('CreateNotification', {bubbles: true, detail: msgDetail});
- document.dispatchEvent(msgEvent);
- //rebuild list
- document.getElementById("QCTable").tBodies.item(0).innerHTML = "";
- printQCList();
- }
- //Removes these Side Menu options
- function removeQCOption() {
- var checkQC = document.getElementById("qcDiv");
- if (checkQC) checkQC.remove();
- }
- function removeRemOption() {
- var checkRem = document.getElementById("remDiv");
- if (checkRem) checkRem.remove();
- }
- function removePreviewOption() {
- var checkPreview = document.getElementById("previewImg");
- if (checkPreview) checkPreview.remove();
- }
- //Get border color for <hr> hack
- function getHRColor () {
- var sample = document.getElementById("imgResizeMenu");
- return window.getComputedStyle(sample, null).getPropertyValue("border-bottom-color");
- }
- //Image viewer
- function showImage(img, size, width, height, filename) {
- var overlay = document.createElement("div");
- overlay.id = "pvOverlay";
- //-----------------------------------------------
- var pvHeader = document.createElement("div");
- pvHeader.id = "pvHeader";
- pvHeader.className = "dialog";
- //opacity hack
- pvHeader.classList.add("pvOpct");
- pvHeader.innerHTML = filename + "<br>(" + formatBytes(size)+ ", " + Math.round(width) + "x" + Math.round(height) + ")";
- //-----------------------------------------------
- var closePv = document.createElement("a");
- closePv.className = "close fa fa-times";
- closePv.style = "float: right;";
- closePv.onclick = function(){ overlay.remove(); };
- //-----------------------------------------------
- var pvImg = document.createElement("img");
- pvImg.id = "pvImg";
- pvImg.classList.add("centerImg");
- pvImg.title = "Click to close";
- pvImg.src = img;
- pvImg.onclick = function(){ overlay.remove(); };
- //-----------------------------------------------
- document.body.appendChild(overlay);
- //overlay.appendChild(closePv);
- overlay.appendChild(pvImg);
- overlay.appendChild(pvHeader);
- //opacity hack
- setTimeout(function() { pvHeader.classList.toggle("pvOpct"); }, 2000);
- }
- //json file exporter
- function downloadObjectAsJson(exportObj, exportName) {
- var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(exportObj));
- var downloadAnchorNode = document.createElement('a');
- downloadAnchorNode.setAttribute("href", dataStr);
- downloadAnchorNode.setAttribute("download", exportName + ".json");
- document.body.appendChild(downloadAnchorNode);
- downloadAnchorNode.click();
- downloadAnchorNode.remove();
- }
- //Prevent multiple event listeners
- var scListenerExists = false;
- //Quick Convert shortcut | Ctrl+Q
- if (getSettings().shortcut && !scListenerExists) { document.addEventListener('keyup', qCShortcut); scListenerExists = true ; }
- function qCShortcut(e) {
- var convertBtn = document.getElementById("quickConvert");
- var rememberBtn = document.getElementById("rememberMD5");
- //if shortcut is enabled, simulate clicks
- if (getSettings().shortcut) {
- if (e.ctrlKey && e.keyCode == 81 && convertBtn) {
- convertBtn.click();
- }
- else if (e.ctrlKey && e.keyCode == 81 && rememberBtn) {
- rememberBtn.click();
- }
- }
- }
- //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];}