import Mona from "./wslib-mona"
import { englishPage } from "./language"

async function handleRequest(request) {
    let {pathname, host} = new URL(request.url.toLowerCase())
    if (pathname.endsWith('/'))  pathname = pathname.substring(0, pathname.length - 1)

    if (pathname === '/mona-examples.html') {
        let page = englishPage(request) ? HOMEPAGE_EN : HOMEPAGE
        page = page.replace('$[{host}]', host)
        return new Response(page, {headers: {"Content-Type": "text/html; charset=utf-8"}})
    } else {
        return handleResize(request)
    }
}

async function handleResize(request) {
    const data = await request.json()

    // Parameter check
    if (!data.url) {
      return new Response('Please enter the image URL!')
    }

    if (data.operations.length == 0) {
        return new Response('Please select an operation!')
    }

    // Only one 'Original Image' operation, return the original image.
    if (data.operations.length == 1 && data.operations[0].operation == "original") {
        return fetch(data.url, {headers: request.headers, cdnProxy: false})
    }

    // Create a Mona instance
    const mona = new Mona()
    
    // Apply all operation parameters
    // In actual coding, you can use method chaining to complete the following operations in one line:
    // return await mona.gray().blur(2, 4).optQuality().get(data.url, {headers: request.headers})
    for (const op of data.operations) {
        const {operation, params} = op
        switch (operation) {
            case "extract":
                mona.extract(params.start, params.end)
                break
            case "progressive":
                mona.progressive(params.percent)
                break
            case "skip":
                mona.skip()
                break
            case "bgcolor":
                mona.bgcolor(params.bgcolor)
                break
            case "border":
                mona.border(params.width, params.color)
                break
            case "composeXY":
                mona.composeXY(params.x, params.y, params.image, params.t)
                break
            case "composeWH":
                mona.composeWH(params.w, params.h, params.image, params.t)
                break
            case "info":
                mona.info(params.type)
                break
            case "average-hue":
                mona.averageHue()
                break
            case "interlace":
                mona.interlace()
                break
            // Watermark
            case "watermarkImage":
                mona.watermarkImage(params.image, 
                                    params.transparency, 
                                    params.direction, 
                                    params.dx, 
                                    params.dy, 
                                    params.percent)
                break
            case "watermarkText":
                mona.watermarkText(params.text, 
                                   params.transparency, 
                                   params.direction, 
                                   params.dx, 
                                   params.dy, 
                                   params.percent,
                                   params.font,
                                   params.color,
                                   params.size,
                                   params.rotate,
                                   params.strokeColor,
                                   params.strokeThickness)
                break
            case "watermarkImageText":
                mona.watermarkImageText(params.image,
                                        params.text, 
                                        params.transparency, 
                                        params.direction, 
                                        params.dx, 
                                        params.dy, 
                                        params.percent,
                                        params.font,
                                        params.color,
                                        params.size,
                                        params.rotate,
                                        params.strokeColor,
                                        params.strokeThickness)
                break
            case "watermarkTextImage":
                mona.watermarkTextImage(params.text,
                                        params.image, 
                                        params.transparency, 
                                        params.direction, 
                                        params.dx, 
                                        params.dy, 
                                        params.percent,
                                        params.font,
                                        params.color,
                                        params.size,
                                        params.rotate,
                                        params.strokeColor,
                                        params.strokeThickness)
                break
            case "annimg":
                mona.annimg()
                break
            // Image cropping
            case "anchorCrop":
                mona.cropByAnchor(params.x, params.y, params.w, params.h)
                break
            case "centerCrop":
                mona.cropByCenter(params.w, params.h)
                break
            case "gravityCrop":
                mona.cropByGravity(params.x, params.y, params.w, params.h, params.position)
                break
            case "indexCrop":
                mona.cropByIndex(params.x, params.y, params.index)
                break
            case "inscribedCircleCrop":
                mona.cropToInscribedCircle(params.radius)
                break
            case "squareEnlargeCrop":
                mona.cropBySquareEnlarge(params.square, params.enlarge)
                break
            case "extendCrop":
                mona.cropByExtend(params.w, params.h)
                break
            case "percentageCrop":
                mona.cropByPercentage(params.x, params.y, params.w, params.h)
                break
            case "scaleGravityCrop":
                mona.scaleAndCropByGravity(params.w, params.h, params.position)
                break
            case "scaleAnchorCrop":
                mona.scaleAndCropByAnchor(params.x, params.y, params.w, params.h)
                break
            case "format":
                mona.format(params.format)
                break
            // Image scaling
            case "stretch":
                mona.stretch(params.w, params.h, params.sh)
                break;
            case "scaleByPercentage":
                mona.scaleByPercentage(params.w, params.h, params.sh)
                break;
            case "scaleToFit":
                mona.scaleToFit(params.w, params.h, params.color)
                break;
            case "scaleAndCrop":
                mona.scaleAndCrop(params.w, params.h)
                break;
            case "scaleDownTo":
                mona.scaleDownTo(params.w, params.h, params.l, params.s, params.sh)
                break;
            case "scaleDownToMinimum":
                mona.scaleDownToMinimum(params.w, params.h)
                break;
            case "scaleUpProportionally":
                mona.scaleUpProportionally(params.w, params.h)
                break;
            case "scaleProportionally":
                mona.scaleProportionally(params.a)
                break;
            case "restrictDimensions":
                mona.restrictDimensions(params.w, params.h)
                break;
            // Image effects
            case "blur":
                mona.blur(params.radius, params.sigma)
                break
            case "sharpen":
                mona.sharpen(params.sharpen)
                break
            case "opt-quality":
                mona.optQuality()
                break
            case "contrast":
                mona.contrast(params.contrast)
                break
            case "bright":
                mona.contrast(params.bright)
                break
            case "gray":
                mona.gray()
                break
            // Image rotation
            case "autoRotate":
                mona.autoRotate(params.o)
                break
            case "rotate":
                mona.rotate(params.r)
                break
            // Image compression
            case "compressAbsolute":
                mona.compressAbsolute(params.q)
                break
            case "compressRelative":
                mona.compressRelative(params.Q)
                break
            case "compressLossy":
                mona.compressLossy()
                break
            default:
                break
        }
    }

    // Send an operation request to Mona using GET method
    let resp = await mona.get(data.url, {headers: request.headers})

    // Send an operation request to Mona using POST method
    // let resp = await fetch(data.url, {headers: request.headers, cdnProxy: false})
    // const headers = {"content-type": resp.headers.get("content-type")}
    // resp = await mona.post(data.url, {headers: headers, body: await resp.arrayBuffer()})

    if (resp.status != 200) {
        throw Error(`[[--操作参数--]]${mona.argString()}[[--状态信息--]]: ${resp.status} ${resp.statusText}[[--错误信息--]]${resp.headers.get("Ws-Eca-Err-Msg")}`)
    } 

    return resp
}



const HOMEPAGE_EN = `
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>MONA Image Processing Example</title>
    <style>
        body {
            background-color: #f3f3f3;
            display: flex;
            justify-content: center;
            font-family: Arial, sans-serif;
            padding-top: 20px; /* Add some top padding to avoid content sticking to the browser edge */
            // min-height: 50vh; /* Ensure the body has at least the height of the viewport */
        }

        .container {
            border: 1px solid #ccc;
            padding: 20px;
            background-color: #fff;
        }

        .input-container {
            margin-bottom: 20px;
        }

        .input-container label {
            display: block;
            margin-bottom: 5px;
        }

        .input-container input {
            width: 666px;
            padding: 5px;
            font-size: 14px;
        }

        .button {
            background-color: #0088cc;
            color: #fff;
            padding: 10px;
            border: none;
            cursor: pointer;
            border-radius: 4px;
            font-size: 14px;
        }

        .image-container {
            margin-top: 20px;
        }

        .image-container img {
            max-width: 100%;
            height: auto;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="input-container">
            <label for="imageUrl">Original Image URL (Our nodes may not be able to access)</label>
            <input type="text" id="imageUrl" value="http://$[{host}]/ECA-test/pet-shop-website-template/img/blog-2.jpg">
        </div>
        <div class="input-container">
            <label for="operation">Select Image Operation (Non-conflicting operations can be stacked)</label>
            <select id="operation" onchange="handleOperationChange()">
                <option value="original">Original Image</option>
                <option value="extract">GIF Frame Extraction</option>
                <option value="progressive">Progressive JPEG</option>
                <option value="skip">Skip</option>
                <option value="bgcolor">Background Color</option>
                <option value="border">Border</option>
                <option value="composeXY">Overlay XY</option>
                <option value="composeWH">Overlay WH</option>
                <option value="info">Get Info</option>
                <option value="average-hue">Get Main Hue</option>
                <option value="interlace">Interlace</option>
                <optgroup label="Watermark">
                    <option value="watermarkImage">1. Image Watermark</option>
                    <option value="watermarkText">2. Text Watermark</option>
                    <option value="watermarkImageText">3. Image and Text Watermark</option>
                    <option value="watermarkTextImage">4. Text and Image Watermark</option>
                    <option value="annimg">5. Annotation Watermark</option>
                </optgroup>
                <optgroup label="Image Cropping">
                    <option value="anchorCrop">1. Anchor Crop</option>
                    <option value="centerCrop">2. Center Crop</option>
                    <option value="gravityCrop">3. Gravity Crop</option>
                    <option value="indexCrop">4. Index Crop</option>
                    <option value="inscribedCircleCrop">5. Inscribed Circle Crop</option>
                    <option value="squareEnlargeCrop">6. Square Enlarge Crop</option>
                    <option value="extendCrop">7. Extend Crop</option>
                    <!--option value="gravityCropAndFill">8. Gravity Crop and Fill</option-->
                    <option value="percentageCrop">9. Percentage Crop</option>
                    <option value="scaleGravityCrop">10. Scale Gravity Crop</option>
                    <option value="scaleAnchorCrop">11. Scale Anchor Crop</option>
                </optgroup>
                <option value="format">Image Format Conversion</option>
                <optgroup label="Image Scaling">
                    <option value="stretch">1. Stretch</option>
                    <option value="scaleByPercentage">2. Scale by Percentage</option>
                    <option value="scaleToFit">3. Scale to Fit</option>
                    <option value="scaleAndCrop">4. Scale and Crop</option>
                    <option value="scaleDownTo">5. Scale Down Proportionally (Not Exceeding Specified Width and Height)</option>
                    <option value="scaleDownToMinimum">6. Scale Down Proportionally (Not Less Than Specified Width and Height)</option>
                    <option value="scaleUpProportionally">7. Scale Up Proportionally</option>
                    <option value="scaleProportionally">8. Scale Proportionally</option>
                    <option value="restrictDimensions">9. Restrict Dimensions</option>
                </optgroup>
                <optgroup label="Image Effects">
                    <option value="blur">1. Blur</option>
                    <option value="sharpen">2. Sharpen</option>
                    <option value="opt-quality">3. Improve Scaled Image Quality</option>
                    <option value="contrast">4. Image Contrast</option>
                    <option value="bright">5. Image Brightness</option>
                    <option value="gray">6. Grayscale</option>
                </optgroup>
                <optgroup label="Image Rotation">
                    <option value="autoRotate">1. Auto Rotate</option>
                    <option value="rotate">2. Rotate</option>
                </optgroup>
                <optgroup label="Image Compression">
                    <option value="compressAbsolute">1. Absolute Compression for JPG/WebP</option>
                    <option value="compressRelative">2. Relative Compression for JPG/WebP</option>
                    <option value="compressLossy">3. PNG Compression</option>
                </optgroup>
            </select>
        </div>
        <div class="input-container">
            <label id="operationDesc" style="color:gray;word-wrap:break-word;max-width:500px;">Display Original Image</label>
        </div>
        <div class="input-container" id="operationParams">
            <!-- Input fields for operation parameters will be dynamically added here based on selection -->
        </div>
        <div class="input-container">
            <label for="imageUrl">Image Operation List:</label>
            <ol id="operationList"></ol>
        </div>
        <div class="input-container">
            <button class="button" onclick="addOperation()">Add Operation</button>
            <button class="button" onclick="clearOperation()">Clear Operations</button>
            <button class="button" onclick="executeOperation()">Execute Operation</button>
        </div>
        <div class="input-container">
            <label for="imageUrl">Response Image Information:</label>
            <ul id="imageInfo"></ul>
        </div>
        <div class="image-container" id="resultContainer"></div>
    </div>

    <script>
        var operations = []
        function handleOperationChange() {
            var operation = document.getElementById("operation").value;
            var operationParamsContainer = document.getElementById("operationParams");
            operationParamsContainer.innerHTML = ""; // Clear any existing parameter input controls

            switch (operation) {
                case "original":
                    addDescription("Display Original Image");
                    break;
                case "extract":
                    addDescription("Extract frames from animated images");
                    addInput("extractStart", "Start frame index for extraction. >= 0, integer");
                    addInput("extractEnd", "End frame index for extraction. >= 0, integer");
                    break;
                case "progressive":
                    addDescription("Convert JPEG (Baseline JPEG) to Progressive JPEG");
                    addInput("progressive", "Use the percent value of the original image for the blurry image. (0, 100], integer");
                    break;
                case "skip":
                    addDescription("If a parameter parsing fails or image processing fails, do not return 404 directly, but continue to process the remaining parameters and operations");
                    break;
                case "bgcolor":
                    addDescription("Set the background color for the extra blank area created by rotation or other operations");
                    addInput("bgcolor", "Word, ~Hexadecimal");
                    break;
                case "border":
                    addDescription("Set the image border");
                    addInput("borderWidth", "Specify the border width. [1, +∞), integer. Default value is 1");
                    addInput("borderColor", "Specify the border color. Word, ~Hexadecimal. Default value is ~FFFFFF");
                    break;
                case "composeXY":
                    addDescription("Combine two images, one stacked on top of the other. Specify the x and y coordinates of the bottom image as the starting position for stacking the top image");
                    addInput("composeX", "Specify the x-coordinate of the bottom image for the starting position of stacking the top image. [0, +∞), integer");
                    addInput("composeY", "Specify the y-coordinate of the bottom image for the starting position of stacking the top image. [0, +∞), integer");
                    addInput("composeImage", "Specify the URL of the top image, in plain text.");
                    addInput("composeT", "Specify the transparency of the top image. [0, 100]. Default value is 100");
                    break;
                case "composeWH":
                    addDescription("Combine two images, one stacked on top of the other. Specify the width and height percentage of the bottom image as the starting position for stacking the top image");
                    addInput("composeW", "Specify the width percentage of the bottom image for the starting position of stacking the top image. [0, 100], integer");
                    addInput("composeH", "Specify the height percentage of the bottom image for the starting position of stacking the top image. [0, 100], integer");
                    addInput("composeImage", "Specify the URL of the top image, in plain text.");
                    addInput("composeT", "Specify the transparency of the top image. [0, 100]. Default value is 100");
                    break;
                case "info":
                    addDescription("Get basic information or exif information of the image");
                    addInput("infoType", "0: Do not get information, 1: Get basic image information, 2: Get image exif information. Default value is 0");
                    break;
                case "average-hue":
                    addDescription("Get the dominant hue of the image");
                    break;
                case "interlace":
                    addDescription("First display a blurry outline of the whole image, and then gradually load until the full image is shown. Supports jpg and png formats");
                    break;
                // Watermark
                case "watermarkImage":
                    addDescription("Image watermark");
                    addInput("wmImage", "URL of the watermark image");
                    addInput("wmTransparency", "Transparency. [0, 100], integer. Default 100");
                    addInput("wmDirection", "Watermark position. [nw, north, ne, west, center, east, sw, south, se]. Default se");
                    addInput("wmDX", "Horizontal margin. Greater than 0, unit: pixel. Default 10");
                    addInput("wmDY", "Vertical margin. Greater than 0, unit: pixel. Default 10");
                    addInput("wmPercent", "Adaptive percentage. [0, 100], integer. Default 0");
                    break;
                case "watermarkText":
                    addDescription("Text watermark");
                    addInput("wmText", "Text content");
                    addInput("wmTransparency", "Transparency. [0, 100], integer. Default 100");
                    addInput("wmDirection", "Watermark position. [nw, north, ne, west, center, east, sw, south, se]. Default se");
                    addInput("wmDX", "Horizontal margin. Greater than 0, unit: pixel. Default 10");
                    addInput("wmDY", "Vertical margin. Greater than 0, unit: pixel. Default 10");
                    addInput("wmPercent", "Adaptive percentage. [0, 100], integer. Default 0");
                    addInput("wmFont", "Font name in English. Default simhei");
                    addInput("wmColor", 'Text color. Format supports "blue", "#0000ff", "rgb(0,0,255)". Default white');
                    addInput("wmSize", "Text size. (0, 1000], integer. Default 30");
                    addInput("wmRotate", "Text rotation angle. [0, 360], integer. Default 0");
                    addInput("wmStrokeColor", 'Stroke color. Format supports "blue", "#0000ff", "rgb(0,0,255)". Must be explicitly set, otherwise not effective');
                    addInput("wmStrokeThickness", "Stroke thickness of the watermark text. When bc is absent, bs is ineffective. Default 1");
                    break;
                case "watermarkImageText":
                case "watermarkTextImage":
                    if (operation == "watermarkImageText") {
                        addDescription("Mixed watermark, image first then text");
                    } else {
                        addDescription("Mixed watermark, text first then image");
                    }
                    addInput("wmImage", "URL of the watermark image");
                    addInput("wmText", "Text content");
                    addInput("wmTransparency", "Transparency. [0, 100], integer. Default 100");
                    addInput("wmDirection", "Watermark position. [nw, north, ne, west, center, east, sw, south, se]. Default se");
                    addInput("wmDX", "Horizontal margin. Greater than 0, unit: pixel. Default 10");
                    addInput("wmDY", "Vertical margin. Greater than 0, unit: pixel. Default 10");
                    addInput("wmPercent", "Adaptive percentage. [0, 100], integer. Default 0");
                    addInput("wmFont", "Font name in English. Default simhei");
                    addInput("wmColor", 'Text color. Format supports "blue", "#0000ff", "rgb(0,0,255)". Default white');
                    addInput("wmSize", "Text size. (0, 1000], integer. Default 30");
                    addInput("wmRotate", "Text rotation angle. [0, 360], integer. Default 0");
                    addInput("wmStrokeColor", 'Stroke color. Format supports "blue", "#0000ff", "rgb(0,0,255)". Must be explicitly set, otherwise not effective');
                    addInput("wmStrokeThickness", "Stroke thickness of the watermark text. When bc is absent, bs is ineffective. Default 1");
                    break;
                // Image Cropping
                case "anchorCrop":
                    addDescription("Start from the top-left corner of the original image, use anchor coordinates (x, y) to crop an image of specified width and height (w, h). If the width (or height) from the starting point exceeds the original image, it will crop directly to the end of the original image. If the starting point exceeds the original image, it returns 404 (squid returns the original image)");
                    addInput("cropX", "Starting anchor x-coordinate. [0, +∞), integer. Default 0");
                    addInput("cropY", "Starting anchor y-coordinate. [0, +∞), integer. Default 0");
                    addInput("cropW", "Width resolution of the cropped image. [0, +∞), integer");
                    addInput("cropH", "Height resolution of the cropped image. [0, +∞), integer");
                    break;
                case "centerCrop":
                    addDescription("Use the center point of the original image as the center point of the cropped image, crop an image of specified width and height (w, h). If w or h is invalid, it returns 404 (squid returns the original image)");
                    addInput("cropW", "Width resolution of the cropped image. [0, +∞), integer");
                    addInput("cropH", "Height resolution of the cropped image. [0, +∞), integer");
                    break;  
                case "gravityCrop":
                    addDescription("Crop an image of specified width and height (w, h) based on the position set by the parameter position and offset by x and y");
                    addInput("cropX", "Starting anchor x-coordinate. [0, +∞), integer. Default 0");
                    addInput("cropY", "Starting anchor y-coordinate. [0, +∞), integer. Default 0");
                    addInput("cropW", "Width resolution of the cropped image. [0, +∞), integer");
                    addInput("cropH", "Height resolution of the cropped image. [0, +∞), integer");
                    addInput("cropPosition", "Position in the nine-grid. [nw, north, ne, west, center, east, sw, south, se]");
                    break; 
                case "indexCrop":
                    addDescription("Cut the original image along the x-axis (or y-axis) at the specified length, and get the image of the specified index position");
                    addInput("cropX", "Starting anchor x-coordinate. [0, +∞), integer. Default 0");
                    addInput("cropY", "Starting anchor y-coordinate. [0, +∞), integer. Default 0");
                    addInput("cropIndex", "Specified index position. [0, +∞), integer. Default 0");
                    break;
                case "inscribedCircleCrop":
                    addDescription("Crop the original image into a circular pattern");
                    addInput("cropRadius", "Radius of the inscribed circle. [0, +∞), integer. Default 0");
                    break;
                case "squareEnlargeCrop":
                    addDescription("Crop the original image into a square and enlarge it according to the longest side of the original image");
                    addInput("cropSquare", "Whether to crop into a square. 0, 1");
                    addInput("cropEnlarge", "Whether to enlarge according to the longest side of the original image. 0, 1");
                    break;
                case "extendCrop":
                    addDescription("Center crop, supports padding");
                    addInput("cropW", "Width resolution of the cropped image. [0, +∞), integer");
                    addInput("cropH", "Height resolution of the cropped image. [0, +∞), integer");
                    break;
                case "percentageCrop":
                    addDescription("Crop the image according to the specified width and height percentage");
                    addInput("cropX", "Starting anchor x-coordinate. [0, +∞), integer. Default 0");
                    addInput("cropY", "Starting anchor y-coordinate. [0, +∞), integer. Default 0");
                    addInput("cropW", "Width resolution of the cropped image. [0, +∞), integer");
                    addInput("cropH", "Height resolution of the cropped image. [0, +∞), integer");
                    break;
                case "scaleGravityCrop":
                    addDescription("Proportionally scale down the original image so that the scaled-down image has a width and height greater than or equal to w*h; then crop according to the specified g to get a thumbnail of w*h");
                    addInput("cropW", "Width resolution of the cropped image. [0, +∞), integer");
                    addInput("cropH", "Height resolution of the cropped image. [0, +∞), integer");
                    addInput("cropPosition", "Image position in the nine-grid. [nw, north, ne, west, center, east, sw, south, se]");
                    break;
                case "scaleAnchorCrop":
                    addDescription("Start from the top-left corner of the original image, use anchor coordinates (x, y) to crop an image of specified width and height (w, h). If the width (or height) from the starting point exceeds the original image, it will crop directly to the end of the original image. If the starting point exceeds the original image, it returns 404 (squid returns the original image). The difference with p_0 is that it can only be effective with the resize function (resize functions p_0, p_1, p_4, p_5, p_6, p_7, p_8), and cannot be effective alone.");
                    addInput("cropX", "Starting anchor x-coordinate. [0, +∞), integer. Default 0");
                    addInput("cropY", "Starting anchor y-coordinate. [0, +∞), integer. Default 0");
                    addInput("cropW", "Width resolution of the cropped image. [0, +∞), integer");
                    addInput("cropH", "Height resolution of the cropped image. [0, +∞), integer");
                    break;
                case "format":
                    addDescription("Support four image formats (jpeg/jpg, png, gif, webp) conversion. The response Content-Type of the request after conversion is the corresponding image type");
                    addInput("format", "jpg/jpeg, png, gif, webp, jp2, jxr");
                    break;
                // Image Scaling
                case "stretch":
                    addDescription("Stretch the original image according to the resolution w*h");
                    addInput("resizeW", "Pixel width of the scaled image. [1, 10000], integer");
                    addInput("resizeH", "Pixel height of the scaled image. [1, 10000], integer");
                    addInput("resizeSH", "shrink processing. 0 or 1");
                    break;
                case "scaleByPercentage":
                    addDescription("Scale the image according to the percentage specified by w and h");
                    addInput("resizeW", "Percentage of the width of the original image to be scaled. [1, 1000], integer");
                    addInput("resizeH", "Percentage of the height of the original image to be scaled. [1, 1000], integer");
                    addInput("resizeSH", "shrink processing. 0 or 1");
                    break;
                case "scaleToFit":
                    addDescription("Set w and h, the original image is proportionally scaled, with the width and height not exceeding w*h, and the scaled image is centered. The part smaller than w*h is filled with color");
                    addInput("resizeW", "Pixel width of the scaled image. [1, 10000], integer");
                    addInput("resizeH", "Pixel height of the scaled image. [1, 10000], integer");
                    addInput("resizeColor", "Fill color. Hexadecimal RGB value. Default ffffff");
                    break;
                case "scaleAndCrop":
                    addDescription("Proportionally scale and crop according to the specified width and height");
                    addInput("resizeW", "Pixel width of the scaled image. [1, 10000], integer");
                    addInput("resizeH", "Pixel height of the scaled image. [1, 10000], integer");
                    break;
                case "scaleDownTo":
                    addDescription("Proportionally scale down the image, with the width and height of the scaled-down image not exceeding the specified w*h");
                    addInput("resizeW", "Pixel width of the scaled image. [1, 10000], integer");
                    addInput("resizeH", "Pixel height of the scaled image. [1, 10000], integer");
                    addInput("resizeL", "Long edge scaling parameter. [1, 10000], integer");
                    addInput("resizeS", "Short edge scaling parameter. [1, 10000], integer");
                    addInput("resizeSH", "shrink processing. 0 or 1");
                    break;
                case "scaleDownToMinimum":
                    addDescription("Proportionally scale down the image, with the width and height of the scaled-down image not less than the specified w*h");
                    addInput("resizeW", "Pixel width of the scaled image. [1, 10000], integer");
                    addInput("resizeH", "Pixel height of the scaled image. [1, 10000], integer");
                    break;
                case "scaleUpProportionally":
                    addDescription("Proportionally scale up the image");
                    addInput("resizeW", "Pixel width of the scaled image. [1, 10000], integer");
                    addInput("resizeH", "Pixel height of the scaled image. [1, 10000], integer");
                    break;
                case "scaleProportionally":
                    addDescription("Proportionally scale the original image, and the number of pixels in the width multiplied by the height does not exceed the specified pixel size");
                    addInput("resizeA", "Pixel size. [1, 24999999], integer");
                    break;
                case "restrictDimensions":
                    addDescription("When the requested image exceeds a certain width and height, proportionally scale it down, and the width and height of the thumbnail are less than or equal to the specified width and height; otherwise, do not process it");
                    addInput("resizeW", "Pixel width of the scaled image. [1, 10000], integer");
                    addInput("resizeH", "Pixel height of the scaled image. [1, 10000], integer");
                    break;
                // Image Effects
                case "blur":
                    addDescription("Apply a blur effect to the image");
                    addInput("blurRadius", "Blur radius, the larger the value, the more blurred the image. [1, 50], integer");
                    addInput("blurSigma", "Standard deviation of the normal distribution, the larger the value, the more blurred the image. [1, 50], integer");
                    break;
                case "sharpen":
                    addDescription("Apply a sharpening effect to the image");
                    addInput("sharpen", "(0, 100], float, up to four decimal places");
                    break;
                case "opt-quality":
                    addDescription("Improve the effect of scaled images, the processed image will also be relatively larger");
                    break;
                case "contrast":
                    addDescription("Adjust the contrast of the image");
                    addInput("contrast", "0: original contrast, <0: lower contrast than the original, >0: higher contrast than the original. [-100, 100], integer. Default value is 0");
                    break;
                case "bright":
                    addDescription("Adjust the brightness of the image");
                    addInput("bright", "0: original brightness, <0: lower brightness than the original, >0: higher brightness than the original. [-100, 100], integer. Default value is 0");
                    break;
                case "gray":
                    addDescription("Remove color from the image and convert it to grayscale");
                    break;
                // Image Rotation
                case "autoRotate":
                    addDescription("Some photos taken by mobile phones may have rotation parameters (stored in the exif information of the photo). You can set whether to automatically rotate these images");
                    addInput("rotateO", "0: Follow the default direction of the original image, do not auto-rotate; 1: Automatically rotate the image according to the rotation parameters, if there are also thumbnail parameters, scale first, then rotate; 2: Automatically rotate the image according to the rotation parameters, if there are also thumbnail parameters, rotate first, then scale");
                    break;
                case "rotate":
                    addDescription("Rotate the image clockwise, with a white background for the rotated area");
                    addInput("rotateR", "Angle to rotate the image clockwise. [0, 360], accurate to a decimal place. Default 0");
                    break;
                // Image Compression
                case "compressAbsolute":
                    addDescription("Absolute quality, compress the original image according to q, if the quality of the original image is less than q, do not compress");
                    addInput("compressQ", "[0, 100], integer. Default 0");
                    break;
                case "compressRelative":
                    addDescription("Relative quality, compress the original image quality according to Q%");
                    addInput("compressQQ", "[0, 100], integer. Default 0");
                    break;
                case "compressLossy":
                    addDescription("PNG image quality compression, the lower the image quality, the higher the compression ratio, and the smaller the Content-length of the image");
                    break;
                default:
                    break;
            }
        }
        function addDescription(des) {
            var operationDesc = document.getElementById("operationDesc");
            operationDesc.textContent = des;
        }
        function addInput(id, placeholder) {
            var operationParamsContainer = document.getElementById("operationParams");
            var input = document.createElement("input");
            input.type = "text";
            input.id = id;
            input.placeholder = placeholder;
            input.style.display = "block";
            input.style.marginBottom = "8px"
            operationParamsContainer.appendChild(input);
        }
        function addParam(params, attr, elementId) {
            const value = document.getElementById(elementId).value;
            if (value) {
                params[attr] = value;
            }
        }
        function addOperation() {
            var operation = document.getElementById("operation").value;
            var operationParams = {};

            // Collect parameters based on the selected operation
		    switch (operation) {
                case "extract":
                    addParam(operationParams, "start", "extractStart");
                    addParam(operationParams, "end", "extractEnd");
                    break;
                case "progressive":
                    addParam(operationParams, "percent", "progressive");
                    break;
                case "bgcolor":
                    addParam(operationParams, "bgcolor", "bgcolor");
                    break;
                case "border":
                    addParam(operationParams, "width", "borderWidth");
                    addParam(operationParams, "color", "borderColor");
                    break;
                case "composeXY":
                    addParam(operationParams, "x", "composeX");
                    addParam(operationParams, "y", "composeY");
                    addParam(operationParams, "image", "composeImage");
                    addParam(operationParams, "t", "composeT");
                    break;
                case "composeWH":
                    addParam(operationParams, "w", "composeW");
                    addParam(operationParams, "h", "composeH");
                    addParam(operationParams, "image", "composeImage");
                    addParam(operationParams, "t", "composeT");
                    break;
                case "info":
                    addParam(operationParams, "type", "infoType");
                    break;
                // Watermark
                case "watermarkImage":
                    addParam(operationParams, "image", "wmImage");
                    addParam(operationParams, "transparency", "wmTransparency");
                    addParam(operationParams, "direction", "wmDirection");
                    addParam(operationParams, "dx", "wmDX");
                    addParam(operationParams, "dy", "wmDY");
                    addParam(operationParams, "percent", "wmPercent");
                    break;
                case "watermarkText":
                    addParam(operationParams, "text", "wmText");
                    addParam(operationParams, "transparency", "wmTransparency");
                    addParam(operationParams, "direction", "wmDirection");
                    addParam(operationParams, "dx", "wmDX");
                    addParam(operationParams, "dy", "wmDY");
                    addParam(operationParams, "percent", "wmPercent");
                    addParam(operationParams, "font", "wmFont");
                    addParam(operationParams, "color", "wmColor");
                    addParam(operationParams, "size", "wmSize");
                    addParam(operationParams, "rotate", "wmRotate");
                    addParam(operationParams, "strokeColor", "wmStrokeColor");
                    addParam(operationParams, "strokeThickness", "wmStrokeThickness");
                    break;
                case "watermarkImageText":
                case "watermarkTextImage":
                    addParam(operationParams, "image", "wmImage");
                    addParam(operationParams, "text", "wmText");
                    addParam(operationParams, "transparency", "wmTransparency");
                    addParam(operationParams, "direction", "wmDirection");
                    addParam(operationParams, "dx", "wmDX");
                    addParam(operationParams, "dy", "wmDY");
                    addParam(operationParams, "percent", "wmPercent");
                    addParam(operationParams, "font", "wmFont");
                    addParam(operationParams, "color", "wmColor");
                    addParam(operationParams, "size", "wmSize");
                    addParam(operationParams, "rotate", "wmRotate");
                    addParam(operationParams, "strokeColor", "wmStrokeColor");
                    addParam(operationParams, "strokeThickness", "wmStrokeThickness");
                    break;
                // Image cropping
                case "anchorCrop":
                    addParam(operationParams, "x", "cropX");
                    addParam(operationParams, "y", "cropY");
                    addParam(operationParams, "w", "cropW");
                    addParam(operationParams, "h", "cropH");
                    break;
                case "centerCrop":
                    addParam(operationParams, "w", "cropW");
                    addParam(operationParams, "h", "cropH");
                    break;
                case "gravityCrop":
                    addParam(operationParams, "x", "cropX");
                    addParam(operationParams, "y", "cropY");
                    addParam(operationParams, "w", "cropW");
                    addParam(operationParams, "h", "cropH");
                    addParam(operationParams, "position", "cropPosition");
                    break;
                case "indexCrop":
                    addParam(operationParams, "x", "cropX");
                    addParam(operationParams, "y", "cropY");
                    addParam(operationParams, "index", "cropIndex");
                    break;
                case "inscribedCircleCrop":
                    addParam(operationParams, "radius", "cropRadius");
                    break;
                case "squareEnlargeCrop":
                    addParam(operationParams, "square", "cropSquare");
                    addParam(operationParams, "enlarge", "cropEnlarge");
                    break;
                case "extendCrop":
                    addParam(operationParams, "w", "cropW");
                    addParam(operationParams, "h", "cropH");
                    break;
                case "percentageCrop":
                    addParam(operationParams, "x", "cropX");
                    addParam(operationParams, "y", "cropY");
                    addParam(operationParams, "w", "cropW");
                    addParam(operationParams, "h", "cropH");
                    break;
                case "scaleGravityCrop":
                    addParam(operationParams, "w", "cropW");
                    addParam(operationParams, "h", "cropH");
                    addParam(operationParams, "position", "cropPosition");
                    break;
                case "scaleAnchorCrop":
                    addParam(operationParams, "x", "cropX");
                    addParam(operationParams, "y", "cropY");
                    addParam(operationParams, "w", "cropW");
                    addParam(operationParams, "h", "cropH");
                    break;
                case "format":
                    addParam(operationParams, "format", "format");
                    break;
                // Image scaling
                case "stretch":
                    addParam(operationParams, "w", "resizeW");
                    addParam(operationParams, "h", "resizeH");
                    addParam(operationParams, "sh", "resizeSH");
                    break;
                case "scaleByPercentage":
                    addParam(operationParams, "w", "resizeW");
                    addParam(operationParams, "h", "resizeH");
                    addParam(operationParams, "sh", "resizeSH");
                    break;
                case "scaleToFit":
                    addParam(operationParams, "w", "resizeW");
                    addParam(operationParams, "h", "resizeH");
                    addParam(operationParams, "color", "resizeColor");
                    break;
                case "scaleAndCrop":
                    addParam(operationParams, "w", "resizeW");
                    addParam(operationParams, "h", "resizeH");
                    break;
                case "scaleDownTo":
                    addParam(operationParams, "w", "resizeW");
                    addParam(operationParams, "h", "resizeH");
                    addParam(operationParams, "l", "resizeL");
                    addParam(operationParams, "s", "resizeS");
                    addParam(operationParams, "sh", "resizeSH");
                    break;
                case "scaleDownToMinimum":
                    addParam(operationParams, "w", "resizeW");
                    addParam(operationParams, "h", "resizeH");
                    break;
                case "scaleUpProportionally":
                    addParam(operationParams, "w", "resizeW");
                    addParam(operationParams, "h", "resizeH");
                    break;
                case "scaleProportionally":
                    addParam(operationParams, "a", "resizeA");
                    break;
                case "restrictDimensions":
                    addParam(operationParams, "w", "resizeW");
                    addParam(operationParams, "h", "resizeH");
                    break;
                // Image effects
                case "blur":
                    addParam(operationParams, "radius", "blurRadius");
                    addParam(operationParams, "sigma", "blurSigma");
                    break;
                case "sharpen":
                    addParam(operationParams, "sharpen", "sharpen");
                    break;
                case "contrast":
                    addParam(operationParams, "contrast", "contrast");
                    break;
                case "bright":
                    addParam(operationParams, "bright", "bright");
                    break;
                // Image rotation
                case "autoRotate":
                    addParam(operationParams, "o", "rotateO");
                    break
                case "rotate":
                    addParam(operationParams, "r", "rotateR");
                    break
                // Image compression
                case "compressAbsolute":
                    addParam(operationParams, "q", "compressQ");
                    break;
                case "compressRelative":
                    addParam(operationParams, "Q", "compressQQ");
                    break;
		        default:
		        	break;
		    }
            const op = { operation: operation, params: operationParams }
            operations.push(op)

            const list = document.querySelector("#operationList")
            const item = document.createElement("li")
            item.innerText = JSON.stringify(op)
            list.appendChild(item)
        }
        function clearOperation() {
            operations = []
            const list = document.querySelector("#operationList")
            list.innerText = ""
        }
        function executeOperation() {
            var resultContainer = document.getElementById("resultContainer");
            resultContainer.innerHTML = "";
            
            var imageUrl = document.getElementById("imageUrl").value;
            var data = {
                url: imageUrl,
                operations: operations,
            };

            fetch("/mona-examples", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json"
                },
                body: JSON.stringify(data)
            })
            .then(response => {
                if (response.headers.has("content-type") && response.headers.get("content-type").startsWith("image/")) {
                    return response.blob();
                } else {
                    return response.text();
                }
            })
            .then(result => {
                const list = document.querySelector("#imageInfo");
                list.innerHTML = "";
                if (result instanceof Blob) {
                    var url = URL.createObjectURL(result);
                    var image = document.createElement("img");
                    image.src = url;
    
                    // Listen for the image to finish loading to ensure dimensions can be read
                    image.onload = () => {
                        const resultContainer = document.getElementById("resultContainer");
                        resultContainer.innerHTML = ""; // Clear any existing content
                        resultContainer.appendChild(image);

                        // At this point, the width and height of the image can be obtained
                        const item = document.createElement("li")
                        item.innerText = "Dimensions: " + String(image.width) + " x " + String(image.height)
                        list.appendChild(item)

                        // Revoke the created object URL to reduce resource usage
                        URL.revokeObjectURL(url);
                    };

                    let item = document.createElement("li")
                    item.innerText = "Size: " + String(result.size) + " bytes"
                    list.appendChild(item)

                    item = document.createElement("li")
                    item.innerText = "Type: " + result.type
                    list.appendChild(item)
                } else {
                    var resultContainer = document.getElementById("resultContainer");
                    var label = document.createElement("label")
                    label.style.wordBreak = "break-word";
                    label.style.maxWidth = "500px";
                    label.style.display = "inline-block";
                    label.style.color = "blue";
                    label.textContent = result;
                    resultContainer.appendChild(label);
                }
            })
            .catch(error => {
                alert(error);
            });
        }
    </script>
</body>
</html>
`
                    
addEventListener("fetch", event => {
    return event.respondWith(handleRequest(event.request))
})