4chan Image Resizer

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

目前为 2020-05-29 提交的版本。查看 最新版本

// ==UserScript==
// @name         4chan Image Resizer
// @namespace    https://gf.qytechs.cn/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
// ==/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("<FILTER START>");
                        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<END>");
                            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<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;
                            }
                        }
                        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("<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 {
            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 = '&#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 = "Discard";
    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]}

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址