4chan Image Resizer

Automatically downscales images based on custom presets. Requires 4chan X.

当前为 2020-09-19 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         4chan Image Resizer
// @namespace    https://greasyfork.org/en/users/393416
// @version      2.0.1
// @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/hQp5BTf.png
// ==/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; }\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' +
    '#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 dialog 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("Acceptable File type: " + 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("<FILTER START>");
                        if(DEBUG) console.log("INPUT Dimensions: " + img.width + "x" + img.height);
                        if(DEBUG) console.log("INPUT File size: " + formatBytes(file.size));
                        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;
                            }
                            //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 (!getSettings().convert && (matchCount == 0 || matchCount > 1)) {
                            if(DEBUG) console.log("Image didn't match any presets.\n<END>");
                            return;
                        }
                        //PNG -> JPEG
                        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<END>");
                                return;
                            }
                        }
                        if(DEBUG) console.log("<FILTER END>");
                        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;
                            }
                        }
                        // 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));
                        //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: file.name
                        };
                        var event = new CustomEvent('QRSetFile', {
                            bubbles: true,
                            detail: detail
                        });
                        document.dispatchEvent(event);
                        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);
                        }
                        //Restart classObserver
                        classObserver.observe(targetNode, observerOptions);
                        if(DEBUG) console.log("<END> \n[classObserver] Restarting...");
                    }
                }
                // Read the file
                reader.readAsDataURL(file);
            } else {
                if(DEBUG) console.log("[Error] Presets not found.\n<END>");
            }
        } else {
                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 {
            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 <p>
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 {
            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 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 = ""; 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 = '&#8594;';
            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 = "Close";
    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>&ensp;' +
        '' +
        '<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> ' +
        '&ensp; &#8594; &ensp; <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 = "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 = '' +
        '<input type="checkbox" id="enableSet" title="" size="1"></input>' +
        '<label for="enableSet">Enable Resizer</label>:&ensp;' +
        '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 = '' +
        '<input type="checkbox" id="displaySet" title="" size="1"></input>' +
        '<label for="displaySet">Display Notifications</label>:&ensp;' +
        '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 = '' +
        '<input type="checkbox" id="convertSet" title="" size="1"></input>' +
        '<label for="convertSet">Convert All PNGs</label>:&ensp;' +
        '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 = '' +
        '<input type="text" id="imgQuality" title="JPEG Quality" size="1"></input>' +
        '<label for="imgQuality">JPEG Quality</label>:&ensp;' +
        '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 = "<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(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 = '<span style="font-weight:bold;">4chan Image <span style="text-decoration-line: line-through;">Resizer</span></span> <s>can\'t upscale</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 submit (drag & drop) 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 <span style="font-weight:bold;">JPEG</span>. ' +
        '<br><br><span style="font-weight:bold;">Note</span> that output dimensions are constrained by input dimensions <span style="font-weight:bold;">aspect ratio</span>. ' +
        '<br><span style="font-weight:bold;">Also note</span> that <span style="font-weight:bold;">setting JPEG output quality to 1</span> 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]}