#include <string.h>
#include "novnc_assets.h"

const char *get_novnc_asset(const char *path) {
const char *novnc_base64_js =
"/* This Source Code Form is subject to the terms of the Mozilla Public\n"
" * License, v. 2.0. If a copy of the MPL was not distributed with this\n"
" * file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n"
"\n"
"// From: http://hg.mozilla.org/mozilla-central/raw-file/ec10630b1a54/js/src/devtools/jint/sunspider/string-base64.js\n"
"\n"
"\n"
"window.Base64 = {\n"
"    /* Convert data (an array of integers) to a Base64 string. */\n"
"    toBase64Table: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),\n"
"    base64Pad: '=',\n"
"\n"
"    encode(data) {\n"
"        \"use strict\";\n"
"        let result = '';\n"
"        const length = data.length;\n"
"        const lengthpad = (length % 3);\n"
"        // Convert every three bytes to 4 ascii characters.\n"
"\n"
"        for (let i = 0; i < (length - 2); i += 3) {\n"
"            result += this.toBase64Table[data[i] >> 2];\n"
"            result += this.toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];\n"
"            result += this.toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];\n"
"            result += this.toBase64Table[data[i + 2] & 0x3f];\n"
"        }\n"
"\n"
"        // Convert the remaining 1 or 2 bytes, pad out to 4 characters.\n"
"        const j = length - lengthpad;\n"
"        if (lengthpad === 2) {\n"
"            result += this.toBase64Table[data[j] >> 2];\n"
"            result += this.toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)];\n"
"            result += this.toBase64Table[(data[j + 1] & 0x0f) << 2];\n"
"            result += this.toBase64Table[64];\n"
"        } else if (lengthpad === 1) {\n"
"            result += this.toBase64Table[data[j] >> 2];\n"
"            result += this.toBase64Table[(data[j] & 0x03) << 4];\n"
"            result += this.toBase64Table[64];\n"
"            result += this.toBase64Table[64];\n"
"        }\n"
"\n"
"        return result;\n"
"    },\n"
"\n"
"    /* Convert Base64 data to a string */\n"
"    /* eslint-disable comma-spacing */\n"
"    toBinaryTable: [\n"
"        -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,\n"
"        -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,\n"
"        -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,\n"
"        52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,\n"
"        -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,\n"
"        15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,\n"
"        -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,\n"
"        41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1\n"
"    ],\n"
"    /* eslint-enable comma-spacing */\n"
"\n"
"    decode(data, offset = 0) {\n"
"        let dataLength = data.indexOf('=') - offset;\n"
"        if (dataLength < 0) { dataLength = data.length - offset; }\n"
"\n"
"        /* Every four characters is 3 resulting numbers */\n"
"        const resultLength = (dataLength >> 2) * 3 + Math.floor((dataLength % 4) / 1.5);\n"
"        const result = new Array(resultLength);\n"
"\n"
"        // Convert one by one.\n"
"\n"
"        let leftbits = 0; // number of bits decoded, but yet to be appended\n"
"        let leftdata = 0; // bits decoded, but yet to be appended\n"
"        for (let idx = 0, i = offset; i < data.length; i++) {\n"
"            const c = this.toBinaryTable[data.charCodeAt(i) & 0x7f];\n"
"            const padding = (data.charAt(i) === this.base64Pad);\n"
"            // Skip illegal characters and whitespace\n"
"            if (c === -1) {\n"
"                Log.Error(\"Illegal character code \" + data.charCodeAt(i) + \" at position \" + i);\n"
"                continue;\n"
"            }\n"
"\n"
"            // Collect data into leftdata, update bitcount\n"
"            leftdata = (leftdata << 6) | c;\n"
"            leftbits += 6;\n"
"\n"
"            // If we have 8 or more bits, append 8 bits to the result\n"
"            if (leftbits >= 8) {\n"
"                leftbits -= 8;\n"
"                // Append if not padding.\n"
"                if (!padding) {\n"
"                    result[idx++] = (leftdata >> leftbits) & 0xff;\n"
"                }\n"
"                leftdata &= (1 << leftbits) - 1;\n"
"            }\n"
"        }\n"
"\n"
"        // If there are any bits left, the base64 string was corrupted\n"
"        if (leftbits) {\n"
"            const err = new Error('Corrupted base64 string');\n"
"            err.name = 'Base64-Error';\n"
"            throw err;\n"
"        }\n"
"\n"
"        return result;\n"
"    }\n"
"}; /* End of Base64 namespace */\n"
;
    if (strcmp(path, "/novnc/base64.js") == 0) return novnc_base64_js;
const char *novnc_decoders_copyrect_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2019 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" *\n"
" */\n"
"\n"
"class CopyRectDecoder {\n"
"    decodeRect(x, y, width, height, sock, display, depth) {\n"
"        if (sock.rQwait(\"COPYRECT\", 4)) {\n"
"            return false;\n"
"        }\n"
"\n"
"        let deltaX = sock.rQshift16();\n"
"        let deltaY = sock.rQshift16();\n"
"\n"
"        if ((width === 0) || (height === 0)) {\n"
"            return true;\n"
"        }\n"
"\n"
"        display.copyImage(deltaX, deltaY, x, y, width, height);\n"
"\n"
"        return true;\n"
"    }\n"
"}\n"
"window.CopyRectDecoder = CopyRectDecoder;\n"
;
    if (strcmp(path, "/novnc/decoders/copyrect.js") == 0) return novnc_decoders_copyrect_js;
const char *novnc_decoders_hextile_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2019 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" *\n"
" */\n"
"\n"
"\n"
"class HextileDecoder {\n"
"    constructor() {\n"
"        this._tiles = 0;\n"
"        this._lastsubencoding = 0;\n"
"        this._tileBuffer = new Uint8Array(16 * 16 * 4);\n"
"    }\n"
"\n"
"    decodeRect(x, y, width, height, sock, display, depth) {\n"
"        if (this._tiles === 0) {\n"
"            this._tilesX = Math.ceil(width / 16);\n"
"            this._tilesY = Math.ceil(height / 16);\n"
"            this._totalTiles = this._tilesX * this._tilesY;\n"
"            this._tiles = this._totalTiles;\n"
"        }\n"
"\n"
"        while (this._tiles > 0) {\n"
"            let bytes = 1;\n"
"\n"
"            if (sock.rQwait(\"HEXTILE\", bytes)) {\n"
"                return false;\n"
"            }\n"
"\n"
"            let rQ = sock.rQ;\n"
"            let rQi = sock.rQi;\n"
"\n"
"            let subencoding = rQ[rQi];  // Peek\n"
"            if (subencoding > 30) {  // Raw\n"
"                throw new Error(\"Illegal hextile subencoding (subencoding: \" +\n"
"                            subencoding + \")\");\n"
"            }\n"
"\n"
"            const currTile = this._totalTiles - this._tiles;\n"
"            const tileX = currTile % this._tilesX;\n"
"            const tileY = Math.floor(currTile / this._tilesX);\n"
"            const tx = x + tileX * 16;\n"
"            const ty = y + tileY * 16;\n"
"            const tw = Math.min(16, (x + width) - tx);\n"
"            const th = Math.min(16, (y + height) - ty);\n"
"\n"
"            // Figure out how much we are expecting\n"
"            if (subencoding & 0x01) {  // Raw\n"
"                bytes += tw * th * 4;\n"
"            } else {\n"
"                if (subencoding & 0x02) {  // Background\n"
"                    bytes += 4;\n"
"                }\n"
"                if (subencoding & 0x04) {  // Foreground\n"
"                    bytes += 4;\n"
"                }\n"
"                if (subencoding & 0x08) {  // AnySubrects\n"
"                    bytes++;  // Since we aren't shifting it off\n"
"\n"
"                    if (sock.rQwait(\"HEXTILE\", bytes)) {\n"
"                        return false;\n"
"                    }\n"
"\n"
"                    let subrects = rQ[rQi + bytes - 1];  // Peek\n"
"                    if (subencoding & 0x10) {  // SubrectsColoured\n"
"                        bytes += subrects * (4 + 2);\n"
"                    } else {\n"
"                        bytes += subrects * 2;\n"
"                    }\n"
"                }\n"
"            }\n"
"\n"
"            if (sock.rQwait(\"HEXTILE\", bytes)) {\n"
"                return false;\n"
"            }\n"
"\n"
"            // We know the encoding and have a whole tile\n"
"            rQi++;\n"
"            if (subencoding === 0) {\n"
"                if (this._lastsubencoding & 0x01) {\n"
"                    // Weird: ignore blanks are RAW\n"
"                    Log.Debug(\"     Ignoring blank after RAW\");\n"
"                } else {\n"
"                    display.fillRect(tx, ty, tw, th, this._background);\n"
"                }\n"
"            } else if (subencoding & 0x01) {  // Raw\n"
"                let pixels = tw * th;\n"
"                // Max sure the image is fully opaque\n"
"                for (let i = 0;i <  pixels;i++) {\n"
"                    rQ[rQi + i * 4 + 3] = 255;\n"
"                }\n"
"                display.blitImage(tx, ty, tw, th, rQ, rQi);\n"
"                rQi += bytes - 1;\n"
"            } else {\n"
"                if (subencoding & 0x02) {  // Background\n"
"                    this._background = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];\n"
"                    rQi += 4;\n"
"                }\n"
"                if (subencoding & 0x04) {  // Foreground\n"
"                    this._foreground = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];\n"
"                    rQi += 4;\n"
"                }\n"
"\n"
"                this._startTile(tx, ty, tw, th, this._background);\n"
"                if (subencoding & 0x08) {  // AnySubrects\n"
"                    let subrects = rQ[rQi];\n"
"                    rQi++;\n"
"\n"
"                    for (let s = 0; s < subrects; s++) {\n"
"                        let color;\n"
"                        if (subencoding & 0x10) {  // SubrectsColoured\n"
"                            color = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];\n"
"                            rQi += 4;\n"
"                        } else {\n"
"                            color = this._foreground;\n"
"                        }\n"
"                        const xy = rQ[rQi];\n"
"                        rQi++;\n"
"                        const sx = (xy >> 4);\n"
"                        const sy = (xy & 0x0f);\n"
"\n"
"                        const wh = rQ[rQi];\n"
"                        rQi++;\n"
"                        const sw = (wh >> 4) + 1;\n"
"                        const sh = (wh & 0x0f) + 1;\n"
"\n"
"                        this._subTile(sx, sy, sw, sh, color);\n"
"                    }\n"
"                }\n"
"                this._finishTile(display);\n"
"            }\n"
"            sock.rQi = rQi;\n"
"            this._lastsubencoding = subencoding;\n"
"            this._tiles--;\n"
"        }\n"
"\n"
"        return true;\n"
"    }\n"
"\n"
"    // start updating a tile\n"
"    _startTile(x, y, width, height, color) {\n"
"        this._tileX = x;\n"
"        this._tileY = y;\n"
"        this._tileW = width;\n"
"        this._tileH = height;\n"
"\n"
"        const red = color[0];\n"
"        const green = color[1];\n"
"        const blue = color[2];\n"
"\n"
"        const data = this._tileBuffer;\n"
"        for (let i = 0; i < width * height * 4; i += 4) {\n"
"            data[i]     = red;\n"
"            data[i + 1] = green;\n"
"            data[i + 2] = blue;\n"
"            data[i + 3] = 255;\n"
"        }\n"
"    }\n"
"\n"
"    // update sub-rectangle of the current tile\n"
"    _subTile(x, y, w, h, color) {\n"
"        const red = color[0];\n"
"        const green = color[1];\n"
"        const blue = color[2];\n"
"        const xend = x + w;\n"
"        const yend = y + h;\n"
"\n"
"        const data = this._tileBuffer;\n"
"        const width = this._tileW;\n"
"        for (let j = y; j < yend; j++) {\n"
"            for (let i = x; i < xend; i++) {\n"
"                const p = (i + (j * width)) * 4;\n"
"                data[p]     = red;\n"
"                data[p + 1] = green;\n"
"                data[p + 2] = blue;\n"
"                data[p + 3] = 255;\n"
"            }\n"
"        }\n"
"    }\n"
"\n"
"    // draw the current tile to the screen\n"
"    _finishTile(display) {\n"
"        display.blitImage(this._tileX, this._tileY,\n"
"                          this._tileW, this._tileH,\n"
"                          this._tileBuffer, 0);\n"
"    }\n"
"}\n"
"window.HextileDecoder = HextileDecoder;\n"
;
    if (strcmp(path, "/novnc/decoders/hextile.js") == 0) return novnc_decoders_hextile_js;
const char *novnc_decoders_jpeg_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2019 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" *\n"
" */\n"
"\n"
"class JPEGDecoder {\n"
"    constructor() {\n"
"        // RealVNC will reuse the quantization tables\n"
"        // and Huffman tables, so we need to cache them.\n"
"        this._quantTables = [];\n"
"        this._huffmanTables = [];\n"
"        this._cachedQuantTables = [];\n"
"        this._cachedHuffmanTables = [];\n"
"\n"
"        this._jpegLength = 0;\n"
"        this._segments = [];\n"
"    }\n"
"\n"
"    decodeRect(x, y, width, height, sock, display, depth) {\n"
"        // A rect of JPEG encodings is simply a JPEG file\n"
"        if (!this._parseJPEG(sock.rQslice(0))) {\n"
"            return false;\n"
"        }\n"
"        const data = sock.rQshiftBytes(this._jpegLength);\n"
"        if (this._quantTables.length != 0 && this._huffmanTables.length != 0) {\n"
"            // If there are quantization tables and Huffman tables in the JPEG\n"
"            // image, we can directly render it.\n"
"            display.imageRect(x, y, width, height, \"image/jpeg\", data);\n"
"            return true;\n"
"        } else {\n"
"            // Otherwise we need to insert cached tables.\n"
"            const sofIndex = this._segments.findIndex(\n"
"                x => x[1] == 0xC0 || x[1] == 0xC2\n"
"            );\n"
"            if (sofIndex == -1) {\n"
"                throw new Error(\"Illegal JPEG image without SOF\");\n"
"            }\n"
"            let segments = this._segments.slice(0, sofIndex);\n"
"            segments = segments.concat(this._quantTables.length ?\n"
"                this._quantTables :\n"
"                this._cachedQuantTables);\n"
"            segments.push(this._segments[sofIndex]);\n"
"            segments = segments.concat(this._huffmanTables.length ?\n"
"                this._huffmanTables :\n"
"                this._cachedHuffmanTables,\n"
"                                       this._segments.slice(sofIndex + 1));\n"
"            let length = 0;\n"
"            for (let i = 0; i < segments.length; i++) {\n"
"                length += segments[i].length;\n"
"            }\n"
"            const data = new Uint8Array(length);\n"
"            length = 0;\n"
"            for (let i = 0; i < segments.length; i++) {\n"
"                data.set(segments[i], length);\n"
"                length += segments[i].length;\n"
"            }\n"
"            display.imageRect(x, y, width, height, \"image/jpeg\", data);\n"
"            return true;\n"
"        }\n"
"    }\n"
"\n"
"    _parseJPEG(buffer) {\n"
"        if (this._quantTables.length != 0) {\n"
"            this._cachedQuantTables = this._quantTables;\n"
"        }\n"
"        if (this._huffmanTables.length != 0) {\n"
"            this._cachedHuffmanTables = this._huffmanTables;\n"
"        }\n"
"        this._quantTables = [];\n"
"        this._huffmanTables = [];\n"
"        this._segments = [];\n"
"        let i = 0;\n"
"        let bufferLength = buffer.length;\n"
"        while (true) {\n"
"            let j = i;\n"
"            if (j + 2 > bufferLength) {\n"
"                return false;\n"
"            }\n"
"            if (buffer[j] != 0xFF) {\n"
"                throw new Error(\"Illegal JPEG marker received (byte: \" +\n"
"                                   buffer[j] + \")\");\n"
"            }\n"
"            const type = buffer[j+1];\n"
"            j += 2;\n"
"            if (type == 0xD9) {\n"
"                this._jpegLength = j;\n"
"                this._segments.push(buffer.slice(i, j));\n"
"                return true;\n"
"            } else if (type == 0xDA) {\n"
"                // start of scan\n"
"                let hasFoundEndOfScan = false;\n"
"                for (let k = j + 3; k + 1 < bufferLength; k++) {\n"
"                    if (buffer[k] == 0xFF && buffer[k+1] != 0x00 &&\n"
"                        !(buffer[k+1] >= 0xD0 && buffer[k+1] <= 0xD7)) {\n"
"                        j = k;\n"
"                        hasFoundEndOfScan = true;\n"
"                        break;\n"
"                    }\n"
"                }\n"
"                if (!hasFoundEndOfScan) {\n"
"                    return false;\n"
"                }\n"
"                this._segments.push(buffer.slice(i, j));\n"
"                i = j;\n"
"                continue;\n"
"            } else if (type >= 0xD0 && type < 0xD9 || type == 0x01) {\n"
"                // No length after marker\n"
"                this._segments.push(buffer.slice(i, j));\n"
"                i = j;\n"
"                continue;\n"
"            }\n"
"            if (j + 2 > bufferLength) {\n"
"                return false;\n"
"            }\n"
"            const length = (buffer[j] << 8) + buffer[j+1] - 2;\n"
"            if (length < 0) {\n"
"                throw new Error(\"Illegal JPEG length received (length: \" +\n"
"                                   length + \")\");\n"
"            }\n"
"            j += 2;\n"
"            if (j + length > bufferLength) {\n"
"                return false;\n"
"            }\n"
"            j += length;\n"
"            const segment = buffer.slice(i, j);\n"
"            if (type == 0xC4) {\n"
"                // Huffman tables\n"
"                this._huffmanTables.push(segment);\n"
"            } else if (type == 0xDB) {\n"
"                // Quantization tables\n"
"                this._quantTables.push(segment);\n"
"            }\n"
"            this._segments.push(segment);\n"
"            i = j;\n"
"        }\n"
"    }\n"
"}\n"
"window.JPEGDecoder = JPEGDecoder;\n"
;
    if (strcmp(path, "/novnc/decoders/jpeg.js") == 0) return novnc_decoders_jpeg_js;
const char *novnc_decoders_raw_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2019 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" *\n"
" */\n"
"\n"
"class RawDecoder {\n"
"    constructor() {\n"
"        this._lines = 0;\n"
"    }\n"
"\n"
"    decodeRect(x, y, width, height, sock, display, depth) {\n"
"        if ((width === 0) || (height === 0)) {\n"
"            return true;\n"
"        }\n"
"\n"
"        if (this._lines === 0) {\n"
"            this._lines = height;\n"
"        }\n"
"\n"
"        const pixelSize = depth == 8 ? 1 : 4;\n"
"        const bytesPerLine = width * pixelSize;\n"
"\n"
"        if (sock.rQwait(\"RAW\", bytesPerLine)) {\n"
"            return false;\n"
"        }\n"
"\n"
"        const curY = y + (height - this._lines);\n"
"        const currHeight = Math.min(this._lines,\n"
"                                    Math.floor(sock.rQlen / bytesPerLine));\n"
"        const pixels = width * currHeight;\n"
"\n"
"        let data = sock.rQ;\n"
"        let index = sock.rQi;\n"
"\n"
"        // Convert data if needed\n"
"        if (depth == 8) {\n"
"            const newdata = new Uint8Array(pixels * 4);\n"
"            for (let i = 0; i < pixels; i++) {\n"
"                newdata[i * 4 + 0] = ((data[index + i] >> 0) & 0x3) * 255 / 3;\n"
"                newdata[i * 4 + 1] = ((data[index + i] >> 2) & 0x3) * 255 / 3;\n"
"                newdata[i * 4 + 2] = ((data[index + i] >> 4) & 0x3) * 255 / 3;\n"
"                newdata[i * 4 + 3] = 255;\n"
"            }\n"
"            data = newdata;\n"
"            index = 0;\n"
"        }\n"
"\n"
"        // Max sure the image is fully opaque\n"
"        for (let i = 0; i < pixels; i++) {\n"
"            data[index + i * 4 + 3] = 255;\n"
"        }\n"
"\n"
"        display.blitImage(x, curY, width, currHeight, data, index);\n"
"        sock.rQskipBytes(currHeight * bytesPerLine);\n"
"        this._lines -= currHeight;\n"
"        if (this._lines > 0) {\n"
"            return false;\n"
"        }\n"
"\n"
"        return true;\n"
"    }\n"
"}\n"
"window.RawDecoder = RawDecoder;\n"
;
    if (strcmp(path, "/novnc/decoders/raw.js") == 0) return novnc_decoders_raw_js;
const char *novnc_decoders_rre_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2019 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" *\n"
" */\n"
"\n"
"class RREDecoder {\n"
"    constructor() {\n"
"        this._subrects = 0;\n"
"    }\n"
"\n"
"    decodeRect(x, y, width, height, sock, display, depth) {\n"
"        if (this._subrects === 0) {\n"
"            if (sock.rQwait(\"RRE\", 4 + 4)) {\n"
"                return false;\n"
"            }\n"
"\n"
"            this._subrects = sock.rQshift32();\n"
"\n"
"            let color = sock.rQshiftBytes(4);  // Background\n"
"            display.fillRect(x, y, width, height, color);\n"
"        }\n"
"\n"
"        while (this._subrects > 0) {\n"
"            if (sock.rQwait(\"RRE\", 4 + 8)) {\n"
"                return false;\n"
"            }\n"
"\n"
"            let color = sock.rQshiftBytes(4);\n"
"            let sx = sock.rQshift16();\n"
"            let sy = sock.rQshift16();\n"
"            let swidth = sock.rQshift16();\n"
"            let sheight = sock.rQshift16();\n"
"            display.fillRect(x + sx, y + sy, swidth, sheight, color);\n"
"\n"
"            this._subrects--;\n"
"        }\n"
"\n"
"        return true;\n"
"    }\n"
"}\n"
"window.RREDecoder = RREDecoder;\n"
;
    if (strcmp(path, "/novnc/decoders/rre.js") == 0) return novnc_decoders_rre_js;
const char *novnc_decoders_tight_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2019 The noVNC Authors\n"
" * (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" *\n"
" */\n"
"\n"
"\n"
"class TightDecoder {\n"
"    constructor() {\n"
"        this._ctl = null;\n"
"        this._filter = null;\n"
"        this._numColors = 0;\n"
"        this._palette = new Uint8Array(1024);  // 256 * 4 (max palette size * max bytes-per-pixel)\n"
"        this._len = 0;\n"
"\n"
"        this._zlibs = [];\n"
"        for (let i = 0; i < 4; i++) {\n"
"            this._zlibs[i] = new Inflate();\n"
"        }\n"
"    }\n"
"\n"
"    decodeRect(x, y, width, height, sock, display, depth) {\n"
"        if (this._ctl === null) {\n"
"            if (sock.rQwait(\"TIGHT compression-control\", 1)) {\n"
"                return false;\n"
"            }\n"
"\n"
"            this._ctl = sock.rQshift8();\n"
"\n"
"            // Reset streams if the server requests it\n"
"            for (let i = 0; i < 4; i++) {\n"
"                if ((this._ctl >> i) & 1) {\n"
"                    this._zlibs[i].reset();\n"
"                    Log.Info(\"Reset zlib stream \" + i);\n"
"                }\n"
"            }\n"
"\n"
"            // Figure out filter\n"
"            this._ctl = this._ctl >> 4;\n"
"        }\n"
"\n"
"        let ret;\n"
"\n"
"        if (this._ctl === 0x08) {\n"
"            ret = this._fillRect(x, y, width, height,\n"
"                                 sock, display, depth);\n"
"        } else if (this._ctl === 0x09) {\n"
"            ret = this._jpegRect(x, y, width, height,\n"
"                                 sock, display, depth);\n"
"        } else if (this._ctl === 0x0A) {\n"
"            ret = this._pngRect(x, y, width, height,\n"
"                                sock, display, depth);\n"
"        } else if ((this._ctl & 0x08) == 0) {\n"
"            ret = this._basicRect(this._ctl, x, y, width, height,\n"
"                                  sock, display, depth);\n"
"        } else {\n"
"            throw new Error(\"Illegal tight compression received (ctl: \" +\n"
"                                   this._ctl + \")\");\n"
"        }\n"
"\n"
"        if (ret) {\n"
"            this._ctl = null;\n"
"        }\n"
"\n"
"        return ret;\n"
"    }\n"
"\n"
"    _fillRect(x, y, width, height, sock, display, depth) {\n"
"        if (sock.rQwait(\"TIGHT\", 3)) {\n"
"            return false;\n"
"        }\n"
"\n"
"        const rQi = sock.rQi;\n"
"        const rQ = sock.rQ;\n"
"\n"
"        display.fillRect(x, y, width, height,\n"
"                         [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2]], false);\n"
"        sock.rQskipBytes(3);\n"
"\n"
"        return true;\n"
"    }\n"
"\n"
"    _jpegRect(x, y, width, height, sock, display, depth) {\n"
"        let data = this._readData(sock);\n"
"        if (data === null) {\n"
"            return false;\n"
"        }\n"
"\n"
"        display.imageRect(x, y, width, height, \"image/jpeg\", data);\n"
"\n"
"        return true;\n"
"    }\n"
"\n"
"    _pngRect(x, y, width, height, sock, display, depth) {\n"
"        throw new Error(\"PNG received in standard Tight rect\");\n"
"    }\n"
"\n"
"    _basicRect(ctl, x, y, width, height, sock, display, depth) {\n"
"        if (this._filter === null) {\n"
"            if (ctl & 0x4) {\n"
"                if (sock.rQwait(\"TIGHT\", 1)) {\n"
"                    return false;\n"
"                }\n"
"\n"
"                this._filter = sock.rQshift8();\n"
"            } else {\n"
"                // Implicit CopyFilter\n"
"                this._filter = 0;\n"
"            }\n"
"        }\n"
"\n"
"        let streamId = ctl & 0x3;\n"
"\n"
"        let ret;\n"
"\n"
"        switch (this._filter) {\n"
"            case 0: // CopyFilter\n"
"                ret = this._copyFilter(streamId, x, y, width, height,\n"
"                                       sock, display, depth);\n"
"                break;\n"
"            case 1: // PaletteFilter\n"
"                ret = this._paletteFilter(streamId, x, y, width, height,\n"
"                                          sock, display, depth);\n"
"                break;\n"
"            case 2: // GradientFilter\n"
"                ret = this._gradientFilter(streamId, x, y, width, height,\n"
"                                           sock, display, depth);\n"
"                break;\n"
"            default:\n"
"                throw new Error(\"Illegal tight filter received (ctl: \" +\n"
"                                       this._filter + \")\");\n"
"        }\n"
"\n"
"        if (ret) {\n"
"            this._filter = null;\n"
"        }\n"
"\n"
"        return ret;\n"
"    }\n"
"\n"
"    _copyFilter(streamId, x, y, width, height, sock, display, depth) {\n"
"        const uncompressedSize = width * height * 3;\n"
"        let data;\n"
"\n"
"        if (uncompressedSize === 0) {\n"
"            return true;\n"
"        }\n"
"\n"
"        if (uncompressedSize < 12) {\n"
"            if (sock.rQwait(\"TIGHT\", uncompressedSize)) {\n"
"                return false;\n"
"            }\n"
"\n"
"            data = sock.rQshiftBytes(uncompressedSize);\n"
"        } else {\n"
"            data = this._readData(sock);\n"
"            if (data === null) {\n"
"                return false;\n"
"            }\n"
"\n"
"            this._zlibs[streamId].setInput(data);\n"
"            data = this._zlibs[streamId].inflate(uncompressedSize);\n"
"            this._zlibs[streamId].setInput(null);\n"
"        }\n"
"\n"
"        let rgbx = new Uint8Array(width * height * 4);\n"
"        for (let i = 0, j = 0; i < width * height * 4; i += 4, j += 3) {\n"
"            rgbx[i]     = data[j];\n"
"            rgbx[i + 1] = data[j + 1];\n"
"            rgbx[i + 2] = data[j + 2];\n"
"            rgbx[i + 3] = 255;  // Alpha\n"
"        }\n"
"\n"
"        display.blitImage(x, y, width, height, rgbx, 0, false);\n"
"\n"
"        return true;\n"
"    }\n"
"\n"
"    _paletteFilter(streamId, x, y, width, height, sock, display, depth) {\n"
"        if (this._numColors === 0) {\n"
"            if (sock.rQwait(\"TIGHT palette\", 1)) {\n"
"                return false;\n"
"            }\n"
"\n"
"            const numColors = sock.rQpeek8() + 1;\n"
"            const paletteSize = numColors * 3;\n"
"\n"
"            if (sock.rQwait(\"TIGHT palette\", 1 + paletteSize)) {\n"
"                return false;\n"
"            }\n"
"\n"
"            this._numColors = numColors;\n"
"            sock.rQskipBytes(1);\n"
"\n"
"            sock.rQshiftTo(this._palette, paletteSize);\n"
"        }\n"
"\n"
"        const bpp = (this._numColors <= 2) ? 1 : 8;\n"
"        const rowSize = Math.floor((width * bpp + 7) / 8);\n"
"        const uncompressedSize = rowSize * height;\n"
"\n"
"        let data;\n"
"\n"
"        if (uncompressedSize === 0) {\n"
"            return true;\n"
"        }\n"
"\n"
"        if (uncompressedSize < 12) {\n"
"            if (sock.rQwait(\"TIGHT\", uncompressedSize)) {\n"
"                return false;\n"
"            }\n"
"\n"
"            data = sock.rQshiftBytes(uncompressedSize);\n"
"        } else {\n"
"            data = this._readData(sock);\n"
"            if (data === null) {\n"
"                return false;\n"
"            }\n"
"\n"
"            this._zlibs[streamId].setInput(data);\n"
"            data = this._zlibs[streamId].inflate(uncompressedSize);\n"
"            this._zlibs[streamId].setInput(null);\n"
"        }\n"
"\n"
"        // Convert indexed (palette based) image data to RGB\n"
"        if (this._numColors == 2) {\n"
"            this._monoRect(x, y, width, height, data, this._palette, display);\n"
"        } else {\n"
"            this._paletteRect(x, y, width, height, data, this._palette, display);\n"
"        }\n"
"\n"
"        this._numColors = 0;\n"
"\n"
"        return true;\n"
"    }\n"
"\n"
"    _monoRect(x, y, width, height, data, palette, display) {\n"
"        // Convert indexed (palette based) image data to RGB\n"
"        // TODO: reduce number of calculations inside loop\n"
"        const dest = this._getScratchBuffer(width * height * 4);\n"
"        const w = Math.floor((width + 7) / 8);\n"
"        const w1 = Math.floor(width / 8);\n"
"\n"
"        for (let y = 0; y < height; y++) {\n"
"            let dp, sp, x;\n"
"            for (x = 0; x < w1; x++) {\n"
"                for (let b = 7; b >= 0; b--) {\n"
"                    dp = (y * width + x * 8 + 7 - b) * 4;\n"
"                    sp = (data[y * w + x] >> b & 1) * 3;\n"
"                    dest[dp]     = palette[sp];\n"
"                    dest[dp + 1] = palette[sp + 1];\n"
"                    dest[dp + 2] = palette[sp + 2];\n"
"                    dest[dp + 3] = 255;\n"
"                }\n"
"            }\n"
"\n"
"            for (let b = 7; b >= 8 - width % 8; b--) {\n"
"                dp = (y * width + x * 8 + 7 - b) * 4;\n"
"                sp = (data[y * w + x] >> b & 1) * 3;\n"
"                dest[dp]     = palette[sp];\n"
"                dest[dp + 1] = palette[sp + 1];\n"
"                dest[dp + 2] = palette[sp + 2];\n"
"                dest[dp + 3] = 255;\n"
"            }\n"
"        }\n"
"\n"
"        display.blitImage(x, y, width, height, dest, 0, false);\n"
"    }\n"
"\n"
"    _paletteRect(x, y, width, height, data, palette, display) {\n"
"        // Convert indexed (palette based) image data to RGB\n"
"        const dest = this._getScratchBuffer(width * height * 4);\n"
"        const total = width * height * 4;\n"
"        for (let i = 0, j = 0; i < total; i += 4, j++) {\n"
"            const sp = data[j] * 3;\n"
"            dest[i]     = palette[sp];\n"
"            dest[i + 1] = palette[sp + 1];\n"
"            dest[i + 2] = palette[sp + 2];\n"
"            dest[i + 3] = 255;\n"
"        }\n"
"\n"
"        display.blitImage(x, y, width, height, dest, 0, false);\n"
"    }\n"
"\n"
"    _gradientFilter(streamId, x, y, width, height, sock, display, depth) {\n"
"        throw new Error(\"Gradient filter not implemented\");\n"
"    }\n"
"\n"
"    _readData(sock) {\n"
"        if (this._len === 0) {\n"
"            if (sock.rQwait(\"TIGHT\", 3)) {\n"
"                return null;\n"
"            }\n"
"\n"
"            let byte;\n"
"\n"
"            byte = sock.rQshift8();\n"
"            this._len = byte & 0x7f;\n"
"            if (byte & 0x80) {\n"
"                byte = sock.rQshift8();\n"
"                this._len |= (byte & 0x7f) << 7;\n"
"                if (byte & 0x80) {\n"
"                    byte = sock.rQshift8();\n"
"                    this._len |= byte << 14;\n"
"                }\n"
"            }\n"
"        }\n"
"\n"
"        if (sock.rQwait(\"TIGHT\", this._len)) {\n"
"            return null;\n"
"        }\n"
"\n"
"        let data = sock.rQshiftBytes(this._len);\n"
"        this._len = 0;\n"
"\n"
"        return data;\n"
"    }\n"
"\n"
"    _getScratchBuffer(size) {\n"
"        if (!this._scratchBuffer || (this._scratchBuffer.length < size)) {\n"
"            this._scratchBuffer = new Uint8Array(size);\n"
"        }\n"
"        return this._scratchBuffer;\n"
"    }\n"
"}\n"
"window.TightDecoder = TightDecoder;\n"
;
    if (strcmp(path, "/novnc/decoders/tight.js") == 0) return novnc_decoders_tight_js;
const char *novnc_decoders_tightpng_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2019 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" *\n"
" */\n"
"\n"
"\n"
"class TightPNGDecoder extends TightDecoder {\n"
"    _pngRect(x, y, width, height, sock, display, depth) {\n"
"        let data = this._readData(sock);\n"
"        if (data === null) {\n"
"            return false;\n"
"        }\n"
"\n"
"        display.imageRect(x, y, width, height, \"image/png\", data);\n"
"\n"
"        return true;\n"
"    }\n"
"\n"
"    _basicRect(ctl, x, y, width, height, sock, display, depth) {\n"
"        throw new Error(\"BasicCompression received in TightPNG rect\");\n"
"    }\n"
"}\n"
"window.TightPNGDecoder = TightPNGDecoder;\n"
;
    if (strcmp(path, "/novnc/decoders/tightpng.js") == 0) return novnc_decoders_tightpng_js;
const char *novnc_decoders_zrle_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2021 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" *\n"
" */\n"
"\n"
"\n"
"const ZRLE_TILE_WIDTH = 64;\n"
"const ZRLE_TILE_HEIGHT = 64;\n"
"\n"
"class ZRLEDecoder {\n"
"    constructor() {\n"
"        this._length = 0;\n"
"        this._inflator = new Inflate();\n"
"\n"
"        this._pixelBuffer = new Uint8Array(ZRLE_TILE_WIDTH * ZRLE_TILE_HEIGHT * 4);\n"
"        this._tileBuffer = new Uint8Array(ZRLE_TILE_WIDTH * ZRLE_TILE_HEIGHT * 4);\n"
"    }\n"
"\n"
"    decodeRect(x, y, width, height, sock, display, depth) {\n"
"        if (this._length === 0) {\n"
"            if (sock.rQwait(\"ZLib data length\", 4)) {\n"
"                return false;\n"
"            }\n"
"            this._length = sock.rQshift32();\n"
"        }\n"
"        if (sock.rQwait(\"Zlib data\", this._length)) {\n"
"            return false;\n"
"        }\n"
"\n"
"        const data = sock.rQshiftBytes(this._length);\n"
"\n"
"        this._inflator.setInput(data);\n"
"\n"
"        for (let ty = y; ty < y + height; ty += ZRLE_TILE_HEIGHT) {\n"
"            let th = Math.min(ZRLE_TILE_HEIGHT, y + height - ty);\n"
"\n"
"            for (let tx = x; tx < x + width; tx += ZRLE_TILE_WIDTH) {\n"
"                let tw = Math.min(ZRLE_TILE_WIDTH, x + width - tx);\n"
"\n"
"                const tileSize = tw * th;\n"
"                const subencoding = this._inflator.inflate(1)[0];\n"
"                if (subencoding === 0) {\n"
"                    // raw data\n"
"                    const data = this._readPixels(tileSize);\n"
"                    display.blitImage(tx, ty, tw, th, data, 0, false);\n"
"                } else if (subencoding === 1) {\n"
"                    // solid\n"
"                    const background = this._readPixels(1);\n"
"                    display.fillRect(tx, ty, tw, th, [background[0], background[1], background[2]]);\n"
"                } else if (subencoding >= 2 && subencoding <= 16) {\n"
"                    const data = this._decodePaletteTile(subencoding, tileSize, tw, th);\n"
"                    display.blitImage(tx, ty, tw, th, data, 0, false);\n"
"                } else if (subencoding === 128) {\n"
"                    const data = this._decodeRLETile(tileSize);\n"
"                    display.blitImage(tx, ty, tw, th, data, 0, false);\n"
"                } else if (subencoding >= 130 && subencoding <= 255) {\n"
"                    const data = this._decodeRLEPaletteTile(subencoding - 128, tileSize);\n"
"                    display.blitImage(tx, ty, tw, th, data, 0, false);\n"
"                } else {\n"
"                    throw new Error('Unknown subencoding: ' + subencoding);\n"
"                }\n"
"            }\n"
"        }\n"
"        this._length = 0;\n"
"        return true;\n"
"    }\n"
"\n"
"    _getBitsPerPixelInPalette(paletteSize) {\n"
"        if (paletteSize <= 2) {\n"
"            return 1;\n"
"        } else if (paletteSize <= 4) {\n"
"            return 2;\n"
"        } else if (paletteSize <= 16) {\n"
"            return 4;\n"
"        }\n"
"    }\n"
"\n"
"    _readPixels(pixels) {\n"
"        let data = this._pixelBuffer;\n"
"        const buffer = this._inflator.inflate(3*pixels);\n"
"        for (let i = 0, j = 0; i < pixels*4; i += 4, j += 3) {\n"
"            data[i]     = buffer[j];\n"
"            data[i + 1] = buffer[j + 1];\n"
"            data[i + 2] = buffer[j + 2];\n"
"            data[i + 3] = 255;  // Add the Alpha\n"
"        }\n"
"        return data;\n"
"    }\n"
"\n"
"    _decodePaletteTile(paletteSize, tileSize, tilew, tileh) {\n"
"        const data = this._tileBuffer;\n"
"        const palette = this._readPixels(paletteSize);\n"
"        const bitsPerPixel = this._getBitsPerPixelInPalette(paletteSize);\n"
"        const mask = (1 << bitsPerPixel) - 1;\n"
"\n"
"        let offset = 0;\n"
"        let encoded = this._inflator.inflate(1)[0];\n"
"\n"
"        for (let y=0; y<tileh; y++) {\n"
"            let shift = 8-bitsPerPixel;\n"
"            for (let x=0; x<tilew; x++) {\n"
"                if (shift<0) {\n"
"                    shift=8-bitsPerPixel;\n"
"                    encoded = this._inflator.inflate(1)[0];\n"
"                }\n"
"                let indexInPalette = (encoded>>shift) & mask;\n"
"\n"
"                data[offset] = palette[indexInPalette * 4];\n"
"                data[offset + 1] = palette[indexInPalette * 4 + 1];\n"
"                data[offset + 2] = palette[indexInPalette * 4 + 2];\n"
"                data[offset + 3] = palette[indexInPalette * 4 + 3];\n"
"                offset += 4;\n"
"                shift-=bitsPerPixel;\n"
"            }\n"
"            if (shift<8-bitsPerPixel && y<tileh-1) {\n"
"                encoded =  this._inflator.inflate(1)[0];\n"
"            }\n"
"        }\n"
"        return data;\n"
"    }\n"
"\n"
"    _decodeRLETile(tileSize) {\n"
"        const data = this._tileBuffer;\n"
"        let i = 0;\n"
"        while (i < tileSize) {\n"
"            const pixel = this._readPixels(1);\n"
"            const length = this._readRLELength();\n"
"            for (let j = 0; j < length; j++) {\n"
"                data[i * 4] = pixel[0];\n"
"                data[i * 4 + 1] = pixel[1];\n"
"                data[i * 4 + 2] = pixel[2];\n"
"                data[i * 4 + 3] = pixel[3];\n"
"                i++;\n"
"            }\n"
"        }\n"
"        return data;\n"
"    }\n"
"\n"
"    _decodeRLEPaletteTile(paletteSize, tileSize) {\n"
"        const data = this._tileBuffer;\n"
"\n"
"        // palette\n"
"        const palette = this._readPixels(paletteSize);\n"
"\n"
"        let offset = 0;\n"
"        while (offset < tileSize) {\n"
"            let indexInPalette = this._inflator.inflate(1)[0];\n"
"            let length = 1;\n"
"            if (indexInPalette >= 128) {\n"
"                indexInPalette -= 128;\n"
"                length = this._readRLELength();\n"
"            }\n"
"            if (indexInPalette > paletteSize) {\n"
"                throw new Error('Too big index in palette: ' + indexInPalette + ', palette size: ' + paletteSize);\n"
"            }\n"
"            if (offset + length > tileSize) {\n"
"                throw new Error('Too big rle length in palette mode: ' + length + ', allowed length is: ' + (tileSize - offset));\n"
"            }\n"
"\n"
"            for (let j = 0; j < length; j++) {\n"
"                data[offset * 4] = palette[indexInPalette * 4];\n"
"                data[offset * 4 + 1] = palette[indexInPalette * 4 + 1];\n"
"                data[offset * 4 + 2] = palette[indexInPalette * 4 + 2];\n"
"                data[offset * 4 + 3] = palette[indexInPalette * 4 + 3];\n"
"                offset++;\n"
"            }\n"
"        }\n"
"        return data;\n"
"    }\n"
"\n"
"    _readRLELength() {\n"
"        let length = 0;\n"
"        let current = 0;\n"
"        do {\n"
"            current = this._inflator.inflate(1)[0];\n"
"            length += current;\n"
"        } while (current === 255);\n"
"        return length + 1;\n"
"    }\n"
"}\n"
"window.ZRLE_TILE_WIDTH = ZRLE_TILE_WIDTH;\n"
"window.ZRLE_TILE_HEIGHT = ZRLE_TILE_HEIGHT;\n"
"window.ZRLEDecoder = ZRLEDecoder;\n"
;
    if (strcmp(path, "/novnc/decoders/zrle.js") == 0) return novnc_decoders_zrle_js;
const char *novnc_deflator_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2020 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" */\n"
"\n"
"\n"
"class Deflator {\n"
"    constructor() {\n"
"        this.strm = new ZStream();\n"
"        this.chunkSize = 1024 * 10 * 10;\n"
"        this.outputBuffer = new Uint8Array(this.chunkSize);\n"
"        this.windowBits = 5;\n"
"\n"
"        deflateInit(this.strm, this.windowBits);\n"
"    }\n"
"\n"
"    deflate(inData) {\n"
"        /* eslint-disable camelcase */\n"
"        this.strm.input = inData;\n"
"        this.strm.avail_in = this.strm.input.length;\n"
"        this.strm.next_in = 0;\n"
"        this.strm.output = this.outputBuffer;\n"
"        this.strm.avail_out = this.chunkSize;\n"
"        this.strm.next_out = 0;\n"
"        /* eslint-enable camelcase */\n"
"\n"
"        let lastRet = deflate(this.strm, Z_FULL_FLUSH);\n"
"        let outData = new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);\n"
"\n"
"        if (lastRet < 0) {\n"
"            throw new Error(\"zlib deflate failed\");\n"
"        }\n"
"\n"
"        if (this.strm.avail_in > 0) {\n"
"            // Read chunks until done\n"
"\n"
"            let chunks = [outData];\n"
"            let totalLen = outData.length;\n"
"            do {\n"
"                /* eslint-disable camelcase */\n"
"                this.strm.output = new Uint8Array(this.chunkSize);\n"
"                this.strm.next_out = 0;\n"
"                this.strm.avail_out = this.chunkSize;\n"
"                /* eslint-enable camelcase */\n"
"\n"
"                lastRet = deflate(this.strm, Z_FULL_FLUSH);\n"
"\n"
"                if (lastRet < 0) {\n"
"                    throw new Error(\"zlib deflate failed\");\n"
"                }\n"
"\n"
"                let chunk = new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);\n"
"                totalLen += chunk.length;\n"
"                chunks.push(chunk);\n"
"            } while (this.strm.avail_in > 0);\n"
"\n"
"            // Combine chunks into a single data\n"
"\n"
"            let newData = new Uint8Array(totalLen);\n"
"            let offset = 0;\n"
"\n"
"            for (let i = 0; i < chunks.length; i++) {\n"
"                newData.set(chunks[i], offset);\n"
"                offset += chunks[i].length;\n"
"            }\n"
"\n"
"            outData = newData;\n"
"        }\n"
"\n"
"        /* eslint-disable camelcase */\n"
"        this.strm.input = null;\n"
"        this.strm.avail_in = 0;\n"
"        this.strm.next_in = 0;\n"
"        /* eslint-enable camelcase */\n"
"\n"
"        return outData;\n"
"    }\n"
"\n"
"}\n"
"window.Deflator = Deflator;\n"
;
    if (strcmp(path, "/novnc/deflator.js") == 0) return novnc_deflator_js;
const char *novnc_des_js =
"/*\n"
" * Ported from Flashlight VNC ActionScript implementation:\n"
" *     http://www.wizhelp.com/flashlight-vnc/\n"
" *\n"
" * Full attribution follows:\n"
" *\n"
" * -------------------------------------------------------------------------\n"
" *\n"
" * This DES class has been extracted from package Acme.Crypto for use in VNC.\n"
" * The unnecessary odd parity code has been removed.\n"
" *\n"
" * These changes are:\n"
" *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.\n"
" *\n"
" * This software is distributed in the hope that it will be useful,\n"
" * but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
" *\n"
"\n"
" * DesCipher - the DES encryption method\n"
" *\n"
" * The meat of this code is by Dave Zimmerman <dzimm@widget.com>, and is:\n"
" *\n"
" * Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved.\n"
" *\n"
" * Permission to use, copy, modify, and distribute this software\n"
" * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and\n"
" * without fee is hereby granted, provided that this copyright notice is kept\n"
" * intact.\n"
" *\n"
" * WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY\n"
" * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\n"
" * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\n"
" * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE LIABLE\n"
" * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR\n"
" * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.\n"
" *\n"
" * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE\n"
" * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE\n"
" * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT\n"
" * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE\n"
" * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE\n"
" * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE\n"
" * PHYSICAL OR ENVIRONMENTAL DAMAGE (\"HIGH RISK ACTIVITIES\").  WIDGET WORKSHOP\n"
" * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR\n"
" * HIGH RISK ACTIVITIES.\n"
" *\n"
" *\n"
" * The rest is:\n"
" *\n"
" * Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>.  All rights reserved.\n"
" *\n"
" * Redistribution and use in source and binary forms, with or without\n"
" * modification, are permitted provided that the following conditions\n"
" * are met:\n"
" * 1. Redistributions of source code must retain the above copyright\n"
" *    notice, this list of conditions and the following disclaimer.\n"
" * 2. Redistributions in binary form must reproduce the above copyright\n"
" *    notice, this list of conditions and the following disclaimer in the\n"
" *    documentation and/or other materials provided with the distribution.\n"
" *\n"
" * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n"
" * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n"
" * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
" * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n"
" * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n"
" * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n"
" * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n"
" * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n"
" * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n"
" * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n"
" * SUCH DAMAGE.\n"
" *\n"
" * Visit the ACME Labs Java page for up-to-date versions of this and other\n"
" * fine Java utilities: http://www.acme.com/java/\n"
" */\n"
"\n"
"/* eslint-disable comma-spacing */\n"
"\n"
"// Tables, permutations, S-boxes, etc.\n"
"const PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,\n"
"             25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,\n"
"             50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],\n"
"      totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28];\n"
"\n"
"const z = 0x0;\n"
"let a,b,c,d,e,f;\n"
"a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e;\n"
"const SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d,\n"
"             z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z,\n"
"             a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f,\n"
"             c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d];\n"
"a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e;\n"
"const SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d,\n"
"             a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f,\n"
"             z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z,\n"
"             z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e];\n"
"a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e;\n"
"const SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f,\n"
"             b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z,\n"
"             c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d,\n"
"             b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e];\n"
"a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e;\n"
"const SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d,\n"
"             z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f,\n"
"             b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e,\n"
"             c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e];\n"
"a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e;\n"
"const SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z,\n"
"             a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f,\n"
"             z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e,\n"
"             c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d];\n"
"a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e;\n"
"const SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f,\n"
"             z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z,\n"
"             b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z,\n"
"             a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f];\n"
"a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e;\n"
"const SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f,\n"
"             b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e,\n"
"             b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e,\n"
"             z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d];\n"
"a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e;\n"
"const SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d,\n"
"             c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z,\n"
"             a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f,\n"
"             z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e];\n"
"\n"
"/* eslint-enable comma-spacing */\n"
"\n"
"class DES {\n"
"    constructor(password) {\n"
"        this.keys = [];\n"
"\n"
"        // Set the key.\n"
"        const pc1m = [], pcr = [], kn = [];\n"
"\n"
"        for (let j = 0, l = 56; j < 56; ++j, l -= 8) {\n"
"            l += l < -5 ? 65 : l < -3 ? 31 : l < -1 ? 63 : l === 27 ? 35 : 0; // PC1\n"
"            const m = l & 0x7;\n"
"            pc1m[j] = ((password[l >>> 3] & (1<<m)) !== 0) ? 1: 0;\n"
"        }\n"
"\n"
"        for (let i = 0; i < 16; ++i) {\n"
"            const m = i << 1;\n"
"            const n = m + 1;\n"
"            kn[m] = kn[n] = 0;\n"
"            for (let o = 28; o < 59; o += 28) {\n"
"                for (let j = o - 28; j < o; ++j) {\n"
"                    const l = j + totrot[i];\n"
"                    pcr[j] = l < o ? pc1m[l] : pc1m[l - 28];\n"
"                }\n"
"            }\n"
"            for (let j = 0; j < 24; ++j) {\n"
"                if (pcr[PC2[j]] !== 0) {\n"
"                    kn[m] |= 1 << (23 - j);\n"
"                }\n"
"                if (pcr[PC2[j + 24]] !== 0) {\n"
"                    kn[n] |= 1 << (23 - j);\n"
"                }\n"
"            }\n"
"        }\n"
"\n"
"        // cookey\n"
"        for (let i = 0, rawi = 0, KnLi = 0; i < 16; ++i) {\n"
"            const raw0 = kn[rawi++];\n"
"            const raw1 = kn[rawi++];\n"
"            this.keys[KnLi] = (raw0 & 0x00fc0000) << 6;\n"
"            this.keys[KnLi] |= (raw0 & 0x00000fc0) << 10;\n"
"            this.keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10;\n"
"            this.keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6;\n"
"            ++KnLi;\n"
"            this.keys[KnLi] = (raw0 & 0x0003f000) << 12;\n"
"            this.keys[KnLi] |= (raw0 & 0x0000003f) << 16;\n"
"            this.keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;\n"
"            this.keys[KnLi] |= (raw1 & 0x0000003f);\n"
"            ++KnLi;\n"
"        }\n"
"    }\n"
"\n"
"    // Encrypt 8 bytes of text\n"
"    enc8(text) {\n"
"        const b = text.slice();\n"
"        let i = 0, l, r, x; // left, right, accumulator\n"
"\n"
"        // Squash 8 bytes to 2 ints\n"
"        l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];\n"
"        r = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];\n"
"\n"
"        x = ((l >>> 4) ^ r) & 0x0f0f0f0f;\n"
"        r ^= x;\n"
"        l ^= (x << 4);\n"
"        x = ((l >>> 16) ^ r) & 0x0000ffff;\n"
"        r ^= x;\n"
"        l ^= (x << 16);\n"
"        x = ((r >>> 2) ^ l) & 0x33333333;\n"
"        l ^= x;\n"
"        r ^= (x << 2);\n"
"        x = ((r >>> 8) ^ l) & 0x00ff00ff;\n"
"        l ^= x;\n"
"        r ^= (x << 8);\n"
"        r = (r << 1) | ((r >>> 31) & 1);\n"
"        x = (l ^ r) & 0xaaaaaaaa;\n"
"        l ^= x;\n"
"        r ^= x;\n"
"        l = (l << 1) | ((l >>> 31) & 1);\n"
"\n"
"        for (let i = 0, keysi = 0; i < 8; ++i) {\n"
"            x = (r << 28) | (r >>> 4);\n"
"            x ^= this.keys[keysi++];\n"
"            let fval =  SP7[x & 0x3f];\n"
"            fval |= SP5[(x >>> 8) & 0x3f];\n"
"            fval |= SP3[(x >>> 16) & 0x3f];\n"
"            fval |= SP1[(x >>> 24) & 0x3f];\n"
"            x = r ^ this.keys[keysi++];\n"
"            fval |= SP8[x & 0x3f];\n"
"            fval |= SP6[(x >>> 8) & 0x3f];\n"
"            fval |= SP4[(x >>> 16) & 0x3f];\n"
"            fval |= SP2[(x >>> 24) & 0x3f];\n"
"            l ^= fval;\n"
"            x = (l << 28) | (l >>> 4);\n"
"            x ^= this.keys[keysi++];\n"
"            fval =  SP7[x & 0x3f];\n"
"            fval |= SP5[(x >>> 8) & 0x3f];\n"
"            fval |= SP3[(x >>> 16) & 0x3f];\n"
"            fval |= SP1[(x >>> 24) & 0x3f];\n"
"            x = l ^ this.keys[keysi++];\n"
"            fval |= SP8[x & 0x0000003f];\n"
"            fval |= SP6[(x >>> 8) & 0x3f];\n"
"            fval |= SP4[(x >>> 16) & 0x3f];\n"
"            fval |= SP2[(x >>> 24) & 0x3f];\n"
"            r ^= fval;\n"
"        }\n"
"\n"
"        r = (r << 31) | (r >>> 1);\n"
"        x = (l ^ r) & 0xaaaaaaaa;\n"
"        l ^= x;\n"
"        r ^= x;\n"
"        l = (l << 31) | (l >>> 1);\n"
"        x = ((l >>> 8) ^ r) & 0x00ff00ff;\n"
"        r ^= x;\n"
"        l ^= (x << 8);\n"
"        x = ((l >>> 2) ^ r) & 0x33333333;\n"
"        r ^= x;\n"
"        l ^= (x << 2);\n"
"        x = ((r >>> 16) ^ l) & 0x0000ffff;\n"
"        l ^= x;\n"
"        r ^= (x << 16);\n"
"        x = ((r >>> 4) ^ l) & 0x0f0f0f0f;\n"
"        l ^= x;\n"
"        r ^= (x << 4);\n"
"\n"
"        // Spread ints to bytes\n"
"        x = [r, l];\n"
"        for (i = 0; i < 8; i++) {\n"
"            b[i] = (x[i>>>2] >>> (8 * (3 - (i % 4)))) % 256;\n"
"            if (b[i] < 0) { b[i] += 256; } // unsigned\n"
"        }\n"
"        return b;\n"
"    }\n"
"\n"
"    // Encrypt 16 bytes of text using passwd as key\n"
"    encrypt(t) {\n"
"        return this.enc8(t.slice(0, 8)).concat(this.enc8(t.slice(8, 16)));\n"
"    }\n"
"}\n"
"window.PC2 = PC2;\n"
"window.z = z;\n"
"window.a = a;\n"
"window.SP1 = SP1;\n"
"window.SP2 = SP2;\n"
"window.SP3 = SP3;\n"
"window.SP4 = SP4;\n"
"window.SP5 = SP5;\n"
"window.SP6 = SP6;\n"
"window.SP7 = SP7;\n"
"window.SP8 = SP8;\n"
"window.DES = DES;\n"
;
    if (strcmp(path, "/novnc/des.js") == 0) return novnc_des_js;
const char *novnc_display_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2019 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" */\n"
"\n"
"\n"
"class Display {\n"
"    constructor(target) {\n"
"        this._drawCtx = null;\n"
"\n"
"        this._renderQ = [];  // queue drawing actions for in-oder rendering\n"
"        this._flushing = false;\n"
"\n"
"        // the full frame buffer (logical canvas) size\n"
"        this._fbWidth = 0;\n"
"        this._fbHeight = 0;\n"
"\n"
"        this._prevDrawStyle = \"\";\n"
"\n"
"        Log.Debug(\">> Display.constructor\");\n"
"\n"
"        // The visible canvas\n"
"        this._target = target;\n"
"\n"
"        if (!this._target) {\n"
"            throw new Error(\"Target must be set\");\n"
"        }\n"
"\n"
"        if (typeof this._target === 'string') {\n"
"            throw new Error('target must be a DOM element');\n"
"        }\n"
"\n"
"        if (!this._target.getContext) {\n"
"            throw new Error(\"no getContext method\");\n"
"        }\n"
"\n"
"        this._targetCtx = this._target.getContext('2d');\n"
"\n"
"        // the visible canvas viewport (i.e. what actually gets seen)\n"
"        this._viewportLoc = { 'x': 0, 'y': 0, 'w': this._target.width, 'h': this._target.height };\n"
"\n"
"        // The hidden canvas, where we do the actual rendering\n"
"        this._backbuffer = document.createElement('canvas');\n"
"        this._drawCtx = this._backbuffer.getContext('2d');\n"
"\n"
"        this._damageBounds = { left: 0, top: 0,\n"
"                               right: this._backbuffer.width,\n"
"                               bottom: this._backbuffer.height };\n"
"\n"
"        Log.Debug(\"User Agent: \" + navigator.userAgent);\n"
"\n"
"        Log.Debug(\"<< Display.constructor\");\n"
"\n"
"        // ===== PROPERTIES =====\n"
"\n"
"        this._scale = 1.0;\n"
"        this._clipViewport = false;\n"
"\n"
"        // ===== EVENT HANDLERS =====\n"
"\n"
"        this.onflush = () => {}; // A flush request has finished\n"
"    }\n"
"\n"
"    // ===== PROPERTIES =====\n"
"\n"
"    get scale() { return this._scale; }\n"
"    set scale(scale) {\n"
"        this._rescale(scale);\n"
"    }\n"
"\n"
"    get clipViewport() { return this._clipViewport; }\n"
"    set clipViewport(viewport) {\n"
"        this._clipViewport = viewport;\n"
"        // May need to readjust the viewport dimensions\n"
"        const vp = this._viewportLoc;\n"
"        this.viewportChangeSize(vp.w, vp.h);\n"
"        this.viewportChangePos(0, 0);\n"
"    }\n"
"\n"
"    get width() {\n"
"        return this._fbWidth;\n"
"    }\n"
"\n"
"    get height() {\n"
"        return this._fbHeight;\n"
"    }\n"
"\n"
"    // ===== PUBLIC METHODS =====\n"
"\n"
"    viewportChangePos(deltaX, deltaY) {\n"
"        const vp = this._viewportLoc;\n"
"        deltaX = Math.floor(deltaX);\n"
"        deltaY = Math.floor(deltaY);\n"
"\n"
"        if (!this._clipViewport) {\n"
"            deltaX = -vp.w;  // clamped later of out of bounds\n"
"            deltaY = -vp.h;\n"
"        }\n"
"\n"
"        const vx2 = vp.x + vp.w - 1;\n"
"        const vy2 = vp.y + vp.h - 1;\n"
"\n"
"        // Position change\n"
"\n"
"        if (deltaX < 0 && vp.x + deltaX < 0) {\n"
"            deltaX = -vp.x;\n"
"        }\n"
"        if (vx2 + deltaX >= this._fbWidth) {\n"
"            deltaX -= vx2 + deltaX - this._fbWidth + 1;\n"
"        }\n"
"\n"
"        if (vp.y + deltaY < 0) {\n"
"            deltaY = -vp.y;\n"
"        }\n"
"        if (vy2 + deltaY >= this._fbHeight) {\n"
"            deltaY -= (vy2 + deltaY - this._fbHeight + 1);\n"
"        }\n"
"\n"
"        if (deltaX === 0 && deltaY === 0) {\n"
"            return;\n"
"        }\n"
"        Log.Debug(\"viewportChange deltaX: \" + deltaX + \", deltaY: \" + deltaY);\n"
"\n"
"        vp.x += deltaX;\n"
"        vp.y += deltaY;\n"
"\n"
"        this._damage(vp.x, vp.y, vp.w, vp.h);\n"
"\n"
"        this.flip();\n"
"    }\n"
"\n"
"    viewportChangeSize(width, height) {\n"
"\n"
"        if (!this._clipViewport ||\n"
"            typeof(width) === \"undefined\" ||\n"
"            typeof(height) === \"undefined\") {\n"
"\n"
"            Log.Debug(\"Setting viewport to full display region\");\n"
"            width = this._fbWidth;\n"
"            height = this._fbHeight;\n"
"        }\n"
"\n"
"        width = Math.floor(width);\n"
"        height = Math.floor(height);\n"
"\n"
"        if (width > this._fbWidth) {\n"
"            width = this._fbWidth;\n"
"        }\n"
"        if (height > this._fbHeight) {\n"
"            height = this._fbHeight;\n"
"        }\n"
"\n"
"        const vp = this._viewportLoc;\n"
"        if (vp.w !== width || vp.h !== height) {\n"
"            vp.w = width;\n"
"            vp.h = height;\n"
"\n"
"            const canvas = this._target;\n"
"            canvas.width = width;\n"
"            canvas.height = height;\n"
"\n"
"            // The position might need to be updated if we've grown\n"
"            this.viewportChangePos(0, 0);\n"
"\n"
"            this._damage(vp.x, vp.y, vp.w, vp.h);\n"
"            this.flip();\n"
"\n"
"            // Update the visible size of the target canvas\n"
"            this._rescale(this._scale);\n"
"        }\n"
"    }\n"
"\n"
"    absX(x) {\n"
"        if (this._scale === 0) {\n"
"            return 0;\n"
"        }\n"
"        return toSigned32bit(x / this._scale + this._viewportLoc.x);\n"
"    }\n"
"\n"
"    absY(y) {\n"
"        if (this._scale === 0) {\n"
"            return 0;\n"
"        }\n"
"        return toSigned32bit(y / this._scale + this._viewportLoc.y);\n"
"    }\n"
"\n"
"    resize(width, height) {\n"
"        this._prevDrawStyle = \"\";\n"
"\n"
"        this._fbWidth = width;\n"
"        this._fbHeight = height;\n"
"\n"
"        const canvas = this._backbuffer;\n"
"        if (canvas.width !== width || canvas.height !== height) {\n"
"\n"
"            // We have to save the canvas data since changing the size will clear it\n"
"            let saveImg = null;\n"
"            if (canvas.width > 0 && canvas.height > 0) {\n"
"                saveImg = this._drawCtx.getImageData(0, 0, canvas.width, canvas.height);\n"
"            }\n"
"\n"
"            if (canvas.width !== width) {\n"
"                canvas.width = width;\n"
"            }\n"
"            if (canvas.height !== height) {\n"
"                canvas.height = height;\n"
"            }\n"
"\n"
"            if (saveImg) {\n"
"                this._drawCtx.putImageData(saveImg, 0, 0);\n"
"            }\n"
"        }\n"
"\n"
"        // Readjust the viewport as it may be incorrectly sized\n"
"        // and positioned\n"
"        const vp = this._viewportLoc;\n"
"        this.viewportChangeSize(vp.w, vp.h);\n"
"        this.viewportChangePos(0, 0);\n"
"    }\n"
"\n"
"    getImageData() {\n"
"        return this._drawCtx.getImageData(0, 0, this.width, this.height);\n"
"    }\n"
"\n"
"    toDataURL(type, encoderOptions) {\n"
"        return this._backbuffer.toDataURL(type, encoderOptions);\n"
"    }\n"
"\n"
"    toBlob(callback, type, quality) {\n"
"        return this._backbuffer.toBlob(callback, type, quality);\n"
"    }\n"
"\n"
"    // Track what parts of the visible canvas that need updating\n"
"    _damage(x, y, w, h) {\n"
"        if (x < this._damageBounds.left) {\n"
"            this._damageBounds.left = x;\n"
"        }\n"
"        if (y < this._damageBounds.top) {\n"
"            this._damageBounds.top = y;\n"
"        }\n"
"        if ((x + w) > this._damageBounds.right) {\n"
"            this._damageBounds.right = x + w;\n"
"        }\n"
"        if ((y + h) > this._damageBounds.bottom) {\n"
"            this._damageBounds.bottom = y + h;\n"
"        }\n"
"    }\n"
"\n"
"    // Update the visible canvas with the contents of the\n"
"    // rendering canvas\n"
"    flip(fromQueue) {\n"
"        if (this._renderQ.length !== 0 && !fromQueue) {\n"
"            this._renderQPush({\n"
"                'type': 'flip'\n"
"            });\n"
"        } else {\n"
"            let x = this._damageBounds.left;\n"
"            let y = this._damageBounds.top;\n"
"            let w = this._damageBounds.right - x;\n"
"            let h = this._damageBounds.bottom - y;\n"
"\n"
"            let vx = x - this._viewportLoc.x;\n"
"            let vy = y - this._viewportLoc.y;\n"
"\n"
"            if (vx < 0) {\n"
"                w += vx;\n"
"                x -= vx;\n"
"                vx = 0;\n"
"            }\n"
"            if (vy < 0) {\n"
"                h += vy;\n"
"                y -= vy;\n"
"                vy = 0;\n"
"            }\n"
"\n"
"            if ((vx + w) > this._viewportLoc.w) {\n"
"                w = this._viewportLoc.w - vx;\n"
"            }\n"
"            if ((vy + h) > this._viewportLoc.h) {\n"
"                h = this._viewportLoc.h - vy;\n"
"            }\n"
"\n"
"            if ((w > 0) && (h > 0)) {\n"
"                // FIXME: We may need to disable image smoothing here\n"
"                //        as well (see copyImage()), but we haven't\n"
"                //        noticed any problem yet.\n"
"                this._targetCtx.drawImage(this._backbuffer,\n"
"                                          x, y, w, h,\n"
"                                          vx, vy, w, h);\n"
"            }\n"
"\n"
"            this._damageBounds.left = this._damageBounds.top = 65535;\n"
"            this._damageBounds.right = this._damageBounds.bottom = 0;\n"
"        }\n"
"    }\n"
"\n"
"    pending() {\n"
"        return this._renderQ.length > 0;\n"
"    }\n"
"\n"
"    flush() {\n"
"        if (this._renderQ.length === 0) {\n"
"            this.onflush();\n"
"        } else {\n"
"            this._flushing = true;\n"
"        }\n"
"    }\n"
"\n"
"    fillRect(x, y, width, height, color, fromQueue) {\n"
"        if (this._renderQ.length !== 0 && !fromQueue) {\n"
"            this._renderQPush({\n"
"                'type': 'fill',\n"
"                'x': x,\n"
"                'y': y,\n"
"                'width': width,\n"
"                'height': height,\n"
"                'color': color\n"
"            });\n"
"        } else {\n"
"            this._setFillColor(color);\n"
"            this._drawCtx.fillRect(x, y, width, height);\n"
"            this._damage(x, y, width, height);\n"
"        }\n"
"    }\n"
"\n"
"    copyImage(oldX, oldY, newX, newY, w, h, fromQueue) {\n"
"        if (this._renderQ.length !== 0 && !fromQueue) {\n"
"            this._renderQPush({\n"
"                'type': 'copy',\n"
"                'oldX': oldX,\n"
"                'oldY': oldY,\n"
"                'x': newX,\n"
"                'y': newY,\n"
"                'width': w,\n"
"                'height': h,\n"
"            });\n"
"        } else {\n"
"            // Due to this bug among others [1] we need to disable the image-smoothing to\n"
"            // avoid getting a blur effect when copying data.\n"
"            //\n"
"            // 1. https://bugzilla.mozilla.org/show_bug.cgi?id=1194719\n"
"            //\n"
"            // We need to set these every time since all properties are reset\n"
"            // when the the size is changed\n"
"            this._drawCtx.mozImageSmoothingEnabled = false;\n"
"            this._drawCtx.webkitImageSmoothingEnabled = false;\n"
"            this._drawCtx.msImageSmoothingEnabled = false;\n"
"            this._drawCtx.imageSmoothingEnabled = false;\n"
"\n"
"            this._drawCtx.drawImage(this._backbuffer,\n"
"                                    oldX, oldY, w, h,\n"
"                                    newX, newY, w, h);\n"
"            this._damage(newX, newY, w, h);\n"
"        }\n"
"    }\n"
"\n"
"    imageRect(x, y, width, height, mime, arr) {\n"
"        /* The internal logic cannot handle empty images, so bail early */\n"
"        if ((width === 0) || (height === 0)) {\n"
"            return;\n"
"        }\n"
"\n"
"        const img = new Image();\n"
"        img.src = \"data: \" + mime + \";base64,\" + Base64.encode(arr);\n"
"\n"
"        this._renderQPush({\n"
"            'type': 'img',\n"
"            'img': img,\n"
"            'x': x,\n"
"            'y': y,\n"
"            'width': width,\n"
"            'height': height\n"
"        });\n"
"    }\n"
"\n"
"    blitImage(x, y, width, height, arr, offset, fromQueue) {\n"
"        if (this._renderQ.length !== 0 && !fromQueue) {\n"
"            // NB(directxman12): it's technically more performant here to use preallocated arrays,\n"
"            // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,\n"
"            // this probably isn't getting called *nearly* as much\n"
"            const newArr = new Uint8Array(width * height * 4);\n"
"            newArr.set(new Uint8Array(arr.buffer, 0, newArr.length));\n"
"            this._renderQPush({\n"
"                'type': 'blit',\n"
"                'data': newArr,\n"
"                'x': x,\n"
"                'y': y,\n"
"                'width': width,\n"
"                'height': height,\n"
"            });\n"
"        } else {\n"
"            // NB(directxman12): arr must be an Type Array view\n"
"            let data = new Uint8ClampedArray(arr.buffer,\n"
"                                             arr.byteOffset + offset,\n"
"                                             width * height * 4);\n"
"            let img = new ImageData(data, width, height);\n"
"            this._drawCtx.putImageData(img, x, y);\n"
"            this._damage(x, y, width, height);\n"
"        }\n"
"    }\n"
"\n"
"    drawImage(img, x, y) {\n"
"        this._drawCtx.drawImage(img, x, y);\n"
"        this._damage(x, y, img.width, img.height);\n"
"    }\n"
"\n"
"    autoscale(containerWidth, containerHeight) {\n"
"        let scaleRatio;\n"
"\n"
"        if (containerWidth === 0 || containerHeight === 0) {\n"
"            scaleRatio = 0;\n"
"\n"
"        } else {\n"
"\n"
"            const vp = this._viewportLoc;\n"
"            const targetAspectRatio = containerWidth / containerHeight;\n"
"            const fbAspectRatio = vp.w / vp.h;\n"
"\n"
"            if (fbAspectRatio >= targetAspectRatio) {\n"
"                scaleRatio = containerWidth / vp.w;\n"
"            } else {\n"
"                scaleRatio = containerHeight / vp.h;\n"
"            }\n"
"        }\n"
"\n"
"        this._rescale(scaleRatio);\n"
"    }\n"
"\n"
"    // ===== PRIVATE METHODS =====\n"
"\n"
"    _rescale(factor) {\n"
"        this._scale = factor;\n"
"        const vp = this._viewportLoc;\n"
"\n"
"        // NB(directxman12): If you set the width directly, or set the\n"
"        //                   style width to a number, the canvas is cleared.\n"
"        //                   However, if you set the style width to a string\n"
"        //                   ('NNNpx'), the canvas is scaled without clearing.\n"
"        const width = factor * vp.w + 'px';\n"
"        const height = factor * vp.h + 'px';\n"
"\n"
"        if ((this._target.style.width !== width) ||\n"
"            (this._target.style.height !== height)) {\n"
"            this._target.style.width = width;\n"
"            this._target.style.height = height;\n"
"        }\n"
"    }\n"
"\n"
"    _setFillColor(color) {\n"
"        const newStyle = 'rgb(' + color[0] + ',' + color[1] + ',' + color[2] + ')';\n"
"        if (newStyle !== this._prevDrawStyle) {\n"
"            this._drawCtx.fillStyle = newStyle;\n"
"            this._prevDrawStyle = newStyle;\n"
"        }\n"
"    }\n"
"\n"
"    _renderQPush(action) {\n"
"        this._renderQ.push(action);\n"
"        if (this._renderQ.length === 1) {\n"
"            // If this can be rendered immediately it will be, otherwise\n"
"            // the scanner will wait for the relevant event\n"
"            this._scanRenderQ();\n"
"        }\n"
"    }\n"
"\n"
"    _resumeRenderQ() {\n"
"        // \"this\" is the object that is ready, not the\n"
"        // display object\n"
"        this.removeEventListener('load', this._noVNCDisplay._resumeRenderQ);\n"
"        this._noVNCDisplay._scanRenderQ();\n"
"    }\n"
"\n"
"    _scanRenderQ() {\n"
"        let ready = true;\n"
"        while (ready && this._renderQ.length > 0) {\n"
"            const a = this._renderQ[0];\n"
"            switch (a.type) {\n"
"                case 'flip':\n"
"                    this.flip(true);\n"
"                    break;\n"
"                case 'copy':\n"
"                    this.copyImage(a.oldX, a.oldY, a.x, a.y, a.width, a.height, true);\n"
"                    break;\n"
"                case 'fill':\n"
"                    this.fillRect(a.x, a.y, a.width, a.height, a.color, true);\n"
"                    break;\n"
"                case 'blit':\n"
"                    this.blitImage(a.x, a.y, a.width, a.height, a.data, 0, true);\n"
"                    break;\n"
"                case 'img':\n"
"                    if (a.img.complete) {\n"
"                        if (a.img.width !== a.width || a.img.height !== a.height) {\n"
"                            Log.Error(\"Decoded image has incorrect dimensions. Got \" +\n"
"                                      a.img.width + \"x\" + a.img.height + \". Expected \" +\n"
"                                      a.width + \"x\" + a.height + \".\");\n"
"                            return;\n"
"                        }\n"
"                        this.drawImage(a.img, a.x, a.y);\n"
"                    } else {\n"
"                        a.img._noVNCDisplay = this;\n"
"                        a.img.addEventListener('load', this._resumeRenderQ);\n"
"                        // We need to wait for this image to 'load'\n"
"                        // to keep things in-order\n"
"                        ready = false;\n"
"                    }\n"
"                    break;\n"
"            }\n"
"\n"
"            if (ready) {\n"
"                this._renderQ.shift();\n"
"            }\n"
"        }\n"
"\n"
"        if (this._renderQ.length === 0 && this._flushing) {\n"
"            this._flushing = false;\n"
"            this.onflush();\n"
"        }\n"
"    }\n"
"}\n"
"window.Display = Display;\n"
;
    if (strcmp(path, "/novnc/display.js") == 0) return novnc_display_js;
const char *novnc_encodings_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2019 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" */\n"
"\n"
"const encodings = {\n"
"    encodingRaw: 0,\n"
"    encodingCopyRect: 1,\n"
"    encodingRRE: 2,\n"
"    encodingHextile: 5,\n"
"    encodingTight: 7,\n"
"    encodingZRLE: 16,\n"
"    encodingTightPNG: -260,\n"
"    encodingJPEG: 21,\n"
"\n"
"    pseudoEncodingQualityLevel9: -23,\n"
"    pseudoEncodingQualityLevel0: -32,\n"
"    pseudoEncodingDesktopSize: -223,\n"
"    pseudoEncodingLastRect: -224,\n"
"    pseudoEncodingCursor: -239,\n"
"    pseudoEncodingQEMUExtendedKeyEvent: -258,\n"
"    pseudoEncodingDesktopName: -307,\n"
"    pseudoEncodingExtendedDesktopSize: -308,\n"
"    pseudoEncodingXvp: -309,\n"
"    pseudoEncodingFence: -312,\n"
"    pseudoEncodingContinuousUpdates: -313,\n"
"    pseudoEncodingCompressLevel9: -247,\n"
"    pseudoEncodingCompressLevel0: -256,\n"
"    pseudoEncodingVMwareCursor: 0x574d5664,\n"
"    pseudoEncodingExtendedClipboard: 0xc0a1e5ce\n"
"};\n"
"\n"
"function encodingName(num) {\n"
"    switch (num) {\n"
"        case encodings.encodingRaw:      return \"Raw\";\n"
"        case encodings.encodingCopyRect: return \"CopyRect\";\n"
"        case encodings.encodingRRE:      return \"RRE\";\n"
"        case encodings.encodingHextile:  return \"Hextile\";\n"
"        case encodings.encodingTight:    return \"Tight\";\n"
"        case encodings.encodingZRLE:     return \"ZRLE\";\n"
"        case encodings.encodingTightPNG: return \"TightPNG\";\n"
"        case encodings.encodingJPEG:     return \"JPEG\";\n"
"        default:                         return \"[unknown encoding \" + num + \"]\";\n"
"    }\n"
"}\n"
"window.encodings = encodings;\n"
"window.encodingName = encodingName;\n"
;
    if (strcmp(path, "/novnc/encodings.js") == 0) return novnc_encodings_js;
const char *novnc_inflator_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2020 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" */\n"
"\n"
"\n"
"class Inflate {\n"
"    constructor() {\n"
"        this.strm = new ZStream();\n"
"        this.chunkSize = 1024 * 10 * 10;\n"
"        this.strm.output = new Uint8Array(this.chunkSize);\n"
"        this.windowBits = 5;\n"
"\n"
"        inflateInit(this.strm, this.windowBits);\n"
"    }\n"
"\n"
"    setInput(data) {\n"
"        if (!data) {\n"
"            //FIXME: flush remaining data.\n"
"            /* eslint-disable camelcase */\n"
"            this.strm.input = null;\n"
"            this.strm.avail_in = 0;\n"
"            this.strm.next_in = 0;\n"
"        } else {\n"
"            this.strm.input = data;\n"
"            this.strm.avail_in = this.strm.input.length;\n"
"            this.strm.next_in = 0;\n"
"            /* eslint-enable camelcase */\n"
"        }\n"
"    }\n"
"\n"
"    inflate(expected) {\n"
"        // resize our output buffer if it's too small\n"
"        // (we could just use multiple chunks, but that would cause an extra\n"
"        // allocation each time to flatten the chunks)\n"
"        if (expected > this.chunkSize) {\n"
"            this.chunkSize = expected;\n"
"            this.strm.output = new Uint8Array(this.chunkSize);\n"
"        }\n"
"\n"
"        /* eslint-disable camelcase */\n"
"        this.strm.next_out = 0;\n"
"        this.strm.avail_out = expected;\n"
"        /* eslint-enable camelcase */\n"
"\n"
"        let ret = inflate(this.strm, 0); // Flush argument not used.\n"
"        if (ret < 0) {\n"
"            throw new Error(\"zlib inflate failed\");\n"
"        }\n"
"\n"
"        if (this.strm.next_out != expected) {\n"
"            throw new Error(\"Incomplete zlib block\");\n"
"        }\n"
"\n"
"        return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);\n"
"    }\n"
"\n"
"    reset() {\n"
"        inflateReset(this.strm);\n"
"    }\n"
"}\n"
"window.Inflate = Inflate;\n"
;
    if (strcmp(path, "/novnc/inflator.js") == 0) return novnc_inflator_js;
const char *novnc_input_domkeytable_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2018 The noVNC Authors\n"
" * Licensed under MPL 2.0 or any later version (see LICENSE.txt)\n"
" */\n"
"\n"
"\n"
"/*\n"
" * Mapping between HTML key values and VNC/X11 keysyms for \"special\"\n"
" * keys that cannot be handled via their Unicode codepoint.\n"
" *\n"
" * See https://www.w3.org/TR/uievents-key/ for possible values.\n"
" */\n"
"\n"
"const DOMKeyTable = {};\n"
"\n"
"function addStandard(key, standard) {\n"
"    if (standard === undefined) throw new Error(\"Undefined keysym for key \\\"\" + key + \"\\\"\");\n"
"    if (key in DOMKeyTable) throw new Error(\"Duplicate entry for key \\\"\" + key + \"\\\"\");\n"
"    DOMKeyTable[key] = [standard, standard, standard, standard];\n"
"}\n"
"\n"
"function addLeftRight(key, left, right) {\n"
"    if (left === undefined) throw new Error(\"Undefined keysym for key \\\"\" + key + \"\\\"\");\n"
"    if (right === undefined) throw new Error(\"Undefined keysym for key \\\"\" + key + \"\\\"\");\n"
"    if (key in DOMKeyTable) throw new Error(\"Duplicate entry for key \\\"\" + key + \"\\\"\");\n"
"    DOMKeyTable[key] = [left, left, right, left];\n"
"}\n"
"\n"
"function addNumpad(key, standard, numpad) {\n"
"    if (standard === undefined) throw new Error(\"Undefined keysym for key \\\"\" + key + \"\\\"\");\n"
"    if (numpad === undefined) throw new Error(\"Undefined keysym for key \\\"\" + key + \"\\\"\");\n"
"    if (key in DOMKeyTable) throw new Error(\"Duplicate entry for key \\\"\" + key + \"\\\"\");\n"
"    DOMKeyTable[key] = [standard, standard, standard, numpad];\n"
"}\n"
"\n"
"// 3.2. Modifier Keys\n"
"\n"
"addLeftRight(\"Alt\", KeyTable.XK_Alt_L, KeyTable.XK_Alt_R);\n"
"addStandard(\"AltGraph\", KeyTable.XK_ISO_Level3_Shift);\n"
"addStandard(\"CapsLock\", KeyTable.XK_Caps_Lock);\n"
"addLeftRight(\"Control\", KeyTable.XK_Control_L, KeyTable.XK_Control_R);\n"
"// - Fn\n"
"// - FnLock\n"
"addLeftRight(\"Meta\", KeyTable.XK_Super_L, KeyTable.XK_Super_R);\n"
"addStandard(\"NumLock\", KeyTable.XK_Num_Lock);\n"
"addStandard(\"ScrollLock\", KeyTable.XK_Scroll_Lock);\n"
"addLeftRight(\"Shift\", KeyTable.XK_Shift_L, KeyTable.XK_Shift_R);\n"
"// - Symbol\n"
"// - SymbolLock\n"
"// - Hyper\n"
"// - Super\n"
"\n"
"// 3.3. Whitespace Keys\n"
"\n"
"addNumpad(\"Enter\", KeyTable.XK_Return, KeyTable.XK_KP_Enter);\n"
"addStandard(\"Tab\", KeyTable.XK_Tab);\n"
"addNumpad(\" \", KeyTable.XK_space, KeyTable.XK_KP_Space);\n"
"\n"
"// 3.4. Navigation Keys\n"
"\n"
"addNumpad(\"ArrowDown\", KeyTable.XK_Down, KeyTable.XK_KP_Down);\n"
"addNumpad(\"ArrowLeft\", KeyTable.XK_Left, KeyTable.XK_KP_Left);\n"
"addNumpad(\"ArrowRight\", KeyTable.XK_Right, KeyTable.XK_KP_Right);\n"
"addNumpad(\"ArrowUp\", KeyTable.XK_Up, KeyTable.XK_KP_Up);\n"
"addNumpad(\"End\", KeyTable.XK_End, KeyTable.XK_KP_End);\n"
"addNumpad(\"Home\", KeyTable.XK_Home, KeyTable.XK_KP_Home);\n"
"addNumpad(\"PageDown\", KeyTable.XK_Next, KeyTable.XK_KP_Next);\n"
"addNumpad(\"PageUp\", KeyTable.XK_Prior, KeyTable.XK_KP_Prior);\n"
"\n"
"// 3.5. Editing Keys\n"
"\n"
"addStandard(\"Backspace\", KeyTable.XK_BackSpace);\n"
"// Browsers send \"Clear\" for the numpad 5 without NumLock because\n"
"// Windows uses VK_Clear for that key. But Unix expects KP_Begin for\n"
"// that scenario.\n"
"addNumpad(\"Clear\", KeyTable.XK_Clear, KeyTable.XK_KP_Begin);\n"
"addStandard(\"Copy\", KeyTable.XF86XK_Copy);\n"
"// - CrSel\n"
"addStandard(\"Cut\", KeyTable.XF86XK_Cut);\n"
"addNumpad(\"Delete\", KeyTable.XK_Delete, KeyTable.XK_KP_Delete);\n"
"// - EraseEof\n"
"// - ExSel\n"
"addNumpad(\"Insert\", KeyTable.XK_Insert, KeyTable.XK_KP_Insert);\n"
"addStandard(\"Paste\", KeyTable.XF86XK_Paste);\n"
"addStandard(\"Redo\", KeyTable.XK_Redo);\n"
"addStandard(\"Undo\", KeyTable.XK_Undo);\n"
"\n"
"// 3.6. UI Keys\n"
"\n"
"// - Accept\n"
"// - Again (could just be XK_Redo)\n"
"// - Attn\n"
"addStandard(\"Cancel\", KeyTable.XK_Cancel);\n"
"addStandard(\"ContextMenu\", KeyTable.XK_Menu);\n"
"addStandard(\"Escape\", KeyTable.XK_Escape);\n"
"addStandard(\"Execute\", KeyTable.XK_Execute);\n"
"addStandard(\"Find\", KeyTable.XK_Find);\n"
"addStandard(\"Help\", KeyTable.XK_Help);\n"
"addStandard(\"Pause\", KeyTable.XK_Pause);\n"
"// - Play\n"
"// - Props\n"
"addStandard(\"Select\", KeyTable.XK_Select);\n"
"addStandard(\"ZoomIn\", KeyTable.XF86XK_ZoomIn);\n"
"addStandard(\"ZoomOut\", KeyTable.XF86XK_ZoomOut);\n"
"\n"
"// 3.7. Device Keys\n"
"\n"
"addStandard(\"BrightnessDown\", KeyTable.XF86XK_MonBrightnessDown);\n"
"addStandard(\"BrightnessUp\", KeyTable.XF86XK_MonBrightnessUp);\n"
"addStandard(\"Eject\", KeyTable.XF86XK_Eject);\n"
"addStandard(\"LogOff\", KeyTable.XF86XK_LogOff);\n"
"addStandard(\"Power\", KeyTable.XF86XK_PowerOff);\n"
"addStandard(\"PowerOff\", KeyTable.XF86XK_PowerDown);\n"
"addStandard(\"PrintScreen\", KeyTable.XK_Print);\n"
"addStandard(\"Hibernate\", KeyTable.XF86XK_Hibernate);\n"
"addStandard(\"Standby\", KeyTable.XF86XK_Standby);\n"
"addStandard(\"WakeUp\", KeyTable.XF86XK_WakeUp);\n"
"\n"
"// 3.8. IME and Composition Keys\n"
"\n"
"addStandard(\"AllCandidates\", KeyTable.XK_MultipleCandidate);\n"
"addStandard(\"Alphanumeric\", KeyTable.XK_Eisu_toggle);\n"
"addStandard(\"CodeInput\", KeyTable.XK_Codeinput);\n"
"addStandard(\"Compose\", KeyTable.XK_Multi_key);\n"
"addStandard(\"Convert\", KeyTable.XK_Henkan);\n"
"// - Dead\n"
"// - FinalMode\n"
"addStandard(\"GroupFirst\", KeyTable.XK_ISO_First_Group);\n"
"addStandard(\"GroupLast\", KeyTable.XK_ISO_Last_Group);\n"
"addStandard(\"GroupNext\", KeyTable.XK_ISO_Next_Group);\n"
"addStandard(\"GroupPrevious\", KeyTable.XK_ISO_Prev_Group);\n"
"// - ModeChange (XK_Mode_switch is often used for AltGr)\n"
"// - NextCandidate\n"
"addStandard(\"NonConvert\", KeyTable.XK_Muhenkan);\n"
"addStandard(\"PreviousCandidate\", KeyTable.XK_PreviousCandidate);\n"
"// - Process\n"
"addStandard(\"SingleCandidate\", KeyTable.XK_SingleCandidate);\n"
"addStandard(\"HangulMode\", KeyTable.XK_Hangul);\n"
"addStandard(\"HanjaMode\", KeyTable.XK_Hangul_Hanja);\n"
"addStandard(\"JunjaMode\", KeyTable.XK_Hangul_Jeonja);\n"
"addStandard(\"Eisu\", KeyTable.XK_Eisu_toggle);\n"
"addStandard(\"Hankaku\", KeyTable.XK_Hankaku);\n"
"addStandard(\"Hiragana\", KeyTable.XK_Hiragana);\n"
"addStandard(\"HiraganaKatakana\", KeyTable.XK_Hiragana_Katakana);\n"
"addStandard(\"KanaMode\", KeyTable.XK_Kana_Shift); // could also be _Kana_Lock\n"
"addStandard(\"KanjiMode\", KeyTable.XK_Kanji);\n"
"addStandard(\"Katakana\", KeyTable.XK_Katakana);\n"
"addStandard(\"Romaji\", KeyTable.XK_Romaji);\n"
"addStandard(\"Zenkaku\", KeyTable.XK_Zenkaku);\n"
"addStandard(\"ZenkakuHankaku\", KeyTable.XK_Zenkaku_Hankaku);\n"
"\n"
"// 3.9. General-Purpose Function Keys\n"
"\n"
"addStandard(\"F1\", KeyTable.XK_F1);\n"
"addStandard(\"F2\", KeyTable.XK_F2);\n"
"addStandard(\"F3\", KeyTable.XK_F3);\n"
"addStandard(\"F4\", KeyTable.XK_F4);\n"
"addStandard(\"F5\", KeyTable.XK_F5);\n"
"addStandard(\"F6\", KeyTable.XK_F6);\n"
"addStandard(\"F7\", KeyTable.XK_F7);\n"
"addStandard(\"F8\", KeyTable.XK_F8);\n"
"addStandard(\"F9\", KeyTable.XK_F9);\n"
"addStandard(\"F10\", KeyTable.XK_F10);\n"
"addStandard(\"F11\", KeyTable.XK_F11);\n"
"addStandard(\"F12\", KeyTable.XK_F12);\n"
"addStandard(\"F13\", KeyTable.XK_F13);\n"
"addStandard(\"F14\", KeyTable.XK_F14);\n"
"addStandard(\"F15\", KeyTable.XK_F15);\n"
"addStandard(\"F16\", KeyTable.XK_F16);\n"
"addStandard(\"F17\", KeyTable.XK_F17);\n"
"addStandard(\"F18\", KeyTable.XK_F18);\n"
"addStandard(\"F19\", KeyTable.XK_F19);\n"
"addStandard(\"F20\", KeyTable.XK_F20);\n"
"addStandard(\"F21\", KeyTable.XK_F21);\n"
"addStandard(\"F22\", KeyTable.XK_F22);\n"
"addStandard(\"F23\", KeyTable.XK_F23);\n"
"addStandard(\"F24\", KeyTable.XK_F24);\n"
"addStandard(\"F25\", KeyTable.XK_F25);\n"
"addStandard(\"F26\", KeyTable.XK_F26);\n"
"addStandard(\"F27\", KeyTable.XK_F27);\n"
"addStandard(\"F28\", KeyTable.XK_F28);\n"
"addStandard(\"F29\", KeyTable.XK_F29);\n"
"addStandard(\"F30\", KeyTable.XK_F30);\n"
"addStandard(\"F31\", KeyTable.XK_F31);\n"
"addStandard(\"F32\", KeyTable.XK_F32);\n"
"addStandard(\"F33\", KeyTable.XK_F33);\n"
"addStandard(\"F34\", KeyTable.XK_F34);\n"
"addStandard(\"F35\", KeyTable.XK_F35);\n"
"// - Soft1...\n"
"\n"
"// 3.10. Multimedia Keys\n"
"\n"
"// - ChannelDown\n"
"// - ChannelUp\n"
"addStandard(\"Close\", KeyTable.XF86XK_Close);\n"
"addStandard(\"MailForward\", KeyTable.XF86XK_MailForward);\n"
"addStandard(\"MailReply\", KeyTable.XF86XK_Reply);\n"
"addStandard(\"MailSend\", KeyTable.XF86XK_Send);\n"
"// - MediaClose\n"
"addStandard(\"MediaFastForward\", KeyTable.XF86XK_AudioForward);\n"
"addStandard(\"MediaPause\", KeyTable.XF86XK_AudioPause);\n"
"addStandard(\"MediaPlay\", KeyTable.XF86XK_AudioPlay);\n"
"// - MediaPlayPause\n"
"addStandard(\"MediaRecord\", KeyTable.XF86XK_AudioRecord);\n"
"addStandard(\"MediaRewind\", KeyTable.XF86XK_AudioRewind);\n"
"addStandard(\"MediaStop\", KeyTable.XF86XK_AudioStop);\n"
"addStandard(\"MediaTrackNext\", KeyTable.XF86XK_AudioNext);\n"
"addStandard(\"MediaTrackPrevious\", KeyTable.XF86XK_AudioPrev);\n"
"addStandard(\"New\", KeyTable.XF86XK_New);\n"
"addStandard(\"Open\", KeyTable.XF86XK_Open);\n"
"addStandard(\"Print\", KeyTable.XK_Print);\n"
"addStandard(\"Save\", KeyTable.XF86XK_Save);\n"
"addStandard(\"SpellCheck\", KeyTable.XF86XK_Spell);\n"
"\n"
"// 3.11. Multimedia Numpad Keys\n"
"\n"
"// - Key11\n"
"// - Key12\n"
"\n"
"// 3.12. Audio Keys\n"
"\n"
"// - AudioBalanceLeft\n"
"// - AudioBalanceRight\n"
"// - AudioBassBoostDown\n"
"// - AudioBassBoostToggle\n"
"// - AudioBassBoostUp\n"
"// - AudioFaderFront\n"
"// - AudioFaderRear\n"
"// - AudioSurroundModeNext\n"
"// - AudioTrebleDown\n"
"// - AudioTrebleUp\n"
"addStandard(\"AudioVolumeDown\", KeyTable.XF86XK_AudioLowerVolume);\n"
"addStandard(\"AudioVolumeUp\", KeyTable.XF86XK_AudioRaiseVolume);\n"
"addStandard(\"AudioVolumeMute\", KeyTable.XF86XK_AudioMute);\n"
"// - MicrophoneToggle\n"
"// - MicrophoneVolumeDown\n"
"// - MicrophoneVolumeUp\n"
"addStandard(\"MicrophoneVolumeMute\", KeyTable.XF86XK_AudioMicMute);\n"
"\n"
"// 3.13. Speech Keys\n"
"\n"
"// - SpeechCorrectionList\n"
"// - SpeechInputToggle\n"
"\n"
"// 3.14. Application Keys\n"
"\n"
"addStandard(\"LaunchApplication1\", KeyTable.XF86XK_MyComputer);\n"
"addStandard(\"LaunchApplication2\", KeyTable.XF86XK_Calculator);\n"
"addStandard(\"LaunchCalendar\", KeyTable.XF86XK_Calendar);\n"
"// - LaunchContacts\n"
"addStandard(\"LaunchMail\", KeyTable.XF86XK_Mail);\n"
"addStandard(\"LaunchMediaPlayer\", KeyTable.XF86XK_AudioMedia);\n"
"addStandard(\"LaunchMusicPlayer\", KeyTable.XF86XK_Music);\n"
"addStandard(\"LaunchPhone\", KeyTable.XF86XK_Phone);\n"
"addStandard(\"LaunchScreenSaver\", KeyTable.XF86XK_ScreenSaver);\n"
"addStandard(\"LaunchSpreadsheet\", KeyTable.XF86XK_Excel);\n"
"addStandard(\"LaunchWebBrowser\", KeyTable.XF86XK_WWW);\n"
"addStandard(\"LaunchWebCam\", KeyTable.XF86XK_WebCam);\n"
"addStandard(\"LaunchWordProcessor\", KeyTable.XF86XK_Word);\n"
"\n"
"// 3.15. Browser Keys\n"
"\n"
"addStandard(\"BrowserBack\", KeyTable.XF86XK_Back);\n"
"addStandard(\"BrowserFavorites\", KeyTable.XF86XK_Favorites);\n"
"addStandard(\"BrowserForward\", KeyTable.XF86XK_Forward);\n"
"addStandard(\"BrowserHome\", KeyTable.XF86XK_HomePage);\n"
"addStandard(\"BrowserRefresh\", KeyTable.XF86XK_Refresh);\n"
"addStandard(\"BrowserSearch\", KeyTable.XF86XK_Search);\n"
"addStandard(\"BrowserStop\", KeyTable.XF86XK_Stop);\n"
"\n"
"// 3.16. Mobile Phone Keys\n"
"\n"
"// - A whole bunch...\n"
"\n"
"// 3.17. TV Keys\n"
"\n"
"// - A whole bunch...\n"
"\n"
"// 3.18. Media Controller Keys\n"
"\n"
"// - A whole bunch...\n"
"addStandard(\"Dimmer\", KeyTable.XF86XK_BrightnessAdjust);\n"
"addStandard(\"MediaAudioTrack\", KeyTable.XF86XK_AudioCycleTrack);\n"
"addStandard(\"RandomToggle\", KeyTable.XF86XK_AudioRandomPlay);\n"
"addStandard(\"SplitScreenToggle\", KeyTable.XF86XK_SplitScreen);\n"
"addStandard(\"Subtitle\", KeyTable.XF86XK_Subtitle);\n"
"addStandard(\"VideoModeNext\", KeyTable.XF86XK_Next_VMode);\n"
"\n"
"// Extra: Numpad\n"
"\n"
"addNumpad(\"=\", KeyTable.XK_equal, KeyTable.XK_KP_Equal);\n"
"addNumpad(\"+\", KeyTable.XK_plus, KeyTable.XK_KP_Add);\n"
"addNumpad(\"-\", KeyTable.XK_minus, KeyTable.XK_KP_Subtract);\n"
"addNumpad(\"*\", KeyTable.XK_asterisk, KeyTable.XK_KP_Multiply);\n"
"addNumpad(\"/\", KeyTable.XK_slash, KeyTable.XK_KP_Divide);\n"
"addNumpad(\".\", KeyTable.XK_period, KeyTable.XK_KP_Decimal);\n"
"addNumpad(\",\", KeyTable.XK_comma, KeyTable.XK_KP_Separator);\n"
"addNumpad(\"0\", KeyTable.XK_0, KeyTable.XK_KP_0);\n"
"addNumpad(\"1\", KeyTable.XK_1, KeyTable.XK_KP_1);\n"
"addNumpad(\"2\", KeyTable.XK_2, KeyTable.XK_KP_2);\n"
"addNumpad(\"3\", KeyTable.XK_3, KeyTable.XK_KP_3);\n"
"addNumpad(\"4\", KeyTable.XK_4, KeyTable.XK_KP_4);\n"
"addNumpad(\"5\", KeyTable.XK_5, KeyTable.XK_KP_5);\n"
"addNumpad(\"6\", KeyTable.XK_6, KeyTable.XK_KP_6);\n"
"addNumpad(\"7\", KeyTable.XK_7, KeyTable.XK_KP_7);\n"
"addNumpad(\"8\", KeyTable.XK_8, KeyTable.XK_KP_8);\n"
"addNumpad(\"9\", KeyTable.XK_9, KeyTable.XK_KP_9);\n"
"\n"
"window.DOMKeyTable;\n"
"window.addStandard = addStandard;\n"
"window.addLeftRight = addLeftRight;\n"
"window.addNumpad = addNumpad;\n"
;
    if (strcmp(path, "/novnc/input/domkeytable.js") == 0) return novnc_input_domkeytable_js;
const char *novnc_input_fixedkeys_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2018 The noVNC Authors\n"
" * Licensed under MPL 2.0 or any later version (see LICENSE.txt)\n"
" */\n"
"\n"
"/*\n"
" * Fallback mapping between HTML key codes (physical keys) and\n"
" * HTML key values. This only works for keys that don't vary\n"
" * between layouts. We also omit those who manage fine by mapping the\n"
" * Unicode representation.\n"
" *\n"
" * See https://www.w3.org/TR/uievents-code/ for possible codes.\n"
" * See https://www.w3.org/TR/uievents-key/ for possible values.\n"
" */\n"
"\n"
"/* eslint-disable key-spacing */\n"
"\n"
"window.{\n"
"\n"
"// 3.1.1.1. Writing System Keys\n"
"\n"
"    'Backspace':        'Backspace',\n"
"\n"
"// 3.1.1.2. Functional Keys\n"
"\n"
"    'AltLeft':          'Alt',\n"
"    'AltRight':         'Alt', // This could also be 'AltGraph'\n"
"    'CapsLock':         'CapsLock',\n"
"    'ContextMenu':      'ContextMenu',\n"
"    'ControlLeft':      'Control',\n"
"    'ControlRight':     'Control',\n"
"    'Enter':            'Enter',\n"
"    'MetaLeft':         'Meta',\n"
"    'MetaRight':        'Meta',\n"
"    'ShiftLeft':        'Shift',\n"
"    'ShiftRight':       'Shift',\n"
"    'Tab':              'Tab',\n"
"    // FIXME: Japanese/Korean keys\n"
"\n"
"// 3.1.2. Control Pad Section\n"
"\n"
"    'Delete':           'Delete',\n"
"    'End':              'End',\n"
"    'Help':             'Help',\n"
"    'Home':             'Home',\n"
"    'Insert':           'Insert',\n"
"    'PageDown':         'PageDown',\n"
"    'PageUp':           'PageUp',\n"
"\n"
"// 3.1.3. Arrow Pad Section\n"
"\n"
"    'ArrowDown':        'ArrowDown',\n"
"    'ArrowLeft':        'ArrowLeft',\n"
"    'ArrowRight':       'ArrowRight',\n"
"    'ArrowUp':          'ArrowUp',\n"
"\n"
"// 3.1.4. Numpad Section\n"
"\n"
"    'NumLock':          'NumLock',\n"
"    'NumpadBackspace':  'Backspace',\n"
"    'NumpadClear':      'Clear',\n"
"\n"
"// 3.1.5. Function Section\n"
"\n"
"    'Escape':           'Escape',\n"
"    'F1':               'F1',\n"
"    'F2':               'F2',\n"
"    'F3':               'F3',\n"
"    'F4':               'F4',\n"
"    'F5':               'F5',\n"
"    'F6':               'F6',\n"
"    'F7':               'F7',\n"
"    'F8':               'F8',\n"
"    'F9':               'F9',\n"
"    'F10':              'F10',\n"
"    'F11':              'F11',\n"
"    'F12':              'F12',\n"
"    'F13':              'F13',\n"
"    'F14':              'F14',\n"
"    'F15':              'F15',\n"
"    'F16':              'F16',\n"
"    'F17':              'F17',\n"
"    'F18':              'F18',\n"
"    'F19':              'F19',\n"
"    'F20':              'F20',\n"
"    'F21':              'F21',\n"
"    'F22':              'F22',\n"
"    'F23':              'F23',\n"
"    'F24':              'F24',\n"
"    'F25':              'F25',\n"
"    'F26':              'F26',\n"
"    'F27':              'F27',\n"
"    'F28':              'F28',\n"
"    'F29':              'F29',\n"
"    'F30':              'F30',\n"
"    'F31':              'F31',\n"
"    'F32':              'F32',\n"
"    'F33':              'F33',\n"
"    'F34':              'F34',\n"
"    'F35':              'F35',\n"
"    'PrintScreen':      'PrintScreen',\n"
"    'ScrollLock':       'ScrollLock',\n"
"    'Pause':            'Pause',\n"
"\n"
"// 3.1.6. Media Keys\n"
"\n"
"    'BrowserBack':      'BrowserBack',\n"
"    'BrowserFavorites': 'BrowserFavorites',\n"
"    'BrowserForward':   'BrowserForward',\n"
"    'BrowserHome':      'BrowserHome',\n"
"    'BrowserRefresh':   'BrowserRefresh',\n"
"    'BrowserSearch':    'BrowserSearch',\n"
"    'BrowserStop':      'BrowserStop',\n"
"    'Eject':            'Eject',\n"
"    'LaunchApp1':       'LaunchMyComputer',\n"
"    'LaunchApp2':       'LaunchCalendar',\n"
"    'LaunchMail':       'LaunchMail',\n"
"    'MediaPlayPause':   'MediaPlay',\n"
"    'MediaStop':        'MediaStop',\n"
"    'MediaTrackNext':   'MediaTrackNext',\n"
"    'MediaTrackPrevious': 'MediaTrackPrevious',\n"
"    'Power':            'Power',\n"
"    'Sleep':            'Sleep',\n"
"    'AudioVolumeDown':  'AudioVolumeDown',\n"
"    'AudioVolumeMute':  'AudioVolumeMute',\n"
"    'AudioVolumeUp':    'AudioVolumeUp',\n"
"    'WakeUp':           'WakeUp',\n"
"};\n"
;
    if (strcmp(path, "/novnc/input/fixedkeys.js") == 0) return novnc_input_fixedkeys_js;
const char *novnc_input_gesturehandler_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2020 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" *\n"
" */\n"
"\n"
"const GH_NOGESTURE = 0;\n"
"const GH_ONETAP    = 1;\n"
"const GH_TWOTAP    = 2;\n"
"const GH_THREETAP  = 4;\n"
"const GH_DRAG      = 8;\n"
"const GH_LONGPRESS = 16;\n"
"const GH_TWODRAG   = 32;\n"
"const GH_PINCH     = 64;\n"
"\n"
"const GH_INITSTATE = 127;\n"
"\n"
"const GH_MOVE_THRESHOLD = 50;\n"
"const GH_ANGLE_THRESHOLD = 90; // Degrees\n"
"\n"
"// Timeout when waiting for gestures (ms)\n"
"const GH_MULTITOUCH_TIMEOUT = 250;\n"
"\n"
"// Maximum time between press and release for a tap (ms)\n"
"const GH_TAP_TIMEOUT = 1000;\n"
"\n"
"// Timeout when waiting for longpress (ms)\n"
"const GH_LONGPRESS_TIMEOUT = 1000;\n"
"\n"
"// Timeout when waiting to decide between PINCH and TWODRAG (ms)\n"
"const GH_TWOTOUCH_TIMEOUT = 50;\n"
"\n"
"class GestureHandler {\n"
"    constructor() {\n"
"        this._target = null;\n"
"\n"
"        this._state = GH_INITSTATE;\n"
"\n"
"        this._tracked = [];\n"
"        this._ignored = [];\n"
"\n"
"        this._waitingRelease = false;\n"
"        this._releaseStart = 0.0;\n"
"\n"
"        this._longpressTimeoutId = null;\n"
"        this._twoTouchTimeoutId = null;\n"
"\n"
"        this._boundEventHandler = this._eventHandler.bind(this);\n"
"    }\n"
"\n"
"    attach(target) {\n"
"        this.detach();\n"
"\n"
"        this._target = target;\n"
"        this._target.addEventListener('touchstart',\n"
"                                      this._boundEventHandler);\n"
"        this._target.addEventListener('touchmove',\n"
"                                      this._boundEventHandler);\n"
"        this._target.addEventListener('touchend',\n"
"                                      this._boundEventHandler);\n"
"        this._target.addEventListener('touchcancel',\n"
"                                      this._boundEventHandler);\n"
"    }\n"
"\n"
"    detach() {\n"
"        if (!this._target) {\n"
"            return;\n"
"        }\n"
"\n"
"        this._stopLongpressTimeout();\n"
"        this._stopTwoTouchTimeout();\n"
"\n"
"        this._target.removeEventListener('touchstart',\n"
"                                         this._boundEventHandler);\n"
"        this._target.removeEventListener('touchmove',\n"
"                                         this._boundEventHandler);\n"
"        this._target.removeEventListener('touchend',\n"
"                                         this._boundEventHandler);\n"
"        this._target.removeEventListener('touchcancel',\n"
"                                         this._boundEventHandler);\n"
"        this._target = null;\n"
"    }\n"
"\n"
"    _eventHandler(e) {\n"
"        let fn;\n"
"\n"
"        e.stopPropagation();\n"
"        e.preventDefault();\n"
"\n"
"        switch (e.type) {\n"
"            case 'touchstart':\n"
"                fn = this._touchStart;\n"
"                break;\n"
"            case 'touchmove':\n"
"                fn = this._touchMove;\n"
"                break;\n"
"            case 'touchend':\n"
"            case 'touchcancel':\n"
"                fn = this._touchEnd;\n"
"                break;\n"
"        }\n"
"\n"
"        for (let i = 0; i < e.changedTouches.length; i++) {\n"
"            let touch = e.changedTouches[i];\n"
"            fn.call(this, touch.identifier, touch.clientX, touch.clientY);\n"
"        }\n"
"    }\n"
"\n"
"    _touchStart(id, x, y) {\n"
"        // Ignore any new touches if there is already an active gesture,\n"
"        // or we're in a cleanup state\n"
"        if (this._hasDetectedGesture() || (this._state === GH_NOGESTURE)) {\n"
"            this._ignored.push(id);\n"
"            return;\n"
"        }\n"
"\n"
"        // Did it take too long between touches that we should no longer\n"
"        // consider this a single gesture?\n"
"        if ((this._tracked.length > 0) &&\n"
"            ((Date.now() - this._tracked[0].started) > GH_MULTITOUCH_TIMEOUT)) {\n"
"            this._state = GH_NOGESTURE;\n"
"            this._ignored.push(id);\n"
"            return;\n"
"        }\n"
"\n"
"        // If we're waiting for fingers to release then we should no longer\n"
"        // recognize new touches\n"
"        if (this._waitingRelease) {\n"
"            this._state = GH_NOGESTURE;\n"
"            this._ignored.push(id);\n"
"            return;\n"
"        }\n"
"\n"
"        this._tracked.push({\n"
"            id: id,\n"
"            started: Date.now(),\n"
"            active: true,\n"
"            firstX: x,\n"
"            firstY: y,\n"
"            lastX: x,\n"
"            lastY: y,\n"
"            angle: 0\n"
"        });\n"
"\n"
"        switch (this._tracked.length) {\n"
"            case 1:\n"
"                this._startLongpressTimeout();\n"
"                break;\n"
"\n"
"            case 2:\n"
"                this._state &= ~(GH_ONETAP | GH_DRAG | GH_LONGPRESS);\n"
"                this._stopLongpressTimeout();\n"
"                break;\n"
"\n"
"            case 3:\n"
"                this._state &= ~(GH_TWOTAP | GH_TWODRAG | GH_PINCH);\n"
"                break;\n"
"\n"
"            default:\n"
"                this._state = GH_NOGESTURE;\n"
"        }\n"
"    }\n"
"\n"
"    _touchMove(id, x, y) {\n"
"        let touch = this._tracked.find(t => t.id === id);\n"
"\n"
"        // If this is an update for a touch we're not tracking, ignore it\n"
"        if (touch === undefined) {\n"
"            return;\n"
"        }\n"
"\n"
"        // Update the touches last position with the event coordinates\n"
"        touch.lastX = x;\n"
"        touch.lastY = y;\n"
"\n"
"        let deltaX = x - touch.firstX;\n"
"        let deltaY = y - touch.firstY;\n"
"\n"
"        // Update angle when the touch has moved\n"
"        if ((touch.firstX !== touch.lastX) ||\n"
"            (touch.firstY !== touch.lastY)) {\n"
"            touch.angle = Math.atan2(deltaY, deltaX) * 180 / Math.PI;\n"
"        }\n"
"\n"
"        if (!this._hasDetectedGesture()) {\n"
"            // Ignore moves smaller than the minimum threshold\n"
"            if (Math.hypot(deltaX, deltaY) < GH_MOVE_THRESHOLD) {\n"
"                return;\n"
"            }\n"
"\n"
"            // Can't be a tap or long press as we've seen movement\n"
"            this._state &= ~(GH_ONETAP | GH_TWOTAP | GH_THREETAP | GH_LONGPRESS);\n"
"            this._stopLongpressTimeout();\n"
"\n"
"            if (this._tracked.length !== 1) {\n"
"                this._state &= ~(GH_DRAG);\n"
"            }\n"
"            if (this._tracked.length !== 2) {\n"
"                this._state &= ~(GH_TWODRAG | GH_PINCH);\n"
"            }\n"
"\n"
"            // We need to figure out which of our different two touch gestures\n"
"            // this might be\n"
"            if (this._tracked.length === 2) {\n"
"\n"
"                // The other touch is the one where the id doesn't match\n"
"                let prevTouch = this._tracked.find(t => t.id !== id);\n"
"\n"
"                // How far the previous touch point has moved since start\n"
"                let prevDeltaMove = Math.hypot(prevTouch.firstX - prevTouch.lastX,\n"
"                                               prevTouch.firstY - prevTouch.lastY);\n"
"\n"
"                // We know that the current touch moved far enough,\n"
"                // but unless both touches moved further than their\n"
"                // threshold we don't want to disqualify any gestures\n"
"                if (prevDeltaMove > GH_MOVE_THRESHOLD) {\n"
"\n"
"                    // The angle difference between the direction of the touch points\n"
"                    let deltaAngle = Math.abs(touch.angle - prevTouch.angle);\n"
"                    deltaAngle = Math.abs(((deltaAngle + 180) % 360) - 180);\n"
"\n"
"                    // PINCH or TWODRAG can be eliminated depending on the angle\n"
"                    if (deltaAngle > GH_ANGLE_THRESHOLD) {\n"
"                        this._state &= ~GH_TWODRAG;\n"
"                    } else {\n"
"                        this._state &= ~GH_PINCH;\n"
"                    }\n"
"\n"
"                    if (this._isTwoTouchTimeoutRunning()) {\n"
"                        this._stopTwoTouchTimeout();\n"
"                    }\n"
"                } else if (!this._isTwoTouchTimeoutRunning()) {\n"
"                    // We can't determine the gesture right now, let's\n"
"                    // wait and see if more events are on their way\n"
"                    this._startTwoTouchTimeout();\n"
"                }\n"
"            }\n"
"\n"
"            if (!this._hasDetectedGesture()) {\n"
"                return;\n"
"            }\n"
"\n"
"            this._pushEvent('gesturestart');\n"
"        }\n"
"\n"
"        this._pushEvent('gesturemove');\n"
"    }\n"
"\n"
"    _touchEnd(id, x, y) {\n"
"        // Check if this is an ignored touch\n"
"        if (this._ignored.indexOf(id) !== -1) {\n"
"            // Remove this touch from ignored\n"
"            this._ignored.splice(this._ignored.indexOf(id), 1);\n"
"\n"
"            // And reset the state if there are no more touches\n"
"            if ((this._ignored.length === 0) &&\n"
"                (this._tracked.length === 0)) {\n"
"                this._state = GH_INITSTATE;\n"
"                this._waitingRelease = false;\n"
"            }\n"
"            return;\n"
"        }\n"
"\n"
"        // We got a touchend before the timer triggered,\n"
"        // this cannot result in a gesture anymore.\n"
"        if (!this._hasDetectedGesture() &&\n"
"            this._isTwoTouchTimeoutRunning()) {\n"
"            this._stopTwoTouchTimeout();\n"
"            this._state = GH_NOGESTURE;\n"
"        }\n"
"\n"
"        // Some gestures don't trigger until a touch is released\n"
"        if (!this._hasDetectedGesture()) {\n"
"            // Can't be a gesture that relies on movement\n"
"            this._state &= ~(GH_DRAG | GH_TWODRAG | GH_PINCH);\n"
"            // Or something that relies on more time\n"
"            this._state &= ~GH_LONGPRESS;\n"
"            this._stopLongpressTimeout();\n"
"\n"
"            if (!this._waitingRelease) {\n"
"                this._releaseStart = Date.now();\n"
"                this._waitingRelease = true;\n"
"\n"
"                // Can't be a tap that requires more touches than we current have\n"
"                switch (this._tracked.length) {\n"
"                    case 1:\n"
"                        this._state &= ~(GH_TWOTAP | GH_THREETAP);\n"
"                        break;\n"
"\n"
"                    case 2:\n"
"                        this._state &= ~(GH_ONETAP | GH_THREETAP);\n"
"                        break;\n"
"                }\n"
"            }\n"
"        }\n"
"\n"
"        // Waiting for all touches to release? (i.e. some tap)\n"
"        if (this._waitingRelease) {\n"
"            // Were all touches released at roughly the same time?\n"
"            if ((Date.now() - this._releaseStart) > GH_MULTITOUCH_TIMEOUT) {\n"
"                this._state = GH_NOGESTURE;\n"
"            }\n"
"\n"
"            // Did too long time pass between press and release?\n"
"            if (this._tracked.some(t => (Date.now() - t.started) > GH_TAP_TIMEOUT)) {\n"
"                this._state = GH_NOGESTURE;\n"
"            }\n"
"\n"
"            let touch = this._tracked.find(t => t.id === id);\n"
"            touch.active = false;\n"
"\n"
"            // Are we still waiting for more releases?\n"
"            if (this._hasDetectedGesture()) {\n"
"                this._pushEvent('gesturestart');\n"
"            } else {\n"
"                // Have we reached a dead end?\n"
"                if (this._state !== GH_NOGESTURE) {\n"
"                    return;\n"
"                }\n"
"            }\n"
"        }\n"
"\n"
"        if (this._hasDetectedGesture()) {\n"
"            this._pushEvent('gestureend');\n"
"        }\n"
"\n"
"        // Ignore any remaining touches until they are ended\n"
"        for (let i = 0; i < this._tracked.length; i++) {\n"
"            if (this._tracked[i].active) {\n"
"                this._ignored.push(this._tracked[i].id);\n"
"            }\n"
"        }\n"
"        this._tracked = [];\n"
"\n"
"        this._state = GH_NOGESTURE;\n"
"\n"
"        // Remove this touch from ignored if it's in there\n"
"        if (this._ignored.indexOf(id) !== -1) {\n"
"            this._ignored.splice(this._ignored.indexOf(id), 1);\n"
"        }\n"
"\n"
"        // We reset the state if ignored is empty\n"
"        if ((this._ignored.length === 0)) {\n"
"            this._state = GH_INITSTATE;\n"
"            this._waitingRelease = false;\n"
"        }\n"
"    }\n"
"\n"
"    _hasDetectedGesture() {\n"
"        if (this._state === GH_NOGESTURE) {\n"
"            return false;\n"
"        }\n"
"        // Check to see if the bitmask value is a power of 2\n"
"        // (i.e. only one bit set). If it is, we have a state.\n"
"        if (this._state & (this._state - 1)) {\n"
"            return false;\n"
"        }\n"
"\n"
"        // For taps we also need to have all touches released\n"
"        // before we've fully detected the gesture\n"
"        if (this._state & (GH_ONETAP | GH_TWOTAP | GH_THREETAP)) {\n"
"            if (this._tracked.some(t => t.active)) {\n"
"                return false;\n"
"            }\n"
"        }\n"
"\n"
"        return true;\n"
"    }\n"
"\n"
"    _startLongpressTimeout() {\n"
"        this._stopLongpressTimeout();\n"
"        this._longpressTimeoutId = setTimeout(() => this._longpressTimeout(),\n"
"                                              GH_LONGPRESS_TIMEOUT);\n"
"    }\n"
"\n"
"    _stopLongpressTimeout() {\n"
"        clearTimeout(this._longpressTimeoutId);\n"
"        this._longpressTimeoutId = null;\n"
"    }\n"
"\n"
"    _longpressTimeout() {\n"
"        if (this._hasDetectedGesture()) {\n"
"            throw new Error(\"A longpress gesture failed, conflict with a different gesture\");\n"
"        }\n"
"\n"
"        this._state = GH_LONGPRESS;\n"
"        this._pushEvent('gesturestart');\n"
"    }\n"
"\n"
"    _startTwoTouchTimeout() {\n"
"        this._stopTwoTouchTimeout();\n"
"        this._twoTouchTimeoutId = setTimeout(() => this._twoTouchTimeout(),\n"
"                                             GH_TWOTOUCH_TIMEOUT);\n"
"    }\n"
"\n"
"    _stopTwoTouchTimeout() {\n"
"        clearTimeout(this._twoTouchTimeoutId);\n"
"        this._twoTouchTimeoutId = null;\n"
"    }\n"
"\n"
"    _isTwoTouchTimeoutRunning() {\n"
"        return this._twoTouchTimeoutId !== null;\n"
"    }\n"
"\n"
"    _twoTouchTimeout() {\n"
"        if (this._tracked.length === 0) {\n"
"            throw new Error(\"A pinch or two drag gesture failed, no tracked touches\");\n"
"        }\n"
"\n"
"        // How far each touch point has moved since start\n"
"        let avgM = this._getAverageMovement();\n"
"        let avgMoveH = Math.abs(avgM.x);\n"
"        let avgMoveV = Math.abs(avgM.y);\n"
"\n"
"        // The difference in the distance between where\n"
"        // the touch points started and where they are now\n"
"        let avgD = this._getAverageDistance();\n"
"        let deltaTouchDistance = Math.abs(Math.hypot(avgD.first.x, avgD.first.y) -\n"
"                                          Math.hypot(avgD.last.x, avgD.last.y));\n"
"\n"
"        if ((avgMoveV < deltaTouchDistance) &&\n"
"            (avgMoveH < deltaTouchDistance)) {\n"
"            this._state = GH_PINCH;\n"
"        } else {\n"
"            this._state = GH_TWODRAG;\n"
"        }\n"
"\n"
"        this._pushEvent('gesturestart');\n"
"        this._pushEvent('gesturemove');\n"
"    }\n"
"\n"
"    _pushEvent(type) {\n"
"        let detail = { type: this._stateToGesture(this._state) };\n"
"\n"
"        // For most gesture events the current (average) position is the\n"
"        // most useful\n"
"        let avg = this._getPosition();\n"
"        let pos = avg.last;\n"
"\n"
"        // However we have a slight distance to detect gestures, so for the\n"
"        // first gesture event we want to use the first positions we saw\n"
"        if (type === 'gesturestart') {\n"
"            pos = avg.first;\n"
"        }\n"
"\n"
"        // For these gestures, we always want the event coordinates\n"
"        // to be where the gesture began, not the current touch location.\n"
"        switch (this._state) {\n"
"            case GH_TWODRAG:\n"
"            case GH_PINCH:\n"
"                pos = avg.first;\n"
"                break;\n"
"        }\n"
"\n"
"        detail['clientX'] = pos.x;\n"
"        detail['clientY'] = pos.y;\n"
"\n"
"        // FIXME: other coordinates?\n"
"\n"
"        // Some gestures also have a magnitude\n"
"        if (this._state === GH_PINCH) {\n"
"            let distance = this._getAverageDistance();\n"
"            if (type === 'gesturestart') {\n"
"                detail['magnitudeX'] = distance.first.x;\n"
"                detail['magnitudeY'] = distance.first.y;\n"
"            } else {\n"
"                detail['magnitudeX'] = distance.last.x;\n"
"                detail['magnitudeY'] = distance.last.y;\n"
"            }\n"
"        } else if (this._state === GH_TWODRAG) {\n"
"            if (type === 'gesturestart') {\n"
"                detail['magnitudeX'] = 0.0;\n"
"                detail['magnitudeY'] = 0.0;\n"
"            } else {\n"
"                let movement = this._getAverageMovement();\n"
"                detail['magnitudeX'] = movement.x;\n"
"                detail['magnitudeY'] = movement.y;\n"
"            }\n"
"        }\n"
"\n"
"        let gev = new CustomEvent(type, { detail: detail });\n"
"        this._target.dispatchEvent(gev);\n"
"    }\n"
"\n"
"    _stateToGesture(state) {\n"
"        switch (state) {\n"
"            case GH_ONETAP:\n"
"                return 'onetap';\n"
"            case GH_TWOTAP:\n"
"                return 'twotap';\n"
"            case GH_THREETAP:\n"
"                return 'threetap';\n"
"            case GH_DRAG:\n"
"                return 'drag';\n"
"            case GH_LONGPRESS:\n"
"                return 'longpress';\n"
"            case GH_TWODRAG:\n"
"                return 'twodrag';\n"
"            case GH_PINCH:\n"
"                return 'pinch';\n"
"        }\n"
"\n"
"        throw new Error(\"Unknown gesture state: \" + state);\n"
"    }\n"
"\n"
"    _getPosition() {\n"
"        if (this._tracked.length === 0) {\n"
"            throw new Error(\"Failed to get gesture position, no tracked touches\");\n"
"        }\n"
"\n"
"        let size = this._tracked.length;\n"
"        let fx = 0, fy = 0, lx = 0, ly = 0;\n"
"\n"
"        for (let i = 0; i < this._tracked.length; i++) {\n"
"            fx += this._tracked[i].firstX;\n"
"            fy += this._tracked[i].firstY;\n"
"            lx += this._tracked[i].lastX;\n"
"            ly += this._tracked[i].lastY;\n"
"        }\n"
"\n"
"        return { first: { x: fx / size,\n"
"                          y: fy / size },\n"
"                 last: { x: lx / size,\n"
"                         y: ly / size } };\n"
"    }\n"
"\n"
"    _getAverageMovement() {\n"
"        if (this._tracked.length === 0) {\n"
"            throw new Error(\"Failed to get gesture movement, no tracked touches\");\n"
"        }\n"
"\n"
"        let totalH, totalV;\n"
"        totalH = totalV = 0;\n"
"        let size = this._tracked.length;\n"
"\n"
"        for (let i = 0; i < this._tracked.length; i++) {\n"
"            totalH += this._tracked[i].lastX - this._tracked[i].firstX;\n"
"            totalV += this._tracked[i].lastY - this._tracked[i].firstY;\n"
"        }\n"
"\n"
"        return { x: totalH / size,\n"
"                 y: totalV / size };\n"
"    }\n"
"\n"
"    _getAverageDistance() {\n"
"        if (this._tracked.length === 0) {\n"
"            throw new Error(\"Failed to get gesture distance, no tracked touches\");\n"
"        }\n"
"\n"
"        // Distance between the first and last tracked touches\n"
"\n"
"        let first = this._tracked[0];\n"
"        let last = this._tracked[this._tracked.length - 1];\n"
"\n"
"        let fdx = Math.abs(last.firstX - first.firstX);\n"
"        let fdy = Math.abs(last.firstY - first.firstY);\n"
"\n"
"        let ldx = Math.abs(last.lastX - first.lastX);\n"
"        let ldy = Math.abs(last.lastY - first.lastY);\n"
"\n"
"        return { first: { x: fdx, y: fdy },\n"
"                 last: { x: ldx, y: ldy } };\n"
"    }\n"
"}\n"
"window.GH_NOGESTURE = GH_NOGESTURE;\n"
"window.GH_ONETAP = GH_ONETAP;\n"
"window.GH_TWOTAP = GH_TWOTAP;\n"
"window.GH_THREETAP = GH_THREETAP;\n"
"window.GH_DRAG = GH_DRAG;\n"
"window.GH_LONGPRESS = GH_LONGPRESS;\n"
"window.GH_TWODRAG = GH_TWODRAG;\n"
"window.GH_PINCH = GH_PINCH;\n"
"window.GH_INITSTATE = GH_INITSTATE;\n"
"window.GH_MOVE_THRESHOLD = GH_MOVE_THRESHOLD;\n"
"window.GH_ANGLE_THRESHOLD = GH_ANGLE_THRESHOLD;\n"
"window.GH_MULTITOUCH_TIMEOUT = GH_MULTITOUCH_TIMEOUT;\n"
"window.GH_TAP_TIMEOUT = GH_TAP_TIMEOUT;\n"
"window.GH_LONGPRESS_TIMEOUT = GH_LONGPRESS_TIMEOUT;\n"
"window.GH_TWOTOUCH_TIMEOUT = GH_TWOTOUCH_TIMEOUT;\n"
"window.GestureHandler = GestureHandler;\n"
;
    if (strcmp(path, "/novnc/input/gesturehandler.js") == 0) return novnc_input_gesturehandler_js;
const char *novnc_input_keyboard_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2019 The noVNC Authors\n"
" * Licensed under MPL 2.0 or any later version (see LICENSE.txt)\n"
" */\n"
"\n"
"\n"
"//\n"
"// Keyboard event handler\n"
"//\n"
"\n"
"class Keyboard {\n"
"    constructor(target) {\n"
"        this._target = target || null;\n"
"\n"
"        this._keyDownList = {};         // List of depressed keys\n"
"                                        // (even if they are happy)\n"
"        this._altGrArmed = false;       // Windows AltGr detection\n"
"\n"
"        // keep these here so we can refer to them later\n"
"        this._eventHandlers = {\n"
"            'keyup': this._handleKeyUp.bind(this),\n"
"            'keydown': this._handleKeyDown.bind(this),\n"
"            'blur': this._allKeysUp.bind(this),\n"
"        };\n"
"\n"
"        // ===== EVENT HANDLERS =====\n"
"\n"
"        this.onkeyevent = () => {}; // Handler for key press/release\n"
"    }\n"
"\n"
"    // ===== PRIVATE METHODS =====\n"
"\n"
"    _sendKeyEvent(keysym, code, down) {\n"
"        if (down) {\n"
"            this._keyDownList[code] = keysym;\n"
"        } else {\n"
"            // Do we really think this key is down?\n"
"            if (!(code in this._keyDownList)) {\n"
"                return;\n"
"            }\n"
"            delete this._keyDownList[code];\n"
"        }\n"
"\n"
"        Log.Debug(\"onkeyevent \" + (down ? \"down\" : \"up\") +\n"
"                  \", keysym: \" + keysym, \", code: \" + code);\n"
"        this.onkeyevent(keysym, code, down);\n"
"    }\n"
"\n"
"    _getKeyCode(e) {\n"
"        const code = KeyboardUtil.getKeycode(e);\n"
"        if (code !== 'Unidentified') {\n"
"            return code;\n"
"        }\n"
"\n"
"        // Unstable, but we don't have anything else to go on\n"
"        if (e.keyCode) {\n"
"            // 229 is used for composition events\n"
"            if (e.keyCode !== 229) {\n"
"                return 'Platform' + e.keyCode;\n"
"            }\n"
"        }\n"
"\n"
"        // A precursor to the final DOM3 standard. Unfortunately it\n"
"        // is not layout independent, so it is as bad as using keyCode\n"
"        if (e.keyIdentifier) {\n"
"            // Non-character key?\n"
"            if (e.keyIdentifier.substr(0, 2) !== 'U+') {\n"
"                return e.keyIdentifier;\n"
"            }\n"
"\n"
"            const codepoint = parseInt(e.keyIdentifier.substr(2), 16);\n"
"            const char = String.fromCharCode(codepoint).toUpperCase();\n"
"\n"
"            return 'Platform' + char.charCodeAt();\n"
"        }\n"
"\n"
"        return 'Unidentified';\n"
"    }\n"
"\n"
"    _handleKeyDown(e) {\n"
"        const code = this._getKeyCode(e);\n"
"        let keysym = KeyboardUtil.getKeysym(e);\n"
"\n"
"        // Windows doesn't have a proper AltGr, but handles it using\n"
"        // fake Ctrl+Alt. However the remote end might not be Windows,\n"
"        // so we need to merge those in to a single AltGr event. We\n"
"        // detect this case by seeing the two key events directly after\n"
"        // each other with a very short time between them (<50ms).\n"
"        if (this._altGrArmed) {\n"
"            this._altGrArmed = false;\n"
"            clearTimeout(this._altGrTimeout);\n"
"\n"
"            if ((code === \"AltRight\") &&\n"
"                ((e.timeStamp - this._altGrCtrlTime) < 50)) {\n"
"                // FIXME: We fail to detect this if either Ctrl key is\n"
"                //        first manually pressed as Windows then no\n"
"                //        longer sends the fake Ctrl down event. It\n"
"                //        does however happily send real Ctrl events\n"
"                //        even when AltGr is already down. Some\n"
"                //        browsers detect this for us though and set the\n"
"                //        key to \"AltGraph\".\n"
"                keysym = KeyTable.XK_ISO_Level3_Shift;\n"
"            } else {\n"
"                this._sendKeyEvent(KeyTable.XK_Control_L, \"ControlLeft\", true);\n"
"            }\n"
"        }\n"
"\n"
"        // We cannot handle keys we cannot track, but we also need\n"
"        // to deal with virtual keyboards which omit key info\n"
"        if (code === 'Unidentified') {\n"
"            if (keysym) {\n"
"                // If it's a virtual keyboard then it should be\n"
"                // sufficient to just send press and release right\n"
"                // after each other\n"
"                this._sendKeyEvent(keysym, code, true);\n"
"                this._sendKeyEvent(keysym, code, false);\n"
"            }\n"
"\n"
"            stopEvent(e);\n"
"            return;\n"
"        }\n"
"\n"
"        // Alt behaves more like AltGraph on macOS, so shuffle the\n"
"        // keys around a bit to make things more sane for the remote\n"
"        // server. This method is used by RealVNC and TigerVNC (and\n"
"        // possibly others).\n"
"        if (browser.isMac() || browser.isIOS()) {\n"
"            switch (keysym) {\n"
"                case KeyTable.XK_Super_L:\n"
"                    keysym = KeyTable.XK_Alt_L;\n"
"                    break;\n"
"                case KeyTable.XK_Super_R:\n"
"                    keysym = KeyTable.XK_Super_L;\n"
"                    break;\n"
"                case KeyTable.XK_Alt_L:\n"
"                    keysym = KeyTable.XK_Mode_switch;\n"
"                    break;\n"
"                case KeyTable.XK_Alt_R:\n"
"                    keysym = KeyTable.XK_ISO_Level3_Shift;\n"
"                    break;\n"
"            }\n"
"        }\n"
"\n"
"        // Is this key already pressed? If so, then we must use the\n"
"        // same keysym or we'll confuse the server\n"
"        if (code in this._keyDownList) {\n"
"            keysym = this._keyDownList[code];\n"
"        }\n"
"\n"
"        // macOS doesn't send proper key releases if a key is pressed\n"
"        // while meta is held down\n"
"        if ((browser.isMac() || browser.isIOS()) &&\n"
"            (e.metaKey && code !== 'MetaLeft' && code !== 'MetaRight')) {\n"
"            this._sendKeyEvent(keysym, code, true);\n"
"            this._sendKeyEvent(keysym, code, false);\n"
"            stopEvent(e);\n"
"            return;\n"
"        }\n"
"\n"
"        // macOS doesn't send proper key events for modifiers, only\n"
"        // state change events. That gets extra confusing for CapsLock\n"
"        // which toggles on each press, but not on release. So pretend\n"
"        // it was a quick press and release of the button.\n"
"        if ((browser.isMac() || browser.isIOS()) && (code === 'CapsLock')) {\n"
"            this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);\n"
"            this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);\n"
"            stopEvent(e);\n"
"            return;\n"
"        }\n"
"\n"
"        // Windows doesn't send proper key releases for a bunch of\n"
"        // Japanese IM keys so we have to fake the release right away\n"
"        const jpBadKeys = [ KeyTable.XK_Zenkaku_Hankaku,\n"
"                            KeyTable.XK_Eisu_toggle,\n"
"                            KeyTable.XK_Katakana,\n"
"                            KeyTable.XK_Hiragana,\n"
"                            KeyTable.XK_Romaji ];\n"
"        if (browser.isWindows() && jpBadKeys.includes(keysym)) {\n"
"            this._sendKeyEvent(keysym, code, true);\n"
"            this._sendKeyEvent(keysym, code, false);\n"
"            stopEvent(e);\n"
"            return;\n"
"        }\n"
"\n"
"        stopEvent(e);\n"
"\n"
"        // Possible start of AltGr sequence? (see above)\n"
"        if ((code === \"ControlLeft\") && browser.isWindows() &&\n"
"            !(\"ControlLeft\" in this._keyDownList)) {\n"
"            this._altGrArmed = true;\n"
"            this._altGrTimeout = setTimeout(this._handleAltGrTimeout.bind(this), 100);\n"
"            this._altGrCtrlTime = e.timeStamp;\n"
"            return;\n"
"        }\n"
"\n"
"        this._sendKeyEvent(keysym, code, true);\n"
"    }\n"
"\n"
"    _handleKeyUp(e) {\n"
"        stopEvent(e);\n"
"\n"
"        const code = this._getKeyCode(e);\n"
"\n"
"        // We can't get a release in the middle of an AltGr sequence, so\n"
"        // abort that detection\n"
"        if (this._altGrArmed) {\n"
"            this._altGrArmed = false;\n"
"            clearTimeout(this._altGrTimeout);\n"
"            this._sendKeyEvent(KeyTable.XK_Control_L, \"ControlLeft\", true);\n"
"        }\n"
"\n"
"        // See comment in _handleKeyDown()\n"
"        if ((browser.isMac() || browser.isIOS()) && (code === 'CapsLock')) {\n"
"            this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);\n"
"            this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);\n"
"            return;\n"
"        }\n"
"\n"
"        this._sendKeyEvent(this._keyDownList[code], code, false);\n"
"\n"
"        // Windows has a rather nasty bug where it won't send key\n"
"        // release events for a Shift button if the other Shift is still\n"
"        // pressed\n"
"        if (browser.isWindows() && ((code === 'ShiftLeft') ||\n"
"                                    (code === 'ShiftRight'))) {\n"
"            if ('ShiftRight' in this._keyDownList) {\n"
"                this._sendKeyEvent(this._keyDownList['ShiftRight'],\n"
"                                   'ShiftRight', false);\n"
"            }\n"
"            if ('ShiftLeft' in this._keyDownList) {\n"
"                this._sendKeyEvent(this._keyDownList['ShiftLeft'],\n"
"                                   'ShiftLeft', false);\n"
"            }\n"
"        }\n"
"    }\n"
"\n"
"    _handleAltGrTimeout() {\n"
"        this._altGrArmed = false;\n"
"        clearTimeout(this._altGrTimeout);\n"
"        this._sendKeyEvent(KeyTable.XK_Control_L, \"ControlLeft\", true);\n"
"    }\n"
"\n"
"    _allKeysUp() {\n"
"        Log.Debug(\">> Keyboard.allKeysUp\");\n"
"        for (let code in this._keyDownList) {\n"
"            this._sendKeyEvent(this._keyDownList[code], code, false);\n"
"        }\n"
"        Log.Debug(\"<< Keyboard.allKeysUp\");\n"
"    }\n"
"\n"
"    // ===== PUBLIC METHODS =====\n"
"\n"
"    grab() {\n"
"        //Log.Debug(\">> Keyboard.grab\");\n"
"\n"
"        this._target.addEventListener('keydown', this._eventHandlers.keydown);\n"
"        this._target.addEventListener('keyup', this._eventHandlers.keyup);\n"
"\n"
"        // Release (key up) if window loses focus\n"
"        window.addEventListener('blur', this._eventHandlers.blur);\n"
"\n"
"        //Log.Debug(\"<< Keyboard.grab\");\n"
"    }\n"
"\n"
"    ungrab() {\n"
"        //Log.Debug(\">> Keyboard.ungrab\");\n"
"\n"
"        this._target.removeEventListener('keydown', this._eventHandlers.keydown);\n"
"        this._target.removeEventListener('keyup', this._eventHandlers.keyup);\n"
"        window.removeEventListener('blur', this._eventHandlers.blur);\n"
"\n"
"        // Release (key up) all keys that are in a down state\n"
"        this._allKeysUp();\n"
"\n"
"        //Log.Debug(\">> Keyboard.ungrab\");\n"
"    }\n"
"}\n"
"window.Keyboard = Keyboard;\n"
;
    if (strcmp(path, "/novnc/input/keyboard.js") == 0) return novnc_input_keyboard_js;
const char *novnc_input_keysym_js =
"/* eslint-disable key-spacing */\n"
"\n"
"window.KeyTable = {\n"
"    XK_VoidSymbol:                  0xffffff, /* Void symbol */\n"
"\n"
"    XK_BackSpace:                   0xff08, /* Back space, back char */\n"
"    XK_Tab:                         0xff09,\n"
"    XK_Linefeed:                    0xff0a, /* Linefeed, LF */\n"
"    XK_Clear:                       0xff0b,\n"
"    XK_Return:                      0xff0d, /* Return, enter */\n"
"    XK_Pause:                       0xff13, /* Pause, hold */\n"
"    XK_Scroll_Lock:                 0xff14,\n"
"    XK_Sys_Req:                     0xff15,\n"
"    XK_Escape:                      0xff1b,\n"
"    XK_Delete:                      0xffff, /* Delete, rubout */\n"
"\n"
"    /* International & multi-key character composition */\n"
"\n"
"    XK_Multi_key:                   0xff20, /* Multi-key character compose */\n"
"    XK_Codeinput:                   0xff37,\n"
"    XK_SingleCandidate:             0xff3c,\n"
"    XK_MultipleCandidate:           0xff3d,\n"
"    XK_PreviousCandidate:           0xff3e,\n"
"\n"
"    /* Japanese keyboard support */\n"
"\n"
"    XK_Kanji:                       0xff21, /* Kanji, Kanji convert */\n"
"    XK_Muhenkan:                    0xff22, /* Cancel Conversion */\n"
"    XK_Henkan_Mode:                 0xff23, /* Start/Stop Conversion */\n"
"    XK_Henkan:                      0xff23, /* Alias for Henkan_Mode */\n"
"    XK_Romaji:                      0xff24, /* to Romaji */\n"
"    XK_Hiragana:                    0xff25, /* to Hiragana */\n"
"    XK_Katakana:                    0xff26, /* to Katakana */\n"
"    XK_Hiragana_Katakana:           0xff27, /* Hiragana/Katakana toggle */\n"
"    XK_Zenkaku:                     0xff28, /* to Zenkaku */\n"
"    XK_Hankaku:                     0xff29, /* to Hankaku */\n"
"    XK_Zenkaku_Hankaku:             0xff2a, /* Zenkaku/Hankaku toggle */\n"
"    XK_Touroku:                     0xff2b, /* Add to Dictionary */\n"
"    XK_Massyo:                      0xff2c, /* Delete from Dictionary */\n"
"    XK_Kana_Lock:                   0xff2d, /* Kana Lock */\n"
"    XK_Kana_Shift:                  0xff2e, /* Kana Shift */\n"
"    XK_Eisu_Shift:                  0xff2f, /* Alphanumeric Shift */\n"
"    XK_Eisu_toggle:                 0xff30, /* Alphanumeric toggle */\n"
"    XK_Kanji_Bangou:                0xff37, /* Codeinput */\n"
"    XK_Zen_Koho:                    0xff3d, /* Multiple/All Candidate(s) */\n"
"    XK_Mae_Koho:                    0xff3e, /* Previous Candidate */\n"
"\n"
"    /* Cursor control & motion */\n"
"\n"
"    XK_Home:                        0xff50,\n"
"    XK_Left:                        0xff51, /* Move left, left arrow */\n"
"    XK_Up:                          0xff52, /* Move up, up arrow */\n"
"    XK_Right:                       0xff53, /* Move right, right arrow */\n"
"    XK_Down:                        0xff54, /* Move down, down arrow */\n"
"    XK_Prior:                       0xff55, /* Prior, previous */\n"
"    XK_Page_Up:                     0xff55,\n"
"    XK_Next:                        0xff56, /* Next */\n"
"    XK_Page_Down:                   0xff56,\n"
"    XK_End:                         0xff57, /* EOL */\n"
"    XK_Begin:                       0xff58, /* BOL */\n"
"\n"
"\n"
"    /* Misc functions */\n"
"\n"
"    XK_Select:                      0xff60, /* Select, mark */\n"
"    XK_Print:                       0xff61,\n"
"    XK_Execute:                     0xff62, /* Execute, run, do */\n"
"    XK_Insert:                      0xff63, /* Insert, insert here */\n"
"    XK_Undo:                        0xff65,\n"
"    XK_Redo:                        0xff66, /* Redo, again */\n"
"    XK_Menu:                        0xff67,\n"
"    XK_Find:                        0xff68, /* Find, search */\n"
"    XK_Cancel:                      0xff69, /* Cancel, stop, abort, exit */\n"
"    XK_Help:                        0xff6a, /* Help */\n"
"    XK_Break:                       0xff6b,\n"
"    XK_Mode_switch:                 0xff7e, /* Character set switch */\n"
"    XK_script_switch:               0xff7e, /* Alias for mode_switch */\n"
"    XK_Num_Lock:                    0xff7f,\n"
"\n"
"    /* Keypad functions, keypad numbers cleverly chosen to map to ASCII */\n"
"\n"
"    XK_KP_Space:                    0xff80, /* Space */\n"
"    XK_KP_Tab:                      0xff89,\n"
"    XK_KP_Enter:                    0xff8d, /* Enter */\n"
"    XK_KP_F1:                       0xff91, /* PF1, KP_A, ... */\n"
"    XK_KP_F2:                       0xff92,\n"
"    XK_KP_F3:                       0xff93,\n"
"    XK_KP_F4:                       0xff94,\n"
"    XK_KP_Home:                     0xff95,\n"
"    XK_KP_Left:                     0xff96,\n"
"    XK_KP_Up:                       0xff97,\n"
"    XK_KP_Right:                    0xff98,\n"
"    XK_KP_Down:                     0xff99,\n"
"    XK_KP_Prior:                    0xff9a,\n"
"    XK_KP_Page_Up:                  0xff9a,\n"
"    XK_KP_Next:                     0xff9b,\n"
"    XK_KP_Page_Down:                0xff9b,\n"
"    XK_KP_End:                      0xff9c,\n"
"    XK_KP_Begin:                    0xff9d,\n"
"    XK_KP_Insert:                   0xff9e,\n"
"    XK_KP_Delete:                   0xff9f,\n"
"    XK_KP_Equal:                    0xffbd, /* Equals */\n"
"    XK_KP_Multiply:                 0xffaa,\n"
"    XK_KP_Add:                      0xffab,\n"
"    XK_KP_Separator:                0xffac, /* Separator, often comma */\n"
"    XK_KP_Subtract:                 0xffad,\n"
"    XK_KP_Decimal:                  0xffae,\n"
"    XK_KP_Divide:                   0xffaf,\n"
"\n"
"    XK_KP_0:                        0xffb0,\n"
"    XK_KP_1:                        0xffb1,\n"
"    XK_KP_2:                        0xffb2,\n"
"    XK_KP_3:                        0xffb3,\n"
"    XK_KP_4:                        0xffb4,\n"
"    XK_KP_5:                        0xffb5,\n"
"    XK_KP_6:                        0xffb6,\n"
"    XK_KP_7:                        0xffb7,\n"
"    XK_KP_8:                        0xffb8,\n"
"    XK_KP_9:                        0xffb9,\n"
"\n"
"    /*\n"
"     * Auxiliary functions; note the duplicate definitions for left and right\n"
"     * function keys;  Sun keyboards and a few other manufacturers have such\n"
"     * function key groups on the left and/or right sides of the keyboard.\n"
"     * We've not found a keyboard with more than 35 function keys total.\n"
"     */\n"
"\n"
"    XK_F1:                          0xffbe,\n"
"    XK_F2:                          0xffbf,\n"
"    XK_F3:                          0xffc0,\n"
"    XK_F4:                          0xffc1,\n"
"    XK_F5:                          0xffc2,\n"
"    XK_F6:                          0xffc3,\n"
"    XK_F7:                          0xffc4,\n"
"    XK_F8:                          0xffc5,\n"
"    XK_F9:                          0xffc6,\n"
"    XK_F10:                         0xffc7,\n"
"    XK_F11:                         0xffc8,\n"
"    XK_L1:                          0xffc8,\n"
"    XK_F12:                         0xffc9,\n"
"    XK_L2:                          0xffc9,\n"
"    XK_F13:                         0xffca,\n"
"    XK_L3:                          0xffca,\n"
"    XK_F14:                         0xffcb,\n"
"    XK_L4:                          0xffcb,\n"
"    XK_F15:                         0xffcc,\n"
"    XK_L5:                          0xffcc,\n"
"    XK_F16:                         0xffcd,\n"
"    XK_L6:                          0xffcd,\n"
"    XK_F17:                         0xffce,\n"
"    XK_L7:                          0xffce,\n"
"    XK_F18:                         0xffcf,\n"
"    XK_L8:                          0xffcf,\n"
"    XK_F19:                         0xffd0,\n"
"    XK_L9:                          0xffd0,\n"
"    XK_F20:                         0xffd1,\n"
"    XK_L10:                         0xffd1,\n"
"    XK_F21:                         0xffd2,\n"
"    XK_R1:                          0xffd2,\n"
"    XK_F22:                         0xffd3,\n"
"    XK_R2:                          0xffd3,\n"
"    XK_F23:                         0xffd4,\n"
"    XK_R3:                          0xffd4,\n"
"    XK_F24:                         0xffd5,\n"
"    XK_R4:                          0xffd5,\n"
"    XK_F25:                         0xffd6,\n"
"    XK_R5:                          0xffd6,\n"
"    XK_F26:                         0xffd7,\n"
"    XK_R6:                          0xffd7,\n"
"    XK_F27:                         0xffd8,\n"
"    XK_R7:                          0xffd8,\n"
"    XK_F28:                         0xffd9,\n"
"    XK_R8:                          0xffd9,\n"
"    XK_F29:                         0xffda,\n"
"    XK_R9:                          0xffda,\n"
"    XK_F30:                         0xffdb,\n"
"    XK_R10:                         0xffdb,\n"
"    XK_F31:                         0xffdc,\n"
"    XK_R11:                         0xffdc,\n"
"    XK_F32:                         0xffdd,\n"
"    XK_R12:                         0xffdd,\n"
"    XK_F33:                         0xffde,\n"
"    XK_R13:                         0xffde,\n"
"    XK_F34:                         0xffdf,\n"
"    XK_R14:                         0xffdf,\n"
"    XK_F35:                         0xffe0,\n"
"    XK_R15:                         0xffe0,\n"
"\n"
"    /* Modifiers */\n"
"\n"
"    XK_Shift_L:                     0xffe1, /* Left shift */\n"
"    XK_Shift_R:                     0xffe2, /* Right shift */\n"
"    XK_Control_L:                   0xffe3, /* Left control */\n"
"    XK_Control_R:                   0xffe4, /* Right control */\n"
"    XK_Caps_Lock:                   0xffe5, /* Caps lock */\n"
"    XK_Shift_Lock:                  0xffe6, /* Shift lock */\n"
"\n"
"    XK_Meta_L:                      0xffe7, /* Left meta */\n"
"    XK_Meta_R:                      0xffe8, /* Right meta */\n"
"    XK_Alt_L:                       0xffe9, /* Left alt */\n"
"    XK_Alt_R:                       0xffea, /* Right alt */\n"
"    XK_Super_L:                     0xffeb, /* Left super */\n"
"    XK_Super_R:                     0xffec, /* Right super */\n"
"    XK_Hyper_L:                     0xffed, /* Left hyper */\n"
"    XK_Hyper_R:                     0xffee, /* Right hyper */\n"
"\n"
"    /*\n"
"     * Keyboard (XKB) Extension function and modifier keys\n"
"     * (from Appendix C of \"The X Keyboard Extension: Protocol Specification\")\n"
"     * Byte 3 = 0xfe\n"
"     */\n"
"\n"
"    XK_ISO_Level3_Shift:            0xfe03, /* AltGr */\n"
"    XK_ISO_Next_Group:              0xfe08,\n"
"    XK_ISO_Prev_Group:              0xfe0a,\n"
"    XK_ISO_First_Group:             0xfe0c,\n"
"    XK_ISO_Last_Group:              0xfe0e,\n"
"\n"
"    /*\n"
"     * Latin 1\n"
"     * (ISO/IEC 8859-1: Unicode U+0020..U+00FF)\n"
"     * Byte 3: 0\n"
"     */\n"
"\n"
"    XK_space:                       0x0020, /* U+0020 SPACE */\n"
"    XK_exclam:                      0x0021, /* U+0021 EXCLAMATION MARK */\n"
"    XK_quotedbl:                    0x0022, /* U+0022 QUOTATION MARK */\n"
"    XK_numbersign:                  0x0023, /* U+0023 NUMBER SIGN */\n"
"    XK_dollar:                      0x0024, /* U+0024 DOLLAR SIGN */\n"
"    XK_percent:                     0x0025, /* U+0025 PERCENT SIGN */\n"
"    XK_ampersand:                   0x0026, /* U+0026 AMPERSAND */\n"
"    XK_apostrophe:                  0x0027, /* U+0027 APOSTROPHE */\n"
"    XK_quoteright:                  0x0027, /* deprecated */\n"
"    XK_parenleft:                   0x0028, /* U+0028 LEFT PARENTHESIS */\n"
"    XK_parenright:                  0x0029, /* U+0029 RIGHT PARENTHESIS */\n"
"    XK_asterisk:                    0x002a, /* U+002A ASTERISK */\n"
"    XK_plus:                        0x002b, /* U+002B PLUS SIGN */\n"
"    XK_comma:                       0x002c, /* U+002C COMMA */\n"
"    XK_minus:                       0x002d, /* U+002D HYPHEN-MINUS */\n"
"    XK_period:                      0x002e, /* U+002E FULL STOP */\n"
"    XK_slash:                       0x002f, /* U+002F SOLIDUS */\n"
"    XK_0:                           0x0030, /* U+0030 DIGIT ZERO */\n"
"    XK_1:                           0x0031, /* U+0031 DIGIT ONE */\n"
"    XK_2:                           0x0032, /* U+0032 DIGIT TWO */\n"
"    XK_3:                           0x0033, /* U+0033 DIGIT THREE */\n"
"    XK_4:                           0x0034, /* U+0034 DIGIT FOUR */\n"
"    XK_5:                           0x0035, /* U+0035 DIGIT FIVE */\n"
"    XK_6:                           0x0036, /* U+0036 DIGIT SIX */\n"
"    XK_7:                           0x0037, /* U+0037 DIGIT SEVEN */\n"
"    XK_8:                           0x0038, /* U+0038 DIGIT EIGHT */\n"
"    XK_9:                           0x0039, /* U+0039 DIGIT NINE */\n"
"    XK_colon:                       0x003a, /* U+003A COLON */\n"
"    XK_semicolon:                   0x003b, /* U+003B SEMICOLON */\n"
"    XK_less:                        0x003c, /* U+003C LESS-THAN SIGN */\n"
"    XK_equal:                       0x003d, /* U+003D EQUALS SIGN */\n"
"    XK_greater:                     0x003e, /* U+003E GREATER-THAN SIGN */\n"
"    XK_question:                    0x003f, /* U+003F QUESTION MARK */\n"
"    XK_at:                          0x0040, /* U+0040 COMMERCIAL AT */\n"
"    XK_A:                           0x0041, /* U+0041 LATIN CAPITAL LETTER A */\n"
"    XK_B:                           0x0042, /* U+0042 LATIN CAPITAL LETTER B */\n"
"    XK_C:                           0x0043, /* U+0043 LATIN CAPITAL LETTER C */\n"
"    XK_D:                           0x0044, /* U+0044 LATIN CAPITAL LETTER D */\n"
"    XK_E:                           0x0045, /* U+0045 LATIN CAPITAL LETTER E */\n"
"    XK_F:                           0x0046, /* U+0046 LATIN CAPITAL LETTER F */\n"
"    XK_G:                           0x0047, /* U+0047 LATIN CAPITAL LETTER G */\n"
"    XK_H:                           0x0048, /* U+0048 LATIN CAPITAL LETTER H */\n"
"    XK_I:                           0x0049, /* U+0049 LATIN CAPITAL LETTER I */\n"
"    XK_J:                           0x004a, /* U+004A LATIN CAPITAL LETTER J */\n"
"    XK_K:                           0x004b, /* U+004B LATIN CAPITAL LETTER K */\n"
"    XK_L:                           0x004c, /* U+004C LATIN CAPITAL LETTER L */\n"
"    XK_M:                           0x004d, /* U+004D LATIN CAPITAL LETTER M */\n"
"    XK_N:                           0x004e, /* U+004E LATIN CAPITAL LETTER N */\n"
"    XK_O:                           0x004f, /* U+004F LATIN CAPITAL LETTER O */\n"
"    XK_P:                           0x0050, /* U+0050 LATIN CAPITAL LETTER P */\n"
"    XK_Q:                           0x0051, /* U+0051 LATIN CAPITAL LETTER Q */\n"
"    XK_R:                           0x0052, /* U+0052 LATIN CAPITAL LETTER R */\n"
"    XK_S:                           0x0053, /* U+0053 LATIN CAPITAL LETTER S */\n"
"    XK_T:                           0x0054, /* U+0054 LATIN CAPITAL LETTER T */\n"
"    XK_U:                           0x0055, /* U+0055 LATIN CAPITAL LETTER U */\n"
"    XK_V:                           0x0056, /* U+0056 LATIN CAPITAL LETTER V */\n"
"    XK_W:                           0x0057, /* U+0057 LATIN CAPITAL LETTER W */\n"
"    XK_X:                           0x0058, /* U+0058 LATIN CAPITAL LETTER X */\n"
"    XK_Y:                           0x0059, /* U+0059 LATIN CAPITAL LETTER Y */\n"
"    XK_Z:                           0x005a, /* U+005A LATIN CAPITAL LETTER Z */\n"
"    XK_bracketleft:                 0x005b, /* U+005B LEFT SQUARE BRACKET */\n"
"    XK_backslash:                   0x005c, /* U+005C REVERSE SOLIDUS */\n"
"    XK_bracketright:                0x005d, /* U+005D RIGHT SQUARE BRACKET */\n"
"    XK_asciicircum:                 0x005e, /* U+005E CIRCUMFLEX ACCENT */\n"
"    XK_underscore:                  0x005f, /* U+005F LOW LINE */\n"
"    XK_grave:                       0x0060, /* U+0060 GRAVE ACCENT */\n"
"    XK_quoteleft:                   0x0060, /* deprecated */\n"
"    XK_a:                           0x0061, /* U+0061 LATIN SMALL LETTER A */\n"
"    XK_b:                           0x0062, /* U+0062 LATIN SMALL LETTER B */\n"
"    XK_c:                           0x0063, /* U+0063 LATIN SMALL LETTER C */\n"
"    XK_d:                           0x0064, /* U+0064 LATIN SMALL LETTER D */\n"
"    XK_e:                           0x0065, /* U+0065 LATIN SMALL LETTER E */\n"
"    XK_f:                           0x0066, /* U+0066 LATIN SMALL LETTER F */\n"
"    XK_g:                           0x0067, /* U+0067 LATIN SMALL LETTER G */\n"
"    XK_h:                           0x0068, /* U+0068 LATIN SMALL LETTER H */\n"
"    XK_i:                           0x0069, /* U+0069 LATIN SMALL LETTER I */\n"
"    XK_j:                           0x006a, /* U+006A LATIN SMALL LETTER J */\n"
"    XK_k:                           0x006b, /* U+006B LATIN SMALL LETTER K */\n"
"    XK_l:                           0x006c, /* U+006C LATIN SMALL LETTER L */\n"
"    XK_m:                           0x006d, /* U+006D LATIN SMALL LETTER M */\n"
"    XK_n:                           0x006e, /* U+006E LATIN SMALL LETTER N */\n"
"    XK_o:                           0x006f, /* U+006F LATIN SMALL LETTER O */\n"
"    XK_p:                           0x0070, /* U+0070 LATIN SMALL LETTER P */\n"
"    XK_q:                           0x0071, /* U+0071 LATIN SMALL LETTER Q */\n"
"    XK_r:                           0x0072, /* U+0072 LATIN SMALL LETTER R */\n"
"    XK_s:                           0x0073, /* U+0073 LATIN SMALL LETTER S */\n"
"    XK_t:                           0x0074, /* U+0074 LATIN SMALL LETTER T */\n"
"    XK_u:                           0x0075, /* U+0075 LATIN SMALL LETTER U */\n"
"    XK_v:                           0x0076, /* U+0076 LATIN SMALL LETTER V */\n"
"    XK_w:                           0x0077, /* U+0077 LATIN SMALL LETTER W */\n"
"    XK_x:                           0x0078, /* U+0078 LATIN SMALL LETTER X */\n"
"    XK_y:                           0x0079, /* U+0079 LATIN SMALL LETTER Y */\n"
"    XK_z:                           0x007a, /* U+007A LATIN SMALL LETTER Z */\n"
"    XK_braceleft:                   0x007b, /* U+007B LEFT CURLY BRACKET */\n"
"    XK_bar:                         0x007c, /* U+007C VERTICAL LINE */\n"
"    XK_braceright:                  0x007d, /* U+007D RIGHT CURLY BRACKET */\n"
"    XK_asciitilde:                  0x007e, /* U+007E TILDE */\n"
"\n"
"    XK_nobreakspace:                0x00a0, /* U+00A0 NO-BREAK SPACE */\n"
"    XK_exclamdown:                  0x00a1, /* U+00A1 INVERTED EXCLAMATION MARK */\n"
"    XK_cent:                        0x00a2, /* U+00A2 CENT SIGN */\n"
"    XK_sterling:                    0x00a3, /* U+00A3 POUND SIGN */\n"
"    XK_currency:                    0x00a4, /* U+00A4 CURRENCY SIGN */\n"
"    XK_yen:                         0x00a5, /* U+00A5 YEN SIGN */\n"
"    XK_brokenbar:                   0x00a6, /* U+00A6 BROKEN BAR */\n"
"    XK_section:                     0x00a7, /* U+00A7 SECTION SIGN */\n"
"    XK_diaeresis:                   0x00a8, /* U+00A8 DIAERESIS */\n"
"    XK_copyright:                   0x00a9, /* U+00A9 COPYRIGHT SIGN */\n"
"    XK_ordfeminine:                 0x00aa, /* U+00AA FEMININE ORDINAL INDICATOR */\n"
"    XK_guillemotleft:               0x00ab, /* U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */\n"
"    XK_notsign:                     0x00ac, /* U+00AC NOT SIGN */\n"
"    XK_hyphen:                      0x00ad, /* U+00AD SOFT HYPHEN */\n"
"    XK_registered:                  0x00ae, /* U+00AE REGISTERED SIGN */\n"
"    XK_macron:                      0x00af, /* U+00AF MACRON */\n"
"    XK_degree:                      0x00b0, /* U+00B0 DEGREE SIGN */\n"
"    XK_plusminus:                   0x00b1, /* U+00B1 PLUS-MINUS SIGN */\n"
"    XK_twosuperior:                 0x00b2, /* U+00B2 SUPERSCRIPT TWO */\n"
"    XK_threesuperior:               0x00b3, /* U+00B3 SUPERSCRIPT THREE */\n"
"    XK_acute:                       0x00b4, /* U+00B4 ACUTE ACCENT */\n"
"    XK_mu:                          0x00b5, /* U+00B5 MICRO SIGN */\n"
"    XK_paragraph:                   0x00b6, /* U+00B6 PILCROW SIGN */\n"
"    XK_periodcentered:              0x00b7, /* U+00B7 MIDDLE DOT */\n"
"    XK_cedilla:                     0x00b8, /* U+00B8 CEDILLA */\n"
"    XK_onesuperior:                 0x00b9, /* U+00B9 SUPERSCRIPT ONE */\n"
"    XK_masculine:                   0x00ba, /* U+00BA MASCULINE ORDINAL INDICATOR */\n"
"    XK_guillemotright:              0x00bb, /* U+00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */\n"
"    XK_onequarter:                  0x00bc, /* U+00BC VULGAR FRACTION ONE QUARTER */\n"
"    XK_onehalf:                     0x00bd, /* U+00BD VULGAR FRACTION ONE HALF */\n"
"    XK_threequarters:               0x00be, /* U+00BE VULGAR FRACTION THREE QUARTERS */\n"
"    XK_questiondown:                0x00bf, /* U+00BF INVERTED QUESTION MARK */\n"
"    XK_Agrave:                      0x00c0, /* U+00C0 LATIN CAPITAL LETTER A WITH GRAVE */\n"
"    XK_Aacute:                      0x00c1, /* U+00C1 LATIN CAPITAL LETTER A WITH ACUTE */\n"
"    XK_Acircumflex:                 0x00c2, /* U+00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */\n"
"    XK_Atilde:                      0x00c3, /* U+00C3 LATIN CAPITAL LETTER A WITH TILDE */\n"
"    XK_Adiaeresis:                  0x00c4, /* U+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS */\n"
"    XK_Aring:                       0x00c5, /* U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE */\n"
"    XK_AE:                          0x00c6, /* U+00C6 LATIN CAPITAL LETTER AE */\n"
"    XK_Ccedilla:                    0x00c7, /* U+00C7 LATIN CAPITAL LETTER C WITH CEDILLA */\n"
"    XK_Egrave:                      0x00c8, /* U+00C8 LATIN CAPITAL LETTER E WITH GRAVE */\n"
"    XK_Eacute:                      0x00c9, /* U+00C9 LATIN CAPITAL LETTER E WITH ACUTE */\n"
"    XK_Ecircumflex:                 0x00ca, /* U+00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX */\n"
"    XK_Ediaeresis:                  0x00cb, /* U+00CB LATIN CAPITAL LETTER E WITH DIAERESIS */\n"
"    XK_Igrave:                      0x00cc, /* U+00CC LATIN CAPITAL LETTER I WITH GRAVE */\n"
"    XK_Iacute:                      0x00cd, /* U+00CD LATIN CAPITAL LETTER I WITH ACUTE */\n"
"    XK_Icircumflex:                 0x00ce, /* U+00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX */\n"
"    XK_Idiaeresis:                  0x00cf, /* U+00CF LATIN CAPITAL LETTER I WITH DIAERESIS */\n"
"    XK_ETH:                         0x00d0, /* U+00D0 LATIN CAPITAL LETTER ETH */\n"
"    XK_Eth:                         0x00d0, /* deprecated */\n"
"    XK_Ntilde:                      0x00d1, /* U+00D1 LATIN CAPITAL LETTER N WITH TILDE */\n"
"    XK_Ograve:                      0x00d2, /* U+00D2 LATIN CAPITAL LETTER O WITH GRAVE */\n"
"    XK_Oacute:                      0x00d3, /* U+00D3 LATIN CAPITAL LETTER O WITH ACUTE */\n"
"    XK_Ocircumflex:                 0x00d4, /* U+00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */\n"
"    XK_Otilde:                      0x00d5, /* U+00D5 LATIN CAPITAL LETTER O WITH TILDE */\n"
"    XK_Odiaeresis:                  0x00d6, /* U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS */\n"
"    XK_multiply:                    0x00d7, /* U+00D7 MULTIPLICATION SIGN */\n"
"    XK_Oslash:                      0x00d8, /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */\n"
"    XK_Ooblique:                    0x00d8, /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */\n"
"    XK_Ugrave:                      0x00d9, /* U+00D9 LATIN CAPITAL LETTER U WITH GRAVE */\n"
"    XK_Uacute:                      0x00da, /* U+00DA LATIN CAPITAL LETTER U WITH ACUTE */\n"
"    XK_Ucircumflex:                 0x00db, /* U+00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX */\n"
"    XK_Udiaeresis:                  0x00dc, /* U+00DC LATIN CAPITAL LETTER U WITH DIAERESIS */\n"
"    XK_Yacute:                      0x00dd, /* U+00DD LATIN CAPITAL LETTER Y WITH ACUTE */\n"
"    XK_THORN:                       0x00de, /* U+00DE LATIN CAPITAL LETTER THORN */\n"
"    XK_Thorn:                       0x00de, /* deprecated */\n"
"    XK_ssharp:                      0x00df, /* U+00DF LATIN SMALL LETTER SHARP S */\n"
"    XK_agrave:                      0x00e0, /* U+00E0 LATIN SMALL LETTER A WITH GRAVE */\n"
"    XK_aacute:                      0x00e1, /* U+00E1 LATIN SMALL LETTER A WITH ACUTE */\n"
"    XK_acircumflex:                 0x00e2, /* U+00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX */\n"
"    XK_atilde:                      0x00e3, /* U+00E3 LATIN SMALL LETTER A WITH TILDE */\n"
"    XK_adiaeresis:                  0x00e4, /* U+00E4 LATIN SMALL LETTER A WITH DIAERESIS */\n"
"    XK_aring:                       0x00e5, /* U+00E5 LATIN SMALL LETTER A WITH RING ABOVE */\n"
"    XK_ae:                          0x00e6, /* U+00E6 LATIN SMALL LETTER AE */\n"
"    XK_ccedilla:                    0x00e7, /* U+00E7 LATIN SMALL LETTER C WITH CEDILLA */\n"
"    XK_egrave:                      0x00e8, /* U+00E8 LATIN SMALL LETTER E WITH GRAVE */\n"
"    XK_eacute:                      0x00e9, /* U+00E9 LATIN SMALL LETTER E WITH ACUTE */\n"
"    XK_ecircumflex:                 0x00ea, /* U+00EA LATIN SMALL LETTER E WITH CIRCUMFLEX */\n"
"    XK_ediaeresis:                  0x00eb, /* U+00EB LATIN SMALL LETTER E WITH DIAERESIS */\n"
"    XK_igrave:                      0x00ec, /* U+00EC LATIN SMALL LETTER I WITH GRAVE */\n"
"    XK_iacute:                      0x00ed, /* U+00ED LATIN SMALL LETTER I WITH ACUTE */\n"
"    XK_icircumflex:                 0x00ee, /* U+00EE LATIN SMALL LETTER I WITH CIRCUMFLEX */\n"
"    XK_idiaeresis:                  0x00ef, /* U+00EF LATIN SMALL LETTER I WITH DIAERESIS */\n"
"    XK_eth:                         0x00f0, /* U+00F0 LATIN SMALL LETTER ETH */\n"
"    XK_ntilde:                      0x00f1, /* U+00F1 LATIN SMALL LETTER N WITH TILDE */\n"
"    XK_ograve:                      0x00f2, /* U+00F2 LATIN SMALL LETTER O WITH GRAVE */\n"
"    XK_oacute:                      0x00f3, /* U+00F3 LATIN SMALL LETTER O WITH ACUTE */\n"
"    XK_ocircumflex:                 0x00f4, /* U+00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX */\n"
"    XK_otilde:                      0x00f5, /* U+00F5 LATIN SMALL LETTER O WITH TILDE */\n"
"    XK_odiaeresis:                  0x00f6, /* U+00F6 LATIN SMALL LETTER O WITH DIAERESIS */\n"
"    XK_division:                    0x00f7, /* U+00F7 DIVISION SIGN */\n"
"    XK_oslash:                      0x00f8, /* U+00F8 LATIN SMALL LETTER O WITH STROKE */\n"
"    XK_ooblique:                    0x00f8, /* U+00F8 LATIN SMALL LETTER O WITH STROKE */\n"
"    XK_ugrave:                      0x00f9, /* U+00F9 LATIN SMALL LETTER U WITH GRAVE */\n"
"    XK_uacute:                      0x00fa, /* U+00FA LATIN SMALL LETTER U WITH ACUTE */\n"
"    XK_ucircumflex:                 0x00fb, /* U+00FB LATIN SMALL LETTER U WITH CIRCUMFLEX */\n"
"    XK_udiaeresis:                  0x00fc, /* U+00FC LATIN SMALL LETTER U WITH DIAERESIS */\n"
"    XK_yacute:                      0x00fd, /* U+00FD LATIN SMALL LETTER Y WITH ACUTE */\n"
"    XK_thorn:                       0x00fe, /* U+00FE LATIN SMALL LETTER THORN */\n"
"    XK_ydiaeresis:                  0x00ff, /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */\n"
"\n"
"    /*\n"
"     * Korean\n"
"     * Byte 3 = 0x0e\n"
"     */\n"
"\n"
"    XK_Hangul:                      0xff31, /* Hangul start/stop(toggle) */\n"
"    XK_Hangul_Hanja:                0xff34, /* Start Hangul->Hanja Conversion */\n"
"    XK_Hangul_Jeonja:               0xff38, /* Jeonja mode */\n"
"\n"
"    /*\n"
"     * XFree86 vendor specific keysyms.\n"
"     *\n"
"     * The XFree86 keysym range is 0x10080001 - 0x1008FFFF.\n"
"     */\n"
"\n"
"    XF86XK_ModeLock:                0x1008FF01,\n"
"    XF86XK_MonBrightnessUp:         0x1008FF02,\n"
"    XF86XK_MonBrightnessDown:       0x1008FF03,\n"
"    XF86XK_KbdLightOnOff:           0x1008FF04,\n"
"    XF86XK_KbdBrightnessUp:         0x1008FF05,\n"
"    XF86XK_KbdBrightnessDown:       0x1008FF06,\n"
"    XF86XK_Standby:                 0x1008FF10,\n"
"    XF86XK_AudioLowerVolume:        0x1008FF11,\n"
"    XF86XK_AudioMute:               0x1008FF12,\n"
"    XF86XK_AudioRaiseVolume:        0x1008FF13,\n"
"    XF86XK_AudioPlay:               0x1008FF14,\n"
"    XF86XK_AudioStop:               0x1008FF15,\n"
"    XF86XK_AudioPrev:               0x1008FF16,\n"
"    XF86XK_AudioNext:               0x1008FF17,\n"
"    XF86XK_HomePage:                0x1008FF18,\n"
"    XF86XK_Mail:                    0x1008FF19,\n"
"    XF86XK_Start:                   0x1008FF1A,\n"
"    XF86XK_Search:                  0x1008FF1B,\n"
"    XF86XK_AudioRecord:             0x1008FF1C,\n"
"    XF86XK_Calculator:              0x1008FF1D,\n"
"    XF86XK_Memo:                    0x1008FF1E,\n"
"    XF86XK_ToDoList:                0x1008FF1F,\n"
"    XF86XK_Calendar:                0x1008FF20,\n"
"    XF86XK_PowerDown:               0x1008FF21,\n"
"    XF86XK_ContrastAdjust:          0x1008FF22,\n"
"    XF86XK_RockerUp:                0x1008FF23,\n"
"    XF86XK_RockerDown:              0x1008FF24,\n"
"    XF86XK_RockerEnter:             0x1008FF25,\n"
"    XF86XK_Back:                    0x1008FF26,\n"
"    XF86XK_Forward:                 0x1008FF27,\n"
"    XF86XK_Stop:                    0x1008FF28,\n"
"    XF86XK_Refresh:                 0x1008FF29,\n"
"    XF86XK_PowerOff:                0x1008FF2A,\n"
"    XF86XK_WakeUp:                  0x1008FF2B,\n"
"    XF86XK_Eject:                   0x1008FF2C,\n"
"    XF86XK_ScreenSaver:             0x1008FF2D,\n"
"    XF86XK_WWW:                     0x1008FF2E,\n"
"    XF86XK_Sleep:                   0x1008FF2F,\n"
"    XF86XK_Favorites:               0x1008FF30,\n"
"    XF86XK_AudioPause:              0x1008FF31,\n"
"    XF86XK_AudioMedia:              0x1008FF32,\n"
"    XF86XK_MyComputer:              0x1008FF33,\n"
"    XF86XK_VendorHome:              0x1008FF34,\n"
"    XF86XK_LightBulb:               0x1008FF35,\n"
"    XF86XK_Shop:                    0x1008FF36,\n"
"    XF86XK_History:                 0x1008FF37,\n"
"    XF86XK_OpenURL:                 0x1008FF38,\n"
"    XF86XK_AddFavorite:             0x1008FF39,\n"
"    XF86XK_HotLinks:                0x1008FF3A,\n"
"    XF86XK_BrightnessAdjust:        0x1008FF3B,\n"
"    XF86XK_Finance:                 0x1008FF3C,\n"
"    XF86XK_Community:               0x1008FF3D,\n"
"    XF86XK_AudioRewind:             0x1008FF3E,\n"
"    XF86XK_BackForward:             0x1008FF3F,\n"
"    XF86XK_Launch0:                 0x1008FF40,\n"
"    XF86XK_Launch1:                 0x1008FF41,\n"
"    XF86XK_Launch2:                 0x1008FF42,\n"
"    XF86XK_Launch3:                 0x1008FF43,\n"
"    XF86XK_Launch4:                 0x1008FF44,\n"
"    XF86XK_Launch5:                 0x1008FF45,\n"
"    XF86XK_Launch6:                 0x1008FF46,\n"
"    XF86XK_Launch7:                 0x1008FF47,\n"
"    XF86XK_Launch8:                 0x1008FF48,\n"
"    XF86XK_Launch9:                 0x1008FF49,\n"
"    XF86XK_LaunchA:                 0x1008FF4A,\n"
"    XF86XK_LaunchB:                 0x1008FF4B,\n"
"    XF86XK_LaunchC:                 0x1008FF4C,\n"
"    XF86XK_LaunchD:                 0x1008FF4D,\n"
"    XF86XK_LaunchE:                 0x1008FF4E,\n"
"    XF86XK_LaunchF:                 0x1008FF4F,\n"
"    XF86XK_ApplicationLeft:         0x1008FF50,\n"
"    XF86XK_ApplicationRight:        0x1008FF51,\n"
"    XF86XK_Book:                    0x1008FF52,\n"
"    XF86XK_CD:                      0x1008FF53,\n"
"    XF86XK_Calculater:              0x1008FF54,\n"
"    XF86XK_Clear:                   0x1008FF55,\n"
"    XF86XK_Close:                   0x1008FF56,\n"
"    XF86XK_Copy:                    0x1008FF57,\n"
"    XF86XK_Cut:                     0x1008FF58,\n"
"    XF86XK_Display:                 0x1008FF59,\n"
"    XF86XK_DOS:                     0x1008FF5A,\n"
"    XF86XK_Documents:               0x1008FF5B,\n"
"    XF86XK_Excel:                   0x1008FF5C,\n"
"    XF86XK_Explorer:                0x1008FF5D,\n"
"    XF86XK_Game:                    0x1008FF5E,\n"
"    XF86XK_Go:                      0x1008FF5F,\n"
"    XF86XK_iTouch:                  0x1008FF60,\n"
"    XF86XK_LogOff:                  0x1008FF61,\n"
"    XF86XK_Market:                  0x1008FF62,\n"
"    XF86XK_Meeting:                 0x1008FF63,\n"
"    XF86XK_MenuKB:                  0x1008FF65,\n"
"    XF86XK_MenuPB:                  0x1008FF66,\n"
"    XF86XK_MySites:                 0x1008FF67,\n"
"    XF86XK_New:                     0x1008FF68,\n"
"    XF86XK_News:                    0x1008FF69,\n"
"    XF86XK_OfficeHome:              0x1008FF6A,\n"
"    XF86XK_Open:                    0x1008FF6B,\n"
"    XF86XK_Option:                  0x1008FF6C,\n"
"    XF86XK_Paste:                   0x1008FF6D,\n"
"    XF86XK_Phone:                   0x1008FF6E,\n"
"    XF86XK_Q:                       0x1008FF70,\n"
"    XF86XK_Reply:                   0x1008FF72,\n"
"    XF86XK_Reload:                  0x1008FF73,\n"
"    XF86XK_RotateWindows:           0x1008FF74,\n"
"    XF86XK_RotationPB:              0x1008FF75,\n"
"    XF86XK_RotationKB:              0x1008FF76,\n"
"    XF86XK_Save:                    0x1008FF77,\n"
"    XF86XK_ScrollUp:                0x1008FF78,\n"
"    XF86XK_ScrollDown:              0x1008FF79,\n"
"    XF86XK_ScrollClick:             0x1008FF7A,\n"
"    XF86XK_Send:                    0x1008FF7B,\n"
"    XF86XK_Spell:                   0x1008FF7C,\n"
"    XF86XK_SplitScreen:             0x1008FF7D,\n"
"    XF86XK_Support:                 0x1008FF7E,\n"
"    XF86XK_TaskPane:                0x1008FF7F,\n"
"    XF86XK_Terminal:                0x1008FF80,\n"
"    XF86XK_Tools:                   0x1008FF81,\n"
"    XF86XK_Travel:                  0x1008FF82,\n"
"    XF86XK_UserPB:                  0x1008FF84,\n"
"    XF86XK_User1KB:                 0x1008FF85,\n"
"    XF86XK_User2KB:                 0x1008FF86,\n"
"    XF86XK_Video:                   0x1008FF87,\n"
"    XF86XK_WheelButton:             0x1008FF88,\n"
"    XF86XK_Word:                    0x1008FF89,\n"
"    XF86XK_Xfer:                    0x1008FF8A,\n"
"    XF86XK_ZoomIn:                  0x1008FF8B,\n"
"    XF86XK_ZoomOut:                 0x1008FF8C,\n"
"    XF86XK_Away:                    0x1008FF8D,\n"
"    XF86XK_Messenger:               0x1008FF8E,\n"
"    XF86XK_WebCam:                  0x1008FF8F,\n"
"    XF86XK_MailForward:             0x1008FF90,\n"
"    XF86XK_Pictures:                0x1008FF91,\n"
"    XF86XK_Music:                   0x1008FF92,\n"
"    XF86XK_Battery:                 0x1008FF93,\n"
"    XF86XK_Bluetooth:               0x1008FF94,\n"
"    XF86XK_WLAN:                    0x1008FF95,\n"
"    XF86XK_UWB:                     0x1008FF96,\n"
"    XF86XK_AudioForward:            0x1008FF97,\n"
"    XF86XK_AudioRepeat:             0x1008FF98,\n"
"    XF86XK_AudioRandomPlay:         0x1008FF99,\n"
"    XF86XK_Subtitle:                0x1008FF9A,\n"
"    XF86XK_AudioCycleTrack:         0x1008FF9B,\n"
"    XF86XK_CycleAngle:              0x1008FF9C,\n"
"    XF86XK_FrameBack:               0x1008FF9D,\n"
"    XF86XK_FrameForward:            0x1008FF9E,\n"
"    XF86XK_Time:                    0x1008FF9F,\n"
"    XF86XK_Select:                  0x1008FFA0,\n"
"    XF86XK_View:                    0x1008FFA1,\n"
"    XF86XK_TopMenu:                 0x1008FFA2,\n"
"    XF86XK_Red:                     0x1008FFA3,\n"
"    XF86XK_Green:                   0x1008FFA4,\n"
"    XF86XK_Yellow:                  0x1008FFA5,\n"
"    XF86XK_Blue:                    0x1008FFA6,\n"
"    XF86XK_Suspend:                 0x1008FFA7,\n"
"    XF86XK_Hibernate:               0x1008FFA8,\n"
"    XF86XK_TouchpadToggle:          0x1008FFA9,\n"
"    XF86XK_TouchpadOn:              0x1008FFB0,\n"
"    XF86XK_TouchpadOff:             0x1008FFB1,\n"
"    XF86XK_AudioMicMute:            0x1008FFB2,\n"
"    XF86XK_Switch_VT_1:             0x1008FE01,\n"
"    XF86XK_Switch_VT_2:             0x1008FE02,\n"
"    XF86XK_Switch_VT_3:             0x1008FE03,\n"
"    XF86XK_Switch_VT_4:             0x1008FE04,\n"
"    XF86XK_Switch_VT_5:             0x1008FE05,\n"
"    XF86XK_Switch_VT_6:             0x1008FE06,\n"
"    XF86XK_Switch_VT_7:             0x1008FE07,\n"
"    XF86XK_Switch_VT_8:             0x1008FE08,\n"
"    XF86XK_Switch_VT_9:             0x1008FE09,\n"
"    XF86XK_Switch_VT_10:            0x1008FE0A,\n"
"    XF86XK_Switch_VT_11:            0x1008FE0B,\n"
"    XF86XK_Switch_VT_12:            0x1008FE0C,\n"
"    XF86XK_Ungrab:                  0x1008FE20,\n"
"    XF86XK_ClearGrab:               0x1008FE21,\n"
"    XF86XK_Next_VMode:              0x1008FE22,\n"
"    XF86XK_Prev_VMode:              0x1008FE23,\n"
"    XF86XK_LogWindowTree:           0x1008FE24,\n"
"    XF86XK_LogGrabInfo:             0x1008FE25,\n"
"};\n"
;
    if (strcmp(path, "/novnc/input/keysym.js") == 0) return novnc_input_keysym_js;
const char *novnc_input_keysymdef_js =
"/*\n"
" * Mapping from Unicode codepoints to X11/RFB keysyms\n"
" *\n"
" * This file was automatically generated from keysymdef.h\n"
" * DO NOT EDIT!\n"
" */\n"
"\n"
"/* Functions at the bottom */\n"
"\n"
"const codepoints = {\n"
"    0x0100: 0x03c0, // XK_Amacron\n"
"    0x0101: 0x03e0, // XK_amacron\n"
"    0x0102: 0x01c3, // XK_Abreve\n"
"    0x0103: 0x01e3, // XK_abreve\n"
"    0x0104: 0x01a1, // XK_Aogonek\n"
"    0x0105: 0x01b1, // XK_aogonek\n"
"    0x0106: 0x01c6, // XK_Cacute\n"
"    0x0107: 0x01e6, // XK_cacute\n"
"    0x0108: 0x02c6, // XK_Ccircumflex\n"
"    0x0109: 0x02e6, // XK_ccircumflex\n"
"    0x010a: 0x02c5, // XK_Cabovedot\n"
"    0x010b: 0x02e5, // XK_cabovedot\n"
"    0x010c: 0x01c8, // XK_Ccaron\n"
"    0x010d: 0x01e8, // XK_ccaron\n"
"    0x010e: 0x01cf, // XK_Dcaron\n"
"    0x010f: 0x01ef, // XK_dcaron\n"
"    0x0110: 0x01d0, // XK_Dstroke\n"
"    0x0111: 0x01f0, // XK_dstroke\n"
"    0x0112: 0x03aa, // XK_Emacron\n"
"    0x0113: 0x03ba, // XK_emacron\n"
"    0x0116: 0x03cc, // XK_Eabovedot\n"
"    0x0117: 0x03ec, // XK_eabovedot\n"
"    0x0118: 0x01ca, // XK_Eogonek\n"
"    0x0119: 0x01ea, // XK_eogonek\n"
"    0x011a: 0x01cc, // XK_Ecaron\n"
"    0x011b: 0x01ec, // XK_ecaron\n"
"    0x011c: 0x02d8, // XK_Gcircumflex\n"
"    0x011d: 0x02f8, // XK_gcircumflex\n"
"    0x011e: 0x02ab, // XK_Gbreve\n"
"    0x011f: 0x02bb, // XK_gbreve\n"
"    0x0120: 0x02d5, // XK_Gabovedot\n"
"    0x0121: 0x02f5, // XK_gabovedot\n"
"    0x0122: 0x03ab, // XK_Gcedilla\n"
"    0x0123: 0x03bb, // XK_gcedilla\n"
"    0x0124: 0x02a6, // XK_Hcircumflex\n"
"    0x0125: 0x02b6, // XK_hcircumflex\n"
"    0x0126: 0x02a1, // XK_Hstroke\n"
"    0x0127: 0x02b1, // XK_hstroke\n"
"    0x0128: 0x03a5, // XK_Itilde\n"
"    0x0129: 0x03b5, // XK_itilde\n"
"    0x012a: 0x03cf, // XK_Imacron\n"
"    0x012b: 0x03ef, // XK_imacron\n"
"    0x012e: 0x03c7, // XK_Iogonek\n"
"    0x012f: 0x03e7, // XK_iogonek\n"
"    0x0130: 0x02a9, // XK_Iabovedot\n"
"    0x0131: 0x02b9, // XK_idotless\n"
"    0x0134: 0x02ac, // XK_Jcircumflex\n"
"    0x0135: 0x02bc, // XK_jcircumflex\n"
"    0x0136: 0x03d3, // XK_Kcedilla\n"
"    0x0137: 0x03f3, // XK_kcedilla\n"
"    0x0138: 0x03a2, // XK_kra\n"
"    0x0139: 0x01c5, // XK_Lacute\n"
"    0x013a: 0x01e5, // XK_lacute\n"
"    0x013b: 0x03a6, // XK_Lcedilla\n"
"    0x013c: 0x03b6, // XK_lcedilla\n"
"    0x013d: 0x01a5, // XK_Lcaron\n"
"    0x013e: 0x01b5, // XK_lcaron\n"
"    0x0141: 0x01a3, // XK_Lstroke\n"
"    0x0142: 0x01b3, // XK_lstroke\n"
"    0x0143: 0x01d1, // XK_Nacute\n"
"    0x0144: 0x01f1, // XK_nacute\n"
"    0x0145: 0x03d1, // XK_Ncedilla\n"
"    0x0146: 0x03f1, // XK_ncedilla\n"
"    0x0147: 0x01d2, // XK_Ncaron\n"
"    0x0148: 0x01f2, // XK_ncaron\n"
"    0x014a: 0x03bd, // XK_ENG\n"
"    0x014b: 0x03bf, // XK_eng\n"
"    0x014c: 0x03d2, // XK_Omacron\n"
"    0x014d: 0x03f2, // XK_omacron\n"
"    0x0150: 0x01d5, // XK_Odoubleacute\n"
"    0x0151: 0x01f5, // XK_odoubleacute\n"
"    0x0152: 0x13bc, // XK_OE\n"
"    0x0153: 0x13bd, // XK_oe\n"
"    0x0154: 0x01c0, // XK_Racute\n"
"    0x0155: 0x01e0, // XK_racute\n"
"    0x0156: 0x03a3, // XK_Rcedilla\n"
"    0x0157: 0x03b3, // XK_rcedilla\n"
"    0x0158: 0x01d8, // XK_Rcaron\n"
"    0x0159: 0x01f8, // XK_rcaron\n"
"    0x015a: 0x01a6, // XK_Sacute\n"
"    0x015b: 0x01b6, // XK_sacute\n"
"    0x015c: 0x02de, // XK_Scircumflex\n"
"    0x015d: 0x02fe, // XK_scircumflex\n"
"    0x015e: 0x01aa, // XK_Scedilla\n"
"    0x015f: 0x01ba, // XK_scedilla\n"
"    0x0160: 0x01a9, // XK_Scaron\n"
"    0x0161: 0x01b9, // XK_scaron\n"
"    0x0162: 0x01de, // XK_Tcedilla\n"
"    0x0163: 0x01fe, // XK_tcedilla\n"
"    0x0164: 0x01ab, // XK_Tcaron\n"
"    0x0165: 0x01bb, // XK_tcaron\n"
"    0x0166: 0x03ac, // XK_Tslash\n"
"    0x0167: 0x03bc, // XK_tslash\n"
"    0x0168: 0x03dd, // XK_Utilde\n"
"    0x0169: 0x03fd, // XK_utilde\n"
"    0x016a: 0x03de, // XK_Umacron\n"
"    0x016b: 0x03fe, // XK_umacron\n"
"    0x016c: 0x02dd, // XK_Ubreve\n"
"    0x016d: 0x02fd, // XK_ubreve\n"
"    0x016e: 0x01d9, // XK_Uring\n"
"    0x016f: 0x01f9, // XK_uring\n"
"    0x0170: 0x01db, // XK_Udoubleacute\n"
"    0x0171: 0x01fb, // XK_udoubleacute\n"
"    0x0172: 0x03d9, // XK_Uogonek\n"
"    0x0173: 0x03f9, // XK_uogonek\n"
"    0x0178: 0x13be, // XK_Ydiaeresis\n"
"    0x0179: 0x01ac, // XK_Zacute\n"
"    0x017a: 0x01bc, // XK_zacute\n"
"    0x017b: 0x01af, // XK_Zabovedot\n"
"    0x017c: 0x01bf, // XK_zabovedot\n"
"    0x017d: 0x01ae, // XK_Zcaron\n"
"    0x017e: 0x01be, // XK_zcaron\n"
"    0x0192: 0x08f6, // XK_function\n"
"    0x01d2: 0x10001d1, // XK_Ocaron\n"
"    0x02c7: 0x01b7, // XK_caron\n"
"    0x02d8: 0x01a2, // XK_breve\n"
"    0x02d9: 0x01ff, // XK_abovedot\n"
"    0x02db: 0x01b2, // XK_ogonek\n"
"    0x02dd: 0x01bd, // XK_doubleacute\n"
"    0x0385: 0x07ae, // XK_Greek_accentdieresis\n"
"    0x0386: 0x07a1, // XK_Greek_ALPHAaccent\n"
"    0x0388: 0x07a2, // XK_Greek_EPSILONaccent\n"
"    0x0389: 0x07a3, // XK_Greek_ETAaccent\n"
"    0x038a: 0x07a4, // XK_Greek_IOTAaccent\n"
"    0x038c: 0x07a7, // XK_Greek_OMICRONaccent\n"
"    0x038e: 0x07a8, // XK_Greek_UPSILONaccent\n"
"    0x038f: 0x07ab, // XK_Greek_OMEGAaccent\n"
"    0x0390: 0x07b6, // XK_Greek_iotaaccentdieresis\n"
"    0x0391: 0x07c1, // XK_Greek_ALPHA\n"
"    0x0392: 0x07c2, // XK_Greek_BETA\n"
"    0x0393: 0x07c3, // XK_Greek_GAMMA\n"
"    0x0394: 0x07c4, // XK_Greek_DELTA\n"
"    0x0395: 0x07c5, // XK_Greek_EPSILON\n"
"    0x0396: 0x07c6, // XK_Greek_ZETA\n"
"    0x0397: 0x07c7, // XK_Greek_ETA\n"
"    0x0398: 0x07c8, // XK_Greek_THETA\n"
"    0x0399: 0x07c9, // XK_Greek_IOTA\n"
"    0x039a: 0x07ca, // XK_Greek_KAPPA\n"
"    0x039b: 0x07cb, // XK_Greek_LAMDA\n"
"    0x039c: 0x07cc, // XK_Greek_MU\n"
"    0x039d: 0x07cd, // XK_Greek_NU\n"
"    0x039e: 0x07ce, // XK_Greek_XI\n"
"    0x039f: 0x07cf, // XK_Greek_OMICRON\n"
"    0x03a0: 0x07d0, // XK_Greek_PI\n"
"    0x03a1: 0x07d1, // XK_Greek_RHO\n"
"    0x03a3: 0x07d2, // XK_Greek_SIGMA\n"
"    0x03a4: 0x07d4, // XK_Greek_TAU\n"
"    0x03a5: 0x07d5, // XK_Greek_UPSILON\n"
"    0x03a6: 0x07d6, // XK_Greek_PHI\n"
"    0x03a7: 0x07d7, // XK_Greek_CHI\n"
"    0x03a8: 0x07d8, // XK_Greek_PSI\n"
"    0x03a9: 0x07d9, // XK_Greek_OMEGA\n"
"    0x03aa: 0x07a5, // XK_Greek_IOTAdieresis\n"
"    0x03ab: 0x07a9, // XK_Greek_UPSILONdieresis\n"
"    0x03ac: 0x07b1, // XK_Greek_alphaaccent\n"
"    0x03ad: 0x07b2, // XK_Greek_epsilonaccent\n"
"    0x03ae: 0x07b3, // XK_Greek_etaaccent\n"
"    0x03af: 0x07b4, // XK_Greek_iotaaccent\n"
"    0x03b0: 0x07ba, // XK_Greek_upsilonaccentdieresis\n"
"    0x03b1: 0x07e1, // XK_Greek_alpha\n"
"    0x03b2: 0x07e2, // XK_Greek_beta\n"
"    0x03b3: 0x07e3, // XK_Greek_gamma\n"
"    0x03b4: 0x07e4, // XK_Greek_delta\n"
"    0x03b5: 0x07e5, // XK_Greek_epsilon\n"
"    0x03b6: 0x07e6, // XK_Greek_zeta\n"
"    0x03b7: 0x07e7, // XK_Greek_eta\n"
"    0x03b8: 0x07e8, // XK_Greek_theta\n"
"    0x03b9: 0x07e9, // XK_Greek_iota\n"
"    0x03ba: 0x07ea, // XK_Greek_kappa\n"
"    0x03bb: 0x07eb, // XK_Greek_lamda\n"
"    0x03bc: 0x07ec, // XK_Greek_mu\n"
"    0x03bd: 0x07ed, // XK_Greek_nu\n"
"    0x03be: 0x07ee, // XK_Greek_xi\n"
"    0x03bf: 0x07ef, // XK_Greek_omicron\n"
"    0x03c0: 0x07f0, // XK_Greek_pi\n"
"    0x03c1: 0x07f1, // XK_Greek_rho\n"
"    0x03c2: 0x07f3, // XK_Greek_finalsmallsigma\n"
"    0x03c3: 0x07f2, // XK_Greek_sigma\n"
"    0x03c4: 0x07f4, // XK_Greek_tau\n"
"    0x03c5: 0x07f5, // XK_Greek_upsilon\n"
"    0x03c6: 0x07f6, // XK_Greek_phi\n"
"    0x03c7: 0x07f7, // XK_Greek_chi\n"
"    0x03c8: 0x07f8, // XK_Greek_psi\n"
"    0x03c9: 0x07f9, // XK_Greek_omega\n"
"    0x03ca: 0x07b5, // XK_Greek_iotadieresis\n"
"    0x03cb: 0x07b9, // XK_Greek_upsilondieresis\n"
"    0x03cc: 0x07b7, // XK_Greek_omicronaccent\n"
"    0x03cd: 0x07b8, // XK_Greek_upsilonaccent\n"
"    0x03ce: 0x07bb, // XK_Greek_omegaaccent\n"
"    0x0401: 0x06b3, // XK_Cyrillic_IO\n"
"    0x0402: 0x06b1, // XK_Serbian_DJE\n"
"    0x0403: 0x06b2, // XK_Macedonia_GJE\n"
"    0x0404: 0x06b4, // XK_Ukrainian_IE\n"
"    0x0405: 0x06b5, // XK_Macedonia_DSE\n"
"    0x0406: 0x06b6, // XK_Ukrainian_I\n"
"    0x0407: 0x06b7, // XK_Ukrainian_YI\n"
"    0x0408: 0x06b8, // XK_Cyrillic_JE\n"
"    0x0409: 0x06b9, // XK_Cyrillic_LJE\n"
"    0x040a: 0x06ba, // XK_Cyrillic_NJE\n"
"    0x040b: 0x06bb, // XK_Serbian_TSHE\n"
"    0x040c: 0x06bc, // XK_Macedonia_KJE\n"
"    0x040e: 0x06be, // XK_Byelorussian_SHORTU\n"
"    0x040f: 0x06bf, // XK_Cyrillic_DZHE\n"
"    0x0410: 0x06e1, // XK_Cyrillic_A\n"
"    0x0411: 0x06e2, // XK_Cyrillic_BE\n"
"    0x0412: 0x06f7, // XK_Cyrillic_VE\n"
"    0x0413: 0x06e7, // XK_Cyrillic_GHE\n"
"    0x0414: 0x06e4, // XK_Cyrillic_DE\n"
"    0x0415: 0x06e5, // XK_Cyrillic_IE\n"
"    0x0416: 0x06f6, // XK_Cyrillic_ZHE\n"
"    0x0417: 0x06fa, // XK_Cyrillic_ZE\n"
"    0x0418: 0x06e9, // XK_Cyrillic_I\n"
"    0x0419: 0x06ea, // XK_Cyrillic_SHORTI\n"
"    0x041a: 0x06eb, // XK_Cyrillic_KA\n"
"    0x041b: 0x06ec, // XK_Cyrillic_EL\n"
"    0x041c: 0x06ed, // XK_Cyrillic_EM\n"
"    0x041d: 0x06ee, // XK_Cyrillic_EN\n"
"    0x041e: 0x06ef, // XK_Cyrillic_O\n"
"    0x041f: 0x06f0, // XK_Cyrillic_PE\n"
"    0x0420: 0x06f2, // XK_Cyrillic_ER\n"
"    0x0421: 0x06f3, // XK_Cyrillic_ES\n"
"    0x0422: 0x06f4, // XK_Cyrillic_TE\n"
"    0x0423: 0x06f5, // XK_Cyrillic_U\n"
"    0x0424: 0x06e6, // XK_Cyrillic_EF\n"
"    0x0425: 0x06e8, // XK_Cyrillic_HA\n"
"    0x0426: 0x06e3, // XK_Cyrillic_TSE\n"
"    0x0427: 0x06fe, // XK_Cyrillic_CHE\n"
"    0x0428: 0x06fb, // XK_Cyrillic_SHA\n"
"    0x0429: 0x06fd, // XK_Cyrillic_SHCHA\n"
"    0x042a: 0x06ff, // XK_Cyrillic_HARDSIGN\n"
"    0x042b: 0x06f9, // XK_Cyrillic_YERU\n"
"    0x042c: 0x06f8, // XK_Cyrillic_SOFTSIGN\n"
"    0x042d: 0x06fc, // XK_Cyrillic_E\n"
"    0x042e: 0x06e0, // XK_Cyrillic_YU\n"
"    0x042f: 0x06f1, // XK_Cyrillic_YA\n"
"    0x0430: 0x06c1, // XK_Cyrillic_a\n"
"    0x0431: 0x06c2, // XK_Cyrillic_be\n"
"    0x0432: 0x06d7, // XK_Cyrillic_ve\n"
"    0x0433: 0x06c7, // XK_Cyrillic_ghe\n"
"    0x0434: 0x06c4, // XK_Cyrillic_de\n"
"    0x0435: 0x06c5, // XK_Cyrillic_ie\n"
"    0x0436: 0x06d6, // XK_Cyrillic_zhe\n"
"    0x0437: 0x06da, // XK_Cyrillic_ze\n"
"    0x0438: 0x06c9, // XK_Cyrillic_i\n"
"    0x0439: 0x06ca, // XK_Cyrillic_shorti\n"
"    0x043a: 0x06cb, // XK_Cyrillic_ka\n"
"    0x043b: 0x06cc, // XK_Cyrillic_el\n"
"    0x043c: 0x06cd, // XK_Cyrillic_em\n"
"    0x043d: 0x06ce, // XK_Cyrillic_en\n"
"    0x043e: 0x06cf, // XK_Cyrillic_o\n"
"    0x043f: 0x06d0, // XK_Cyrillic_pe\n"
"    0x0440: 0x06d2, // XK_Cyrillic_er\n"
"    0x0441: 0x06d3, // XK_Cyrillic_es\n"
"    0x0442: 0x06d4, // XK_Cyrillic_te\n"
"    0x0443: 0x06d5, // XK_Cyrillic_u\n"
"    0x0444: 0x06c6, // XK_Cyrillic_ef\n"
"    0x0445: 0x06c8, // XK_Cyrillic_ha\n"
"    0x0446: 0x06c3, // XK_Cyrillic_tse\n"
"    0x0447: 0x06de, // XK_Cyrillic_che\n"
"    0x0448: 0x06db, // XK_Cyrillic_sha\n"
"    0x0449: 0x06dd, // XK_Cyrillic_shcha\n"
"    0x044a: 0x06df, // XK_Cyrillic_hardsign\n"
"    0x044b: 0x06d9, // XK_Cyrillic_yeru\n"
"    0x044c: 0x06d8, // XK_Cyrillic_softsign\n"
"    0x044d: 0x06dc, // XK_Cyrillic_e\n"
"    0x044e: 0x06c0, // XK_Cyrillic_yu\n"
"    0x044f: 0x06d1, // XK_Cyrillic_ya\n"
"    0x0451: 0x06a3, // XK_Cyrillic_io\n"
"    0x0452: 0x06a1, // XK_Serbian_dje\n"
"    0x0453: 0x06a2, // XK_Macedonia_gje\n"
"    0x0454: 0x06a4, // XK_Ukrainian_ie\n"
"    0x0455: 0x06a5, // XK_Macedonia_dse\n"
"    0x0456: 0x06a6, // XK_Ukrainian_i\n"
"    0x0457: 0x06a7, // XK_Ukrainian_yi\n"
"    0x0458: 0x06a8, // XK_Cyrillic_je\n"
"    0x0459: 0x06a9, // XK_Cyrillic_lje\n"
"    0x045a: 0x06aa, // XK_Cyrillic_nje\n"
"    0x045b: 0x06ab, // XK_Serbian_tshe\n"
"    0x045c: 0x06ac, // XK_Macedonia_kje\n"
"    0x045e: 0x06ae, // XK_Byelorussian_shortu\n"
"    0x045f: 0x06af, // XK_Cyrillic_dzhe\n"
"    0x0490: 0x06bd, // XK_Ukrainian_GHE_WITH_UPTURN\n"
"    0x0491: 0x06ad, // XK_Ukrainian_ghe_with_upturn\n"
"    0x05d0: 0x0ce0, // XK_hebrew_aleph\n"
"    0x05d1: 0x0ce1, // XK_hebrew_bet\n"
"    0x05d2: 0x0ce2, // XK_hebrew_gimel\n"
"    0x05d3: 0x0ce3, // XK_hebrew_dalet\n"
"    0x05d4: 0x0ce4, // XK_hebrew_he\n"
"    0x05d5: 0x0ce5, // XK_hebrew_waw\n"
"    0x05d6: 0x0ce6, // XK_hebrew_zain\n"
"    0x05d7: 0x0ce7, // XK_hebrew_chet\n"
"    0x05d8: 0x0ce8, // XK_hebrew_tet\n"
"    0x05d9: 0x0ce9, // XK_hebrew_yod\n"
"    0x05da: 0x0cea, // XK_hebrew_finalkaph\n"
"    0x05db: 0x0ceb, // XK_hebrew_kaph\n"
"    0x05dc: 0x0cec, // XK_hebrew_lamed\n"
"    0x05dd: 0x0ced, // XK_hebrew_finalmem\n"
"    0x05de: 0x0cee, // XK_hebrew_mem\n"
"    0x05df: 0x0cef, // XK_hebrew_finalnun\n"
"    0x05e0: 0x0cf0, // XK_hebrew_nun\n"
"    0x05e1: 0x0cf1, // XK_hebrew_samech\n"
"    0x05e2: 0x0cf2, // XK_hebrew_ayin\n"
"    0x05e3: 0x0cf3, // XK_hebrew_finalpe\n"
"    0x05e4: 0x0cf4, // XK_hebrew_pe\n"
"    0x05e5: 0x0cf5, // XK_hebrew_finalzade\n"
"    0x05e6: 0x0cf6, // XK_hebrew_zade\n"
"    0x05e7: 0x0cf7, // XK_hebrew_qoph\n"
"    0x05e8: 0x0cf8, // XK_hebrew_resh\n"
"    0x05e9: 0x0cf9, // XK_hebrew_shin\n"
"    0x05ea: 0x0cfa, // XK_hebrew_taw\n"
"    0x060c: 0x05ac, // XK_Arabic_comma\n"
"    0x061b: 0x05bb, // XK_Arabic_semicolon\n"
"    0x061f: 0x05bf, // XK_Arabic_question_mark\n"
"    0x0621: 0x05c1, // XK_Arabic_hamza\n"
"    0x0622: 0x05c2, // XK_Arabic_maddaonalef\n"
"    0x0623: 0x05c3, // XK_Arabic_hamzaonalef\n"
"    0x0624: 0x05c4, // XK_Arabic_hamzaonwaw\n"
"    0x0625: 0x05c5, // XK_Arabic_hamzaunderalef\n"
"    0x0626: 0x05c6, // XK_Arabic_hamzaonyeh\n"
"    0x0627: 0x05c7, // XK_Arabic_alef\n"
"    0x0628: 0x05c8, // XK_Arabic_beh\n"
"    0x0629: 0x05c9, // XK_Arabic_tehmarbuta\n"
"    0x062a: 0x05ca, // XK_Arabic_teh\n"
"    0x062b: 0x05cb, // XK_Arabic_theh\n"
"    0x062c: 0x05cc, // XK_Arabic_jeem\n"
"    0x062d: 0x05cd, // XK_Arabic_hah\n"
"    0x062e: 0x05ce, // XK_Arabic_khah\n"
"    0x062f: 0x05cf, // XK_Arabic_dal\n"
"    0x0630: 0x05d0, // XK_Arabic_thal\n"
"    0x0631: 0x05d1, // XK_Arabic_ra\n"
"    0x0632: 0x05d2, // XK_Arabic_zain\n"
"    0x0633: 0x05d3, // XK_Arabic_seen\n"
"    0x0634: 0x05d4, // XK_Arabic_sheen\n"
"    0x0635: 0x05d5, // XK_Arabic_sad\n"
"    0x0636: 0x05d6, // XK_Arabic_dad\n"
"    0x0637: 0x05d7, // XK_Arabic_tah\n"
"    0x0638: 0x05d8, // XK_Arabic_zah\n"
"    0x0639: 0x05d9, // XK_Arabic_ain\n"
"    0x063a: 0x05da, // XK_Arabic_ghain\n"
"    0x0640: 0x05e0, // XK_Arabic_tatweel\n"
"    0x0641: 0x05e1, // XK_Arabic_feh\n"
"    0x0642: 0x05e2, // XK_Arabic_qaf\n"
"    0x0643: 0x05e3, // XK_Arabic_kaf\n"
"    0x0644: 0x05e4, // XK_Arabic_lam\n"
"    0x0645: 0x05e5, // XK_Arabic_meem\n"
"    0x0646: 0x05e6, // XK_Arabic_noon\n"
"    0x0647: 0x05e7, // XK_Arabic_ha\n"
"    0x0648: 0x05e8, // XK_Arabic_waw\n"
"    0x0649: 0x05e9, // XK_Arabic_alefmaksura\n"
"    0x064a: 0x05ea, // XK_Arabic_yeh\n"
"    0x064b: 0x05eb, // XK_Arabic_fathatan\n"
"    0x064c: 0x05ec, // XK_Arabic_dammatan\n"
"    0x064d: 0x05ed, // XK_Arabic_kasratan\n"
"    0x064e: 0x05ee, // XK_Arabic_fatha\n"
"    0x064f: 0x05ef, // XK_Arabic_damma\n"
"    0x0650: 0x05f0, // XK_Arabic_kasra\n"
"    0x0651: 0x05f1, // XK_Arabic_shadda\n"
"    0x0652: 0x05f2, // XK_Arabic_sukun\n"
"    0x0e01: 0x0da1, // XK_Thai_kokai\n"
"    0x0e02: 0x0da2, // XK_Thai_khokhai\n"
"    0x0e03: 0x0da3, // XK_Thai_khokhuat\n"
"    0x0e04: 0x0da4, // XK_Thai_khokhwai\n"
"    0x0e05: 0x0da5, // XK_Thai_khokhon\n"
"    0x0e06: 0x0da6, // XK_Thai_khorakhang\n"
"    0x0e07: 0x0da7, // XK_Thai_ngongu\n"
"    0x0e08: 0x0da8, // XK_Thai_chochan\n"
"    0x0e09: 0x0da9, // XK_Thai_choching\n"
"    0x0e0a: 0x0daa, // XK_Thai_chochang\n"
"    0x0e0b: 0x0dab, // XK_Thai_soso\n"
"    0x0e0c: 0x0dac, // XK_Thai_chochoe\n"
"    0x0e0d: 0x0dad, // XK_Thai_yoying\n"
"    0x0e0e: 0x0dae, // XK_Thai_dochada\n"
"    0x0e0f: 0x0daf, // XK_Thai_topatak\n"
"    0x0e10: 0x0db0, // XK_Thai_thothan\n"
"    0x0e11: 0x0db1, // XK_Thai_thonangmontho\n"
"    0x0e12: 0x0db2, // XK_Thai_thophuthao\n"
"    0x0e13: 0x0db3, // XK_Thai_nonen\n"
"    0x0e14: 0x0db4, // XK_Thai_dodek\n"
"    0x0e15: 0x0db5, // XK_Thai_totao\n"
"    0x0e16: 0x0db6, // XK_Thai_thothung\n"
"    0x0e17: 0x0db7, // XK_Thai_thothahan\n"
"    0x0e18: 0x0db8, // XK_Thai_thothong\n"
"    0x0e19: 0x0db9, // XK_Thai_nonu\n"
"    0x0e1a: 0x0dba, // XK_Thai_bobaimai\n"
"    0x0e1b: 0x0dbb, // XK_Thai_popla\n"
"    0x0e1c: 0x0dbc, // XK_Thai_phophung\n"
"    0x0e1d: 0x0dbd, // XK_Thai_fofa\n"
"    0x0e1e: 0x0dbe, // XK_Thai_phophan\n"
"    0x0e1f: 0x0dbf, // XK_Thai_fofan\n"
"    0x0e20: 0x0dc0, // XK_Thai_phosamphao\n"
"    0x0e21: 0x0dc1, // XK_Thai_moma\n"
"    0x0e22: 0x0dc2, // XK_Thai_yoyak\n"
"    0x0e23: 0x0dc3, // XK_Thai_rorua\n"
"    0x0e24: 0x0dc4, // XK_Thai_ru\n"
"    0x0e25: 0x0dc5, // XK_Thai_loling\n"
"    0x0e26: 0x0dc6, // XK_Thai_lu\n"
"    0x0e27: 0x0dc7, // XK_Thai_wowaen\n"
"    0x0e28: 0x0dc8, // XK_Thai_sosala\n"
"    0x0e29: 0x0dc9, // XK_Thai_sorusi\n"
"    0x0e2a: 0x0dca, // XK_Thai_sosua\n"
"    0x0e2b: 0x0dcb, // XK_Thai_hohip\n"
"    0x0e2c: 0x0dcc, // XK_Thai_lochula\n"
"    0x0e2d: 0x0dcd, // XK_Thai_oang\n"
"    0x0e2e: 0x0dce, // XK_Thai_honokhuk\n"
"    0x0e2f: 0x0dcf, // XK_Thai_paiyannoi\n"
"    0x0e30: 0x0dd0, // XK_Thai_saraa\n"
"    0x0e31: 0x0dd1, // XK_Thai_maihanakat\n"
"    0x0e32: 0x0dd2, // XK_Thai_saraaa\n"
"    0x0e33: 0x0dd3, // XK_Thai_saraam\n"
"    0x0e34: 0x0dd4, // XK_Thai_sarai\n"
"    0x0e35: 0x0dd5, // XK_Thai_saraii\n"
"    0x0e36: 0x0dd6, // XK_Thai_saraue\n"
"    0x0e37: 0x0dd7, // XK_Thai_sarauee\n"
"    0x0e38: 0x0dd8, // XK_Thai_sarau\n"
"    0x0e39: 0x0dd9, // XK_Thai_sarauu\n"
"    0x0e3a: 0x0dda, // XK_Thai_phinthu\n"
"    0x0e3f: 0x0ddf, // XK_Thai_baht\n"
"    0x0e40: 0x0de0, // XK_Thai_sarae\n"
"    0x0e41: 0x0de1, // XK_Thai_saraae\n"
"    0x0e42: 0x0de2, // XK_Thai_sarao\n"
"    0x0e43: 0x0de3, // XK_Thai_saraaimaimuan\n"
"    0x0e44: 0x0de4, // XK_Thai_saraaimaimalai\n"
"    0x0e45: 0x0de5, // XK_Thai_lakkhangyao\n"
"    0x0e46: 0x0de6, // XK_Thai_maiyamok\n"
"    0x0e47: 0x0de7, // XK_Thai_maitaikhu\n"
"    0x0e48: 0x0de8, // XK_Thai_maiek\n"
"    0x0e49: 0x0de9, // XK_Thai_maitho\n"
"    0x0e4a: 0x0dea, // XK_Thai_maitri\n"
"    0x0e4b: 0x0deb, // XK_Thai_maichattawa\n"
"    0x0e4c: 0x0dec, // XK_Thai_thanthakhat\n"
"    0x0e4d: 0x0ded, // XK_Thai_nikhahit\n"
"    0x0e50: 0x0df0, // XK_Thai_leksun\n"
"    0x0e51: 0x0df1, // XK_Thai_leknung\n"
"    0x0e52: 0x0df2, // XK_Thai_leksong\n"
"    0x0e53: 0x0df3, // XK_Thai_leksam\n"
"    0x0e54: 0x0df4, // XK_Thai_leksi\n"
"    0x0e55: 0x0df5, // XK_Thai_lekha\n"
"    0x0e56: 0x0df6, // XK_Thai_lekhok\n"
"    0x0e57: 0x0df7, // XK_Thai_lekchet\n"
"    0x0e58: 0x0df8, // XK_Thai_lekpaet\n"
"    0x0e59: 0x0df9, // XK_Thai_lekkao\n"
"    0x2002: 0x0aa2, // XK_enspace\n"
"    0x2003: 0x0aa1, // XK_emspace\n"
"    0x2004: 0x0aa3, // XK_em3space\n"
"    0x2005: 0x0aa4, // XK_em4space\n"
"    0x2007: 0x0aa5, // XK_digitspace\n"
"    0x2008: 0x0aa6, // XK_punctspace\n"
"    0x2009: 0x0aa7, // XK_thinspace\n"
"    0x200a: 0x0aa8, // XK_hairspace\n"
"    0x2012: 0x0abb, // XK_figdash\n"
"    0x2013: 0x0aaa, // XK_endash\n"
"    0x2014: 0x0aa9, // XK_emdash\n"
"    0x2015: 0x07af, // XK_Greek_horizbar\n"
"    0x2017: 0x0cdf, // XK_hebrew_doublelowline\n"
"    0x2018: 0x0ad0, // XK_leftsinglequotemark\n"
"    0x2019: 0x0ad1, // XK_rightsinglequotemark\n"
"    0x201a: 0x0afd, // XK_singlelowquotemark\n"
"    0x201c: 0x0ad2, // XK_leftdoublequotemark\n"
"    0x201d: 0x0ad3, // XK_rightdoublequotemark\n"
"    0x201e: 0x0afe, // XK_doublelowquotemark\n"
"    0x2020: 0x0af1, // XK_dagger\n"
"    0x2021: 0x0af2, // XK_doubledagger\n"
"    0x2022: 0x0ae6, // XK_enfilledcircbullet\n"
"    0x2025: 0x0aaf, // XK_doubbaselinedot\n"
"    0x2026: 0x0aae, // XK_ellipsis\n"
"    0x2030: 0x0ad5, // XK_permille\n"
"    0x2032: 0x0ad6, // XK_minutes\n"
"    0x2033: 0x0ad7, // XK_seconds\n"
"    0x2038: 0x0afc, // XK_caret\n"
"    0x203e: 0x047e, // XK_overline\n"
"    0x20a9: 0x0eff, // XK_Korean_Won\n"
"    0x20ac: 0x20ac, // XK_EuroSign\n"
"    0x2105: 0x0ab8, // XK_careof\n"
"    0x2116: 0x06b0, // XK_numerosign\n"
"    0x2117: 0x0afb, // XK_phonographcopyright\n"
"    0x211e: 0x0ad4, // XK_prescription\n"
"    0x2122: 0x0ac9, // XK_trademark\n"
"    0x2153: 0x0ab0, // XK_onethird\n"
"    0x2154: 0x0ab1, // XK_twothirds\n"
"    0x2155: 0x0ab2, // XK_onefifth\n"
"    0x2156: 0x0ab3, // XK_twofifths\n"
"    0x2157: 0x0ab4, // XK_threefifths\n"
"    0x2158: 0x0ab5, // XK_fourfifths\n"
"    0x2159: 0x0ab6, // XK_onesixth\n"
"    0x215a: 0x0ab7, // XK_fivesixths\n"
"    0x215b: 0x0ac3, // XK_oneeighth\n"
"    0x215c: 0x0ac4, // XK_threeeighths\n"
"    0x215d: 0x0ac5, // XK_fiveeighths\n"
"    0x215e: 0x0ac6, // XK_seveneighths\n"
"    0x2190: 0x08fb, // XK_leftarrow\n"
"    0x2191: 0x08fc, // XK_uparrow\n"
"    0x2192: 0x08fd, // XK_rightarrow\n"
"    0x2193: 0x08fe, // XK_downarrow\n"
"    0x21d2: 0x08ce, // XK_implies\n"
"    0x21d4: 0x08cd, // XK_ifonlyif\n"
"    0x2202: 0x08ef, // XK_partialderivative\n"
"    0x2207: 0x08c5, // XK_nabla\n"
"    0x2218: 0x0bca, // XK_jot\n"
"    0x221a: 0x08d6, // XK_radical\n"
"    0x221d: 0x08c1, // XK_variation\n"
"    0x221e: 0x08c2, // XK_infinity\n"
"    0x2227: 0x08de, // XK_logicaland\n"
"    0x2228: 0x08df, // XK_logicalor\n"
"    0x2229: 0x08dc, // XK_intersection\n"
"    0x222a: 0x08dd, // XK_union\n"
"    0x222b: 0x08bf, // XK_integral\n"
"    0x2234: 0x08c0, // XK_therefore\n"
"    0x223c: 0x08c8, // XK_approximate\n"
"    0x2243: 0x08c9, // XK_similarequal\n"
"    0x2245: 0x1002248, // XK_approxeq\n"
"    0x2260: 0x08bd, // XK_notequal\n"
"    0x2261: 0x08cf, // XK_identical\n"
"    0x2264: 0x08bc, // XK_lessthanequal\n"
"    0x2265: 0x08be, // XK_greaterthanequal\n"
"    0x2282: 0x08da, // XK_includedin\n"
"    0x2283: 0x08db, // XK_includes\n"
"    0x22a2: 0x0bfc, // XK_righttack\n"
"    0x22a3: 0x0bdc, // XK_lefttack\n"
"    0x22a4: 0x0bc2, // XK_downtack\n"
"    0x22a5: 0x0bce, // XK_uptack\n"
"    0x2308: 0x0bd3, // XK_upstile\n"
"    0x230a: 0x0bc4, // XK_downstile\n"
"    0x2315: 0x0afa, // XK_telephonerecorder\n"
"    0x2320: 0x08a4, // XK_topintegral\n"
"    0x2321: 0x08a5, // XK_botintegral\n"
"    0x2395: 0x0bcc, // XK_quad\n"
"    0x239b: 0x08ab, // XK_topleftparens\n"
"    0x239d: 0x08ac, // XK_botleftparens\n"
"    0x239e: 0x08ad, // XK_toprightparens\n"
"    0x23a0: 0x08ae, // XK_botrightparens\n"
"    0x23a1: 0x08a7, // XK_topleftsqbracket\n"
"    0x23a3: 0x08a8, // XK_botleftsqbracket\n"
"    0x23a4: 0x08a9, // XK_toprightsqbracket\n"
"    0x23a6: 0x08aa, // XK_botrightsqbracket\n"
"    0x23a8: 0x08af, // XK_leftmiddlecurlybrace\n"
"    0x23ac: 0x08b0, // XK_rightmiddlecurlybrace\n"
"    0x23b7: 0x08a1, // XK_leftradical\n"
"    0x23ba: 0x09ef, // XK_horizlinescan1\n"
"    0x23bb: 0x09f0, // XK_horizlinescan3\n"
"    0x23bc: 0x09f2, // XK_horizlinescan7\n"
"    0x23bd: 0x09f3, // XK_horizlinescan9\n"
"    0x2409: 0x09e2, // XK_ht\n"
"    0x240a: 0x09e5, // XK_lf\n"
"    0x240b: 0x09e9, // XK_vt\n"
"    0x240c: 0x09e3, // XK_ff\n"
"    0x240d: 0x09e4, // XK_cr\n"
"    0x2423: 0x0aac, // XK_signifblank\n"
"    0x2424: 0x09e8, // XK_nl\n"
"    0x2500: 0x08a3, // XK_horizconnector\n"
"    0x2502: 0x08a6, // XK_vertconnector\n"
"    0x250c: 0x08a2, // XK_topleftradical\n"
"    0x2510: 0x09eb, // XK_uprightcorner\n"
"    0x2514: 0x09ed, // XK_lowleftcorner\n"
"    0x2518: 0x09ea, // XK_lowrightcorner\n"
"    0x251c: 0x09f4, // XK_leftt\n"
"    0x2524: 0x09f5, // XK_rightt\n"
"    0x252c: 0x09f7, // XK_topt\n"
"    0x2534: 0x09f6, // XK_bott\n"
"    0x253c: 0x09ee, // XK_crossinglines\n"
"    0x2592: 0x09e1, // XK_checkerboard\n"
"    0x25aa: 0x0ae7, // XK_enfilledsqbullet\n"
"    0x25ab: 0x0ae1, // XK_enopensquarebullet\n"
"    0x25ac: 0x0adb, // XK_filledrectbullet\n"
"    0x25ad: 0x0ae2, // XK_openrectbullet\n"
"    0x25ae: 0x0adf, // XK_emfilledrect\n"
"    0x25af: 0x0acf, // XK_emopenrectangle\n"
"    0x25b2: 0x0ae8, // XK_filledtribulletup\n"
"    0x25b3: 0x0ae3, // XK_opentribulletup\n"
"    0x25b6: 0x0add, // XK_filledrighttribullet\n"
"    0x25b7: 0x0acd, // XK_rightopentriangle\n"
"    0x25bc: 0x0ae9, // XK_filledtribulletdown\n"
"    0x25bd: 0x0ae4, // XK_opentribulletdown\n"
"    0x25c0: 0x0adc, // XK_filledlefttribullet\n"
"    0x25c1: 0x0acc, // XK_leftopentriangle\n"
"    0x25c6: 0x09e0, // XK_soliddiamond\n"
"    0x25cb: 0x0ace, // XK_emopencircle\n"
"    0x25cf: 0x0ade, // XK_emfilledcircle\n"
"    0x25e6: 0x0ae0, // XK_enopencircbullet\n"
"    0x2606: 0x0ae5, // XK_openstar\n"
"    0x260e: 0x0af9, // XK_telephone\n"
"    0x2613: 0x0aca, // XK_signaturemark\n"
"    0x261c: 0x0aea, // XK_leftpointer\n"
"    0x261e: 0x0aeb, // XK_rightpointer\n"
"    0x2640: 0x0af8, // XK_femalesymbol\n"
"    0x2642: 0x0af7, // XK_malesymbol\n"
"    0x2663: 0x0aec, // XK_club\n"
"    0x2665: 0x0aee, // XK_heart\n"
"    0x2666: 0x0aed, // XK_diamond\n"
"    0x266d: 0x0af6, // XK_musicalflat\n"
"    0x266f: 0x0af5, // XK_musicalsharp\n"
"    0x2713: 0x0af3, // XK_checkmark\n"
"    0x2717: 0x0af4, // XK_ballotcross\n"
"    0x271d: 0x0ad9, // XK_latincross\n"
"    0x2720: 0x0af0, // XK_maltesecross\n"
"    0x27e8: 0x0abc, // XK_leftanglebracket\n"
"    0x27e9: 0x0abe, // XK_rightanglebracket\n"
"    0x3001: 0x04a4, // XK_kana_comma\n"
"    0x3002: 0x04a1, // XK_kana_fullstop\n"
"    0x300c: 0x04a2, // XK_kana_openingbracket\n"
"    0x300d: 0x04a3, // XK_kana_closingbracket\n"
"    0x309b: 0x04de, // XK_voicedsound\n"
"    0x309c: 0x04df, // XK_semivoicedsound\n"
"    0x30a1: 0x04a7, // XK_kana_a\n"
"    0x30a2: 0x04b1, // XK_kana_A\n"
"    0x30a3: 0x04a8, // XK_kana_i\n"
"    0x30a4: 0x04b2, // XK_kana_I\n"
"    0x30a5: 0x04a9, // XK_kana_u\n"
"    0x30a6: 0x04b3, // XK_kana_U\n"
"    0x30a7: 0x04aa, // XK_kana_e\n"
"    0x30a8: 0x04b4, // XK_kana_E\n"
"    0x30a9: 0x04ab, // XK_kana_o\n"
"    0x30aa: 0x04b5, // XK_kana_O\n"
"    0x30ab: 0x04b6, // XK_kana_KA\n"
"    0x30ad: 0x04b7, // XK_kana_KI\n"
"    0x30af: 0x04b8, // XK_kana_KU\n"
"    0x30b1: 0x04b9, // XK_kana_KE\n"
"    0x30b3: 0x04ba, // XK_kana_KO\n"
"    0x30b5: 0x04bb, // XK_kana_SA\n"
"    0x30b7: 0x04bc, // XK_kana_SHI\n"
"    0x30b9: 0x04bd, // XK_kana_SU\n"
"    0x30bb: 0x04be, // XK_kana_SE\n"
"    0x30bd: 0x04bf, // XK_kana_SO\n"
"    0x30bf: 0x04c0, // XK_kana_TA\n"
"    0x30c1: 0x04c1, // XK_kana_CHI\n"
"    0x30c3: 0x04af, // XK_kana_tsu\n"
"    0x30c4: 0x04c2, // XK_kana_TSU\n"
"    0x30c6: 0x04c3, // XK_kana_TE\n"
"    0x30c8: 0x04c4, // XK_kana_TO\n"
"    0x30ca: 0x04c5, // XK_kana_NA\n"
"    0x30cb: 0x04c6, // XK_kana_NI\n"
"    0x30cc: 0x04c7, // XK_kana_NU\n"
"    0x30cd: 0x04c8, // XK_kana_NE\n"
"    0x30ce: 0x04c9, // XK_kana_NO\n"
"    0x30cf: 0x04ca, // XK_kana_HA\n"
"    0x30d2: 0x04cb, // XK_kana_HI\n"
"    0x30d5: 0x04cc, // XK_kana_FU\n"
"    0x30d8: 0x04cd, // XK_kana_HE\n"
"    0x30db: 0x04ce, // XK_kana_HO\n"
"    0x30de: 0x04cf, // XK_kana_MA\n"
"    0x30df: 0x04d0, // XK_kana_MI\n"
"    0x30e0: 0x04d1, // XK_kana_MU\n"
"    0x30e1: 0x04d2, // XK_kana_ME\n"
"    0x30e2: 0x04d3, // XK_kana_MO\n"
"    0x30e3: 0x04ac, // XK_kana_ya\n"
"    0x30e4: 0x04d4, // XK_kana_YA\n"
"    0x30e5: 0x04ad, // XK_kana_yu\n"
"    0x30e6: 0x04d5, // XK_kana_YU\n"
"    0x30e7: 0x04ae, // XK_kana_yo\n"
"    0x30e8: 0x04d6, // XK_kana_YO\n"
"    0x30e9: 0x04d7, // XK_kana_RA\n"
"    0x30ea: 0x04d8, // XK_kana_RI\n"
"    0x30eb: 0x04d9, // XK_kana_RU\n"
"    0x30ec: 0x04da, // XK_kana_RE\n"
"    0x30ed: 0x04db, // XK_kana_RO\n"
"    0x30ef: 0x04dc, // XK_kana_WA\n"
"    0x30f2: 0x04a6, // XK_kana_WO\n"
"    0x30f3: 0x04dd, // XK_kana_N\n"
"    0x30fb: 0x04a5, // XK_kana_conjunctive\n"
"    0x30fc: 0x04b0, // XK_prolongedsound\n"
"};\n"
"\n"
"window.lookup = function(u) {\n"
"    // Latin-1 is one-to-one mapping\n"
"    if ((u >= 0x20) && (u <= 0xff)) {\n"
"        return u;\n"
"    }\n"
"\n"
"    // Lookup table (fairly random)\n"
"    const keysym = codepoints[u];\n"
"    if (keysym !== undefined) {\n"
"        return keysym;\n"
"    }\n"
"\n"
"    // General mapping as final fallback\n"
"    return 0x01000000 | u;\n"
"};\n"
"window.codepoints = codepoints;\n"
"\n"
"// Create keysyms object for compatibility with util.js\n"
"window.keysyms = {\n"
"    lookup: window.lookup\n"
"};\n"
;
    if (strcmp(path, "/novnc/input/keysymdef.js") == 0) return novnc_input_keysymdef_js;
const char *novnc_input_util_js =
"\n"
"// Get 'KeyboardEvent.code', handling legacy browsers\n"
"function getKeycode(evt) {\n"
"    // Are we getting proper key identifiers?\n"
"    // (unfortunately Firefox and Chrome are crappy here and gives\n"
"    // us an empty string on some platforms, rather than leaving it\n"
"    // undefined)\n"
"    if (evt.code) {\n"
"        // Mozilla isn't fully in sync with the spec yet\n"
"        switch (evt.code) {\n"
"            case 'OSLeft': return 'MetaLeft';\n"
"            case 'OSRight': return 'MetaRight';\n"
"        }\n"
"\n"
"        return evt.code;\n"
"    }\n"
"\n"
"    // The de-facto standard is to use Windows Virtual-Key codes\n"
"    // in the 'keyCode' field for non-printable characters\n"
"    if (evt.keyCode in vkeys) {\n"
"        let code = vkeys[evt.keyCode];\n"
"\n"
"        // macOS has messed up this code for some reason\n"
"        if (browser.isMac() && (code === 'ContextMenu')) {\n"
"            code = 'MetaRight';\n"
"        }\n"
"\n"
"        // The keyCode doesn't distinguish between left and right\n"
"        // for the standard modifiers\n"
"        if (evt.location === 2) {\n"
"            switch (code) {\n"
"                case 'ShiftLeft': return 'ShiftRight';\n"
"                case 'ControlLeft': return 'ControlRight';\n"
"                case 'AltLeft': return 'AltRight';\n"
"            }\n"
"        }\n"
"\n"
"        // Nor a bunch of the numpad keys\n"
"        if (evt.location === 3) {\n"
"            switch (code) {\n"
"                case 'Delete': return 'NumpadDecimal';\n"
"                case 'Insert': return 'Numpad0';\n"
"                case 'End': return 'Numpad1';\n"
"                case 'ArrowDown': return 'Numpad2';\n"
"                case 'PageDown': return 'Numpad3';\n"
"                case 'ArrowLeft': return 'Numpad4';\n"
"                case 'ArrowRight': return 'Numpad6';\n"
"                case 'Home': return 'Numpad7';\n"
"                case 'ArrowUp': return 'Numpad8';\n"
"                case 'PageUp': return 'Numpad9';\n"
"                case 'Enter': return 'NumpadEnter';\n"
"            }\n"
"        }\n"
"\n"
"        return code;\n"
"    }\n"
"\n"
"    return 'Unidentified';\n"
"}\n"
"\n"
"// Get 'KeyboardEvent.key', handling legacy browsers\n"
"function getKey(evt) {\n"
"    // Are we getting a proper key value?\n"
"    if (evt.key !== undefined) {\n"
"        // Mozilla isn't fully in sync with the spec yet\n"
"        switch (evt.key) {\n"
"            case 'OS': return 'Meta';\n"
"            case 'LaunchMyComputer': return 'LaunchApplication1';\n"
"            case 'LaunchCalculator': return 'LaunchApplication2';\n"
"        }\n"
"\n"
"        // iOS leaks some OS names\n"
"        switch (evt.key) {\n"
"            case 'UIKeyInputUpArrow': return 'ArrowUp';\n"
"            case 'UIKeyInputDownArrow': return 'ArrowDown';\n"
"            case 'UIKeyInputLeftArrow': return 'ArrowLeft';\n"
"            case 'UIKeyInputRightArrow': return 'ArrowRight';\n"
"            case 'UIKeyInputEscape': return 'Escape';\n"
"        }\n"
"\n"
"        // Broken behaviour in Chrome\n"
"        if ((evt.key === '\\x00') && (evt.code === 'NumpadDecimal')) {\n"
"            return 'Delete';\n"
"        }\n"
"\n"
"        return evt.key;\n"
"    }\n"
"\n"
"    // Try to deduce it based on the physical key\n"
"    const code = getKeycode(evt);\n"
"    if (code in fixedkeys) {\n"
"        return fixedkeys[code];\n"
"    }\n"
"\n"
"    // If that failed, then see if we have a printable character\n"
"    if (evt.charCode) {\n"
"        return String.fromCharCode(evt.charCode);\n"
"    }\n"
"\n"
"    // At this point we have nothing left to go on\n"
"    return 'Unidentified';\n"
"}\n"
"\n"
"// Get the most reliable keysym value we can get from a key event\n"
"function getKeysym(evt) {\n"
"    const key = getKey(evt);\n"
"\n"
"    if (key === 'Unidentified') {\n"
"        return null;\n"
"    }\n"
"\n"
"    // First look up special keys\n"
"    if (key in DOMKeyTable) {\n"
"        let location = evt.location;\n"
"\n"
"        // Safari screws up location for the right cmd key\n"
"        if ((key === 'Meta') && (location === 0)) {\n"
"            location = 2;\n"
"        }\n"
"\n"
"        // And for Clear\n"
"        if ((key === 'Clear') && (location === 3)) {\n"
"            let code = getKeycode(evt);\n"
"            if (code === 'NumLock') {\n"
"                location = 0;\n"
"            }\n"
"        }\n"
"\n"
"        if ((location === undefined) || (location > 3)) {\n"
"            location = 0;\n"
"        }\n"
"\n"
"        // The original Meta key now gets confused with the Windows key\n"
"        // https://bugs.chromium.org/p/chromium/issues/detail?id=1020141\n"
"        // https://bugzilla.mozilla.org/show_bug.cgi?id=1232918\n"
"        if (key === 'Meta') {\n"
"            let code = getKeycode(evt);\n"
"            if (code === 'AltLeft') {\n"
"                return KeyTable.XK_Meta_L;\n"
"            } else if (code === 'AltRight') {\n"
"                return KeyTable.XK_Meta_R;\n"
"            }\n"
"        }\n"
"\n"
"        // macOS has Clear instead of NumLock, but the remote system is\n"
"        // probably not macOS, so lying here is probably best...\n"
"        if (key === 'Clear') {\n"
"            let code = getKeycode(evt);\n"
"            if (code === 'NumLock') {\n"
"                return KeyTable.XK_Num_Lock;\n"
"            }\n"
"        }\n"
"\n"
"        // Windows sends alternating symbols for some keys when using a\n"
"        // Japanese layout. We have no way of synchronising with the IM\n"
"        // running on the remote system, so we send some combined keysym\n"
"        // instead and hope for the best.\n"
"        if (browser.isWindows()) {\n"
"            switch (key) {\n"
"                case 'Zenkaku':\n"
"                case 'Hankaku':\n"
"                    return KeyTable.XK_Zenkaku_Hankaku;\n"
"                case 'Romaji':\n"
"                case 'KanaMode':\n"
"                    return KeyTable.XK_Romaji;\n"
"            }\n"
"        }\n"
"\n"
"        return DOMKeyTable[key][location];\n"
"    }\n"
"\n"
"    // Now we need to look at the Unicode symbol instead\n"
"\n"
"    // Special key? (FIXME: Should have been caught earlier)\n"
"    if (key.length !== 1) {\n"
"        return null;\n"
"    }\n"
"\n"
"    const codepoint = key.charCodeAt();\n"
"    if (codepoint) {\n"
"        return keysyms.lookup(codepoint);\n"
"    }\n"
"\n"
"    return null;\n"
"}\n"
"window.getKeycode = getKeycode;\n"
"window.getKeysym = getKeysym;\n"
"\n"
"// Create KeyboardUtil object for compatibility\n"
"window.KeyboardUtil = {\n"
"    getKeycode: getKeycode,\n"
"    getKeysym: getKeysym\n"
"};\n"
;
    if (strcmp(path, "/novnc/input/util.js") == 0) return novnc_input_util_js;
const char *novnc_input_vkeys_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2018 The noVNC Authors\n"
" * Licensed under MPL 2.0 or any later version (see LICENSE.txt)\n"
" */\n"
"\n"
"/*\n"
" * Mapping between Microsoft® Windows® Virtual-Key codes and\n"
" * HTML key codes.\n"
" */\n"
"\n"
"window.{\n"
"    0x08: 'Backspace',\n"
"    0x09: 'Tab',\n"
"    0x0a: 'NumpadClear',\n"
"    0x0d: 'Enter',\n"
"    0x10: 'ShiftLeft',\n"
"    0x11: 'ControlLeft',\n"
"    0x12: 'AltLeft',\n"
"    0x13: 'Pause',\n"
"    0x14: 'CapsLock',\n"
"    0x15: 'Lang1',\n"
"    0x19: 'Lang2',\n"
"    0x1b: 'Escape',\n"
"    0x1c: 'Convert',\n"
"    0x1d: 'NonConvert',\n"
"    0x20: 'Space',\n"
"    0x21: 'PageUp',\n"
"    0x22: 'PageDown',\n"
"    0x23: 'End',\n"
"    0x24: 'Home',\n"
"    0x25: 'ArrowLeft',\n"
"    0x26: 'ArrowUp',\n"
"    0x27: 'ArrowRight',\n"
"    0x28: 'ArrowDown',\n"
"    0x29: 'Select',\n"
"    0x2c: 'PrintScreen',\n"
"    0x2d: 'Insert',\n"
"    0x2e: 'Delete',\n"
"    0x2f: 'Help',\n"
"    0x30: 'Digit0',\n"
"    0x31: 'Digit1',\n"
"    0x32: 'Digit2',\n"
"    0x33: 'Digit3',\n"
"    0x34: 'Digit4',\n"
"    0x35: 'Digit5',\n"
"    0x36: 'Digit6',\n"
"    0x37: 'Digit7',\n"
"    0x38: 'Digit8',\n"
"    0x39: 'Digit9',\n"
"    0x5b: 'MetaLeft',\n"
"    0x5c: 'MetaRight',\n"
"    0x5d: 'ContextMenu',\n"
"    0x5f: 'Sleep',\n"
"    0x60: 'Numpad0',\n"
"    0x61: 'Numpad1',\n"
"    0x62: 'Numpad2',\n"
"    0x63: 'Numpad3',\n"
"    0x64: 'Numpad4',\n"
"    0x65: 'Numpad5',\n"
"    0x66: 'Numpad6',\n"
"    0x67: 'Numpad7',\n"
"    0x68: 'Numpad8',\n"
"    0x69: 'Numpad9',\n"
"    0x6a: 'NumpadMultiply',\n"
"    0x6b: 'NumpadAdd',\n"
"    0x6c: 'NumpadDecimal',\n"
"    0x6d: 'NumpadSubtract',\n"
"    0x6e: 'NumpadDecimal', // Duplicate, because buggy on Windows\n"
"    0x6f: 'NumpadDivide',\n"
"    0x70: 'F1',\n"
"    0x71: 'F2',\n"
"    0x72: 'F3',\n"
"    0x73: 'F4',\n"
"    0x74: 'F5',\n"
"    0x75: 'F6',\n"
"    0x76: 'F7',\n"
"    0x77: 'F8',\n"
"    0x78: 'F9',\n"
"    0x79: 'F10',\n"
"    0x7a: 'F11',\n"
"    0x7b: 'F12',\n"
"    0x7c: 'F13',\n"
"    0x7d: 'F14',\n"
"    0x7e: 'F15',\n"
"    0x7f: 'F16',\n"
"    0x80: 'F17',\n"
"    0x81: 'F18',\n"
"    0x82: 'F19',\n"
"    0x83: 'F20',\n"
"    0x84: 'F21',\n"
"    0x85: 'F22',\n"
"    0x86: 'F23',\n"
"    0x87: 'F24',\n"
"    0x90: 'NumLock',\n"
"    0x91: 'ScrollLock',\n"
"    0xa6: 'BrowserBack',\n"
"    0xa7: 'BrowserForward',\n"
"    0xa8: 'BrowserRefresh',\n"
"    0xa9: 'BrowserStop',\n"
"    0xaa: 'BrowserSearch',\n"
"    0xab: 'BrowserFavorites',\n"
"    0xac: 'BrowserHome',\n"
"    0xad: 'AudioVolumeMute',\n"
"    0xae: 'AudioVolumeDown',\n"
"    0xaf: 'AudioVolumeUp',\n"
"    0xb0: 'MediaTrackNext',\n"
"    0xb1: 'MediaTrackPrevious',\n"
"    0xb2: 'MediaStop',\n"
"    0xb3: 'MediaPlayPause',\n"
"    0xb4: 'LaunchMail',\n"
"    0xb5: 'MediaSelect',\n"
"    0xb6: 'LaunchApp1',\n"
"    0xb7: 'LaunchApp2',\n"
"    0xe1: 'AltRight', // Only when it is AltGraph\n"
"};\n"
;
    if (strcmp(path, "/novnc/input/vkeys.js") == 0) return novnc_input_vkeys_js;
const char *novnc_input_xtscancodes_js =
"/*\n"
" * This file is auto-generated from keymaps.csv\n"
" * Database checksum sha256(76d68c10e97d37fe2ea459e210125ae41796253fb217e900bf2983ade13a7920)\n"
" * To re-generate, run:\n"
" *   keymap-gen code-map --lang=js keymaps.csv html atset1\n"
"*/\n"
"window.XtScancode = {\n"
"  \"Again\": 0xe005, /* html:Again (Again) -> linux:129 (KEY_AGAIN) -> atset1:57349 */\n"
"  \"AltLeft\": 0x38, /* html:AltLeft (AltLeft) -> linux:56 (KEY_LEFTALT) -> atset1:56 */\n"
"  \"AltRight\": 0xe038, /* html:AltRight (AltRight) -> linux:100 (KEY_RIGHTALT) -> atset1:57400 */\n"
"  \"ArrowDown\": 0xe050, /* html:ArrowDown (ArrowDown) -> linux:108 (KEY_DOWN) -> atset1:57424 */\n"
"  \"ArrowLeft\": 0xe04b, /* html:ArrowLeft (ArrowLeft) -> linux:105 (KEY_LEFT) -> atset1:57419 */\n"
"  \"ArrowRight\": 0xe04d, /* html:ArrowRight (ArrowRight) -> linux:106 (KEY_RIGHT) -> atset1:57421 */\n"
"  \"ArrowUp\": 0xe048, /* html:ArrowUp (ArrowUp) -> linux:103 (KEY_UP) -> atset1:57416 */\n"
"  \"AudioVolumeDown\": 0xe02e, /* html:AudioVolumeDown (AudioVolumeDown) -> linux:114 (KEY_VOLUMEDOWN) -> atset1:57390 */\n"
"  \"AudioVolumeMute\": 0xe020, /* html:AudioVolumeMute (AudioVolumeMute) -> linux:113 (KEY_MUTE) -> atset1:57376 */\n"
"  \"AudioVolumeUp\": 0xe030, /* html:AudioVolumeUp (AudioVolumeUp) -> linux:115 (KEY_VOLUMEUP) -> atset1:57392 */\n"
"  \"Backquote\": 0x29, /* html:Backquote (Backquote) -> linux:41 (KEY_GRAVE) -> atset1:41 */\n"
"  \"Backslash\": 0x2b, /* html:Backslash (Backslash) -> linux:43 (KEY_BACKSLASH) -> atset1:43 */\n"
"  \"Backspace\": 0xe, /* html:Backspace (Backspace) -> linux:14 (KEY_BACKSPACE) -> atset1:14 */\n"
"  \"BracketLeft\": 0x1a, /* html:BracketLeft (BracketLeft) -> linux:26 (KEY_LEFTBRACE) -> atset1:26 */\n"
"  \"BracketRight\": 0x1b, /* html:BracketRight (BracketRight) -> linux:27 (KEY_RIGHTBRACE) -> atset1:27 */\n"
"  \"BrowserBack\": 0xe06a, /* html:BrowserBack (BrowserBack) -> linux:158 (KEY_BACK) -> atset1:57450 */\n"
"  \"BrowserFavorites\": 0xe066, /* html:BrowserFavorites (BrowserFavorites) -> linux:156 (KEY_BOOKMARKS) -> atset1:57446 */\n"
"  \"BrowserForward\": 0xe069, /* html:BrowserForward (BrowserForward) -> linux:159 (KEY_FORWARD) -> atset1:57449 */\n"
"  \"BrowserHome\": 0xe032, /* html:BrowserHome (BrowserHome) -> linux:172 (KEY_HOMEPAGE) -> atset1:57394 */\n"
"  \"BrowserRefresh\": 0xe067, /* html:BrowserRefresh (BrowserRefresh) -> linux:173 (KEY_REFRESH) -> atset1:57447 */\n"
"  \"BrowserSearch\": 0xe065, /* html:BrowserSearch (BrowserSearch) -> linux:217 (KEY_SEARCH) -> atset1:57445 */\n"
"  \"BrowserStop\": 0xe068, /* html:BrowserStop (BrowserStop) -> linux:128 (KEY_STOP) -> atset1:57448 */\n"
"  \"CapsLock\": 0x3a, /* html:CapsLock (CapsLock) -> linux:58 (KEY_CAPSLOCK) -> atset1:58 */\n"
"  \"Comma\": 0x33, /* html:Comma (Comma) -> linux:51 (KEY_COMMA) -> atset1:51 */\n"
"  \"ContextMenu\": 0xe05d, /* html:ContextMenu (ContextMenu) -> linux:127 (KEY_COMPOSE) -> atset1:57437 */\n"
"  \"ControlLeft\": 0x1d, /* html:ControlLeft (ControlLeft) -> linux:29 (KEY_LEFTCTRL) -> atset1:29 */\n"
"  \"ControlRight\": 0xe01d, /* html:ControlRight (ControlRight) -> linux:97 (KEY_RIGHTCTRL) -> atset1:57373 */\n"
"  \"Convert\": 0x79, /* html:Convert (Convert) -> linux:92 (KEY_HENKAN) -> atset1:121 */\n"
"  \"Copy\": 0xe078, /* html:Copy (Copy) -> linux:133 (KEY_COPY) -> atset1:57464 */\n"
"  \"Cut\": 0xe03c, /* html:Cut (Cut) -> linux:137 (KEY_CUT) -> atset1:57404 */\n"
"  \"Delete\": 0xe053, /* html:Delete (Delete) -> linux:111 (KEY_DELETE) -> atset1:57427 */\n"
"  \"Digit0\": 0xb, /* html:Digit0 (Digit0) -> linux:11 (KEY_0) -> atset1:11 */\n"
"  \"Digit1\": 0x2, /* html:Digit1 (Digit1) -> linux:2 (KEY_1) -> atset1:2 */\n"
"  \"Digit2\": 0x3, /* html:Digit2 (Digit2) -> linux:3 (KEY_2) -> atset1:3 */\n"
"  \"Digit3\": 0x4, /* html:Digit3 (Digit3) -> linux:4 (KEY_3) -> atset1:4 */\n"
"  \"Digit4\": 0x5, /* html:Digit4 (Digit4) -> linux:5 (KEY_4) -> atset1:5 */\n"
"  \"Digit5\": 0x6, /* html:Digit5 (Digit5) -> linux:6 (KEY_5) -> atset1:6 */\n"
"  \"Digit6\": 0x7, /* html:Digit6 (Digit6) -> linux:7 (KEY_6) -> atset1:7 */\n"
"  \"Digit7\": 0x8, /* html:Digit7 (Digit7) -> linux:8 (KEY_7) -> atset1:8 */\n"
"  \"Digit8\": 0x9, /* html:Digit8 (Digit8) -> linux:9 (KEY_8) -> atset1:9 */\n"
"  \"Digit9\": 0xa, /* html:Digit9 (Digit9) -> linux:10 (KEY_9) -> atset1:10 */\n"
"  \"Eject\": 0xe07d, /* html:Eject (Eject) -> linux:162 (KEY_EJECTCLOSECD) -> atset1:57469 */\n"
"  \"End\": 0xe04f, /* html:End (End) -> linux:107 (KEY_END) -> atset1:57423 */\n"
"  \"Enter\": 0x1c, /* html:Enter (Enter) -> linux:28 (KEY_ENTER) -> atset1:28 */\n"
"  \"Equal\": 0xd, /* html:Equal (Equal) -> linux:13 (KEY_EQUAL) -> atset1:13 */\n"
"  \"Escape\": 0x1, /* html:Escape (Escape) -> linux:1 (KEY_ESC) -> atset1:1 */\n"
"  \"F1\": 0x3b, /* html:F1 (F1) -> linux:59 (KEY_F1) -> atset1:59 */\n"
"  \"F10\": 0x44, /* html:F10 (F10) -> linux:68 (KEY_F10) -> atset1:68 */\n"
"  \"F11\": 0x57, /* html:F11 (F11) -> linux:87 (KEY_F11) -> atset1:87 */\n"
"  \"F12\": 0x58, /* html:F12 (F12) -> linux:88 (KEY_F12) -> atset1:88 */\n"
"  \"F13\": 0x5d, /* html:F13 (F13) -> linux:183 (KEY_F13) -> atset1:93 */\n"
"  \"F14\": 0x5e, /* html:F14 (F14) -> linux:184 (KEY_F14) -> atset1:94 */\n"
"  \"F15\": 0x5f, /* html:F15 (F15) -> linux:185 (KEY_F15) -> atset1:95 */\n"
"  \"F16\": 0x55, /* html:F16 (F16) -> linux:186 (KEY_F16) -> atset1:85 */\n"
"  \"F17\": 0xe003, /* html:F17 (F17) -> linux:187 (KEY_F17) -> atset1:57347 */\n"
"  \"F18\": 0xe077, /* html:F18 (F18) -> linux:188 (KEY_F18) -> atset1:57463 */\n"
"  \"F19\": 0xe004, /* html:F19 (F19) -> linux:189 (KEY_F19) -> atset1:57348 */\n"
"  \"F2\": 0x3c, /* html:F2 (F2) -> linux:60 (KEY_F2) -> atset1:60 */\n"
"  \"F20\": 0x5a, /* html:F20 (F20) -> linux:190 (KEY_F20) -> atset1:90 */\n"
"  \"F21\": 0x74, /* html:F21 (F21) -> linux:191 (KEY_F21) -> atset1:116 */\n"
"  \"F22\": 0xe079, /* html:F22 (F22) -> linux:192 (KEY_F22) -> atset1:57465 */\n"
"  \"F23\": 0x6d, /* html:F23 (F23) -> linux:193 (KEY_F23) -> atset1:109 */\n"
"  \"F24\": 0x6f, /* html:F24 (F24) -> linux:194 (KEY_F24) -> atset1:111 */\n"
"  \"F3\": 0x3d, /* html:F3 (F3) -> linux:61 (KEY_F3) -> atset1:61 */\n"
"  \"F4\": 0x3e, /* html:F4 (F4) -> linux:62 (KEY_F4) -> atset1:62 */\n"
"  \"F5\": 0x3f, /* html:F5 (F5) -> linux:63 (KEY_F5) -> atset1:63 */\n"
"  \"F6\": 0x40, /* html:F6 (F6) -> linux:64 (KEY_F6) -> atset1:64 */\n"
"  \"F7\": 0x41, /* html:F7 (F7) -> linux:65 (KEY_F7) -> atset1:65 */\n"
"  \"F8\": 0x42, /* html:F8 (F8) -> linux:66 (KEY_F8) -> atset1:66 */\n"
"  \"F9\": 0x43, /* html:F9 (F9) -> linux:67 (KEY_F9) -> atset1:67 */\n"
"  \"Find\": 0xe041, /* html:Find (Find) -> linux:136 (KEY_FIND) -> atset1:57409 */\n"
"  \"Help\": 0xe075, /* html:Help (Help) -> linux:138 (KEY_HELP) -> atset1:57461 */\n"
"  \"Hiragana\": 0x77, /* html:Hiragana (Lang4) -> linux:91 (KEY_HIRAGANA) -> atset1:119 */\n"
"  \"Home\": 0xe047, /* html:Home (Home) -> linux:102 (KEY_HOME) -> atset1:57415 */\n"
"  \"Insert\": 0xe052, /* html:Insert (Insert) -> linux:110 (KEY_INSERT) -> atset1:57426 */\n"
"  \"IntlBackslash\": 0x56, /* html:IntlBackslash (IntlBackslash) -> linux:86 (KEY_102ND) -> atset1:86 */\n"
"  \"IntlRo\": 0x73, /* html:IntlRo (IntlRo) -> linux:89 (KEY_RO) -> atset1:115 */\n"
"  \"IntlYen\": 0x7d, /* html:IntlYen (IntlYen) -> linux:124 (KEY_YEN) -> atset1:125 */\n"
"  \"KanaMode\": 0x70, /* html:KanaMode (KanaMode) -> linux:93 (KEY_KATAKANAHIRAGANA) -> atset1:112 */\n"
"  \"Katakana\": 0x78, /* html:Katakana (Lang3) -> linux:90 (KEY_KATAKANA) -> atset1:120 */\n"
"  \"KeyA\": 0x1e, /* html:KeyA (KeyA) -> linux:30 (KEY_A) -> atset1:30 */\n"
"  \"KeyB\": 0x30, /* html:KeyB (KeyB) -> linux:48 (KEY_B) -> atset1:48 */\n"
"  \"KeyC\": 0x2e, /* html:KeyC (KeyC) -> linux:46 (KEY_C) -> atset1:46 */\n"
"  \"KeyD\": 0x20, /* html:KeyD (KeyD) -> linux:32 (KEY_D) -> atset1:32 */\n"
"  \"KeyE\": 0x12, /* html:KeyE (KeyE) -> linux:18 (KEY_E) -> atset1:18 */\n"
"  \"KeyF\": 0x21, /* html:KeyF (KeyF) -> linux:33 (KEY_F) -> atset1:33 */\n"
"  \"KeyG\": 0x22, /* html:KeyG (KeyG) -> linux:34 (KEY_G) -> atset1:34 */\n"
"  \"KeyH\": 0x23, /* html:KeyH (KeyH) -> linux:35 (KEY_H) -> atset1:35 */\n"
"  \"KeyI\": 0x17, /* html:KeyI (KeyI) -> linux:23 (KEY_I) -> atset1:23 */\n"
"  \"KeyJ\": 0x24, /* html:KeyJ (KeyJ) -> linux:36 (KEY_J) -> atset1:36 */\n"
"  \"KeyK\": 0x25, /* html:KeyK (KeyK) -> linux:37 (KEY_K) -> atset1:37 */\n"
"  \"KeyL\": 0x26, /* html:KeyL (KeyL) -> linux:38 (KEY_L) -> atset1:38 */\n"
"  \"KeyM\": 0x32, /* html:KeyM (KeyM) -> linux:50 (KEY_M) -> atset1:50 */\n"
"  \"KeyN\": 0x31, /* html:KeyN (KeyN) -> linux:49 (KEY_N) -> atset1:49 */\n"
"  \"KeyO\": 0x18, /* html:KeyO (KeyO) -> linux:24 (KEY_O) -> atset1:24 */\n"
"  \"KeyP\": 0x19, /* html:KeyP (KeyP) -> linux:25 (KEY_P) -> atset1:25 */\n"
"  \"KeyQ\": 0x10, /* html:KeyQ (KeyQ) -> linux:16 (KEY_Q) -> atset1:16 */\n"
"  \"KeyR\": 0x13, /* html:KeyR (KeyR) -> linux:19 (KEY_R) -> atset1:19 */\n"
"  \"KeyS\": 0x1f, /* html:KeyS (KeyS) -> linux:31 (KEY_S) -> atset1:31 */\n"
"  \"KeyT\": 0x14, /* html:KeyT (KeyT) -> linux:20 (KEY_T) -> atset1:20 */\n"
"  \"KeyU\": 0x16, /* html:KeyU (KeyU) -> linux:22 (KEY_U) -> atset1:22 */\n"
"  \"KeyV\": 0x2f, /* html:KeyV (KeyV) -> linux:47 (KEY_V) -> atset1:47 */\n"
"  \"KeyW\": 0x11, /* html:KeyW (KeyW) -> linux:17 (KEY_W) -> atset1:17 */\n"
"  \"KeyX\": 0x2d, /* html:KeyX (KeyX) -> linux:45 (KEY_X) -> atset1:45 */\n"
"  \"KeyY\": 0x15, /* html:KeyY (KeyY) -> linux:21 (KEY_Y) -> atset1:21 */\n"
"  \"KeyZ\": 0x2c, /* html:KeyZ (KeyZ) -> linux:44 (KEY_Z) -> atset1:44 */\n"
"  \"Lang1\": 0x72, /* html:Lang1 (Lang1) -> linux:122 (KEY_HANGEUL) -> atset1:114 */\n"
"  \"Lang2\": 0x71, /* html:Lang2 (Lang2) -> linux:123 (KEY_HANJA) -> atset1:113 */\n"
"  \"Lang3\": 0x78, /* html:Lang3 (Lang3) -> linux:90 (KEY_KATAKANA) -> atset1:120 */\n"
"  \"Lang4\": 0x77, /* html:Lang4 (Lang4) -> linux:91 (KEY_HIRAGANA) -> atset1:119 */\n"
"  \"Lang5\": 0x76, /* html:Lang5 (Lang5) -> linux:85 (KEY_ZENKAKUHANKAKU) -> atset1:118 */\n"
"  \"LaunchApp1\": 0xe06b, /* html:LaunchApp1 (LaunchApp1) -> linux:157 (KEY_COMPUTER) -> atset1:57451 */\n"
"  \"LaunchApp2\": 0xe021, /* html:LaunchApp2 (LaunchApp2) -> linux:140 (KEY_CALC) -> atset1:57377 */\n"
"  \"LaunchMail\": 0xe06c, /* html:LaunchMail (LaunchMail) -> linux:155 (KEY_MAIL) -> atset1:57452 */\n"
"  \"MediaPlayPause\": 0xe022, /* html:MediaPlayPause (MediaPlayPause) -> linux:164 (KEY_PLAYPAUSE) -> atset1:57378 */\n"
"  \"MediaSelect\": 0xe06d, /* html:MediaSelect (MediaSelect) -> linux:226 (KEY_MEDIA) -> atset1:57453 */\n"
"  \"MediaStop\": 0xe024, /* html:MediaStop (MediaStop) -> linux:166 (KEY_STOPCD) -> atset1:57380 */\n"
"  \"MediaTrackNext\": 0xe019, /* html:MediaTrackNext (MediaTrackNext) -> linux:163 (KEY_NEXTSONG) -> atset1:57369 */\n"
"  \"MediaTrackPrevious\": 0xe010, /* html:MediaTrackPrevious (MediaTrackPrevious) -> linux:165 (KEY_PREVIOUSSONG) -> atset1:57360 */\n"
"  \"MetaLeft\": 0xe05b, /* html:MetaLeft (MetaLeft) -> linux:125 (KEY_LEFTMETA) -> atset1:57435 */\n"
"  \"MetaRight\": 0xe05c, /* html:MetaRight (MetaRight) -> linux:126 (KEY_RIGHTMETA) -> atset1:57436 */\n"
"  \"Minus\": 0xc, /* html:Minus (Minus) -> linux:12 (KEY_MINUS) -> atset1:12 */\n"
"  \"NonConvert\": 0x7b, /* html:NonConvert (NonConvert) -> linux:94 (KEY_MUHENKAN) -> atset1:123 */\n"
"  \"NumLock\": 0x45, /* html:NumLock (NumLock) -> linux:69 (KEY_NUMLOCK) -> atset1:69 */\n"
"  \"Numpad0\": 0x52, /* html:Numpad0 (Numpad0) -> linux:82 (KEY_KP0) -> atset1:82 */\n"
"  \"Numpad1\": 0x4f, /* html:Numpad1 (Numpad1) -> linux:79 (KEY_KP1) -> atset1:79 */\n"
"  \"Numpad2\": 0x50, /* html:Numpad2 (Numpad2) -> linux:80 (KEY_KP2) -> atset1:80 */\n"
"  \"Numpad3\": 0x51, /* html:Numpad3 (Numpad3) -> linux:81 (KEY_KP3) -> atset1:81 */\n"
"  \"Numpad4\": 0x4b, /* html:Numpad4 (Numpad4) -> linux:75 (KEY_KP4) -> atset1:75 */\n"
"  \"Numpad5\": 0x4c, /* html:Numpad5 (Numpad5) -> linux:76 (KEY_KP5) -> atset1:76 */\n"
"  \"Numpad6\": 0x4d, /* html:Numpad6 (Numpad6) -> linux:77 (KEY_KP6) -> atset1:77 */\n"
"  \"Numpad7\": 0x47, /* html:Numpad7 (Numpad7) -> linux:71 (KEY_KP7) -> atset1:71 */\n"
"  \"Numpad8\": 0x48, /* html:Numpad8 (Numpad8) -> linux:72 (KEY_KP8) -> atset1:72 */\n"
"  \"Numpad9\": 0x49, /* html:Numpad9 (Numpad9) -> linux:73 (KEY_KP9) -> atset1:73 */\n"
"  \"NumpadAdd\": 0x4e, /* html:NumpadAdd (NumpadAdd) -> linux:78 (KEY_KPPLUS) -> atset1:78 */\n"
"  \"NumpadComma\": 0x7e, /* html:NumpadComma (NumpadComma) -> linux:121 (KEY_KPCOMMA) -> atset1:126 */\n"
"  \"NumpadDecimal\": 0x53, /* html:NumpadDecimal (NumpadDecimal) -> linux:83 (KEY_KPDOT) -> atset1:83 */\n"
"  \"NumpadDivide\": 0xe035, /* html:NumpadDivide (NumpadDivide) -> linux:98 (KEY_KPSLASH) -> atset1:57397 */\n"
"  \"NumpadEnter\": 0xe01c, /* html:NumpadEnter (NumpadEnter) -> linux:96 (KEY_KPENTER) -> atset1:57372 */\n"
"  \"NumpadEqual\": 0x59, /* html:NumpadEqual (NumpadEqual) -> linux:117 (KEY_KPEQUAL) -> atset1:89 */\n"
"  \"NumpadMultiply\": 0x37, /* html:NumpadMultiply (NumpadMultiply) -> linux:55 (KEY_KPASTERISK) -> atset1:55 */\n"
"  \"NumpadParenLeft\": 0xe076, /* html:NumpadParenLeft (NumpadParenLeft) -> linux:179 (KEY_KPLEFTPAREN) -> atset1:57462 */\n"
"  \"NumpadParenRight\": 0xe07b, /* html:NumpadParenRight (NumpadParenRight) -> linux:180 (KEY_KPRIGHTPAREN) -> atset1:57467 */\n"
"  \"NumpadSubtract\": 0x4a, /* html:NumpadSubtract (NumpadSubtract) -> linux:74 (KEY_KPMINUS) -> atset1:74 */\n"
"  \"Open\": 0x64, /* html:Open (Open) -> linux:134 (KEY_OPEN) -> atset1:100 */\n"
"  \"PageDown\": 0xe051, /* html:PageDown (PageDown) -> linux:109 (KEY_PAGEDOWN) -> atset1:57425 */\n"
"  \"PageUp\": 0xe049, /* html:PageUp (PageUp) -> linux:104 (KEY_PAGEUP) -> atset1:57417 */\n"
"  \"Paste\": 0x65, /* html:Paste (Paste) -> linux:135 (KEY_PASTE) -> atset1:101 */\n"
"  \"Pause\": 0xe046, /* html:Pause (Pause) -> linux:119 (KEY_PAUSE) -> atset1:57414 */\n"
"  \"Period\": 0x34, /* html:Period (Period) -> linux:52 (KEY_DOT) -> atset1:52 */\n"
"  \"Power\": 0xe05e, /* html:Power (Power) -> linux:116 (KEY_POWER) -> atset1:57438 */\n"
"  \"PrintScreen\": 0x54, /* html:PrintScreen (PrintScreen) -> linux:99 (KEY_SYSRQ) -> atset1:84 */\n"
"  \"Props\": 0xe006, /* html:Props (Props) -> linux:130 (KEY_PROPS) -> atset1:57350 */\n"
"  \"Quote\": 0x28, /* html:Quote (Quote) -> linux:40 (KEY_APOSTROPHE) -> atset1:40 */\n"
"  \"ScrollLock\": 0x46, /* html:ScrollLock (ScrollLock) -> linux:70 (KEY_SCROLLLOCK) -> atset1:70 */\n"
"  \"Semicolon\": 0x27, /* html:Semicolon (Semicolon) -> linux:39 (KEY_SEMICOLON) -> atset1:39 */\n"
"  \"ShiftLeft\": 0x2a, /* html:ShiftLeft (ShiftLeft) -> linux:42 (KEY_LEFTSHIFT) -> atset1:42 */\n"
"  \"ShiftRight\": 0x36, /* html:ShiftRight (ShiftRight) -> linux:54 (KEY_RIGHTSHIFT) -> atset1:54 */\n"
"  \"Slash\": 0x35, /* html:Slash (Slash) -> linux:53 (KEY_SLASH) -> atset1:53 */\n"
"  \"Sleep\": 0xe05f, /* html:Sleep (Sleep) -> linux:142 (KEY_SLEEP) -> atset1:57439 */\n"
"  \"Space\": 0x39, /* html:Space (Space) -> linux:57 (KEY_SPACE) -> atset1:57 */\n"
"  \"Suspend\": 0xe025, /* html:Suspend (Suspend) -> linux:205 (KEY_SUSPEND) -> atset1:57381 */\n"
"  \"Tab\": 0xf, /* html:Tab (Tab) -> linux:15 (KEY_TAB) -> atset1:15 */\n"
"  \"Undo\": 0xe007, /* html:Undo (Undo) -> linux:131 (KEY_UNDO) -> atset1:57351 */\n"
"  \"WakeUp\": 0xe063, /* html:WakeUp (WakeUp) -> linux:143 (KEY_WAKEUP) -> atset1:57443 */\n"
"};\n"
;
    if (strcmp(path, "/novnc/input/xtscancodes.js") == 0) return novnc_input_xtscancodes_js;
const char *novnc_ra2_js =
"\n"
"class AESEAXCipher {\n"
"    constructor() {\n"
"        this._rawKey = null;\n"
"        this._ctrKey = null;\n"
"        this._cbcKey = null;\n"
"        this._zeroBlock = new Uint8Array(16);\n"
"        this._prefixBlock0 = this._zeroBlock;\n"
"        this._prefixBlock1 = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);\n"
"        this._prefixBlock2 = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]);\n"
"    }\n"
"\n"
"    async _encryptBlock(block) {\n"
"        const encrypted = await window.crypto.subtle.encrypt({\n"
"            name: \"AES-CBC\",\n"
"            iv: this._zeroBlock,\n"
"        }, this._cbcKey, block);\n"
"        return new Uint8Array(encrypted).slice(0, 16);\n"
"    }\n"
"\n"
"    async _initCMAC() {\n"
"        const k1 = await this._encryptBlock(this._zeroBlock);\n"
"        const k2 = new Uint8Array(16);\n"
"        const v = k1[0] >>> 6;\n"
"        for (let i = 0; i < 15; i++) {\n"
"            k2[i] = (k1[i + 1] >> 6) | (k1[i] << 2);\n"
"            k1[i] = (k1[i + 1] >> 7) | (k1[i] << 1);\n"
"        }\n"
"        const lut = [0x0, 0x87, 0x0e, 0x89];\n"
"        k2[14] ^= v >>> 1;\n"
"        k2[15] = (k1[15] << 2) ^ lut[v];\n"
"        k1[15] = (k1[15] << 1) ^ lut[v >> 1];\n"
"        this._k1 = k1;\n"
"        this._k2 = k2;\n"
"    }\n"
"\n"
"    async _encryptCTR(data, counter) {\n"
"        const encrypted = await window.crypto.subtle.encrypt({\n"
"            \"name\": \"AES-CTR\",\n"
"            counter: counter,\n"
"            length: 128\n"
"        }, this._ctrKey, data);\n"
"        return new Uint8Array(encrypted);\n"
"    }\n"
"\n"
"    async _decryptCTR(data, counter) {\n"
"        const decrypted = await window.crypto.subtle.decrypt({\n"
"            \"name\": \"AES-CTR\",\n"
"            counter: counter,\n"
"            length: 128\n"
"        }, this._ctrKey, data);\n"
"        return new Uint8Array(decrypted);\n"
"    }\n"
"\n"
"    async _computeCMAC(data, prefixBlock) {\n"
"        if (prefixBlock.length !== 16) {\n"
"            return null;\n"
"        }\n"
"        const n = Math.floor(data.length / 16);\n"
"        const m = Math.ceil(data.length / 16);\n"
"        const r = data.length - n * 16;\n"
"        const cbcData = new Uint8Array((m + 1) * 16);\n"
"        cbcData.set(prefixBlock);\n"
"        cbcData.set(data, 16);\n"
"        if (r === 0) {\n"
"            for (let i = 0; i < 16; i++) {\n"
"                cbcData[n * 16 + i] ^= this._k1[i];\n"
"            }\n"
"        } else {\n"
"            cbcData[(n + 1) * 16 + r] = 0x80;\n"
"            for (let i = 0; i < 16; i++) {\n"
"                cbcData[(n + 1) * 16 + i] ^= this._k2[i];\n"
"            }\n"
"        }\n"
"        let cbcEncrypted = await window.crypto.subtle.encrypt({\n"
"            name: \"AES-CBC\",\n"
"            iv: this._zeroBlock,\n"
"        }, this._cbcKey, cbcData);\n"
"\n"
"        cbcEncrypted = new Uint8Array(cbcEncrypted);\n"
"        const mac = cbcEncrypted.slice(cbcEncrypted.length - 32, cbcEncrypted.length - 16);\n"
"        return mac;\n"
"    }\n"
"\n"
"    async setKey(key) {\n"
"        this._rawKey = key;\n"
"        this._ctrKey = await window.crypto.subtle.importKey(\n"
"            \"raw\", key, {\"name\": \"AES-CTR\"}, false, [\"encrypt\", \"decrypt\"]);\n"
"        this._cbcKey = await window.crypto.subtle.importKey(\n"
"            \"raw\", key, {\"name\": \"AES-CBC\"}, false, [\"encrypt\", \"decrypt\"]);\n"
"        await this._initCMAC();\n"
"    }\n"
"\n"
"    async encrypt(message, associatedData, nonce) {\n"
"        const nCMAC = await this._computeCMAC(nonce, this._prefixBlock0);\n"
"        const encrypted = await this._encryptCTR(message, nCMAC);\n"
"        const adCMAC = await this._computeCMAC(associatedData, this._prefixBlock1);\n"
"        const mac = await this._computeCMAC(encrypted, this._prefixBlock2);\n"
"        for (let i = 0; i < 16; i++) {\n"
"            mac[i] ^= nCMAC[i] ^ adCMAC[i];\n"
"        }\n"
"        const res = new Uint8Array(16 + encrypted.length);\n"
"        res.set(encrypted);\n"
"        res.set(mac, encrypted.length);\n"
"        return res;\n"
"    }\n"
"\n"
"    async decrypt(encrypted, associatedData, nonce, mac) {\n"
"        const nCMAC = await this._computeCMAC(nonce, this._prefixBlock0);\n"
"        const adCMAC = await this._computeCMAC(associatedData, this._prefixBlock1);\n"
"        const computedMac = await this._computeCMAC(encrypted, this._prefixBlock2);\n"
"        for (let i = 0; i < 16; i++) {\n"
"            computedMac[i] ^= nCMAC[i] ^ adCMAC[i];\n"
"        }\n"
"        if (computedMac.length !== mac.length) {\n"
"            return null;\n"
"        }\n"
"        for (let i = 0; i < mac.length; i++) {\n"
"            if (computedMac[i] !== mac[i]) {\n"
"                return null;\n"
"            }\n"
"        }\n"
"        const res = await this._decryptCTR(encrypted, nCMAC);\n"
"        return res;\n"
"    }\n"
"}\n"
"\n"
"class RA2Cipher {\n"
"    constructor() {\n"
"        this._cipher = new AESEAXCipher();\n"
"        this._counter = new Uint8Array(16);\n"
"    }\n"
"\n"
"    async setKey(key) {\n"
"        await this._cipher.setKey(key);\n"
"    }\n"
"\n"
"    async makeMessage(message) {\n"
"        const ad = new Uint8Array([(message.length & 0xff00) >>> 8, message.length & 0xff]);\n"
"        const encrypted = await this._cipher.encrypt(message, ad, this._counter);\n"
"        for (let i = 0; i < 16 && this._counter[i]++ === 255; i++);\n"
"        const res = new Uint8Array(message.length + 2 + 16);\n"
"        res.set(ad);\n"
"        res.set(encrypted, 2);\n"
"        return res;\n"
"    }\n"
"\n"
"    async receiveMessage(length, encrypted, mac) {\n"
"        const ad = new Uint8Array([(length & 0xff00) >>> 8, length & 0xff]);\n"
"        const res = await this._cipher.decrypt(encrypted, ad, this._counter, mac);\n"
"        for (let i = 0; i < 16 && this._counter[i]++ === 255; i++);\n"
"        return res;\n"
"    }\n"
"}\n"
"\n"
"class RSACipher {\n"
"    constructor(keyLength) {\n"
"        this._key = null;\n"
"        this._keyLength = keyLength;\n"
"        this._keyBytes = Math.ceil(keyLength / 8);\n"
"        this._n = null;\n"
"        this._e = null;\n"
"        this._d = null;\n"
"        this._nBigInt = null;\n"
"        this._eBigInt = null;\n"
"        this._dBigInt = null;\n"
"    }\n"
"\n"
"    _base64urlDecode(data) {\n"
"        data = data.replace(/-/g, \"+\").replace(/_/g, \"/\");\n"
"        data = data.padEnd(Math.ceil(data.length / 4) * 4, \"=\");\n"
"        return Base64.decode(data);\n"
"    }\n"
"\n"
"    _u8ArrayToBigInt(arr) {\n"
"        let hex = '0x';\n"
"        for (let i = 0; i < arr.length; i++) {\n"
"            hex += arr[i].toString(16).padStart(2, '0');\n"
"        }\n"
"        return BigInt(hex);\n"
"    }\n"
"\n"
"    _padArray(arr, length) {\n"
"        const res = new Uint8Array(length);\n"
"        res.set(arr, length - arr.length);\n"
"        return res;\n"
"    }\n"
"\n"
"    _bigIntToU8Array(bigint, padLength=0) {\n"
"        let hex = bigint.toString(16);\n"
"        if (padLength === 0) {\n"
"            padLength = Math.ceil(hex.length / 2) * 2;\n"
"        }\n"
"        hex = hex.padStart(padLength * 2, '0');\n"
"        const length = hex.length / 2;\n"
"        const arr = new Uint8Array(length);\n"
"        for (let i = 0; i < length; i++) {\n"
"            arr[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n"
"        }\n"
"        return arr;\n"
"    }\n"
"\n"
"    _modPow(b, e, m) {\n"
"        if (m === 1n) {\n"
"            return 0;\n"
"        }\n"
"        let r = 1n;\n"
"        b = b % m;\n"
"        while (e > 0) {\n"
"            if (e % 2n === 1n) {\n"
"                r = (r * b) % m;\n"
"            }\n"
"            e = e / 2n;\n"
"            b = (b * b) % m;\n"
"        }\n"
"        return r;\n"
"    }\n"
"\n"
"    async generateKey() {\n"
"        this._key = await window.crypto.subtle.generateKey(\n"
"            {\n"
"                name: \"RSA-OAEP\",\n"
"                modulusLength: this._keyLength,\n"
"                publicExponent: new Uint8Array([0x01, 0x00, 0x01]),\n"
"                hash: {name: \"SHA-256\"},\n"
"            },\n"
"            true, [\"encrypt\", \"decrypt\"]);\n"
"        const privateKey = await window.crypto.subtle.exportKey(\"jwk\", this._key.privateKey);\n"
"        this._n = this._padArray(this._base64urlDecode(privateKey.n), this._keyBytes);\n"
"        this._nBigInt = this._u8ArrayToBigInt(this._n);\n"
"        this._e = this._padArray(this._base64urlDecode(privateKey.e), this._keyBytes);\n"
"        this._eBigInt = this._u8ArrayToBigInt(this._e);\n"
"        this._d = this._padArray(this._base64urlDecode(privateKey.d), this._keyBytes);\n"
"        this._dBigInt = this._u8ArrayToBigInt(this._d);\n"
"    }\n"
"\n"
"    setPublicKey(n, e) {\n"
"        if (n.length !== this._keyBytes || e.length !== this._keyBytes) {\n"
"            return;\n"
"        }\n"
"        this._n = new Uint8Array(this._keyBytes);\n"
"        this._e = new Uint8Array(this._keyBytes);\n"
"        this._n.set(n);\n"
"        this._e.set(e);\n"
"        this._nBigInt = this._u8ArrayToBigInt(this._n);\n"
"        this._eBigInt = this._u8ArrayToBigInt(this._e);\n"
"    }\n"
"\n"
"    encrypt(message) {\n"
"        if (message.length > this._keyBytes - 11) {\n"
"            return null;\n"
"        }\n"
"        const ps = new Uint8Array(this._keyBytes - message.length - 3);\n"
"        window.crypto.getRandomValues(ps);\n"
"        for (let i = 0; i < ps.length; i++) {\n"
"            ps[i] = Math.floor(ps[i] * 254 / 255 + 1);\n"
"        }\n"
"        const em = new Uint8Array(this._keyBytes);\n"
"        em[1] = 0x02;\n"
"        em.set(ps, 2);\n"
"        em.set(message, ps.length + 3);\n"
"        const emBigInt = this._u8ArrayToBigInt(em);\n"
"        const c = this._modPow(emBigInt, this._eBigInt, this._nBigInt);\n"
"        return this._bigIntToU8Array(c, this._keyBytes);\n"
"    }\n"
"\n"
"    decrypt(message) {\n"
"        if (message.length !== this._keyBytes) {\n"
"            return null;\n"
"        }\n"
"        const msgBigInt = this._u8ArrayToBigInt(message);\n"
"        const emBigInt = this._modPow(msgBigInt, this._dBigInt, this._nBigInt);\n"
"        const em = this._bigIntToU8Array(emBigInt, this._keyBytes);\n"
"        if (em[0] !== 0x00 || em[1] !== 0x02) {\n"
"            return null;\n"
"        }\n"
"        let i = 2;\n"
"        for (; i < em.length; i++) {\n"
"            if (em[i] === 0x00) {\n"
"                break;\n"
"            }\n"
"        }\n"
"        if (i === em.length) {\n"
"            return null;\n"
"        }\n"
"        return em.slice(i + 1, em.length);\n"
"    }\n"
"\n"
"    get keyLength() {\n"
"        return this._keyLength;\n"
"    }\n"
"\n"
"    get n() {\n"
"        return this._n;\n"
"    }\n"
"\n"
"    get e() {\n"
"        return this._e;\n"
"    }\n"
"\n"
"    get d() {\n"
"        return this._d;\n"
"    }\n"
"}\n"
"\n"
"class RSAAESAuthenticationState extends EventTargetMixin {\n"
"    constructor(sock, getCredentials) {\n"
"        super();\n"
"        this._hasStarted = false;\n"
"        this._checkSock = null;\n"
"        this._checkCredentials = null;\n"
"        this._approveServerResolve = null;\n"
"        this._sockReject = null;\n"
"        this._credentialsReject = null;\n"
"        this._approveServerReject = null;\n"
"        this._sock = sock;\n"
"        this._getCredentials = getCredentials;\n"
"    }\n"
"\n"
"    _waitSockAsync(len) {\n"
"        return new Promise((resolve, reject) => {\n"
"            const hasData = () => !this._sock.rQwait('RA2', len);\n"
"            if (hasData()) {\n"
"                resolve();\n"
"            } else {\n"
"                this._checkSock = () => {\n"
"                    if (hasData()) {\n"
"                        resolve();\n"
"                        this._checkSock = null;\n"
"                        this._sockReject = null;\n"
"                    }\n"
"                };\n"
"                this._sockReject = reject;\n"
"            }\n"
"        });\n"
"    }\n"
"\n"
"    _waitApproveKeyAsync() {\n"
"        return new Promise((resolve, reject) => {\n"
"            this._approveServerResolve = resolve;\n"
"            this._approveServerReject = reject;\n"
"        });\n"
"    }\n"
"\n"
"    _waitCredentialsAsync(subtype) {\n"
"        const hasCredentials = () => {\n"
"            if (subtype === 1 && this._getCredentials().username !== undefined &&\n"
"                this._getCredentials().password !== undefined) {\n"
"                return true;\n"
"            } else if (subtype === 2 && this._getCredentials().password !== undefined) {\n"
"                return true;\n"
"            }\n"
"            return false;\n"
"        };\n"
"        return new Promise((resolve, reject) => {\n"
"            if (hasCredentials()) {\n"
"                resolve();\n"
"            } else {\n"
"                this._checkCredentials = () => {\n"
"                    if (hasCredentials()) {\n"
"                        resolve();\n"
"                        this._checkCredentials = null;\n"
"                        this._credentialsReject = null;\n"
"                    }\n"
"                };\n"
"                this._credentialsReject = reject;\n"
"            }\n"
"        });\n"
"    }\n"
"\n"
"    checkInternalEvents() {\n"
"        if (this._checkSock !== null) {\n"
"            this._checkSock();\n"
"        }\n"
"        if (this._checkCredentials !== null) {\n"
"            this._checkCredentials();\n"
"        }\n"
"    }\n"
"\n"
"    approveServer() {\n"
"        if (this._approveServerResolve !== null) {\n"
"            this._approveServerResolve();\n"
"            this._approveServerResolve = null;\n"
"        }\n"
"    }\n"
"\n"
"    disconnect() {\n"
"        if (this._sockReject !== null) {\n"
"            this._sockReject(new Error(\"disconnect normally\"));\n"
"            this._sockReject = null;\n"
"        }\n"
"        if (this._credentialsReject !== null) {\n"
"            this._credentialsReject(new Error(\"disconnect normally\"));\n"
"            this._credentialsReject = null;\n"
"        }\n"
"        if (this._approveServerReject !== null) {\n"
"            this._approveServerReject(new Error(\"disconnect normally\"));\n"
"            this._approveServerReject = null;\n"
"        }\n"
"    }\n"
"\n"
"    async negotiateRA2neAuthAsync() {\n"
"        this._hasStarted = true;\n"
"        // 1: Receive server public key\n"
"        await this._waitSockAsync(4);\n"
"        const serverKeyLengthBuffer = this._sock.rQslice(0, 4);\n"
"        const serverKeyLength = this._sock.rQshift32();\n"
"        if (serverKeyLength < 1024) {\n"
"            throw new Error(\"RA2: server public key is too short: \" + serverKeyLength);\n"
"        } else if (serverKeyLength > 8192) {\n"
"            throw new Error(\"RA2: server public key is too long: \" + serverKeyLength);\n"
"        }\n"
"        const serverKeyBytes = Math.ceil(serverKeyLength / 8);\n"
"        await this._waitSockAsync(serverKeyBytes * 2);\n"
"        const serverN = this._sock.rQshiftBytes(serverKeyBytes);\n"
"        const serverE = this._sock.rQshiftBytes(serverKeyBytes);\n"
"        const serverRSACipher = new RSACipher(serverKeyLength);\n"
"        serverRSACipher.setPublicKey(serverN, serverE);\n"
"        const serverPublickey = new Uint8Array(4 + serverKeyBytes * 2);\n"
"        serverPublickey.set(serverKeyLengthBuffer);\n"
"        serverPublickey.set(serverN, 4);\n"
"        serverPublickey.set(serverE, 4 + serverKeyBytes);\n"
"\n"
"        // verify server public key\n"
"        this.dispatchEvent(new CustomEvent(\"serververification\", {\n"
"            detail: { type: \"RSA\", publickey: serverPublickey }\n"
"        }));\n"
"        await this._waitApproveKeyAsync();\n"
"\n"
"        // 2: Send client public key\n"
"        const clientKeyLength = 2048;\n"
"        const clientKeyBytes = Math.ceil(clientKeyLength / 8);\n"
"        const clientRSACipher = new RSACipher(clientKeyLength);\n"
"        await clientRSACipher.generateKey();\n"
"        const clientN = clientRSACipher.n;\n"
"        const clientE = clientRSACipher.e;\n"
"        const clientPublicKey = new Uint8Array(4 + clientKeyBytes * 2);\n"
"        clientPublicKey[0] = (clientKeyLength & 0xff000000) >>> 24;\n"
"        clientPublicKey[1] = (clientKeyLength & 0xff0000) >>> 16;\n"
"        clientPublicKey[2] = (clientKeyLength & 0xff00) >>> 8;\n"
"        clientPublicKey[3] = clientKeyLength & 0xff;\n"
"        clientPublicKey.set(clientN, 4);\n"
"        clientPublicKey.set(clientE, 4 + clientKeyBytes);\n"
"        this._sock.send(clientPublicKey);\n"
"\n"
"        // 3: Send client random\n"
"        const clientRandom = new Uint8Array(16);\n"
"        window.crypto.getRandomValues(clientRandom);\n"
"        const clientEncryptedRandom = serverRSACipher.encrypt(clientRandom);\n"
"        const clientRandomMessage = new Uint8Array(2 + serverKeyBytes);\n"
"        clientRandomMessage[0] = (serverKeyBytes & 0xff00) >>> 8;\n"
"        clientRandomMessage[1] = serverKeyBytes & 0xff;\n"
"        clientRandomMessage.set(clientEncryptedRandom, 2);\n"
"        this._sock.send(clientRandomMessage);\n"
"\n"
"        // 4: Receive server random\n"
"        await this._waitSockAsync(2);\n"
"        if (this._sock.rQshift16() !== clientKeyBytes) {\n"
"            throw new Error(\"RA2: wrong encrypted message length\");\n"
"        }\n"
"        const serverEncryptedRandom = this._sock.rQshiftBytes(clientKeyBytes);\n"
"        const serverRandom = clientRSACipher.decrypt(serverEncryptedRandom);\n"
"        if (serverRandom === null || serverRandom.length !== 16) {\n"
"            throw new Error(\"RA2: corrupted server encrypted random\");\n"
"        }\n"
"\n"
"        // 5: Compute session keys and set ciphers\n"
"        let clientSessionKey = new Uint8Array(32);\n"
"        let serverSessionKey = new Uint8Array(32);\n"
"        clientSessionKey.set(serverRandom);\n"
"        clientSessionKey.set(clientRandom, 16);\n"
"        serverSessionKey.set(clientRandom);\n"
"        serverSessionKey.set(serverRandom, 16);\n"
"        clientSessionKey = await window.crypto.subtle.digest(\"SHA-1\", clientSessionKey);\n"
"        clientSessionKey = new Uint8Array(clientSessionKey).slice(0, 16);\n"
"        serverSessionKey = await window.crypto.subtle.digest(\"SHA-1\", serverSessionKey);\n"
"        serverSessionKey = new Uint8Array(serverSessionKey).slice(0, 16);\n"
"        const clientCipher = new RA2Cipher();\n"
"        await clientCipher.setKey(clientSessionKey);\n"
"        const serverCipher = new RA2Cipher();\n"
"        await serverCipher.setKey(serverSessionKey);\n"
"\n"
"        // 6: Compute and exchange hashes\n"
"        let serverHash = new Uint8Array(8 + serverKeyBytes * 2 + clientKeyBytes * 2);\n"
"        let clientHash = new Uint8Array(8 + serverKeyBytes * 2 + clientKeyBytes * 2);\n"
"        serverHash.set(serverPublickey);\n"
"        serverHash.set(clientPublicKey, 4 + serverKeyBytes * 2);\n"
"        clientHash.set(clientPublicKey);\n"
"        clientHash.set(serverPublickey, 4 + clientKeyBytes * 2);\n"
"        serverHash = await window.crypto.subtle.digest(\"SHA-1\", serverHash);\n"
"        clientHash = await window.crypto.subtle.digest(\"SHA-1\", clientHash);\n"
"        serverHash = new Uint8Array(serverHash);\n"
"        clientHash = new Uint8Array(clientHash);\n"
"        this._sock.send(await clientCipher.makeMessage(clientHash));\n"
"        await this._waitSockAsync(2 + 20 + 16);\n"
"        if (this._sock.rQshift16() !== 20) {\n"
"            throw new Error(\"RA2: wrong server hash\");\n"
"        }\n"
"        const serverHashReceived = await serverCipher.receiveMessage(\n"
"            20, this._sock.rQshiftBytes(20), this._sock.rQshiftBytes(16));\n"
"        if (serverHashReceived === null) {\n"
"            throw new Error(\"RA2: failed to authenticate the message\");\n"
"        }\n"
"        for (let i = 0; i < 20; i++) {\n"
"            if (serverHashReceived[i] !== serverHash[i]) {\n"
"                throw new Error(\"RA2: wrong server hash\");\n"
"            }\n"
"        }\n"
"\n"
"        // 7: Receive subtype\n"
"        await this._waitSockAsync(2 + 1 + 16);\n"
"        if (this._sock.rQshift16() !== 1) {\n"
"            throw new Error(\"RA2: wrong subtype\");\n"
"        }\n"
"        let subtype = (await serverCipher.receiveMessage(\n"
"            1, this._sock.rQshiftBytes(1), this._sock.rQshiftBytes(16)));\n"
"        if (subtype === null) {\n"
"            throw new Error(\"RA2: failed to authenticate the message\");\n"
"        }\n"
"        subtype = subtype[0];\n"
"        if (subtype === 1) {\n"
"            if (this._getCredentials().username === undefined ||\n"
"                this._getCredentials().password === undefined) {\n"
"                this.dispatchEvent(new CustomEvent(\n"
"                    \"credentialsrequired\",\n"
"                    { detail: { types: [\"username\", \"password\"] } }));\n"
"            }\n"
"        } else if (subtype === 2) {\n"
"            if (this._getCredentials().password === undefined) {\n"
"                this.dispatchEvent(new CustomEvent(\n"
"                    \"credentialsrequired\",\n"
"                    { detail: { types: [\"password\"] } }));\n"
"            }\n"
"        } else {\n"
"            throw new Error(\"RA2: wrong subtype\");\n"
"        }\n"
"        await this._waitCredentialsAsync(subtype);\n"
"        let username;\n"
"        if (subtype === 1) {\n"
"            username = encodeUTF8(this._getCredentials().username).slice(0, 255);\n"
"        } else {\n"
"            username = \"\";\n"
"        }\n"
"        const password = encodeUTF8(this._getCredentials().password).slice(0, 255);\n"
"        const credentials = new Uint8Array(username.length + password.length + 2);\n"
"        credentials[0] = username.length;\n"
"        credentials[username.length + 1] = password.length;\n"
"        for (let i = 0; i < username.length; i++) {\n"
"            credentials[i + 1] = username.charCodeAt(i);\n"
"        }\n"
"        for (let i = 0; i < password.length; i++) {\n"
"            credentials[username.length + 2 + i] = password.charCodeAt(i);\n"
"        }\n"
"        this._sock.send(await clientCipher.makeMessage(credentials));\n"
"    }\n"
"\n"
"    get hasStarted() {\n"
"        return this._hasStarted;\n"
"    }\n"
"\n"
"    set hasStarted(s) {\n"
"        this._hasStarted = s;\n"
"    }\n"
"}window.AESEAXCipher = AESEAXCipher;\n"
"window.RA2Cipher = RA2Cipher;\n"
"window.RSACipher = RSACipher;\n"
"window.RSAAESAuthenticationState = RSAAESAuthenticationState;\n"
;
    if (strcmp(path, "/novnc/ra2.js") == 0) return novnc_ra2_js;
const char *novnc_rfb_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2020 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" *\n"
" */\n"
"\n"
"\n"
"\n"
"// How many seconds to wait for a disconnect to finish\n"
"const DISCONNECT_TIMEOUT = 3;\n"
"const DEFAULT_BACKGROUND = 'rgb(40, 40, 40)';\n"
"\n"
"// Minimum wait (ms) between two mouse moves\n"
"const MOUSE_MOVE_DELAY = 17;\n"
"\n"
"// Wheel thresholds\n"
"const WHEEL_STEP = 50; // Pixels needed for one step\n"
"const WHEEL_LINE_HEIGHT = 19; // Assumed pixels for one line step\n"
"\n"
"// Gesture thresholds\n"
"const GESTURE_ZOOMSENS = 75;\n"
"const GESTURE_SCRLSENS = 50;\n"
"const DOUBLE_TAP_TIMEOUT = 1000;\n"
"const DOUBLE_TAP_THRESHOLD = 50;\n"
"\n"
"// Security types\n"
"const securityTypeNone              = 1;\n"
"const securityTypeVNCAuth           = 2;\n"
"const securityTypeRA2ne             = 6;\n"
"const securityTypeTight             = 16;\n"
"const securityTypeVeNCrypt          = 19;\n"
"const securityTypeXVP               = 22;\n"
"const securityTypeARD               = 30;\n"
"const securityTypeMSLogonII         = 113;\n"
"\n"
"// Special Tight security types\n"
"const securityTypeUnixLogon         = 129;\n"
"\n"
"// VeNCrypt security types\n"
"const securityTypePlain             = 256;\n"
"\n"
"// Extended clipboard pseudo-encoding formats\n"
"const extendedClipboardFormatText   = 1;\n"
"/*eslint-disable no-unused-vars */\n"
"const extendedClipboardFormatRtf    = 1 << 1;\n"
"const extendedClipboardFormatHtml   = 1 << 2;\n"
"const extendedClipboardFormatDib    = 1 << 3;\n"
"const extendedClipboardFormatFiles  = 1 << 4;\n"
"/*eslint-enable */\n"
"\n"
"// Extended clipboard pseudo-encoding actions\n"
"const extendedClipboardActionCaps    = 1 << 24;\n"
"const extendedClipboardActionRequest = 1 << 25;\n"
"const extendedClipboardActionPeek    = 1 << 26;\n"
"const extendedClipboardActionNotify  = 1 << 27;\n"
"const extendedClipboardActionProvide = 1 << 28;\n"
"\n"
"class RFB extends EventTargetMixin {\n"
"    constructor(target, urlOrChannel, options) {\n"
"        if (!target) {\n"
"            throw new Error(\"Must specify target\");\n"
"        }\n"
"        if (!urlOrChannel) {\n"
"            throw new Error(\"Must specify URL, WebSocket or RTCDataChannel\");\n"
"        }\n"
"\n"
"        super();\n"
"\n"
"        this._target = target;\n"
"\n"
"        if (typeof urlOrChannel === \"string\") {\n"
"            this._url = urlOrChannel;\n"
"        } else {\n"
"            this._url = null;\n"
"            this._rawChannel = urlOrChannel;\n"
"        }\n"
"\n"
"        // Connection details\n"
"        options = options || {};\n"
"        this._rfbCredentials = options.credentials || {};\n"
"        this._shared = 'shared' in options ? !!options.shared : true;\n"
"        this._repeaterID = options.repeaterID || '';\n"
"        this._wsProtocols = options.wsProtocols || [];\n"
"\n"
"        // Internal state\n"
"        this._rfbConnectionState = '';\n"
"        this._rfbInitState = '';\n"
"        this._rfbAuthScheme = -1;\n"
"        this._rfbCleanDisconnect = true;\n"
"        this._rfbRSAAESAuthenticationState = null;\n"
"\n"
"        // Server capabilities\n"
"        this._rfbVersion = 0;\n"
"        this._rfbMaxVersion = 3.8;\n"
"        this._rfbTightVNC = false;\n"
"        this._rfbVeNCryptState = 0;\n"
"        this._rfbXvpVer = 0;\n"
"\n"
"        this._fbWidth = 0;\n"
"        this._fbHeight = 0;\n"
"\n"
"        this._fbName = \"\";\n"
"\n"
"        this._capabilities = { power: false };\n"
"\n"
"        this._supportsFence = false;\n"
"\n"
"        this._supportsContinuousUpdates = false;\n"
"        this._enabledContinuousUpdates = false;\n"
"\n"
"        this._supportsSetDesktopSize = false;\n"
"        this._screenID = 0;\n"
"        this._screenFlags = 0;\n"
"\n"
"        this._qemuExtKeyEventSupported = false;\n"
"\n"
"        this._clipboardText = null;\n"
"        this._clipboardServerCapabilitiesActions = {};\n"
"        this._clipboardServerCapabilitiesFormats = {};\n"
"\n"
"        // Internal objects\n"
"        this._sock = null;              // Websock object\n"
"        this._display = null;           // Display object\n"
"        this._flushing = false;         // Display flushing state\n"
"        this._keyboard = null;          // Keyboard input handler object\n"
"        this._gestures = null;          // Gesture input handler object\n"
"        this._resizeObserver = null;    // Resize observer object\n"
"\n"
"        // Timers\n"
"        this._disconnTimer = null;      // disconnection timer\n"
"        this._resizeTimeout = null;     // resize rate limiting\n"
"        this._mouseMoveTimer = null;\n"
"\n"
"        // Decoder states\n"
"        this._decoders = {};\n"
"\n"
"        this._FBU = {\n"
"            rects: 0,\n"
"            x: 0,\n"
"            y: 0,\n"
"            width: 0,\n"
"            height: 0,\n"
"            encoding: null,\n"
"        };\n"
"\n"
"        // Mouse state\n"
"        this._mousePos = {};\n"
"        this._mouseButtonMask = 0;\n"
"        this._mouseLastMoveTime = 0;\n"
"        this._viewportDragging = false;\n"
"        this._viewportDragPos = {};\n"
"        this._viewportHasMoved = false;\n"
"        this._accumulatedWheelDeltaX = 0;\n"
"        this._accumulatedWheelDeltaY = 0;\n"
"\n"
"        // Gesture state\n"
"        this._gestureLastTapTime = null;\n"
"        this._gestureFirstDoubleTapEv = null;\n"
"        this._gestureLastMagnitudeX = 0;\n"
"        this._gestureLastMagnitudeY = 0;\n"
"\n"
"        // Bound event handlers\n"
"        this._eventHandlers = {\n"
"            focusCanvas: this._focusCanvas.bind(this),\n"
"            handleResize: this._handleResize.bind(this),\n"
"            handleMouse: this._handleMouse.bind(this),\n"
"            handleWheel: this._handleWheel.bind(this),\n"
"            handleGesture: this._handleGesture.bind(this),\n"
"            handleRSAAESCredentialsRequired: this._handleRSAAESCredentialsRequired.bind(this),\n"
"            handleRSAAESServerVerification: this._handleRSAAESServerVerification.bind(this),\n"
"        };\n"
"\n"
"        // main setup\n"
"        Log.Debug(\">> RFB.constructor\");\n"
"\n"
"        // Create DOM elements\n"
"        this._screen = document.createElement('div');\n"
"        this._screen.style.display = 'flex';\n"
"        this._screen.style.width = '100%';\n"
"        this._screen.style.height = '100%';\n"
"        this._screen.style.overflow = 'auto';\n"
"        this._screen.style.background = DEFAULT_BACKGROUND;\n"
"        this._canvas = document.createElement('canvas');\n"
"        this._canvas.style.margin = 'auto';\n"
"        // Some browsers add an outline on focus\n"
"        this._canvas.style.outline = 'none';\n"
"        this._canvas.width = 0;\n"
"        this._canvas.height = 0;\n"
"        this._canvas.tabIndex = -1;\n"
"        this._screen.appendChild(this._canvas);\n"
"\n"
"        // Cursor\n"
"        this._cursor = new Cursor();\n"
"\n"
"        // XXX: TightVNC 2.8.11 sends no cursor at all until Windows changes\n"
"        // it. Result: no cursor at all until a window border or an edit field\n"
"        // is hit blindly. But there are also VNC servers that draw the cursor\n"
"        // in the framebuffer and don't send the empty local cursor. There is\n"
"        // no way to satisfy both sides.\n"
"        //\n"
"        // The spec is unclear on this \"initial cursor\" issue. Many other\n"
"        // viewers (TigerVNC, RealVNC, Remmina) display an arrow as the\n"
"        // initial cursor instead.\n"
"        this._cursorImage = RFB.cursors.none;\n"
"\n"
"        // populate decoder array with objects\n"
"        this._decoders[encodings.encodingRaw] = new RawDecoder();\n"
"        this._decoders[encodings.encodingCopyRect] = new CopyRectDecoder();\n"
"        this._decoders[encodings.encodingRRE] = new RREDecoder();\n"
"        this._decoders[encodings.encodingHextile] = new HextileDecoder();\n"
"        this._decoders[encodings.encodingTight] = new TightDecoder();\n"
"        this._decoders[encodings.encodingTightPNG] = new TightPNGDecoder();\n"
"        this._decoders[encodings.encodingZRLE] = new ZRLEDecoder();\n"
"        this._decoders[encodings.encodingJPEG] = new JPEGDecoder();\n"
"\n"
"        // NB: nothing that needs explicit teardown should be done\n"
"        // before this point, since this can throw an exception\n"
"        try {\n"
"            this._display = new Display(this._canvas);\n"
"        } catch (exc) {\n"
"            Log.Error(\"Display exception: \" + exc);\n"
"            throw exc;\n"
"        }\n"
"        this._display.onflush = this._onFlush.bind(this);\n"
"\n"
"        this._keyboard = new Keyboard(this._canvas);\n"
"        this._keyboard.onkeyevent = this._handleKeyEvent.bind(this);\n"
"\n"
"        this._gestures = new GestureHandler();\n"
"\n"
"        this._sock = new Websock();\n"
"        this._sock.on('open', this._socketOpen.bind(this));\n"
"        this._sock.on('close', this._socketClose.bind(this));\n"
"        this._sock.on('message', this._handleMessage.bind(this));\n"
"        this._sock.on('error', this._socketError.bind(this));\n"
"\n"
"        this._expectedClientWidth = null;\n"
"        this._expectedClientHeight = null;\n"
"        this._resizeObserver = new ResizeObserver(this._eventHandlers.handleResize);\n"
"\n"
"        // All prepared, kick off the connection\n"
"        this._updateConnectionState('connecting');\n"
"\n"
"        Log.Debug(\"<< RFB.constructor\");\n"
"\n"
"        // ===== PROPERTIES =====\n"
"\n"
"        this.dragViewport = false;\n"
"        this.focusOnClick = true;\n"
"\n"
"        this._viewOnly = false;\n"
"        this._clipViewport = false;  // Disable viewport clipping for large screens - let scaling handle it\n"
"        this._clippingViewport = false;\n"
"        this._scaleViewport = true;  // Enable viewport scaling by default for web VNC\n"
"        this._resizeSession = false;\n"
"\n"
"        this._showDotCursor = false;\n"
"        if (options.showDotCursor !== undefined) {\n"
"            Log.Warn(\"Specifying showDotCursor as a RFB constructor argument is deprecated\");\n"
"            this._showDotCursor = options.showDotCursor;\n"
"        }\n"
"\n"
"        this._qualityLevel = 6;\n"
"        this._compressionLevel = 2;\n"
"    }\n"
"\n"
"    // ===== PROPERTIES =====\n"
"\n"
"    get viewOnly() { return this._viewOnly; }\n"
"    set viewOnly(viewOnly) {\n"
"        this._viewOnly = viewOnly;\n"
"\n"
"        if (this._rfbConnectionState === \"connecting\" ||\n"
"            this._rfbConnectionState === \"connected\") {\n"
"            if (viewOnly) {\n"
"                this._keyboard.ungrab();\n"
"            } else {\n"
"                this._keyboard.grab();\n"
"            }\n"
"        }\n"
"    }\n"
"\n"
"    get capabilities() { return this._capabilities; }\n"
"\n"
"    get clippingViewport() { return this._clippingViewport; }\n"
"    _setClippingViewport(on) {\n"
"        if (on === this._clippingViewport) {\n"
"            return;\n"
"        }\n"
"        this._clippingViewport = on;\n"
"        this.dispatchEvent(new CustomEvent(\"clippingviewport\",\n"
"                                           { detail: this._clippingViewport }));\n"
"    }\n"
"\n"
"    get touchButton() { return 0; }\n"
"    set touchButton(button) { Log.Warn(\"Using old API!\"); }\n"
"\n"
"    get clipViewport() { return this._clipViewport; }\n"
"    set clipViewport(viewport) {\n"
"        this._clipViewport = viewport;\n"
"        this._updateClip();\n"
"    }\n"
"\n"
"    get scaleViewport() { return this._scaleViewport; }\n"
"    set scaleViewport(scale) {\n"
"        this._scaleViewport = scale;\n"
"        // Scaling trumps clipping, so we may need to adjust\n"
"        // clipping when enabling or disabling scaling\n"
"        if (scale && this._clipViewport) {\n"
"            this._updateClip();\n"
"        }\n"
"        this._updateScale();\n"
"        if (!scale && this._clipViewport) {\n"
"            this._updateClip();\n"
"        }\n"
"    }\n"
"\n"
"    get resizeSession() { return this._resizeSession; }\n"
"    set resizeSession(resize) {\n"
"        this._resizeSession = resize;\n"
"        if (resize) {\n"
"            this._requestRemoteResize();\n"
"        }\n"
"    }\n"
"\n"
"    get showDotCursor() { return this._showDotCursor; }\n"
"    set showDotCursor(show) {\n"
"        this._showDotCursor = show;\n"
"        this._refreshCursor();\n"
"    }\n"
"\n"
"    get background() { return this._screen.style.background; }\n"
"    set background(cssValue) { this._screen.style.background = cssValue; }\n"
"\n"
"    get qualityLevel() {\n"
"        return this._qualityLevel;\n"
"    }\n"
"    set qualityLevel(qualityLevel) {\n"
"        if (!Number.isInteger(qualityLevel) || qualityLevel < 0 || qualityLevel > 9) {\n"
"            Log.Error(\"qualityLevel must be an integer between 0 and 9\");\n"
"            return;\n"
"        }\n"
"\n"
"        if (this._qualityLevel === qualityLevel) {\n"
"            return;\n"
"        }\n"
"\n"
"        this._qualityLevel = qualityLevel;\n"
"\n"
"        if (this._rfbConnectionState === 'connected') {\n"
"            this._sendEncodings();\n"
"        }\n"
"    }\n"
"\n"
"    get compressionLevel() {\n"
"        return this._compressionLevel;\n"
"    }\n"
"    set compressionLevel(compressionLevel) {\n"
"        if (!Number.isInteger(compressionLevel) || compressionLevel < 0 || compressionLevel > 9) {\n"
"            Log.Error(\"compressionLevel must be an integer between 0 and 9\");\n"
"            return;\n"
"        }\n"
"\n"
"        if (this._compressionLevel === compressionLevel) {\n"
"            return;\n"
"        }\n"
"\n"
"        this._compressionLevel = compressionLevel;\n"
"\n"
"        if (this._rfbConnectionState === 'connected') {\n"
"            this._sendEncodings();\n"
"        }\n"
"    }\n"
"\n"
"    // ===== PUBLIC METHODS =====\n"
"\n"
"    disconnect() {\n"
"        this._updateConnectionState('disconnecting');\n"
"        this._sock.off('error');\n"
"        this._sock.off('message');\n"
"        this._sock.off('open');\n"
"        if (this._rfbRSAAESAuthenticationState !== null) {\n"
"            this._rfbRSAAESAuthenticationState.disconnect();\n"
"        }\n"
"    }\n"
"\n"
"    approveServer() {\n"
"        if (this._rfbRSAAESAuthenticationState !== null) {\n"
"            this._rfbRSAAESAuthenticationState.approveServer();\n"
"        }\n"
"    }\n"
"\n"
"    sendCredentials(creds) {\n"
"        this._rfbCredentials = creds;\n"
"        this._resumeAuthentication();\n"
"    }\n"
"\n"
"    sendCtrlAltDel() {\n"
"        if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; }\n"
"        Log.Info(\"Sending Ctrl-Alt-Del\");\n"
"\n"
"        this.sendKey(KeyTable.XK_Control_L, \"ControlLeft\", true);\n"
"        this.sendKey(KeyTable.XK_Alt_L, \"AltLeft\", true);\n"
"        this.sendKey(KeyTable.XK_Delete, \"Delete\", true);\n"
"        this.sendKey(KeyTable.XK_Delete, \"Delete\", false);\n"
"        this.sendKey(KeyTable.XK_Alt_L, \"AltLeft\", false);\n"
"        this.sendKey(KeyTable.XK_Control_L, \"ControlLeft\", false);\n"
"    }\n"
"\n"
"    machineShutdown() {\n"
"        this._xvpOp(1, 2);\n"
"    }\n"
"\n"
"    machineReboot() {\n"
"        this._xvpOp(1, 3);\n"
"    }\n"
"\n"
"    machineReset() {\n"
"        this._xvpOp(1, 4);\n"
"    }\n"
"\n"
"    // Send a key press. If 'down' is not specified then send a down key\n"
"    // followed by an up key.\n"
"    sendKey(keysym, code, down) {\n"
"        if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; }\n"
"\n"
"        if (down === undefined) {\n"
"            this.sendKey(keysym, code, true);\n"
"            this.sendKey(keysym, code, false);\n"
"            return;\n"
"        }\n"
"\n"
"        const scancode = XtScancode[code];\n"
"\n"
"        if (this._qemuExtKeyEventSupported && scancode) {\n"
"            // 0 is NoSymbol\n"
"            keysym = keysym || 0;\n"
"\n"
"            Log.Info(\"Sending key (\" + (down ? \"down\" : \"up\") + \"): keysym \" + keysym + \", scancode \" + scancode);\n"
"\n"
"            RFB.messages.QEMUExtendedKeyEvent(this._sock, keysym, down, scancode);\n"
"        } else {\n"
"            if (!keysym) {\n"
"                return;\n"
"            }\n"
"            Log.Info(\"Sending keysym (\" + (down ? \"down\" : \"up\") + \"): \" + keysym);\n"
"            RFB.messages.keyEvent(this._sock, keysym, down ? 1 : 0);\n"
"        }\n"
"    }\n"
"\n"
"    focus(options) {\n"
"        this._canvas.focus(options);\n"
"    }\n"
"\n"
"    blur() {\n"
"        this._canvas.blur();\n"
"    }\n"
"\n"
"    clipboardPasteFrom(text) {\n"
"        if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; }\n"
"\n"
"        if (this._clipboardServerCapabilitiesFormats[extendedClipboardFormatText] &&\n"
"            this._clipboardServerCapabilitiesActions[extendedClipboardActionNotify]) {\n"
"\n"
"            this._clipboardText = text;\n"
"            RFB.messages.extendedClipboardNotify(this._sock, [extendedClipboardFormatText]);\n"
"        } else {\n"
"            let length, i;\n"
"            let data;\n"
"\n"
"            length = 0;\n"
"            // eslint-disable-next-line no-unused-vars\n"
"            for (let codePoint of text) {\n"
"                length++;\n"
"            }\n"
"\n"
"            data = new Uint8Array(length);\n"
"\n"
"            i = 0;\n"
"            for (let codePoint of text) {\n"
"                let code = codePoint.codePointAt(0);\n"
"\n"
"                /* Only ISO 8859-1 is supported */\n"
"                if (code > 0xff) {\n"
"                    code = 0x3f; // '?'\n"
"                }\n"
"\n"
"                data[i++] = code;\n"
"            }\n"
"\n"
"            RFB.messages.clientCutText(this._sock, data);\n"
"        }\n"
"    }\n"
"\n"
"    getImageData() {\n"
"        return this._display.getImageData();\n"
"    }\n"
"\n"
"    toDataURL(type, encoderOptions) {\n"
"        return this._display.toDataURL(type, encoderOptions);\n"
"    }\n"
"\n"
"    toBlob(callback, type, quality) {\n"
"        return this._display.toBlob(callback, type, quality);\n"
"    }\n"
"\n"
"    // ===== PRIVATE METHODS =====\n"
"\n"
"    _connect() {\n"
"        console.log(\"[VNC-CLIENT] _connect() called\");\n"
"        console.log(\"[VNC-CLIENT] URL:\", this._url);\n"
"        console.log(\"[VNC-CLIENT] wsProtocols:\", this._wsProtocols);\n"
"        console.log(\"[VNC-CLIENT] rawChannel:\", this._rawChannel);\n"
"\n"
"        Log.Debug(\">> RFB.connect\");\n"
"\n"
"        if (this._url) {\n"
"            console.log(\"[VNC-CLIENT] Opening WebSocket connection to:\", this._url);\n"
"            Log.Info(`connecting to ${this._url}`);\n"
"            this._sock.open(this._url, this._wsProtocols);\n"
"            console.log(\"[VNC-CLIENT] WebSocket.open() called with protocols:\", this._wsProtocols);\n"
"        } else {\n"
"            console.log(\"[VNC-CLIENT] Attaching to raw channel\");\n"
"            Log.Info(`attaching ${this._rawChannel} to Websock`);\n"
"            this._sock.attach(this._rawChannel);\n"
"\n"
"            if (this._sock.readyState === 'closed') {\n"
"                console.log(\"[VNC-CLIENT] Raw channel is closed - cannot use\");\n"
"                throw Error(\"Cannot use already closed WebSocket/RTCDataChannel\");\n"
"            }\n"
"\n"
"            if (this._sock.readyState === 'open') {\n"
"                console.log(\"[VNC-CLIENT] Raw channel is already open - calling _socketOpen()\");\n"
"                // FIXME: _socketOpen() can in theory call _fail(), which\n"
"                //        isn't allowed this early, but I'm not sure that can\n"
"                //        happen without a bug messing up our state variables\n"
"                this._socketOpen();\n"
"            } else {\n"
"                console.log(\"[VNC-CLIENT] Raw channel readyState:\", this._sock.readyState);\n"
"            }\n"
"        }\n"
"\n"
"        // Make our elements part of the page\n"
"        this._target.appendChild(this._screen);\n"
"\n"
"        this._gestures.attach(this._canvas);\n"
"\n"
"        this._cursor.attach(this._canvas);\n"
"        this._refreshCursor();\n"
"\n"
"        // Monitor size changes of the screen element\n"
"        this._resizeObserver.observe(this._screen);\n"
"\n"
"        // Always grab focus on some kind of click event\n"
"        this._canvas.addEventListener(\"mousedown\", this._eventHandlers.focusCanvas);\n"
"        this._canvas.addEventListener(\"touchstart\", this._eventHandlers.focusCanvas);\n"
"\n"
"        // Mouse events\n"
"        this._canvas.addEventListener('mousedown', this._eventHandlers.handleMouse);\n"
"        this._canvas.addEventListener('mouseup', this._eventHandlers.handleMouse);\n"
"        this._canvas.addEventListener('mousemove', this._eventHandlers.handleMouse);\n"
"        // Prevent middle-click pasting (see handler for why we bind to document)\n"
"        this._canvas.addEventListener('click', this._eventHandlers.handleMouse);\n"
"        // preventDefault() on mousedown doesn't stop this event for some\n"
"        // reason so we have to explicitly block it\n"
"        this._canvas.addEventListener('contextmenu', this._eventHandlers.handleMouse);\n"
"\n"
"        // Wheel events\n"
"        this._canvas.addEventListener(\"wheel\", this._eventHandlers.handleWheel);\n"
"\n"
"        // Gesture events\n"
"        this._canvas.addEventListener(\"gesturestart\", this._eventHandlers.handleGesture);\n"
"        this._canvas.addEventListener(\"gesturemove\", this._eventHandlers.handleGesture);\n"
"        this._canvas.addEventListener(\"gestureend\", this._eventHandlers.handleGesture);\n"
"\n"
"        Log.Debug(\"<< RFB.connect\");\n"
"    }\n"
"\n"
"    _disconnect() {\n"
"        Log.Debug(\">> RFB.disconnect\");\n"
"        this._cursor.detach();\n"
"        this._canvas.removeEventListener(\"gesturestart\", this._eventHandlers.handleGesture);\n"
"        this._canvas.removeEventListener(\"gesturemove\", this._eventHandlers.handleGesture);\n"
"        this._canvas.removeEventListener(\"gestureend\", this._eventHandlers.handleGesture);\n"
"        this._canvas.removeEventListener(\"wheel\", this._eventHandlers.handleWheel);\n"
"        this._canvas.removeEventListener('mousedown', this._eventHandlers.handleMouse);\n"
"        this._canvas.removeEventListener('mouseup', this._eventHandlers.handleMouse);\n"
"        this._canvas.removeEventListener('mousemove', this._eventHandlers.handleMouse);\n"
"        this._canvas.removeEventListener('click', this._eventHandlers.handleMouse);\n"
"        this._canvas.removeEventListener('contextmenu', this._eventHandlers.handleMouse);\n"
"        this._canvas.removeEventListener(\"mousedown\", this._eventHandlers.focusCanvas);\n"
"        this._canvas.removeEventListener(\"touchstart\", this._eventHandlers.focusCanvas);\n"
"        this._resizeObserver.disconnect();\n"
"        this._keyboard.ungrab();\n"
"        this._gestures.detach();\n"
"        this._sock.close();\n"
"        try {\n"
"            this._target.removeChild(this._screen);\n"
"        } catch (e) {\n"
"            if (e.name === 'NotFoundError') {\n"
"                // Some cases where the initial connection fails\n"
"                // can disconnect before the _screen is created\n"
"            } else {\n"
"                throw e;\n"
"            }\n"
"        }\n"
"        clearTimeout(this._resizeTimeout);\n"
"        clearTimeout(this._mouseMoveTimer);\n"
"        Log.Debug(\"<< RFB.disconnect\");\n"
"    }\n"
"\n"
"    _socketOpen() {\n"
"        console.log(\"[VNC-CLIENT] WebSocket opened, connectionState:\", this._rfbConnectionState, \"initState:\", this._rfbInitState);\n"
"        console.log(\"[VNC-CLIENT] WebSocket URL:\", this._url);\n"
"        console.log(\"[VNC-CLIENT] WebSocket readyState:\", this._sock.readyState);\n"
"        console.log(\"[VNC-CLIENT] WebSocket protocol:\", this._sock.protocol);\n"
"        console.log(\"[VNC-CLIENT] WebSocket binaryType:\", this._sock.binaryType);\n"
"        console.log(\"[VNC-CLIENT] WebSocket bufferedAmount:\", this._sock.bufferedAmount);\n"
"        console.log(\"[VNC-CLIENT] WebSocket extensions:\", this._sock.extensions);\n"
"\n"
"        if ((this._rfbConnectionState === 'connecting') &&\n"
"            (this._rfbInitState === '')) {\n"
"            this._rfbInitState = 'ProtocolVersion';\n"
"            Log.Debug(\"Starting VNC handshake\");\n"
"            console.log(\"[VNC-CLIENT] Starting VNC handshake, set init state to ProtocolVersion\");\n"
"        } else {\n"
"            console.log(\"[VNC-CLIENT] Unexpected socket open - connectionState:\", this._rfbConnectionState, \"initState:\", this._rfbInitState);\n"
"            this._fail(\"Unexpected server connection while \" +\n"
"                        this._rfbConnectionState);\n"
"        }\n"
"    }\n"
"\n"
"    _socketClose(e) {\n"
"        console.log(\"[VNC-CLIENT] WebSocket on-close event\");\n"
"        console.log(\"[VNC-CLIENT] Close event details:\", e);\n"
"        console.log(\"[VNC-CLIENT] Close code:\", e.code);\n"
"        console.log(\"[VNC-CLIENT] Close reason:\", e.reason);\n"
"        console.log(\"[VNC-CLIENT] Close wasClean:\", e.wasClean);\n"
"        console.log(\"[VNC-CLIENT] Current connectionState:\", this._rfbConnectionState);\n"
"        console.log(\"[VNC-CLIENT] Current initState:\", this._rfbInitState);\n"
"        console.log(\"[VNC-CLIENT] WebSocket readyState:\", this._sock.readyState);\n"
"\n"
"        Log.Debug(\"WebSocket on-close event\");\n"
"        let msg = \"\";\n"
"        if (e.code) {\n"
"            msg = \"(code: \" + e.code;\n"
"            if (e.reason) {\n"
"                msg += \", reason: \" + e.reason;\n"
"            }\n"
"            msg += \")\";\n"
"        }\n"
"        console.log(\"[VNC-CLIENT] Connection close message:\", msg);\n"
"\n"
"        switch (this._rfbConnectionState) {\n"
"            case 'connecting':\n"
"                console.log(\"[VNC-CLIENT] Connection closed while connecting\");\n"
"                this._fail(\"Connection closed \" + msg);\n"
"                break;\n"
"            case 'connected':\n"
"                console.log(\"[VNC-CLIENT] Connection closed while connected - server-side disconnect\");\n"
"                // Handle disconnects that were initiated server-side\n"
"                this._updateConnectionState('disconnecting');\n"
"                this._updateConnectionState('disconnected');\n"
"                break;\n"
"            case 'disconnecting':\n"
"                console.log(\"[VNC-CLIENT] Connection closed while disconnecting - normal path\");\n"
"                // Normal disconnection path\n"
"                this._updateConnectionState('disconnected');\n"
"                break;\n"
"            case 'disconnected':\n"
"                console.log(\"[VNC-CLIENT] Connection closed while already disconnected - unexpected\");\n"
"                this._fail(\"Unexpected server disconnect \" +\n"
"                            \"when already disconnected \" + msg);\n"
"                break;\n"
"            default:\n"
"                console.log(\"[VNC-CLIENT] Connection closed in unknown state:\", this._rfbConnectionState);\n"
"                this._fail(\"Unexpected server disconnect before connecting \" +\n"
"                            msg);\n"
"                break;\n"
"        }\n"
"        this._sock.off('close');\n"
"        // Delete reference to raw channel to allow cleanup.\n"
"        this._rawChannel = null;\n"
"    }\n"
"\n"
"    _socketError(e) {\n"
"        console.log(\"[VNC-CLIENT] WebSocket on-error event\");\n"
"        console.log(\"[VNC-CLIENT] Error event details:\", e);\n"
"        console.log(\"[VNC-CLIENT] Current connectionState:\", this._rfbConnectionState);\n"
"        console.log(\"[VNC-CLIENT] Current initState:\", this._rfbInitState);\n"
"        console.log(\"[VNC-CLIENT] WebSocket readyState:\", this._sock.readyState);\n"
"        console.log(\"[VNC-CLIENT] WebSocket URL:\", this._url);\n"
"        console.log(\"[VNC-CLIENT] WebSocket protocol:\", this._sock.protocol);\n"
"\n"
"        Log.Warn(\"WebSocket on-error event\");\n"
"    }\n"
"\n"
"    _focusCanvas(event) {\n"
"        if (!this.focusOnClick) {\n"
"            return;\n"
"        }\n"
"\n"
"        this.focus({ preventScroll: true });\n"
"    }\n"
"\n"
"    _setDesktopName(name) {\n"
"        this._fbName = name;\n"
"        this.dispatchEvent(new CustomEvent(\n"
"            \"desktopname\",\n"
"            { detail: { name: this._fbName } }));\n"
"    }\n"
"\n"
"    _saveExpectedClientSize() {\n"
"        this._expectedClientWidth = this._screen.clientWidth;\n"
"        this._expectedClientHeight = this._screen.clientHeight;\n"
"    }\n"
"\n"
"    _currentClientSize() {\n"
"        return [this._screen.clientWidth, this._screen.clientHeight];\n"
"    }\n"
"\n"
"    _clientHasExpectedSize() {\n"
"        const [currentWidth, currentHeight] = this._currentClientSize();\n"
"        return currentWidth == this._expectedClientWidth &&\n"
"            currentHeight == this._expectedClientHeight;\n"
"    }\n"
"\n"
"    _handleResize() {\n"
"        // Don't change anything if the client size is already as expected\n"
"        if (this._clientHasExpectedSize()) {\n"
"            return;\n"
"        }\n"
"        // If the window resized then our screen element might have\n"
"        // as well. Update the viewport dimensions.\n"
"        window.requestAnimationFrame(() => {\n"
"            this._updateClip();\n"
"            this._updateScale();\n"
"        });\n"
"\n"
"        if (this._resizeSession) {\n"
"            // Request changing the resolution of the remote display to\n"
"            // the size of the local browser viewport.\n"
"\n"
"            // In order to not send multiple requests before the browser-resize\n"
"            // is finished we wait 0.5 seconds before sending the request.\n"
"            clearTimeout(this._resizeTimeout);\n"
"            this._resizeTimeout = setTimeout(this._requestRemoteResize.bind(this), 500);\n"
"        }\n"
"    }\n"
"\n"
"    // Update state of clipping in Display object, and make sure the\n"
"    // configured viewport matches the current screen size\n"
"    _updateClip() {\n"
"        const curClip = this._display.clipViewport;\n"
"        let newClip = this._clipViewport;\n"
"\n"
"        if (this._scaleViewport) {\n"
"            // Disable viewport clipping if we are scaling\n"
"            newClip = false;\n"
"        }\n"
"\n"
"        if (curClip !== newClip) {\n"
"            this._display.clipViewport = newClip;\n"
"        }\n"
"\n"
"        if (newClip) {\n"
"            // When clipping is enabled, the screen is limited to\n"
"            // the size of the container.\n"
"            const size = this._screenSize();\n"
"            this._display.viewportChangeSize(size.w, size.h);\n"
"            this._fixScrollbars();\n"
"            this._setClippingViewport(size.w < this._display.width ||\n"
"                                      size.h < this._display.height);\n"
"        } else {\n"
"            this._setClippingViewport(false);\n"
"        }\n"
"\n"
"        // When changing clipping we might show or hide scrollbars.\n"
"        // This causes the expected client dimensions to change.\n"
"        if (curClip !== newClip) {\n"
"            this._saveExpectedClientSize();\n"
"        }\n"
"    }\n"
"\n"
"    _updateScale() {\n"
"        if (!this._scaleViewport) {\n"
"            this._display.scale = 1.0;\n"
"        } else {\n"
"            const size = this._screenSize();\n"
"            this._display.autoscale(size.w, size.h);\n"
"        }\n"
"        this._fixScrollbars();\n"
"    }\n"
"\n"
"    // Requests a change of remote desktop size. This message is an extension\n"
"    // and may only be sent if we have received an ExtendedDesktopSize message\n"
"    _requestRemoteResize() {\n"
"        clearTimeout(this._resizeTimeout);\n"
"        this._resizeTimeout = null;\n"
"\n"
"        if (!this._resizeSession || this._viewOnly ||\n"
"            !this._supportsSetDesktopSize) {\n"
"            return;\n"
"        }\n"
"\n"
"        const size = this._screenSize();\n"
"\n"
"        RFB.messages.setDesktopSize(this._sock,\n"
"                                    Math.floor(size.w), Math.floor(size.h),\n"
"                                    this._screenID, this._screenFlags);\n"
"\n"
"        Log.Debug('Requested new desktop size: ' +\n"
"                   size.w + 'x' + size.h);\n"
"    }\n"
"\n"
"    // Gets the the size of the available screen\n"
"    _screenSize() {\n"
"        let r = this._screen.getBoundingClientRect();\n"
"        return { w: r.width, h: r.height };\n"
"    }\n"
"\n"
"    _fixScrollbars() {\n"
"        // This is a hack because Safari on macOS screws up the calculation\n"
"        // for when scrollbars are needed. We get scrollbars when making the\n"
"        // browser smaller, despite remote resize being enabled. So to fix it\n"
"        // we temporarily toggle them off and on.\n"
"        const orig = this._screen.style.overflow;\n"
"        this._screen.style.overflow = 'hidden';\n"
"        // Force Safari to recalculate the layout by asking for\n"
"        // an element's dimensions\n"
"        this._screen.getBoundingClientRect();\n"
"        this._screen.style.overflow = orig;\n"
"    }\n"
"\n"
"    /*\n"
"     * Connection states:\n"
"     *   connecting\n"
"     *   connected\n"
"     *   disconnecting\n"
"     *   disconnected - permanent state\n"
"     */\n"
"    _updateConnectionState(state) {\n"
"        const oldstate = this._rfbConnectionState;\n"
"        // console.log(\"[VNC-DEBUG] _updateConnectionState called:\", oldstate, \"->\", state);\n"
"\n"
"        if (state === oldstate) {\n"
"            Log.Debug(\"Already in state '\" + state + \"', ignoring\");\n"
"            // console.log(\"[VNC-DEBUG] Already in state '\" + state + \"', ignoring\");\n"
"            return;\n"
"        }\n"
"\n"
"        // The 'disconnected' state is permanent for each RFB object\n"
"        if (oldstate === 'disconnected') {\n"
"            Log.Error(\"Tried changing state of a disconnected RFB object\");\n"
"            return;\n"
"        }\n"
"\n"
"        // Ensure proper transitions before doing anything\n"
"        switch (state) {\n"
"            case 'connected':\n"
"                if (oldstate !== 'connecting') {\n"
"                    Log.Error(\"Bad transition to connected state, \" +\n"
"                               \"previous connection state: \" + oldstate);\n"
"                    return;\n"
"                }\n"
"                break;\n"
"\n"
"            case 'disconnected':\n"
"                if (oldstate !== 'disconnecting') {\n"
"                    Log.Error(\"Bad transition to disconnected state, \" +\n"
"                               \"previous connection state: \" + oldstate);\n"
"                    return;\n"
"                }\n"
"                break;\n"
"\n"
"            case 'connecting':\n"
"                if (oldstate !== '') {\n"
"                    Log.Error(\"Bad transition to connecting state, \" +\n"
"                               \"previous connection state: \" + oldstate);\n"
"                    return;\n"
"                }\n"
"                break;\n"
"\n"
"            case 'disconnecting':\n"
"                if (oldstate !== 'connected' && oldstate !== 'connecting') {\n"
"                    Log.Error(\"Bad transition to disconnecting state, \" +\n"
"                               \"previous connection state: \" + oldstate);\n"
"                    return;\n"
"                }\n"
"                break;\n"
"\n"
"            default:\n"
"                Log.Error(\"Unknown connection state: \" + state);\n"
"                return;\n"
"        }\n"
"\n"
"        // State change actions\n"
"\n"
"        this._rfbConnectionState = state;\n"
"\n"
"        Log.Debug(\"New state '\" + state + \"', was '\" + oldstate + \"'.\");\n"
"\n"
"        if (this._disconnTimer && state !== 'disconnecting') {\n"
"            Log.Debug(\"Clearing disconnect timer\");\n"
"            clearTimeout(this._disconnTimer);\n"
"            this._disconnTimer = null;\n"
"\n"
"            // make sure we don't get a double event\n"
"            this._sock.off('close');\n"
"        }\n"
"\n"
"        switch (state) {\n"
"            case 'connecting':\n"
"                this._connect();\n"
"                break;\n"
"\n"
"            case 'connected':\n"
"                this.dispatchEvent(new CustomEvent(\"connect\", { detail: {} }));\n"
"                break;\n"
"\n"
"            case 'disconnecting':\n"
"                this._disconnect();\n"
"\n"
"                this._disconnTimer = setTimeout(() => {\n"
"                    Log.Error(\"Disconnection timed out.\");\n"
"                    this._updateConnectionState('disconnected');\n"
"                }, DISCONNECT_TIMEOUT * 1000);\n"
"                break;\n"
"\n"
"            case 'disconnected':\n"
"                this.dispatchEvent(new CustomEvent(\n"
"                    \"disconnect\", { detail:\n"
"                                    { clean: this._rfbCleanDisconnect } }));\n"
"                break;\n"
"        }\n"
"    }\n"
"\n"
"    /* Print errors and disconnect\n"
"     *\n"
"     * The parameter 'details' is used for information that\n"
"     * should be logged but not sent to the user interface.\n"
"     */\n"
"    _fail(details) {\n"
"        switch (this._rfbConnectionState) {\n"
"            case 'disconnecting':\n"
"                Log.Error(\"Failed when disconnecting: \" + details);\n"
"                break;\n"
"            case 'connected':\n"
"                Log.Error(\"Failed while connected: \" + details);\n"
"                break;\n"
"            case 'connecting':\n"
"                Log.Error(\"Failed when connecting: \" + details);\n"
"                break;\n"
"            default:\n"
"                Log.Error(\"RFB failure: \" + details);\n"
"                break;\n"
"        }\n"
"        this._rfbCleanDisconnect = false; //This is sent to the UI\n"
"\n"
"        // Transition to disconnected without waiting for socket to close\n"
"        this._updateConnectionState('disconnecting');\n"
"        this._updateConnectionState('disconnected');\n"
"\n"
"        return false;\n"
"    }\n"
"\n"
"    _setCapability(cap, val) {\n"
"        this._capabilities[cap] = val;\n"
"        this.dispatchEvent(new CustomEvent(\"capabilities\",\n"
"                                           { detail: { capabilities: this._capabilities } }));\n"
"    }\n"
"\n"
"    _handleMessage() {\n"
"        console.log(\"[VNC-CLIENT] _handleMessage called\");\n"
"        console.log(\"[VNC-CLIENT] Current connectionState:\", this._rfbConnectionState);\n"
"        console.log(\"[VNC-CLIENT] Current initState:\", this._rfbInitState);\n"
"        console.log(\"[VNC-CLIENT] WebSocket rQlen:\", this._sock.rQlen);\n"
"        console.log(\"[VNC-CLIENT] WebSocket readyState:\", this._sock.readyState);\n"
"\n"
"        if (this._sock.rQlen === 0) {\n"
"            console.log(\"[VNC-CLIENT] Empty receive queue - no data to process\");\n"
"            Log.Warn(\"handleMessage called on an empty receive queue\");\n"
"            return;\n"
"        }\n"
"\n"
"        // Debug: log received data\n"
"        const debugLen = Math.min(50, this._sock.rQlen);\n"
"        const debugData = new Uint8Array(this._sock.rQ.buffer, this._sock.rQi, debugLen);\n"
"        console.log(\"[VNC-CLIENT] Received data (first\", debugLen, \"bytes):\", Array.from(debugData).map(b => b.toString(16).padStart(2, '0')).join(' '));\n"
"\n"
"        console.log(\"[VNC-CLIENT] Processing message in state:\", this._rfbConnectionState);\n"
"\n"
"        switch (this._rfbConnectionState) {\n"
"            case 'disconnected':\n"
"                console.log(\"[VNC-CLIENT] Received data while disconnected - ignoring\");\n"
"                Log.Error(\"Got data while disconnected\");\n"
"                break;\n"
"            case 'connected':\n"
"                console.log(\"[VNC-CLIENT] Processing messages in connected state\");\n"
"                let msgCount = 0;\n"
"                while (true) {\n"
"                    if (this._flushing) {\n"
"                        console.log(\"[VNC-CLIENT] Flushing in progress, breaking message loop\");\n"
"                        break;\n"
"                    }\n"
"                    console.log(\"[VNC-CLIENT] Processing message\", ++msgCount, \"in connected state\");\n"
"                    if (!this._normalMsg()) {\n"
"                        console.log(\"[VNC-CLIENT] _normalMsg returned false, breaking\");\n"
"                        break;\n"
"                    }\n"
"                    if (this._sock.rQlen === 0) {\n"
"                        console.log(\"[VNC-CLIENT] No more data in queue\");\n"
"                        break;\n"
"                    }\n"
"                }\n"
"                break;\n"
"            case 'connecting':\n"
"                console.log(\"[VNC-CLIENT] Processing messages in connecting state\");\n"
"                while (this._rfbConnectionState === 'connecting') {\n"
"                    console.log(\"[VNC-CLIENT] Calling _initMsg in connecting state\");\n"
"                    if (!this._initMsg()) {\n"
"                        console.log(\"[VNC-CLIENT] _initMsg returned false, breaking\");\n"
"                        break;\n"
"                    }\n"
"                }\n"
"                break;\n"
"            default:\n"
"                console.log(\"[VNC-CLIENT] Received data in invalid state:\", this._rfbConnectionState);\n"
"                Log.Error(\"Got data while in an invalid state\");\n"
"                break;\n"
"        }\n"
"    }\n"
"\n"
"    _handleKeyEvent(keysym, code, down) {\n"
"        this.sendKey(keysym, code, down);\n"
"    }\n"
"\n"
"    _handleMouse(ev) {\n"
"        /*\n"
"         * We don't check connection status or viewOnly here as the\n"
"         * mouse events might be used to control the viewport\n"
"         */\n"
"\n"
"        if (ev.type === 'click') {\n"
"            /*\n"
"             * Note: This is only needed for the 'click' event as it fails\n"
"             *       to fire properly for the target element so we have\n"
"             *       to listen on the document element instead.\n"
"             */\n"
"            if (ev.target !== this._canvas) {\n"
"                return;\n"
"            }\n"
"        }\n"
"\n"
"        // FIXME: if we're in view-only and not dragging,\n"
"        //        should we stop events?\n"
"        ev.stopPropagation();\n"
"        ev.preventDefault();\n"
"\n"
"        if ((ev.type === 'click') || (ev.type === 'contextmenu')) {\n"
"            return;\n"
"        }\n"
"\n"
"        let pos = clientToElement(ev.clientX, ev.clientY,\n"
"                                  this._canvas);\n"
"\n"
"        switch (ev.type) {\n"
"            case 'mousedown':\n"
"                setCapture(this._canvas);\n"
"                this._handleMouseButton(pos.x, pos.y,\n"
"                                        true, 1 << ev.button);\n"
"                break;\n"
"            case 'mouseup':\n"
"                this._handleMouseButton(pos.x, pos.y,\n"
"                                        false, 1 << ev.button);\n"
"                break;\n"
"            case 'mousemove':\n"
"                this._handleMouseMove(pos.x, pos.y);\n"
"                break;\n"
"        }\n"
"    }\n"
"\n"
"    _handleMouseButton(x, y, down, bmask) {\n"
"        if (this.dragViewport) {\n"
"            if (down && !this._viewportDragging) {\n"
"                this._viewportDragging = true;\n"
"                this._viewportDragPos = {'x': x, 'y': y};\n"
"                this._viewportHasMoved = false;\n"
"\n"
"                // Skip sending mouse events\n"
"                return;\n"
"            } else {\n"
"                this._viewportDragging = false;\n"
"\n"
"                // If we actually performed a drag then we are done\n"
"                // here and should not send any mouse events\n"
"                if (this._viewportHasMoved) {\n"
"                    return;\n"
"                }\n"
"\n"
"                // Otherwise we treat this as a mouse click event.\n"
"                // Send the button down event here, as the button up\n"
"                // event is sent at the end of this function.\n"
"                this._sendMouse(x, y, bmask);\n"
"            }\n"
"        }\n"
"\n"
"        // Flush waiting move event first\n"
"        if (this._mouseMoveTimer !== null) {\n"
"            clearTimeout(this._mouseMoveTimer);\n"
"            this._mouseMoveTimer = null;\n"
"            this._sendMouse(x, y, this._mouseButtonMask);\n"
"        }\n"
"\n"
"        if (down) {\n"
"            this._mouseButtonMask |= bmask;\n"
"        } else {\n"
"            this._mouseButtonMask &= ~bmask;\n"
"        }\n"
"\n"
"        this._sendMouse(x, y, this._mouseButtonMask);\n"
"    }\n"
"\n"
"    _handleMouseMove(x, y) {\n"
"        if (this._viewportDragging) {\n"
"            const deltaX = this._viewportDragPos.x - x;\n"
"            const deltaY = this._viewportDragPos.y - y;\n"
"\n"
"            if (this._viewportHasMoved || (Math.abs(deltaX) > dragThreshold ||\n"
"                                           Math.abs(deltaY) > dragThreshold)) {\n"
"                this._viewportHasMoved = true;\n"
"\n"
"                this._viewportDragPos = {'x': x, 'y': y};\n"
"                this._display.viewportChangePos(deltaX, deltaY);\n"
"            }\n"
"\n"
"            // Skip sending mouse events\n"
"            return;\n"
"        }\n"
"\n"
"        this._mousePos = { 'x': x, 'y': y };\n"
"\n"
"        // Limit many mouse move events to one every MOUSE_MOVE_DELAY ms\n"
"        if (this._mouseMoveTimer == null) {\n"
"\n"
"            const timeSinceLastMove = Date.now() - this._mouseLastMoveTime;\n"
"            if (timeSinceLastMove > MOUSE_MOVE_DELAY) {\n"
"                this._sendMouse(x, y, this._mouseButtonMask);\n"
"                this._mouseLastMoveTime = Date.now();\n"
"            } else {\n"
"                // Too soon since the latest move, wait the remaining time\n"
"                this._mouseMoveTimer = setTimeout(() => {\n"
"                    this._handleDelayedMouseMove();\n"
"                }, MOUSE_MOVE_DELAY - timeSinceLastMove);\n"
"            }\n"
"        }\n"
"    }\n"
"\n"
"    _handleDelayedMouseMove() {\n"
"        this._mouseMoveTimer = null;\n"
"        this._sendMouse(this._mousePos.x, this._mousePos.y,\n"
"                        this._mouseButtonMask);\n"
"        this._mouseLastMoveTime = Date.now();\n"
"    }\n"
"\n"
"    _sendMouse(x, y, mask) {\n"
"        if (this._rfbConnectionState !== 'connected') { return; }\n"
"        if (this._viewOnly) { return; } // View only, skip mouse events\n"
"\n"
"        RFB.messages.pointerEvent(this._sock, this._display.absX(x),\n"
"                                  this._display.absY(y), mask);\n"
"    }\n"
"\n"
"    _handleWheel(ev) {\n"
"        if (this._rfbConnectionState !== 'connected') { return; }\n"
"        if (this._viewOnly) { return; } // View only, skip mouse events\n"
"\n"
"        ev.stopPropagation();\n"
"        ev.preventDefault();\n"
"\n"
"        let pos = clientToElement(ev.clientX, ev.clientY,\n"
"                                  this._canvas);\n"
"\n"
"        let dX = ev.deltaX;\n"
"        let dY = ev.deltaY;\n"
"\n"
"        // Pixel units unless it's non-zero.\n"
"        // Note that if deltamode is line or page won't matter since we aren't\n"
"        // sending the mouse wheel delta to the server anyway.\n"
"        // The difference between pixel and line can be important however since\n"
"        // we have a threshold that can be smaller than the line height.\n"
"        if (ev.deltaMode !== 0) {\n"
"            dX *= WHEEL_LINE_HEIGHT;\n"
"            dY *= WHEEL_LINE_HEIGHT;\n"
"        }\n"
"\n"
"        // Mouse wheel events are sent in steps over VNC. This means that the VNC\n"
"        // protocol can't handle a wheel event with specific distance or speed.\n"
"        // Therefor, if we get a lot of small mouse wheel events we combine them.\n"
"        this._accumulatedWheelDeltaX += dX;\n"
"        this._accumulatedWheelDeltaY += dY;\n"
"\n"
"        // Generate a mouse wheel step event when the accumulated delta\n"
"        // for one of the axes is large enough.\n"
"        if (Math.abs(this._accumulatedWheelDeltaX) >= WHEEL_STEP) {\n"
"            if (this._accumulatedWheelDeltaX < 0) {\n"
"                this._handleMouseButton(pos.x, pos.y, true, 1 << 5);\n"
"                this._handleMouseButton(pos.x, pos.y, false, 1 << 5);\n"
"            } else if (this._accumulatedWheelDeltaX > 0) {\n"
"                this._handleMouseButton(pos.x, pos.y, true, 1 << 6);\n"
"                this._handleMouseButton(pos.x, pos.y, false, 1 << 6);\n"
"            }\n"
"\n"
"            this._accumulatedWheelDeltaX = 0;\n"
"        }\n"
"        if (Math.abs(this._accumulatedWheelDeltaY) >= WHEEL_STEP) {\n"
"            if (this._accumulatedWheelDeltaY < 0) {\n"
"                this._handleMouseButton(pos.x, pos.y, true, 1 << 3);\n"
"                this._handleMouseButton(pos.x, pos.y, false, 1 << 3);\n"
"            } else if (this._accumulatedWheelDeltaY > 0) {\n"
"                this._handleMouseButton(pos.x, pos.y, true, 1 << 4);\n"
"                this._handleMouseButton(pos.x, pos.y, false, 1 << 4);\n"
"            }\n"
"\n"
"            this._accumulatedWheelDeltaY = 0;\n"
"        }\n"
"    }\n"
"\n"
"    _fakeMouseMove(ev, elementX, elementY) {\n"
"        this._handleMouseMove(elementX, elementY);\n"
"        this._cursor.move(ev.detail.clientX, ev.detail.clientY);\n"
"    }\n"
"\n"
"    _handleTapEvent(ev, bmask) {\n"
"        let pos = clientToElement(ev.detail.clientX, ev.detail.clientY,\n"
"                                  this._canvas);\n"
"\n"
"        // If the user quickly taps multiple times we assume they meant to\n"
"        // hit the same spot, so slightly adjust coordinates\n"
"\n"
"        if ((this._gestureLastTapTime !== null) &&\n"
"            ((Date.now() - this._gestureLastTapTime) < DOUBLE_TAP_TIMEOUT) &&\n"
"            (this._gestureFirstDoubleTapEv.detail.type === ev.detail.type)) {\n"
"            let dx = this._gestureFirstDoubleTapEv.detail.clientX - ev.detail.clientX;\n"
"            let dy = this._gestureFirstDoubleTapEv.detail.clientY - ev.detail.clientY;\n"
"            let distance = Math.hypot(dx, dy);\n"
"\n"
"            if (distance < DOUBLE_TAP_THRESHOLD) {\n"
"                pos = clientToElement(this._gestureFirstDoubleTapEv.detail.clientX,\n"
"                                      this._gestureFirstDoubleTapEv.detail.clientY,\n"
"                                      this._canvas);\n"
"            } else {\n"
"                this._gestureFirstDoubleTapEv = ev;\n"
"            }\n"
"        } else {\n"
"            this._gestureFirstDoubleTapEv = ev;\n"
"        }\n"
"        this._gestureLastTapTime = Date.now();\n"
"\n"
"        this._fakeMouseMove(this._gestureFirstDoubleTapEv, pos.x, pos.y);\n"
"        this._handleMouseButton(pos.x, pos.y, true, bmask);\n"
"        this._handleMouseButton(pos.x, pos.y, false, bmask);\n"
"    }\n"
"\n"
"    _handleGesture(ev) {\n"
"        let magnitude;\n"
"\n"
"        let pos = clientToElement(ev.detail.clientX, ev.detail.clientY,\n"
"                                  this._canvas);\n"
"        switch (ev.type) {\n"
"            case 'gesturestart':\n"
"                switch (ev.detail.type) {\n"
"                    case 'onetap':\n"
"                        this._handleTapEvent(ev, 0x1);\n"
"                        break;\n"
"                    case 'twotap':\n"
"                        this._handleTapEvent(ev, 0x4);\n"
"                        break;\n"
"                    case 'threetap':\n"
"                        this._handleTapEvent(ev, 0x2);\n"
"                        break;\n"
"                    case 'drag':\n"
"                        this._fakeMouseMove(ev, pos.x, pos.y);\n"
"                        this._handleMouseButton(pos.x, pos.y, true, 0x1);\n"
"                        break;\n"
"                    case 'longpress':\n"
"                        this._fakeMouseMove(ev, pos.x, pos.y);\n"
"                        this._handleMouseButton(pos.x, pos.y, true, 0x4);\n"
"                        break;\n"
"\n"
"                    case 'twodrag':\n"
"                        this._gestureLastMagnitudeX = ev.detail.magnitudeX;\n"
"                        this._gestureLastMagnitudeY = ev.detail.magnitudeY;\n"
"                        this._fakeMouseMove(ev, pos.x, pos.y);\n"
"                        break;\n"
"                    case 'pinch':\n"
"                        this._gestureLastMagnitudeX = Math.hypot(ev.detail.magnitudeX,\n"
"                                                                 ev.detail.magnitudeY);\n"
"                        this._fakeMouseMove(ev, pos.x, pos.y);\n"
"                        break;\n"
"                }\n"
"                break;\n"
"\n"
"            case 'gesturemove':\n"
"                switch (ev.detail.type) {\n"
"                    case 'onetap':\n"
"                    case 'twotap':\n"
"                    case 'threetap':\n"
"                        break;\n"
"                    case 'drag':\n"
"                    case 'longpress':\n"
"                        this._fakeMouseMove(ev, pos.x, pos.y);\n"
"                        break;\n"
"                    case 'twodrag':\n"
"                        // Always scroll in the same position.\n"
"                        // We don't know if the mouse was moved so we need to move it\n"
"                        // every update.\n"
"                        this._fakeMouseMove(ev, pos.x, pos.y);\n"
"                        while ((ev.detail.magnitudeY - this._gestureLastMagnitudeY) > GESTURE_SCRLSENS) {\n"
"                            this._handleMouseButton(pos.x, pos.y, true, 0x8);\n"
"                            this._handleMouseButton(pos.x, pos.y, false, 0x8);\n"
"                            this._gestureLastMagnitudeY += GESTURE_SCRLSENS;\n"
"                        }\n"
"                        while ((ev.detail.magnitudeY - this._gestureLastMagnitudeY) < -GESTURE_SCRLSENS) {\n"
"                            this._handleMouseButton(pos.x, pos.y, true, 0x10);\n"
"                            this._handleMouseButton(pos.x, pos.y, false, 0x10);\n"
"                            this._gestureLastMagnitudeY -= GESTURE_SCRLSENS;\n"
"                        }\n"
"                        while ((ev.detail.magnitudeX - this._gestureLastMagnitudeX) > GESTURE_SCRLSENS) {\n"
"                            this._handleMouseButton(pos.x, pos.y, true, 0x20);\n"
"                            this._handleMouseButton(pos.x, pos.y, false, 0x20);\n"
"                            this._gestureLastMagnitudeX += GESTURE_SCRLSENS;\n"
"                        }\n"
"                        while ((ev.detail.magnitudeX - this._gestureLastMagnitudeX) < -GESTURE_SCRLSENS) {\n"
"                            this._handleMouseButton(pos.x, pos.y, true, 0x40);\n"
"                            this._handleMouseButton(pos.x, pos.y, false, 0x40);\n"
"                            this._gestureLastMagnitudeX -= GESTURE_SCRLSENS;\n"
"                        }\n"
"                        break;\n"
"                    case 'pinch':\n"
"                        // Always scroll in the same position.\n"
"                        // We don't know if the mouse was moved so we need to move it\n"
"                        // every update.\n"
"                        this._fakeMouseMove(ev, pos.x, pos.y);\n"
"                        magnitude = Math.hypot(ev.detail.magnitudeX, ev.detail.magnitudeY);\n"
"                        if (Math.abs(magnitude - this._gestureLastMagnitudeX) > GESTURE_ZOOMSENS) {\n"
"                            this._handleKeyEvent(KeyTable.XK_Control_L, \"ControlLeft\", true);\n"
"                            while ((magnitude - this._gestureLastMagnitudeX) > GESTURE_ZOOMSENS) {\n"
"                                this._handleMouseButton(pos.x, pos.y, true, 0x8);\n"
"                                this._handleMouseButton(pos.x, pos.y, false, 0x8);\n"
"                                this._gestureLastMagnitudeX += GESTURE_ZOOMSENS;\n"
"                            }\n"
"                            while ((magnitude -  this._gestureLastMagnitudeX) < -GESTURE_ZOOMSENS) {\n"
"                                this._handleMouseButton(pos.x, pos.y, true, 0x10);\n"
"                                this._handleMouseButton(pos.x, pos.y, false, 0x10);\n"
"                                this._gestureLastMagnitudeX -= GESTURE_ZOOMSENS;\n"
"                            }\n"
"                        }\n"
"                        this._handleKeyEvent(KeyTable.XK_Control_L, \"ControlLeft\", false);\n"
"                        break;\n"
"                }\n"
"                break;\n"
"\n"
"            case 'gestureend':\n"
"                switch (ev.detail.type) {\n"
"                    case 'onetap':\n"
"                    case 'twotap':\n"
"                    case 'threetap':\n"
"                    case 'pinch':\n"
"                    case 'twodrag':\n"
"                        break;\n"
"                    case 'drag':\n"
"                        this._fakeMouseMove(ev, pos.x, pos.y);\n"
"                        this._handleMouseButton(pos.x, pos.y, false, 0x1);\n"
"                        break;\n"
"                    case 'longpress':\n"
"                        this._fakeMouseMove(ev, pos.x, pos.y);\n"
"                        this._handleMouseButton(pos.x, pos.y, false, 0x4);\n"
"                        break;\n"
"                }\n"
"                break;\n"
"        }\n"
"    }\n"
"\n"
"    // Message Handlers\n"
"\n"
"    _negotiateProtocolVersion() {\n"
"        console.log(\"[VNC-CLIENT] _negotiateProtocolVersion called\");\n"
"        console.log(\"[VNC-CLIENT] Waiting for version data (12 bytes)\");\n"
"\n"
"        if (this._sock.rQwait(\"version\", 12)) {\n"
"            console.log(\"[VNC-CLIENT] Not enough data for version - waiting\");\n"
"            return false;\n"
"        }\n"
"\n"
"        const versionStr = this._sock.rQshiftStr(12);\n"
"        console.log(\"[VNC-CLIENT] Received version string:\", versionStr);\n"
"        console.log(\"[VNC-CLIENT] Raw version bytes:\", Array.from(versionStr).map(c => c.charCodeAt(0)));\n"
"\n"
"        const sversion = versionStr.substr(4, 7);\n"
"        console.log(\"[VNC-CLIENT] Parsed server version:\", sversion);\n"
"\n"
"        Log.Info(\"Server ProtocolVersion: \" + sversion);\n"
"        let isRepeater = 0;\n"
"        switch (sversion) {\n"
"            case \"000.000\":  // UltraVNC repeater\n"
"                isRepeater = 1;\n"
"                break;\n"
"            case \"003.003\":\n"
"            case \"003.006\":  // UltraVNC\n"
"                this._rfbVersion = 3.3;\n"
"                break;\n"
"            case \"003.007\":\n"
"                this._rfbVersion = 3.7;\n"
"                break;\n"
"            case \"003.008\":\n"
"            case \"003.889\":  // Apple Remote Desktop\n"
"            case \"004.000\":  // Intel AMT KVM\n"
"            case \"004.001\":  // RealVNC 4.6\n"
"            case \"005.000\":  // RealVNC 5.3\n"
"                this._rfbVersion = 3.8;\n"
"                break;\n"
"            default:\n"
"                return this._fail(\"Invalid server version \" + sversion);\n"
"        }\n"
"\n"
"        if (isRepeater) {\n"
"            let repeaterID = \"ID:\" + this._repeaterID;\n"
"            while (repeaterID.length < 250) {\n"
"                repeaterID += \"\\0\";\n"
"            }\n"
"            this._sock.sendString(repeaterID);\n"
"            return true;\n"
"        }\n"
"\n"
"        if (this._rfbVersion > this._rfbMaxVersion) {\n"
"            this._rfbVersion = this._rfbMaxVersion;\n"
"        }\n"
"\n"
"        const cversion = \"00\" + parseInt(this._rfbVersion, 10) +\n"
"                       \".00\" + ((this._rfbVersion * 10) % 10);\n"
"        this._sock.sendString(\"RFB \" + cversion + \"\\n\");\n"
"        Log.Debug('Sent ProtocolVersion: ' + cversion);\n"
"\n"
"        this._rfbInitState = 'Security';\n"
"    }\n"
"\n"
"    _isSupportedSecurityType(type) {\n"
"        const clientTypes = [\n"
"            securityTypeNone,\n"
"            securityTypeVNCAuth,\n"
"            securityTypeRA2ne,\n"
"            securityTypeTight,\n"
"            securityTypeVeNCrypt,\n"
"            securityTypeXVP,\n"
"            securityTypeARD,\n"
"            securityTypeMSLogonII,\n"
"            securityTypePlain,\n"
"        ];\n"
"\n"
"        return clientTypes.includes(type);\n"
"    }\n"
"\n"
"    _negotiateSecurity() {\n"
"        if (this._rfbVersion >= 3.7) {\n"
"            // Server sends supported list, client decides\n"
"            const numTypes = this._sock.rQshift8();\n"
"            if (this._sock.rQwait(\"security type\", numTypes, 1)) { return false; }\n"
"\n"
"            if (numTypes === 0) {\n"
"                this._rfbInitState = \"SecurityReason\";\n"
"                this._securityContext = \"no security types\";\n"
"                this._securityStatus = 1;\n"
"                return true;\n"
"            }\n"
"\n"
"            const types = this._sock.rQshiftBytes(numTypes);\n"
"            Log.Debug(\"Server security types: \" + types);\n"
"\n"
"            // Look for a matching security type in the order that the\n"
"            // server prefers\n"
"            this._rfbAuthScheme = -1;\n"
"            for (let type of types) {\n"
"                if (this._isSupportedSecurityType(type)) {\n"
"                    this._rfbAuthScheme = type;\n"
"                    break;\n"
"                }\n"
"            }\n"
"\n"
"            if (this._rfbAuthScheme === -1) {\n"
"                return this._fail(\"Unsupported security types (types: \" + types + \")\");\n"
"            }\n"
"\n"
"            this._sock.send([this._rfbAuthScheme]);\n"
"        } else {\n"
"            // Server decides\n"
"            if (this._sock.rQwait(\"security scheme\", 4)) { return false; }\n"
"            this._rfbAuthScheme = this._sock.rQshift32();\n"
"\n"
"            if (this._rfbAuthScheme == 0) {\n"
"                this._rfbInitState = \"SecurityReason\";\n"
"                this._securityContext = \"authentication scheme\";\n"
"                this._securityStatus = 1;\n"
"                return true;\n"
"            }\n"
"        }\n"
"\n"
"        this._rfbInitState = 'Authentication';\n"
"        Log.Debug('Authenticating using scheme: ' + this._rfbAuthScheme);\n"
"\n"
"        return true;\n"
"    }\n"
"\n"
"    _handleSecurityReason() {\n"
"        if (this._sock.rQwait(\"reason length\", 4)) {\n"
"            return false;\n"
"        }\n"
"        const strlen = this._sock.rQshift32();\n"
"        let reason = \"\";\n"
"\n"
"        if (strlen > 0) {\n"
"            if (this._sock.rQwait(\"reason\", strlen, 4)) { return false; }\n"
"            reason = this._sock.rQshiftStr(strlen);\n"
"        }\n"
"\n"
"        if (reason !== \"\") {\n"
"            this.dispatchEvent(new CustomEvent(\n"
"                \"securityfailure\",\n"
"                { detail: { status: this._securityStatus,\n"
"                            reason: reason } }));\n"
"\n"
"            return this._fail(\"Security negotiation failed on \" +\n"
"                              this._securityContext +\n"
"                              \" (reason: \" + reason + \")\");\n"
"        } else {\n"
"            this.dispatchEvent(new CustomEvent(\n"
"                \"securityfailure\",\n"
"                { detail: { status: this._securityStatus } }));\n"
"\n"
"            return this._fail(\"Security negotiation failed on \" +\n"
"                              this._securityContext);\n"
"        }\n"
"    }\n"
"\n"
"    // authentication\n"
"    _negotiateXvpAuth() {\n"
"        if (this._rfbCredentials.username === undefined ||\n"
"            this._rfbCredentials.password === undefined ||\n"
"            this._rfbCredentials.target === undefined) {\n"
"            this.dispatchEvent(new CustomEvent(\n"
"                \"credentialsrequired\",\n"
"                { detail: { types: [\"username\", \"password\", \"target\"] } }));\n"
"            return false;\n"
"        }\n"
"\n"
"        const xvpAuthStr = String.fromCharCode(this._rfbCredentials.username.length) +\n"
"                           String.fromCharCode(this._rfbCredentials.target.length) +\n"
"                           this._rfbCredentials.username +\n"
"                           this._rfbCredentials.target;\n"
"        this._sock.sendString(xvpAuthStr);\n"
"        this._rfbAuthScheme = securityTypeVNCAuth;\n"
"        return this._negotiateAuthentication();\n"
"    }\n"
"\n"
"    // VeNCrypt authentication, currently only supports version 0.2 and only Plain subtype\n"
"    _negotiateVeNCryptAuth() {\n"
"\n"
"        // waiting for VeNCrypt version\n"
"        if (this._rfbVeNCryptState == 0) {\n"
"            if (this._sock.rQwait(\"vencrypt version\", 2)) { return false; }\n"
"\n"
"            const major = this._sock.rQshift8();\n"
"            const minor = this._sock.rQshift8();\n"
"\n"
"            if (!(major == 0 && minor == 2)) {\n"
"                return this._fail(\"Unsupported VeNCrypt version \" + major + \".\" + minor);\n"
"            }\n"
"\n"
"            this._sock.send([0, 2]);\n"
"            this._rfbVeNCryptState = 1;\n"
"        }\n"
"\n"
"        // waiting for ACK\n"
"        if (this._rfbVeNCryptState == 1) {\n"
"            if (this._sock.rQwait(\"vencrypt ack\", 1)) { return false; }\n"
"\n"
"            const res = this._sock.rQshift8();\n"
"\n"
"            if (res != 0) {\n"
"                return this._fail(\"VeNCrypt failure \" + res);\n"
"            }\n"
"\n"
"            this._rfbVeNCryptState = 2;\n"
"        }\n"
"        // must fall through here (i.e. no \"else if\"), beacause we may have already received\n"
"        // the subtypes length and won't be called again\n"
"\n"
"        if (this._rfbVeNCryptState == 2) { // waiting for subtypes length\n"
"            if (this._sock.rQwait(\"vencrypt subtypes length\", 1)) { return false; }\n"
"\n"
"            const subtypesLength = this._sock.rQshift8();\n"
"            if (subtypesLength < 1) {\n"
"                return this._fail(\"VeNCrypt subtypes empty\");\n"
"            }\n"
"\n"
"            this._rfbVeNCryptSubtypesLength = subtypesLength;\n"
"            this._rfbVeNCryptState = 3;\n"
"        }\n"
"\n"
"        // waiting for subtypes list\n"
"        if (this._rfbVeNCryptState == 3) {\n"
"            if (this._sock.rQwait(\"vencrypt subtypes\", 4 * this._rfbVeNCryptSubtypesLength)) { return false; }\n"
"\n"
"            const subtypes = [];\n"
"            for (let i = 0; i < this._rfbVeNCryptSubtypesLength; i++) {\n"
"                subtypes.push(this._sock.rQshift32());\n"
"            }\n"
"\n"
"            // Look for a matching security type in the order that the\n"
"            // server prefers\n"
"            this._rfbAuthScheme = -1;\n"
"            for (let type of subtypes) {\n"
"                // Avoid getting in to a loop\n"
"                if (type === securityTypeVeNCrypt) {\n"
"                    continue;\n"
"                }\n"
"\n"
"                if (this._isSupportedSecurityType(type)) {\n"
"                    this._rfbAuthScheme = type;\n"
"                    break;\n"
"                }\n"
"            }\n"
"\n"
"            if (this._rfbAuthScheme === -1) {\n"
"                return this._fail(\"Unsupported security types (types: \" + subtypes + \")\");\n"
"            }\n"
"\n"
"            this._sock.send([this._rfbAuthScheme >> 24,\n"
"                             this._rfbAuthScheme >> 16,\n"
"                             this._rfbAuthScheme >> 8,\n"
"                             this._rfbAuthScheme]);\n"
"\n"
"            this._rfbVeNCryptState == 4;\n"
"            return true;\n"
"        }\n"
"    }\n"
"\n"
"    _negotiatePlainAuth() {\n"
"        if (this._rfbCredentials.username === undefined ||\n"
"            this._rfbCredentials.password === undefined) {\n"
"            this.dispatchEvent(new CustomEvent(\n"
"                \"credentialsrequired\",\n"
"                { detail: { types: [\"username\", \"password\"] } }));\n"
"            return false;\n"
"        }\n"
"\n"
"        const user = encodeUTF8(this._rfbCredentials.username);\n"
"        const pass = encodeUTF8(this._rfbCredentials.password);\n"
"\n"
"        this._sock.send([\n"
"            (user.length >> 24) & 0xFF,\n"
"            (user.length >> 16) & 0xFF,\n"
"            (user.length >> 8) & 0xFF,\n"
"            user.length & 0xFF\n"
"        ]);\n"
"        this._sock.send([\n"
"            (pass.length >> 24) & 0xFF,\n"
"            (pass.length >> 16) & 0xFF,\n"
"            (pass.length >> 8) & 0xFF,\n"
"            pass.length & 0xFF\n"
"        ]);\n"
"        this._sock.sendString(user);\n"
"        this._sock.sendString(pass);\n"
"\n"
"        this._rfbInitState = \"SecurityResult\";\n"
"        return true;\n"
"    }\n"
"\n"
"    _negotiateStdVNCAuth() {\n"
"        if (this._sock.rQwait(\"auth challenge\", 16)) { return false; }\n"
"\n"
"        if (this._rfbCredentials.password === undefined) {\n"
"            this.dispatchEvent(new CustomEvent(\n"
"                \"credentialsrequired\",\n"
"                { detail: { types: [\"password\"] } }));\n"
"            return false;\n"
"        }\n"
"\n"
"        // TODO(directxman12): make genDES not require an Array\n"
"        const challenge = Array.prototype.slice.call(this._sock.rQshiftBytes(16));\n"
"        const response = RFB.genDES(this._rfbCredentials.password, challenge);\n"
"        this._sock.send(response);\n"
"        this._rfbInitState = \"SecurityResult\";\n"
"        return true;\n"
"    }\n"
"\n"
"    _negotiateARDAuth() {\n"
"\n"
"        if (this._rfbCredentials.username === undefined ||\n"
"            this._rfbCredentials.password === undefined) {\n"
"            this.dispatchEvent(new CustomEvent(\n"
"                \"credentialsrequired\",\n"
"                { detail: { types: [\"username\", \"password\"] } }));\n"
"            return false;\n"
"        }\n"
"\n"
"        if (this._rfbCredentials.ardPublicKey != undefined &&\n"
"            this._rfbCredentials.ardCredentials != undefined) {\n"
"            // if the async web crypto is done return the results\n"
"            this._sock.send(this._rfbCredentials.ardCredentials);\n"
"            this._sock.send(this._rfbCredentials.ardPublicKey);\n"
"            this._rfbCredentials.ardCredentials = null;\n"
"            this._rfbCredentials.ardPublicKey = null;\n"
"            this._rfbInitState = \"SecurityResult\";\n"
"            return true;\n"
"        }\n"
"\n"
"        if (this._sock.rQwait(\"read ard\", 4)) { return false; }\n"
"\n"
"        let generator = this._sock.rQshiftBytes(2);   // DH base generator value\n"
"\n"
"        let keyLength = this._sock.rQshift16();\n"
"\n"
"        if (this._sock.rQwait(\"read ard keylength\", keyLength*2, 4)) { return false; }\n"
"\n"
"        // read the server values\n"
"        let prime = this._sock.rQshiftBytes(keyLength);  // predetermined prime modulus\n"
"        let serverPublicKey = this._sock.rQshiftBytes(keyLength); // other party's public key\n"
"\n"
"        let clientPrivateKey = window.crypto.getRandomValues(new Uint8Array(keyLength));\n"
"        let padding = Array.from(window.crypto.getRandomValues(new Uint8Array(64)), byte => String.fromCharCode(65+byte%26)).join('');\n"
"\n"
"        this._negotiateARDAuthAsync(generator, keyLength, prime, serverPublicKey, clientPrivateKey, padding);\n"
"\n"
"        return false;\n"
"    }\n"
"\n"
"    _modPow(base, exponent, modulus) {\n"
"\n"
"        let baseHex = \"0x\"+Array.from(base, byte => ('0' + (byte & 0xFF).toString(16)).slice(-2)).join('');\n"
"        let exponentHex = \"0x\"+Array.from(exponent, byte => ('0' + (byte & 0xFF).toString(16)).slice(-2)).join('');\n"
"        let modulusHex = \"0x\"+Array.from(modulus, byte => ('0' + (byte & 0xFF).toString(16)).slice(-2)).join('');\n"
"\n"
"        let b = BigInt(baseHex);\n"
"        let e = BigInt(exponentHex);\n"
"        let m = BigInt(modulusHex);\n"
"        let r = 1n;\n"
"        b = b % m;\n"
"        while (e > 0) {\n"
"            if (e % 2n === 1n) {\n"
"                r = (r * b) % m;\n"
"            }\n"
"            e = e / 2n;\n"
"            b = (b * b) % m;\n"
"        }\n"
"        let hexResult = r.toString(16);\n"
"\n"
"        while (hexResult.length/2<exponent.length || (hexResult.length%2 != 0)) {\n"
"            hexResult = \"0\"+hexResult;\n"
"        }\n"
"\n"
"        let bytesResult = [];\n"
"        for (let c = 0; c < hexResult.length; c += 2) {\n"
"            bytesResult.push(parseInt(hexResult.substr(c, 2), 16));\n"
"        }\n"
"        return bytesResult;\n"
"    }\n"
"\n"
"    async _aesEcbEncrypt(string, key) {\n"
"        // perform AES-ECB blocks\n"
"        let keyString = Array.from(key, byte => String.fromCharCode(byte)).join('');\n"
"        let aesKey = await window.crypto.subtle.importKey(\"raw\", MD5(keyString), {name: \"AES-CBC\"}, false, [\"encrypt\"]);\n"
"        let data = new Uint8Array(string.length);\n"
"        for (let i = 0; i < string.length; ++i) {\n"
"            data[i] = string.charCodeAt(i);\n"
"        }\n"
"        let encrypted = new Uint8Array(data.length);\n"
"        for (let i=0;i<data.length;i+=16) {\n"
"            let block = data.slice(i, i+16);\n"
"            let encryptedBlock = await window.crypto.subtle.encrypt({name: \"AES-CBC\", iv: block},\n"
"                                                                    aesKey, new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])\n"
"            );\n"
"            encrypted.set((new Uint8Array(encryptedBlock)).slice(0, 16), i);\n"
"        }\n"
"        return encrypted;\n"
"    }\n"
"\n"
"    async _negotiateARDAuthAsync(generator, keyLength, prime, serverPublicKey, clientPrivateKey, padding) {\n"
"        // calculate the DH keys\n"
"        let clientPublicKey = this._modPow(generator, clientPrivateKey, prime);\n"
"        let sharedKey = this._modPow(serverPublicKey, clientPrivateKey, prime);\n"
"\n"
"        let username = encodeUTF8(this._rfbCredentials.username).substring(0, 63);\n"
"        let password = encodeUTF8(this._rfbCredentials.password).substring(0, 63);\n"
"\n"
"        let paddedUsername = username + '\\0' + padding.substring(0, 63);\n"
"        let paddedPassword = password + '\\0' + padding.substring(0, 63);\n"
"        let credentials = paddedUsername.substring(0, 64) + paddedPassword.substring(0, 64);\n"
"\n"
"        let encrypted = await this._aesEcbEncrypt(credentials, sharedKey);\n"
"\n"
"        this._rfbCredentials.ardCredentials = encrypted;\n"
"        this._rfbCredentials.ardPublicKey = clientPublicKey;\n"
"\n"
"        this._resumeAuthentication();\n"
"    }\n"
"\n"
"    _negotiateTightUnixAuth() {\n"
"        if (this._rfbCredentials.username === undefined ||\n"
"            this._rfbCredentials.password === undefined) {\n"
"            this.dispatchEvent(new CustomEvent(\n"
"                \"credentialsrequired\",\n"
"                { detail: { types: [\"username\", \"password\"] } }));\n"
"            return false;\n"
"        }\n"
"\n"
"        this._sock.send([0, 0, 0, this._rfbCredentials.username.length]);\n"
"        this._sock.send([0, 0, 0, this._rfbCredentials.password.length]);\n"
"        this._sock.sendString(this._rfbCredentials.username);\n"
"        this._sock.sendString(this._rfbCredentials.password);\n"
"        this._rfbInitState = \"SecurityResult\";\n"
"        return true;\n"
"    }\n"
"\n"
"    _negotiateTightTunnels(numTunnels) {\n"
"        const clientSupportedTunnelTypes = {\n"
"            0: { vendor: 'TGHT', signature: 'NOTUNNEL' }\n"
"        };\n"
"        const serverSupportedTunnelTypes = {};\n"
"        // receive tunnel capabilities\n"
"        for (let i = 0; i < numTunnels; i++) {\n"
"            const capCode = this._sock.rQshift32();\n"
"            const capVendor = this._sock.rQshiftStr(4);\n"
"            const capSignature = this._sock.rQshiftStr(8);\n"
"            serverSupportedTunnelTypes[capCode] = { vendor: capVendor, signature: capSignature };\n"
"        }\n"
"\n"
"        Log.Debug(\"Server Tight tunnel types: \" + serverSupportedTunnelTypes);\n"
"\n"
"        // Siemens touch panels have a VNC server that supports NOTUNNEL,\n"
"        // but forgets to advertise it. Try to detect such servers by\n"
"        // looking for their custom tunnel type.\n"
"        if (serverSupportedTunnelTypes[1] &&\n"
"            (serverSupportedTunnelTypes[1].vendor === \"SICR\") &&\n"
"            (serverSupportedTunnelTypes[1].signature === \"SCHANNEL\")) {\n"
"            Log.Debug(\"Detected Siemens server. Assuming NOTUNNEL support.\");\n"
"            serverSupportedTunnelTypes[0] = { vendor: 'TGHT', signature: 'NOTUNNEL' };\n"
"        }\n"
"\n"
"        // choose the notunnel type\n"
"        if (serverSupportedTunnelTypes[0]) {\n"
"            if (serverSupportedTunnelTypes[0].vendor != clientSupportedTunnelTypes[0].vendor ||\n"
"                serverSupportedTunnelTypes[0].signature != clientSupportedTunnelTypes[0].signature) {\n"
"                return this._fail(\"Client's tunnel type had the incorrect \" +\n"
"                                  \"vendor or signature\");\n"
"            }\n"
"            Log.Debug(\"Selected tunnel type: \" + clientSupportedTunnelTypes[0]);\n"
"            this._sock.send([0, 0, 0, 0]);  // use NOTUNNEL\n"
"            return false; // wait until we receive the sub auth count to continue\n"
"        } else {\n"
"            return this._fail(\"Server wanted tunnels, but doesn't support \" +\n"
"                              \"the notunnel type\");\n"
"        }\n"
"    }\n"
"\n"
"    _negotiateTightAuth() {\n"
"        if (!this._rfbTightVNC) {  // first pass, do the tunnel negotiation\n"
"            if (this._sock.rQwait(\"num tunnels\", 4)) { return false; }\n"
"            const numTunnels = this._sock.rQshift32();\n"
"            if (numTunnels > 0 && this._sock.rQwait(\"tunnel capabilities\", 16 * numTunnels, 4)) { return false; }\n"
"\n"
"            this._rfbTightVNC = true;\n"
"\n"
"            if (numTunnels > 0) {\n"
"                this._negotiateTightTunnels(numTunnels);\n"
"                return false;  // wait until we receive the sub auth to continue\n"
"            }\n"
"        }\n"
"\n"
"        // second pass, do the sub-auth negotiation\n"
"        if (this._sock.rQwait(\"sub auth count\", 4)) { return false; }\n"
"        const subAuthCount = this._sock.rQshift32();\n"
"        if (subAuthCount === 0) {  // empty sub-auth list received means 'no auth' subtype selected\n"
"            this._rfbInitState = 'SecurityResult';\n"
"            return true;\n"
"        }\n"
"\n"
"        if (this._sock.rQwait(\"sub auth capabilities\", 16 * subAuthCount, 4)) { return false; }\n"
"\n"
"        const clientSupportedTypes = {\n"
"            'STDVNOAUTH__': 1,\n"
"            'STDVVNCAUTH_': 2,\n"
"            'TGHTULGNAUTH': 129\n"
"        };\n"
"\n"
"        const serverSupportedTypes = [];\n"
"\n"
"        for (let i = 0; i < subAuthCount; i++) {\n"
"            this._sock.rQshift32(); // capNum\n"
"            const capabilities = this._sock.rQshiftStr(12);\n"
"            serverSupportedTypes.push(capabilities);\n"
"        }\n"
"\n"
"        Log.Debug(\"Server Tight authentication types: \" + serverSupportedTypes);\n"
"\n"
"        for (let authType in clientSupportedTypes) {\n"
"            if (serverSupportedTypes.indexOf(authType) != -1) {\n"
"                this._sock.send([0, 0, 0, clientSupportedTypes[authType]]);\n"
"                Log.Debug(\"Selected authentication type: \" + authType);\n"
"\n"
"                switch (authType) {\n"
"                    case 'STDVNOAUTH__':  // no auth\n"
"                        this._rfbInitState = 'SecurityResult';\n"
"                        return true;\n"
"                    case 'STDVVNCAUTH_':\n"
"                        this._rfbAuthScheme = securityTypeVNCAuth;\n"
"                        return true;\n"
"                    case 'TGHTULGNAUTH':\n"
"                        this._rfbAuthScheme = securityTypeUnixLogon;\n"
"                        return true;\n"
"                    default:\n"
"                        return this._fail(\"Unsupported tiny auth scheme \" +\n"
"                                          \"(scheme: \" + authType + \")\");\n"
"                }\n"
"            }\n"
"        }\n"
"\n"
"        return this._fail(\"No supported sub-auth types!\");\n"
"    }\n"
"\n"
"    _handleRSAAESCredentialsRequired(event) {\n"
"        this.dispatchEvent(event);\n"
"    }\n"
"\n"
"    _handleRSAAESServerVerification(event) {\n"
"        this.dispatchEvent(event);\n"
"    }\n"
"\n"
"    _negotiateRA2neAuth() {\n"
"        if (this._rfbRSAAESAuthenticationState === null) {\n"
"            this._rfbRSAAESAuthenticationState = new RSAAESAuthenticationState(this._sock, () => this._rfbCredentials);\n"
"            this._rfbRSAAESAuthenticationState.addEventListener(\n"
"                \"serververification\", this._eventHandlers.handleRSAAESServerVerification);\n"
"            this._rfbRSAAESAuthenticationState.addEventListener(\n"
"                \"credentialsrequired\", this._eventHandlers.handleRSAAESCredentialsRequired);\n"
"        }\n"
"        this._rfbRSAAESAuthenticationState.checkInternalEvents();\n"
"        if (!this._rfbRSAAESAuthenticationState.hasStarted) {\n"
"            this._rfbRSAAESAuthenticationState.negotiateRA2neAuthAsync()\n"
"                .catch((e) => {\n"
"                    if (e.message !== \"disconnect normally\") {\n"
"                        this._fail(e.message);\n"
"                    }\n"
"                }).then(() => {\n"
"                    this.dispatchEvent(new CustomEvent('securityresult'));\n"
"                    this._rfbInitState = \"SecurityResult\";\n"
"                    return true;\n"
"                }).finally(() => {\n"
"                    this._rfbRSAAESAuthenticationState.removeEventListener(\n"
"                        \"serververification\", this._eventHandlers.handleRSAAESServerVerification);\n"
"                    this._rfbRSAAESAuthenticationState.removeEventListener(\n"
"                        \"credentialsrequired\", this._eventHandlers.handleRSAAESCredentialsRequired);\n"
"                    this._rfbRSAAESAuthenticationState = null;\n"
"                });\n"
"        }\n"
"        return false;\n"
"    }\n"
"\n"
"    _negotiateMSLogonIIAuth() {\n"
"        if (this._sock.rQwait(\"mslogonii dh param\", 24)) { return false; }\n"
"\n"
"        if (this._rfbCredentials.username === undefined ||\n"
"            this._rfbCredentials.password === undefined) {\n"
"            this.dispatchEvent(new CustomEvent(\n"
"                \"credentialsrequired\",\n"
"                { detail: { types: [\"username\", \"password\"] } }));\n"
"            return false;\n"
"        }\n"
"\n"
"        const g = this._sock.rQshiftBytes(8);\n"
"        const p = this._sock.rQshiftBytes(8);\n"
"        const A = this._sock.rQshiftBytes(8);\n"
"        const b = window.crypto.getRandomValues(new Uint8Array(8));\n"
"        const B = new Uint8Array(this._modPow(g, b, p));\n"
"        const secret = new Uint8Array(this._modPow(A, b, p));\n"
"\n"
"        const des = new DES(secret);\n"
"        const username = encodeUTF8(this._rfbCredentials.username).substring(0, 255);\n"
"        const password = encodeUTF8(this._rfbCredentials.password).substring(0, 63);\n"
"        const usernameBytes = new Uint8Array(256);\n"
"        const passwordBytes = new Uint8Array(64);\n"
"        window.crypto.getRandomValues(usernameBytes);\n"
"        window.crypto.getRandomValues(passwordBytes);\n"
"        for (let i = 0; i < username.length; i++) {\n"
"            usernameBytes[i] = username.charCodeAt(i);\n"
"        }\n"
"        usernameBytes[username.length] = 0;\n"
"        for (let i = 0; i < password.length; i++) {\n"
"            passwordBytes[i] = password.charCodeAt(i);\n"
"        }\n"
"        passwordBytes[password.length] = 0;\n"
"        let x = new Uint8Array(secret);\n"
"        for (let i = 0; i < 32; i++) {\n"
"            for (let j = 0; j < 8; j++) {\n"
"                x[j] ^= usernameBytes[i * 8 + j];\n"
"            }\n"
"            x = des.enc8(x);\n"
"            usernameBytes.set(x, i * 8);\n"
"        }\n"
"        x = new Uint8Array(secret);\n"
"        for (let i = 0; i < 8; i++) {\n"
"            for (let j = 0; j < 8; j++) {\n"
"                x[j] ^= passwordBytes[i * 8 + j];\n"
"            }\n"
"            x = des.enc8(x);\n"
"            passwordBytes.set(x, i * 8);\n"
"        }\n"
"        this._sock.send(B);\n"
"        this._sock.send(usernameBytes);\n"
"        this._sock.send(passwordBytes);\n"
"        this._rfbInitState = \"SecurityResult\";\n"
"        return true;\n"
"    }\n"
"\n"
"    _negotiateAuthentication() {\n"
"        switch (this._rfbAuthScheme) {\n"
"            case securityTypeNone:\n"
"                this._rfbInitState = 'SecurityResult';\n"
"                return true;\n"
"\n"
"            case securityTypeXVP:\n"
"                return this._negotiateXvpAuth();\n"
"\n"
"            case securityTypeARD:\n"
"                return this._negotiateARDAuth();\n"
"\n"
"            case securityTypeVNCAuth:\n"
"                return this._negotiateStdVNCAuth();\n"
"\n"
"            case securityTypeTight:\n"
"                return this._negotiateTightAuth();\n"
"\n"
"            case securityTypeVeNCrypt:\n"
"                return this._negotiateVeNCryptAuth();\n"
"\n"
"            case securityTypePlain:\n"
"                return this._negotiatePlainAuth();\n"
"\n"
"            case securityTypeUnixLogon:\n"
"                return this._negotiateTightUnixAuth();\n"
"\n"
"            case securityTypeRA2ne:\n"
"                return this._negotiateRA2neAuth();\n"
"\n"
"            case securityTypeMSLogonII:\n"
"                return this._negotiateMSLogonIIAuth();\n"
"\n"
"            default:\n"
"                return this._fail(\"Unsupported auth scheme (scheme: \" +\n"
"                                  this._rfbAuthScheme + \")\");\n"
"        }\n"
"    }\n"
"\n"
"    _handleSecurityResult() {\n"
"        // There is no security choice, and hence no security result\n"
"        // until RFB 3.7\n"
"        if (this._rfbVersion < 3.7) {\n"
"            this._rfbInitState = 'ClientInitialisation';\n"
"            return true;\n"
"        }\n"
"\n"
"        if (this._sock.rQwait('VNC auth response ', 4)) { return false; }\n"
"\n"
"        const status = this._sock.rQshift32();\n"
"\n"
"        if (status === 0) { // OK\n"
"            this._rfbInitState = 'ClientInitialisation';\n"
"            Log.Debug('Authentication OK');\n"
"            return true;\n"
"        } else {\n"
"            if (this._rfbVersion >= 3.8) {\n"
"                this._rfbInitState = \"SecurityReason\";\n"
"                this._securityContext = \"security result\";\n"
"                this._securityStatus = status;\n"
"                return true;\n"
"            } else {\n"
"                this.dispatchEvent(new CustomEvent(\n"
"                    \"securityfailure\",\n"
"                    { detail: { status: status } }));\n"
"\n"
"                return this._fail(\"Security handshake failed\");\n"
"            }\n"
"        }\n"
"    }\n"
"\n"
"    _negotiateServerInit() {\n"
"        console.log(\"[VNC-CLIENT] _negotiateServerInit called, rQlen:\", this._sock.rQlen);\n"
"\n"
"        if (this._sock.rQwait(\"server initialization\", 24)) {\n"
"            console.log(\"[VNC-CLIENT] Not enough data for server init (need 24, have\", this._sock.rQlen, \")\");\n"
"            return false;\n"
"        }\n"
"\n"
"        console.log(\"[VNC-CLIENT] Parsing server initialization message\");\n"
"\n"
"        /* Screen size */\n"
"        const width = this._sock.rQshift16();\n"
"        const height = this._sock.rQshift16();\n"
"\n"
"        console.log(\"[VNC-CLIENT] Server screen size:\", width, \"x\", height);\n"
"\n"
"        /* PIXEL_FORMAT */\n"
"        const bpp         = this._sock.rQshift8();\n"
"        const depth       = this._sock.rQshift8();\n"
"        const bigEndian  = this._sock.rQshift8();\n"
"        const trueColor  = this._sock.rQshift8();\n"
"\n"
"        const redMax     = this._sock.rQshift16();\n"
"        const greenMax   = this._sock.rQshift16();\n"
"        const blueMax    = this._sock.rQshift16();\n"
"        const redShift   = this._sock.rQshift8();\n"
"        const greenShift = this._sock.rQshift8();\n"
"        const blueShift  = this._sock.rQshift8();\n"
"        this._sock.rQskipBytes(3);  // padding\n"
"\n"
"        // NB(directxman12): we don't want to call any callbacks or print messages until\n"
"        //                   *after* we're past the point where we could backtrack\n"
"\n"
"        /* Connection name/title */\n"
"        const nameLength = this._sock.rQshift32();\n"
"        if (this._sock.rQwait('server init name', nameLength, 24)) { return false; }\n"
"        let name = this._sock.rQshiftStr(nameLength);\n"
"        name = decodeUTF8(name, true);\n"
"\n"
"        if (this._rfbTightVNC) {\n"
"            if (this._sock.rQwait('TightVNC extended server init header', 8, 24 + nameLength)) { return false; }\n"
"            // In TightVNC mode, ServerInit message is extended\n"
"            const numServerMessages = this._sock.rQshift16();\n"
"            const numClientMessages = this._sock.rQshift16();\n"
"            const numEncodings = this._sock.rQshift16();\n"
"            this._sock.rQskipBytes(2);  // padding\n"
"\n"
"            const totalMessagesLength = (numServerMessages + numClientMessages + numEncodings) * 16;\n"
"            if (this._sock.rQwait('TightVNC extended server init header', totalMessagesLength, 32 + nameLength)) { return false; }\n"
"\n"
"            // we don't actually do anything with the capability information that TIGHT sends,\n"
"            // so we just skip the all of this.\n"
"\n"
"            // TIGHT server message capabilities\n"
"            this._sock.rQskipBytes(16 * numServerMessages);\n"
"\n"
"            // TIGHT client message capabilities\n"
"            this._sock.rQskipBytes(16 * numClientMessages);\n"
"\n"
"            // TIGHT encoding capabilities\n"
"            this._sock.rQskipBytes(16 * numEncodings);\n"
"        }\n"
"\n"
"        // NB(directxman12): these are down here so that we don't run them multiple times\n"
"        //                   if we backtrack\n"
"        Log.Info(\"Screen: \" + width + \"x\" + height +\n"
"                  \", bpp: \" + bpp + \", depth: \" + depth +\n"
"                  \", bigEndian: \" + bigEndian +\n"
"                  \", trueColor: \" + trueColor +\n"
"                  \", redMax: \" + redMax +\n"
"                  \", greenMax: \" + greenMax +\n"
"                  \", blueMax: \" + blueMax +\n"
"                  \", redShift: \" + redShift +\n"
"                  \", greenShift: \" + greenShift +\n"
"                  \", blueShift: \" + blueShift);\n"
"\n"
"        // we're past the point where we could backtrack, so it's safe to call this\n"
"        this._setDesktopName(name);\n"
"        this._resize(width, height);\n"
"\n"
"        if (!this._viewOnly) { this._keyboard.grab(); }\n"
"\n"
"        this._fbDepth = 24;\n"
"\n"
"        if (this._fbName === \"Intel(r) AMT KVM\") {\n"
"            Log.Warn(\"Intel AMT KVM only supports 8/16 bit depths. Using low color mode.\");\n"
"            this._fbDepth = 8;\n"
"        }\n"
"\n"
"        RFB.messages.pixelFormat(this._sock, this._fbDepth, true);\n"
"        this._sendEncodings();\n"
"        RFB.messages.fbUpdateRequest(this._sock, false, 0, 0, this._fbWidth, this._fbHeight);\n"
"\n"
"        this._updateConnectionState('connected');\n"
"        return true;\n"
"    }\n"
"\n"
"    _sendEncodings() {\n"
"        const encs = [];\n"
"\n"
"        // In preference order\n"
"        encs.push(encodings.encodingCopyRect);\n"
"        // Only supported with full depth support\n"
"        if (this._fbDepth == 24) {\n"
"            encs.push(encodings.encodingTight);\n"
"            encs.push(encodings.encodingTightPNG);\n"
"            encs.push(encodings.encodingZRLE);\n"
"            encs.push(encodings.encodingJPEG);\n"
"            encs.push(encodings.encodingHextile);\n"
"            encs.push(encodings.encodingRRE);\n"
"        }\n"
"        encs.push(encodings.encodingRaw);\n"
"\n"
"        // Psuedo-encoding settings\n"
"        encs.push(encodings.pseudoEncodingQualityLevel0 + this._qualityLevel);\n"
"        encs.push(encodings.pseudoEncodingCompressLevel0 + this._compressionLevel);\n"
"\n"
"        encs.push(encodings.pseudoEncodingDesktopSize);\n"
"        encs.push(encodings.pseudoEncodingLastRect);\n"
"        encs.push(encodings.pseudoEncodingQEMUExtendedKeyEvent);\n"
"        encs.push(encodings.pseudoEncodingExtendedDesktopSize);\n"
"        encs.push(encodings.pseudoEncodingXvp);\n"
"        encs.push(encodings.pseudoEncodingFence);\n"
"        encs.push(encodings.pseudoEncodingContinuousUpdates);\n"
"        encs.push(encodings.pseudoEncodingDesktopName);\n"
"        encs.push(encodings.pseudoEncodingExtendedClipboard);\n"
"\n"
"        if (this._fbDepth == 24) {\n"
"            encs.push(encodings.pseudoEncodingVMwareCursor);\n"
"            encs.push(encodings.pseudoEncodingCursor);\n"
"        }\n"
"\n"
"        RFB.messages.clientEncodings(this._sock, encs);\n"
"    }\n"
"\n"
"    /* RFB protocol initialization states:\n"
"     *   ProtocolVersion\n"
"     *   Security\n"
"     *   Authentication\n"
"     *   SecurityResult\n"
"     *   ClientInitialization - not triggered by server message\n"
"     *   ServerInitialization\n"
"     */\n"
"    _initMsg() {\n"
"        // console.log(\"[VNC-DEBUG] _initMsg called, initState:\", this._rfbInitState);\n"
"        switch (this._rfbInitState) {\n"
"            case 'ProtocolVersion':\n"
"                // console.log(\"[VNC-DEBUG] Negotiating protocol version\");\n"
"                return this._negotiateProtocolVersion();\n"
"\n"
"            case 'Security':\n"
"                // console.log(\"[VNC-DEBUG] Negotiating security\");\n"
"                return this._negotiateSecurity();\n"
"\n"
"            case 'Authentication':\n"
"                // console.log(\"[VNC-DEBUG] Negotiating authentication\");\n"
"                return this._negotiateAuthentication();\n"
"\n"
"            case 'SecurityResult':\n"
"                // console.log(\"[VNC-DEBUG] Handling security result\");\n"
"                return this._handleSecurityResult();\n"
"\n"
"            case 'SecurityReason':\n"
"                // console.log(\"[VNC-DEBUG] Handling security reason\");\n"
"                return this._handleSecurityReason();\n"
"\n"
"            case 'ClientInitialisation':\n"
"                // console.log(\"[VNC-DEBUG] Sending client initialization\");\n"
"                this._sock.send([this._shared ? 1 : 0]); // ClientInitialisation\n"
"                this._rfbInitState = 'ServerInitialisation';\n"
"                return true;\n"
"\n"
"            case 'ServerInitialisation':\n"
"                // console.log(\"[VNC-DEBUG] Negotiating server initialization\");\n"
"                return this._negotiateServerInit();\n"
"\n"
"            default:\n"
"                // console.log(\"[VNC-DEBUG] Unknown init state:\", this._rfbInitState);\n"
"                return this._fail(\"Unknown init state (state: \" +\n"
"                                   this._rfbInitState + \")\");\n"
"        }\n"
"    }\n"
"\n"
"    // Resume authentication handshake after it was paused for some\n"
"    // reason, e.g. waiting for a password from the user\n"
"    _resumeAuthentication() {\n"
"        // We use setTimeout() so it's run in its own context, just like\n"
"        // it originally did via the WebSocket's event handler\n"
"        setTimeout(this._initMsg.bind(this), 0);\n"
"    }\n"
"\n"
"    _handleSetColourMapMsg() {\n"
"        Log.Debug(\"SetColorMapEntries\");\n"
"\n"
"        return this._fail(\"Unexpected SetColorMapEntries message\");\n"
"    }\n"
"\n"
"    _handleServerCutText() {\n"
"        Log.Debug(\"ServerCutText\");\n"
"\n"
"        if (this._sock.rQwait(\"ServerCutText header\", 7, 1)) { return false; }\n"
"\n"
"        this._sock.rQskipBytes(3);  // Padding\n"
"\n"
"        let length = this._sock.rQshift32();\n"
"        length = toSigned32bit(length);\n"
"\n"
"        if (this._sock.rQwait(\"ServerCutText content\", Math.abs(length), 8)) { return false; }\n"
"\n"
"        if (length >= 0) {\n"
"            //Standard msg\n"
"            const text = this._sock.rQshiftStr(length);\n"
"            if (this._viewOnly) {\n"
"                return true;\n"
"            }\n"
"\n"
"            this.dispatchEvent(new CustomEvent(\n"
"                \"clipboard\",\n"
"                { detail: { text: text } }));\n"
"\n"
"        } else {\n"
"            //Extended msg.\n"
"            length = Math.abs(length);\n"
"            const flags = this._sock.rQshift32();\n"
"            let formats = flags & 0x0000FFFF;\n"
"            let actions = flags & 0xFF000000;\n"
"\n"
"            let isCaps = (!!(actions & extendedClipboardActionCaps));\n"
"            if (isCaps) {\n"
"                this._clipboardServerCapabilitiesFormats = {};\n"
"                this._clipboardServerCapabilitiesActions = {};\n"
"\n"
"                // Update our server capabilities for Formats\n"
"                for (let i = 0; i <= 15; i++) {\n"
"                    let index = 1 << i;\n"
"\n"
"                    // Check if format flag is set.\n"
"                    if ((formats & index)) {\n"
"                        this._clipboardServerCapabilitiesFormats[index] = true;\n"
"                        // We don't send unsolicited clipboard, so we\n"
"                        // ignore the size\n"
"                        this._sock.rQshift32();\n"
"                    }\n"
"                }\n"
"\n"
"                // Update our server capabilities for Actions\n"
"                for (let i = 24; i <= 31; i++) {\n"
"                    let index = 1 << i;\n"
"                    this._clipboardServerCapabilitiesActions[index] = !!(actions & index);\n"
"                }\n"
"\n"
"                /*  Caps handling done, send caps with the clients\n"
"                    capabilities set as a response */\n"
"                let clientActions = [\n"
"                    extendedClipboardActionCaps,\n"
"                    extendedClipboardActionRequest,\n"
"                    extendedClipboardActionPeek,\n"
"                    extendedClipboardActionNotify,\n"
"                    extendedClipboardActionProvide\n"
"                ];\n"
"                RFB.messages.extendedClipboardCaps(this._sock, clientActions, {extendedClipboardFormatText: 0});\n"
"\n"
"            } else if (actions === extendedClipboardActionRequest) {\n"
"                if (this._viewOnly) {\n"
"                    return true;\n"
"                }\n"
"\n"
"                // Check if server has told us it can handle Provide and there is clipboard data to send.\n"
"                if (this._clipboardText != null &&\n"
"                    this._clipboardServerCapabilitiesActions[extendedClipboardActionProvide]) {\n"
"\n"
"                    if (formats & extendedClipboardFormatText) {\n"
"                        RFB.messages.extendedClipboardProvide(this._sock, [extendedClipboardFormatText], [this._clipboardText]);\n"
"                    }\n"
"                }\n"
"\n"
"            } else if (actions === extendedClipboardActionPeek) {\n"
"                if (this._viewOnly) {\n"
"                    return true;\n"
"                }\n"
"\n"
"                if (this._clipboardServerCapabilitiesActions[extendedClipboardActionNotify]) {\n"
"\n"
"                    if (this._clipboardText != null) {\n"
"                        RFB.messages.extendedClipboardNotify(this._sock, [extendedClipboardFormatText]);\n"
"                    } else {\n"
"                        RFB.messages.extendedClipboardNotify(this._sock, []);\n"
"                    }\n"
"                }\n"
"\n"
"            } else if (actions === extendedClipboardActionNotify) {\n"
"                if (this._viewOnly) {\n"
"                    return true;\n"
"                }\n"
"\n"
"                if (this._clipboardServerCapabilitiesActions[extendedClipboardActionRequest]) {\n"
"\n"
"                    if (formats & extendedClipboardFormatText) {\n"
"                        RFB.messages.extendedClipboardRequest(this._sock, [extendedClipboardFormatText]);\n"
"                    }\n"
"                }\n"
"\n"
"            } else if (actions === extendedClipboardActionProvide) {\n"
"                if (this._viewOnly) {\n"
"                    return true;\n"
"                }\n"
"\n"
"                if (!(formats & extendedClipboardFormatText)) {\n"
"                    return true;\n"
"                }\n"
"                // Ignore what we had in our clipboard client side.\n"
"                this._clipboardText = null;\n"
"\n"
"                // FIXME: Should probably verify that this data was actually requested\n"
"                let zlibStream = this._sock.rQshiftBytes(length - 4);\n"
"                let streamInflator = new Inflator();\n"
"                let textData = null;\n"
"\n"
"                streamInflator.setInput(zlibStream);\n"
"                for (let i = 0; i <= 15; i++) {\n"
"                    let format = 1 << i;\n"
"\n"
"                    if (formats & format) {\n"
"\n"
"                        let size = 0x00;\n"
"                        let sizeArray = streamInflator.inflate(4);\n"
"\n"
"                        size |= (sizeArray[0] << 24);\n"
"                        size |= (sizeArray[1] << 16);\n"
"                        size |= (sizeArray[2] << 8);\n"
"                        size |= (sizeArray[3]);\n"
"                        let chunk = streamInflator.inflate(size);\n"
"\n"
"                        if (format === extendedClipboardFormatText) {\n"
"                            textData = chunk;\n"
"                        }\n"
"                    }\n"
"                }\n"
"                streamInflator.setInput(null);\n"
"\n"
"                if (textData !== null) {\n"
"                    let tmpText = \"\";\n"
"                    for (let i = 0; i < textData.length; i++) {\n"
"                        tmpText += String.fromCharCode(textData[i]);\n"
"                    }\n"
"                    textData = tmpText;\n"
"\n"
"                    textData = decodeUTF8(textData);\n"
"                    if ((textData.length > 0) && \"\\0\" === textData.charAt(textData.length - 1)) {\n"
"                        textData = textData.slice(0, -1);\n"
"                    }\n"
"\n"
"                    textData = textData.replace(\"\\r\\n\", \"\\n\");\n"
"\n"
"                    this.dispatchEvent(new CustomEvent(\n"
"                        \"clipboard\",\n"
"                        { detail: { text: textData } }));\n"
"                }\n"
"            } else {\n"
"                return this._fail(\"Unexpected action in extended clipboard message: \" + actions);\n"
"            }\n"
"        }\n"
"        return true;\n"
"    }\n"
"\n"
"    _handleServerFenceMsg() {\n"
"        if (this._sock.rQwait(\"ServerFence header\", 8, 1)) { return false; }\n"
"        this._sock.rQskipBytes(3); // Padding\n"
"        let flags = this._sock.rQshift32();\n"
"        let length = this._sock.rQshift8();\n"
"\n"
"        if (this._sock.rQwait(\"ServerFence payload\", length, 9)) { return false; }\n"
"\n"
"        if (length > 64) {\n"
"            Log.Warn(\"Bad payload length (\" + length + \") in fence response\");\n"
"            length = 64;\n"
"        }\n"
"\n"
"        const payload = this._sock.rQshiftStr(length);\n"
"\n"
"        this._supportsFence = true;\n"
"\n"
"        /*\n"
"         * Fence flags\n"
"         *\n"
"         *  (1<<0)  - BlockBefore\n"
"         *  (1<<1)  - BlockAfter\n"
"         *  (1<<2)  - SyncNext\n"
"         *  (1<<31) - Request\n"
"         */\n"
"\n"
"        if (!(flags & (1<<31))) {\n"
"            return this._fail(\"Unexpected fence response\");\n"
"        }\n"
"\n"
"        // Filter out unsupported flags\n"
"        // FIXME: support syncNext\n"
"        flags &= (1<<0) | (1<<1);\n"
"\n"
"        // BlockBefore and BlockAfter are automatically handled by\n"
"        // the fact that we process each incoming message\n"
"        // synchronuosly.\n"
"        RFB.messages.clientFence(this._sock, flags, payload);\n"
"\n"
"        return true;\n"
"    }\n"
"\n"
"    _handleXvpMsg() {\n"
"        if (this._sock.rQwait(\"XVP version and message\", 3, 1)) { return false; }\n"
"        this._sock.rQskipBytes(1);  // Padding\n"
"        const xvpVer = this._sock.rQshift8();\n"
"        const xvpMsg = this._sock.rQshift8();\n"
"\n"
"        switch (xvpMsg) {\n"
"            case 0:  // XVP_FAIL\n"
"                Log.Error(\"XVP Operation Failed\");\n"
"                break;\n"
"            case 1:  // XVP_INIT\n"
"                this._rfbXvpVer = xvpVer;\n"
"                Log.Info(\"XVP extensions enabled (version \" + this._rfbXvpVer + \")\");\n"
"                this._setCapability(\"power\", true);\n"
"                break;\n"
"            default:\n"
"                this._fail(\"Illegal server XVP message (msg: \" + xvpMsg + \")\");\n"
"                break;\n"
"        }\n"
"\n"
"        return true;\n"
"    }\n"
"\n"
"    _normalMsg() {\n"
"        let msgType;\n"
"        if (this._FBU.rects > 0) {\n"
"            msgType = 0;\n"
"        } else {\n"
"            if (this._sock.rQlen < 1) {\n"
"                return false;\n"
"            }\n"
"            msgType = this._sock.rQshift8();\n"
"        }\n"
"\n"
"        console.log(\"[VNC-MESSAGE] Processing message type:\", msgType, \"FBU.rects:\", this._FBU.rects, \"rQlen:\", this._sock.rQlen);\n"
"\n"
"        let first, ret;\n"
"        switch (msgType) {\n"
"            case 0:  // FramebufferUpdate\n"
"                console.log(\"[VNC-DEBUG] Handling FramebufferUpdate\");\n"
"                ret = this._framebufferUpdate();\n"
"                if (ret && !this._enabledContinuousUpdates) {\n"
"                    console.log(\"[VNC-DEBUG] Sending FramebufferUpdateRequest\");\n"
"                    RFB.messages.fbUpdateRequest(this._sock, true, 0, 0,\n"
"                                                   this._fbWidth, this._fbHeight);\n"
"                }\n"
"                return ret;\n"
"\n"
"            case 1:  // SetColorMapEntries\n"
"                return this._handleSetColourMapMsg();\n"
"\n"
"            case 2:  // Bell\n"
"                Log.Debug(\"Bell\");\n"
"                this.dispatchEvent(new CustomEvent(\n"
"                    \"bell\",\n"
"                    { detail: {} }));\n"
"                return true;\n"
"\n"
"            case 3:  // ServerCutText\n"
"                return this._handleServerCutText();\n"
"\n"
"            case 150: // EndOfContinuousUpdates\n"
"                first = !this._supportsContinuousUpdates;\n"
"                this._supportsContinuousUpdates = true;\n"
"                this._enabledContinuousUpdates = false;\n"
"                if (first) {\n"
"                    this._enabledContinuousUpdates = true;\n"
"                    this._updateContinuousUpdates();\n"
"                    Log.Info(\"Enabling continuous updates.\");\n"
"                } else {\n"
"                    // FIXME: We need to send a framebufferupdaterequest here\n"
"                    // if we add support for turning off continuous updates\n"
"                }\n"
"                return true;\n"
"\n"
"            case 248: // ServerFence\n"
"                return this._handleServerFenceMsg();\n"
"\n"
"            case 250:  // XVP\n"
"                return this._handleXvpMsg();\n"
"\n"
"            default:\n"
"                Log.Warn(\"Ignoring unknown server message (type \" + msgType + \")\");\n"
"                Log.Debug(\"sock.rQslice(0, 30): \" + this._sock.rQslice(0, 30));\n"
"                // Skip the unknown message by consuming any remaining data in the current frame\n"
"                this._sock.rQskipBytes(this._sock.rQlen);\n"
"                return true;\n"
"        }\n"
"    }\n"
"\n"
"    _onFlush() {\n"
"        this._flushing = false;\n"
"        // Resume processing\n"
"        if (this._sock.rQlen > 0) {\n"
"            this._handleMessage();\n"
"        }\n"
"    }\n"
"\n"
"    _framebufferUpdate() {\n"
"        console.log(\"[VNC-FBU] _framebufferUpdate called, FBU.rects:\", this._FBU.rects, \"rQlen:\", this._sock.rQlen);\n"
"        if (this._FBU.rects === 0) {\n"
"            if (this._sock.rQwait(\"FBU header\", 3, 1)) {\n"
"                console.log(\"[VNC-FBU] Waiting for FBU header, not enough data\");\n"
"                return false;\n"
"            }\n"
"            this._sock.rQskipBytes(1);  // Padding\n"
"            this._FBU.rects = this._sock.rQshift16();\n"
"            console.log(\"[VNC-FBU] Received FBU header, rects:\", this._FBU.rects);\n"
"\n"
"            // Validate rectangle count - should be reasonable (not 65534 or similar corruption)\n"
"            if (this._FBU.rects > 10000 || this._FBU.rects < 0) {\n"
"                console.log(\"[VNC-FBU] Invalid rectangle count:\", this._FBU.rects, \"- skipping corrupted message\");\n"
"                Log.Warn(\"Invalid rectangle count in FramebufferUpdate (\" + this._FBU.rects + \"), skipping corrupted message\");\n"
"                // Skip the entire FramebufferUpdate message by consuming all remaining data for this message\n"
"                // We need to skip past all the rectangle data\n"
"                const skipBytes = this._FBU.rects * 12; // Each rectangle header is 12 bytes\n"
"                if (this._sock.rQlen >= skipBytes) {\n"
"                    this._sock.rQskipBytes(skipBytes);\n"
"                } else {\n"
"                    return false; // Wait for more data\n"
"                }\n"
"                this._FBU.rects = 0; // Reset for next message\n"
"                return true; // Continue processing\n"
"            }\n"
"\n"
"            // Make sure the previous frame is fully rendered first\n"
"            // to avoid building up an excessive queue\n"
"            if (this._display.pending()) {\n"
"                console.log(\"[VNC-FBU] Display pending, flushing and returning false\");\n"
"                this._flushing = true;\n"
"                this._display.flush();\n"
"                return false;\n"
"            }\n"
"        }\n"
"\n"
"        while (this._FBU.rects > 0) {\n"
"            if (this._FBU.encoding === null) {\n"
"                if (this._sock.rQwait(\"rect header\", 12)) {\n"
"                    return false;\n"
"                }\n"
"                /* New FramebufferUpdate */\n"
"\n"
"                const hdr = this._sock.rQshiftBytes(12);\n"
"                this._FBU.x        = (hdr[0] << 8) + hdr[1];\n"
"                this._FBU.y        = (hdr[2] << 8) + hdr[3];\n"
"                this._FBU.width    = (hdr[4] << 8) + hdr[5];\n"
"                this._FBU.height   = (hdr[6] << 8) + hdr[7];\n"
"                this._FBU.encoding = parseInt((hdr[8] << 24) + (hdr[9] << 16) +\n"
"                                              (hdr[10] << 8) + hdr[11], 10);\n"
"            }\n"
"\n"
"            if (!this._handleRect()) {\n"
"                return false;\n"
"            }\n"
"\n"
"            this._FBU.rects--;\n"
"            this._FBU.encoding = null;\n"
"        }\n"
"\n"
"        this._display.flip();\n"
"\n"
"        return true;  // We finished this FBU\n"
"    }\n"
"\n"
"    _handleRect() {\n"
"        console.log(\"[VNC-RECT] _handleRect called, encoding:\", this._FBU.encoding, \"x:\", this._FBU.x, \"y:\", this._FBU.y, \"width:\", this._FBU.width, \"height:\", this._FBU.height);\n"
"        switch (this._FBU.encoding) {\n"
"            case encodings.pseudoEncodingLastRect:\n"
"                console.log(\"[VNC-RECT] Handling pseudoEncodingLastRect\");\n"
"                this._FBU.rects = 1; // Will be decreased when we return\n"
"                return true;\n"
"\n"
"            case encodings.pseudoEncodingVMwareCursor:\n"
"                console.log(\"[VNC-RECT] Handling pseudoEncodingVMwareCursor\");\n"
"                return this._handleVMwareCursor();\n"
"\n"
"            case encodings.pseudoEncodingCursor:\n"
"                console.log(\"[VNC-RECT] Handling pseudoEncodingCursor\");\n"
"                return this._handleCursor();\n"
"\n"
"            case encodings.pseudoEncodingQEMUExtendedKeyEvent:\n"
"                console.log(\"[VNC-RECT] Handling pseudoEncodingQEMUExtendedKeyEvent\");\n"
"                this._qemuExtKeyEventSupported = true;\n"
"                return true;\n"
"\n"
"            case encodings.pseudoEncodingDesktopName:\n"
"                console.log(\"[VNC-RECT] Handling pseudoEncodingDesktopName\");\n"
"                return this._handleDesktopName();\n"
"\n"
"            case encodings.pseudoEncodingDesktopSize:\n"
"                console.log(\"[VNC-RECT] Handling pseudoEncodingDesktopSize\");\n"
"                this._resize(this._FBU.width, this._FBU.height);\n"
"                return true;\n"
"\n"
"            case encodings.pseudoEncodingExtendedDesktopSize:\n"
"                console.log(\"[VNC-RECT] Handling pseudoEncodingExtendedDesktopSize\");\n"
"                return this._handleExtendedDesktopSize();\n"
"\n"
"            default:\n"
"                console.log(\"[VNC-RECT] Handling data rect with encoding:\", this._FBU.encoding);\n"
"                return this._handleDataRect();\n"
"        }\n"
"    }\n"
"\n"
"    _handleVMwareCursor() {\n"
"        const hotx = this._FBU.x;  // hotspot-x\n"
"        const hoty = this._FBU.y;  // hotspot-y\n"
"        const w = this._FBU.width;\n"
"        const h = this._FBU.height;\n"
"        if (this._sock.rQwait(\"VMware cursor encoding\", 1)) {\n"
"            return false;\n"
"        }\n"
"\n"
"        const cursorType = this._sock.rQshift8();\n"
"\n"
"        this._sock.rQshift8(); //Padding\n"
"\n"
"        let rgba;\n"
"        const bytesPerPixel = 4;\n"
"\n"
"        //Classic cursor\n"
"        if (cursorType == 0) {\n"
"            //Used to filter away unimportant bits.\n"
"            //OR is used for correct conversion in js.\n"
"            const PIXEL_MASK = 0xffffff00 | 0;\n"
"            rgba = new Array(w * h * bytesPerPixel);\n"
"\n"
"            if (this._sock.rQwait(\"VMware cursor classic encoding\",\n"
"                                  (w * h * bytesPerPixel) * 2, 2)) {\n"
"                return false;\n"
"            }\n"
"\n"
"            let andMask = new Array(w * h);\n"
"            for (let pixel = 0; pixel < (w * h); pixel++) {\n"
"                andMask[pixel] = this._sock.rQshift32();\n"
"            }\n"
"\n"
"            let xorMask = new Array(w * h);\n"
"            for (let pixel = 0; pixel < (w * h); pixel++) {\n"
"                xorMask[pixel] = this._sock.rQshift32();\n"
"            }\n"
"\n"
"            for (let pixel = 0; pixel < (w * h); pixel++) {\n"
"                if (andMask[pixel] == 0) {\n"
"                    //Fully opaque pixel\n"
"                    let bgr = xorMask[pixel];\n"
"                    let r   = bgr >> 8  & 0xff;\n"
"                    let g   = bgr >> 16 & 0xff;\n"
"                    let b   = bgr >> 24 & 0xff;\n"
"\n"
"                    rgba[(pixel * bytesPerPixel)     ] = r;    //r\n"
"                    rgba[(pixel * bytesPerPixel) + 1 ] = g;    //g\n"
"                    rgba[(pixel * bytesPerPixel) + 2 ] = b;    //b\n"
"                    rgba[(pixel * bytesPerPixel) + 3 ] = 0xff; //a\n"
"\n"
"                } else if ((andMask[pixel] & PIXEL_MASK) ==\n"
"                           PIXEL_MASK) {\n"
"                    //Only screen value matters, no mouse colouring\n"
"                    if (xorMask[pixel] == 0) {\n"
"                        //Transparent pixel\n"
"                        rgba[(pixel * bytesPerPixel)     ] = 0x00;\n"
"                        rgba[(pixel * bytesPerPixel) + 1 ] = 0x00;\n"
"                        rgba[(pixel * bytesPerPixel) + 2 ] = 0x00;\n"
"                        rgba[(pixel * bytesPerPixel) + 3 ] = 0x00;\n"
"\n"
"                    } else if ((xorMask[pixel] & PIXEL_MASK) ==\n"
"                               PIXEL_MASK) {\n"
"                        //Inverted pixel, not supported in browsers.\n"
"                        //Fully opaque instead.\n"
"                        rgba[(pixel * bytesPerPixel)     ] = 0x00;\n"
"                        rgba[(pixel * bytesPerPixel) + 1 ] = 0x00;\n"
"                        rgba[(pixel * bytesPerPixel) + 2 ] = 0x00;\n"
"                        rgba[(pixel * bytesPerPixel) + 3 ] = 0xff;\n"
"\n"
"                    } else {\n"
"                        //Unhandled xorMask\n"
"                        rgba[(pixel * bytesPerPixel)     ] = 0x00;\n"
"                        rgba[(pixel * bytesPerPixel) + 1 ] = 0x00;\n"
"                        rgba[(pixel * bytesPerPixel) + 2 ] = 0x00;\n"
"                        rgba[(pixel * bytesPerPixel) + 3 ] = 0xff;\n"
"                    }\n"
"\n"
"                } else {\n"
"                    //Unhandled andMask\n"
"                    rgba[(pixel * bytesPerPixel)     ] = 0x00;\n"
"                    rgba[(pixel * bytesPerPixel) + 1 ] = 0x00;\n"
"                    rgba[(pixel * bytesPerPixel) + 2 ] = 0x00;\n"
"                    rgba[(pixel * bytesPerPixel) + 3 ] = 0xff;\n"
"                }\n"
"            }\n"
"\n"
"        //Alpha cursor.\n"
"        } else if (cursorType == 1) {\n"
"            if (this._sock.rQwait(\"VMware cursor alpha encoding\",\n"
"                                  (w * h * 4), 2)) {\n"
"                return false;\n"
"            }\n"
"\n"
"            rgba = new Array(w * h * bytesPerPixel);\n"
"\n"
"            for (let pixel = 0; pixel < (w * h); pixel++) {\n"
"                let data = this._sock.rQshift32();\n"
"\n"
"                rgba[(pixel * 4)     ] = data >> 24 & 0xff; //r\n"
"                rgba[(pixel * 4) + 1 ] = data >> 16 & 0xff; //g\n"
"                rgba[(pixel * 4) + 2 ] = data >> 8 & 0xff;  //b\n"
"                rgba[(pixel * 4) + 3 ] = data & 0xff;       //a\n"
"            }\n"
"\n"
"        } else {\n"
"            Log.Warn(\"The given cursor type is not supported: \"\n"
"                      + cursorType + \" given.\");\n"
"            return false;\n"
"        }\n"
"\n"
"        this._updateCursor(rgba, hotx, hoty, w, h);\n"
"\n"
"        return true;\n"
"    }\n"
"\n"
"    _handleCursor() {\n"
"        const hotx = this._FBU.x;  // hotspot-x\n"
"        const hoty = this._FBU.y;  // hotspot-y\n"
"        const w = this._FBU.width;\n"
"        const h = this._FBU.height;\n"
"\n"
"        const pixelslength = w * h * 4;\n"
"        const masklength = Math.ceil(w / 8) * h;\n"
"\n"
"        let bytes = pixelslength + masklength;\n"
"        if (this._sock.rQwait(\"cursor encoding\", bytes)) {\n"
"            return false;\n"
"        }\n"
"\n"
"        // Decode from BGRX pixels + bit mask to RGBA\n"
"        const pixels = this._sock.rQshiftBytes(pixelslength);\n"
"        const mask = this._sock.rQshiftBytes(masklength);\n"
"        let rgba = new Uint8Array(w * h * 4);\n"
"\n"
"        let pixIdx = 0;\n"
"        for (let y = 0; y < h; y++) {\n"
"            for (let x = 0; x < w; x++) {\n"
"                let maskIdx = y * Math.ceil(w / 8) + Math.floor(x / 8);\n"
"                let alpha = (mask[maskIdx] << (x % 8)) & 0x80 ? 255 : 0;\n"
"                rgba[pixIdx    ] = pixels[pixIdx + 2];\n"
"                rgba[pixIdx + 1] = pixels[pixIdx + 1];\n"
"                rgba[pixIdx + 2] = pixels[pixIdx];\n"
"                rgba[pixIdx + 3] = alpha;\n"
"                pixIdx += 4;\n"
"            }\n"
"        }\n"
"\n"
"        this._updateCursor(rgba, hotx, hoty, w, h);\n"
"\n"
"        return true;\n"
"    }\n"
"\n"
"    _handleDesktopName() {\n"
"        if (this._sock.rQwait(\"DesktopName\", 4)) {\n"
"            return false;\n"
"        }\n"
"\n"
"        let length = this._sock.rQshift32();\n"
"\n"
"        if (this._sock.rQwait(\"DesktopName\", length, 4)) {\n"
"            return false;\n"
"        }\n"
"\n"
"        let name = this._sock.rQshiftStr(length);\n"
"        name = decodeUTF8(name, true);\n"
"\n"
"        this._setDesktopName(name);\n"
"\n"
"        return true;\n"
"    }\n"
"\n"
"    _handleExtendedDesktopSize() {\n"
"        if (this._sock.rQwait(\"ExtendedDesktopSize\", 4)) {\n"
"            return false;\n"
"        }\n"
"\n"
"        const numberOfScreens = this._sock.rQpeek8();\n"
"\n"
"        let bytes = 4 + (numberOfScreens * 16);\n"
"        if (this._sock.rQwait(\"ExtendedDesktopSize\", bytes)) {\n"
"            return false;\n"
"        }\n"
"\n"
"        const firstUpdate = !this._supportsSetDesktopSize;\n"
"        this._supportsSetDesktopSize = true;\n"
"\n"
"        // Normally we only apply the current resize mode after a\n"
"        // window resize event. However there is no such trigger on the\n"
"        // initial connect. And we don't know if the server supports\n"
"        // resizing until we've gotten here.\n"
"        if (firstUpdate) {\n"
"            this._requestRemoteResize();\n"
"        }\n"
"\n"
"        this._sock.rQskipBytes(1);  // number-of-screens\n"
"        this._sock.rQskipBytes(3);  // padding\n"
"\n"
"        for (let i = 0; i < numberOfScreens; i += 1) {\n"
"            // Save the id and flags of the first screen\n"
"            if (i === 0) {\n"
"                this._screenID = this._sock.rQshiftBytes(4);    // id\n"
"                this._sock.rQskipBytes(2);                       // x-position\n"
"                this._sock.rQskipBytes(2);                       // y-position\n"
"                this._sock.rQskipBytes(2);                       // width\n"
"                this._sock.rQskipBytes(2);                       // height\n"
"                this._screenFlags = this._sock.rQshiftBytes(4); // flags\n"
"            } else {\n"
"                this._sock.rQskipBytes(16);\n"
"            }\n"
"        }\n"
"\n"
"        /*\n"
"         * The x-position indicates the reason for the change:\n"
"         *\n"
"         *  0 - server resized on its own\n"
"         *  1 - this client requested the resize\n"
"         *  2 - another client requested the resize\n"
"         */\n"
"\n"
"        // We need to handle errors when we requested the resize.\n"
"        if (this._FBU.x === 1 && this._FBU.y !== 0) {\n"
"            let msg = \"\";\n"
"            // The y-position indicates the status code from the server\n"
"            switch (this._FBU.y) {\n"
"                case 1:\n"
"                    msg = \"Resize is administratively prohibited\";\n"
"                    break;\n"
"                case 2:\n"
"                    msg = \"Out of resources\";\n"
"                    break;\n"
"                case 3:\n"
"                    msg = \"Invalid screen layout\";\n"
"                    break;\n"
"                default:\n"
"                    msg = \"Unknown reason\";\n"
"                    break;\n"
"            }\n"
"            Log.Warn(\"Server did not accept the resize request: \"\n"
"                     + msg);\n"
"        } else {\n"
"            this._resize(this._FBU.width, this._FBU.height);\n"
"        }\n"
"\n"
"        return true;\n"
"    }\n"
"\n"
"    _handleDataRect() {\n"
"        console.log(\"[VNC-DATARECT] _handleDataRect called, encoding:\", this._FBU.encoding, \"rQlen:\", this._sock.rQlen);\n"
"        let decoder = this._decoders[this._FBU.encoding];\n"
"        if (!decoder) {\n"
"            console.log(\"[VNC-DATARECT] No decoder found for encoding:\", this._FBU.encoding, \"- skipping rectangle\");\n"
"            Log.Warn(\"Unsupported encoding (encoding: \" +\n"
"                        this._FBU.encoding + \"), skipping rectangle\");\n"
"\n"
"            // For unsupported encodings, we need to skip the rectangle data to avoid getting stuck\n"
"            // Calculate how much data this rectangle should contain\n"
"            // This is a rough estimate - most encodings need at least width*height bytes\n"
"            const minDataSize = this._FBU.width * this._FBU.height;\n"
"            console.log(\"[VNC-DATARECT] Skipping\", minDataSize, \"bytes for unsupported encoding\");\n"
"            if (this._sock.rQlen >= minDataSize) {\n"
"                // Skip a reasonable amount of data for this rectangle\n"
"                const skipSize = Math.min(minDataSize, this._sock.rQlen);\n"
"                this._sock.rQskipBytes(skipSize);\n"
"            } else {\n"
"                console.log(\"[VNC-DATARECT] Not enough data to skip, waiting for more\");\n"
"                return false; // Wait for more data\n"
"            }\n"
"            return true;\n"
"        }\n"
"\n"
"        console.log(\"[VNC-DATARECT] Calling decoder.decodeRect for\", decoder.constructor.name);\n"
"        try {\n"
"            const result = decoder.decodeRect(this._FBU.x, this._FBU.y,\n"
"                                      this._FBU.width, this._FBU.height,\n"
"                                      this._sock, this._display,\n"
"                                      this._fbDepth);\n"
"            console.log(\"[VNC-DATARECT] decodeRect returned:\", result);\n"
"            return result;\n"
"        } catch (err) {\n"
"            console.log(\"[VNC-DATARECT] Error decoding rect:\", err, \"- skipping corrupted rectangle\");\n"
"            Log.Warn(\"Error decoding rect: \" + err + \" - skipping corrupted rectangle\");\n"
"            // For decoding errors, also skip data to avoid getting stuck\n"
"            const minDataSize = this._FBU.width * this._FBU.height;\n"
"            if (this._sock.rQlen >= minDataSize) {\n"
"                const skipSize = Math.min(minDataSize, this._sock.rQlen);\n"
"                this._sock.rQskipBytes(skipSize);\n"
"            }\n"
"            return true; // Continue processing instead of failing\n"
"        }\n"
"    }\n"
"\n"
"    _updateContinuousUpdates() {\n"
"        if (!this._enabledContinuousUpdates) { return; }\n"
"\n"
"        RFB.messages.enableContinuousUpdates(this._sock, true, 0, 0,\n"
"                                             this._fbWidth, this._fbHeight);\n"
"    }\n"
"\n"
"    _resize(width, height) {\n"
"        this._fbWidth = width;\n"
"        this._fbHeight = height;\n"
"\n"
"        this._display.resize(this._fbWidth, this._fbHeight);\n"
"\n"
"        // Adjust the visible viewport based on the new dimensions\n"
"        this._updateClip();\n"
"        this._updateScale();\n"
"\n"
"        this._updateContinuousUpdates();\n"
"\n"
"        // Keep this size until browser client size changes\n"
"        this._saveExpectedClientSize();\n"
"    }\n"
"\n"
"    _xvpOp(ver, op) {\n"
"        if (this._rfbXvpVer < ver) { return; }\n"
"        Log.Info(\"Sending XVP operation \" + op + \" (version \" + ver + \")\");\n"
"        RFB.messages.xvpOp(this._sock, ver, op);\n"
"    }\n"
"\n"
"    _updateCursor(rgba, hotx, hoty, w, h) {\n"
"        this._cursorImage = {\n"
"            rgbaPixels: rgba,\n"
"            hotx: hotx, hoty: hoty, w: w, h: h,\n"
"        };\n"
"        this._refreshCursor();\n"
"    }\n"
"\n"
"    _shouldShowDotCursor() {\n"
"        // Called when this._cursorImage is updated\n"
"        if (!this._showDotCursor) {\n"
"            // User does not want to see the dot, so...\n"
"            return false;\n"
"        }\n"
"\n"
"        // The dot should not be shown if the cursor is already visible,\n"
"        // i.e. contains at least one not-fully-transparent pixel.\n"
"        // So iterate through all alpha bytes in rgba and stop at the\n"
"        // first non-zero.\n"
"        for (let i = 3; i < this._cursorImage.rgbaPixels.length; i += 4) {\n"
"            if (this._cursorImage.rgbaPixels[i]) {\n"
"                return false;\n"
"            }\n"
"        }\n"
"\n"
"        // At this point, we know that the cursor is fully transparent, and\n"
"        // the user wants to see the dot instead of this.\n"
"        return true;\n"
"    }\n"
"\n"
"    _refreshCursor() {\n"
"        if (this._rfbConnectionState !== \"connecting\" &&\n"
"            this._rfbConnectionState !== \"connected\") {\n"
"            return;\n"
"        }\n"
"        const image = this._shouldShowDotCursor() ? RFB.cursors.dot : this._cursorImage;\n"
"        this._cursor.change(image.rgbaPixels,\n"
"                            image.hotx, image.hoty,\n"
"                            image.w, image.h\n"
"        );\n"
"    }\n"
"\n"
"    static genDES(password, challenge) {\n"
"        const passwordChars = password.split('').map(c => c.charCodeAt(0));\n"
"        return (new DES(passwordChars)).encrypt(challenge);\n"
"    }\n"
"}\n"
"\n"
"// Class Methods\n"
"RFB.messages = {\n"
"    keyEvent(sock, keysym, down) {\n"
"        const buff = sock._sQ;\n"
"        const offset = sock._sQlen;\n"
"\n"
"        buff[offset] = 4;  // msg-type\n"
"        buff[offset + 1] = down;\n"
"\n"
"        buff[offset + 2] = 0;\n"
"        buff[offset + 3] = 0;\n"
"\n"
"        buff[offset + 4] = (keysym >> 24);\n"
"        buff[offset + 5] = (keysym >> 16);\n"
"        buff[offset + 6] = (keysym >> 8);\n"
"        buff[offset + 7] = keysym;\n"
"\n"
"        sock._sQlen += 8;\n"
"        sock.flush();\n"
"    },\n"
"\n"
"    QEMUExtendedKeyEvent(sock, keysym, down, keycode) {\n"
"        function getRFBkeycode(xtScanCode) {\n"
"            const upperByte = (keycode >> 8);\n"
"            const lowerByte = (keycode & 0x00ff);\n"
"            if (upperByte === 0xe0 && lowerByte < 0x7f) {\n"
"                return lowerByte | 0x80;\n"
"            }\n"
"            return xtScanCode;\n"
"        }\n"
"\n"
"        const buff = sock._sQ;\n"
"        const offset = sock._sQlen;\n"
"\n"
"        buff[offset] = 255; // msg-type\n"
"        buff[offset + 1] = 0; // sub msg-type\n"
"\n"
"        buff[offset + 2] = (down >> 8);\n"
"        buff[offset + 3] = down;\n"
"\n"
"        buff[offset + 4] = (keysym >> 24);\n"
"        buff[offset + 5] = (keysym >> 16);\n"
"        buff[offset + 6] = (keysym >> 8);\n"
"        buff[offset + 7] = keysym;\n"
"\n"
"        const RFBkeycode = getRFBkeycode(keycode);\n"
"\n"
"        buff[offset + 8] = (RFBkeycode >> 24);\n"
"        buff[offset + 9] = (RFBkeycode >> 16);\n"
"        buff[offset + 10] = (RFBkeycode >> 8);\n"
"        buff[offset + 11] = RFBkeycode;\n"
"\n"
"        sock._sQlen += 12;\n"
"        sock.flush();\n"
"    },\n"
"\n"
"    pointerEvent(sock, x, y, mask) {\n"
"        const buff = sock._sQ;\n"
"        const offset = sock._sQlen;\n"
"\n"
"        buff[offset] = 5; // msg-type\n"
"\n"
"        buff[offset + 1] = mask;\n"
"\n"
"        buff[offset + 2] = x >> 8;\n"
"        buff[offset + 3] = x;\n"
"\n"
"        buff[offset + 4] = y >> 8;\n"
"        buff[offset + 5] = y;\n"
"\n"
"        sock._sQlen += 6;\n"
"        sock.flush();\n"
"    },\n"
"\n"
"    // Used to build Notify and Request data.\n"
"    _buildExtendedClipboardFlags(actions, formats) {\n"
"        let data = new Uint8Array(4);\n"
"        let formatFlag = 0x00000000;\n"
"        let actionFlag = 0x00000000;\n"
"\n"
"        for (let i = 0; i < actions.length; i++) {\n"
"            actionFlag |= actions[i];\n"
"        }\n"
"\n"
"        for (let i = 0; i < formats.length; i++) {\n"
"            formatFlag |= formats[i];\n"
"        }\n"
"\n"
"        data[0] = actionFlag >> 24; // Actions\n"
"        data[1] = 0x00;             // Reserved\n"
"        data[2] = 0x00;             // Reserved\n"
"        data[3] = formatFlag;       // Formats\n"
"\n"
"        return data;\n"
"    },\n"
"\n"
"    extendedClipboardProvide(sock, formats, inData) {\n"
"        // Deflate incomming data and their sizes\n"
"        let deflator = new Deflator();\n"
"        let dataToDeflate = [];\n"
"\n"
"        for (let i = 0; i < formats.length; i++) {\n"
"            // We only support the format Text at this time\n"
"            if (formats[i] != extendedClipboardFormatText) {\n"
"                throw new Error(\"Unsupported extended clipboard format for Provide message.\");\n"
"            }\n"
"\n"
"            // Change lone \\r or \\n into \\r\\n as defined in rfbproto\n"
"            inData[i] = inData[i].replace(/\\r\\n|\\r|\\n/gm, \"\\r\\n\");\n"
"\n"
"            // Check if it already has \\0\n"
"            let text = encodeUTF8(inData[i] + \"\\0\");\n"
"\n"
"            dataToDeflate.push( (text.length >> 24) & 0xFF,\n"
"                                (text.length >> 16) & 0xFF,\n"
"                                (text.length >>  8) & 0xFF,\n"
"                                (text.length & 0xFF));\n"
"\n"
"            for (let j = 0; j < text.length; j++) {\n"
"                dataToDeflate.push(text.charCodeAt(j));\n"
"            }\n"
"        }\n"
"\n"
"        let deflatedData = deflator.deflate(new Uint8Array(dataToDeflate));\n"
"\n"
"        // Build data  to send\n"
"        let data = new Uint8Array(4 + deflatedData.length);\n"
"        data.set(RFB.messages._buildExtendedClipboardFlags([extendedClipboardActionProvide],\n"
"                                                           formats));\n"
"        data.set(deflatedData, 4);\n"
"\n"
"        RFB.messages.clientCutText(sock, data, true);\n"
"    },\n"
"\n"
"    extendedClipboardNotify(sock, formats) {\n"
"        let flags = RFB.messages._buildExtendedClipboardFlags([extendedClipboardActionNotify],\n"
"                                                              formats);\n"
"        RFB.messages.clientCutText(sock, flags, true);\n"
"    },\n"
"\n"
"    extendedClipboardRequest(sock, formats) {\n"
"        let flags = RFB.messages._buildExtendedClipboardFlags([extendedClipboardActionRequest],\n"
"                                                              formats);\n"
"        RFB.messages.clientCutText(sock, flags, true);\n"
"    },\n"
"\n"
"    extendedClipboardCaps(sock, actions, formats) {\n"
"        let formatKeys = Object.keys(formats);\n"
"        let data  = new Uint8Array(4 + (4 * formatKeys.length));\n"
"\n"
"        formatKeys.map(x => parseInt(x));\n"
"        formatKeys.sort((a, b) =>  a - b);\n"
"\n"
"        data.set(RFB.messages._buildExtendedClipboardFlags(actions, []));\n"
"\n"
"        let loopOffset = 4;\n"
"        for (let i = 0; i < formatKeys.length; i++) {\n"
"            data[loopOffset]     = formats[formatKeys[i]] >> 24;\n"
"            data[loopOffset + 1] = formats[formatKeys[i]] >> 16;\n"
"            data[loopOffset + 2] = formats[formatKeys[i]] >> 8;\n"
"            data[loopOffset + 3] = formats[formatKeys[i]] >> 0;\n"
"\n"
"            loopOffset += 4;\n"
"            data[3] |= (1 << formatKeys[i]); // Update our format flags\n"
"        }\n"
"\n"
"        RFB.messages.clientCutText(sock, data, true);\n"
"    },\n"
"\n"
"    clientCutText(sock, data, extended = false) {\n"
"        const buff = sock._sQ;\n"
"        const offset = sock._sQlen;\n"
"\n"
"        buff[offset] = 6; // msg-type\n"
"\n"
"        buff[offset + 1] = 0; // padding\n"
"        buff[offset + 2] = 0; // padding\n"
"        buff[offset + 3] = 0; // padding\n"
"\n"
"        let length;\n"
"        if (extended) {\n"
"            length = toUnsigned32bit(-data.length);\n"
"        } else {\n"
"            length = data.length;\n"
"        }\n"
"\n"
"        buff[offset + 4] = length >> 24;\n"
"        buff[offset + 5] = length >> 16;\n"
"        buff[offset + 6] = length >> 8;\n"
"        buff[offset + 7] = length;\n"
"\n"
"        sock._sQlen += 8;\n"
"\n"
"        // We have to keep track of from where in the data we begin creating the\n"
"        // buffer for the flush in the next iteration.\n"
"        let dataOffset = 0;\n"
"\n"
"        let remaining = data.length;\n"
"        while (remaining > 0) {\n"
"\n"
"            let flushSize = Math.min(remaining, (sock._sQbufferSize - sock._sQlen));\n"
"            for (let i = 0; i < flushSize; i++) {\n"
"                buff[sock._sQlen + i] = data[dataOffset + i];\n"
"            }\n"
"\n"
"            sock._sQlen += flushSize;\n"
"            sock.flush();\n"
"\n"
"            remaining -= flushSize;\n"
"            dataOffset += flushSize;\n"
"        }\n"
"\n"
"    },\n"
"\n"
"    setDesktopSize(sock, width, height, id, flags) {\n"
"        const buff = sock._sQ;\n"
"        const offset = sock._sQlen;\n"
"\n"
"        buff[offset] = 251;              // msg-type\n"
"        buff[offset + 1] = 0;            // padding\n"
"        buff[offset + 2] = width >> 8;   // width\n"
"        buff[offset + 3] = width;\n"
"        buff[offset + 4] = height >> 8;  // height\n"
"        buff[offset + 5] = height;\n"
"\n"
"        buff[offset + 6] = 1;            // number-of-screens\n"
"        buff[offset + 7] = 0;            // padding\n"
"\n"
"        // screen array\n"
"        buff[offset + 8] = id >> 24;     // id\n"
"        buff[offset + 9] = id >> 16;\n"
"        buff[offset + 10] = id >> 8;\n"
"        buff[offset + 11] = id;\n"
"        buff[offset + 12] = 0;           // x-position\n"
"        buff[offset + 13] = 0;\n"
"        buff[offset + 14] = 0;           // y-position\n"
"        buff[offset + 15] = 0;\n"
"        buff[offset + 16] = width >> 8;  // width\n"
"        buff[offset + 17] = width;\n"
"        buff[offset + 18] = height >> 8; // height\n"
"        buff[offset + 19] = height;\n"
"        buff[offset + 20] = flags >> 24; // flags\n"
"        buff[offset + 21] = flags >> 16;\n"
"        buff[offset + 22] = flags >> 8;\n"
"        buff[offset + 23] = flags;\n"
"\n"
"        sock._sQlen += 24;\n"
"        sock.flush();\n"
"    },\n"
"\n"
"    clientFence(sock, flags, payload) {\n"
"        const buff = sock._sQ;\n"
"        const offset = sock._sQlen;\n"
"\n"
"        buff[offset] = 248; // msg-type\n"
"\n"
"        buff[offset + 1] = 0; // padding\n"
"        buff[offset + 2] = 0; // padding\n"
"        buff[offset + 3] = 0; // padding\n"
"\n"
"        buff[offset + 4] = flags >> 24; // flags\n"
"        buff[offset + 5] = flags >> 16;\n"
"        buff[offset + 6] = flags >> 8;\n"
"        buff[offset + 7] = flags;\n"
"\n"
"        const n = payload.length;\n"
"\n"
"        buff[offset + 8] = n; // length\n"
"\n"
"        for (let i = 0; i < n; i++) {\n"
"            buff[offset + 9 + i] = payload.charCodeAt(i);\n"
"        }\n"
"\n"
"        sock._sQlen += 9 + n;\n"
"        sock.flush();\n"
"    },\n"
"\n"
"    enableContinuousUpdates(sock, enable, x, y, width, height) {\n"
"        const buff = sock._sQ;\n"
"        const offset = sock._sQlen;\n"
"\n"
"        buff[offset] = 150;             // msg-type\n"
"        buff[offset + 1] = enable;      // enable-flag\n"
"\n"
"        buff[offset + 2] = x >> 8;      // x\n"
"        buff[offset + 3] = x;\n"
"        buff[offset + 4] = y >> 8;      // y\n"
"        buff[offset + 5] = y;\n"
"        buff[offset + 6] = width >> 8;  // width\n"
"        buff[offset + 7] = width;\n"
"        buff[offset + 8] = height >> 8; // height\n"
"        buff[offset + 9] = height;\n"
"\n"
"        sock._sQlen += 10;\n"
"        sock.flush();\n"
"    },\n"
"\n"
"    pixelFormat(sock, depth, trueColor) {\n"
"        const buff = sock._sQ;\n"
"        const offset = sock._sQlen;\n"
"\n"
"        let bpp;\n"
"\n"
"        if (depth > 16) {\n"
"            bpp = 32;\n"
"        } else if (depth > 8) {\n"
"            bpp = 16;\n"
"        } else {\n"
"            bpp = 8;\n"
"        }\n"
"\n"
"        const bits = Math.floor(depth/3);\n"
"\n"
"        buff[offset] = 0;  // msg-type\n"
"\n"
"        buff[offset + 1] = 0; // padding\n"
"        buff[offset + 2] = 0; // padding\n"
"        buff[offset + 3] = 0; // padding\n"
"\n"
"        buff[offset + 4] = bpp;                 // bits-per-pixel\n"
"        buff[offset + 5] = depth;               // depth\n"
"        buff[offset + 6] = 0;                   // little-endian\n"
"        buff[offset + 7] = trueColor ? 1 : 0;  // true-color\n"
"\n"
"        buff[offset + 8] = 0;    // red-max\n"
"        buff[offset + 9] = (1 << bits) - 1;  // red-max\n"
"\n"
"        buff[offset + 10] = 0;   // green-max\n"
"        buff[offset + 11] = (1 << bits) - 1; // green-max\n"
"\n"
"        buff[offset + 12] = 0;   // blue-max\n"
"        buff[offset + 13] = (1 << bits) - 1; // blue-max\n"
"\n"
"        buff[offset + 14] = bits * 0; // red-shift\n"
"        buff[offset + 15] = bits * 1; // green-shift\n"
"        buff[offset + 16] = bits * 2; // blue-shift\n"
"\n"
"        buff[offset + 17] = 0;   // padding\n"
"        buff[offset + 18] = 0;   // padding\n"
"        buff[offset + 19] = 0;   // padding\n"
"\n"
"        sock._sQlen += 20;\n"
"        sock.flush();\n"
"    },\n"
"\n"
"    clientEncodings(sock, encodings) {\n"
"        const buff = sock._sQ;\n"
"        const offset = sock._sQlen;\n"
"\n"
"        buff[offset] = 2; // msg-type\n"
"        buff[offset + 1] = 0; // padding\n"
"\n"
"        buff[offset + 2] = encodings.length >> 8;\n"
"        buff[offset + 3] = encodings.length;\n"
"\n"
"        let j = offset + 4;\n"
"        for (let i = 0; i < encodings.length; i++) {\n"
"            const enc = encodings[i];\n"
"            buff[j] = enc >> 24;\n"
"            buff[j + 1] = enc >> 16;\n"
"            buff[j + 2] = enc >> 8;\n"
"            buff[j + 3] = enc;\n"
"\n"
"            j += 4;\n"
"        }\n"
"\n"
"        sock._sQlen += j - offset;\n"
"        sock.flush();\n"
"    },\n"
"\n"
"    fbUpdateRequest(sock, incremental, x, y, w, h) {\n"
"        const buff = sock._sQ;\n"
"        const offset = sock._sQlen;\n"
"\n"
"        if (typeof(x) === \"undefined\") { x = 0; }\n"
"        if (typeof(y) === \"undefined\") { y = 0; }\n"
"\n"
"        buff[offset] = 3;  // msg-type\n"
"        buff[offset + 1] = incremental ? 1 : 0;\n"
"\n"
"        buff[offset + 2] = (x >> 8) & 0xFF;\n"
"        buff[offset + 3] = x & 0xFF;\n"
"\n"
"        buff[offset + 4] = (y >> 8) & 0xFF;\n"
"        buff[offset + 5] = y & 0xFF;\n"
"\n"
"        buff[offset + 6] = (w >> 8) & 0xFF;\n"
"        buff[offset + 7] = w & 0xFF;\n"
"\n"
"        buff[offset + 8] = (h >> 8) & 0xFF;\n"
"        buff[offset + 9] = h & 0xFF;\n"
"\n"
"        sock._sQlen += 10;\n"
"        sock.flush();\n"
"    },\n"
"\n"
"    xvpOp(sock, ver, op) {\n"
"        const buff = sock._sQ;\n"
"        const offset = sock._sQlen;\n"
"\n"
"        buff[offset] = 250; // msg-type\n"
"        buff[offset + 1] = 0; // padding\n"
"\n"
"        buff[offset + 2] = ver;\n"
"        buff[offset + 3] = op;\n"
"\n"
"        sock._sQlen += 4;\n"
"        sock.flush();\n"
"    }\n"
"};\n"
"\n"
"RFB.cursors = {\n"
"    none: {\n"
"        rgbaPixels: new Uint8Array(),\n"
"        w: 0, h: 0,\n"
"        hotx: 0, hoty: 0,\n"
"    },\n"
"\n"
"    dot: {\n"
"        /* eslint-disable indent */\n"
"        rgbaPixels: new Uint8Array([\n"
"            255, 255, 255, 255,   0,   0,   0, 255, 255, 255, 255, 255,\n"
"              0,   0,   0, 255,   0,   0,   0,   0,   0,   0,  0,  255,\n"
"            255, 255, 255, 255,   0,   0,   0, 255, 255, 255, 255, 255,\n"
"        ]),\n"
"        /* eslint-enable indent */\n"
"        w: 3, h: 3,\n"
"        hotx: 1, hoty: 1,\n"
"    }\n"
"};\n"
"\n"
"// Make RFB available globally\n"
"window.RFB = RFB;\n"
"window.DISCONNECT_TIMEOUT = DISCONNECT_TIMEOUT;\n"
"window.DEFAULT_BACKGROUND = DEFAULT_BACKGROUND;\n"
"window.MOUSE_MOVE_DELAY = MOUSE_MOVE_DELAY;\n"
"window.WHEEL_STEP = WHEEL_STEP;\n"
"window.WHEEL_LINE_HEIGHT = WHEEL_LINE_HEIGHT;\n"
"window.GESTURE_ZOOMSENS = GESTURE_ZOOMSENS;\n"
"window.GESTURE_SCRLSENS = GESTURE_SCRLSENS;\n"
"window.DOUBLE_TAP_TIMEOUT = DOUBLE_TAP_TIMEOUT;\n"
"window.DOUBLE_TAP_THRESHOLD = DOUBLE_TAP_THRESHOLD;\n"
"window.securityTypeNone = securityTypeNone;\n"
"window.securityTypeVNCAuth = securityTypeVNCAuth;\n"
"window.securityTypeRA2ne = securityTypeRA2ne;\n"
"window.securityTypeTight = securityTypeTight;\n"
"window.securityTypeVeNCrypt = securityTypeVeNCrypt;\n"
"window.securityTypeXVP = securityTypeXVP;\n"
"window.securityTypeARD = securityTypeARD;\n"
"window.securityTypeMSLogonII = securityTypeMSLogonII;\n"
"window.securityTypeUnixLogon = securityTypeUnixLogon;\n"
"window.securityTypePlain = securityTypePlain;\n"
"window.extendedClipboardFormatText = extendedClipboardFormatText;\n"
"window.extendedClipboardFormatRtf = extendedClipboardFormatRtf;\n"
"window.extendedClipboardFormatHtml = extendedClipboardFormatHtml;\n"
"window.extendedClipboardFormatDib = extendedClipboardFormatDib;\n"
"window.extendedClipboardFormatFiles = extendedClipboardFormatFiles;\n"
"window.extendedClipboardActionCaps = extendedClipboardActionCaps;\n"
"window.extendedClipboardActionRequest = extendedClipboardActionRequest;\n"
"window.extendedClipboardActionPeek = extendedClipboardActionPeek;\n"
"window.extendedClipboardActionNotify = extendedClipboardActionNotify;\n"
"window.extendedClipboardActionProvide = extendedClipboardActionProvide;\n"
;
    if (strcmp(path, "/novnc/rfb.js") == 0) return novnc_rfb_js;
const char *novnc_util_browser_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2019 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" *\n"
" * Browser feature support detection\n"
" */\n"
"\n"
"\n"
"// Touch detection\n"
"let isTouchDevice = ('ontouchstart' in document.documentElement) ||\n"
"                                 // requried for Chrome debugger\n"
"                                 (document.ontouchstart !== undefined) ||\n"
"                                 // required for MS Surface\n"
"                                 (navigator.maxTouchPoints > 0) ||\n"
"                                 (navigator.msMaxTouchPoints > 0);\n"
"window.addEventListener('touchstart', function onFirstTouch() {\n"
"    isTouchDevice = true;\n"
"    window.removeEventListener('touchstart', onFirstTouch, false);\n"
"}, false);\n"
"\n"
"\n"
"// The goal is to find a certain physical width, the devicePixelRatio\n"
"// brings us a bit closer but is not optimal.\n"
"let dragThreshold = 10 * (window.devicePixelRatio || 1);\n"
"\n"
"let _supportsCursorURIs = false;\n"
"\n"
"try {\n"
"    const target = document.createElement('canvas');\n"
"    target.style.cursor = 'url(\"data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==\") 2 2, default';\n"
"\n"
"    if (target.style.cursor.indexOf(\"url\") === 0) {\n"
"        Log.Info(\"Data URI scheme cursor supported\");\n"
"        _supportsCursorURIs = true;\n"
"    } else {\n"
"        Log.Warn(\"Data URI scheme cursor not supported\");\n"
"    }\n"
"} catch (exc) {\n"
"    Log.Error(\"Data URI scheme cursor test exception: \" + exc);\n"
"}\n"
"\n"
"const supportsCursorURIs = _supportsCursorURIs;\n"
"\n"
"let _hasScrollbarGutter = true;\n"
"try {\n"
"    // Create invisible container\n"
"    const container = document.createElement('div');\n"
"    container.style.visibility = 'hidden';\n"
"    container.style.overflow = 'scroll'; // forcing scrollbars\n"
"    document.body.appendChild(container);\n"
"\n"
"    // Create a div and place it in the container\n"
"    const child = document.createElement('div');\n"
"    container.appendChild(child);\n"
"\n"
"    // Calculate the difference between the container's full width\n"
"    // and the child's width - the difference is the scrollbars\n"
"    const scrollbarWidth = (container.offsetWidth - child.offsetWidth);\n"
"\n"
"    // Clean up\n"
"    container.parentNode.removeChild(container);\n"
"\n"
"    _hasScrollbarGutter = scrollbarWidth != 0;\n"
"} catch (exc) {\n"
"    Log.Error(\"Scrollbar test exception: \" + exc);\n"
"}\n"
"const hasScrollbarGutter = _hasScrollbarGutter;\n"
"\n"
"/*\n"
" * The functions for detection of platforms and browsers below are exported\n"
" * but the use of these should be minimized as much as possible.\n"
" *\n"
" * It's better to use feature detection than platform detection.\n"
" */\n"
"\n"
"/* OS */\n"
"\n"
"function isMac() {\n"
"    return !!(/mac/i).exec(navigator.platform);\n"
"}\n"
"\n"
"function isWindows() {\n"
"    return !!(/win/i).exec(navigator.platform);\n"
"}\n"
"\n"
"function isIOS() {\n"
"    return (!!(/ipad/i).exec(navigator.platform) ||\n"
"            !!(/iphone/i).exec(navigator.platform) ||\n"
"            !!(/ipod/i).exec(navigator.platform));\n"
"}\n"
"\n"
"function isAndroid() {\n"
"    /* Android sets navigator.platform to Linux :/ */\n"
"    return !!navigator.userAgent.match('Android ');\n"
"}\n"
"\n"
"function isChromeOS() {\n"
"    /* ChromeOS sets navigator.platform to Linux :/ */\n"
"    return !!navigator.userAgent.match(' CrOS ');\n"
"}\n"
"\n"
"/* Browser */\n"
"\n"
"function isSafari() {\n"
"    return !!navigator.userAgent.match('Safari/...') &&\n"
"           !navigator.userAgent.match('Chrome/...') &&\n"
"           !navigator.userAgent.match('Chromium/...') &&\n"
"           !navigator.userAgent.match('Epiphany/...');\n"
"}\n"
"\n"
"function isFirefox() {\n"
"    return !!navigator.userAgent.match('Firefox/...') &&\n"
"           !navigator.userAgent.match('Seamonkey/...');\n"
"}\n"
"\n"
"function isChrome() {\n"
"    return !!navigator.userAgent.match('Chrome/...') &&\n"
"           !navigator.userAgent.match('Chromium/...') &&\n"
"           !navigator.userAgent.match('Edg/...') &&\n"
"           !navigator.userAgent.match('OPR/...');\n"
"}\n"
"\n"
"function isChromium() {\n"
"    return !!navigator.userAgent.match('Chromium/...');\n"
"}\n"
"\n"
"function isOpera() {\n"
"    return !!navigator.userAgent.match('OPR/...');\n"
"}\n"
"\n"
"function isEdge() {\n"
"    return !!navigator.userAgent.match('Edg/...');\n"
"}\n"
"\n"
"/* Engine */\n"
"\n"
"function isGecko() {\n"
"    return !!navigator.userAgent.match('Gecko/...');\n"
"}\n"
"\n"
"function isWebKit() {\n"
"    return !!navigator.userAgent.match('AppleWebKit/...') &&\n"
"           !navigator.userAgent.match('Chrome/...');\n"
"}\n"
"\n"
"function isBlink() {\n"
"    return !!navigator.userAgent.match('Chrome/...');\n"
"}\n"
"window.isTouchDevice = isTouchDevice;\n"
"window.dragThreshold = dragThreshold;\n"
"window._supportsCursorURIs = _supportsCursorURIs;\n"
"window.supportsCursorURIs = supportsCursorURIs;\n"
"window._hasScrollbarGutter = _hasScrollbarGutter;\n"
"window.hasScrollbarGutter = hasScrollbarGutter;\n"
"window.isMac = isMac;\n"
"window.isWindows = isWindows;\n"
"window.isIOS = isIOS;\n"
"window.isAndroid = isAndroid;\n"
"window.isChromeOS = isChromeOS;\n"
"window.isSafari = isSafari;\n"
"window.isFirefox = isFirefox;\n"
"window.isChromium = isChromium;\n"
"window.isOpera = isOpera;\n"
"window.isEdge = isEdge;\n"
"window.isGecko = isGecko;\n"
"window.isWebKit = isWebKit;\n"
"window.isBlink = isBlink;\n"
"\n"
"// Create browser object for compatibility\n"
"window.browser = {\n"
"    isMac: isMac,\n"
"    isWindows: isWindows,\n"
"    isIOS: isIOS,\n"
"    isAndroid: isAndroid,\n"
"    isChromeOS: isChromeOS,\n"
"    isSafari: isSafari,\n"
"    isFirefox: isFirefox,\n"
"    isChrome: isChrome,\n"
"    isChromium: isChromium,\n"
"    isOpera: isOpera,\n"
"    isEdge: isEdge,\n"
"    isGecko: isGecko,\n"
"    isWebKit: isWebKit,\n"
"    isBlink: isBlink\n"
"};\n"
;
    if (strcmp(path, "/novnc/util/browser.js") == 0) return novnc_util_browser_js;
const char *novnc_util_cursor_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2019 The noVNC Authors\n"
" * Licensed under MPL 2.0 or any later version (see LICENSE.txt)\n"
" */\n"
"\n"
"\n"
"const useFallback = !supportsCursorURIs || isTouchDevice;\n"
"\n"
"class Cursor {\n"
"    constructor() {\n"
"        this._target = null;\n"
"\n"
"        this._canvas = document.createElement('canvas');\n"
"\n"
"        if (useFallback) {\n"
"            this._canvas.style.position = 'fixed';\n"
"            this._canvas.style.zIndex = '65535';\n"
"            this._canvas.style.pointerEvents = 'none';\n"
"            // Safari on iOS can select the cursor image\n"
"            // https://bugs.webkit.org/show_bug.cgi?id=249223\n"
"            this._canvas.style.userSelect = 'none';\n"
"            this._canvas.style.WebkitUserSelect = 'none';\n"
"            // Can't use \"display\" because of Firefox bug #1445997\n"
"            this._canvas.style.visibility = 'hidden';\n"
"        }\n"
"\n"
"        this._position = { x: 0, y: 0 };\n"
"        this._hotSpot = { x: 0, y: 0 };\n"
"\n"
"        this._eventHandlers = {\n"
"            'mouseover': this._handleMouseOver.bind(this),\n"
"            'mouseleave': this._handleMouseLeave.bind(this),\n"
"            'mousemove': this._handleMouseMove.bind(this),\n"
"            'mouseup': this._handleMouseUp.bind(this),\n"
"        };\n"
"    }\n"
"\n"
"    attach(target) {\n"
"        if (this._target) {\n"
"            this.detach();\n"
"        }\n"
"\n"
"        this._target = target;\n"
"\n"
"        if (useFallback) {\n"
"            document.body.appendChild(this._canvas);\n"
"\n"
"            const options = { capture: true, passive: true };\n"
"            this._target.addEventListener('mouseover', this._eventHandlers.mouseover, options);\n"
"            this._target.addEventListener('mouseleave', this._eventHandlers.mouseleave, options);\n"
"            this._target.addEventListener('mousemove', this._eventHandlers.mousemove, options);\n"
"            this._target.addEventListener('mouseup', this._eventHandlers.mouseup, options);\n"
"        }\n"
"\n"
"        this.clear();\n"
"    }\n"
"\n"
"    detach() {\n"
"        if (!this._target) {\n"
"            return;\n"
"        }\n"
"\n"
"        if (useFallback) {\n"
"            const options = { capture: true, passive: true };\n"
"            this._target.removeEventListener('mouseover', this._eventHandlers.mouseover, options);\n"
"            this._target.removeEventListener('mouseleave', this._eventHandlers.mouseleave, options);\n"
"            this._target.removeEventListener('mousemove', this._eventHandlers.mousemove, options);\n"
"            this._target.removeEventListener('mouseup', this._eventHandlers.mouseup, options);\n"
"\n"
"            document.body.removeChild(this._canvas);\n"
"        }\n"
"\n"
"        this._target = null;\n"
"    }\n"
"\n"
"    change(rgba, hotx, hoty, w, h) {\n"
"        if ((w === 0) || (h === 0)) {\n"
"            this.clear();\n"
"            return;\n"
"        }\n"
"\n"
"        this._position.x = this._position.x + this._hotSpot.x - hotx;\n"
"        this._position.y = this._position.y + this._hotSpot.y - hoty;\n"
"        this._hotSpot.x = hotx;\n"
"        this._hotSpot.y = hoty;\n"
"\n"
"        let ctx = this._canvas.getContext('2d');\n"
"\n"
"        this._canvas.width = w;\n"
"        this._canvas.height = h;\n"
"\n"
"        let img = new ImageData(new Uint8ClampedArray(rgba), w, h);\n"
"        ctx.clearRect(0, 0, w, h);\n"
"        ctx.putImageData(img, 0, 0);\n"
"\n"
"        if (useFallback) {\n"
"            this._updatePosition();\n"
"        } else {\n"
"            let url = this._canvas.toDataURL();\n"
"            this._target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';\n"
"        }\n"
"    }\n"
"\n"
"    clear() {\n"
"        this._target.style.cursor = 'none';\n"
"        this._canvas.width = 0;\n"
"        this._canvas.height = 0;\n"
"        this._position.x = this._position.x + this._hotSpot.x;\n"
"        this._position.y = this._position.y + this._hotSpot.y;\n"
"        this._hotSpot.x = 0;\n"
"        this._hotSpot.y = 0;\n"
"    }\n"
"\n"
"    // Mouse events might be emulated, this allows\n"
"    // moving the cursor in such cases\n"
"    move(clientX, clientY) {\n"
"        if (!useFallback) {\n"
"            return;\n"
"        }\n"
"        // clientX/clientY are relative the _visual viewport_,\n"
"        // but our position is relative the _layout viewport_,\n"
"        // so try to compensate when we can\n"
"        if (window.visualViewport) {\n"
"            this._position.x = clientX + window.visualViewport.offsetLeft;\n"
"            this._position.y = clientY + window.visualViewport.offsetTop;\n"
"        } else {\n"
"            this._position.x = clientX;\n"
"            this._position.y = clientY;\n"
"        }\n"
"        this._updatePosition();\n"
"        let target = document.elementFromPoint(clientX, clientY);\n"
"        this._updateVisibility(target);\n"
"    }\n"
"\n"
"    _handleMouseOver(event) {\n"
"        // This event could be because we're entering the target, or\n"
"        // moving around amongst its sub elements. Let the move handler\n"
"        // sort things out.\n"
"        this._handleMouseMove(event);\n"
"    }\n"
"\n"
"    _handleMouseLeave(event) {\n"
"        // Check if we should show the cursor on the element we are leaving to\n"
"        this._updateVisibility(event.relatedTarget);\n"
"    }\n"
"\n"
"    _handleMouseMove(event) {\n"
"        this._updateVisibility(event.target);\n"
"\n"
"        this._position.x = event.clientX - this._hotSpot.x;\n"
"        this._position.y = event.clientY - this._hotSpot.y;\n"
"\n"
"        this._updatePosition();\n"
"    }\n"
"\n"
"    _handleMouseUp(event) {\n"
"        // We might get this event because of a drag operation that\n"
"        // moved outside of the target. Check what's under the cursor\n"
"        // now and adjust visibility based on that.\n"
"        let target = document.elementFromPoint(event.clientX, event.clientY);\n"
"        this._updateVisibility(target);\n"
"\n"
"        // Captures end with a mouseup but we can't know the event order of\n"
"        // mouseup vs releaseCapture.\n"
"        //\n"
"        // In the cases when releaseCapture comes first, the code above is\n"
"        // enough.\n"
"        //\n"
"        // In the cases when the mouseup comes first, we need wait for the\n"
"        // browser to flush all events and then check again if the cursor\n"
"        // should be visible.\n"
"        if (this._captureIsActive()) {\n"
"            window.setTimeout(() => {\n"
"                // We might have detached at this point\n"
"                if (!this._target) {\n"
"                    return;\n"
"                }\n"
"                // Refresh the target from elementFromPoint since queued events\n"
"                // might have altered the DOM\n"
"                target = document.elementFromPoint(event.clientX,\n"
"                                                   event.clientY);\n"
"                this._updateVisibility(target);\n"
"            }, 0);\n"
"        }\n"
"    }\n"
"\n"
"    _showCursor() {\n"
"        if (this._canvas.style.visibility === 'hidden') {\n"
"            this._canvas.style.visibility = '';\n"
"        }\n"
"    }\n"
"\n"
"    _hideCursor() {\n"
"        if (this._canvas.style.visibility !== 'hidden') {\n"
"            this._canvas.style.visibility = 'hidden';\n"
"        }\n"
"    }\n"
"\n"
"    // Should we currently display the cursor?\n"
"    // (i.e. are we over the target, or a child of the target without a\n"
"    // different cursor set)\n"
"    _shouldShowCursor(target) {\n"
"        if (!target) {\n"
"            return false;\n"
"        }\n"
"        // Easy case\n"
"        if (target === this._target) {\n"
"            return true;\n"
"        }\n"
"        // Other part of the DOM?\n"
"        if (!this._target.contains(target)) {\n"
"            return false;\n"
"        }\n"
"        // Has the child its own cursor?\n"
"        // FIXME: How can we tell that a sub element has an\n"
"        //        explicit \"cursor: none;\"?\n"
"        if (window.getComputedStyle(target).cursor !== 'none') {\n"
"            return false;\n"
"        }\n"
"        return true;\n"
"    }\n"
"\n"
"    _updateVisibility(target) {\n"
"        // When the cursor target has capture we want to show the cursor.\n"
"        // So, if a capture is active - look at the captured element instead.\n"
"        if (this._captureIsActive()) {\n"
"            target = document.captureElement;\n"
"        }\n"
"        if (this._shouldShowCursor(target)) {\n"
"            this._showCursor();\n"
"        } else {\n"
"            this._hideCursor();\n"
"        }\n"
"    }\n"
"\n"
"    _updatePosition() {\n"
"        this._canvas.style.left = this._position.x + \"px\";\n"
"        this._canvas.style.top = this._position.y + \"px\";\n"
"    }\n"
"\n"
"    _captureIsActive() {\n"
"        return document.captureElement &&\n"
"            document.documentElement.contains(document.captureElement);\n"
"    }\n"
"}\n"
"window.useFallback = useFallback;\n"
"window.Cursor = Cursor;\n"
;
    if (strcmp(path, "/novnc/util/cursor.js") == 0) return novnc_util_cursor_js;
const char *novnc_util_element_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2020 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" */\n"
"\n"
"/*\n"
" * HTML element utility functions\n"
" */\n"
"\n"
"function clientToElement(x, y, elem) {\n"
"    const bounds = elem.getBoundingClientRect();\n"
"    let pos = { x: 0, y: 0 };\n"
"    // Clip to target bounds\n"
"    if (x < bounds.left) {\n"
"        pos.x = 0;\n"
"    } else if (x >= bounds.right) {\n"
"        pos.x = bounds.width - 1;\n"
"    } else {\n"
"        pos.x = x - bounds.left;\n"
"    }\n"
"    if (y < bounds.top) {\n"
"        pos.y = 0;\n"
"    } else if (y >= bounds.bottom) {\n"
"        pos.y = bounds.height - 1;\n"
"    } else {\n"
"        pos.y = y - bounds.top;\n"
"    }\n"
"    return pos;\n"
"}\n"
"window.clientToElement = clientToElement;\n"
;
    if (strcmp(path, "/novnc/util/element.js") == 0) return novnc_util_element_js;
const char *novnc_util_events_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2018 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" */\n"
"\n"
"/*\n"
" * Cross-browser event and position routines\n"
" */\n"
"\n"
"function getPointerEvent(e) {\n"
"    return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e;\n"
"}\n"
"\n"
"function stopEvent(e) {\n"
"    e.stopPropagation();\n"
"    e.preventDefault();\n"
"}\n"
"\n"
"// Emulate Element.setCapture() when not supported\n"
"let _captureRecursion = false;\n"
"let _elementForUnflushedEvents = null;\n"
"document.captureElement = null;\n"
"function _captureProxy(e) {\n"
"    // Recursion protection as we'll see our own event\n"
"    if (_captureRecursion) return;\n"
"\n"
"    // Clone the event as we cannot dispatch an already dispatched event\n"
"    const newEv = new e.constructor(e.type, e);\n"
"\n"
"    _captureRecursion = true;\n"
"    if (document.captureElement) {\n"
"        document.captureElement.dispatchEvent(newEv);\n"
"    } else {\n"
"        _elementForUnflushedEvents.dispatchEvent(newEv);\n"
"    }\n"
"    _captureRecursion = false;\n"
"\n"
"    // Avoid double events\n"
"    e.stopPropagation();\n"
"\n"
"    // Respect the wishes of the redirected event handlers\n"
"    if (newEv.defaultPrevented) {\n"
"        e.preventDefault();\n"
"    }\n"
"\n"
"    // Implicitly release the capture on button release\n"
"    if (e.type === \"mouseup\") {\n"
"        releaseCapture();\n"
"    }\n"
"}\n"
"\n"
"// Follow cursor style of target element\n"
"function _capturedElemChanged() {\n"
"    const proxyElem = document.getElementById(\"noVNC_mouse_capture_elem\");\n"
"    proxyElem.style.cursor = window.getComputedStyle(document.captureElement).cursor;\n"
"}\n"
"\n"
"const _captureObserver = new MutationObserver(_capturedElemChanged);\n"
"\n"
"function setCapture(target) {\n"
"    if (target.setCapture) {\n"
"\n"
"        target.setCapture();\n"
"        document.captureElement = target;\n"
"    } else {\n"
"        // Release any existing capture in case this method is\n"
"        // called multiple times without coordination\n"
"        releaseCapture();\n"
"\n"
"        let proxyElem = document.getElementById(\"noVNC_mouse_capture_elem\");\n"
"\n"
"        if (proxyElem === null) {\n"
"            proxyElem = document.createElement(\"div\");\n"
"            proxyElem.id = \"noVNC_mouse_capture_elem\";\n"
"            proxyElem.style.position = \"fixed\";\n"
"            proxyElem.style.top = \"0px\";\n"
"            proxyElem.style.left = \"0px\";\n"
"            proxyElem.style.width = \"100%\";\n"
"            proxyElem.style.height = \"100%\";\n"
"            proxyElem.style.zIndex = 10000;\n"
"            proxyElem.style.display = \"none\";\n"
"            document.body.appendChild(proxyElem);\n"
"\n"
"            // This is to make sure callers don't get confused by having\n"
"            // our blocking element as the target\n"
"            proxyElem.addEventListener('contextmenu', _captureProxy);\n"
"\n"
"            proxyElem.addEventListener('mousemove', _captureProxy);\n"
"            proxyElem.addEventListener('mouseup', _captureProxy);\n"
"        }\n"
"\n"
"        document.captureElement = target;\n"
"\n"
"        // Track cursor and get initial cursor\n"
"        _captureObserver.observe(target, {attributes: true});\n"
"        _capturedElemChanged();\n"
"\n"
"        proxyElem.style.display = \"\";\n"
"\n"
"        // We listen to events on window in order to keep tracking if it\n"
"        // happens to leave the viewport\n"
"        window.addEventListener('mousemove', _captureProxy);\n"
"        window.addEventListener('mouseup', _captureProxy);\n"
"    }\n"
"}\n"
"\n"
"function releaseCapture() {\n"
"    if (document.releaseCapture) {\n"
"\n"
"        document.releaseCapture();\n"
"        document.captureElement = null;\n"
"\n"
"    } else {\n"
"        if (!document.captureElement) {\n"
"            return;\n"
"        }\n"
"\n"
"        // There might be events already queued. The event proxy needs\n"
"        // access to the captured element for these queued events.\n"
"        // E.g. contextmenu (right-click) in Microsoft Edge\n"
"        //\n"
"        // Before removing the capturedElem pointer we save it to a\n"
"        // temporary variable that the unflushed events can use.\n"
"        _elementForUnflushedEvents = document.captureElement;\n"
"        document.captureElement = null;\n"
"\n"
"        _captureObserver.disconnect();\n"
"\n"
"        const proxyElem = document.getElementById(\"noVNC_mouse_capture_elem\");\n"
"        proxyElem.style.display = \"none\";\n"
"\n"
"        window.removeEventListener('mousemove', _captureProxy);\n"
"        window.removeEventListener('mouseup', _captureProxy);\n"
"    }\n"
"}\n"
"window.getPointerEvent = getPointerEvent;\n"
"window.stopEvent = stopEvent;\n"
"window._captureRecursion = _captureRecursion;\n"
"window._elementForUnflushedEvents = _elementForUnflushedEvents;\n"
"window._captureProxy = _captureProxy;\n"
"window._capturedElemChanged = _capturedElemChanged;\n"
"window._captureObserver = _captureObserver;\n"
"window.setCapture = setCapture;\n"
"window.releaseCapture = releaseCapture;\n"
;
    if (strcmp(path, "/novnc/util/events.js") == 0) return novnc_util_events_js;
const char *novnc_util_eventtarget_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2019 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" */\n"
"\n"
"class EventTargetMixin {\n"
"    constructor() {\n"
"        this._listeners = new Map();\n"
"    }\n"
"\n"
"    addEventListener(type, callback) {\n"
"        if (!this._listeners.has(type)) {\n"
"            this._listeners.set(type, new Set());\n"
"        }\n"
"        this._listeners.get(type).add(callback);\n"
"    }\n"
"\n"
"    removeEventListener(type, callback) {\n"
"        if (this._listeners.has(type)) {\n"
"            this._listeners.get(type).delete(callback);\n"
"        }\n"
"    }\n"
"\n"
"    dispatchEvent(event) {\n"
"        if (!this._listeners.has(event.type)) {\n"
"            return true;\n"
"        }\n"
"        this._listeners.get(event.type)\n"
"            .forEach(callback => callback.call(this, event));\n"
"        return !event.defaultPrevented;\n"
"    }\n"
"}\n"
"window.EventTargetMixin = EventTargetMixin;\n"
;
    if (strcmp(path, "/novnc/util/eventtarget.js") == 0) return novnc_util_eventtarget_js;
const char *novnc_util_int_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2020 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" */\n"
"\n"
"function toUnsigned32bit(toConvert) {\n"
"    return toConvert >>> 0;\n"
"}\n"
"\n"
"function toSigned32bit(toConvert) {\n"
"    return toConvert | 0;\n"
"}\n"
"window.toUnsigned32bit = toUnsigned32bit;\n"
"window.toSigned32bit = toSigned32bit;\n"
;
    if (strcmp(path, "/novnc/util/int.js") == 0) return novnc_util_int_js;
const char *novnc_util_logging_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2019 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" */\n"
"\n"
"/*\n"
" * Logging/debug routines\n"
" */\n"
"\n"
"let _logLevel = 'warn';\n"
"\n"
"let Debug = () => {};\n"
"let Info = () => {};\n"
"let Warn = () => {};\n"
"let Error = () => {};\n"
"\n"
"function initLogging(level) {\n"
"    if (typeof level === 'undefined') {\n"
"        level = _logLevel;\n"
"    } else {\n"
"        _logLevel = level;\n"
"    }\n"
"\n"
"    Debug = Info = Warn = Error = () => {};\n"
"\n"
"    if (typeof window.console !== \"undefined\") {\n"
"        /* eslint-disable no-console, no-fallthrough */\n"
"        switch (level) {\n"
"            case 'debug':\n"
"                Debug = console.debug.bind(window.console);\n"
"            case 'info':\n"
"                Info  = console.info.bind(window.console);\n"
"            case 'warn':\n"
"                Warn  = console.warn.bind(window.console);\n"
"            case 'error':\n"
"                Error = console.error.bind(window.console);\n"
"            case 'none':\n"
"                break;\n"
"            default:\n"
"                throw new window.Error(\"invalid logging type '\" + level + \"'\");\n"
"        }\n"
"        /* eslint-enable no-console, no-fallthrough */\n"
"    }\n"
"}\n"
"\n"
"function getLogging() {\n"
"    return _logLevel;\n"
"}\n"
"\n"
"window.Log = { Debug, Info, Warn, Error };\n"
"\n"
"// Initialize logging level\n"
"initLogging();\n"
"window._logLevel = _logLevel;\n"
"window.Debug = Debug;\n"
"window.Info = Info;\n"
"window.Warn = Warn;\n"
"window.Error = Error;\n"
"window.initLogging = initLogging;\n"
"window.getLogging = getLogging;\n"
;
    if (strcmp(path, "/novnc/util/logging.js") == 0) return novnc_util_logging_js;
const char *novnc_util_md5_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2021 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" */\n"
"\n"
"/*\n"
" * Performs MD5 hashing on a string of binary characters, returns an array of bytes\n"
" */\n"
"\n"
"function MD5(d) {\n"
"    let r = M(V(Y(X(d), 8 * d.length)));\n"
"    return r;\n"
"}\n"
"\n"
"function M(d) {\n"
"    let f = new Uint8Array(d.length);\n"
"    for (let i=0;i<d.length;i++) {\n"
"        f[i] = d.charCodeAt(i);\n"
"    }\n"
"    return f;\n"
"}\n"
"\n"
"function X(d) {\n"
"    let r = Array(d.length >> 2);\n"
"    for (let m = 0; m < r.length; m++) r[m] = 0;\n"
"    for (let m = 0; m < 8 * d.length; m += 8) r[m >> 5] |= (255 & d.charCodeAt(m / 8)) << m % 32;\n"
"    return r;\n"
"}\n"
"\n"
"function V(d) {\n"
"    let r = \"\";\n"
"    for (let m = 0; m < 32 * d.length; m += 8) r += String.fromCharCode(d[m >> 5] >>> m % 32 & 255);\n"
"    return r;\n"
"}\n"
"\n"
"function Y(d, g) {\n"
"    d[g >> 5] |= 128 << g % 32, d[14 + (g + 64 >>> 9 << 4)] = g;\n"
"    let m = 1732584193, f = -271733879, r = -1732584194, i = 271733878;\n"
"    for (let n = 0; n < d.length; n += 16) {\n"
"        let h = m,\n"
"            t = f,\n"
"            g = r,\n"
"            e = i;\n"
"        f = ii(f = ii(f = ii(f = ii(f = hh(f = hh(f = hh(f = hh(f = gg(f = gg(f = gg(f = gg(f = ff(f = ff(f = ff(f = ff(f, r = ff(r, i = ff(i, m = ff(m, f, r, i, d[n + 0], 7, -680876936), f, r, d[n + 1], 12, -389564586), m, f, d[n + 2], 17, 606105819), i, m, d[n + 3], 22, -1044525330), r = ff(r, i = ff(i, m = ff(m, f, r, i, d[n + 4], 7, -176418897), f, r, d[n + 5], 12, 1200080426), m, f, d[n + 6], 17, -1473231341), i, m, d[n + 7], 22, -45705983), r = ff(r, i = ff(i, m = ff(m, f, r, i, d[n + 8], 7, 1770035416), f, r, d[n + 9], 12, -1958414417), m, f, d[n + 10], 17, -42063), i, m, d[n + 11], 22, -1990404162), r = ff(r, i = ff(i, m = ff(m, f, r, i, d[n + 12], 7, 1804603682), f, r, d[n + 13], 12, -40341101), m, f, d[n + 14], 17, -1502002290), i, m, d[n + 15], 22, 1236535329), r = gg(r, i = gg(i, m = gg(m, f, r, i, d[n + 1], 5, -165796510), f, r, d[n + 6], 9, -1069501632), m, f, d[n + 11], 14, 643717713), i, m, d[n + 0], 20, -373897302), r = gg(r, i = gg(i, m = gg(m, f, r, i, d[n + 5], 5, -701558691), f, r, d[n + 10], 9, 38016083), m, f, d[n + 15], 14, -660478335), i, m, d[n + 4], 20, -405537848), r = gg(r, i = gg(i, m = gg(m, f, r, i, d[n + 9], 5, 568446438), f, r, d[n + 14], 9, -1019803690), m, f, d[n + 3], 14, -187363961), i, m, d[n + 8], 20, 1163531501), r = gg(r, i = gg(i, m = gg(m, f, r, i, d[n + 13], 5, -1444681467), f, r, d[n + 2], 9, -51403784), m, f, d[n + 7], 14, 1735328473), i, m, d[n + 12], 20, -1926607734), r = hh(r, i = hh(i, m = hh(m, f, r, i, d[n + 5], 4, -378558), f, r, d[n + 8], 11, -2022574463), m, f, d[n + 11], 16, 1839030562), i, m, d[n + 14], 23, -35309556), r = hh(r, i = hh(i, m = hh(m, f, r, i, d[n + 1], 4, -1530992060), f, r, d[n + 4], 11, 1272893353), m, f, d[n + 7], 16, -155497632), i, m, d[n + 10], 23, -1094730640), r = hh(r, i = hh(i, m = hh(m, f, r, i, d[n + 13], 4, 681279174), f, r, d[n + 0], 11, -358537222), m, f, d[n + 3], 16, -722521979), i, m, d[n + 6], 23, 76029189), r = hh(r, i = hh(i, m = hh(m, f, r, i, d[n + 9], 4, -640364487), f, r, d[n + 12], 11, -421815835), m, f, d[n + 15], 16, 530742520), i, m, d[n + 2], 23, -995338651), r = ii(r, i = ii(i, m = ii(m, f, r, i, d[n + 0], 6, -198630844), f, r, d[n + 7], 10, 1126891415), m, f, d[n + 14], 15, -1416354905), i, m, d[n + 5], 21, -57434055), r = ii(r, i = ii(i, m = ii(m, f, r, i, d[n + 12], 6, 1700485571), f, r, d[n + 3], 10, -1894986606), m, f, d[n + 10], 15, -1051523), i, m, d[n + 1], 21, -2054922799), r = ii(r, i = ii(i, m = ii(m, f, r, i, d[n + 8], 6, 1873313359), f, r, d[n + 15], 10, -30611744), m, f, d[n + 6], 15, -1560198380), i, m, d[n + 13], 21, 1309151649), r = ii(r, i = ii(i, m = ii(m, f, r, i, d[n + 4], 6, -145523070), f, r, d[n + 11], 10, -1120210379), m, f, d[n + 2], 15, 718787259), i, m, d[n + 9], 21, -343485551), m = add(m, h), f = add(f, t), r = add(r, g), i = add(i, e);\n"
"    }\n"
"    return Array(m, f, r, i);\n"
"}\n"
"\n"
"function cmn(d, g, m, f, r, i) {\n"
"    return add(rol(add(add(g, d), add(f, i)), r), m);\n"
"}\n"
"\n"
"function ff(d, g, m, f, r, i, n) {\n"
"    return cmn(g & m | ~g & f, d, g, r, i, n);\n"
"}\n"
"\n"
"function gg(d, g, m, f, r, i, n) {\n"
"    return cmn(g & f | m & ~f, d, g, r, i, n);\n"
"}\n"
"\n"
"function hh(d, g, m, f, r, i, n) {\n"
"    return cmn(g ^ m ^ f, d, g, r, i, n);\n"
"}\n"
"\n"
"function ii(d, g, m, f, r, i, n) {\n"
"    return cmn(m ^ (g | ~f), d, g, r, i, n);\n"
"}\n"
"\n"
"function add(d, g) {\n"
"    let m = (65535 & d) + (65535 & g);\n"
"    return (d >> 16) + (g >> 16) + (m >> 16) << 16 | 65535 & m;\n"
"}\n"
"\n"
"function rol(d, g) {\n"
"    return d << g | d >>> 32 - g;\n"
"}window.MD5 = MD5;\n"
"window.X = X;\n"
"window.V = V;\n"
"window.Y = Y;\n"
"window.cmn = cmn;\n"
"window.ff = ff;\n"
"window.gg = gg;\n"
"window.hh = hh;\n"
"window.ii = ii;\n"
"window.add = add;\n"
"window.rol = rol;\n"
;
    if (strcmp(path, "/novnc/util/md5.js") == 0) return novnc_util_md5_js;
const char *novnc_util_strings_js =
"/*\n"
" * noVNC: HTML5 VNC client\n"
" * Copyright (C) 2019 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * See README.md for usage and integration instructions.\n"
" */\n"
"\n"
"// Decode from UTF-8\n"
"function decodeUTF8(utf8string, allowLatin1=false) {\n"
"    try {\n"
"        return decodeURIComponent(escape(utf8string));\n"
"    } catch (e) {\n"
"        if (e instanceof URIError) {\n"
"            if (allowLatin1) {\n"
"                // If we allow Latin1 we can ignore any decoding fails\n"
"                // and in these cases return the original string\n"
"                return utf8string;\n"
"            }\n"
"        }\n"
"        throw e;\n"
"    }\n"
"}\n"
"\n"
"// Encode to UTF-8\n"
"function encodeUTF8(DOMString) {\n"
"    return unescape(encodeURIComponent(DOMString));\n"
"}\n"
"window.decodeUTF8 = decodeUTF8;\n"
"window.encodeUTF8 = encodeUTF8;\n"
;
    if (strcmp(path, "/novnc/util/strings.js") == 0) return novnc_util_strings_js;
const char *novnc_vendor_pako_lib_zlib_adler32_js =
"'use strict';\n"
"\n"
"// Note: adler32 takes 12% for level 0 and 2% for level 6.\n"
"// It isn't worth it to make additional optimizations as in original.\n"
"// Small size is preferable.\n"
"\n"
"// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n"
"// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n"
"//\n"
"// This software is provided 'as-is', without any express or implied\n"
"// warranty. In no event will the authors be held liable for any damages\n"
"// arising from the use of this software.\n"
"//\n"
"// Permission is granted to anyone to use this software for any purpose,\n"
"// including commercial applications, and to alter it and redistribute it\n"
"// freely, subject to the following restrictions:\n"
"//\n"
"// 1. The origin of this software must not be misrepresented; you must not\n"
"//   claim that you wrote the original software. If you use this software\n"
"//   in a product, an acknowledgment in the product documentation would be\n"
"//   appreciated but is not required.\n"
"// 2. Altered source versions must be plainly marked as such, and must not be\n"
"//   misrepresented as being the original software.\n"
"// 3. This notice may not be removed or altered from any source distribution.\n"
"\n"
"const adler32 = (adler, buf, len, pos) => {\n"
"  let s1 = (adler & 0xffff) |0,\n"
"      s2 = ((adler >>> 16) & 0xffff) |0,\n"
"      n = 0;\n"
"\n"
"  while (len !== 0) {\n"
"    // Set limit ~ twice less than 5552, to keep\n"
"    // s2 in 31-bits, because we force signed ints.\n"
"    // in other case %= will fail.\n"
"    n = len > 2000 ? 2000 : len;\n"
"    len -= n;\n"
"\n"
"    do {\n"
"      s1 = (s1 + buf[pos++]) |0;\n"
"      s2 = (s2 + s1) |0;\n"
"    } while (--n);\n"
"\n"
"    s1 %= 65521;\n"
"    s2 %= 65521;\n"
"  }\n"
"\n"
"  return (s1 | (s2 << 16)) |0;\n"
"};\n"
"\n"
"\n"
"window.adler32;\n"
;
    if (strcmp(path, "/novnc/vendor/pako/lib/zlib/adler32.js") == 0) return novnc_vendor_pako_lib_zlib_adler32_js;
const char *novnc_vendor_pako_lib_zlib_constants_js =
"'use strict';\n"
"\n"
"// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n"
"// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n"
"//\n"
"// This software is provided 'as-is', without any express or implied\n"
"// warranty. In no event will the authors be held liable for any damages\n"
"// arising from the use of this software.\n"
"//\n"
"// Permission is granted to anyone to use this software for any purpose,\n"
"// including commercial applications, and to alter it and redistribute it\n"
"// freely, subject to the following restrictions:\n"
"//\n"
"// 1. The origin of this software must not be misrepresented; you must not\n"
"//   claim that you wrote the original software. If you use this software\n"
"//   in a product, an acknowledgment in the product documentation would be\n"
"//   appreciated but is not required.\n"
"// 2. Altered source versions must be plainly marked as such, and must not be\n"
"//   misrepresented as being the original software.\n"
"// 3. This notice may not be removed or altered from any source distribution.\n"
"\n"
"const Z_NO_FLUSH = 0;\n"
"const Z_PARTIAL_FLUSH = 1;\n"
"const Z_SYNC_FLUSH = 2;\n"
"const Z_FULL_FLUSH = 3;\n"
"const Z_FINISH = 4;\n"
"const Z_BLOCK = 5;\n"
"const Z_TREES = 6;\n"
"\n"
"const Z_OK = 0;\n"
"const Z_STREAM_END = 1;\n"
"const Z_NEED_DICT = 2;\n"
"const Z_ERRNO = -1;\n"
"const Z_STREAM_ERROR = -2;\n"
"const Z_DATA_ERROR = -3;\n"
"const Z_MEM_ERROR = -4;\n"
"const Z_BUF_ERROR = -5;\n"
"\n"
"const Z_NO_COMPRESSION = 0;\n"
"const Z_BEST_SPEED = 1;\n"
"const Z_BEST_COMPRESSION = 9;\n"
"const Z_DEFAULT_COMPRESSION = -1;\n"
"\n"
"const Z_FILTERED = 1;\n"
"const Z_HUFFMAN_ONLY = 2;\n"
"const Z_RLE = 3;\n"
"const Z_FIXED = 4;\n"
"const Z_DEFAULT_STRATEGY = 0;\n"
"\n"
"const Z_BINARY = 0;\n"
"const Z_TEXT = 1;\n"
"const Z_UNKNOWN = 2;\n"
"\n"
"const Z_DEFLATED = 8;\n"
"window.Z_NO_FLUSH = Z_NO_FLUSH;\n"
"window.Z_PARTIAL_FLUSH = Z_PARTIAL_FLUSH;\n"
"window.Z_SYNC_FLUSH = Z_SYNC_FLUSH;\n"
"window.Z_FULL_FLUSH = Z_FULL_FLUSH;\n"
"window.Z_FINISH = Z_FINISH;\n"
"window.Z_BLOCK = Z_BLOCK;\n"
"window.Z_TREES = Z_TREES;\n"
"window.Z_OK = Z_OK;\n"
"window.Z_STREAM_END = Z_STREAM_END;\n"
"window.Z_NEED_DICT = Z_NEED_DICT;\n"
"window.Z_ERRNO = Z_ERRNO;\n"
"window.Z_STREAM_ERROR = Z_STREAM_ERROR;\n"
"window.Z_DATA_ERROR = Z_DATA_ERROR;\n"
"window.Z_MEM_ERROR = Z_MEM_ERROR;\n"
"window.Z_BUF_ERROR = Z_BUF_ERROR;\n"
"window.Z_NO_COMPRESSION = Z_NO_COMPRESSION;\n"
"window.Z_BEST_SPEED = Z_BEST_SPEED;\n"
"window.Z_BEST_COMPRESSION = Z_BEST_COMPRESSION;\n"
"window.Z_DEFAULT_COMPRESSION = Z_DEFAULT_COMPRESSION;\n"
"window.Z_FILTERED = Z_FILTERED;\n"
"window.Z_HUFFMAN_ONLY = Z_HUFFMAN_ONLY;\n"
"window.Z_RLE = Z_RLE;\n"
"window.Z_FIXED = Z_FIXED;\n"
"window.Z_DEFAULT_STRATEGY = Z_DEFAULT_STRATEGY;\n"
"window.Z_BINARY = Z_BINARY;\n"
"window.Z_TEXT = Z_TEXT;\n"
"window.Z_UNKNOWN = Z_UNKNOWN;\n"
"window.Z_DEFLATED = Z_DEFLATED;\n"
;
    if (strcmp(path, "/novnc/vendor/pako/lib/zlib/constants.js") == 0) return novnc_vendor_pako_lib_zlib_constants_js;
const char *novnc_vendor_pako_lib_zlib_crc32_js =
"'use strict';\n"
"\n"
"// Note: we can't get significant speed boost here.\n"
"// So write code to minimize size - no pregenerated tables\n"
"// and array tools dependencies.\n"
"\n"
"// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n"
"// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n"
"//\n"
"// This software is provided 'as-is', without any express or implied\n"
"// warranty. In no event will the authors be held liable for any damages\n"
"// arising from the use of this software.\n"
"//\n"
"// Permission is granted to anyone to use this software for any purpose,\n"
"// including commercial applications, and to alter it and redistribute it\n"
"// freely, subject to the following restrictions:\n"
"//\n"
"// 1. The origin of this software must not be misrepresented; you must not\n"
"//   claim that you wrote the original software. If you use this software\n"
"//   in a product, an acknowledgment in the product documentation would be\n"
"//   appreciated but is not required.\n"
"// 2. Altered source versions must be plainly marked as such, and must not be\n"
"//   misrepresented as being the original software.\n"
"// 3. This notice may not be removed or altered from any source distribution.\n"
"\n"
"// Use ordinary array, since untyped makes no boost here\n"
"const makeTable = () => {\n"
"  let c, table = [];\n"
"\n"
"  for (var n = 0; n < 256; n++) {\n"
"    c = n;\n"
"    for (var k = 0; k < 8; k++) {\n"
"      c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));\n"
"    }\n"
"    table[n] = c;\n"
"  }\n"
"\n"
"  return table;\n"
"};\n"
"\n"
"// Create table on load. Just 255 signed longs. Not a problem.\n"
"const crcTable = new Uint32Array(makeTable());\n"
"\n"
"\n"
"const crc32 = (crc, buf, len, pos) => {\n"
"  const t = crcTable;\n"
"  const end = pos + len;\n"
"\n"
"  crc ^= -1;\n"
"\n"
"  for (let i = pos; i < end; i++) {\n"
"    crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF];\n"
"  }\n"
"\n"
"  return (crc ^ (-1)); // >>> 0;\n"
"};\n"
"\n"
"\n"
"window.crc32;\n"
"window.makeTable = makeTable;\n"
"window.crcTable = crcTable;\n"
;
    if (strcmp(path, "/novnc/vendor/pako/lib/zlib/crc32.js") == 0) return novnc_vendor_pako_lib_zlib_crc32_js;
const char *novnc_vendor_pako_lib_zlib_deflate_js =
"'use strict';\n"
"\n"
"// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n"
"// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n"
"//\n"
"// This software is provided 'as-is', without any express or implied\n"
"// warranty. In no event will the authors be held liable for any damages\n"
"// arising from the use of this software.\n"
"//\n"
"// Permission is granted to anyone to use this software for any purpose,\n"
"// including commercial applications, and to alter it and redistribute it\n"
"// freely, subject to the following restrictions:\n"
"//\n"
"// 1. The origin of this software must not be misrepresented; you must not\n"
"//   claim that you wrote the original software. If you use this software\n"
"//   in a product, an acknowledgment in the product documentation would be\n"
"//   appreciated but is not required.\n"
"// 2. Altered source versions must be plainly marked as such, and must not be\n"
"//   misrepresented as being the original software.\n"
"// 3. This notice may not be removed or altered from any source distribution.\n"
"\n"
"\n"
"/* Public constants ==========================================================*/\n"
"/* ===========================================================================*/\n"
"\n"
"/*============================================================================*/\n"
"\n"
"\n"
"const MAX_MEM_LEVEL = 9;\n"
"/* Maximum value for memLevel in deflateInit2 */\n"
"const MAX_WBITS = 15;\n"
"/* 32K LZ77 window */\n"
"const DEF_MEM_LEVEL = 8;\n"
"\n"
"\n"
"const LENGTH_CODES  = 29;\n"
"/* number of length codes, not counting the special END_BLOCK code */\n"
"const LITERALS      = 256;\n"
"/* number of literal bytes 0..255 */\n"
"const L_CODES       = LITERALS + 1 + LENGTH_CODES;\n"
"/* number of Literal or Length codes, including the END_BLOCK code */\n"
"const D_CODES       = 30;\n"
"/* number of distance codes */\n"
"const BL_CODES      = 19;\n"
"/* number of codes used to transfer the bit lengths */\n"
"const HEAP_SIZE     = 2 * L_CODES + 1;\n"
"/* maximum heap size */\n"
"const MAX_BITS  = 15;\n"
"/* All codes must not exceed MAX_BITS bits */\n"
"\n"
"const MIN_MATCH = 3;\n"
"const MAX_MATCH = 258;\n"
"const MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1);\n"
"\n"
"const PRESET_DICT = 0x20;\n"
"\n"
"const INIT_STATE    =  42;    /* zlib header -> BUSY_STATE */\n"
"//#ifdef GZIP\n"
"const GZIP_STATE    =  57;    /* gzip header -> BUSY_STATE | EXTRA_STATE */\n"
"//#endif\n"
"const EXTRA_STATE   =  69;    /* gzip extra block -> NAME_STATE */\n"
"const NAME_STATE    =  73;    /* gzip file name -> COMMENT_STATE */\n"
"const COMMENT_STATE =  91;    /* gzip comment -> HCRC_STATE */\n"
"const HCRC_STATE    = 103;    /* gzip header CRC -> BUSY_STATE */\n"
"const BUSY_STATE    = 113;    /* deflate -> FINISH_STATE */\n"
"const FINISH_STATE  = 666;    /* stream complete */\n"
"\n"
"const BS_NEED_MORE      = 1; /* block not completed, need more input or more output */\n"
"const BS_BLOCK_DONE     = 2; /* block flush performed */\n"
"const BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */\n"
"const BS_FINISH_DONE    = 4; /* finish done, accept no more input or output */\n"
"\n"
"const OS_CODE = 0x03; // Unix :) . Don't detect, use this default.\n"
"\n"
"const err = (strm, errorCode) => {\n"
"  strm.msg = msg[errorCode];\n"
"  return errorCode;\n"
"};\n"
"\n"
"const rank = (f) => {\n"
"  return ((f) * 2) - ((f) > 4 ? 9 : 0);\n"
"};\n"
"\n"
"const zero = (buf) => {\n"
"  let len = buf.length; while (--len >= 0) { buf[len] = 0; }\n"
"};\n"
"\n"
"/* ===========================================================================\n"
" * Slide the hash table when sliding the window down (could be avoided with 32\n"
" * bit values at the expense of memory usage). We slide even when level == 0 to\n"
" * keep the hash table consistent if we switch back to level > 0 later.\n"
" */\n"
"const slide_hash = (s) => {\n"
"  let n, m;\n"
"  let p;\n"
"  let wsize = s.w_size;\n"
"\n"
"  n = s.hash_size;\n"
"  p = n;\n"
"  do {\n"
"    m = s.head[--p];\n"
"    s.head[p] = (m >= wsize ? m - wsize : 0);\n"
"  } while (--n);\n"
"  n = wsize;\n"
"//#ifndef FASTEST\n"
"  p = n;\n"
"  do {\n"
"    m = s.prev[--p];\n"
"    s.prev[p] = (m >= wsize ? m - wsize : 0);\n"
"    /* If n is not on any hash chain, prev[n] is garbage but\n"
"     * its value will never be used.\n"
"     */\n"
"  } while (--n);\n"
"//#endif\n"
"};\n"
"\n"
"/* eslint-disable new-cap */\n"
"let HASH_ZLIB = (s, prev, data) => ((prev << s.hash_shift) ^ data) & s.hash_mask;\n"
"// This hash causes less collisions, https://github.com/nodeca/pako/issues/135\n"
"// But breaks binary compatibility\n"
"//let HASH_FAST = (s, prev, data) => ((prev << 8) + (prev >> 8) + (data << 4)) & s.hash_mask;\n"
"let HASH = HASH_ZLIB;\n"
"\n"
"\n"
"/* =========================================================================\n"
" * Flush as much pending output as possible. All deflate() output, except for\n"
" * some deflate_stored() output, goes through this function so some\n"
" * applications may wish to modify it to avoid allocating a large\n"
" * strm->next_out buffer and copying into it. (See also read_buf()).\n"
" */\n"
"const flush_pending = (strm) => {\n"
"  const s = strm.state;\n"
"\n"
"  //_tr_flush_bits(s);\n"
"  let len = s.pending;\n"
"  if (len > strm.avail_out) {\n"
"    len = strm.avail_out;\n"
"  }\n"
"  if (len === 0) { return; }\n"
"\n"
"  strm.output.set(s.pending_buf.subarray(s.pending_out, s.pending_out + len), strm.next_out);\n"
"  strm.next_out  += len;\n"
"  s.pending_out  += len;\n"
"  strm.total_out += len;\n"
"  strm.avail_out -= len;\n"
"  s.pending      -= len;\n"
"  if (s.pending === 0) {\n"
"    s.pending_out = 0;\n"
"  }\n"
"};\n"
"\n"
"\n"
"const flush_block_only = (s, last) => {\n"
"  _tr_flush_block(s, (s.block_start >= 0 ? s.block_start : -1), s.strstart - s.block_start, last);\n"
"  s.block_start = s.strstart;\n"
"  flush_pending(s.strm);\n"
"};\n"
"\n"
"\n"
"const put_byte = (s, b) => {\n"
"  s.pending_buf[s.pending++] = b;\n"
"};\n"
"\n"
"\n"
"/* =========================================================================\n"
" * Put a short in the pending buffer. The 16-bit value is put in MSB order.\n"
" * IN assertion: the stream state is correct and there is enough room in\n"
" * pending_buf.\n"
" */\n"
"const putShortMSB = (s, b) => {\n"
"\n"
"  //  put_byte(s, (Byte)(b >> 8));\n"
"//  put_byte(s, (Byte)(b & 0xff));\n"
"  s.pending_buf[s.pending++] = (b >>> 8) & 0xff;\n"
"  s.pending_buf[s.pending++] = b & 0xff;\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Read a new buffer from the current input stream, update the adler32\n"
" * and total number of bytes read.  All deflate() input goes through\n"
" * this function so some applications may wish to modify it to avoid\n"
" * allocating a large strm->input buffer and copying from it.\n"
" * (See also flush_pending()).\n"
" */\n"
"const read_buf = (strm, buf, start, size) => {\n"
"\n"
"  let len = strm.avail_in;\n"
"\n"
"  if (len > size) { len = size; }\n"
"  if (len === 0) { return 0; }\n"
"\n"
"  strm.avail_in -= len;\n"
"\n"
"  // zmemcpy(buf, strm->next_in, len);\n"
"  buf.set(strm.input.subarray(strm.next_in, strm.next_in + len), start);\n"
"  if (strm.state.wrap === 1) {\n"
"    strm.adler = adler32(strm.adler, buf, len, start);\n"
"  }\n"
"\n"
"  else if (strm.state.wrap === 2) {\n"
"    strm.adler = crc32(strm.adler, buf, len, start);\n"
"  }\n"
"\n"
"  strm.next_in += len;\n"
"  strm.total_in += len;\n"
"\n"
"  return len;\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Set match_start to the longest match starting at the given string and\n"
" * return its length. Matches shorter or equal to prev_length are discarded,\n"
" * in which case the result is equal to prev_length and match_start is\n"
" * garbage.\n"
" * IN assertions: cur_match is the head of the hash chain for the current\n"
" *   string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1\n"
" * OUT assertion: the match length is not greater than s->lookahead.\n"
" */\n"
"const longest_match = (s, cur_match) => {\n"
"\n"
"  let chain_length = s.max_chain_length;      /* max hash chain length */\n"
"  let scan = s.strstart; /* current string */\n"
"  let match;                       /* matched string */\n"
"  let len;                           /* length of current match */\n"
"  let best_len = s.prev_length;              /* best match length so far */\n"
"  let nice_match = s.nice_match;             /* stop if match long enough */\n"
"  const limit = (s.strstart > (s.w_size - MIN_LOOKAHEAD)) ?\n"
"      s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0/*NIL*/;\n"
"\n"
"  const _win = s.window; // shortcut\n"
"\n"
"  const wmask = s.w_mask;\n"
"  const prev  = s.prev;\n"
"\n"
"  /* Stop when cur_match becomes <= limit. To simplify the code,\n"
"   * we prevent matches with the string of window index 0.\n"
"   */\n"
"\n"
"  const strend = s.strstart + MAX_MATCH;\n"
"  let scan_end1  = _win[scan + best_len - 1];\n"
"  let scan_end   = _win[scan + best_len];\n"
"\n"
"  /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.\n"
"   * It is easy to get rid of this optimization if necessary.\n"
"   */\n"
"  // Assert(s->hash_bits >= 8 && MAX_MATCH == 258, \"Code too clever\");\n"
"\n"
"  /* Do not waste too much time if we already have a good match: */\n"
"  if (s.prev_length >= s.good_match) {\n"
"    chain_length >>= 2;\n"
"  }\n"
"  /* Do not look for matches beyond the end of the input. This is necessary\n"
"   * to make deflate deterministic.\n"
"   */\n"
"  if (nice_match > s.lookahead) { nice_match = s.lookahead; }\n"
"\n"
"  // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, \"need lookahead\");\n"
"\n"
"  do {\n"
"    // Assert(cur_match < s->strstart, \"no future\");\n"
"    match = cur_match;\n"
"\n"
"    /* Skip to next match if the match length cannot increase\n"
"     * or if the match length is less than 2.  Note that the checks below\n"
"     * for insufficient lookahead only occur occasionally for performance\n"
"     * reasons.  Therefore uninitialized memory will be accessed, and\n"
"     * conditional jumps will be made that depend on those values.\n"
"     * However the length of the match is limited to the lookahead, so\n"
"     * the output of deflate is not affected by the uninitialized values.\n"
"     */\n"
"\n"
"    if (_win[match + best_len]     !== scan_end  ||\n"
"        _win[match + best_len - 1] !== scan_end1 ||\n"
"        _win[match]                !== _win[scan] ||\n"
"        _win[++match]              !== _win[scan + 1]) {\n"
"      continue;\n"
"    }\n"
"\n"
"    /* The check at best_len-1 can be removed because it will be made\n"
"     * again later. (This heuristic is not always a win.)\n"
"     * It is not necessary to compare scan[2] and match[2] since they\n"
"     * are always equal when the other bytes match, given that\n"
"     * the hash keys are equal and that HASH_BITS >= 8.\n"
"     */\n"
"    scan += 2;\n"
"    match++;\n"
"    // Assert(*scan == *match, \"match[2]?\");\n"
"\n"
"    /* We check for insufficient lookahead only every 8th comparison;\n"
"     * the 256th check will be made at strstart+258.\n"
"     */\n"
"    do {\n"
"      /*jshint noempty:false*/\n"
"    } while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&\n"
"             _win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&\n"
"             _win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&\n"
"             _win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&\n"
"             scan < strend);\n"
"\n"
"    // Assert(scan <= s->window+(unsigned)(s->window_size-1), \"wild scan\");\n"
"\n"
"    len = MAX_MATCH - (strend - scan);\n"
"    scan = strend - MAX_MATCH;\n"
"\n"
"    if (len > best_len) {\n"
"      s.match_start = cur_match;\n"
"      best_len = len;\n"
"      if (len >= nice_match) {\n"
"        break;\n"
"      }\n"
"      scan_end1  = _win[scan + best_len - 1];\n"
"      scan_end   = _win[scan + best_len];\n"
"    }\n"
"  } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0);\n"
"\n"
"  if (best_len <= s.lookahead) {\n"
"    return best_len;\n"
"  }\n"
"  return s.lookahead;\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Fill the window when the lookahead becomes insufficient.\n"
" * Updates strstart and lookahead.\n"
" *\n"
" * IN assertion: lookahead < MIN_LOOKAHEAD\n"
" * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD\n"
" *    At least one byte has been read, or avail_in == 0; reads are\n"
" *    performed for at least two bytes (required for the zip translate_eol\n"
" *    option -- not supported here).\n"
" */\n"
"const fill_window = (s) => {\n"
"\n"
"  const _w_size = s.w_size;\n"
"  let n, more, str;\n"
"\n"
"  //Assert(s->lookahead < MIN_LOOKAHEAD, \"already enough lookahead\");\n"
"\n"
"  do {\n"
"    more = s.window_size - s.lookahead - s.strstart;\n"
"\n"
"    // JS ints have 32 bit, block below not needed\n"
"    /* Deal with !@#$% 64K limit: */\n"
"    //if (sizeof(int) <= 2) {\n"
"    //    if (more == 0 && s->strstart == 0 && s->lookahead == 0) {\n"
"    //        more = wsize;\n"
"    //\n"
"    //  } else if (more == (unsigned)(-1)) {\n"
"    //        /* Very unlikely, but possible on 16 bit machine if\n"
"    //         * strstart == 0 && lookahead == 1 (input done a byte at time)\n"
"    //         */\n"
"    //        more--;\n"
"    //    }\n"
"    //}\n"
"\n"
"\n"
"    /* If the window is almost full and there is insufficient lookahead,\n"
"     * move the upper half to the lower one to make room in the upper half.\n"
"     */\n"
"    if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) {\n"
"\n"
"      s.window.set(s.window.subarray(_w_size, _w_size + _w_size - more), 0);\n"
"      s.match_start -= _w_size;\n"
"      s.strstart -= _w_size;\n"
"      /* we now have strstart >= MAX_DIST */\n"
"      s.block_start -= _w_size;\n"
"      if (s.insert > s.strstart) {\n"
"        s.insert = s.strstart;\n"
"      }\n"
"      slide_hash(s);\n"
"      more += _w_size;\n"
"    }\n"
"    if (s.strm.avail_in === 0) {\n"
"      break;\n"
"    }\n"
"\n"
"    /* If there was no sliding:\n"
"     *    strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&\n"
"     *    more == window_size - lookahead - strstart\n"
"     * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)\n"
"     * => more >= window_size - 2*WSIZE + 2\n"
"     * In the BIG_MEM or MMAP case (not yet supported),\n"
"     *   window_size == input_size + MIN_LOOKAHEAD  &&\n"
"     *   strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.\n"
"     * Otherwise, window_size == 2*WSIZE so more >= 2.\n"
"     * If there was sliding, more >= WSIZE. So in all cases, more >= 2.\n"
"     */\n"
"    //Assert(more >= 2, \"more < 2\");\n"
"    n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more);\n"
"    s.lookahead += n;\n"
"\n"
"    /* Initialize the hash value now that we have some input: */\n"
"    if (s.lookahead + s.insert >= MIN_MATCH) {\n"
"      str = s.strstart - s.insert;\n"
"      s.ins_h = s.window[str];\n"
"\n"
"      /* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */\n"
"      s.ins_h = HASH(s, s.ins_h, s.window[str + 1]);\n"
"//#if MIN_MATCH != 3\n"
"//        Call update_hash() MIN_MATCH-3 more times\n"
"//#endif\n"
"      while (s.insert) {\n"
"        /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */\n"
"        s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]);\n"
"\n"
"        s.prev[str & s.w_mask] = s.head[s.ins_h];\n"
"        s.head[s.ins_h] = str;\n"
"        str++;\n"
"        s.insert--;\n"
"        if (s.lookahead + s.insert < MIN_MATCH) {\n"
"          break;\n"
"        }\n"
"      }\n"
"    }\n"
"    /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,\n"
"     * but this is not important since only literal bytes will be emitted.\n"
"     */\n"
"\n"
"  } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0);\n"
"\n"
"  /* If the WIN_INIT bytes after the end of the current data have never been\n"
"   * written, then zero those bytes in order to avoid memory check reports of\n"
"   * the use of uninitialized (or uninitialised as Julian writes) bytes by\n"
"   * the longest match routines.  Update the high water mark for the next\n"
"   * time through here.  WIN_INIT is set to MAX_MATCH since the longest match\n"
"   * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.\n"
"   */\n"
"//  if (s.high_water < s.window_size) {\n"
"//    const curr = s.strstart + s.lookahead;\n"
"//    let init = 0;\n"
"//\n"
"//    if (s.high_water < curr) {\n"
"//      /* Previous high water mark below current data -- zero WIN_INIT\n"
"//       * bytes or up to end of window, whichever is less.\n"
"//       */\n"
"//      init = s.window_size - curr;\n"
"//      if (init > WIN_INIT)\n"
"//        init = WIN_INIT;\n"
"//      zmemzero(s->window + curr, (unsigned)init);\n"
"//      s->high_water = curr + init;\n"
"//    }\n"
"//    else if (s->high_water < (ulg)curr + WIN_INIT) {\n"
"//      /* High water mark at or above current data, but below current data\n"
"//       * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up\n"
"//       * to end of window, whichever is less.\n"
"//       */\n"
"//      init = (ulg)curr + WIN_INIT - s->high_water;\n"
"//      if (init > s->window_size - s->high_water)\n"
"//        init = s->window_size - s->high_water;\n"
"//      zmemzero(s->window + s->high_water, (unsigned)init);\n"
"//      s->high_water += init;\n"
"//    }\n"
"//  }\n"
"//\n"
"//  Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,\n"
"//    \"not enough room for search\");\n"
"};\n"
"\n"
"/* ===========================================================================\n"
" * Copy without compression as much as possible from the input stream, return\n"
" * the current block state.\n"
" *\n"
" * In case deflateParams() is used to later switch to a non-zero compression\n"
" * level, s->matches (otherwise unused when storing) keeps track of the number\n"
" * of hash table slides to perform. If s->matches is 1, then one hash table\n"
" * slide will be done when switching. If s->matches is 2, the maximum value\n"
" * allowed here, then the hash table will be cleared, since two or more slides\n"
" * is the same as a clear.\n"
" *\n"
" * deflate_stored() is written to minimize the number of times an input byte is\n"
" * copied. It is most efficient with large input and output buffers, which\n"
" * maximizes the opportunites to have a single copy from next_in to next_out.\n"
" */\n"
"const deflate_stored = (s, flush) => {\n"
"\n"
"  /* Smallest worthy block size when not flushing or finishing. By default\n"
"   * this is 32K. This can be as small as 507 bytes for memLevel == 1. For\n"
"   * large input and output buffers, the stored block size will be larger.\n"
"   */\n"
"  let min_block = s.pending_buf_size - 5 > s.w_size ? s.w_size : s.pending_buf_size - 5;\n"
"\n"
"  /* Copy as many min_block or larger stored blocks directly to next_out as\n"
"   * possible. If flushing, copy the remaining available input to next_out as\n"
"   * stored blocks, if there is enough space.\n"
"   */\n"
"  let len, left, have, last = 0;\n"
"  let used = s.strm.avail_in;\n"
"  do {\n"
"    /* Set len to the maximum size block that we can copy directly with the\n"
"     * available input data and output space. Set left to how much of that\n"
"     * would be copied from what's left in the window.\n"
"     */\n"
"    len = 65535/* MAX_STORED */;     /* maximum deflate stored block length */\n"
"    have = (s.bi_valid + 42) >> 3;     /* number of header bytes */\n"
"    if (s.strm.avail_out < have) {         /* need room for header */\n"
"      break;\n"
"    }\n"
"      /* maximum stored block length that will fit in avail_out: */\n"
"    have = s.strm.avail_out - have;\n"
"    left = s.strstart - s.block_start;  /* bytes left in window */\n"
"    if (len > left + s.strm.avail_in) {\n"
"      len = left + s.strm.avail_in;   /* limit len to the input */\n"
"    }\n"
"    if (len > have) {\n"
"      len = have;             /* limit len to the output */\n"
"    }\n"
"\n"
"    /* If the stored block would be less than min_block in length, or if\n"
"     * unable to copy all of the available input when flushing, then try\n"
"     * copying to the window and the pending buffer instead. Also don't\n"
"     * write an empty block when flushing -- deflate() does that.\n"
"     */\n"
"    if (len < min_block && ((len === 0 && flush !== Z_FINISH) ||\n"
"                        flush === Z_NO_FLUSH ||\n"
"                        len !== left + s.strm.avail_in)) {\n"
"      break;\n"
"    }\n"
"\n"
"    /* Make a dummy stored block in pending to get the header bytes,\n"
"     * including any pending bits. This also updates the debugging counts.\n"
"     */\n"
"    last = flush === Z_FINISH && len === left + s.strm.avail_in ? 1 : 0;\n"
"    _tr_stored_block(s, 0, 0, last);\n"
"\n"
"    /* Replace the lengths in the dummy stored block with len. */\n"
"    s.pending_buf[s.pending - 4] = len;\n"
"    s.pending_buf[s.pending - 3] = len >> 8;\n"
"    s.pending_buf[s.pending - 2] = ~len;\n"
"    s.pending_buf[s.pending - 1] = ~len >> 8;\n"
"\n"
"    /* Write the stored block header bytes. */\n"
"    flush_pending(s.strm);\n"
"\n"
"//#ifdef ZLIB_DEBUG\n"
"//    /* Update debugging counts for the data about to be copied. */\n"
"//    s->compressed_len += len << 3;\n"
"//    s->bits_sent += len << 3;\n"
"//#endif\n"
"\n"
"    /* Copy uncompressed bytes from the window to next_out. */\n"
"    if (left) {\n"
"      if (left > len) {\n"
"        left = len;\n"
"      }\n"
"      //zmemcpy(s->strm->next_out, s->window + s->block_start, left);\n"
"      s.strm.output.set(s.window.subarray(s.block_start, s.block_start + left), s.strm.next_out);\n"
"      s.strm.next_out += left;\n"
"      s.strm.avail_out -= left;\n"
"      s.strm.total_out += left;\n"
"      s.block_start += left;\n"
"      len -= left;\n"
"    }\n"
"\n"
"    /* Copy uncompressed bytes directly from next_in to next_out, updating\n"
"     * the check value.\n"
"     */\n"
"    if (len) {\n"
"      read_buf(s.strm, s.strm.output, s.strm.next_out, len);\n"
"      s.strm.next_out += len;\n"
"      s.strm.avail_out -= len;\n"
"      s.strm.total_out += len;\n"
"    }\n"
"  } while (last === 0);\n"
"\n"
"  /* Update the sliding window with the last s->w_size bytes of the copied\n"
"   * data, or append all of the copied data to the existing window if less\n"
"   * than s->w_size bytes were copied. Also update the number of bytes to\n"
"   * insert in the hash tables, in the event that deflateParams() switches to\n"
"   * a non-zero compression level.\n"
"   */\n"
"  used -= s.strm.avail_in;    /* number of input bytes directly copied */\n"
"  if (used) {\n"
"    /* If any input was used, then no unused input remains in the window,\n"
"     * therefore s->block_start == s->strstart.\n"
"     */\n"
"    if (used >= s.w_size) {  /* supplant the previous history */\n"
"      s.matches = 2;     /* clear hash */\n"
"      //zmemcpy(s->window, s->strm->next_in - s->w_size, s->w_size);\n"
"      s.window.set(s.strm.input.subarray(s.strm.next_in - s.w_size, s.strm.next_in), 0);\n"
"      s.strstart = s.w_size;\n"
"      s.insert = s.strstart;\n"
"    }\n"
"    else {\n"
"      if (s.window_size - s.strstart <= used) {\n"
"        /* Slide the window down. */\n"
"        s.strstart -= s.w_size;\n"
"        //zmemcpy(s->window, s->window + s->w_size, s->strstart);\n"
"        s.window.set(s.window.subarray(s.w_size, s.w_size + s.strstart), 0);\n"
"        if (s.matches < 2) {\n"
"          s.matches++;   /* add a pending slide_hash() */\n"
"        }\n"
"        if (s.insert > s.strstart) {\n"
"          s.insert = s.strstart;\n"
"        }\n"
"      }\n"
"      //zmemcpy(s->window + s->strstart, s->strm->next_in - used, used);\n"
"      s.window.set(s.strm.input.subarray(s.strm.next_in - used, s.strm.next_in), s.strstart);\n"
"      s.strstart += used;\n"
"      s.insert += used > s.w_size - s.insert ? s.w_size - s.insert : used;\n"
"    }\n"
"    s.block_start = s.strstart;\n"
"  }\n"
"  if (s.high_water < s.strstart) {\n"
"    s.high_water = s.strstart;\n"
"  }\n"
"\n"
"  /* If the last block was written to next_out, then done. */\n"
"  if (last) {\n"
"    return BS_FINISH_DONE;\n"
"  }\n"
"\n"
"  /* If flushing and all input has been consumed, then done. */\n"
"  if (flush !== Z_NO_FLUSH && flush !== Z_FINISH &&\n"
"    s.strm.avail_in === 0 && s.strstart === s.block_start) {\n"
"    return BS_BLOCK_DONE;\n"
"  }\n"
"\n"
"  /* Fill the window with any remaining input. */\n"
"  have = s.window_size - s.strstart;\n"
"  if (s.strm.avail_in > have && s.block_start >= s.w_size) {\n"
"    /* Slide the window down. */\n"
"    s.block_start -= s.w_size;\n"
"    s.strstart -= s.w_size;\n"
"    //zmemcpy(s->window, s->window + s->w_size, s->strstart);\n"
"    s.window.set(s.window.subarray(s.w_size, s.w_size + s.strstart), 0);\n"
"    if (s.matches < 2) {\n"
"      s.matches++;       /* add a pending slide_hash() */\n"
"    }\n"
"    have += s.w_size;      /* more space now */\n"
"    if (s.insert > s.strstart) {\n"
"      s.insert = s.strstart;\n"
"    }\n"
"  }\n"
"  if (have > s.strm.avail_in) {\n"
"    have = s.strm.avail_in;\n"
"  }\n"
"  if (have) {\n"
"    read_buf(s.strm, s.window, s.strstart, have);\n"
"    s.strstart += have;\n"
"    s.insert += have > s.w_size - s.insert ? s.w_size - s.insert : have;\n"
"  }\n"
"  if (s.high_water < s.strstart) {\n"
"    s.high_water = s.strstart;\n"
"  }\n"
"\n"
"  /* There was not enough avail_out to write a complete worthy or flushed\n"
"   * stored block to next_out. Write a stored block to pending instead, if we\n"
"   * have enough input for a worthy block, or if flushing and there is enough\n"
"   * room for the remaining input as a stored block in the pending buffer.\n"
"   */\n"
"  have = (s.bi_valid + 42) >> 3;     /* number of header bytes */\n"
"    /* maximum stored block length that will fit in pending: */\n"
"  have = s.pending_buf_size - have > 65535/* MAX_STORED */ ? 65535/* MAX_STORED */ : s.pending_buf_size - have;\n"
"  min_block = have > s.w_size ? s.w_size : have;\n"
"  left = s.strstart - s.block_start;\n"
"  if (left >= min_block ||\n"
"     ((left || flush === Z_FINISH) && flush !== Z_NO_FLUSH &&\n"
"     s.strm.avail_in === 0 && left <= have)) {\n"
"    len = left > have ? have : left;\n"
"    last = flush === Z_FINISH && s.strm.avail_in === 0 &&\n"
"         len === left ? 1 : 0;\n"
"    _tr_stored_block(s, s.block_start, len, last);\n"
"    s.block_start += len;\n"
"    flush_pending(s.strm);\n"
"  }\n"
"\n"
"  /* We've done all we can with the available input and output. */\n"
"  return last ? BS_FINISH_STARTED : BS_NEED_MORE;\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Compress as much as possible from the input stream, return the current\n"
" * block state.\n"
" * This function does not perform lazy evaluation of matches and inserts\n"
" * new strings in the dictionary only for unmatched strings or for short\n"
" * matches. It is used only for the fast compression options.\n"
" */\n"
"const deflate_fast = (s, flush) => {\n"
"\n"
"  let hash_head;        /* head of the hash chain */\n"
"  let bflush;           /* set if current block must be flushed */\n"
"\n"
"  for (;;) {\n"
"    /* Make sure that we always have enough lookahead, except\n"
"     * at the end of the input file. We need MAX_MATCH bytes\n"
"     * for the next match, plus MIN_MATCH bytes to insert the\n"
"     * string following the next match.\n"
"     */\n"
"    if (s.lookahead < MIN_LOOKAHEAD) {\n"
"      fill_window(s);\n"
"      if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) {\n"
"        return BS_NEED_MORE;\n"
"      }\n"
"      if (s.lookahead === 0) {\n"
"        break; /* flush the current block */\n"
"      }\n"
"    }\n"
"\n"
"    /* Insert the string window[strstart .. strstart+2] in the\n"
"     * dictionary, and set hash_head to the head of the hash chain:\n"
"     */\n"
"    hash_head = 0/*NIL*/;\n"
"    if (s.lookahead >= MIN_MATCH) {\n"
"      /*** INSERT_STRING(s, s.strstart, hash_head); ***/\n"
"      s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);\n"
"      hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];\n"
"      s.head[s.ins_h] = s.strstart;\n"
"      /***/\n"
"    }\n"
"\n"
"    /* Find the longest match, discarding those <= prev_length.\n"
"     * At this point we have always match_length < MIN_MATCH\n"
"     */\n"
"    if (hash_head !== 0/*NIL*/ && ((s.strstart - hash_head) <= (s.w_size - MIN_LOOKAHEAD))) {\n"
"      /* To simplify the code, we prevent matches with the string\n"
"       * of window index 0 (in particular we have to avoid a match\n"
"       * of the string with itself at the start of the input file).\n"
"       */\n"
"      s.match_length = longest_match(s, hash_head);\n"
"      /* longest_match() sets match_start */\n"
"    }\n"
"    if (s.match_length >= MIN_MATCH) {\n"
"      // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only\n"
"\n"
"      /*** _tr_tally_dist(s, s.strstart - s.match_start,\n"
"                     s.match_length - MIN_MATCH, bflush); ***/\n"
"      bflush = _tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH);\n"
"\n"
"      s.lookahead -= s.match_length;\n"
"\n"
"      /* Insert new strings in the hash table only if the match length\n"
"       * is not too large. This saves time but degrades compression.\n"
"       */\n"
"      if (s.match_length <= s.max_lazy_match/*max_insert_length*/ && s.lookahead >= MIN_MATCH) {\n"
"        s.match_length--; /* string at strstart already in table */\n"
"        do {\n"
"          s.strstart++;\n"
"          /*** INSERT_STRING(s, s.strstart, hash_head); ***/\n"
"          s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);\n"
"          hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];\n"
"          s.head[s.ins_h] = s.strstart;\n"
"          /***/\n"
"          /* strstart never exceeds WSIZE-MAX_MATCH, so there are\n"
"           * always MIN_MATCH bytes ahead.\n"
"           */\n"
"        } while (--s.match_length !== 0);\n"
"        s.strstart++;\n"
"      } else\n"
"      {\n"
"        s.strstart += s.match_length;\n"
"        s.match_length = 0;\n"
"        s.ins_h = s.window[s.strstart];\n"
"        /* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */\n"
"        s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + 1]);\n"
"\n"
"//#if MIN_MATCH != 3\n"
"//                Call UPDATE_HASH() MIN_MATCH-3 more times\n"
"//#endif\n"
"        /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not\n"
"         * matter since it will be recomputed at next deflate call.\n"
"         */\n"
"      }\n"
"    } else {\n"
"      /* No match, output a literal byte */\n"
"      //Tracevv((stderr,\"%c\", s.window[s.strstart]));\n"
"      /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/\n"
"      bflush = _tr_tally(s, 0, s.window[s.strstart]);\n"
"\n"
"      s.lookahead--;\n"
"      s.strstart++;\n"
"    }\n"
"    if (bflush) {\n"
"      /*** FLUSH_BLOCK(s, 0); ***/\n"
"      flush_block_only(s, false);\n"
"      if (s.strm.avail_out === 0) {\n"
"        return BS_NEED_MORE;\n"
"      }\n"
"      /***/\n"
"    }\n"
"  }\n"
"  s.insert = ((s.strstart < (MIN_MATCH - 1)) ? s.strstart : MIN_MATCH - 1);\n"
"  if (flush === Z_FINISH) {\n"
"    /*** FLUSH_BLOCK(s, 1); ***/\n"
"    flush_block_only(s, true);\n"
"    if (s.strm.avail_out === 0) {\n"
"      return BS_FINISH_STARTED;\n"
"    }\n"
"    /***/\n"
"    return BS_FINISH_DONE;\n"
"  }\n"
"  if (s.sym_next) {\n"
"    /*** FLUSH_BLOCK(s, 0); ***/\n"
"    flush_block_only(s, false);\n"
"    if (s.strm.avail_out === 0) {\n"
"      return BS_NEED_MORE;\n"
"    }\n"
"    /***/\n"
"  }\n"
"  return BS_BLOCK_DONE;\n"
"};\n"
"\n"
"/* ===========================================================================\n"
" * Same as above, but achieves better compression. We use a lazy\n"
" * evaluation for matches: a match is finally adopted only if there is\n"
" * no better match at the next window position.\n"
" */\n"
"const deflate_slow = (s, flush) => {\n"
"\n"
"  let hash_head;          /* head of hash chain */\n"
"  let bflush;              /* set if current block must be flushed */\n"
"\n"
"  let max_insert;\n"
"\n"
"  /* Process the input block. */\n"
"  for (;;) {\n"
"    /* Make sure that we always have enough lookahead, except\n"
"     * at the end of the input file. We need MAX_MATCH bytes\n"
"     * for the next match, plus MIN_MATCH bytes to insert the\n"
"     * string following the next match.\n"
"     */\n"
"    if (s.lookahead < MIN_LOOKAHEAD) {\n"
"      fill_window(s);\n"
"      if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) {\n"
"        return BS_NEED_MORE;\n"
"      }\n"
"      if (s.lookahead === 0) { break; } /* flush the current block */\n"
"    }\n"
"\n"
"    /* Insert the string window[strstart .. strstart+2] in the\n"
"     * dictionary, and set hash_head to the head of the hash chain:\n"
"     */\n"
"    hash_head = 0/*NIL*/;\n"
"    if (s.lookahead >= MIN_MATCH) {\n"
"      /*** INSERT_STRING(s, s.strstart, hash_head); ***/\n"
"      s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);\n"
"      hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];\n"
"      s.head[s.ins_h] = s.strstart;\n"
"      /***/\n"
"    }\n"
"\n"
"    /* Find the longest match, discarding those <= prev_length.\n"
"     */\n"
"    s.prev_length = s.match_length;\n"
"    s.prev_match = s.match_start;\n"
"    s.match_length = MIN_MATCH - 1;\n"
"\n"
"    if (hash_head !== 0/*NIL*/ && s.prev_length < s.max_lazy_match &&\n"
"        s.strstart - hash_head <= (s.w_size - MIN_LOOKAHEAD)/*MAX_DIST(s)*/) {\n"
"      /* To simplify the code, we prevent matches with the string\n"
"       * of window index 0 (in particular we have to avoid a match\n"
"       * of the string with itself at the start of the input file).\n"
"       */\n"
"      s.match_length = longest_match(s, hash_head);\n"
"      /* longest_match() sets match_start */\n"
"\n"
"      if (s.match_length <= 5 &&\n"
"         (s.strategy === Z_FILTERED || (s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096/*TOO_FAR*/))) {\n"
"\n"
"        /* If prev_match is also MIN_MATCH, match_start is garbage\n"
"         * but we will ignore the current match anyway.\n"
"         */\n"
"        s.match_length = MIN_MATCH - 1;\n"
"      }\n"
"    }\n"
"    /* If there was a match at the previous step and the current\n"
"     * match is not better, output the previous match:\n"
"     */\n"
"    if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) {\n"
"      max_insert = s.strstart + s.lookahead - MIN_MATCH;\n"
"      /* Do not insert strings in hash table beyond this. */\n"
"\n"
"      //check_match(s, s.strstart-1, s.prev_match, s.prev_length);\n"
"\n"
"      /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match,\n"
"                     s.prev_length - MIN_MATCH, bflush);***/\n"
"      bflush = _tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH);\n"
"      /* Insert in hash table all strings up to the end of the match.\n"
"       * strstart-1 and strstart are already inserted. If there is not\n"
"       * enough lookahead, the last two strings are not inserted in\n"
"       * the hash table.\n"
"       */\n"
"      s.lookahead -= s.prev_length - 1;\n"
"      s.prev_length -= 2;\n"
"      do {\n"
"        if (++s.strstart <= max_insert) {\n"
"          /*** INSERT_STRING(s, s.strstart, hash_head); ***/\n"
"          s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);\n"
"          hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];\n"
"          s.head[s.ins_h] = s.strstart;\n"
"          /***/\n"
"        }\n"
"      } while (--s.prev_length !== 0);\n"
"      s.match_available = 0;\n"
"      s.match_length = MIN_MATCH - 1;\n"
"      s.strstart++;\n"
"\n"
"      if (bflush) {\n"
"        /*** FLUSH_BLOCK(s, 0); ***/\n"
"        flush_block_only(s, false);\n"
"        if (s.strm.avail_out === 0) {\n"
"          return BS_NEED_MORE;\n"
"        }\n"
"        /***/\n"
"      }\n"
"\n"
"    } else if (s.match_available) {\n"
"      /* If there was no match at the previous position, output a\n"
"       * single literal. If there was a match but the current match\n"
"       * is longer, truncate the previous match to a single literal.\n"
"       */\n"
"      //Tracevv((stderr,\"%c\", s->window[s->strstart-1]));\n"
"      /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/\n"
"      bflush = _tr_tally(s, 0, s.window[s.strstart - 1]);\n"
"\n"
"      if (bflush) {\n"
"        /*** FLUSH_BLOCK_ONLY(s, 0) ***/\n"
"        flush_block_only(s, false);\n"
"        /***/\n"
"      }\n"
"      s.strstart++;\n"
"      s.lookahead--;\n"
"      if (s.strm.avail_out === 0) {\n"
"        return BS_NEED_MORE;\n"
"      }\n"
"    } else {\n"
"      /* There is no previous match to compare with, wait for\n"
"       * the next step to decide.\n"
"       */\n"
"      s.match_available = 1;\n"
"      s.strstart++;\n"
"      s.lookahead--;\n"
"    }\n"
"  }\n"
"  //Assert (flush != Z_NO_FLUSH, \"no flush?\");\n"
"  if (s.match_available) {\n"
"    //Tracevv((stderr,\"%c\", s->window[s->strstart-1]));\n"
"    /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/\n"
"    bflush = _tr_tally(s, 0, s.window[s.strstart - 1]);\n"
"\n"
"    s.match_available = 0;\n"
"  }\n"
"  s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1;\n"
"  if (flush === Z_FINISH) {\n"
"    /*** FLUSH_BLOCK(s, 1); ***/\n"
"    flush_block_only(s, true);\n"
"    if (s.strm.avail_out === 0) {\n"
"      return BS_FINISH_STARTED;\n"
"    }\n"
"    /***/\n"
"    return BS_FINISH_DONE;\n"
"  }\n"
"  if (s.sym_next) {\n"
"    /*** FLUSH_BLOCK(s, 0); ***/\n"
"    flush_block_only(s, false);\n"
"    if (s.strm.avail_out === 0) {\n"
"      return BS_NEED_MORE;\n"
"    }\n"
"    /***/\n"
"  }\n"
"\n"
"  return BS_BLOCK_DONE;\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * For Z_RLE, simply look for runs of bytes, generate matches only of distance\n"
" * one.  Do not maintain a hash table.  (It will be regenerated if this run of\n"
" * deflate switches away from Z_RLE.)\n"
" */\n"
"const deflate_rle = (s, flush) => {\n"
"\n"
"  let bflush;            /* set if current block must be flushed */\n"
"  let prev;              /* byte at distance one to match */\n"
"  let scan, strend;      /* scan goes up to strend for length of run */\n"
"\n"
"  const _win = s.window;\n"
"\n"
"  for (;;) {\n"
"    /* Make sure that we always have enough lookahead, except\n"
"     * at the end of the input file. We need MAX_MATCH bytes\n"
"     * for the longest run, plus one for the unrolled loop.\n"
"     */\n"
"    if (s.lookahead <= MAX_MATCH) {\n"
"      fill_window(s);\n"
"      if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH) {\n"
"        return BS_NEED_MORE;\n"
"      }\n"
"      if (s.lookahead === 0) { break; } /* flush the current block */\n"
"    }\n"
"\n"
"    /* See how many times the previous byte repeats */\n"
"    s.match_length = 0;\n"
"    if (s.lookahead >= MIN_MATCH && s.strstart > 0) {\n"
"      scan = s.strstart - 1;\n"
"      prev = _win[scan];\n"
"      if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) {\n"
"        strend = s.strstart + MAX_MATCH;\n"
"        do {\n"
"          /*jshint noempty:false*/\n"
"        } while (prev === _win[++scan] && prev === _win[++scan] &&\n"
"                 prev === _win[++scan] && prev === _win[++scan] &&\n"
"                 prev === _win[++scan] && prev === _win[++scan] &&\n"
"                 prev === _win[++scan] && prev === _win[++scan] &&\n"
"                 scan < strend);\n"
"        s.match_length = MAX_MATCH - (strend - scan);\n"
"        if (s.match_length > s.lookahead) {\n"
"          s.match_length = s.lookahead;\n"
"        }\n"
"      }\n"
"      //Assert(scan <= s->window+(uInt)(s->window_size-1), \"wild scan\");\n"
"    }\n"
"\n"
"    /* Emit match if have run of MIN_MATCH or longer, else emit literal */\n"
"    if (s.match_length >= MIN_MATCH) {\n"
"      //check_match(s, s.strstart, s.strstart - 1, s.match_length);\n"
"\n"
"      /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/\n"
"      bflush = _tr_tally(s, 1, s.match_length - MIN_MATCH);\n"
"\n"
"      s.lookahead -= s.match_length;\n"
"      s.strstart += s.match_length;\n"
"      s.match_length = 0;\n"
"    } else {\n"
"      /* No match, output a literal byte */\n"
"      //Tracevv((stderr,\"%c\", s->window[s->strstart]));\n"
"      /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/\n"
"      bflush = _tr_tally(s, 0, s.window[s.strstart]);\n"
"\n"
"      s.lookahead--;\n"
"      s.strstart++;\n"
"    }\n"
"    if (bflush) {\n"
"      /*** FLUSH_BLOCK(s, 0); ***/\n"
"      flush_block_only(s, false);\n"
"      if (s.strm.avail_out === 0) {\n"
"        return BS_NEED_MORE;\n"
"      }\n"
"      /***/\n"
"    }\n"
"  }\n"
"  s.insert = 0;\n"
"  if (flush === Z_FINISH) {\n"
"    /*** FLUSH_BLOCK(s, 1); ***/\n"
"    flush_block_only(s, true);\n"
"    if (s.strm.avail_out === 0) {\n"
"      return BS_FINISH_STARTED;\n"
"    }\n"
"    /***/\n"
"    return BS_FINISH_DONE;\n"
"  }\n"
"  if (s.sym_next) {\n"
"    /*** FLUSH_BLOCK(s, 0); ***/\n"
"    flush_block_only(s, false);\n"
"    if (s.strm.avail_out === 0) {\n"
"      return BS_NEED_MORE;\n"
"    }\n"
"    /***/\n"
"  }\n"
"  return BS_BLOCK_DONE;\n"
"};\n"
"\n"
"/* ===========================================================================\n"
" * For Z_HUFFMAN_ONLY, do not look for matches.  Do not maintain a hash table.\n"
" * (It will be regenerated if this run of deflate switches away from Huffman.)\n"
" */\n"
"const deflate_huff = (s, flush) => {\n"
"\n"
"  let bflush;             /* set if current block must be flushed */\n"
"\n"
"  for (;;) {\n"
"    /* Make sure that we have a literal to write. */\n"
"    if (s.lookahead === 0) {\n"
"      fill_window(s);\n"
"      if (s.lookahead === 0) {\n"
"        if (flush === Z_NO_FLUSH) {\n"
"          return BS_NEED_MORE;\n"
"        }\n"
"        break;      /* flush the current block */\n"
"      }\n"
"    }\n"
"\n"
"    /* Output a literal byte */\n"
"    s.match_length = 0;\n"
"    //Tracevv((stderr,\"%c\", s->window[s->strstart]));\n"
"    /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/\n"
"    bflush = _tr_tally(s, 0, s.window[s.strstart]);\n"
"    s.lookahead--;\n"
"    s.strstart++;\n"
"    if (bflush) {\n"
"      /*** FLUSH_BLOCK(s, 0); ***/\n"
"      flush_block_only(s, false);\n"
"      if (s.strm.avail_out === 0) {\n"
"        return BS_NEED_MORE;\n"
"      }\n"
"      /***/\n"
"    }\n"
"  }\n"
"  s.insert = 0;\n"
"  if (flush === Z_FINISH) {\n"
"    /*** FLUSH_BLOCK(s, 1); ***/\n"
"    flush_block_only(s, true);\n"
"    if (s.strm.avail_out === 0) {\n"
"      return BS_FINISH_STARTED;\n"
"    }\n"
"    /***/\n"
"    return BS_FINISH_DONE;\n"
"  }\n"
"  if (s.sym_next) {\n"
"    /*** FLUSH_BLOCK(s, 0); ***/\n"
"    flush_block_only(s, false);\n"
"    if (s.strm.avail_out === 0) {\n"
"      return BS_NEED_MORE;\n"
"    }\n"
"    /***/\n"
"  }\n"
"  return BS_BLOCK_DONE;\n"
"};\n"
"\n"
"/* Values for max_lazy_match, good_match and max_chain_length, depending on\n"
" * the desired pack level (0..9). The values given below have been tuned to\n"
" * exclude worst case performance for pathological files. Better values may be\n"
" * found for specific files.\n"
" */\n"
"function Config(good_length, max_lazy, nice_length, max_chain, func) {\n"
"\n"
"  this.good_length = good_length;\n"
"  this.max_lazy = max_lazy;\n"
"  this.nice_length = nice_length;\n"
"  this.max_chain = max_chain;\n"
"  this.func = func;\n"
"}\n"
"\n"
"const configuration_table = [\n"
"  /*      good lazy nice chain */\n"
"  new Config(0, 0, 0, 0, deflate_stored),          /* 0 store only */\n"
"  new Config(4, 4, 8, 4, deflate_fast),            /* 1 max speed, no lazy matches */\n"
"  new Config(4, 5, 16, 8, deflate_fast),           /* 2 */\n"
"  new Config(4, 6, 32, 32, deflate_fast),          /* 3 */\n"
"\n"
"  new Config(4, 4, 16, 16, deflate_slow),          /* 4 lazy matches */\n"
"  new Config(8, 16, 32, 32, deflate_slow),         /* 5 */\n"
"  new Config(8, 16, 128, 128, deflate_slow),       /* 6 */\n"
"  new Config(8, 32, 128, 256, deflate_slow),       /* 7 */\n"
"  new Config(32, 128, 258, 1024, deflate_slow),    /* 8 */\n"
"  new Config(32, 258, 258, 4096, deflate_slow)     /* 9 max compression */\n"
"];\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Initialize the \"longest match\" routines for a new zlib stream\n"
" */\n"
"const lm_init = (s) => {\n"
"\n"
"  s.window_size = 2 * s.w_size;\n"
"\n"
"  /*** CLEAR_HASH(s); ***/\n"
"  zero(s.head); // Fill with NIL (= 0);\n"
"\n"
"  /* Set the default configuration parameters:\n"
"   */\n"
"  s.max_lazy_match = configuration_table[s.level].max_lazy;\n"
"  s.good_match = configuration_table[s.level].good_length;\n"
"  s.nice_match = configuration_table[s.level].nice_length;\n"
"  s.max_chain_length = configuration_table[s.level].max_chain;\n"
"\n"
"  s.strstart = 0;\n"
"  s.block_start = 0;\n"
"  s.lookahead = 0;\n"
"  s.insert = 0;\n"
"  s.match_length = s.prev_length = MIN_MATCH - 1;\n"
"  s.match_available = 0;\n"
"  s.ins_h = 0;\n"
"};\n"
"\n"
"\n"
"function DeflateState() {\n"
"  this.strm = null;            /* pointer back to this zlib stream */\n"
"  this.status = 0;            /* as the name implies */\n"
"  this.pending_buf = null;      /* output still pending */\n"
"  this.pending_buf_size = 0;  /* size of pending_buf */\n"
"  this.pending_out = 0;       /* next pending byte to output to the stream */\n"
"  this.pending = 0;           /* nb of bytes in the pending buffer */\n"
"  this.wrap = 0;              /* bit 0 true for zlib, bit 1 true for gzip */\n"
"  this.gzhead = null;         /* gzip header information to write */\n"
"  this.gzindex = 0;           /* where in extra, name, or comment */\n"
"  this.method = Z_DEFLATED; /* can only be DEFLATED */\n"
"  this.last_flush = -1;   /* value of flush param for previous deflate call */\n"
"\n"
"  this.w_size = 0;  /* LZ77 window size (32K by default) */\n"
"  this.w_bits = 0;  /* log2(w_size)  (8..16) */\n"
"  this.w_mask = 0;  /* w_size - 1 */\n"
"\n"
"  this.window = null;\n"
"  /* Sliding window. Input bytes are read into the second half of the window,\n"
"   * and move to the first half later to keep a dictionary of at least wSize\n"
"   * bytes. With this organization, matches are limited to a distance of\n"
"   * wSize-MAX_MATCH bytes, but this ensures that IO is always\n"
"   * performed with a length multiple of the block size.\n"
"   */\n"
"\n"
"  this.window_size = 0;\n"
"  /* Actual size of window: 2*wSize, except when the user input buffer\n"
"   * is directly used as sliding window.\n"
"   */\n"
"\n"
"  this.prev = null;\n"
"  /* Link to older string with same hash index. To limit the size of this\n"
"   * array to 64K, this link is maintained only for the last 32K strings.\n"
"   * An index in this array is thus a window index modulo 32K.\n"
"   */\n"
"\n"
"  this.head = null;   /* Heads of the hash chains or NIL. */\n"
"\n"
"  this.ins_h = 0;       /* hash index of string to be inserted */\n"
"  this.hash_size = 0;   /* number of elements in hash table */\n"
"  this.hash_bits = 0;   /* log2(hash_size) */\n"
"  this.hash_mask = 0;   /* hash_size-1 */\n"
"\n"
"  this.hash_shift = 0;\n"
"  /* Number of bits by which ins_h must be shifted at each input\n"
"   * step. It must be such that after MIN_MATCH steps, the oldest\n"
"   * byte no longer takes part in the hash key, that is:\n"
"   *   hash_shift * MIN_MATCH >= hash_bits\n"
"   */\n"
"\n"
"  this.block_start = 0;\n"
"  /* Window position at the beginning of the current output block. Gets\n"
"   * negative when the window is moved backwards.\n"
"   */\n"
"\n"
"  this.match_length = 0;      /* length of best match */\n"
"  this.prev_match = 0;        /* previous match */\n"
"  this.match_available = 0;   /* set if previous match exists */\n"
"  this.strstart = 0;          /* start of string to insert */\n"
"  this.match_start = 0;       /* start of matching string */\n"
"  this.lookahead = 0;         /* number of valid bytes ahead in window */\n"
"\n"
"  this.prev_length = 0;\n"
"  /* Length of the best match at previous step. Matches not greater than this\n"
"   * are discarded. This is used in the lazy match evaluation.\n"
"   */\n"
"\n"
"  this.max_chain_length = 0;\n"
"  /* To speed up deflation, hash chains are never searched beyond this\n"
"   * length.  A higher limit improves compression ratio but degrades the\n"
"   * speed.\n"
"   */\n"
"\n"
"  this.max_lazy_match = 0;\n"
"  /* Attempt to find a better match only when the current match is strictly\n"
"   * smaller than this value. This mechanism is used only for compression\n"
"   * levels >= 4.\n"
"   */\n"
"  // That's alias to max_lazy_match, don't use directly\n"
"  //this.max_insert_length = 0;\n"
"  /* Insert new strings in the hash table only if the match length is not\n"
"   * greater than this length. This saves time but degrades compression.\n"
"   * max_insert_length is used only for compression levels <= 3.\n"
"   */\n"
"\n"
"  this.level = 0;     /* compression level (1..9) */\n"
"  this.strategy = 0;  /* favor or force Huffman coding*/\n"
"\n"
"  this.good_match = 0;\n"
"  /* Use a faster search when the previous match is longer than this */\n"
"\n"
"  this.nice_match = 0; /* Stop searching when current match exceeds this */\n"
"\n"
"              /* used by trees.c: */\n"
"\n"
"  /* Didn't use ct_data typedef below to suppress compiler warning */\n"
"\n"
"  // struct ct_data_s dyn_ltree[HEAP_SIZE];   /* literal and length tree */\n"
"  // struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */\n"
"  // struct ct_data_s bl_tree[2*BL_CODES+1];  /* Huffman tree for bit lengths */\n"
"\n"
"  // Use flat array of DOUBLE size, with interleaved fata,\n"
"  // because JS does not support effective\n"
"  this.dyn_ltree  = new Uint16Array(HEAP_SIZE * 2);\n"
"  this.dyn_dtree  = new Uint16Array((2 * D_CODES + 1) * 2);\n"
"  this.bl_tree    = new Uint16Array((2 * BL_CODES + 1) * 2);\n"
"  zero(this.dyn_ltree);\n"
"  zero(this.dyn_dtree);\n"
"  zero(this.bl_tree);\n"
"\n"
"  this.l_desc   = null;         /* desc. for literal tree */\n"
"  this.d_desc   = null;         /* desc. for distance tree */\n"
"  this.bl_desc  = null;         /* desc. for bit length tree */\n"
"\n"
"  //ush bl_count[MAX_BITS+1];\n"
"  this.bl_count = new Uint16Array(MAX_BITS + 1);\n"
"  /* number of codes at each bit length for an optimal tree */\n"
"\n"
"  //int heap[2*L_CODES+1];      /* heap used to build the Huffman trees */\n"
"  this.heap = new Uint16Array(2 * L_CODES + 1);  /* heap used to build the Huffman trees */\n"
"  zero(this.heap);\n"
"\n"
"  this.heap_len = 0;               /* number of elements in the heap */\n"
"  this.heap_max = 0;               /* element of largest frequency */\n"
"  /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.\n"
"   * The same heap array is used to build all trees.\n"
"   */\n"
"\n"
"  this.depth = new Uint16Array(2 * L_CODES + 1); //uch depth[2*L_CODES+1];\n"
"  zero(this.depth);\n"
"  /* Depth of each subtree used as tie breaker for trees of equal frequency\n"
"   */\n"
"\n"
"  this.sym_buf = 0;        /* buffer for distances and literals/lengths */\n"
"\n"
"  this.lit_bufsize = 0;\n"
"  /* Size of match buffer for literals/lengths.  There are 4 reasons for\n"
"   * limiting lit_bufsize to 64K:\n"
"   *   - frequencies can be kept in 16 bit counters\n"
"   *   - if compression is not successful for the first block, all input\n"
"   *     data is still in the window so we can still emit a stored block even\n"
"   *     when input comes from standard input.  (This can also be done for\n"
"   *     all blocks if lit_bufsize is not greater than 32K.)\n"
"   *   - if compression is not successful for a file smaller than 64K, we can\n"
"   *     even emit a stored file instead of a stored block (saving 5 bytes).\n"
"   *     This is applicable only for zip (not gzip or zlib).\n"
"   *   - creating new Huffman trees less frequently may not provide fast\n"
"   *     adaptation to changes in the input data statistics. (Take for\n"
"   *     example a binary file with poorly compressible code followed by\n"
"   *     a highly compressible string table.) Smaller buffer sizes give\n"
"   *     fast adaptation but have of course the overhead of transmitting\n"
"   *     trees more frequently.\n"
"   *   - I can't count above 4\n"
"   */\n"
"\n"
"  this.sym_next = 0;      /* running index in sym_buf */\n"
"  this.sym_end = 0;       /* symbol table full when sym_next reaches this */\n"
"\n"
"  this.opt_len = 0;       /* bit length of current block with optimal trees */\n"
"  this.static_len = 0;    /* bit length of current block with static trees */\n"
"  this.matches = 0;       /* number of string matches in current block */\n"
"  this.insert = 0;        /* bytes at end of window left to insert */\n"
"\n"
"\n"
"  this.bi_buf = 0;\n"
"  /* Output buffer. bits are inserted starting at the bottom (least\n"
"   * significant bits).\n"
"   */\n"
"  this.bi_valid = 0;\n"
"  /* Number of valid bits in bi_buf.  All bits above the last valid bit\n"
"   * are always zero.\n"
"   */\n"
"\n"
"  // Used for window memory init. We safely ignore it for JS. That makes\n"
"  // sense only for pointers and memory check tools.\n"
"  //this.high_water = 0;\n"
"  /* High water mark offset in window for initialized bytes -- bytes above\n"
"   * this are set to zero in order to avoid memory check warnings when\n"
"   * longest match routines access bytes past the input.  This is then\n"
"   * updated to the new high water mark.\n"
"   */\n"
"}\n"
"\n"
"\n"
"/* =========================================================================\n"
" * Check for a valid deflate stream state. Return 0 if ok, 1 if not.\n"
" */\n"
"const deflateStateCheck = (strm) => {\n"
"\n"
"  if (!strm) {\n"
"    return 1;\n"
"  }\n"
"  const s = strm.state;\n"
"  if (!s || s.strm !== strm || (s.status !== INIT_STATE &&\n"
"//#ifdef GZIP\n"
"                                s.status !== GZIP_STATE &&\n"
"//#endif\n"
"                                s.status !== EXTRA_STATE &&\n"
"                                s.status !== NAME_STATE &&\n"
"                                s.status !== COMMENT_STATE &&\n"
"                                s.status !== HCRC_STATE &&\n"
"                                s.status !== BUSY_STATE &&\n"
"                                s.status !== FINISH_STATE)) {\n"
"    return 1;\n"
"  }\n"
"  return 0;\n"
"};\n"
"\n"
"\n"
"const deflateResetKeep = (strm) => {\n"
"\n"
"  if (deflateStateCheck(strm)) {\n"
"    return err(strm, Z_STREAM_ERROR);\n"
"  }\n"
"\n"
"  strm.total_in = strm.total_out = 0;\n"
"  strm.data_type = Z_UNKNOWN;\n"
"\n"
"  const s = strm.state;\n"
"  s.pending = 0;\n"
"  s.pending_out = 0;\n"
"\n"
"  if (s.wrap < 0) {\n"
"    s.wrap = -s.wrap;\n"
"    /* was made negative by deflate(..., Z_FINISH); */\n"
"  }\n"
"  s.status =\n"
"//#ifdef GZIP\n"
"    s.wrap === 2 ? GZIP_STATE :\n"
"//#endif\n"
"    s.wrap ? INIT_STATE : BUSY_STATE;\n"
"  strm.adler = (s.wrap === 2) ?\n"
"    0  // crc32(0, Z_NULL, 0)\n"
"  :\n"
"    1; // adler32(0, Z_NULL, 0)\n"
"  s.last_flush = -2;\n"
"  _tr_init(s);\n"
"  return Z_OK;\n"
"};\n"
"\n"
"\n"
"const deflateReset = (strm) => {\n"
"\n"
"  const ret = deflateResetKeep(strm);\n"
"  if (ret === Z_OK) {\n"
"    lm_init(strm.state);\n"
"  }\n"
"  return ret;\n"
"};\n"
"\n"
"\n"
"const deflateSetHeader = (strm, head) => {\n"
"\n"
"  if (deflateStateCheck(strm) || strm.state.wrap !== 2) {\n"
"    return Z_STREAM_ERROR;\n"
"  }\n"
"  strm.state.gzhead = head;\n"
"  return Z_OK;\n"
"};\n"
"\n"
"\n"
"const deflateInit2 = (strm, level, method, windowBits, memLevel, strategy) => {\n"
"\n"
"  if (!strm) { // === Z_NULL\n"
"    return Z_STREAM_ERROR;\n"
"  }\n"
"  let wrap = 1;\n"
"\n"
"  if (level === Z_DEFAULT_COMPRESSION) {\n"
"    level = 6;\n"
"  }\n"
"\n"
"  if (windowBits < 0) { /* suppress zlib wrapper */\n"
"    wrap = 0;\n"
"    windowBits = -windowBits;\n"
"  }\n"
"\n"
"  else if (windowBits > 15) {\n"
"    wrap = 2;           /* write gzip wrapper instead */\n"
"    windowBits -= 16;\n"
"  }\n"
"\n"
"\n"
"  if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED ||\n"
"    windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||\n"
"    strategy < 0 || strategy > Z_FIXED || (windowBits === 8 && wrap !== 1)) {\n"
"    return err(strm, Z_STREAM_ERROR);\n"
"  }\n"
"\n"
"\n"
"  if (windowBits === 8) {\n"
"    windowBits = 9;\n"
"  }\n"
"  /* until 256-byte window bug fixed */\n"
"\n"
"  const s = new DeflateState();\n"
"\n"
"  strm.state = s;\n"
"  s.strm = strm;\n"
"  s.status = INIT_STATE;     /* to pass state test in deflateReset() */\n"
"\n"
"  s.wrap = wrap;\n"
"  s.gzhead = null;\n"
"  s.w_bits = windowBits;\n"
"  s.w_size = 1 << s.w_bits;\n"
"  s.w_mask = s.w_size - 1;\n"
"\n"
"  s.hash_bits = memLevel + 7;\n"
"  s.hash_size = 1 << s.hash_bits;\n"
"  s.hash_mask = s.hash_size - 1;\n"
"  s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH);\n"
"\n"
"  s.window = new Uint8Array(s.w_size * 2);\n"
"  s.head = new Uint16Array(s.hash_size);\n"
"  s.prev = new Uint16Array(s.w_size);\n"
"\n"
"  // Don't need mem init magic for JS.\n"
"  //s.high_water = 0;  /* nothing written to s->window yet */\n"
"\n"
"  s.lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */\n"
"\n"
"  /* We overlay pending_buf and sym_buf. This works since the average size\n"
"   * for length/distance pairs over any compressed block is assured to be 31\n"
"   * bits or less.\n"
"   *\n"
"   * Analysis: The longest fixed codes are a length code of 8 bits plus 5\n"
"   * extra bits, for lengths 131 to 257. The longest fixed distance codes are\n"
"   * 5 bits plus 13 extra bits, for distances 16385 to 32768. The longest\n"
"   * possible fixed-codes length/distance pair is then 31 bits total.\n"
"   *\n"
"   * sym_buf starts one-fourth of the way into pending_buf. So there are\n"
"   * three bytes in sym_buf for every four bytes in pending_buf. Each symbol\n"
"   * in sym_buf is three bytes -- two for the distance and one for the\n"
"   * literal/length. As each symbol is consumed, the pointer to the next\n"
"   * sym_buf value to read moves forward three bytes. From that symbol, up to\n"
"   * 31 bits are written to pending_buf. The closest the written pending_buf\n"
"   * bits gets to the next sym_buf symbol to read is just before the last\n"
"   * code is written. At that time, 31*(n-2) bits have been written, just\n"
"   * after 24*(n-2) bits have been consumed from sym_buf. sym_buf starts at\n"
"   * 8*n bits into pending_buf. (Note that the symbol buffer fills when n-1\n"
"   * symbols are written.) The closest the writing gets to what is unread is\n"
"   * then n+14 bits. Here n is lit_bufsize, which is 16384 by default, and\n"
"   * can range from 128 to 32768.\n"
"   *\n"
"   * Therefore, at a minimum, there are 142 bits of space between what is\n"
"   * written and what is read in the overlain buffers, so the symbols cannot\n"
"   * be overwritten by the compressed data. That space is actually 139 bits,\n"
"   * due to the three-bit fixed-code block header.\n"
"   *\n"
"   * That covers the case where either Z_FIXED is specified, forcing fixed\n"
"   * codes, or when the use of fixed codes is chosen, because that choice\n"
"   * results in a smaller compressed block than dynamic codes. That latter\n"
"   * condition then assures that the above analysis also covers all dynamic\n"
"   * blocks. A dynamic-code block will only be chosen to be emitted if it has\n"
"   * fewer bits than a fixed-code block would for the same set of symbols.\n"
"   * Therefore its average symbol length is assured to be less than 31. So\n"
"   * the compressed data for a dynamic block also cannot overwrite the\n"
"   * symbols from which it is being constructed.\n"
"   */\n"
"\n"
"  s.pending_buf_size = s.lit_bufsize * 4;\n"
"  s.pending_buf = new Uint8Array(s.pending_buf_size);\n"
"\n"
"  // It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`)\n"
"  //s->sym_buf = s->pending_buf + s->lit_bufsize;\n"
"  s.sym_buf = s.lit_bufsize;\n"
"\n"
"  //s->sym_end = (s->lit_bufsize - 1) * 3;\n"
"  s.sym_end = (s.lit_bufsize - 1) * 3;\n"
"  /* We avoid equality with lit_bufsize*3 because of wraparound at 64K\n"
"   * on 16 bit machines and because stored blocks are restricted to\n"
"   * 64K-1 bytes.\n"
"   */\n"
"\n"
"  s.level = level;\n"
"  s.strategy = strategy;\n"
"  s.method = method;\n"
"\n"
"  return deflateReset(strm);\n"
"};\n"
"\n"
"const deflateInit = (strm, level) => {\n"
"\n"
"  return deflateInit2(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);\n"
"};\n"
"\n"
"\n"
"/* ========================================================================= */\n"
"const deflate = (strm, flush) => {\n"
"\n"
"  if (deflateStateCheck(strm) || flush > Z_BLOCK || flush < 0) {\n"
"    return strm ? err(strm, Z_STREAM_ERROR) : Z_STREAM_ERROR;\n"
"  }\n"
"\n"
"  const s = strm.state;\n"
"\n"
"  if (!strm.output ||\n"
"      (strm.avail_in !== 0 && !strm.input) ||\n"
"      (s.status === FINISH_STATE && flush !== Z_FINISH)) {\n"
"    return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR : Z_STREAM_ERROR);\n"
"  }\n"
"\n"
"  const old_flush = s.last_flush;\n"
"  s.last_flush = flush;\n"
"\n"
"  /* Flush as much pending output as possible */\n"
"  if (s.pending !== 0) {\n"
"    flush_pending(strm);\n"
"    if (strm.avail_out === 0) {\n"
"      /* Since avail_out is 0, deflate will be called again with\n"
"       * more output space, but possibly with both pending and\n"
"       * avail_in equal to zero. There won't be anything to do,\n"
"       * but this is not an error situation so make sure we\n"
"       * return OK instead of BUF_ERROR at next call of deflate:\n"
"       */\n"
"      s.last_flush = -1;\n"
"      return Z_OK;\n"
"    }\n"
"\n"
"    /* Make sure there is something to do and avoid duplicate consecutive\n"
"     * flushes. For repeated and useless calls with Z_FINISH, we keep\n"
"     * returning Z_STREAM_END instead of Z_BUF_ERROR.\n"
"     */\n"
"  } else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) &&\n"
"    flush !== Z_FINISH) {\n"
"    return err(strm, Z_BUF_ERROR);\n"
"  }\n"
"\n"
"  /* User must not provide more input after the first FINISH: */\n"
"  if (s.status === FINISH_STATE && strm.avail_in !== 0) {\n"
"    return err(strm, Z_BUF_ERROR);\n"
"  }\n"
"\n"
"  /* Write the header */\n"
"  if (s.status === INIT_STATE && s.wrap === 0) {\n"
"    s.status = BUSY_STATE;\n"
"  }\n"
"  if (s.status === INIT_STATE) {\n"
"    /* zlib header */\n"
"    let header = (Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8;\n"
"    let level_flags = -1;\n"
"\n"
"    if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) {\n"
"      level_flags = 0;\n"
"    } else if (s.level < 6) {\n"
"      level_flags = 1;\n"
"    } else if (s.level === 6) {\n"
"      level_flags = 2;\n"
"    } else {\n"
"      level_flags = 3;\n"
"    }\n"
"    header |= (level_flags << 6);\n"
"    if (s.strstart !== 0) { header |= PRESET_DICT; }\n"
"    header += 31 - (header % 31);\n"
"\n"
"    putShortMSB(s, header);\n"
"\n"
"    /* Save the adler32 of the preset dictionary: */\n"
"    if (s.strstart !== 0) {\n"
"      putShortMSB(s, strm.adler >>> 16);\n"
"      putShortMSB(s, strm.adler & 0xffff);\n"
"    }\n"
"    strm.adler = 1; // adler32(0L, Z_NULL, 0);\n"
"    s.status = BUSY_STATE;\n"
"\n"
"    /* Compression must start with an empty pending buffer */\n"
"    flush_pending(strm);\n"
"    if (s.pending !== 0) {\n"
"      s.last_flush = -1;\n"
"      return Z_OK;\n"
"    }\n"
"  }\n"
"//#ifdef GZIP\n"
"  if (s.status === GZIP_STATE) {\n"
"    /* gzip header */\n"
"    strm.adler = 0;  //crc32(0L, Z_NULL, 0);\n"
"    put_byte(s, 31);\n"
"    put_byte(s, 139);\n"
"    put_byte(s, 8);\n"
"    if (!s.gzhead) { // s->gzhead == Z_NULL\n"
"      put_byte(s, 0);\n"
"      put_byte(s, 0);\n"
"      put_byte(s, 0);\n"
"      put_byte(s, 0);\n"
"      put_byte(s, 0);\n"
"      put_byte(s, s.level === 9 ? 2 :\n"
"                  (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ?\n"
"                   4 : 0));\n"
"      put_byte(s, OS_CODE);\n"
"      s.status = BUSY_STATE;\n"
"\n"
"      /* Compression must start with an empty pending buffer */\n"
"      flush_pending(strm);\n"
"      if (s.pending !== 0) {\n"
"        s.last_flush = -1;\n"
"        return Z_OK;\n"
"      }\n"
"    }\n"
"    else {\n"
"      put_byte(s, (s.gzhead.text ? 1 : 0) +\n"
"                  (s.gzhead.hcrc ? 2 : 0) +\n"
"                  (!s.gzhead.extra ? 0 : 4) +\n"
"                  (!s.gzhead.name ? 0 : 8) +\n"
"                  (!s.gzhead.comment ? 0 : 16)\n"
"      );\n"
"      put_byte(s, s.gzhead.time & 0xff);\n"
"      put_byte(s, (s.gzhead.time >> 8) & 0xff);\n"
"      put_byte(s, (s.gzhead.time >> 16) & 0xff);\n"
"      put_byte(s, (s.gzhead.time >> 24) & 0xff);\n"
"      put_byte(s, s.level === 9 ? 2 :\n"
"                  (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ?\n"
"                   4 : 0));\n"
"      put_byte(s, s.gzhead.os & 0xff);\n"
"      if (s.gzhead.extra && s.gzhead.extra.length) {\n"
"        put_byte(s, s.gzhead.extra.length & 0xff);\n"
"        put_byte(s, (s.gzhead.extra.length >> 8) & 0xff);\n"
"      }\n"
"      if (s.gzhead.hcrc) {\n"
"        strm.adler = crc32(strm.adler, s.pending_buf, s.pending, 0);\n"
"      }\n"
"      s.gzindex = 0;\n"
"      s.status = EXTRA_STATE;\n"
"    }\n"
"  }\n"
"  if (s.status === EXTRA_STATE) {\n"
"    if (s.gzhead.extra/* != Z_NULL*/) {\n"
"      let beg = s.pending;   /* start of bytes to update crc */\n"
"      let left = (s.gzhead.extra.length & 0xffff) - s.gzindex;\n"
"      while (s.pending + left > s.pending_buf_size) {\n"
"        let copy = s.pending_buf_size - s.pending;\n"
"        // zmemcpy(s.pending_buf + s.pending,\n"
"        //    s.gzhead.extra + s.gzindex, copy);\n"
"        s.pending_buf.set(s.gzhead.extra.subarray(s.gzindex, s.gzindex + copy), s.pending);\n"
"        s.pending = s.pending_buf_size;\n"
"        //--- HCRC_UPDATE(beg) ---//\n"
"        if (s.gzhead.hcrc && s.pending > beg) {\n"
"          strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);\n"
"        }\n"
"        //---//\n"
"        s.gzindex += copy;\n"
"        flush_pending(strm);\n"
"        if (s.pending !== 0) {\n"
"          s.last_flush = -1;\n"
"          return Z_OK;\n"
"        }\n"
"        beg = 0;\n"
"        left -= copy;\n"
"      }\n"
"      // JS specific: s.gzhead.extra may be TypedArray or Array for backward compatibility\n"
"      //              TypedArray.slice and TypedArray.from don't exist in IE10-IE11\n"
"      let gzhead_extra = new Uint8Array(s.gzhead.extra);\n"
"      // zmemcpy(s->pending_buf + s->pending,\n"
"      //     s->gzhead->extra + s->gzindex, left);\n"
"      s.pending_buf.set(gzhead_extra.subarray(s.gzindex, s.gzindex + left), s.pending);\n"
"      s.pending += left;\n"
"      //--- HCRC_UPDATE(beg) ---//\n"
"      if (s.gzhead.hcrc && s.pending > beg) {\n"
"        strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);\n"
"      }\n"
"      //---//\n"
"      s.gzindex = 0;\n"
"    }\n"
"    s.status = NAME_STATE;\n"
"  }\n"
"  if (s.status === NAME_STATE) {\n"
"    if (s.gzhead.name/* != Z_NULL*/) {\n"
"      let beg = s.pending;   /* start of bytes to update crc */\n"
"      let val;\n"
"      do {\n"
"        if (s.pending === s.pending_buf_size) {\n"
"          //--- HCRC_UPDATE(beg) ---//\n"
"          if (s.gzhead.hcrc && s.pending > beg) {\n"
"            strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);\n"
"          }\n"
"          //---//\n"
"          flush_pending(strm);\n"
"          if (s.pending !== 0) {\n"
"            s.last_flush = -1;\n"
"            return Z_OK;\n"
"          }\n"
"          beg = 0;\n"
"        }\n"
"        // JS specific: little magic to add zero terminator to end of string\n"
"        if (s.gzindex < s.gzhead.name.length) {\n"
"          val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff;\n"
"        } else {\n"
"          val = 0;\n"
"        }\n"
"        put_byte(s, val);\n"
"      } while (val !== 0);\n"
"      //--- HCRC_UPDATE(beg) ---//\n"
"      if (s.gzhead.hcrc && s.pending > beg) {\n"
"        strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);\n"
"      }\n"
"      //---//\n"
"      s.gzindex = 0;\n"
"    }\n"
"    s.status = COMMENT_STATE;\n"
"  }\n"
"  if (s.status === COMMENT_STATE) {\n"
"    if (s.gzhead.comment/* != Z_NULL*/) {\n"
"      let beg = s.pending;   /* start of bytes to update crc */\n"
"      let val;\n"
"      do {\n"
"        if (s.pending === s.pending_buf_size) {\n"
"          //--- HCRC_UPDATE(beg) ---//\n"
"          if (s.gzhead.hcrc && s.pending > beg) {\n"
"            strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);\n"
"          }\n"
"          //---//\n"
"          flush_pending(strm);\n"
"          if (s.pending !== 0) {\n"
"            s.last_flush = -1;\n"
"            return Z_OK;\n"
"          }\n"
"          beg = 0;\n"
"        }\n"
"        // JS specific: little magic to add zero terminator to end of string\n"
"        if (s.gzindex < s.gzhead.comment.length) {\n"
"          val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff;\n"
"        } else {\n"
"          val = 0;\n"
"        }\n"
"        put_byte(s, val);\n"
"      } while (val !== 0);\n"
"      //--- HCRC_UPDATE(beg) ---//\n"
"      if (s.gzhead.hcrc && s.pending > beg) {\n"
"        strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);\n"
"      }\n"
"      //---//\n"
"    }\n"
"    s.status = HCRC_STATE;\n"
"  }\n"
"  if (s.status === HCRC_STATE) {\n"
"    if (s.gzhead.hcrc) {\n"
"      if (s.pending + 2 > s.pending_buf_size) {\n"
"        flush_pending(strm);\n"
"        if (s.pending !== 0) {\n"
"          s.last_flush = -1;\n"
"          return Z_OK;\n"
"        }\n"
"      }\n"
"      put_byte(s, strm.adler & 0xff);\n"
"      put_byte(s, (strm.adler >> 8) & 0xff);\n"
"      strm.adler = 0; //crc32(0L, Z_NULL, 0);\n"
"    }\n"
"    s.status = BUSY_STATE;\n"
"\n"
"    /* Compression must start with an empty pending buffer */\n"
"    flush_pending(strm);\n"
"    if (s.pending !== 0) {\n"
"      s.last_flush = -1;\n"
"      return Z_OK;\n"
"    }\n"
"  }\n"
"//#endif\n"
"\n"
"  /* Start a new block or continue the current one.\n"
"   */\n"
"  if (strm.avail_in !== 0 || s.lookahead !== 0 ||\n"
"    (flush !== Z_NO_FLUSH && s.status !== FINISH_STATE)) {\n"
"    let bstate = s.level === 0 ? deflate_stored(s, flush) :\n"
"                 s.strategy === Z_HUFFMAN_ONLY ? deflate_huff(s, flush) :\n"
"                 s.strategy === Z_RLE ? deflate_rle(s, flush) :\n"
"                 configuration_table[s.level].func(s, flush);\n"
"\n"
"    if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) {\n"
"      s.status = FINISH_STATE;\n"
"    }\n"
"    if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) {\n"
"      if (strm.avail_out === 0) {\n"
"        s.last_flush = -1;\n"
"        /* avoid BUF_ERROR next call, see above */\n"
"      }\n"
"      return Z_OK;\n"
"      /* If flush != Z_NO_FLUSH && avail_out == 0, the next call\n"
"       * of deflate should use the same flush parameter to make sure\n"
"       * that the flush is complete. So we don't have to output an\n"
"       * empty block here, this will be done at next call. This also\n"
"       * ensures that for a very small output buffer, we emit at most\n"
"       * one empty block.\n"
"       */\n"
"    }\n"
"    if (bstate === BS_BLOCK_DONE) {\n"
"      if (flush === Z_PARTIAL_FLUSH) {\n"
"        _tr_align(s);\n"
"      }\n"
"      else if (flush !== Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */\n"
"\n"
"        _tr_stored_block(s, 0, 0, false);\n"
"        /* For a full flush, this empty block will be recognized\n"
"         * as a special marker by inflate_sync().\n"
"         */\n"
"        if (flush === Z_FULL_FLUSH) {\n"
"          /*** CLEAR_HASH(s); ***/             /* forget history */\n"
"          zero(s.head); // Fill with NIL (= 0);\n"
"\n"
"          if (s.lookahead === 0) {\n"
"            s.strstart = 0;\n"
"            s.block_start = 0;\n"
"            s.insert = 0;\n"
"          }\n"
"        }\n"
"      }\n"
"      flush_pending(strm);\n"
"      if (strm.avail_out === 0) {\n"
"        s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */\n"
"        return Z_OK;\n"
"      }\n"
"    }\n"
"  }\n"
"\n"
"  if (flush !== Z_FINISH) { return Z_OK; }\n"
"  if (s.wrap <= 0) { return Z_STREAM_END; }\n"
"\n"
"  /* Write the trailer */\n"
"  if (s.wrap === 2) {\n"
"    put_byte(s, strm.adler & 0xff);\n"
"    put_byte(s, (strm.adler >> 8) & 0xff);\n"
"    put_byte(s, (strm.adler >> 16) & 0xff);\n"
"    put_byte(s, (strm.adler >> 24) & 0xff);\n"
"    put_byte(s, strm.total_in & 0xff);\n"
"    put_byte(s, (strm.total_in >> 8) & 0xff);\n"
"    put_byte(s, (strm.total_in >> 16) & 0xff);\n"
"    put_byte(s, (strm.total_in >> 24) & 0xff);\n"
"  }\n"
"  else\n"
"  {\n"
"    putShortMSB(s, strm.adler >>> 16);\n"
"    putShortMSB(s, strm.adler & 0xffff);\n"
"  }\n"
"\n"
"  flush_pending(strm);\n"
"  /* If avail_out is zero, the application will call deflate again\n"
"   * to flush the rest.\n"
"   */\n"
"  if (s.wrap > 0) { s.wrap = -s.wrap; }\n"
"  /* write the trailer only once! */\n"
"  return s.pending !== 0 ? Z_OK : Z_STREAM_END;\n"
"};\n"
"\n"
"\n"
"const deflateEnd = (strm) => {\n"
"\n"
"  if (deflateStateCheck(strm)) {\n"
"    return Z_STREAM_ERROR;\n"
"  }\n"
"\n"
"  const status = strm.state.status;\n"
"\n"
"  strm.state = null;\n"
"\n"
"  return status === BUSY_STATE ? err(strm, Z_DATA_ERROR) : Z_OK;\n"
"};\n"
"\n"
"\n"
"/* =========================================================================\n"
" * Initializes the compression dictionary from the given byte\n"
" * sequence without producing any compressed output.\n"
" */\n"
"const deflateSetDictionary = (strm, dictionary) => {\n"
"\n"
"  let dictLength = dictionary.length;\n"
"\n"
"  if (deflateStateCheck(strm)) {\n"
"    return Z_STREAM_ERROR;\n"
"  }\n"
"\n"
"  const s = strm.state;\n"
"  const wrap = s.wrap;\n"
"\n"
"  if (wrap === 2 || (wrap === 1 && s.status !== INIT_STATE) || s.lookahead) {\n"
"    return Z_STREAM_ERROR;\n"
"  }\n"
"\n"
"  /* when using zlib wrappers, compute Adler-32 for provided dictionary */\n"
"  if (wrap === 1) {\n"
"    /* adler32(strm->adler, dictionary, dictLength); */\n"
"    strm.adler = adler32(strm.adler, dictionary, dictLength, 0);\n"
"  }\n"
"\n"
"  s.wrap = 0;   /* avoid computing Adler-32 in read_buf */\n"
"\n"
"  /* if dictionary would fill window, just replace the history */\n"
"  if (dictLength >= s.w_size) {\n"
"    if (wrap === 0) {            /* already empty otherwise */\n"
"      /*** CLEAR_HASH(s); ***/\n"
"      zero(s.head); // Fill with NIL (= 0);\n"
"      s.strstart = 0;\n"
"      s.block_start = 0;\n"
"      s.insert = 0;\n"
"    }\n"
"    /* use the tail */\n"
"    // dictionary = dictionary.slice(dictLength - s.w_size);\n"
"    let tmpDict = new Uint8Array(s.w_size);\n"
"    tmpDict.set(dictionary.subarray(dictLength - s.w_size, dictLength), 0);\n"
"    dictionary = tmpDict;\n"
"    dictLength = s.w_size;\n"
"  }\n"
"  /* insert dictionary into window and hash */\n"
"  const avail = strm.avail_in;\n"
"  const next = strm.next_in;\n"
"  const input = strm.input;\n"
"  strm.avail_in = dictLength;\n"
"  strm.next_in = 0;\n"
"  strm.input = dictionary;\n"
"  fill_window(s);\n"
"  while (s.lookahead >= MIN_MATCH) {\n"
"    let str = s.strstart;\n"
"    let n = s.lookahead - (MIN_MATCH - 1);\n"
"    do {\n"
"      /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */\n"
"      s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]);\n"
"\n"
"      s.prev[str & s.w_mask] = s.head[s.ins_h];\n"
"\n"
"      s.head[s.ins_h] = str;\n"
"      str++;\n"
"    } while (--n);\n"
"    s.strstart = str;\n"
"    s.lookahead = MIN_MATCH - 1;\n"
"    fill_window(s);\n"
"  }\n"
"  s.strstart += s.lookahead;\n"
"  s.block_start = s.strstart;\n"
"  s.insert = s.lookahead;\n"
"  s.lookahead = 0;\n"
"  s.match_length = s.prev_length = MIN_MATCH - 1;\n"
"  s.match_available = 0;\n"
"  strm.next_in = next;\n"
"  strm.input = input;\n"
"  strm.avail_in = avail;\n"
"  s.wrap = wrap;\n"
"  return Z_OK;\n"
"};\n"
"\n"
"\n"
"window.deflateInit = deflateInit;\n"
"window.deflateInit2 = deflateInit2;\n"
"window.deflateReset = deflateReset;\n"
"window.deflateResetKeep = deflateResetKeep;\n"
"window.deflateSetHeader = deflateSetHeader;\n"
"window.deflate = deflate;\n"
"window.deflateEnd = deflateEnd;\n"
"window.deflateSetDictionary = deflateSetDictionary;\n"
"window.MAX_MEM_LEVEL = MAX_MEM_LEVEL;\n"
"window.MAX_WBITS = MAX_WBITS;\n"
"window.DEF_MEM_LEVEL = DEF_MEM_LEVEL;\n"
"window.LENGTH_CODES = LENGTH_CODES;\n"
"window.LITERALS = LITERALS;\n"
"window.L_CODES = L_CODES;\n"
"window.D_CODES = D_CODES;\n"
"window.BL_CODES = BL_CODES;\n"
"window.HEAP_SIZE = HEAP_SIZE;\n"
"window.MAX_BITS = MAX_BITS;\n"
"window.MIN_MATCH = MIN_MATCH;\n"
"window.MAX_MATCH = MAX_MATCH;\n"
"window.MIN_LOOKAHEAD = MIN_LOOKAHEAD;\n"
"window.PRESET_DICT = PRESET_DICT;\n"
"window.INIT_STATE = INIT_STATE;\n"
"window.GZIP_STATE = GZIP_STATE;\n"
"window.EXTRA_STATE = EXTRA_STATE;\n"
"window.NAME_STATE = NAME_STATE;\n"
"window.COMMENT_STATE = COMMENT_STATE;\n"
"window.HCRC_STATE = HCRC_STATE;\n"
"window.BUSY_STATE = BUSY_STATE;\n"
"window.FINISH_STATE = FINISH_STATE;\n"
"window.BS_NEED_MORE = BS_NEED_MORE;\n"
"window.BS_BLOCK_DONE = BS_BLOCK_DONE;\n"
"window.BS_FINISH_STARTED = BS_FINISH_STARTED;\n"
"window.BS_FINISH_DONE = BS_FINISH_DONE;\n"
"window.OS_CODE = OS_CODE;\n"
"window.err = err;\n"
"window.rank = rank;\n"
"window.zero = zero;\n"
"window.slide_hash = slide_hash;\n"
"window.HASH_ZLIB = HASH_ZLIB;\n"
"window.flush_pending = flush_pending;\n"
"window.flush_block_only = flush_block_only;\n"
"window.put_byte = put_byte;\n"
"window.putShortMSB = putShortMSB;\n"
"window.read_buf = read_buf;\n"
"window.longest_match = longest_match;\n"
"window.fill_window = fill_window;\n"
"window.deflate_stored = deflate_stored;\n"
"window.deflate_fast = deflate_fast;\n"
"window.deflate_slow = deflate_slow;\n"
"window.deflate_rle = deflate_rle;\n"
"window.deflate_huff = deflate_huff;\n"
"window.Config = Config;\n"
"window.configuration_table = configuration_table;\n"
"window.lm_init = lm_init;\n"
"window.DeflateState = DeflateState;\n"
"window.deflateStateCheck = deflateStateCheck;\n"
"window.deflateResetKeep = deflateResetKeep;\n"
"window.deflateSetHeader = deflateSetHeader;\n"
"window.deflateInit2 = deflateInit2;\n"
"window.deflateEnd = deflateEnd;\n"
"window.deflateSetDictionary = deflateSetDictionary;\n"
;
    if (strcmp(path, "/novnc/vendor/pako/lib/zlib/deflate.js") == 0) return novnc_vendor_pako_lib_zlib_deflate_js;
const char *novnc_vendor_pako_lib_zlib_inffast_js =
"'use strict';\n"
"\n"
"// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n"
"// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n"
"//\n"
"// This software is provided 'as-is', without any express or implied\n"
"// warranty. In no event will the authors be held liable for any damages\n"
"// arising from the use of this software.\n"
"//\n"
"// Permission is granted to anyone to use this software for any purpose,\n"
"// including commercial applications, and to alter it and redistribute it\n"
"// freely, subject to the following restrictions:\n"
"//\n"
"// 1. The origin of this software must not be misrepresented; you must not\n"
"//   claim that you wrote the original software. If you use this software\n"
"//   in a product, an acknowledgment in the product documentation would be\n"
"//   appreciated but is not required.\n"
"// 2. Altered source versions must be plainly marked as such, and must not be\n"
"//   misrepresented as being the original software.\n"
"// 3. This notice may not be removed or altered from any source distribution.\n"
"\n"
"// See state defs from inflate.js\n"
"\n"
"/*\n"
"   Decode literal, length, and distance codes and write out the resulting\n"
"   literal and match bytes until either not enough input or output is\n"
"   available, an end-of-block is encountered, or a data error is encountered.\n"
"   When large enough input and output buffers are supplied to inflate(), for\n"
"   example, a 16K input buffer and a 64K output buffer, more than 95% of the\n"
"   inflate execution time is spent in this routine.\n"
"\n"
"   Entry assumptions:\n"
"\n"
"        state.mode === LEN\n"
"        strm.avail_in >= 6\n"
"        strm.avail_out >= 258\n"
"        start >= strm.avail_out\n"
"        state.bits < 8\n"
"\n"
"   On return, state.mode is one of:\n"
"\n"
"        LEN -- ran out of enough output space or enough available input\n"
"        TYPE -- reached end of block code, inflate() to interpret next block\n"
"        BAD -- error in block data\n"
"\n"
"   Notes:\n"
"\n"
"    - The maximum input bits used by a length/distance pair is 15 bits for the\n"
"      length code, 5 bits for the length extra, 15 bits for the distance code,\n"
"      and 13 bits for the distance extra.  This totals 48 bits, or six bytes.\n"
"      Therefore if strm.avail_in >= 6, then there is enough input to avoid\n"
"      checking for available input while decoding.\n"
"\n"
"    - The maximum bytes that a single length/distance pair can output is 258\n"
"      bytes, which is the maximum length that can be coded.  inflate_fast()\n"
"      requires strm.avail_out >= 258 for each loop to avoid checking for\n"
"      output space.\n"
" */\n"
"function inflate_fast(strm, start) {\n"
"  let _in;                    /* local strm.input */\n"
"  let last;                   /* have enough input while in < last */\n"
"  let _out;                   /* local strm.output */\n"
"  let beg;                    /* inflate()'s initial strm.output */\n"
"  let end;                    /* while out < end, enough space available */\n"
"//#ifdef INFLATE_STRICT\n"
"  let dmax;                   /* maximum distance from zlib header */\n"
"//#endif\n"
"  let wsize;                  /* window size or zero if not using window */\n"
"  let whave;                  /* valid bytes in the window */\n"
"  let wnext;                  /* window write index */\n"
"  // Use `s_window` instead `window`, avoid conflict with instrumentation tools\n"
"  let s_window;               /* allocated sliding window, if wsize != 0 */\n"
"  let hold;                   /* local strm.hold */\n"
"  let bits;                   /* local strm.bits */\n"
"  let lcode;                  /* local strm.lencode */\n"
"  let dcode;                  /* local strm.distcode */\n"
"  let lmask;                  /* mask for first level of length codes */\n"
"  let dmask;                  /* mask for first level of distance codes */\n"
"  let here;                   /* retrieved table entry */\n"
"  let op;                     /* code bits, operation, extra bits, or */\n"
"                              /*  window position, window bytes to copy */\n"
"  let len;                    /* match length, unused bytes */\n"
"  let dist;                   /* match distance */\n"
"  let from;                   /* where to copy match from */\n"
"  let from_source;\n"
"\n"
"\n"
"  let input, output; // JS specific, because we have no pointers\n"
"\n"
"  /* copy state to local variables */\n"
"  const state = strm.state;\n"
"  //here = state.here;\n"
"  _in = strm.next_in;\n"
"  input = strm.input;\n"
"  last = _in + (strm.avail_in - 5);\n"
"  _out = strm.next_out;\n"
"  output = strm.output;\n"
"  beg = _out - (start - strm.avail_out);\n"
"  end = _out + (strm.avail_out - 257);\n"
"//#ifdef INFLATE_STRICT\n"
"  dmax = state.dmax;\n"
"//#endif\n"
"  wsize = state.wsize;\n"
"  whave = state.whave;\n"
"  wnext = state.wnext;\n"
"  s_window = state.window;\n"
"  hold = state.hold;\n"
"  bits = state.bits;\n"
"  lcode = state.lencode;\n"
"  dcode = state.distcode;\n"
"  lmask = (1 << state.lenbits) - 1;\n"
"  dmask = (1 << state.distbits) - 1;\n"
"\n"
"\n"
"  /* decode literals and length/distances until end-of-block or not enough\n"
"     input data or output space */\n"
"\n"
"  top:\n"
"  do {\n"
"    if (bits < 15) {\n"
"      hold += input[_in++] << bits;\n"
"      bits += 8;\n"
"      hold += input[_in++] << bits;\n"
"      bits += 8;\n"
"    }\n"
"\n"
"    here = lcode[hold & lmask];\n"
"\n"
"    dolen:\n"
"    for (;;) { // Goto emulation\n"
"      op = here >>> 24/*here.bits*/;\n"
"      hold >>>= op;\n"
"      bits -= op;\n"
"      op = (here >>> 16) & 0xff/*here.op*/;\n"
"      if (op === 0) {                          /* literal */\n"
"        //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?\n"
"        //        \"inflate:         literal '%c'\\n\" :\n"
"        //        \"inflate:         literal 0x%02x\\n\", here.val));\n"
"        output[_out++] = here & 0xffff/*here.val*/;\n"
"      }\n"
"      else if (op & 16) {                     /* length base */\n"
"        len = here & 0xffff/*here.val*/;\n"
"        op &= 15;                           /* number of extra bits */\n"
"        if (op) {\n"
"          if (bits < op) {\n"
"            hold += input[_in++] << bits;\n"
"            bits += 8;\n"
"          }\n"
"          len += hold & ((1 << op) - 1);\n"
"          hold >>>= op;\n"
"          bits -= op;\n"
"        }\n"
"        //Tracevv((stderr, \"inflate:         length %u\\n\", len));\n"
"        if (bits < 15) {\n"
"          hold += input[_in++] << bits;\n"
"          bits += 8;\n"
"          hold += input[_in++] << bits;\n"
"          bits += 8;\n"
"        }\n"
"        here = dcode[hold & dmask];\n"
"\n"
"        dodist:\n"
"        for (;;) { // goto emulation\n"
"          op = here >>> 24/*here.bits*/;\n"
"          hold >>>= op;\n"
"          bits -= op;\n"
"          op = (here >>> 16) & 0xff/*here.op*/;\n"
"\n"
"          if (op & 16) {                      /* distance base */\n"
"            dist = here & 0xffff/*here.val*/;\n"
"            op &= 15;                       /* number of extra bits */\n"
"            if (bits < op) {\n"
"              hold += input[_in++] << bits;\n"
"              bits += 8;\n"
"              if (bits < op) {\n"
"                hold += input[_in++] << bits;\n"
"                bits += 8;\n"
"              }\n"
"            }\n"
"            dist += hold & ((1 << op) - 1);\n"
"//#ifdef INFLATE_STRICT\n"
"            if (dist > dmax) {\n"
"              strm.msg = 'invalid distance too far back';\n"
"              state.mode = BAD;\n"
"              break top;\n"
"            }\n"
"//#endif\n"
"            hold >>>= op;\n"
"            bits -= op;\n"
"            //Tracevv((stderr, \"inflate:         distance %u\\n\", dist));\n"
"            op = _out - beg;                /* max distance in output */\n"
"            if (dist > op) {                /* see if copy from window */\n"
"              op = dist - op;               /* distance back in window */\n"
"              if (op > whave) {\n"
"                if (state.sane) {\n"
"                  strm.msg = 'invalid distance too far back';\n"
"                  state.mode = BAD;\n"
"                  break top;\n"
"                }\n"
"\n"
"// (!) This block is disabled in zlib defaults,\n"
"// don't enable it for binary compatibility\n"
"//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR\n"
"//                if (len <= op - whave) {\n"
"//                  do {\n"
"//                    output[_out++] = 0;\n"
"//                  } while (--len);\n"
"//                  continue top;\n"
"//                }\n"
"//                len -= op - whave;\n"
"//                do {\n"
"//                  output[_out++] = 0;\n"
"//                } while (--op > whave);\n"
"//                if (op === 0) {\n"
"//                  from = _out - dist;\n"
"//                  do {\n"
"//                    output[_out++] = output[from++];\n"
"//                  } while (--len);\n"
"//                  continue top;\n"
"//                }\n"
"//#endif\n"
"              }\n"
"              from = 0; // window index\n"
"              from_source = s_window;\n"
"              if (wnext === 0) {           /* very common case */\n"
"                from += wsize - op;\n"
"                if (op < len) {         /* some from window */\n"
"                  len -= op;\n"
"                  do {\n"
"                    output[_out++] = s_window[from++];\n"
"                  } while (--op);\n"
"                  from = _out - dist;  /* rest from output */\n"
"                  from_source = output;\n"
"                }\n"
"              }\n"
"              else if (wnext < op) {      /* wrap around window */\n"
"                from += wsize + wnext - op;\n"
"                op -= wnext;\n"
"                if (op < len) {         /* some from end of window */\n"
"                  len -= op;\n"
"                  do {\n"
"                    output[_out++] = s_window[from++];\n"
"                  } while (--op);\n"
"                  from = 0;\n"
"                  if (wnext < len) {  /* some from start of window */\n"
"                    op = wnext;\n"
"                    len -= op;\n"
"                    do {\n"
"                      output[_out++] = s_window[from++];\n"
"                    } while (--op);\n"
"                    from = _out - dist;      /* rest from output */\n"
"                    from_source = output;\n"
"                  }\n"
"                }\n"
"              }\n"
"              else {                      /* contiguous in window */\n"
"                from += wnext - op;\n"
"                if (op < len) {         /* some from window */\n"
"                  len -= op;\n"
"                  do {\n"
"                    output[_out++] = s_window[from++];\n"
"                  } while (--op);\n"
"                  from = _out - dist;  /* rest from output */\n"
"                  from_source = output;\n"
"                }\n"
"              }\n"
"              while (len > 2) {\n"
"                output[_out++] = from_source[from++];\n"
"                output[_out++] = from_source[from++];\n"
"                output[_out++] = from_source[from++];\n"
"                len -= 3;\n"
"              }\n"
"              if (len) {\n"
"                output[_out++] = from_source[from++];\n"
"                if (len > 1) {\n"
"                  output[_out++] = from_source[from++];\n"
"                }\n"
"              }\n"
"            }\n"
"            else {\n"
"              from = _out - dist;          /* copy direct from output */\n"
"              do {                        /* minimum length is three */\n"
"                output[_out++] = output[from++];\n"
"                output[_out++] = output[from++];\n"
"                output[_out++] = output[from++];\n"
"                len -= 3;\n"
"              } while (len > 2);\n"
"              if (len) {\n"
"                output[_out++] = output[from++];\n"
"                if (len > 1) {\n"
"                  output[_out++] = output[from++];\n"
"                }\n"
"              }\n"
"            }\n"
"          }\n"
"          else if ((op & 64) === 0) {          /* 2nd level distance code */\n"
"            here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];\n"
"            continue dodist;\n"
"          }\n"
"          else {\n"
"            strm.msg = 'invalid distance code';\n"
"            state.mode = BAD;\n"
"            break top;\n"
"          }\n"
"\n"
"          break; // need to emulate goto via \"continue\"\n"
"        }\n"
"      }\n"
"      else if ((op & 64) === 0) {              /* 2nd level length code */\n"
"        here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];\n"
"        continue dolen;\n"
"      }\n"
"      else if (op & 32) {                     /* end-of-block */\n"
"        //Tracevv((stderr, \"inflate:         end of block\\n\"));\n"
"        state.mode = TYPE;\n"
"        break top;\n"
"      }\n"
"      else {\n"
"        strm.msg = 'invalid literal/length code';\n"
"        state.mode = BAD;\n"
"        break top;\n"
"      }\n"
"\n"
"      break; // need to emulate goto via \"continue\"\n"
"    }\n"
"  } while (_in < last && _out < end);\n"
"\n"
"  /* return unused bytes (on entry, bits < 8, so in won't go too far back) */\n"
"  len = bits >> 3;\n"
"  _in -= len;\n"
"  bits -= len << 3;\n"
"  hold &= (1 << bits) - 1;\n"
"\n"
"  /* update state and return */\n"
"  strm.next_in = _in;\n"
"  strm.next_out = _out;\n"
"  strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last));\n"
"  strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end));\n"
"  state.hold = hold;\n"
"  state.bits = bits;\n"
"  return;\n"
"};\n"
"\n"
"window.inflate_fast = inflate_fast;\n"
;
    if (strcmp(path, "/novnc/vendor/pako/lib/zlib/inffast.js") == 0) return novnc_vendor_pako_lib_zlib_inffast_js;
const char *novnc_vendor_pako_lib_zlib_inflate_js =
"'use strict';\n"
"\n"
"// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n"
"// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n"
"//\n"
"// This software is provided 'as-is', without any express or implied\n"
"// warranty. In no event will the authors be held liable for any damages\n"
"// arising from the use of this software.\n"
"//\n"
"// Permission is granted to anyone to use this software for any purpose,\n"
"// including commercial applications, and to alter it and redistribute it\n"
"// freely, subject to the following restrictions:\n"
"//\n"
"// 1. The origin of this software must not be misrepresented; you must not\n"
"//   claim that you wrote the original software. If you use this software\n"
"//   in a product, an acknowledgment in the product documentation would be\n"
"//   appreciated but is not required.\n"
"// 2. Altered source versions must be plainly marked as such, and must not be\n"
"//   misrepresented as being the original software.\n"
"// 3. This notice may not be removed or altered from any source distribution.\n"
"\n"
"\n"
"const CODES = 0;\n"
"const LENS = 1;\n"
"const DISTS = 2;\n"
"\n"
"/* Public constants ==========================================================*/\n"
"/* ===========================================================================*/\n"
"\n"
"\n"
"/* STATES ====================================================================*/\n"
"/* ===========================================================================*/\n"
"\n"
"\n"
"const    HEAD = 16180;       /* i: waiting for magic header */\n"
"const    FLAGS = 16181;      /* i: waiting for method and flags (gzip) */\n"
"const    TIME = 16182;       /* i: waiting for modification time (gzip) */\n"
"const    OS = 16183;         /* i: waiting for extra flags and operating system (gzip) */\n"
"const    EXLEN = 16184;      /* i: waiting for extra length (gzip) */\n"
"const    EXTRA = 16185;      /* i: waiting for extra bytes (gzip) */\n"
"const    NAME = 16186;       /* i: waiting for end of file name (gzip) */\n"
"const    COMMENT = 16187;    /* i: waiting for end of comment (gzip) */\n"
"const    HCRC = 16188;       /* i: waiting for header crc (gzip) */\n"
"const    DICTID = 16189;    /* i: waiting for dictionary check value */\n"
"const    DICT = 16190;      /* waiting for inflateSetDictionary() call */\n"
"const        TYPE = 16191;      /* i: waiting for type bits, including last-flag bit */\n"
"const        TYPEDO = 16192;    /* i: same, but skip check to exit inflate on new block */\n"
"const        STORED = 16193;    /* i: waiting for stored size (length and complement) */\n"
"const        COPY_ = 16194;     /* i/o: same as COPY below, but only first time in */\n"
"const        COPY = 16195;      /* i/o: waiting for input or output to copy stored block */\n"
"const        TABLE = 16196;     /* i: waiting for dynamic block table lengths */\n"
"const        LENLENS = 16197;   /* i: waiting for code length code lengths */\n"
"const        CODELENS = 16198;  /* i: waiting for length/lit and distance code lengths */\n"
"const            LEN_ = 16199;      /* i: same as LEN below, but only first time in */\n"
"const            LEN = 16200;       /* i: waiting for length/lit/eob code */\n"
"const            LENEXT = 16201;    /* i: waiting for length extra bits */\n"
"const            DIST = 16202;      /* i: waiting for distance code */\n"
"const            DISTEXT = 16203;   /* i: waiting for distance extra bits */\n"
"const            MATCH = 16204;     /* o: waiting for output space to copy string */\n"
"const            LIT = 16205;       /* o: waiting for output space to write literal */\n"
"const    CHECK = 16206;     /* i: waiting for 32-bit check value */\n"
"const    LENGTH = 16207;    /* i: waiting for 32-bit length (gzip) */\n"
"const    DONE = 16208;      /* finished check, done -- remain here until reset */\n"
"const    BAD = 16209;       /* got a data error -- remain here until reset */\n"
"const    MEM = 16210;       /* got an inflate() memory error -- remain here until reset */\n"
"const    SYNC = 16211;      /* looking for synchronization bytes to restart inflate() */\n"
"\n"
"/* ===========================================================================*/\n"
"\n"
"\n"
"\n"
"const ENOUGH_LENS = 852;\n"
"const ENOUGH_DISTS = 592;\n"
"//const ENOUGH =  (ENOUGH_LENS+ENOUGH_DISTS);\n"
"\n"
"/* 32K LZ77 window */\n"
"const DEF_WBITS = MAX_WBITS;\n"
"\n"
"\n"
"const zswap32 = (q) => {\n"
"\n"
"  return  (((q >>> 24) & 0xff) +\n"
"          ((q >>> 8) & 0xff00) +\n"
"          ((q & 0xff00) << 8) +\n"
"          ((q & 0xff) << 24));\n"
"};\n"
"\n"
"\n"
"function InflateState() {\n"
"  this.strm = null;           /* pointer back to this zlib stream */\n"
"  this.mode = 0;              /* current inflate mode */\n"
"  this.last = false;          /* true if processing last block */\n"
"  this.wrap = 0;              /* bit 0 true for zlib, bit 1 true for gzip,\n"
"                                 bit 2 true to validate check value */\n"
"  this.havedict = false;      /* true if dictionary provided */\n"
"  this.flags = 0;             /* gzip header method and flags (0 if zlib), or\n"
"                                 -1 if raw or no header yet */\n"
"  this.dmax = 0;              /* zlib header max distance (INFLATE_STRICT) */\n"
"  this.check = 0;             /* protected copy of check value */\n"
"  this.total = 0;             /* protected copy of output count */\n"
"  // TODO: may be {}\n"
"  this.head = null;           /* where to save gzip header information */\n"
"\n"
"  /* sliding window */\n"
"  this.wbits = 0;             /* log base 2 of requested window size */\n"
"  this.wsize = 0;             /* window size or zero if not using window */\n"
"  this.whave = 0;             /* valid bytes in the window */\n"
"  this.wnext = 0;             /* window write index */\n"
"  this.window = null;         /* allocated sliding window, if needed */\n"
"\n"
"  /* bit accumulator */\n"
"  this.hold = 0;              /* input bit accumulator */\n"
"  this.bits = 0;              /* number of bits in \"in\" */\n"
"\n"
"  /* for string and stored block copying */\n"
"  this.length = 0;            /* literal or length of data to copy */\n"
"  this.offset = 0;            /* distance back to copy string from */\n"
"\n"
"  /* for table and code decoding */\n"
"  this.extra = 0;             /* extra bits needed */\n"
"\n"
"  /* fixed and dynamic code tables */\n"
"  this.lencode = null;          /* starting table for length/literal codes */\n"
"  this.distcode = null;         /* starting table for distance codes */\n"
"  this.lenbits = 0;           /* index bits for lencode */\n"
"  this.distbits = 0;          /* index bits for distcode */\n"
"\n"
"  /* dynamic table building */\n"
"  this.ncode = 0;             /* number of code length code lengths */\n"
"  this.nlen = 0;              /* number of length code lengths */\n"
"  this.ndist = 0;             /* number of distance code lengths */\n"
"  this.have = 0;              /* number of code lengths in lens[] */\n"
"  this.next = null;              /* next available space in codes[] */\n"
"\n"
"  this.lens = new Uint16Array(320); /* temporary storage for code lengths */\n"
"  this.work = new Uint16Array(288); /* work area for code table building */\n"
"\n"
"  /*\n"
"   because we don't have pointers in js, we use lencode and distcode directly\n"
"   as buffers so we don't need codes\n"
"  */\n"
"  //this.codes = new Int32Array(ENOUGH);       /* space for code tables */\n"
"  this.lendyn = null;              /* dynamic table for length/literal codes (JS specific) */\n"
"  this.distdyn = null;             /* dynamic table for distance codes (JS specific) */\n"
"  this.sane = 0;                   /* if false, allow invalid distance too far */\n"
"  this.back = 0;                   /* bits back of last unprocessed length/lit */\n"
"  this.was = 0;                    /* initial length of match */\n"
"}\n"
"\n"
"\n"
"const inflateStateCheck = (strm) => {\n"
"\n"
"  if (!strm) {\n"
"    return 1;\n"
"  }\n"
"  const state = strm.state;\n"
"  if (!state || state.strm !== strm ||\n"
"    state.mode < HEAD || state.mode > SYNC) {\n"
"    return 1;\n"
"  }\n"
"  return 0;\n"
"};\n"
"\n"
"\n"
"const inflateResetKeep = (strm) => {\n"
"\n"
"  if (inflateStateCheck(strm)) { return Z_STREAM_ERROR; }\n"
"  const state = strm.state;\n"
"  strm.total_in = strm.total_out = state.total = 0;\n"
"  strm.msg = ''; /*Z_NULL*/\n"
"  if (state.wrap) {       /* to support ill-conceived Java test suite */\n"
"    strm.adler = state.wrap & 1;\n"
"  }\n"
"  state.mode = HEAD;\n"
"  state.last = 0;\n"
"  state.havedict = 0;\n"
"  state.flags = -1;\n"
"  state.dmax = 32768;\n"
"  state.head = null/*Z_NULL*/;\n"
"  state.hold = 0;\n"
"  state.bits = 0;\n"
"  //state.lencode = state.distcode = state.next = state.codes;\n"
"  state.lencode = state.lendyn = new Int32Array(ENOUGH_LENS);\n"
"  state.distcode = state.distdyn = new Int32Array(ENOUGH_DISTS);\n"
"\n"
"  state.sane = 1;\n"
"  state.back = -1;\n"
"  //Tracev((stderr, \"inflate: reset\\n\"));\n"
"  return Z_OK;\n"
"};\n"
"\n"
"\n"
"const inflateReset = (strm) => {\n"
"\n"
"  if (inflateStateCheck(strm)) { return Z_STREAM_ERROR; }\n"
"  const state = strm.state;\n"
"  state.wsize = 0;\n"
"  state.whave = 0;\n"
"  state.wnext = 0;\n"
"  return inflateResetKeep(strm);\n"
"\n"
"};\n"
"\n"
"\n"
"const inflateReset2 = (strm, windowBits) => {\n"
"  let wrap;\n"
"\n"
"  /* get the state */\n"
"  if (inflateStateCheck(strm)) { return Z_STREAM_ERROR; }\n"
"  const state = strm.state;\n"
"\n"
"  /* extract wrap request from windowBits parameter */\n"
"  if (windowBits < 0) {\n"
"    wrap = 0;\n"
"    windowBits = -windowBits;\n"
"  }\n"
"  else {\n"
"    wrap = (windowBits >> 4) + 5;\n"
"    if (windowBits < 48) {\n"
"      windowBits &= 15;\n"
"    }\n"
"  }\n"
"\n"
"  /* set number of window bits, free window if different */\n"
"  if (windowBits && (windowBits < 8 || windowBits > 15)) {\n"
"    return Z_STREAM_ERROR;\n"
"  }\n"
"  if (state.window !== null && state.wbits !== windowBits) {\n"
"    state.window = null;\n"
"  }\n"
"\n"
"  /* update state and reset the rest of it */\n"
"  state.wrap = wrap;\n"
"  state.wbits = windowBits;\n"
"  return inflateReset(strm);\n"
"};\n"
"\n"
"\n"
"const inflateInit2 = (strm, windowBits) => {\n"
"\n"
"  if (!strm) { return Z_STREAM_ERROR; }\n"
"  //strm.msg = Z_NULL;                 /* in case we return an error */\n"
"\n"
"  const state = new InflateState();\n"
"\n"
"  //if (state === Z_NULL) return Z_MEM_ERROR;\n"
"  //Tracev((stderr, \"inflate: allocated\\n\"));\n"
"  strm.state = state;\n"
"  state.strm = strm;\n"
"  state.window = null/*Z_NULL*/;\n"
"  state.mode = HEAD;     /* to pass state test in inflateReset2() */\n"
"  const ret = inflateReset2(strm, windowBits);\n"
"  if (ret !== Z_OK) {\n"
"    strm.state = null/*Z_NULL*/;\n"
"  }\n"
"  return ret;\n"
"};\n"
"\n"
"\n"
"const inflateInit = (strm) => {\n"
"\n"
"  return inflateInit2(strm, DEF_WBITS);\n"
"};\n"
"\n"
"\n"
"/*\n"
" Return state with length and distance decoding tables and index sizes set to\n"
" fixed code decoding.  Normally this returns fixed tables from inffixed.h.\n"
" If BUILDFIXED is defined, then instead this routine builds the tables the\n"
" first time it's called, and returns those tables the first time and\n"
" thereafter.  This reduces the size of the code by about 2K bytes, in\n"
" exchange for a little execution time.  However, BUILDFIXED should not be\n"
" used for threaded applications, since the rewriting of the tables and virgin\n"
" may not be thread-safe.\n"
" */\n"
"let virgin = true;\n"
"\n"
"let lenfix, distfix; // We have no pointers in JS, so keep tables separate\n"
"\n"
"\n"
"const fixedtables = (state) => {\n"
"\n"
"  /* build fixed huffman tables if first call (may not be thread safe) */\n"
"  if (virgin) {\n"
"    lenfix = new Int32Array(512);\n"
"    distfix = new Int32Array(32);\n"
"\n"
"    /* literal/length table */\n"
"    let sym = 0;\n"
"    while (sym < 144) { state.lens[sym++] = 8; }\n"
"    while (sym < 256) { state.lens[sym++] = 9; }\n"
"    while (sym < 280) { state.lens[sym++] = 7; }\n"
"    while (sym < 288) { state.lens[sym++] = 8; }\n"
"\n"
"    inflate_table(LENS,  state.lens, 0, 288, lenfix,   0, state.work, { bits: 9 });\n"
"\n"
"    /* distance table */\n"
"    sym = 0;\n"
"    while (sym < 32) { state.lens[sym++] = 5; }\n"
"\n"
"    inflate_table(DISTS, state.lens, 0, 32,   distfix, 0, state.work, { bits: 5 });\n"
"\n"
"    /* do this just once */\n"
"    virgin = false;\n"
"  }\n"
"\n"
"  state.lencode = lenfix;\n"
"  state.lenbits = 9;\n"
"  state.distcode = distfix;\n"
"  state.distbits = 5;\n"
"};\n"
"\n"
"\n"
"/*\n"
" Update the window with the last wsize (normally 32K) bytes written before\n"
" returning.  If window does not exist yet, create it.  This is only called\n"
" when a window is already in use, or when output has been written during this\n"
" inflate call, but the end of the deflate stream has not been reached yet.\n"
" It is also called to create a window for dictionary data when a dictionary\n"
" is loaded.\n"
"\n"
" Providing output buffers larger than 32K to inflate() should provide a speed\n"
" advantage, since only the last 32K of output is copied to the sliding window\n"
" upon return from inflate(), and since all distances after the first 32K of\n"
" output will fall in the output data, making match copies simpler and faster.\n"
" The advantage may be dependent on the size of the processor's data caches.\n"
" */\n"
"const updatewindow = (strm, src, end, copy) => {\n"
"\n"
"  let dist;\n"
"  const state = strm.state;\n"
"\n"
"  /* if it hasn't been done already, allocate space for the window */\n"
"  if (state.window === null) {\n"
"    state.wsize = 1 << state.wbits;\n"
"    state.wnext = 0;\n"
"    state.whave = 0;\n"
"\n"
"    state.window = new Uint8Array(state.wsize);\n"
"  }\n"
"\n"
"  /* copy state->wsize or less output bytes into the circular window */\n"
"  if (copy >= state.wsize) {\n"
"    state.window.set(src.subarray(end - state.wsize, end), 0);\n"
"    state.wnext = 0;\n"
"    state.whave = state.wsize;\n"
"  }\n"
"  else {\n"
"    dist = state.wsize - state.wnext;\n"
"    if (dist > copy) {\n"
"      dist = copy;\n"
"    }\n"
"    //zmemcpy(state->window + state->wnext, end - copy, dist);\n"
"    state.window.set(src.subarray(end - copy, end - copy + dist), state.wnext);\n"
"    copy -= dist;\n"
"    if (copy) {\n"
"      //zmemcpy(state->window, end - copy, copy);\n"
"      state.window.set(src.subarray(end - copy, end), 0);\n"
"      state.wnext = copy;\n"
"      state.whave = state.wsize;\n"
"    }\n"
"    else {\n"
"      state.wnext += dist;\n"
"      if (state.wnext === state.wsize) { state.wnext = 0; }\n"
"      if (state.whave < state.wsize) { state.whave += dist; }\n"
"    }\n"
"  }\n"
"  return 0;\n"
"};\n"
"\n"
"\n"
"const inflate = (strm, flush) => {\n"
"\n"
"  let state;\n"
"  let input, output;          // input/output buffers\n"
"  let next;                   /* next input INDEX */\n"
"  let put;                    /* next output INDEX */\n"
"  let have, left;             /* available input and output */\n"
"  let hold;                   /* bit buffer */\n"
"  let bits;                   /* bits in bit buffer */\n"
"  let _in, _out;              /* save starting available input and output */\n"
"  let copy;                   /* number of stored or match bytes to copy */\n"
"  let from;                   /* where to copy match bytes from */\n"
"  let from_source;\n"
"  let here = 0;               /* current decoding table entry */\n"
"  let here_bits, here_op, here_val; // paked \"here\" denormalized (JS specific)\n"
"  //let last;                   /* parent table entry */\n"
"  let last_bits, last_op, last_val; // paked \"last\" denormalized (JS specific)\n"
"  let len;                    /* length to copy for repeats, bits to drop */\n"
"  let ret;                    /* return code */\n"
"  const hbuf = new Uint8Array(4);    /* buffer for gzip header crc calculation */\n"
"  let opts;\n"
"\n"
"  let n; // temporary variable for NEED_BITS\n"
"\n"
"  const order = /* permutation of code lengths */\n"
"    new Uint8Array([ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]);\n"
"\n"
"\n"
"  if (inflateStateCheck(strm) || !strm.output ||\n"
"      (!strm.input && strm.avail_in !== 0)) {\n"
"    return Z_STREAM_ERROR;\n"
"  }\n"
"\n"
"  state = strm.state;\n"
"  if (state.mode === TYPE) { state.mode = TYPEDO; }    /* skip check */\n"
"\n"
"\n"
"  //--- LOAD() ---\n"
"  put = strm.next_out;\n"
"  output = strm.output;\n"
"  left = strm.avail_out;\n"
"  next = strm.next_in;\n"
"  input = strm.input;\n"
"  have = strm.avail_in;\n"
"  hold = state.hold;\n"
"  bits = state.bits;\n"
"  //---\n"
"\n"
"  _in = have;\n"
"  _out = left;\n"
"  ret = Z_OK;\n"
"\n"
"  inf_leave: // goto emulation\n"
"  for (;;) {\n"
"    switch (state.mode) {\n"
"      case HEAD:\n"
"        if (state.wrap === 0) {\n"
"          state.mode = TYPEDO;\n"
"          break;\n"
"        }\n"
"        //=== NEEDBITS(16);\n"
"        while (bits < 16) {\n"
"          if (have === 0) { break inf_leave; }\n"
"          have--;\n"
"          hold += input[next++] << bits;\n"
"          bits += 8;\n"
"        }\n"
"        //===//\n"
"        if ((state.wrap & 2) && hold === 0x8b1f) {  /* gzip header */\n"
"          if (state.wbits === 0) {\n"
"            state.wbits = 15;\n"
"          }\n"
"          state.check = 0/*crc32(0L, Z_NULL, 0)*/;\n"
"          //=== CRC2(state.check, hold);\n"
"          hbuf[0] = hold & 0xff;\n"
"          hbuf[1] = (hold >>> 8) & 0xff;\n"
"          state.check = crc32(state.check, hbuf, 2, 0);\n"
"          //===//\n"
"\n"
"          //=== INITBITS();\n"
"          hold = 0;\n"
"          bits = 0;\n"
"          //===//\n"
"          state.mode = FLAGS;\n"
"          break;\n"
"        }\n"
"        if (state.head) {\n"
"          state.head.done = false;\n"
"        }\n"
"        if (!(state.wrap & 1) ||   /* check if zlib header allowed */\n"
"          (((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) {\n"
"          strm.msg = 'incorrect header check';\n"
"          state.mode = BAD;\n"
"          break;\n"
"        }\n"
"        if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) {\n"
"          strm.msg = 'unknown compression method';\n"
"          state.mode = BAD;\n"
"          break;\n"
"        }\n"
"        //--- DROPBITS(4) ---//\n"
"        hold >>>= 4;\n"
"        bits -= 4;\n"
"        //---//\n"
"        len = (hold & 0x0f)/*BITS(4)*/ + 8;\n"
"        if (state.wbits === 0) {\n"
"          state.wbits = len;\n"
"        }\n"
"        if (len > 15 || len > state.wbits) {\n"
"          strm.msg = 'invalid window size';\n"
"          state.mode = BAD;\n"
"          break;\n"
"        }\n"
"\n"
"        // !!! pako patch. Force use `options.windowBits` if passed.\n"
"        // Required to always use max window size by default.\n"
"        state.dmax = 1 << state.wbits;\n"
"        //state.dmax = 1 << len;\n"
"\n"
"        state.flags = 0;               /* indicate zlib header */\n"
"        //Tracev((stderr, \"inflate:   zlib header ok\\n\"));\n"
"        strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;\n"
"        state.mode = hold & 0x200 ? DICTID : TYPE;\n"
"        //=== INITBITS();\n"
"        hold = 0;\n"
"        bits = 0;\n"
"        //===//\n"
"        break;\n"
"      case FLAGS:\n"
"        //=== NEEDBITS(16); */\n"
"        while (bits < 16) {\n"
"          if (have === 0) { break inf_leave; }\n"
"          have--;\n"
"          hold += input[next++] << bits;\n"
"          bits += 8;\n"
"        }\n"
"        //===//\n"
"        state.flags = hold;\n"
"        if ((state.flags & 0xff) !== Z_DEFLATED) {\n"
"          strm.msg = 'unknown compression method';\n"
"          state.mode = BAD;\n"
"          break;\n"
"        }\n"
"        if (state.flags & 0xe000) {\n"
"          strm.msg = 'unknown header flags set';\n"
"          state.mode = BAD;\n"
"          break;\n"
"        }\n"
"        if (state.head) {\n"
"          state.head.text = ((hold >> 8) & 1);\n"
"        }\n"
"        if ((state.flags & 0x0200) && (state.wrap & 4)) {\n"
"          //=== CRC2(state.check, hold);\n"
"          hbuf[0] = hold & 0xff;\n"
"          hbuf[1] = (hold >>> 8) & 0xff;\n"
"          state.check = crc32(state.check, hbuf, 2, 0);\n"
"          //===//\n"
"        }\n"
"        //=== INITBITS();\n"
"        hold = 0;\n"
"        bits = 0;\n"
"        //===//\n"
"        state.mode = TIME;\n"
"        /* falls through */\n"
"      case TIME:\n"
"        //=== NEEDBITS(32); */\n"
"        while (bits < 32) {\n"
"          if (have === 0) { break inf_leave; }\n"
"          have--;\n"
"          hold += input[next++] << bits;\n"
"          bits += 8;\n"
"        }\n"
"        //===//\n"
"        if (state.head) {\n"
"          state.head.time = hold;\n"
"        }\n"
"        if ((state.flags & 0x0200) && (state.wrap & 4)) {\n"
"          //=== CRC4(state.check, hold)\n"
"          hbuf[0] = hold & 0xff;\n"
"          hbuf[1] = (hold >>> 8) & 0xff;\n"
"          hbuf[2] = (hold >>> 16) & 0xff;\n"
"          hbuf[3] = (hold >>> 24) & 0xff;\n"
"          state.check = crc32(state.check, hbuf, 4, 0);\n"
"          //===\n"
"        }\n"
"        //=== INITBITS();\n"
"        hold = 0;\n"
"        bits = 0;\n"
"        //===//\n"
"        state.mode = OS;\n"
"        /* falls through */\n"
"      case OS:\n"
"        //=== NEEDBITS(16); */\n"
"        while (bits < 16) {\n"
"          if (have === 0) { break inf_leave; }\n"
"          have--;\n"
"          hold += input[next++] << bits;\n"
"          bits += 8;\n"
"        }\n"
"        //===//\n"
"        if (state.head) {\n"
"          state.head.xflags = (hold & 0xff);\n"
"          state.head.os = (hold >> 8);\n"
"        }\n"
"        if ((state.flags & 0x0200) && (state.wrap & 4)) {\n"
"          //=== CRC2(state.check, hold);\n"
"          hbuf[0] = hold & 0xff;\n"
"          hbuf[1] = (hold >>> 8) & 0xff;\n"
"          state.check = crc32(state.check, hbuf, 2, 0);\n"
"          //===//\n"
"        }\n"
"        //=== INITBITS();\n"
"        hold = 0;\n"
"        bits = 0;\n"
"        //===//\n"
"        state.mode = EXLEN;\n"
"        /* falls through */\n"
"      case EXLEN:\n"
"        if (state.flags & 0x0400) {\n"
"          //=== NEEDBITS(16); */\n"
"          while (bits < 16) {\n"
"            if (have === 0) { break inf_leave; }\n"
"            have--;\n"
"            hold += input[next++] << bits;\n"
"            bits += 8;\n"
"          }\n"
"          //===//\n"
"          state.length = hold;\n"
"          if (state.head) {\n"
"            state.head.extra_len = hold;\n"
"          }\n"
"          if ((state.flags & 0x0200) && (state.wrap & 4)) {\n"
"            //=== CRC2(state.check, hold);\n"
"            hbuf[0] = hold & 0xff;\n"
"            hbuf[1] = (hold >>> 8) & 0xff;\n"
"            state.check = crc32(state.check, hbuf, 2, 0);\n"
"            //===//\n"
"          }\n"
"          //=== INITBITS();\n"
"          hold = 0;\n"
"          bits = 0;\n"
"          //===//\n"
"        }\n"
"        else if (state.head) {\n"
"          state.head.extra = null/*Z_NULL*/;\n"
"        }\n"
"        state.mode = EXTRA;\n"
"        /* falls through */\n"
"      case EXTRA:\n"
"        if (state.flags & 0x0400) {\n"
"          copy = state.length;\n"
"          if (copy > have) { copy = have; }\n"
"          if (copy) {\n"
"            if (state.head) {\n"
"              len = state.head.extra_len - state.length;\n"
"              if (!state.head.extra) {\n"
"                // Use untyped array for more convenient processing later\n"
"                state.head.extra = new Uint8Array(state.head.extra_len);\n"
"              }\n"
"              state.head.extra.set(\n"
"                input.subarray(\n"
"                  next,\n"
"                  // extra field is limited to 65536 bytes\n"
"                  // - no need for additional size check\n"
"                  next + copy\n"
"                ),\n"
"                /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/\n"
"                len\n"
"              );\n"
"              //zmemcpy(state.head.extra + len, next,\n"
"              //        len + copy > state.head.extra_max ?\n"
"              //        state.head.extra_max - len : copy);\n"
"            }\n"
"            if ((state.flags & 0x0200) && (state.wrap & 4)) {\n"
"              state.check = crc32(state.check, input, copy, next);\n"
"            }\n"
"            have -= copy;\n"
"            next += copy;\n"
"            state.length -= copy;\n"
"          }\n"
"          if (state.length) { break inf_leave; }\n"
"        }\n"
"        state.length = 0;\n"
"        state.mode = NAME;\n"
"        /* falls through */\n"
"      case NAME:\n"
"        if (state.flags & 0x0800) {\n"
"          if (have === 0) { break inf_leave; }\n"
"          copy = 0;\n"
"          do {\n"
"            // TODO: 2 or 1 bytes?\n"
"            len = input[next + copy++];\n"
"            /* use constant limit because in js we should not preallocate memory */\n"
"            if (state.head && len &&\n"
"                (state.length < 65536 /*state.head.name_max*/)) {\n"
"              state.head.name += String.fromCharCode(len);\n"
"            }\n"
"          } while (len && copy < have);\n"
"\n"
"          if ((state.flags & 0x0200) && (state.wrap & 4)) {\n"
"            state.check = crc32(state.check, input, copy, next);\n"
"          }\n"
"          have -= copy;\n"
"          next += copy;\n"
"          if (len) { break inf_leave; }\n"
"        }\n"
"        else if (state.head) {\n"
"          state.head.name = null;\n"
"        }\n"
"        state.length = 0;\n"
"        state.mode = COMMENT;\n"
"        /* falls through */\n"
"      case COMMENT:\n"
"        if (state.flags & 0x1000) {\n"
"          if (have === 0) { break inf_leave; }\n"
"          copy = 0;\n"
"          do {\n"
"            len = input[next + copy++];\n"
"            /* use constant limit because in js we should not preallocate memory */\n"
"            if (state.head && len &&\n"
"                (state.length < 65536 /*state.head.comm_max*/)) {\n"
"              state.head.comment += String.fromCharCode(len);\n"
"            }\n"
"          } while (len && copy < have);\n"
"          if ((state.flags & 0x0200) && (state.wrap & 4)) {\n"
"            state.check = crc32(state.check, input, copy, next);\n"
"          }\n"
"          have -= copy;\n"
"          next += copy;\n"
"          if (len) { break inf_leave; }\n"
"        }\n"
"        else if (state.head) {\n"
"          state.head.comment = null;\n"
"        }\n"
"        state.mode = HCRC;\n"
"        /* falls through */\n"
"      case HCRC:\n"
"        if (state.flags & 0x0200) {\n"
"          //=== NEEDBITS(16); */\n"
"          while (bits < 16) {\n"
"            if (have === 0) { break inf_leave; }\n"
"            have--;\n"
"            hold += input[next++] << bits;\n"
"            bits += 8;\n"
"          }\n"
"          //===//\n"
"          if ((state.wrap & 4) && hold !== (state.check & 0xffff)) {\n"
"            strm.msg = 'header crc mismatch';\n"
"            state.mode = BAD;\n"
"            break;\n"
"          }\n"
"          //=== INITBITS();\n"
"          hold = 0;\n"
"          bits = 0;\n"
"          //===//\n"
"        }\n"
"        if (state.head) {\n"
"          state.head.hcrc = ((state.flags >> 9) & 1);\n"
"          state.head.done = true;\n"
"        }\n"
"        strm.adler = state.check = 0;\n"
"        state.mode = TYPE;\n"
"        break;\n"
"      case DICTID:\n"
"        //=== NEEDBITS(32); */\n"
"        while (bits < 32) {\n"
"          if (have === 0) { break inf_leave; }\n"
"          have--;\n"
"          hold += input[next++] << bits;\n"
"          bits += 8;\n"
"        }\n"
"        //===//\n"
"        strm.adler = state.check = zswap32(hold);\n"
"        //=== INITBITS();\n"
"        hold = 0;\n"
"        bits = 0;\n"
"        //===//\n"
"        state.mode = DICT;\n"
"        /* falls through */\n"
"      case DICT:\n"
"        if (state.havedict === 0) {\n"
"          //--- RESTORE() ---\n"
"          strm.next_out = put;\n"
"          strm.avail_out = left;\n"
"          strm.next_in = next;\n"
"          strm.avail_in = have;\n"
"          state.hold = hold;\n"
"          state.bits = bits;\n"
"          //---\n"
"          return Z_NEED_DICT;\n"
"        }\n"
"        strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;\n"
"        state.mode = TYPE;\n"
"        /* falls through */\n"
"      case TYPE:\n"
"        if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; }\n"
"        /* falls through */\n"
"      case TYPEDO:\n"
"        if (state.last) {\n"
"          //--- BYTEBITS() ---//\n"
"          hold >>>= bits & 7;\n"
"          bits -= bits & 7;\n"
"          //---//\n"
"          state.mode = CHECK;\n"
"          break;\n"
"        }\n"
"        //=== NEEDBITS(3); */\n"
"        while (bits < 3) {\n"
"          if (have === 0) { break inf_leave; }\n"
"          have--;\n"
"          hold += input[next++] << bits;\n"
"          bits += 8;\n"
"        }\n"
"        //===//\n"
"        state.last = (hold & 0x01)/*BITS(1)*/;\n"
"        //--- DROPBITS(1) ---//\n"
"        hold >>>= 1;\n"
"        bits -= 1;\n"
"        //---//\n"
"\n"
"        switch ((hold & 0x03)/*BITS(2)*/) {\n"
"          case 0:                             /* stored block */\n"
"            //Tracev((stderr, \"inflate:     stored block%s\\n\",\n"
"            //        state.last ? \" (last)\" : \"\"));\n"
"            state.mode = STORED;\n"
"            break;\n"
"          case 1:                             /* fixed block */\n"
"            fixedtables(state);\n"
"            //Tracev((stderr, \"inflate:     fixed codes block%s\\n\",\n"
"            //        state.last ? \" (last)\" : \"\"));\n"
"            state.mode = LEN_;             /* decode codes */\n"
"            if (flush === Z_TREES) {\n"
"              //--- DROPBITS(2) ---//\n"
"              hold >>>= 2;\n"
"              bits -= 2;\n"
"              //---//\n"
"              break inf_leave;\n"
"            }\n"
"            break;\n"
"          case 2:                             /* dynamic block */\n"
"            //Tracev((stderr, \"inflate:     dynamic codes block%s\\n\",\n"
"            //        state.last ? \" (last)\" : \"\"));\n"
"            state.mode = TABLE;\n"
"            break;\n"
"          case 3:\n"
"            strm.msg = 'invalid block type';\n"
"            state.mode = BAD;\n"
"        }\n"
"        //--- DROPBITS(2) ---//\n"
"        hold >>>= 2;\n"
"        bits -= 2;\n"
"        //---//\n"
"        break;\n"
"      case STORED:\n"
"        //--- BYTEBITS() ---// /* go to byte boundary */\n"
"        hold >>>= bits & 7;\n"
"        bits -= bits & 7;\n"
"        //---//\n"
"        //=== NEEDBITS(32); */\n"
"        while (bits < 32) {\n"
"          if (have === 0) { break inf_leave; }\n"
"          have--;\n"
"          hold += input[next++] << bits;\n"
"          bits += 8;\n"
"        }\n"
"        //===//\n"
"        if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) {\n"
"          strm.msg = 'invalid stored block lengths';\n"
"          state.mode = BAD;\n"
"          break;\n"
"        }\n"
"        state.length = hold & 0xffff;\n"
"        //Tracev((stderr, \"inflate:       stored length %u\\n\",\n"
"        //        state.length));\n"
"        //=== INITBITS();\n"
"        hold = 0;\n"
"        bits = 0;\n"
"        //===//\n"
"        state.mode = COPY_;\n"
"        if (flush === Z_TREES) { break inf_leave; }\n"
"        /* falls through */\n"
"      case COPY_:\n"
"        state.mode = COPY;\n"
"        /* falls through */\n"
"      case COPY:\n"
"        copy = state.length;\n"
"        if (copy) {\n"
"          if (copy > have) { copy = have; }\n"
"          if (copy > left) { copy = left; }\n"
"          if (copy === 0) { break inf_leave; }\n"
"          //--- zmemcpy(put, next, copy); ---\n"
"          output.set(input.subarray(next, next + copy), put);\n"
"          //---//\n"
"          have -= copy;\n"
"          next += copy;\n"
"          left -= copy;\n"
"          put += copy;\n"
"          state.length -= copy;\n"
"          break;\n"
"        }\n"
"        //Tracev((stderr, \"inflate:       stored end\\n\"));\n"
"        state.mode = TYPE;\n"
"        break;\n"
"      case TABLE:\n"
"        //=== NEEDBITS(14); */\n"
"        while (bits < 14) {\n"
"          if (have === 0) { break inf_leave; }\n"
"          have--;\n"
"          hold += input[next++] << bits;\n"
"          bits += 8;\n"
"        }\n"
"        //===//\n"
"        state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257;\n"
"        //--- DROPBITS(5) ---//\n"
"        hold >>>= 5;\n"
"        bits -= 5;\n"
"        //---//\n"
"        state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1;\n"
"        //--- DROPBITS(5) ---//\n"
"        hold >>>= 5;\n"
"        bits -= 5;\n"
"        //---//\n"
"        state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4;\n"
"        //--- DROPBITS(4) ---//\n"
"        hold >>>= 4;\n"
"        bits -= 4;\n"
"        //---//\n"
"//#ifndef PKZIP_BUG_WORKAROUND\n"
"        if (state.nlen > 286 || state.ndist > 30) {\n"
"          strm.msg = 'too many length or distance symbols';\n"
"          state.mode = BAD;\n"
"          break;\n"
"        }\n"
"//#endif\n"
"        //Tracev((stderr, \"inflate:       table sizes ok\\n\"));\n"
"        state.have = 0;\n"
"        state.mode = LENLENS;\n"
"        /* falls through */\n"
"      case LENLENS:\n"
"        while (state.have < state.ncode) {\n"
"          //=== NEEDBITS(3);\n"
"          while (bits < 3) {\n"
"            if (have === 0) { break inf_leave; }\n"
"            have--;\n"
"            hold += input[next++] << bits;\n"
"            bits += 8;\n"
"          }\n"
"          //===//\n"
"          state.lens[order[state.have++]] = (hold & 0x07);//BITS(3);\n"
"          //--- DROPBITS(3) ---//\n"
"          hold >>>= 3;\n"
"          bits -= 3;\n"
"          //---//\n"
"        }\n"
"        while (state.have < 19) {\n"
"          state.lens[order[state.have++]] = 0;\n"
"        }\n"
"        // We have separate tables & no pointers. 2 commented lines below not needed.\n"
"        //state.next = state.codes;\n"
"        //state.lencode = state.next;\n"
"        // Switch to use dynamic table\n"
"        state.lencode = state.lendyn;\n"
"        state.lenbits = 7;\n"
"\n"
"        opts = { bits: state.lenbits };\n"
"        ret = inflate_table(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts);\n"
"        state.lenbits = opts.bits;\n"
"\n"
"        if (ret) {\n"
"          strm.msg = 'invalid code lengths set';\n"
"          state.mode = BAD;\n"
"          break;\n"
"        }\n"
"        //Tracev((stderr, \"inflate:       code lengths ok\\n\"));\n"
"        state.have = 0;\n"
"        state.mode = CODELENS;\n"
"        /* falls through */\n"
"      case CODELENS:\n"
"        while (state.have < state.nlen + state.ndist) {\n"
"          for (;;) {\n"
"            here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/\n"
"            here_bits = here >>> 24;\n"
"            here_op = (here >>> 16) & 0xff;\n"
"            here_val = here & 0xffff;\n"
"\n"
"            if ((here_bits) <= bits) { break; }\n"
"            //--- PULLBYTE() ---//\n"
"            if (have === 0) { break inf_leave; }\n"
"            have--;\n"
"            hold += input[next++] << bits;\n"
"            bits += 8;\n"
"            //---//\n"
"          }\n"
"          if (here_val < 16) {\n"
"            //--- DROPBITS(here.bits) ---//\n"
"            hold >>>= here_bits;\n"
"            bits -= here_bits;\n"
"            //---//\n"
"            state.lens[state.have++] = here_val;\n"
"          }\n"
"          else {\n"
"            if (here_val === 16) {\n"
"              //=== NEEDBITS(here.bits + 2);\n"
"              n = here_bits + 2;\n"
"              while (bits < n) {\n"
"                if (have === 0) { break inf_leave; }\n"
"                have--;\n"
"                hold += input[next++] << bits;\n"
"                bits += 8;\n"
"              }\n"
"              //===//\n"
"              //--- DROPBITS(here.bits) ---//\n"
"              hold >>>= here_bits;\n"
"              bits -= here_bits;\n"
"              //---//\n"
"              if (state.have === 0) {\n"
"                strm.msg = 'invalid bit length repeat';\n"
"                state.mode = BAD;\n"
"                break;\n"
"              }\n"
"              len = state.lens[state.have - 1];\n"
"              copy = 3 + (hold & 0x03);//BITS(2);\n"
"              //--- DROPBITS(2) ---//\n"
"              hold >>>= 2;\n"
"              bits -= 2;\n"
"              //---//\n"
"            }\n"
"            else if (here_val === 17) {\n"
"              //=== NEEDBITS(here.bits + 3);\n"
"              n = here_bits + 3;\n"
"              while (bits < n) {\n"
"                if (have === 0) { break inf_leave; }\n"
"                have--;\n"
"                hold += input[next++] << bits;\n"
"                bits += 8;\n"
"              }\n"
"              //===//\n"
"              //--- DROPBITS(here.bits) ---//\n"
"              hold >>>= here_bits;\n"
"              bits -= here_bits;\n"
"              //---//\n"
"              len = 0;\n"
"              copy = 3 + (hold & 0x07);//BITS(3);\n"
"              //--- DROPBITS(3) ---//\n"
"              hold >>>= 3;\n"
"              bits -= 3;\n"
"              //---//\n"
"            }\n"
"            else {\n"
"              //=== NEEDBITS(here.bits + 7);\n"
"              n = here_bits + 7;\n"
"              while (bits < n) {\n"
"                if (have === 0) { break inf_leave; }\n"
"                have--;\n"
"                hold += input[next++] << bits;\n"
"                bits += 8;\n"
"              }\n"
"              //===//\n"
"              //--- DROPBITS(here.bits) ---//\n"
"              hold >>>= here_bits;\n"
"              bits -= here_bits;\n"
"              //---//\n"
"              len = 0;\n"
"              copy = 11 + (hold & 0x7f);//BITS(7);\n"
"              //--- DROPBITS(7) ---//\n"
"              hold >>>= 7;\n"
"              bits -= 7;\n"
"              //---//\n"
"            }\n"
"            if (state.have + copy > state.nlen + state.ndist) {\n"
"              strm.msg = 'invalid bit length repeat';\n"
"              state.mode = BAD;\n"
"              break;\n"
"            }\n"
"            while (copy--) {\n"
"              state.lens[state.have++] = len;\n"
"            }\n"
"          }\n"
"        }\n"
"\n"
"        /* handle error breaks in while */\n"
"        if (state.mode === BAD) { break; }\n"
"\n"
"        /* check for end-of-block code (better have one) */\n"
"        if (state.lens[256] === 0) {\n"
"          strm.msg = 'invalid code -- missing end-of-block';\n"
"          state.mode = BAD;\n"
"          break;\n"
"        }\n"
"\n"
"        /* build code tables -- note: do not change the lenbits or distbits\n"
"           values here (9 and 6) without reading the comments in inftrees.h\n"
"           concerning the ENOUGH constants, which depend on those values */\n"
"        state.lenbits = 9;\n"
"\n"
"        opts = { bits: state.lenbits };\n"
"        ret = inflate_table(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts);\n"
"        // We have separate tables & no pointers. 2 commented lines below not needed.\n"
"        // state.next_index = opts.table_index;\n"
"        state.lenbits = opts.bits;\n"
"        // state.lencode = state.next;\n"
"\n"
"        if (ret) {\n"
"          strm.msg = 'invalid literal/lengths set';\n"
"          state.mode = BAD;\n"
"          break;\n"
"        }\n"
"\n"
"        state.distbits = 6;\n"
"        //state.distcode.copy(state.codes);\n"
"        // Switch to use dynamic table\n"
"        state.distcode = state.distdyn;\n"
"        opts = { bits: state.distbits };\n"
"        ret = inflate_table(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts);\n"
"        // We have separate tables & no pointers. 2 commented lines below not needed.\n"
"        // state.next_index = opts.table_index;\n"
"        state.distbits = opts.bits;\n"
"        // state.distcode = state.next;\n"
"\n"
"        if (ret) {\n"
"          strm.msg = 'invalid distances set';\n"
"          state.mode = BAD;\n"
"          break;\n"
"        }\n"
"        //Tracev((stderr, 'inflate:       codes ok\\n'));\n"
"        state.mode = LEN_;\n"
"        if (flush === Z_TREES) { break inf_leave; }\n"
"        /* falls through */\n"
"      case LEN_:\n"
"        state.mode = LEN;\n"
"        /* falls through */\n"
"      case LEN:\n"
"        if (have >= 6 && left >= 258) {\n"
"          //--- RESTORE() ---\n"
"          strm.next_out = put;\n"
"          strm.avail_out = left;\n"
"          strm.next_in = next;\n"
"          strm.avail_in = have;\n"
"          state.hold = hold;\n"
"          state.bits = bits;\n"
"          //---\n"
"          inflate_fast(strm, _out);\n"
"          //--- LOAD() ---\n"
"          put = strm.next_out;\n"
"          output = strm.output;\n"
"          left = strm.avail_out;\n"
"          next = strm.next_in;\n"
"          input = strm.input;\n"
"          have = strm.avail_in;\n"
"          hold = state.hold;\n"
"          bits = state.bits;\n"
"          //---\n"
"\n"
"          if (state.mode === TYPE) {\n"
"            state.back = -1;\n"
"          }\n"
"          break;\n"
"        }\n"
"        state.back = 0;\n"
"        for (;;) {\n"
"          here = state.lencode[hold & ((1 << state.lenbits) - 1)];  /*BITS(state.lenbits)*/\n"
"          here_bits = here >>> 24;\n"
"          here_op = (here >>> 16) & 0xff;\n"
"          here_val = here & 0xffff;\n"
"\n"
"          if (here_bits <= bits) { break; }\n"
"          //--- PULLBYTE() ---//\n"
"          if (have === 0) { break inf_leave; }\n"
"          have--;\n"
"          hold += input[next++] << bits;\n"
"          bits += 8;\n"
"          //---//\n"
"        }\n"
"        if (here_op && (here_op & 0xf0) === 0) {\n"
"          last_bits = here_bits;\n"
"          last_op = here_op;\n"
"          last_val = here_val;\n"
"          for (;;) {\n"
"            here = state.lencode[last_val +\n"
"                    ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)];\n"
"            here_bits = here >>> 24;\n"
"            here_op = (here >>> 16) & 0xff;\n"
"            here_val = here & 0xffff;\n"
"\n"
"            if ((last_bits + here_bits) <= bits) { break; }\n"
"            //--- PULLBYTE() ---//\n"
"            if (have === 0) { break inf_leave; }\n"
"            have--;\n"
"            hold += input[next++] << bits;\n"
"            bits += 8;\n"
"            //---//\n"
"          }\n"
"          //--- DROPBITS(last.bits) ---//\n"
"          hold >>>= last_bits;\n"
"          bits -= last_bits;\n"
"          //---//\n"
"          state.back += last_bits;\n"
"        }\n"
"        //--- DROPBITS(here.bits) ---//\n"
"        hold >>>= here_bits;\n"
"        bits -= here_bits;\n"
"        //---//\n"
"        state.back += here_bits;\n"
"        state.length = here_val;\n"
"        if (here_op === 0) {\n"
"          //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?\n"
"          //        \"inflate:         literal '%c'\\n\" :\n"
"          //        \"inflate:         literal 0x%02x\\n\", here.val));\n"
"          state.mode = LIT;\n"
"          break;\n"
"        }\n"
"        if (here_op & 32) {\n"
"          //Tracevv((stderr, \"inflate:         end of block\\n\"));\n"
"          state.back = -1;\n"
"          state.mode = TYPE;\n"
"          break;\n"
"        }\n"
"        if (here_op & 64) {\n"
"          strm.msg = 'invalid literal/length code';\n"
"          state.mode = BAD;\n"
"          break;\n"
"        }\n"
"        state.extra = here_op & 15;\n"
"        state.mode = LENEXT;\n"
"        /* falls through */\n"
"      case LENEXT:\n"
"        if (state.extra) {\n"
"          //=== NEEDBITS(state.extra);\n"
"          n = state.extra;\n"
"          while (bits < n) {\n"
"            if (have === 0) { break inf_leave; }\n"
"            have--;\n"
"            hold += input[next++] << bits;\n"
"            bits += 8;\n"
"          }\n"
"          //===//\n"
"          state.length += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/;\n"
"          //--- DROPBITS(state.extra) ---//\n"
"          hold >>>= state.extra;\n"
"          bits -= state.extra;\n"
"          //---//\n"
"          state.back += state.extra;\n"
"        }\n"
"        //Tracevv((stderr, \"inflate:         length %u\\n\", state.length));\n"
"        state.was = state.length;\n"
"        state.mode = DIST;\n"
"        /* falls through */\n"
"      case DIST:\n"
"        for (;;) {\n"
"          here = state.distcode[hold & ((1 << state.distbits) - 1)];/*BITS(state.distbits)*/\n"
"          here_bits = here >>> 24;\n"
"          here_op = (here >>> 16) & 0xff;\n"
"          here_val = here & 0xffff;\n"
"\n"
"          if ((here_bits) <= bits) { break; }\n"
"          //--- PULLBYTE() ---//\n"
"          if (have === 0) { break inf_leave; }\n"
"          have--;\n"
"          hold += input[next++] << bits;\n"
"          bits += 8;\n"
"          //---//\n"
"        }\n"
"        if ((here_op & 0xf0) === 0) {\n"
"          last_bits = here_bits;\n"
"          last_op = here_op;\n"
"          last_val = here_val;\n"
"          for (;;) {\n"
"            here = state.distcode[last_val +\n"
"                    ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)];\n"
"            here_bits = here >>> 24;\n"
"            here_op = (here >>> 16) & 0xff;\n"
"            here_val = here & 0xffff;\n"
"\n"
"            if ((last_bits + here_bits) <= bits) { break; }\n"
"            //--- PULLBYTE() ---//\n"
"            if (have === 0) { break inf_leave; }\n"
"            have--;\n"
"            hold += input[next++] << bits;\n"
"            bits += 8;\n"
"            //---//\n"
"          }\n"
"          //--- DROPBITS(last.bits) ---//\n"
"          hold >>>= last_bits;\n"
"          bits -= last_bits;\n"
"          //---//\n"
"          state.back += last_bits;\n"
"        }\n"
"        //--- DROPBITS(here.bits) ---//\n"
"        hold >>>= here_bits;\n"
"        bits -= here_bits;\n"
"        //---//\n"
"        state.back += here_bits;\n"
"        if (here_op & 64) {\n"
"          strm.msg = 'invalid distance code';\n"
"          state.mode = BAD;\n"
"          break;\n"
"        }\n"
"        state.offset = here_val;\n"
"        state.extra = (here_op) & 15;\n"
"        state.mode = DISTEXT;\n"
"        /* falls through */\n"
"      case DISTEXT:\n"
"        if (state.extra) {\n"
"          //=== NEEDBITS(state.extra);\n"
"          n = state.extra;\n"
"          while (bits < n) {\n"
"            if (have === 0) { break inf_leave; }\n"
"            have--;\n"
"            hold += input[next++] << bits;\n"
"            bits += 8;\n"
"          }\n"
"          //===//\n"
"          state.offset += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/;\n"
"          //--- DROPBITS(state.extra) ---//\n"
"          hold >>>= state.extra;\n"
"          bits -= state.extra;\n"
"          //---//\n"
"          state.back += state.extra;\n"
"        }\n"
"//#ifdef INFLATE_STRICT\n"
"        if (state.offset > state.dmax) {\n"
"          strm.msg = 'invalid distance too far back';\n"
"          state.mode = BAD;\n"
"          break;\n"
"        }\n"
"//#endif\n"
"        //Tracevv((stderr, \"inflate:         distance %u\\n\", state.offset));\n"
"        state.mode = MATCH;\n"
"        /* falls through */\n"
"      case MATCH:\n"
"        if (left === 0) { break inf_leave; }\n"
"        copy = _out - left;\n"
"        if (state.offset > copy) {         /* copy from window */\n"
"          copy = state.offset - copy;\n"
"          if (copy > state.whave) {\n"
"            if (state.sane) {\n"
"              strm.msg = 'invalid distance too far back';\n"
"              state.mode = BAD;\n"
"              break;\n"
"            }\n"
"// (!) This block is disabled in zlib defaults,\n"
"// don't enable it for binary compatibility\n"
"//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR\n"
"//          Trace((stderr, \"inflate.c too far\\n\"));\n"
"//          copy -= state.whave;\n"
"//          if (copy > state.length) { copy = state.length; }\n"
"//          if (copy > left) { copy = left; }\n"
"//          left -= copy;\n"
"//          state.length -= copy;\n"
"//          do {\n"
"//            output[put++] = 0;\n"
"//          } while (--copy);\n"
"//          if (state.length === 0) { state.mode = LEN; }\n"
"//          break;\n"
"//#endif\n"
"          }\n"
"          if (copy > state.wnext) {\n"
"            copy -= state.wnext;\n"
"            from = state.wsize - copy;\n"
"          }\n"
"          else {\n"
"            from = state.wnext - copy;\n"
"          }\n"
"          if (copy > state.length) { copy = state.length; }\n"
"          from_source = state.window;\n"
"        }\n"
"        else {                              /* copy from output */\n"
"          from_source = output;\n"
"          from = put - state.offset;\n"
"          copy = state.length;\n"
"        }\n"
"        if (copy > left) { copy = left; }\n"
"        left -= copy;\n"
"        state.length -= copy;\n"
"        do {\n"
"          output[put++] = from_source[from++];\n"
"        } while (--copy);\n"
"        if (state.length === 0) { state.mode = LEN; }\n"
"        break;\n"
"      case LIT:\n"
"        if (left === 0) { break inf_leave; }\n"
"        output[put++] = state.length;\n"
"        left--;\n"
"        state.mode = LEN;\n"
"        break;\n"
"      case CHECK:\n"
"        if (state.wrap) {\n"
"          //=== NEEDBITS(32);\n"
"          while (bits < 32) {\n"
"            if (have === 0) { break inf_leave; }\n"
"            have--;\n"
"            // Use '|' instead of '+' to make sure that result is signed\n"
"            hold |= input[next++] << bits;\n"
"            bits += 8;\n"
"          }\n"
"          //===//\n"
"          _out -= left;\n"
"          strm.total_out += _out;\n"
"          state.total += _out;\n"
"          if ((state.wrap & 4) && _out) {\n"
"            strm.adler = state.check =\n"
"                /*UPDATE_CHECK(state.check, put - _out, _out);*/\n"
"                (state.flags ? crc32(state.check, output, _out, put - _out) : adler32(state.check, output, _out, put - _out));\n"
"\n"
"          }\n"
"          _out = left;\n"
"          // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too\n"
"          if ((state.wrap & 4) && (state.flags ? hold : zswap32(hold)) !== state.check) {\n"
"            strm.msg = 'incorrect data check';\n"
"            state.mode = BAD;\n"
"            break;\n"
"          }\n"
"          //=== INITBITS();\n"
"          hold = 0;\n"
"          bits = 0;\n"
"          //===//\n"
"          //Tracev((stderr, \"inflate:   check matches trailer\\n\"));\n"
"        }\n"
"        state.mode = LENGTH;\n"
"        /* falls through */\n"
"      case LENGTH:\n"
"        if (state.wrap && state.flags) {\n"
"          //=== NEEDBITS(32);\n"
"          while (bits < 32) {\n"
"            if (have === 0) { break inf_leave; }\n"
"            have--;\n"
"            hold += input[next++] << bits;\n"
"            bits += 8;\n"
"          }\n"
"          //===//\n"
"          if ((state.wrap & 4) && hold !== (state.total & 0xffffffff)) {\n"
"            strm.msg = 'incorrect length check';\n"
"            state.mode = BAD;\n"
"            break;\n"
"          }\n"
"          //=== INITBITS();\n"
"          hold = 0;\n"
"          bits = 0;\n"
"          //===//\n"
"          //Tracev((stderr, \"inflate:   length matches trailer\\n\"));\n"
"        }\n"
"        state.mode = DONE;\n"
"        /* falls through */\n"
"      case DONE:\n"
"        ret = Z_STREAM_END;\n"
"        break inf_leave;\n"
"      case BAD:\n"
"        ret = Z_DATA_ERROR;\n"
"        break inf_leave;\n"
"      case MEM:\n"
"        return Z_MEM_ERROR;\n"
"      case SYNC:\n"
"        /* falls through */\n"
"      default:\n"
"        return Z_STREAM_ERROR;\n"
"    }\n"
"  }\n"
"\n"
"  // inf_leave <- here is real place for \"goto inf_leave\", emulated via \"break inf_leave\"\n"
"\n"
"  /*\n"
"     Return from inflate(), updating the total counts and the check value.\n"
"     If there was no progress during the inflate() call, return a buffer\n"
"     error.  Call updatewindow() to create and/or update the window state.\n"
"     Note: a memory error from inflate() is non-recoverable.\n"
"   */\n"
"\n"
"  //--- RESTORE() ---\n"
"  strm.next_out = put;\n"
"  strm.avail_out = left;\n"
"  strm.next_in = next;\n"
"  strm.avail_in = have;\n"
"  state.hold = hold;\n"
"  state.bits = bits;\n"
"  //---\n"
"\n"
"  if (state.wsize || (_out !== strm.avail_out && state.mode < BAD &&\n"
"                      (state.mode < CHECK || flush !== Z_FINISH))) {\n"
"    if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) {\n"
"      state.mode = MEM;\n"
"      return Z_MEM_ERROR;\n"
"    }\n"
"  }\n"
"  _in -= strm.avail_in;\n"
"  _out -= strm.avail_out;\n"
"  strm.total_in += _in;\n"
"  strm.total_out += _out;\n"
"  state.total += _out;\n"
"  if ((state.wrap & 4) && _out) {\n"
"    strm.adler = state.check = /*UPDATE_CHECK(state.check, strm.next_out - _out, _out);*/\n"
"      (state.flags ? crc32(state.check, output, _out, strm.next_out - _out) : adler32(state.check, output, _out, strm.next_out - _out));\n"
"  }\n"
"  strm.data_type = state.bits + (state.last ? 64 : 0) +\n"
"                    (state.mode === TYPE ? 128 : 0) +\n"
"                    (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0);\n"
"  if (((_in === 0 && _out === 0) || flush === Z_FINISH) && ret === Z_OK) {\n"
"    ret = Z_BUF_ERROR;\n"
"  }\n"
"  return ret;\n"
"};\n"
"\n"
"\n"
"const inflateEnd = (strm) => {\n"
"\n"
"  if (inflateStateCheck(strm)) {\n"
"    return Z_STREAM_ERROR;\n"
"  }\n"
"\n"
"  let state = strm.state;\n"
"  if (state.window) {\n"
"    state.window = null;\n"
"  }\n"
"  strm.state = null;\n"
"  return Z_OK;\n"
"};\n"
"\n"
"\n"
"const inflateGetHeader = (strm, head) => {\n"
"\n"
"  /* check state */\n"
"  if (inflateStateCheck(strm)) { return Z_STREAM_ERROR; }\n"
"  const state = strm.state;\n"
"  if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR; }\n"
"\n"
"  /* save header structure */\n"
"  state.head = head;\n"
"  head.done = false;\n"
"  return Z_OK;\n"
"};\n"
"\n"
"\n"
"const inflateSetDictionary = (strm, dictionary) => {\n"
"  const dictLength = dictionary.length;\n"
"\n"
"  let state;\n"
"  let dictid;\n"
"  let ret;\n"
"\n"
"  /* check state */\n"
"  if (inflateStateCheck(strm)) { return Z_STREAM_ERROR; }\n"
"  state = strm.state;\n"
"\n"
"  if (state.wrap !== 0 && state.mode !== DICT) {\n"
"    return Z_STREAM_ERROR;\n"
"  }\n"
"\n"
"  /* check for correct dictionary identifier */\n"
"  if (state.mode === DICT) {\n"
"    dictid = 1; /* adler32(0, null, 0)*/\n"
"    /* dictid = adler32(dictid, dictionary, dictLength); */\n"
"    dictid = adler32(dictid, dictionary, dictLength, 0);\n"
"    if (dictid !== state.check) {\n"
"      return Z_DATA_ERROR;\n"
"    }\n"
"  }\n"
"  /* copy dictionary to window using updatewindow(), which will amend the\n"
"   existing dictionary if appropriate */\n"
"  ret = updatewindow(strm, dictionary, dictLength, dictLength);\n"
"  if (ret) {\n"
"    state.mode = MEM;\n"
"    return Z_MEM_ERROR;\n"
"  }\n"
"  state.havedict = 1;\n"
"  // Tracev((stderr, \"inflate:   dictionary set\\n\"));\n"
"  return Z_OK;\n"
"};\n"
"\n"
"\n"
"window.inflateReset = inflateReset;\n"
"window.inflateReset2 = inflateReset2;\n"
"window.inflateResetKeep = inflateResetKeep;\n"
"window.inflateInit = inflateInit;\n"
"window.inflateInit2 = inflateInit2;\n"
"window.inflate = inflate;\n"
"window.inflateEnd = inflateEnd;\n"
"window.inflateGetHeader = inflateGetHeader;\n"
"window.inflateSetDictionary = inflateSetDictionary;\n"
"const inflateInfo = 'pako inflate (from Nodeca project)';\n"
"\n"
"/* Not implemented\n"
"window. inflateCodesUsed, inflateCopy, inflateGetDictionary, inflateMark, inflatePrime, inflateSync, inflateSyncPoint, inflateUndermine, inflateValidate  = {;\n"
"*/\n"
"window.CODES = CODES;\n"
"window.LENS = LENS;\n"
"window.DISTS = DISTS;\n"
"window.HEAD = HEAD;\n"
"window.FLAGS = FLAGS;\n"
"window.TIME = TIME;\n"
"window.OS = OS;\n"
"window.EXLEN = EXLEN;\n"
"window.EXTRA = EXTRA;\n"
"window.NAME = NAME;\n"
"window.COMMENT = COMMENT;\n"
"window.HCRC = HCRC;\n"
"window.DICTID = DICTID;\n"
"window.TYPE = TYPE;\n"
"window.TYPEDO = TYPEDO;\n"
"window.STORED = STORED;\n"
"window.COPY_ = COPY_;\n"
"window.TABLE = TABLE;\n"
"window.LENLENS = LENLENS;\n"
"window.CODELENS = CODELENS;\n"
"window.LEN_ = LEN_;\n"
"window.LENEXT = LENEXT;\n"
"window.DISTEXT = DISTEXT;\n"
"window.MATCH = MATCH;\n"
"window.LIT = LIT;\n"
"window.CHECK = CHECK;\n"
"window.LENGTH = LENGTH;\n"
"window.DONE = DONE;\n"
"window.BAD = BAD;\n"
"window.MEM = MEM;\n"
"window.SYNC = SYNC;\n"
"window.ENOUGH_LENS = ENOUGH_LENS;\n"
"window.ENOUGH_DISTS = ENOUGH_DISTS;\n"
"window.MAX_WBITS = MAX_WBITS;\n"
"window.DEF_WBITS = DEF_WBITS;\n"
"window.zswap32 = zswap32;\n"
"window.InflateState = InflateState;\n"
"window.inflateStateCheck = inflateStateCheck;\n"
"window.inflateResetKeep = inflateResetKeep;\n"
"window.inflateReset2 = inflateReset2;\n"
"window.inflateInit2 = inflateInit2;\n"
"window.virgin = virgin;\n"
"window.lenfix = lenfix;\n"
"window.fixedtables = fixedtables;\n"
"window.updatewindow = updatewindow;\n"
"window.inflateEnd = inflateEnd;\n"
"window.inflateGetHeader = inflateGetHeader;\n"
"window.inflateSetDictionary = inflateSetDictionary;\n"
"window.inflateInfo = inflateInfo;\n"
;
    if (strcmp(path, "/novnc/vendor/pako/lib/zlib/inflate.js") == 0) return novnc_vendor_pako_lib_zlib_inflate_js;
const char *novnc_vendor_pako_lib_zlib_inftrees_js =
"'use strict';\n"
"\n"
"// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n"
"// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n"
"//\n"
"// This software is provided 'as-is', without any express or implied\n"
"// warranty. In no event will the authors be held liable for any damages\n"
"// arising from the use of this software.\n"
"//\n"
"// Permission is granted to anyone to use this software for any purpose,\n"
"// including commercial applications, and to alter it and redistribute it\n"
"// freely, subject to the following restrictions:\n"
"//\n"
"// 1. The origin of this software must not be misrepresented; you must not\n"
"//   claim that you wrote the original software. If you use this software\n"
"//   in a product, an acknowledgment in the product documentation would be\n"
"//   appreciated but is not required.\n"
"// 2. Altered source versions must be plainly marked as such, and must not be\n"
"//   misrepresented as being the original software.\n"
"// 3. This notice may not be removed or altered from any source distribution.\n"
"\n"
"const MAXBITS = 15;\n"
"//const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);\n"
"\n"
"const lbase = new Uint16Array([ /* Length codes 257..285 base */\n"
"  3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,\n"
"  35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0\n"
"]);\n"
"\n"
"const lext = new Uint8Array([ /* Length codes 257..285 extra */\n"
"  16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,\n"
"  19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78\n"
"]);\n"
"\n"
"const dbase = new Uint16Array([ /* Distance codes 0..29 base */\n"
"  1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,\n"
"  257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,\n"
"  8193, 12289, 16385, 24577, 0, 0\n"
"]);\n"
"\n"
"const dext = new Uint8Array([ /* Distance codes 0..29 extra */\n"
"  16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,\n"
"  23, 23, 24, 24, 25, 25, 26, 26, 27, 27,\n"
"  28, 28, 29, 29, 64, 64\n"
"]);\n"
"\n"
"const inflate_table = (type, lens, lens_index, codes, table, table_index, work, opts) =>\n"
"{\n"
"  const bits = opts.bits;\n"
"      //here = opts.here; /* table entry for duplication */\n"
"\n"
"  let len = 0;               /* a code's length in bits */\n"
"  let sym = 0;               /* index of code symbols */\n"
"  let min = 0, max = 0;          /* minimum and maximum code lengths */\n"
"  let root = 0;              /* number of index bits for root table */\n"
"  let curr = 0;              /* number of index bits for current table */\n"
"  let drop = 0;              /* code bits to drop for sub-table */\n"
"  let left = 0;                   /* number of prefix codes available */\n"
"  let used = 0;              /* code entries in table used */\n"
"  let huff = 0;              /* Huffman code */\n"
"  let incr;              /* for incrementing code, index */\n"
"  let fill;              /* index for replicating entries */\n"
"  let low;               /* low bits for current root entry */\n"
"  let mask;              /* mask for low root bits */\n"
"  let next;             /* next available space in table */\n"
"  let base = null;     /* base value table to use */\n"
"//  let shoextra;    /* extra bits table to use */\n"
"  let match;                  /* use base and extra for symbol >= match */\n"
"  const count = new Uint16Array(MAXBITS + 1); //[MAXBITS+1];    /* number of codes of each length */\n"
"  const offs = new Uint16Array(MAXBITS + 1); //[MAXBITS+1];     /* offsets in table for each length */\n"
"  let extra = null;\n"
"\n"
"  let here_bits, here_op, here_val;\n"
"\n"
"  /*\n"
"   Process a set of code lengths to create a canonical Huffman code.  The\n"
"   code lengths are lens[0..codes-1].  Each length corresponds to the\n"
"   symbols 0..codes-1.  The Huffman code is generated by first sorting the\n"
"   symbols by length from short to long, and retaining the symbol order\n"
"   for codes with equal lengths.  Then the code starts with all zero bits\n"
"   for the first code of the shortest length, and the codes are integer\n"
"   increments for the same length, and zeros are appended as the length\n"
"   increases.  For the deflate format, these bits are stored backwards\n"
"   from their more natural integer increment ordering, and so when the\n"
"   decoding tables are built in the large loop below, the integer codes\n"
"   are incremented backwards.\n"
"\n"
"   This routine assumes, but does not check, that all of the entries in\n"
"   lens[] are in the range 0..MAXBITS.  The caller must assure this.\n"
"   1..MAXBITS is interpreted as that code length.  zero means that that\n"
"   symbol does not occur in this code.\n"
"\n"
"   The codes are sorted by computing a count of codes for each length,\n"
"   creating from that a table of starting indices for each length in the\n"
"   sorted table, and then entering the symbols in order in the sorted\n"
"   table.  The sorted table is work[], with that space being provided by\n"
"   the caller.\n"
"\n"
"   The length counts are used for other purposes as well, i.e. finding\n"
"   the minimum and maximum length codes, determining if there are any\n"
"   codes at all, checking for a valid set of lengths, and looking ahead\n"
"   at length counts to determine sub-table sizes when building the\n"
"   decoding tables.\n"
"   */\n"
"\n"
"  /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */\n"
"  for (len = 0; len <= MAXBITS; len++) {\n"
"    count[len] = 0;\n"
"  }\n"
"  for (sym = 0; sym < codes; sym++) {\n"
"    count[lens[lens_index + sym]]++;\n"
"  }\n"
"\n"
"  /* bound code lengths, force root to be within code lengths */\n"
"  root = bits;\n"
"  for (max = MAXBITS; max >= 1; max--) {\n"
"    if (count[max] !== 0) { break; }\n"
"  }\n"
"  if (root > max) {\n"
"    root = max;\n"
"  }\n"
"  if (max === 0) {                     /* no symbols to code at all */\n"
"    //table.op[opts.table_index] = 64;  //here.op = (var char)64;    /* invalid code marker */\n"
"    //table.bits[opts.table_index] = 1;   //here.bits = (var char)1;\n"
"    //table.val[opts.table_index++] = 0;   //here.val = (var short)0;\n"
"    table[table_index++] = (1 << 24) | (64 << 16) | 0;\n"
"\n"
"\n"
"    //table.op[opts.table_index] = 64;\n"
"    //table.bits[opts.table_index] = 1;\n"
"    //table.val[opts.table_index++] = 0;\n"
"    table[table_index++] = (1 << 24) | (64 << 16) | 0;\n"
"\n"
"    opts.bits = 1;\n"
"    return 0;     /* no symbols, but wait for decoding to report error */\n"
"  }\n"
"  for (min = 1; min < max; min++) {\n"
"    if (count[min] !== 0) { break; }\n"
"  }\n"
"  if (root < min) {\n"
"    root = min;\n"
"  }\n"
"\n"
"  /* check for an over-subscribed or incomplete set of lengths */\n"
"  left = 1;\n"
"  for (len = 1; len <= MAXBITS; len++) {\n"
"    left <<= 1;\n"
"    left -= count[len];\n"
"    if (left < 0) {\n"
"      return -1;\n"
"    }        /* over-subscribed */\n"
"  }\n"
"  if (left > 0 && (type === CODES || max !== 1)) {\n"
"    return -1;                      /* incomplete set */\n"
"  }\n"
"\n"
"  /* generate offsets into symbol table for each length for sorting */\n"
"  offs[1] = 0;\n"
"  for (len = 1; len < MAXBITS; len++) {\n"
"    offs[len + 1] = offs[len] + count[len];\n"
"  }\n"
"\n"
"  /* sort symbols by length, by symbol order within each length */\n"
"  for (sym = 0; sym < codes; sym++) {\n"
"    if (lens[lens_index + sym] !== 0) {\n"
"      work[offs[lens[lens_index + sym]]++] = sym;\n"
"    }\n"
"  }\n"
"\n"
"  /*\n"
"   Create and fill in decoding tables.  In this loop, the table being\n"
"   filled is at next and has curr index bits.  The code being used is huff\n"
"   with length len.  That code is converted to an index by dropping drop\n"
"   bits off of the bottom.  For codes where len is less than drop + curr,\n"
"   those top drop + curr - len bits are incremented through all values to\n"
"   fill the table with replicated entries.\n"
"\n"
"   root is the number of index bits for the root table.  When len exceeds\n"
"   root, sub-tables are created pointed to by the root entry with an index\n"
"   of the low root bits of huff.  This is saved in low to check for when a\n"
"   new sub-table should be started.  drop is zero when the root table is\n"
"   being filled, and drop is root when sub-tables are being filled.\n"
"\n"
"   When a new sub-table is needed, it is necessary to look ahead in the\n"
"   code lengths to determine what size sub-table is needed.  The length\n"
"   counts are used for this, and so count[] is decremented as codes are\n"
"   entered in the tables.\n"
"\n"
"   used keeps track of how many table entries have been allocated from the\n"
"   provided *table space.  It is checked for LENS and DIST tables against\n"
"   the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in\n"
"   the initial root table size constants.  See the comments in inftrees.h\n"
"   for more information.\n"
"\n"
"   sym increments through all symbols, and the loop terminates when\n"
"   all codes of length max, i.e. all codes, have been processed.  This\n"
"   routine permits incomplete codes, so another loop after this one fills\n"
"   in the rest of the decoding tables with invalid code markers.\n"
"   */\n"
"\n"
"  /* set up for code type */\n"
"  // poor man optimization - use if-else instead of switch,\n"
"  // to avoid deopts in old v8\n"
"  if (type === CODES) {\n"
"    base = extra = work;    /* dummy value--not used */\n"
"    match = 20;\n"
"\n"
"  } else if (type === LENS) {\n"
"    base = lbase;\n"
"    extra = lext;\n"
"    match = 257;\n"
"\n"
"  } else {                    /* DISTS */\n"
"    base = dbase;\n"
"    extra = dext;\n"
"    match = 0;\n"
"  }\n"
"\n"
"  /* initialize opts for loop */\n"
"  huff = 0;                   /* starting code */\n"
"  sym = 0;                    /* starting code symbol */\n"
"  len = min;                  /* starting code length */\n"
"  next = table_index;              /* current table to fill in */\n"
"  curr = root;                /* current table index bits */\n"
"  drop = 0;                   /* current bits to drop from code for index */\n"
"  low = -1;                   /* trigger new sub-table when len > root */\n"
"  used = 1 << root;          /* use root table entries */\n"
"  mask = used - 1;            /* mask for comparing low */\n"
"\n"
"  /* check available table space */\n"
"  if ((type === LENS && used > ENOUGH_LENS) ||\n"
"    (type === DISTS && used > ENOUGH_DISTS)) {\n"
"    return 1;\n"
"  }\n"
"\n"
"  /* process all codes and make table entries */\n"
"  for (;;) {\n"
"    /* create table entry */\n"
"    here_bits = len - drop;\n"
"    if (work[sym] + 1 < match) {\n"
"      here_op = 0;\n"
"      here_val = work[sym];\n"
"    }\n"
"    else if (work[sym] >= match) {\n"
"      here_op = extra[work[sym] - match];\n"
"      here_val = base[work[sym] - match];\n"
"    }\n"
"    else {\n"
"      here_op = 32 + 64;         /* end of block */\n"
"      here_val = 0;\n"
"    }\n"
"\n"
"    /* replicate for those indices with low len bits equal to huff */\n"
"    incr = 1 << (len - drop);\n"
"    fill = 1 << curr;\n"
"    min = fill;                 /* save offset to next table */\n"
"    do {\n"
"      fill -= incr;\n"
"      table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0;\n"
"    } while (fill !== 0);\n"
"\n"
"    /* backwards increment the len-bit code huff */\n"
"    incr = 1 << (len - 1);\n"
"    while (huff & incr) {\n"
"      incr >>= 1;\n"
"    }\n"
"    if (incr !== 0) {\n"
"      huff &= incr - 1;\n"
"      huff += incr;\n"
"    } else {\n"
"      huff = 0;\n"
"    }\n"
"\n"
"    /* go to next symbol, update count, len */\n"
"    sym++;\n"
"    if (--count[len] === 0) {\n"
"      if (len === max) { break; }\n"
"      len = lens[lens_index + work[sym]];\n"
"    }\n"
"\n"
"    /* create new sub-table if needed */\n"
"    if (len > root && (huff & mask) !== low) {\n"
"      /* if first time, transition to sub-tables */\n"
"      if (drop === 0) {\n"
"        drop = root;\n"
"      }\n"
"\n"
"      /* increment past last table */\n"
"      next += min;            /* here min is 1 << curr */\n"
"\n"
"      /* determine length of next table */\n"
"      curr = len - drop;\n"
"      left = 1 << curr;\n"
"      while (curr + drop < max) {\n"
"        left -= count[curr + drop];\n"
"        if (left <= 0) { break; }\n"
"        curr++;\n"
"        left <<= 1;\n"
"      }\n"
"\n"
"      /* check for enough space */\n"
"      used += 1 << curr;\n"
"      if ((type === LENS && used > ENOUGH_LENS) ||\n"
"        (type === DISTS && used > ENOUGH_DISTS)) {\n"
"        return 1;\n"
"      }\n"
"\n"
"      /* point entry in root table to sub-table */\n"
"      low = huff & mask;\n"
"      /*table.op[low] = curr;\n"
"      table.bits[low] = root;\n"
"      table.val[low] = next - opts.table_index;*/\n"
"      table[low] = (root << 24) | (curr << 16) | (next - table_index) |0;\n"
"    }\n"
"  }\n"
"\n"
"  /* fill in remaining table entry if code is incomplete (guaranteed to have\n"
"   at most one remaining entry, since if the code is incomplete, the\n"
"   maximum code length that was allowed to get this far is one bit) */\n"
"  if (huff !== 0) {\n"
"    //table.op[next + huff] = 64;            /* invalid code marker */\n"
"    //table.bits[next + huff] = len - drop;\n"
"    //table.val[next + huff] = 0;\n"
"    table[next + huff] = ((len - drop) << 24) | (64 << 16) |0;\n"
"  }\n"
"\n"
"  /* set return parameters */\n"
"  //opts.table_index += used;\n"
"  opts.bits = root;\n"
"  return 0;\n"
"};\n"
"\n"
"\n"
"window.inflate_table;\n"
"window.MAXBITS = MAXBITS;\n"
"window.ENOUGH_LENS = ENOUGH_LENS;\n"
"window.ENOUGH_DISTS = ENOUGH_DISTS;\n"
"window.CODES = CODES;\n"
"window.LENS = LENS;\n"
"window.DISTS = DISTS;\n"
"window.lbase = lbase;\n"
"window.lext = lext;\n"
"window.dbase = dbase;\n"
"window.dext = dext;\n"
;
    if (strcmp(path, "/novnc/vendor/pako/lib/zlib/inftrees.js") == 0) return novnc_vendor_pako_lib_zlib_inftrees_js;
const char *novnc_vendor_pako_lib_zlib_messages_js =
"'use strict';\n"
"\n"
"window.msg = {\n"
"  2: 'need dictionary',     /* Z_NEED_DICT       2  */\n"
"  1: 'stream end',          /* Z_STREAM_END      1  */\n"
"  0: '',                    /* Z_OK              0  */\n"
"  '-1': 'file error',       /* Z_ERRNO         (-1) */\n"
"  '-2': 'stream error',     /* Z_STREAM_ERROR  (-2) */\n"
"  '-3': 'data error',       /* Z_DATA_ERROR    (-3) */\n"
"  '-4': 'insufficient memory', /* Z_MEM_ERROR    (-4) */\n"
"  '-5': 'buffer error',     /* Z_BUF_ERROR    (-5) */\n"
"  '-6': 'incompatible version' /* Z_VERSION_ERROR (-6) */\n"
"};\n"
;
    if (strcmp(path, "/novnc/vendor/pako/lib/zlib/messages.js") == 0) return novnc_vendor_pako_lib_zlib_messages_js;
const char *novnc_vendor_pako_lib_zlib_trees_js =
"'use strict';\n"
"\n"
"// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n"
"// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n"
"//\n"
"// This software is provided 'as-is', without any express or implied\n"
"// warranty. In no event will the authors be held liable for any damages\n"
"// arising from the use of this software.\n"
"//\n"
"// Permission is granted to anyone to use this software for any purpose,\n"
"// including commercial applications, and to alter it and redistribute it\n"
"// freely, subject to the following restrictions:\n"
"//\n"
"// 1. The origin of this software must not be misrepresented; you must not\n"
"//   claim that you wrote the original software. If you use this software\n"
"//   in a product, an acknowledgment in the product documentation would be\n"
"//   appreciated but is not required.\n"
"// 2. Altered source versions must be plainly marked as such, and must not be\n"
"//   misrepresented as being the original software.\n"
"// 3. This notice may not be removed or altered from any source distribution.\n"
"\n"
"/* eslint-disable space-unary-ops */\n"
"\n"
"/* Public constants ==========================================================*/\n"
"/* ===========================================================================*/\n"
"\n"
"\n"
"//const Z_FILTERED          = 1;\n"
"//const Z_HUFFMAN_ONLY      = 2;\n"
"//const Z_RLE               = 3;\n"
"//const Z_DEFAULT_STRATEGY  = 0;\n"
"\n"
"/* Possible values of the data_type field (though see inflate()) */\n"
"//const Z_ASCII             = 1; // = Z_TEXT\n"
"\n"
"/*============================================================================*/\n"
"\n"
"\n"
"\n"
"// From zutil.h\n"
"\n"
"const STORED_BLOCK = 0;\n"
"const STATIC_TREES = 1;\n"
"const DYN_TREES    = 2;\n"
"/* The three kinds of block type */\n"
"/* The minimum and maximum match lengths */\n"
"\n"
"// From deflate.h\n"
"/* ===========================================================================\n"
" * Internal compression state.\n"
" */\n"
"\n"
"/* number of literal bytes 0..255 */\n"
"\n"
"/* number of Literal or Length codes, including the END_BLOCK code */\n"
"\n"
"/* number of distance codes */\n"
"\n"
"/* number of codes used to transfer the bit lengths */\n"
"\n"
"/* maximum heap size */\n"
"\n"
"/* All codes must not exceed MAX_BITS bits */\n"
"\n"
"const Buf_size      = 16;\n"
"/* size of bit buffer in bi_buf */\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Constants\n"
" */\n"
"\n"
"const MAX_BL_BITS = 7;\n"
"/* Bit length codes must not exceed MAX_BL_BITS bits */\n"
"\n"
"const END_BLOCK   = 256;\n"
"/* end of block literal code */\n"
"\n"
"const REP_3_6     = 16;\n"
"/* repeat previous bit length 3-6 times (2 bits of repeat count) */\n"
"\n"
"const REPZ_3_10   = 17;\n"
"/* repeat a zero length 3-10 times  (3 bits of repeat count) */\n"
"\n"
"const REPZ_11_138 = 18;\n"
"/* repeat a zero length 11-138 times  (7 bits of repeat count) */\n"
"\n"
"/* eslint-disable comma-spacing,array-bracket-spacing */\n"
"const extra_lbits =   /* extra bits for each length code */\n"
"  new Uint8Array([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0]);\n"
"\n"
"const extra_dbits =   /* extra bits for each distance code */\n"
"  new Uint8Array([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13]);\n"
"\n"
"const extra_blbits =  /* extra bits for each bit length code */\n"
"  new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7]);\n"
"\n"
"const bl_order =\n"
"  new Uint8Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]);\n"
"/* eslint-enable comma-spacing,array-bracket-spacing */\n"
"\n"
"/* The lengths of the bit length codes are sent in order of decreasing\n"
" * probability, to avoid transmitting the lengths for unused bit length codes.\n"
" */\n"
"\n"
"/* ===========================================================================\n"
" * Local data. These are initialized only once.\n"
" */\n"
"\n"
"// We pre-fill arrays with 0 to avoid uninitialized gaps\n"
"\n"
"const DIST_CODE_LEN = 512; /* see definition of array dist_code below */\n"
"\n"
"// !!!! Use flat array instead of structure, Freq = i*2, Len = i*2+1\n"
"const static_ltree  = new Array((L_CODES + 2) * 2);\n"
"zero(static_ltree);\n"
"/* The static literal tree. Since the bit lengths are imposed, there is no\n"
" * need for the L_CODES extra codes used during heap construction. However\n"
" * The codes 286 and 287 are needed to build a canonical tree (see _tr_init\n"
" * below).\n"
" */\n"
"\n"
"const static_dtree  = new Array(D_CODES * 2);\n"
"zero(static_dtree);\n"
"/* The static distance tree. (Actually a trivial tree since all codes use\n"
" * 5 bits.)\n"
" */\n"
"\n"
"const _dist_code    = new Array(DIST_CODE_LEN);\n"
"zero(_dist_code);\n"
"/* Distance codes. The first 256 values correspond to the distances\n"
" * 3 .. 258, the last 256 values correspond to the top 8 bits of\n"
" * the 15 bit distances.\n"
" */\n"
"\n"
"const _length_code  = new Array(MAX_MATCH - MIN_MATCH + 1);\n"
"zero(_length_code);\n"
"/* length code for each normalized match length (0 == MIN_MATCH) */\n"
"\n"
"const base_length   = new Array(LENGTH_CODES);\n"
"zero(base_length);\n"
"/* First normalized length for each code (0 = MIN_MATCH) */\n"
"\n"
"const base_dist     = new Array(D_CODES);\n"
"zero(base_dist);\n"
"/* First normalized distance for each code (0 = distance of 1) */\n"
"\n"
"\n"
"function StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) {\n"
"\n"
"  this.static_tree  = static_tree;  /* static tree or NULL */\n"
"  this.extra_bits   = extra_bits;   /* extra bits for each code or NULL */\n"
"  this.extra_base   = extra_base;   /* base index for extra_bits */\n"
"  this.elems        = elems;        /* max number of elements in the tree */\n"
"  this.max_length   = max_length;   /* max bit length for the codes */\n"
"\n"
"  // show if `static_tree` has data or dummy - needed for monomorphic objects\n"
"  this.has_stree    = static_tree && static_tree.length;\n"
"}\n"
"\n"
"\n"
"let static_l_desc;\n"
"let static_d_desc;\n"
"let static_bl_desc;\n"
"\n"
"\n"
"function TreeDesc(dyn_tree, stat_desc) {\n"
"  this.dyn_tree = dyn_tree;     /* the dynamic tree */\n"
"  this.max_code = 0;            /* largest code with non zero frequency */\n"
"  this.stat_desc = stat_desc;   /* the corresponding static tree */\n"
"}\n"
"\n"
"\n"
"\n"
"const d_code = (dist) => {\n"
"\n"
"  return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)];\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Output a short LSB first on the stream.\n"
" * IN assertion: there is enough room in pendingBuf.\n"
" */\n"
"const put_short = (s, w) => {\n"
"//    put_byte(s, (uch)((w) & 0xff));\n"
"//    put_byte(s, (uch)((ush)(w) >> 8));\n"
"  s.pending_buf[s.pending++] = (w) & 0xff;\n"
"  s.pending_buf[s.pending++] = (w >>> 8) & 0xff;\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Send a value on a given number of bits.\n"
" * IN assertion: length <= 16 and value fits in length bits.\n"
" */\n"
"const send_bits = (s, value, length) => {\n"
"\n"
"  if (s.bi_valid > (Buf_size - length)) {\n"
"    s.bi_buf |= (value << s.bi_valid) & 0xffff;\n"
"    put_short(s, s.bi_buf);\n"
"    s.bi_buf = value >> (Buf_size - s.bi_valid);\n"
"    s.bi_valid += length - Buf_size;\n"
"  } else {\n"
"    s.bi_buf |= (value << s.bi_valid) & 0xffff;\n"
"    s.bi_valid += length;\n"
"  }\n"
"};\n"
"\n"
"\n"
"const send_code = (s, c, tree) => {\n"
"\n"
"  send_bits(s, tree[c * 2]/*.Code*/, tree[c * 2 + 1]/*.Len*/);\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Reverse the first len bits of a code, using straightforward code (a faster\n"
" * method would use a table)\n"
" * IN assertion: 1 <= len <= 15\n"
" */\n"
"const bi_reverse = (code, len) => {\n"
"\n"
"  let res = 0;\n"
"  do {\n"
"    res |= code & 1;\n"
"    code >>>= 1;\n"
"    res <<= 1;\n"
"  } while (--len > 0);\n"
"  return res >>> 1;\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Flush the bit buffer, keeping at most 7 bits in it.\n"
" */\n"
"const bi_flush = (s) => {\n"
"\n"
"  if (s.bi_valid === 16) {\n"
"    put_short(s, s.bi_buf);\n"
"    s.bi_buf = 0;\n"
"    s.bi_valid = 0;\n"
"\n"
"  } else if (s.bi_valid >= 8) {\n"
"    s.pending_buf[s.pending++] = s.bi_buf & 0xff;\n"
"    s.bi_buf >>= 8;\n"
"    s.bi_valid -= 8;\n"
"  }\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Compute the optimal bit lengths for a tree and update the total bit length\n"
" * for the current block.\n"
" * IN assertion: the fields freq and dad are set, heap[heap_max] and\n"
" *    above are the tree nodes sorted by increasing frequency.\n"
" * OUT assertions: the field len is set to the optimal bit length, the\n"
" *     array bl_count contains the frequencies for each bit length.\n"
" *     The length opt_len is updated; static_len is also updated if stree is\n"
" *     not null.\n"
" */\n"
"const gen_bitlen = (s, desc) => {\n"
"//    deflate_state *s;\n"
"//    tree_desc *desc;    /* the tree descriptor */\n"
"\n"
"  const tree            = desc.dyn_tree;\n"
"  const max_code        = desc.max_code;\n"
"  const stree           = desc.stat_desc.static_tree;\n"
"  const has_stree       = desc.stat_desc.has_stree;\n"
"  const extra           = desc.stat_desc.extra_bits;\n"
"  const base            = desc.stat_desc.extra_base;\n"
"  const max_length      = desc.stat_desc.max_length;\n"
"  let h;              /* heap index */\n"
"  let n, m;           /* iterate over the tree elements */\n"
"  let bits;           /* bit length */\n"
"  let xbits;          /* extra bits */\n"
"  let f;              /* frequency */\n"
"  let overflow = 0;   /* number of elements with bit length too large */\n"
"\n"
"  for (bits = 0; bits <= MAX_BITS; bits++) {\n"
"    s.bl_count[bits] = 0;\n"
"  }\n"
"\n"
"  /* In a first pass, compute the optimal bit lengths (which may\n"
"   * overflow in the case of the bit length tree).\n"
"   */\n"
"  tree[s.heap[s.heap_max] * 2 + 1]/*.Len*/ = 0; /* root of the heap */\n"
"\n"
"  for (h = s.heap_max + 1; h < HEAP_SIZE; h++) {\n"
"    n = s.heap[h];\n"
"    bits = tree[tree[n * 2 + 1]/*.Dad*/ * 2 + 1]/*.Len*/ + 1;\n"
"    if (bits > max_length) {\n"
"      bits = max_length;\n"
"      overflow++;\n"
"    }\n"
"    tree[n * 2 + 1]/*.Len*/ = bits;\n"
"    /* We overwrite tree[n].Dad which is no longer needed */\n"
"\n"
"    if (n > max_code) { continue; } /* not a leaf node */\n"
"\n"
"    s.bl_count[bits]++;\n"
"    xbits = 0;\n"
"    if (n >= base) {\n"
"      xbits = extra[n - base];\n"
"    }\n"
"    f = tree[n * 2]/*.Freq*/;\n"
"    s.opt_len += f * (bits + xbits);\n"
"    if (has_stree) {\n"
"      s.static_len += f * (stree[n * 2 + 1]/*.Len*/ + xbits);\n"
"    }\n"
"  }\n"
"  if (overflow === 0) { return; }\n"
"\n"
"  // Tracev((stderr,\"\\nbit length overflow\\n\"));\n"
"  /* This happens for example on obj2 and pic of the Calgary corpus */\n"
"\n"
"  /* Find the first bit length which could increase: */\n"
"  do {\n"
"    bits = max_length - 1;\n"
"    while (s.bl_count[bits] === 0) { bits--; }\n"
"    s.bl_count[bits]--;      /* move one leaf down the tree */\n"
"    s.bl_count[bits + 1] += 2; /* move one overflow item as its brother */\n"
"    s.bl_count[max_length]--;\n"
"    /* The brother of the overflow item also moves one step up,\n"
"     * but this does not affect bl_count[max_length]\n"
"     */\n"
"    overflow -= 2;\n"
"  } while (overflow > 0);\n"
"\n"
"  /* Now recompute all bit lengths, scanning in increasing frequency.\n"
"   * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all\n"
"   * lengths instead of fixing only the wrong ones. This idea is taken\n"
"   * from 'ar' written by Haruhiko Okumura.)\n"
"   */\n"
"  for (bits = max_length; bits !== 0; bits--) {\n"
"    n = s.bl_count[bits];\n"
"    while (n !== 0) {\n"
"      m = s.heap[--h];\n"
"      if (m > max_code) { continue; }\n"
"      if (tree[m * 2 + 1]/*.Len*/ !== bits) {\n"
"        // Tracev((stderr,\"code %d bits %d->%d\\n\", m, tree[m].Len, bits));\n"
"        s.opt_len += (bits - tree[m * 2 + 1]/*.Len*/) * tree[m * 2]/*.Freq*/;\n"
"        tree[m * 2 + 1]/*.Len*/ = bits;\n"
"      }\n"
"      n--;\n"
"    }\n"
"  }\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Generate the codes for a given tree and bit counts (which need not be\n"
" * optimal).\n"
" * IN assertion: the array bl_count contains the bit length statistics for\n"
" * the given tree and the field len is set for all tree elements.\n"
" * OUT assertion: the field code is set for all tree elements of non\n"
" *     zero code length.\n"
" */\n"
"const gen_codes = (tree, max_code, bl_count) => {\n"
"//    ct_data *tree;             /* the tree to decorate */\n"
"//    int max_code;              /* largest code with non zero frequency */\n"
"//    ushf *bl_count;            /* number of codes at each bit length */\n"
"\n"
"  const next_code = new Array(MAX_BITS + 1); /* next code value for each bit length */\n"
"  let code = 0;              /* running code value */\n"
"  let bits;                  /* bit index */\n"
"  let n;                     /* code index */\n"
"\n"
"  /* The distribution counts are first used to generate the code values\n"
"   * without bit reversal.\n"
"   */\n"
"  for (bits = 1; bits <= MAX_BITS; bits++) {\n"
"    code = (code + bl_count[bits - 1]) << 1;\n"
"    next_code[bits] = code;\n"
"  }\n"
"  /* Check that the bit counts in bl_count are consistent. The last code\n"
"   * must be all ones.\n"
"   */\n"
"  //Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,\n"
"  //        \"inconsistent bit counts\");\n"
"  //Tracev((stderr,\"\\ngen_codes: max_code %d \", max_code));\n"
"\n"
"  for (n = 0;  n <= max_code; n++) {\n"
"    let len = tree[n * 2 + 1]/*.Len*/;\n"
"    if (len === 0) { continue; }\n"
"    /* Now reverse the bits */\n"
"    tree[n * 2]/*.Code*/ = bi_reverse(next_code[len]++, len);\n"
"\n"
"    //Tracecv(tree != static_ltree, (stderr,\"\\nn %3d %c l %2d c %4x (%x) \",\n"
"    //     n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));\n"
"  }\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Initialize the various 'constant' tables.\n"
" */\n"
"const tr_static_init = () => {\n"
"\n"
"  let n;        /* iterates over tree elements */\n"
"  let bits;     /* bit counter */\n"
"  let length;   /* length value */\n"
"  let code;     /* code value */\n"
"  let dist;     /* distance index */\n"
"  const bl_count = new Array(MAX_BITS + 1);\n"
"  /* number of codes at each bit length for an optimal tree */\n"
"\n"
"  // do check in _tr_init()\n"
"  //if (static_init_done) return;\n"
"\n"
"  /* For some embedded targets, global variables are not initialized: */\n"
"/*#ifdef NO_INIT_GLOBAL_POINTERS\n"
"  static_l_desc.static_tree = static_ltree;\n"
"  static_l_desc.extra_bits = extra_lbits;\n"
"  static_d_desc.static_tree = static_dtree;\n"
"  static_d_desc.extra_bits = extra_dbits;\n"
"  static_bl_desc.extra_bits = extra_blbits;\n"
"#endif*/\n"
"\n"
"  /* Initialize the mapping length (0..255) -> length code (0..28) */\n"
"  length = 0;\n"
"  for (code = 0; code < LENGTH_CODES - 1; code++) {\n"
"    base_length[code] = length;\n"
"    for (n = 0; n < (1 << extra_lbits[code]); n++) {\n"
"      _length_code[length++] = code;\n"
"    }\n"
"  }\n"
"  //Assert (length == 256, \"tr_static_init: length != 256\");\n"
"  /* Note that the length 255 (match length 258) can be represented\n"
"   * in two different ways: code 284 + 5 bits or code 285, so we\n"
"   * overwrite length_code[255] to use the best encoding:\n"
"   */\n"
"  _length_code[length - 1] = code;\n"
"\n"
"  /* Initialize the mapping dist (0..32K) -> dist code (0..29) */\n"
"  dist = 0;\n"
"  for (code = 0; code < 16; code++) {\n"
"    base_dist[code] = dist;\n"
"    for (n = 0; n < (1 << extra_dbits[code]); n++) {\n"
"      _dist_code[dist++] = code;\n"
"    }\n"
"  }\n"
"  //Assert (dist == 256, \"tr_static_init: dist != 256\");\n"
"  dist >>= 7; /* from now on, all distances are divided by 128 */\n"
"  for (; code < D_CODES; code++) {\n"
"    base_dist[code] = dist << 7;\n"
"    for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) {\n"
"      _dist_code[256 + dist++] = code;\n"
"    }\n"
"  }\n"
"  //Assert (dist == 256, \"tr_static_init: 256+dist != 512\");\n"
"\n"
"  /* Construct the codes of the static literal tree */\n"
"  for (bits = 0; bits <= MAX_BITS; bits++) {\n"
"    bl_count[bits] = 0;\n"
"  }\n"
"\n"
"  n = 0;\n"
"  while (n <= 143) {\n"
"    static_ltree[n * 2 + 1]/*.Len*/ = 8;\n"
"    n++;\n"
"    bl_count[8]++;\n"
"  }\n"
"  while (n <= 255) {\n"
"    static_ltree[n * 2 + 1]/*.Len*/ = 9;\n"
"    n++;\n"
"    bl_count[9]++;\n"
"  }\n"
"  while (n <= 279) {\n"
"    static_ltree[n * 2 + 1]/*.Len*/ = 7;\n"
"    n++;\n"
"    bl_count[7]++;\n"
"  }\n"
"  while (n <= 287) {\n"
"    static_ltree[n * 2 + 1]/*.Len*/ = 8;\n"
"    n++;\n"
"    bl_count[8]++;\n"
"  }\n"
"  /* Codes 286 and 287 do not exist, but we must include them in the\n"
"   * tree construction to get a canonical Huffman tree (longest code\n"
"   * all ones)\n"
"   */\n"
"  gen_codes(static_ltree, L_CODES + 1, bl_count);\n"
"\n"
"  /* The static distance tree is trivial: */\n"
"  for (n = 0; n < D_CODES; n++) {\n"
"    static_dtree[n * 2 + 1]/*.Len*/ = 5;\n"
"    static_dtree[n * 2]/*.Code*/ = bi_reverse(n, 5);\n"
"  }\n"
"\n"
"  // Now data ready and we can init static trees\n"
"  static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS + 1, L_CODES, MAX_BITS);\n"
"  static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0,          D_CODES, MAX_BITS);\n"
"  static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0,         BL_CODES, MAX_BL_BITS);\n"
"\n"
"  //static_init_done = true;\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Initialize a new block.\n"
" */\n"
"const init_block = (s) => {\n"
"\n"
"  let n; /* iterates over tree elements */\n"
"\n"
"  /* Initialize the trees. */\n"
"  for (n = 0; n < L_CODES;  n++) { s.dyn_ltree[n * 2]/*.Freq*/ = 0; }\n"
"  for (n = 0; n < D_CODES;  n++) { s.dyn_dtree[n * 2]/*.Freq*/ = 0; }\n"
"  for (n = 0; n < BL_CODES; n++) { s.bl_tree[n * 2]/*.Freq*/ = 0; }\n"
"\n"
"  s.dyn_ltree[END_BLOCK * 2]/*.Freq*/ = 1;\n"
"  s.opt_len = s.static_len = 0;\n"
"  s.sym_next = s.matches = 0;\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Flush the bit buffer and align the output on a byte boundary\n"
" */\n"
"const bi_windup = (s) =>\n"
"{\n"
"  if (s.bi_valid > 8) {\n"
"    put_short(s, s.bi_buf);\n"
"  } else if (s.bi_valid > 0) {\n"
"    //put_byte(s, (Byte)s->bi_buf);\n"
"    s.pending_buf[s.pending++] = s.bi_buf;\n"
"  }\n"
"  s.bi_buf = 0;\n"
"  s.bi_valid = 0;\n"
"};\n"
"\n"
"/* ===========================================================================\n"
" * Compares to subtrees, using the tree depth as tie breaker when\n"
" * the subtrees have equal frequency. This minimizes the worst case length.\n"
" */\n"
"const smaller = (tree, n, m, depth) => {\n"
"\n"
"  const _n2 = n * 2;\n"
"  const _m2 = m * 2;\n"
"  return (tree[_n2]/*.Freq*/ < tree[_m2]/*.Freq*/ ||\n"
"         (tree[_n2]/*.Freq*/ === tree[_m2]/*.Freq*/ && depth[n] <= depth[m]));\n"
"};\n"
"\n"
"/* ===========================================================================\n"
" * Restore the heap property by moving down the tree starting at node k,\n"
" * exchanging a node with the smallest of its two sons if necessary, stopping\n"
" * when the heap property is re-established (each father smaller than its\n"
" * two sons).\n"
" */\n"
"const pqdownheap = (s, tree, k) => {\n"
"//    deflate_state *s;\n"
"//    ct_data *tree;  /* the tree to restore */\n"
"//    int k;               /* node to move down */\n"
"\n"
"  const v = s.heap[k];\n"
"  let j = k << 1;  /* left son of k */\n"
"  while (j <= s.heap_len) {\n"
"    /* Set j to the smallest of the two sons: */\n"
"    if (j < s.heap_len &&\n"
"      smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) {\n"
"      j++;\n"
"    }\n"
"    /* Exit if v is smaller than both sons */\n"
"    if (smaller(tree, v, s.heap[j], s.depth)) { break; }\n"
"\n"
"    /* Exchange v with the smallest son */\n"
"    s.heap[k] = s.heap[j];\n"
"    k = j;\n"
"\n"
"    /* And continue down the tree, setting j to the left son of k */\n"
"    j <<= 1;\n"
"  }\n"
"  s.heap[k] = v;\n"
"};\n"
"\n"
"\n"
"// inlined manually\n"
"// const SMALLEST = 1;\n"
"\n"
"/* ===========================================================================\n"
" * Send the block data compressed using the given Huffman trees\n"
" */\n"
"const compress_block = (s, ltree, dtree) => {\n"
"//    deflate_state *s;\n"
"//    const ct_data *ltree; /* literal tree */\n"
"//    const ct_data *dtree; /* distance tree */\n"
"\n"
"  let dist;           /* distance of matched string */\n"
"  let lc;             /* match length or unmatched char (if dist == 0) */\n"
"  let sx = 0;         /* running index in sym_buf */\n"
"  let code;           /* the code to send */\n"
"  let extra;          /* number of extra bits to send */\n"
"\n"
"  if (s.sym_next !== 0) {\n"
"    do {\n"
"      dist = s.pending_buf[s.sym_buf + sx++] & 0xff;\n"
"      dist += (s.pending_buf[s.sym_buf + sx++] & 0xff) << 8;\n"
"      lc = s.pending_buf[s.sym_buf + sx++];\n"
"      if (dist === 0) {\n"
"        send_code(s, lc, ltree); /* send a literal byte */\n"
"        //Tracecv(isgraph(lc), (stderr,\" '%c' \", lc));\n"
"      } else {\n"
"        /* Here, lc is the match length - MIN_MATCH */\n"
"        code = _length_code[lc];\n"
"        send_code(s, code + LITERALS + 1, ltree); /* send the length code */\n"
"        extra = extra_lbits[code];\n"
"        if (extra !== 0) {\n"
"          lc -= base_length[code];\n"
"          send_bits(s, lc, extra);       /* send the extra length bits */\n"
"        }\n"
"        dist--; /* dist is now the match distance - 1 */\n"
"        code = d_code(dist);\n"
"        //Assert (code < D_CODES, \"bad d_code\");\n"
"\n"
"        send_code(s, code, dtree);       /* send the distance code */\n"
"        extra = extra_dbits[code];\n"
"        if (extra !== 0) {\n"
"          dist -= base_dist[code];\n"
"          send_bits(s, dist, extra);   /* send the extra distance bits */\n"
"        }\n"
"      } /* literal or match pair ? */\n"
"\n"
"      /* Check that the overlay between pending_buf and sym_buf is ok: */\n"
"      //Assert(s->pending < s->lit_bufsize + sx, \"pendingBuf overflow\");\n"
"\n"
"    } while (sx < s.sym_next);\n"
"  }\n"
"\n"
"  send_code(s, END_BLOCK, ltree);\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Construct one Huffman tree and assigns the code bit strings and lengths.\n"
" * Update the total bit length for the current block.\n"
" * IN assertion: the field freq is set for all tree elements.\n"
" * OUT assertions: the fields len and code are set to the optimal bit length\n"
" *     and corresponding code. The length opt_len is updated; static_len is\n"
" *     also updated if stree is not null. The field max_code is set.\n"
" */\n"
"const build_tree = (s, desc) => {\n"
"//    deflate_state *s;\n"
"//    tree_desc *desc; /* the tree descriptor */\n"
"\n"
"  const tree     = desc.dyn_tree;\n"
"  const stree    = desc.stat_desc.static_tree;\n"
"  const has_stree = desc.stat_desc.has_stree;\n"
"  const elems    = desc.stat_desc.elems;\n"
"  let n, m;          /* iterate over heap elements */\n"
"  let max_code = -1; /* largest code with non zero frequency */\n"
"  let node;          /* new node being created */\n"
"\n"
"  /* Construct the initial heap, with least frequent element in\n"
"   * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].\n"
"   * heap[0] is not used.\n"
"   */\n"
"  s.heap_len = 0;\n"
"  s.heap_max = HEAP_SIZE;\n"
"\n"
"  for (n = 0; n < elems; n++) {\n"
"    if (tree[n * 2]/*.Freq*/ !== 0) {\n"
"      s.heap[++s.heap_len] = max_code = n;\n"
"      s.depth[n] = 0;\n"
"\n"
"    } else {\n"
"      tree[n * 2 + 1]/*.Len*/ = 0;\n"
"    }\n"
"  }\n"
"\n"
"  /* The pkzip format requires that at least one distance code exists,\n"
"   * and that at least one bit should be sent even if there is only one\n"
"   * possible code. So to avoid special checks later on we force at least\n"
"   * two codes of non zero frequency.\n"
"   */\n"
"  while (s.heap_len < 2) {\n"
"    node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0);\n"
"    tree[node * 2]/*.Freq*/ = 1;\n"
"    s.depth[node] = 0;\n"
"    s.opt_len--;\n"
"\n"
"    if (has_stree) {\n"
"      s.static_len -= stree[node * 2 + 1]/*.Len*/;\n"
"    }\n"
"    /* node is 0 or 1 so it does not have extra bits */\n"
"  }\n"
"  desc.max_code = max_code;\n"
"\n"
"  /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,\n"
"   * establish sub-heaps of increasing lengths:\n"
"   */\n"
"  for (n = (s.heap_len >> 1/*int /2*/); n >= 1; n--) { pqdownheap(s, tree, n); }\n"
"\n"
"  /* Construct the Huffman tree by repeatedly combining the least two\n"
"   * frequent nodes.\n"
"   */\n"
"  node = elems;              /* next internal node of the tree */\n"
"  do {\n"
"    //pqremove(s, tree, n);  /* n = node of least frequency */\n"
"    /*** pqremove ***/\n"
"    n = s.heap[1/*SMALLEST*/];\n"
"    s.heap[1/*SMALLEST*/] = s.heap[s.heap_len--];\n"
"    pqdownheap(s, tree, 1/*SMALLEST*/);\n"
"    /***/\n"
"\n"
"    m = s.heap[1/*SMALLEST*/]; /* m = node of next least frequency */\n"
"\n"
"    s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */\n"
"    s.heap[--s.heap_max] = m;\n"
"\n"
"    /* Create a new node father of n and m */\n"
"    tree[node * 2]/*.Freq*/ = tree[n * 2]/*.Freq*/ + tree[m * 2]/*.Freq*/;\n"
"    s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1;\n"
"    tree[n * 2 + 1]/*.Dad*/ = tree[m * 2 + 1]/*.Dad*/ = node;\n"
"\n"
"    /* and insert the new node in the heap */\n"
"    s.heap[1/*SMALLEST*/] = node++;\n"
"    pqdownheap(s, tree, 1/*SMALLEST*/);\n"
"\n"
"  } while (s.heap_len >= 2);\n"
"\n"
"  s.heap[--s.heap_max] = s.heap[1/*SMALLEST*/];\n"
"\n"
"  /* At this point, the fields freq and dad are set. We can now\n"
"   * generate the bit lengths.\n"
"   */\n"
"  gen_bitlen(s, desc);\n"
"\n"
"  /* The field len is now set, we can generate the bit codes */\n"
"  gen_codes(tree, max_code, s.bl_count);\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Scan a literal or distance tree to determine the frequencies of the codes\n"
" * in the bit length tree.\n"
" */\n"
"const scan_tree = (s, tree, max_code) => {\n"
"//    deflate_state *s;\n"
"//    ct_data *tree;   /* the tree to be scanned */\n"
"//    int max_code;    /* and its largest code of non zero frequency */\n"
"\n"
"  let n;                     /* iterates over all tree elements */\n"
"  let prevlen = -1;          /* last emitted length */\n"
"  let curlen;                /* length of current code */\n"
"\n"
"  let nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */\n"
"\n"
"  let count = 0;             /* repeat count of the current code */\n"
"  let max_count = 7;         /* max repeat count */\n"
"  let min_count = 4;         /* min repeat count */\n"
"\n"
"  if (nextlen === 0) {\n"
"    max_count = 138;\n"
"    min_count = 3;\n"
"  }\n"
"  tree[(max_code + 1) * 2 + 1]/*.Len*/ = 0xffff; /* guard */\n"
"\n"
"  for (n = 0; n <= max_code; n++) {\n"
"    curlen = nextlen;\n"
"    nextlen = tree[(n + 1) * 2 + 1]/*.Len*/;\n"
"\n"
"    if (++count < max_count && curlen === nextlen) {\n"
"      continue;\n"
"\n"
"    } else if (count < min_count) {\n"
"      s.bl_tree[curlen * 2]/*.Freq*/ += count;\n"
"\n"
"    } else if (curlen !== 0) {\n"
"\n"
"      if (curlen !== prevlen) { s.bl_tree[curlen * 2]/*.Freq*/++; }\n"
"      s.bl_tree[REP_3_6 * 2]/*.Freq*/++;\n"
"\n"
"    } else if (count <= 10) {\n"
"      s.bl_tree[REPZ_3_10 * 2]/*.Freq*/++;\n"
"\n"
"    } else {\n"
"      s.bl_tree[REPZ_11_138 * 2]/*.Freq*/++;\n"
"    }\n"
"\n"
"    count = 0;\n"
"    prevlen = curlen;\n"
"\n"
"    if (nextlen === 0) {\n"
"      max_count = 138;\n"
"      min_count = 3;\n"
"\n"
"    } else if (curlen === nextlen) {\n"
"      max_count = 6;\n"
"      min_count = 3;\n"
"\n"
"    } else {\n"
"      max_count = 7;\n"
"      min_count = 4;\n"
"    }\n"
"  }\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Send a literal or distance tree in compressed form, using the codes in\n"
" * bl_tree.\n"
" */\n"
"const send_tree = (s, tree, max_code) => {\n"
"//    deflate_state *s;\n"
"//    ct_data *tree; /* the tree to be scanned */\n"
"//    int max_code;       /* and its largest code of non zero frequency */\n"
"\n"
"  let n;                     /* iterates over all tree elements */\n"
"  let prevlen = -1;          /* last emitted length */\n"
"  let curlen;                /* length of current code */\n"
"\n"
"  let nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */\n"
"\n"
"  let count = 0;             /* repeat count of the current code */\n"
"  let max_count = 7;         /* max repeat count */\n"
"  let min_count = 4;         /* min repeat count */\n"
"\n"
"  /* tree[max_code+1].Len = -1; */  /* guard already set */\n"
"  if (nextlen === 0) {\n"
"    max_count = 138;\n"
"    min_count = 3;\n"
"  }\n"
"\n"
"  for (n = 0; n <= max_code; n++) {\n"
"    curlen = nextlen;\n"
"    nextlen = tree[(n + 1) * 2 + 1]/*.Len*/;\n"
"\n"
"    if (++count < max_count && curlen === nextlen) {\n"
"      continue;\n"
"\n"
"    } else if (count < min_count) {\n"
"      do { send_code(s, curlen, s.bl_tree); } while (--count !== 0);\n"
"\n"
"    } else if (curlen !== 0) {\n"
"      if (curlen !== prevlen) {\n"
"        send_code(s, curlen, s.bl_tree);\n"
"        count--;\n"
"      }\n"
"      //Assert(count >= 3 && count <= 6, \" 3_6?\");\n"
"      send_code(s, REP_3_6, s.bl_tree);\n"
"      send_bits(s, count - 3, 2);\n"
"\n"
"    } else if (count <= 10) {\n"
"      send_code(s, REPZ_3_10, s.bl_tree);\n"
"      send_bits(s, count - 3, 3);\n"
"\n"
"    } else {\n"
"      send_code(s, REPZ_11_138, s.bl_tree);\n"
"      send_bits(s, count - 11, 7);\n"
"    }\n"
"\n"
"    count = 0;\n"
"    prevlen = curlen;\n"
"    if (nextlen === 0) {\n"
"      max_count = 138;\n"
"      min_count = 3;\n"
"\n"
"    } else if (curlen === nextlen) {\n"
"      max_count = 6;\n"
"      min_count = 3;\n"
"\n"
"    } else {\n"
"      max_count = 7;\n"
"      min_count = 4;\n"
"    }\n"
"  }\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Construct the Huffman tree for the bit lengths and return the index in\n"
" * bl_order of the last bit length code to send.\n"
" */\n"
"const build_bl_tree = (s) => {\n"
"\n"
"  let max_blindex;  /* index of last bit length code of non zero freq */\n"
"\n"
"  /* Determine the bit length frequencies for literal and distance trees */\n"
"  scan_tree(s, s.dyn_ltree, s.l_desc.max_code);\n"
"  scan_tree(s, s.dyn_dtree, s.d_desc.max_code);\n"
"\n"
"  /* Build the bit length tree: */\n"
"  build_tree(s, s.bl_desc);\n"
"  /* opt_len now includes the length of the tree representations, except\n"
"   * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.\n"
"   */\n"
"\n"
"  /* Determine the number of bit length codes to send. The pkzip format\n"
"   * requires that at least 4 bit length codes be sent. (appnote.txt says\n"
"   * 3 but the actual value used is 4.)\n"
"   */\n"
"  for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) {\n"
"    if (s.bl_tree[bl_order[max_blindex] * 2 + 1]/*.Len*/ !== 0) {\n"
"      break;\n"
"    }\n"
"  }\n"
"  /* Update opt_len to include the bit length tree and counts */\n"
"  s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4;\n"
"  //Tracev((stderr, \"\\ndyn trees: dyn %ld, stat %ld\",\n"
"  //        s->opt_len, s->static_len));\n"
"\n"
"  return max_blindex;\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Send the header for a block using dynamic Huffman trees: the counts, the\n"
" * lengths of the bit length codes, the literal tree and the distance tree.\n"
" * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.\n"
" */\n"
"const send_all_trees = (s, lcodes, dcodes, blcodes) => {\n"
"//    deflate_state *s;\n"
"//    int lcodes, dcodes, blcodes; /* number of codes for each tree */\n"
"\n"
"  let rank;                    /* index in bl_order */\n"
"\n"
"  //Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, \"not enough codes\");\n"
"  //Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,\n"
"  //        \"too many codes\");\n"
"  //Tracev((stderr, \"\\nbl counts: \"));\n"
"  send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */\n"
"  send_bits(s, dcodes - 1,   5);\n"
"  send_bits(s, blcodes - 4,  4); /* not -3 as stated in appnote.txt */\n"
"  for (rank = 0; rank < blcodes; rank++) {\n"
"    //Tracev((stderr, \"\\nbl code %2d \", bl_order[rank]));\n"
"    send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1]/*.Len*/, 3);\n"
"  }\n"
"  //Tracev((stderr, \"\\nbl tree: sent %ld\", s->bits_sent));\n"
"\n"
"  send_tree(s, s.dyn_ltree, lcodes - 1); /* literal tree */\n"
"  //Tracev((stderr, \"\\nlit tree: sent %ld\", s->bits_sent));\n"
"\n"
"  send_tree(s, s.dyn_dtree, dcodes - 1); /* distance tree */\n"
"  //Tracev((stderr, \"\\ndist tree: sent %ld\", s->bits_sent));\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Check if the data type is TEXT or BINARY, using the following algorithm:\n"
" * - TEXT if the two conditions below are satisfied:\n"
" *    a) There are no non-portable control characters belonging to the\n"
" *       \"block list\" (0..6, 14..25, 28..31).\n"
" *    b) There is at least one printable character belonging to the\n"
" *       \"allow list\" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).\n"
" * - BINARY otherwise.\n"
" * - The following partially-portable control characters form a\n"
" *   \"gray list\" that is ignored in this detection algorithm:\n"
" *   (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).\n"
" * IN assertion: the fields Freq of dyn_ltree are set.\n"
" */\n"
"const detect_data_type = (s) => {\n"
"  /* block_mask is the bit mask of block-listed bytes\n"
"   * set bits 0..6, 14..25, and 28..31\n"
"   * 0xf3ffc07f = binary 11110011111111111100000001111111\n"
"   */\n"
"  let block_mask = 0xf3ffc07f;\n"
"  let n;\n"
"\n"
"  /* Check for non-textual (\"block-listed\") bytes. */\n"
"  for (n = 0; n <= 31; n++, block_mask >>>= 1) {\n"
"    if ((block_mask & 1) && (s.dyn_ltree[n * 2]/*.Freq*/ !== 0)) {\n"
"      return Z_BINARY;\n"
"    }\n"
"  }\n"
"\n"
"  /* Check for textual (\"allow-listed\") bytes. */\n"
"  if (s.dyn_ltree[9 * 2]/*.Freq*/ !== 0 || s.dyn_ltree[10 * 2]/*.Freq*/ !== 0 ||\n"
"      s.dyn_ltree[13 * 2]/*.Freq*/ !== 0) {\n"
"    return Z_TEXT;\n"
"  }\n"
"  for (n = 32; n < LITERALS; n++) {\n"
"    if (s.dyn_ltree[n * 2]/*.Freq*/ !== 0) {\n"
"      return Z_TEXT;\n"
"    }\n"
"  }\n"
"\n"
"  /* There are no \"block-listed\" or \"allow-listed\" bytes:\n"
"   * this stream either is empty or has tolerated (\"gray-listed\") bytes only.\n"
"   */\n"
"  return Z_BINARY;\n"
"};\n"
"\n"
"\n"
"let static_init_done = false;\n"
"\n"
"/* ===========================================================================\n"
" * Initialize the tree data structures for a new zlib stream.\n"
" */\n"
"const _tr_init = (s) =>\n"
"{\n"
"\n"
"  if (!static_init_done) {\n"
"    tr_static_init();\n"
"    static_init_done = true;\n"
"  }\n"
"\n"
"  s.l_desc  = new TreeDesc(s.dyn_ltree, static_l_desc);\n"
"  s.d_desc  = new TreeDesc(s.dyn_dtree, static_d_desc);\n"
"  s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc);\n"
"\n"
"  s.bi_buf = 0;\n"
"  s.bi_valid = 0;\n"
"\n"
"  /* Initialize the first block of the first file: */\n"
"  init_block(s);\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Send a stored block\n"
" */\n"
"const _tr_stored_block = (s, buf, stored_len, last) => {\n"
"//DeflateState *s;\n"
"//charf *buf;       /* input block */\n"
"//ulg stored_len;   /* length of input block */\n"
"//int last;         /* one if this is the last block for a file */\n"
"\n"
"  send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3);    /* send block type */\n"
"  bi_windup(s);        /* align on byte boundary */\n"
"  put_short(s, stored_len);\n"
"  put_short(s, ~stored_len);\n"
"  if (stored_len) {\n"
"    s.pending_buf.set(s.window.subarray(buf, buf + stored_len), s.pending);\n"
"  }\n"
"  s.pending += stored_len;\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Send one empty static block to give enough lookahead for inflate.\n"
" * This takes 10 bits, of which 7 may remain in the bit buffer.\n"
" */\n"
"const _tr_align = (s) => {\n"
"  send_bits(s, STATIC_TREES << 1, 3);\n"
"  send_code(s, END_BLOCK, static_ltree);\n"
"  bi_flush(s);\n"
"};\n"
"\n"
"\n"
"/* ===========================================================================\n"
" * Determine the best encoding for the current block: dynamic trees, static\n"
" * trees or store, and write out the encoded block.\n"
" */\n"
"const _tr_flush_block = (s, buf, stored_len, last) => {\n"
"//DeflateState *s;\n"
"//charf *buf;       /* input block, or NULL if too old */\n"
"//ulg stored_len;   /* length of input block */\n"
"//int last;         /* one if this is the last block for a file */\n"
"\n"
"  let opt_lenb, static_lenb;  /* opt_len and static_len in bytes */\n"
"  let max_blindex = 0;        /* index of last bit length code of non zero freq */\n"
"\n"
"  /* Build the Huffman trees unless a stored block is forced */\n"
"  if (s.level > 0) {\n"
"\n"
"    /* Check if the file is binary or text */\n"
"    if (s.strm.data_type === Z_UNKNOWN) {\n"
"      s.strm.data_type = detect_data_type(s);\n"
"    }\n"
"\n"
"    /* Construct the literal and distance trees */\n"
"    build_tree(s, s.l_desc);\n"
"    // Tracev((stderr, \"\\nlit data: dyn %ld, stat %ld\", s->opt_len,\n"
"    //        s->static_len));\n"
"\n"
"    build_tree(s, s.d_desc);\n"
"    // Tracev((stderr, \"\\ndist data: dyn %ld, stat %ld\", s->opt_len,\n"
"    //        s->static_len));\n"
"    /* At this point, opt_len and static_len are the total bit lengths of\n"
"     * the compressed block data, excluding the tree representations.\n"
"     */\n"
"\n"
"    /* Build the bit length tree for the above two trees, and get the index\n"
"     * in bl_order of the last bit length code to send.\n"
"     */\n"
"    max_blindex = build_bl_tree(s);\n"
"\n"
"    /* Determine the best encoding. Compute the block lengths in bytes. */\n"
"    opt_lenb = (s.opt_len + 3 + 7) >>> 3;\n"
"    static_lenb = (s.static_len + 3 + 7) >>> 3;\n"
"\n"
"    // Tracev((stderr, \"\\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u \",\n"
"    //        opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,\n"
"    //        s->sym_next / 3));\n"
"\n"
"    if (static_lenb <= opt_lenb) { opt_lenb = static_lenb; }\n"
"\n"
"  } else {\n"
"    // Assert(buf != (char*)0, \"lost buf\");\n"
"    opt_lenb = static_lenb = stored_len + 5; /* force a stored block */\n"
"  }\n"
"\n"
"  if ((stored_len + 4 <= opt_lenb) && (buf !== -1)) {\n"
"    /* 4: two words for the lengths */\n"
"\n"
"    /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.\n"
"     * Otherwise we can't have processed more than WSIZE input bytes since\n"
"     * the last block flush, because compression would have been\n"
"     * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to\n"
"     * transform a block into a stored block.\n"
"     */\n"
"    _tr_stored_block(s, buf, stored_len, last);\n"
"\n"
"  } else if (s.strategy === Z_FIXED || static_lenb === opt_lenb) {\n"
"\n"
"    send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3);\n"
"    compress_block(s, static_ltree, static_dtree);\n"
"\n"
"  } else {\n"
"    send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3);\n"
"    send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1);\n"
"    compress_block(s, s.dyn_ltree, s.dyn_dtree);\n"
"  }\n"
"  // Assert (s->compressed_len == s->bits_sent, \"bad compressed size\");\n"
"  /* The above check is made mod 2^32, for files larger than 512 MB\n"
"   * and uLong implemented on 32 bits.\n"
"   */\n"
"  init_block(s);\n"
"\n"
"  if (last) {\n"
"    bi_windup(s);\n"
"  }\n"
"  // Tracev((stderr,\"\\ncomprlen %lu(%lu) \", s->compressed_len>>3,\n"
"  //       s->compressed_len-7*last));\n"
"};\n"
"\n"
"/* ===========================================================================\n"
" * Save the match info and tally the frequency counts. Return true if\n"
" * the current block must be flushed.\n"
" */\n"
"const _tr_tally = (s, dist, lc) => {\n"
"//    deflate_state *s;\n"
"//    unsigned dist;  /* distance of matched string */\n"
"//    unsigned lc;    /* match length-MIN_MATCH or unmatched char (if dist==0) */\n"
"\n"
"  s.pending_buf[s.sym_buf + s.sym_next++] = dist;\n"
"  s.pending_buf[s.sym_buf + s.sym_next++] = dist >> 8;\n"
"  s.pending_buf[s.sym_buf + s.sym_next++] = lc;\n"
"  if (dist === 0) {\n"
"    /* lc is the unmatched char */\n"
"    s.dyn_ltree[lc * 2]/*.Freq*/++;\n"
"  } else {\n"
"    s.matches++;\n"
"    /* Here, lc is the match length - MIN_MATCH */\n"
"    dist--;             /* dist = match distance - 1 */\n"
"    //Assert((ush)dist < (ush)MAX_DIST(s) &&\n"
"    //       (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&\n"
"    //       (ush)d_code(dist) < (ush)D_CODES,  \"_tr_tally: bad match\");\n"
"\n"
"    s.dyn_ltree[(_length_code[lc] + LITERALS + 1) * 2]/*.Freq*/++;\n"
"    s.dyn_dtree[d_code(dist) * 2]/*.Freq*/++;\n"
"  }\n"
"\n"
"  return (s.sym_next === s.sym_end);\n"
"};\n"
"\n"
"window._tr_init = _tr_init;\n"
"window._tr_stored_block = _tr_stored_block;\n"
"window._tr_flush_block = _tr_flush_block;\n"
"window._tr_tally = _tr_tally;\n"
"window._tr_align = _tr_align;\n"
"window.zero = zero;\n"
"window.STORED_BLOCK = STORED_BLOCK;\n"
"window.STATIC_TREES = STATIC_TREES;\n"
"window.DYN_TREES = DYN_TREES;\n"
"window.MIN_MATCH = MIN_MATCH;\n"
"window.MAX_MATCH = MAX_MATCH;\n"
"window.LENGTH_CODES = LENGTH_CODES;\n"
"window.LITERALS = LITERALS;\n"
"window.L_CODES = L_CODES;\n"
"window.D_CODES = D_CODES;\n"
"window.BL_CODES = BL_CODES;\n"
"window.HEAP_SIZE = HEAP_SIZE;\n"
"window.MAX_BITS = MAX_BITS;\n"
"window.Buf_size = Buf_size;\n"
"window.MAX_BL_BITS = MAX_BL_BITS;\n"
"window.END_BLOCK = END_BLOCK;\n"
"window.REP_3_6 = REP_3_6;\n"
"window.REPZ_3_10 = REPZ_3_10;\n"
"window.REPZ_11_138 = REPZ_11_138;\n"
"window.extra_lbits = extra_lbits;\n"
"window.extra_dbits = extra_dbits;\n"
"window.extra_blbits = extra_blbits;\n"
"window.bl_order = bl_order;\n"
"window.DIST_CODE_LEN = DIST_CODE_LEN;\n"
"window.static_ltree = static_ltree;\n"
"window.static_dtree = static_dtree;\n"
"window._dist_code = _dist_code;\n"
"window._length_code = _length_code;\n"
"window.base_length = base_length;\n"
"window.base_dist = base_dist;\n"
"window.StaticTreeDesc = StaticTreeDesc;\n"
"window.static_l_desc = static_l_desc;\n"
"window.static_d_desc = static_d_desc;\n"
"window.static_bl_desc = static_bl_desc;\n"
"window.TreeDesc = TreeDesc;\n"
"window.d_code = d_code;\n"
"window.put_short = put_short;\n"
"window.send_bits = send_bits;\n"
"window.send_code = send_code;\n"
"window.bi_reverse = bi_reverse;\n"
"window.bi_flush = bi_flush;\n"
"window.gen_bitlen = gen_bitlen;\n"
"window.gen_codes = gen_codes;\n"
"window.tr_static_init = tr_static_init;\n"
"window.init_block = init_block;\n"
"window.bi_windup = bi_windup;\n"
"window.smaller = smaller;\n"
"window.pqdownheap = pqdownheap;\n"
"window.compress_block = compress_block;\n"
"window.build_tree = build_tree;\n"
"window.scan_tree = scan_tree;\n"
"window.send_tree = send_tree;\n"
"window.build_bl_tree = build_bl_tree;\n"
"window.send_all_trees = send_all_trees;\n"
"window.detect_data_type = detect_data_type;\n"
"window.static_init_done = static_init_done;\n"
"window._tr_init = _tr_init;\n"
"window._tr_stored_block = _tr_stored_block;\n"
"window._tr_align = _tr_align;\n"
"window._tr_flush_block = _tr_flush_block;\n"
"window._tr_tally = _tr_tally;\n"
;
    if (strcmp(path, "/novnc/vendor/pako/lib/zlib/trees.js") == 0) return novnc_vendor_pako_lib_zlib_trees_js;
const char *novnc_vendor_pako_lib_zlib_zstream_js =
"'use strict';\n"
"\n"
"// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n"
"// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n"
"//\n"
"// This software is provided 'as-is', without any express or implied\n"
"// warranty. In no event will the authors be held liable for any damages\n"
"// arising from the use of this software.\n"
"//\n"
"// Permission is granted to anyone to use this software for any purpose,\n"
"// including commercial applications, and to alter it and redistribute it\n"
"// freely, subject to the following restrictions:\n"
"//\n"
"// 1. The origin of this software must not be misrepresented; you must not\n"
"//   claim that you wrote the original software. If you use this software\n"
"//   in a product, an acknowledgment in the product documentation would be\n"
"//   appreciated but is not required.\n"
"// 2. Altered source versions must be plainly marked as such, and must not be\n"
"//   misrepresented as being the original software.\n"
"// 3. This notice may not be removed or altered from any source distribution.\n"
"\n"
"function ZStream() {\n"
"  /* next input byte */\n"
"  this.input = null; // JS specific, because we have no pointers\n"
"  this.next_in = 0;\n"
"  /* number of bytes available at input */\n"
"  this.avail_in = 0;\n"
"  /* total number of input bytes read so far */\n"
"  this.total_in = 0;\n"
"  /* next output byte should be put there */\n"
"  this.output = null; // JS specific, because we have no pointers\n"
"  this.next_out = 0;\n"
"  /* remaining free space at output */\n"
"  this.avail_out = 0;\n"
"  /* total number of bytes output so far */\n"
"  this.total_out = 0;\n"
"  /* last error message, NULL if no error */\n"
"  this.msg = ''/*Z_NULL*/;\n"
"  /* not visible by applications */\n"
"  this.state = null;\n"
"  /* best guess about the data type: binary or text */\n"
"  this.data_type = 2/*Z_UNKNOWN*/;\n"
"  /* adler32 value of the uncompressed data */\n"
"  this.adler = 0;\n"
"}\n"
"\n"
"window.ZStream;\n"
;
    if (strcmp(path, "/novnc/vendor/pako/lib/zlib/zstream.js") == 0) return novnc_vendor_pako_lib_zlib_zstream_js;
const char *novnc_websock_js =
"/*\n"
" * Websock: high-performance buffering wrapper\n"
" * Copyright (C) 2019 The noVNC Authors\n"
" * Licensed under MPL 2.0 (see LICENSE.txt)\n"
" *\n"
" * Websock is similar to the standard WebSocket / RTCDataChannel object\n"
" * but with extra buffer handling.\n"
" *\n"
" * Websock has built-in receive queue buffering; the message event\n"
" * does not contain actual data but is simply a notification that\n"
" * there is new data available. Several rQ* methods are available to\n"
" * read binary data off of the receive queue.\n"
" */\n"
"\n"
"\n"
"// this has performance issues in some versions Chromium, and\n"
"// doesn't gain a tremendous amount of performance increase in Firefox\n"
"// at the moment.  It may be valuable to turn it on in the future.\n"
"const MAX_RQ_GROW_SIZE = 40 * 1024 * 1024;  // 40 MiB\n"
"\n"
"// Constants pulled from RTCDataChannelState enum\n"
"// https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel/readyState#RTCDataChannelState_enum\n"
"const DataChannel = {\n"
"    CONNECTING: \"connecting\",\n"
"    OPEN: \"open\",\n"
"    CLOSING: \"closing\",\n"
"    CLOSED: \"closed\"\n"
"};\n"
"\n"
"const ReadyStates = {\n"
"    CONNECTING: [WebSocket.CONNECTING, DataChannel.CONNECTING],\n"
"    OPEN: [WebSocket.OPEN, DataChannel.OPEN],\n"
"    CLOSING: [WebSocket.CLOSING, DataChannel.CLOSING],\n"
"    CLOSED: [WebSocket.CLOSED, DataChannel.CLOSED],\n"
"};\n"
"\n"
"// Properties a raw channel must have, WebSocket and RTCDataChannel are two examples\n"
"const rawChannelProps = [\n"
"    \"send\",\n"
"    \"close\",\n"
"    \"binaryType\",\n"
"    \"onerror\",\n"
"    \"onmessage\",\n"
"    \"onopen\",\n"
"    \"protocol\",\n"
"    \"readyState\",\n"
"];\n"
"\n"
"class Websock {\n"
"    constructor() {\n"
"        this._websocket = null;  // WebSocket or RTCDataChannel object\n"
"\n"
"        this._rQi = 0;           // Receive queue index\n"
"        this._rQlen = 0;         // Next write position in the receive queue\n"
"        this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)\n"
"        // called in init: this._rQ = new Uint8Array(this._rQbufferSize);\n"
"        this._rQ = null; // Receive queue\n"
"\n"
"        this._sQbufferSize = 1024 * 10;  // 10 KiB\n"
"        // called in init: this._sQ = new Uint8Array(this._sQbufferSize);\n"
"        this._sQlen = 0;\n"
"        this._sQ = null;  // Send queue\n"
"\n"
"        this._socketFrameSeq = 0; // Sequence counter for socket debugging\n"
"\n"
"        this._eventHandlers = {\n"
"            message: () => {},\n"
"            open: () => {},\n"
"            close: () => {},\n"
"            error: () => {}\n"
"        };\n"
"    }\n"
"\n"
"    // Getters and Setters\n"
"\n"
"    get readyState() {\n"
"        let subState;\n"
"\n"
"        if (this._websocket === null) {\n"
"            return \"unused\";\n"
"        }\n"
"\n"
"        subState = this._websocket.readyState;\n"
"\n"
"        if (ReadyStates.CONNECTING.includes(subState)) {\n"
"            return \"connecting\";\n"
"        } else if (ReadyStates.OPEN.includes(subState)) {\n"
"            return \"open\";\n"
"        } else if (ReadyStates.CLOSING.includes(subState)) {\n"
"            return \"closing\";\n"
"        } else if (ReadyStates.CLOSED.includes(subState)) {\n"
"            return \"closed\";\n"
"        }\n"
"\n"
"        return \"unknown\";\n"
"    }\n"
"\n"
"    get sQ() {\n"
"        return this._sQ;\n"
"    }\n"
"\n"
"    get rQ() {\n"
"        return this._rQ;\n"
"    }\n"
"\n"
"    get rQi() {\n"
"        return this._rQi;\n"
"    }\n"
"\n"
"    set rQi(val) {\n"
"        this._rQi = val;\n"
"    }\n"
"\n"
"    // Receive Queue\n"
"    get rQlen() {\n"
"        return this._rQlen - this._rQi;\n"
"    }\n"
"\n"
"    rQpeek8() {\n"
"        return this._rQ[this._rQi];\n"
"    }\n"
"\n"
"    rQskipBytes(bytes) {\n"
"        this._rQi += bytes;\n"
"    }\n"
"\n"
"    rQshift8() {\n"
"        return this._rQshift(1);\n"
"    }\n"
"\n"
"    rQshift16() {\n"
"        return this._rQshift(2);\n"
"    }\n"
"\n"
"    rQshift32() {\n"
"        return this._rQshift(4);\n"
"    }\n"
"\n"
"    // TODO(directxman12): test performance with these vs a DataView\n"
"    _rQshift(bytes) {\n"
"        let res = 0;\n"
"        for (let byte = bytes - 1; byte >= 0; byte--) {\n"
"            res += this._rQ[this._rQi++] << (byte * 8);\n"
"        }\n"
"        return res;\n"
"    }\n"
"\n"
"    rQshiftStr(len) {\n"
"        if (typeof(len) === 'undefined') { len = this.rQlen; }\n"
"        let str = \"\";\n"
"        // Handle large arrays in steps to avoid long strings on the stack\n"
"        for (let i = 0; i < len; i += 4096) {\n"
"            let part = this.rQshiftBytes(Math.min(4096, len - i));\n"
"            str += String.fromCharCode.apply(null, part);\n"
"        }\n"
"        return str;\n"
"    }\n"
"\n"
"    rQshiftBytes(len) {\n"
"        if (typeof(len) === 'undefined') { len = this.rQlen; }\n"
"        this._rQi += len;\n"
"        return new Uint8Array(this._rQ.buffer, this._rQi - len, len);\n"
"    }\n"
"\n"
"    rQshiftTo(target, len) {\n"
"        if (len === undefined) { len = this.rQlen; }\n"
"        // TODO: make this just use set with views when using a ArrayBuffer to store the rQ\n"
"        target.set(new Uint8Array(this._rQ.buffer, this._rQi, len));\n"
"        this._rQi += len;\n"
"    }\n"
"\n"
"    rQslice(start, end = this.rQlen) {\n"
"        return new Uint8Array(this._rQ.buffer, this._rQi + start, end - start);\n"
"    }\n"
"\n"
"    // Check to see if we must wait for 'num' bytes (default to FBU.bytes)\n"
"    // to be available in the receive queue. Return true if we need to\n"
"    // wait (and possibly print a debug message), otherwise false.\n"
"    rQwait(msg, num, goback) {\n"
"        if (this.rQlen < num) {\n"
"            if (goback) {\n"
"                if (this._rQi < goback) {\n"
"                    throw new Error(\"rQwait cannot backup \" + goback + \" bytes\");\n"
"                }\n"
"                this._rQi -= goback;\n"
"            }\n"
"            return true; // true means need more data\n"
"        }\n"
"        return false;\n"
"    }\n"
"\n"
"    // Send Queue\n"
"\n"
"    flush() {\n"
"        if (this._sQlen > 0 && this.readyState === 'open') {\n"
"            this._websocket.send(this._encodeMessage());\n"
"            this._sQlen = 0;\n"
"        }\n"
"    }\n"
"\n"
"    send(arr) {\n"
"        this._sQ.set(arr, this._sQlen);\n"
"        this._sQlen += arr.length;\n"
"        this.flush();\n"
"    }\n"
"\n"
"    sendString(str) {\n"
"        this.send(str.split('').map(chr => chr.charCodeAt(0)));\n"
"    }\n"
"\n"
"    // Event Handlers\n"
"    off(evt) {\n"
"        this._eventHandlers[evt] = () => {};\n"
"    }\n"
"\n"
"    on(evt, handler) {\n"
"        this._eventHandlers[evt] = handler;\n"
"    }\n"
"\n"
"    _allocateBuffers() {\n"
"        this._rQ = new Uint8Array(this._rQbufferSize);\n"
"        this._sQ = new Uint8Array(this._sQbufferSize);\n"
"    }\n"
"\n"
"    init() {\n"
"        this._allocateBuffers();\n"
"        this._rQi = 0;\n"
"        this._websocket = null;\n"
"    }\n"
"\n"
"    open(uri, protocols) {\n"
"        this.attach(new WebSocket(uri, protocols));\n"
"    }\n"
"\n"
"    attach(rawChannel) {\n"
"        this.init();\n"
"\n"
"        // Must get object and class methods to be compatible with the tests.\n"
"        const channelProps = [...Object.keys(rawChannel), ...Object.getOwnPropertyNames(Object.getPrototypeOf(rawChannel))];\n"
"        for (let i = 0; i < rawChannelProps.length; i++) {\n"
"            const prop = rawChannelProps[i];\n"
"            if (channelProps.indexOf(prop) < 0) {\n"
"                throw new Error('Raw channel missing property: ' + prop);\n"
"            }\n"
"        }\n"
"\n"
"        this._websocket = rawChannel;\n"
"        this._websocket.binaryType = \"arraybuffer\";\n"
"        this._websocket.onmessage = this._recvMessage.bind(this);\n"
"\n"
"        this._websocket.onopen = () => {\n"
"            Log.Debug('>> WebSock.onopen');\n"
"            if (this._websocket.protocol) {\n"
"                Log.Info(\"Server choose sub-protocol: \" + this._websocket.protocol);\n"
"            }\n"
"\n"
"            this._eventHandlers.open();\n"
"            Log.Debug(\"<< WebSock.onopen\");\n"
"        };\n"
"\n"
"        this._websocket.onclose = (e) => {\n"
"            Log.Debug(\">> WebSock.onclose\");\n"
"            this._eventHandlers.close(e);\n"
"            Log.Debug(\"<< WebSock.onclose\");\n"
"        };\n"
"\n"
"        this._websocket.onerror = (e) => {\n"
"            Log.Debug(\">> WebSock.onerror: \" + e);\n"
"            this._eventHandlers.error(e);\n"
"            Log.Debug(\"<< WebSock.onerror: \" + e);\n"
"        };\n"
"    }\n"
"\n"
"    close() {\n"
"        if (this._websocket) {\n"
"            if (this.readyState === 'connecting' ||\n"
"                this.readyState === 'open') {\n"
"                Log.Info(\"Closing WebSocket connection\");\n"
"                this._websocket.close();\n"
"            }\n"
"\n"
"            this._websocket.onmessage = () => {};\n"
"        }\n"
"    }\n"
"\n"
"    // private methods\n"
"    _encodeMessage() {\n"
"        // Put in a binary arraybuffer\n"
"        // according to the spec, you can send ArrayBufferViews with the send method\n"
"        return new Uint8Array(this._sQ.buffer, 0, this._sQlen);\n"
"    }\n"
"\n"
"    // We want to move all the unread data to the start of the queue,\n"
"    // e.g. compacting.\n"
"    // The function also expands the receive que if needed, and for\n"
"    // performance reasons we combine these two actions to avoid\n"
"    // unneccessary copying.\n"
"    _expandCompactRQ(minFit) {\n"
"        // if we're using less than 1/8th of the buffer even with the incoming bytes, compact in place\n"
"        // instead of resizing\n"
"        const requiredBufferSize =  (this._rQlen - this._rQi + minFit) * 8;\n"
"        const resizeNeeded = this._rQbufferSize < requiredBufferSize;\n"
"\n"
"        if (resizeNeeded) {\n"
"            // Make sure we always *at least* double the buffer size, and have at least space for 8x\n"
"            // the current amount of data\n"
"            this._rQbufferSize = Math.max(this._rQbufferSize * 2, requiredBufferSize);\n"
"        }\n"
"\n"
"        // we don't want to grow unboundedly\n"
"        if (this._rQbufferSize > MAX_RQ_GROW_SIZE) {\n"
"            this._rQbufferSize = MAX_RQ_GROW_SIZE;\n"
"            if (this._rQbufferSize - this.rQlen < minFit) {\n"
"                throw new Error(\"Receive Queue buffer exceeded \" + MAX_RQ_GROW_SIZE + \" bytes, and the new message could not fit\");\n"
"            }\n"
"        }\n"
"\n"
"        if (resizeNeeded) {\n"
"            const oldRQbuffer = this._rQ.buffer;\n"
"            this._rQ = new Uint8Array(this._rQbufferSize);\n"
"            this._rQ.set(new Uint8Array(oldRQbuffer, this._rQi, this._rQlen - this._rQi));\n"
"        } else {\n"
"            this._rQ.copyWithin(0, this._rQi, this._rQlen);\n"
"        }\n"
"\n"
"        this._rQlen = this._rQlen - this._rQi;\n"
"        this._rQi = 0;\n"
"    }\n"
"\n"
"    // push arraybuffer values onto the end of the receive que\n"
"    _DecodeMessage(data) {\n"
"        const u8 = new Uint8Array(data);\n"
"        if (u8.length > this._rQbufferSize - this._rQlen) {\n"
"            this._expandCompactRQ(u8.length);\n"
"        }\n"
"        this._rQ.set(u8, this._rQlen);\n"
"        this._rQlen += u8.length;\n"
"    }\n"
"\n"
"    _recvMessage(e) {\n"
"        this._DecodeMessage(e.data);\n"
"\n"
"        if (this.rQlen > 0) {\n"
"            this._eventHandlers.message();\n"
"            if (this._rQlen == this._rQi) {\n"
"                // All data has now been processed, this means we\n"
"                // can reset the receive queue.\n"
"                this._rQlen = 0;\n"
"                this._rQi = 0;\n"
"            }\n"
"        } else {\n"
"            Log.Debug(\"Ignoring empty message\");\n"
"        }\n"
"    }\n"
"}\n"
"window.MAX_RQ_GROW_SIZE = MAX_RQ_GROW_SIZE;\n"
"window.DataChannel = DataChannel;\n"
"window.ReadyStates = ReadyStates;\n"
"window.rawChannelProps = rawChannelProps;\n"
"window.Websock = Websock;\n"
;
    if (strcmp(path, "/novnc/websock.js") == 0) return novnc_websock_js;
    return NULL;
}
