├── .gitmodules
├── po
├── .eslintrc
├── Makefile
├── po2js
└── xgettext-html
├── snap
├── hooks
│ └── configure
├── local
│ └── svc_wrapper.sh
└── snapcraft.yaml
├── .envrc
├── app
├── sounds
│ ├── bell.mp3
│ ├── bell.oga
│ └── CREDITS
├── styles
│ ├── Orbitron700.ttf
│ └── Orbitron700.woff
├── locale
│ ├── README
│ ├── zh_CN.json
│ ├── zh_TW.json
│ ├── ko.json
│ ├── ja.json
│ ├── es.json
│ ├── tr.json
│ ├── sv.json
│ ├── pl.json
│ ├── cs.json
│ ├── de.json
│ ├── pt_BR.json
│ ├── el.json
│ ├── ru.json
│ └── nl.json
├── images
│ ├── icons
│ │ ├── novnc-120x120.png
│ │ ├── novnc-144x144.png
│ │ ├── novnc-152x152.png
│ │ ├── novnc-16x16.png
│ │ ├── novnc-192x192.png
│ │ ├── novnc-24x24.png
│ │ ├── novnc-32x32.png
│ │ ├── novnc-48x48.png
│ │ ├── novnc-60x60.png
│ │ ├── novnc-64x64.png
│ │ ├── novnc-72x72.png
│ │ ├── novnc-76x76.png
│ │ ├── novnc-96x96.png
│ │ └── Makefile
│ ├── windows.svg
│ ├── handle.svg
│ ├── tab.svg
│ ├── expander.svg
│ ├── settings.svg
│ ├── error.svg
│ ├── fullscreen.svg
│ ├── info.svg
│ ├── ctrlaltdel.svg
│ ├── connect.svg
│ ├── alt.svg
│ ├── warning.svg
│ ├── power.svg
│ └── clipboard.svg
└── error-handler.js
├── docs
├── rfbproto-3.3.pdf
├── rfbproto-3.7.pdf
├── rfbproto-3.8.pdf
├── notes
├── flash_policy.txt
├── LIBRARY.md
├── LICENSE.BSD-2-Clause
├── LICENSE.BSD-3-Clause
├── links
├── EMBEDDING.md
├── rfb_notes
└── API-internal.md
├── utils
├── .eslintrc
├── b64-to-binary.pl
├── README.md
├── validate
├── u2x11
└── genkeysymdef.js
├── .gitignore
├── .github
├── ISSUE_TEMPLATE
│ ├── config.yml
│ ├── feature_request.md
│ └── bug_report.md
└── workflows
│ ├── lint.yml
│ ├── test.yml
│ └── deploy.yml
├── vendor
└── pako
│ ├── README.md
│ ├── lib
│ ├── zlib
│ │ ├── messages.js
│ │ ├── adler32.js
│ │ ├── crc32.js
│ │ ├── zstream.js
│ │ ├── gzheader.js
│ │ └── constants.js
│ └── utils
│ │ └── common.js
│ └── LICENSE
├── tests
├── .eslintrc
├── test.int.js
├── vnc_playback.html
├── test.base64.js
├── fake.websocket.js
├── test.deflator.js
├── test.localization.js
├── test.copyrect.js
├── test.util.js
├── test.rre.js
└── assertions.js
├── core
├── util
│ ├── int.js
│ ├── strings.js
│ ├── element.js
│ ├── eventtarget.js
│ ├── logging.js
│ └── browser.js
├── decoders
│ ├── copyrect.js
│ ├── tightpng.js
│ ├── rre.js
│ └── raw.js
├── encodings.js
├── inflator.js
├── deflator.js
└── input
│ ├── vkeys.js
│ └── fixedkeys.js
├── AUTHORS
├── flake.nix
├── flake.lock
├── LICENSE.txt
├── package.json
├── karma.conf.js
└── eslint.config.mjs
/.gitmodules:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/po/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "node": true,
4 | },
5 | }
6 |
--------------------------------------------------------------------------------
/snap/hooks/configure:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 |
3 | snapctl restart novnc.novncsvc
4 |
--------------------------------------------------------------------------------
/.envrc:
--------------------------------------------------------------------------------
1 | use flake
2 | dotenv_if_exists
3 | source_env_if_exists .local.envrc
4 |
--------------------------------------------------------------------------------
/app/sounds/bell.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/app/sounds/bell.mp3
--------------------------------------------------------------------------------
/app/sounds/bell.oga:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/app/sounds/bell.oga
--------------------------------------------------------------------------------
/docs/rfbproto-3.3.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/docs/rfbproto-3.3.pdf
--------------------------------------------------------------------------------
/docs/rfbproto-3.7.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/docs/rfbproto-3.7.pdf
--------------------------------------------------------------------------------
/docs/rfbproto-3.8.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/docs/rfbproto-3.8.pdf
--------------------------------------------------------------------------------
/app/styles/Orbitron700.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/app/styles/Orbitron700.ttf
--------------------------------------------------------------------------------
/app/styles/Orbitron700.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/app/styles/Orbitron700.woff
--------------------------------------------------------------------------------
/app/locale/README:
--------------------------------------------------------------------------------
1 | DO NOT MODIFY THE FILES IN THIS FOLDER, THEY ARE AUTOMATICALLY GENERATED FROM THE PO-FILES.
2 |
--------------------------------------------------------------------------------
/app/images/icons/novnc-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/app/images/icons/novnc-120x120.png
--------------------------------------------------------------------------------
/app/images/icons/novnc-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/app/images/icons/novnc-144x144.png
--------------------------------------------------------------------------------
/app/images/icons/novnc-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/app/images/icons/novnc-152x152.png
--------------------------------------------------------------------------------
/app/images/icons/novnc-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/app/images/icons/novnc-16x16.png
--------------------------------------------------------------------------------
/app/images/icons/novnc-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/app/images/icons/novnc-192x192.png
--------------------------------------------------------------------------------
/app/images/icons/novnc-24x24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/app/images/icons/novnc-24x24.png
--------------------------------------------------------------------------------
/app/images/icons/novnc-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/app/images/icons/novnc-32x32.png
--------------------------------------------------------------------------------
/app/images/icons/novnc-48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/app/images/icons/novnc-48x48.png
--------------------------------------------------------------------------------
/app/images/icons/novnc-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/app/images/icons/novnc-60x60.png
--------------------------------------------------------------------------------
/app/images/icons/novnc-64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/app/images/icons/novnc-64x64.png
--------------------------------------------------------------------------------
/app/images/icons/novnc-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/app/images/icons/novnc-72x72.png
--------------------------------------------------------------------------------
/app/images/icons/novnc-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/app/images/icons/novnc-76x76.png
--------------------------------------------------------------------------------
/app/images/icons/novnc-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/replit/noVNC/master/app/images/icons/novnc-96x96.png
--------------------------------------------------------------------------------
/utils/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "node": true
4 | },
5 | "rules": {
6 | "no-console": 0
7 | }
8 | }
--------------------------------------------------------------------------------
/docs/notes:
--------------------------------------------------------------------------------
1 | Rebuilding inflator.js
2 |
3 | - Download pako from npm
4 | - Install browserify using npm
5 | - browserify core/inflator.mod.js -o core/inflator.js -s Inflator
6 |
--------------------------------------------------------------------------------
/app/sounds/CREDITS:
--------------------------------------------------------------------------------
1 | bell
2 | Copyright: Dr. Richard Boulanger et al
3 | URL: http://www.archive.org/details/Berklee44v12
4 | License: CC-BY Attribution 3.0 Unported
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.o
3 | tests/data_*.js
4 | utils/rebind.so
5 | utils/websockify
6 | /node_modules
7 | /build
8 | /lib
9 | /.direnv/
10 | recordings
11 | *.swp
12 | *~
13 | noVNC-*.tgz
14 |
--------------------------------------------------------------------------------
/docs/flash_policy.txt:
--------------------------------------------------------------------------------
1 | Manual setup:
2 |
3 | DATA="echo \'\'"
4 | /usr/bin/socat -T 1 TCP-L:843,reuseaddr,fork,crlf SYSTEM:"$DATA"
5 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Question or discussion
4 | url: https://groups.google.com/forum/?fromgroups#!forum/novnc
5 | about: Ask a question or start a discussion
6 |
--------------------------------------------------------------------------------
/vendor/pako/README.md:
--------------------------------------------------------------------------------
1 | This is an ES6-modules-compatible version of
2 | https://github.com/nodeca/pako, based on pako version 1.0.3.
3 |
4 | It's more-or-less a direct translation of the original, with unused parts
5 | removed, and the dynamic support for non-typed arrays removed (since ES6
6 | modules don't work well with dynamic exports).
7 |
--------------------------------------------------------------------------------
/tests/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "node": true,
4 | "mocha": true
5 | },
6 | "globals": {
7 | "chai": false,
8 | "sinon": false
9 | },
10 | "rules": {
11 | "prefer-arrow-callback": 0,
12 | // Too many anonymous callbacks
13 | "func-names": "off",
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/core/util/int.js:
--------------------------------------------------------------------------------
1 | /*
2 | * noVNC: HTML5 VNC client
3 | * Copyright (C) 2020 The noVNC Authors
4 | * Licensed under MPL 2.0 (see LICENSE.txt)
5 | *
6 | * See README.md for usage and integration instructions.
7 | */
8 |
9 | export function toUnsigned32bit(toConvert) {
10 | return toConvert >>> 0;
11 | }
12 |
13 | export function toSigned32bit(toConvert) {
14 | return toConvert | 0;
15 | }
16 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | maintainers:
2 | - Joel Martin (@kanaka)
3 | - Solly Ross (@directxman12)
4 | - Samuel Mannehed for Cendio AB (@samhed)
5 | - Pierre Ossman for Cendio AB (@CendioOssman)
6 | maintainersEmeritus:
7 | - @astrand
8 | contributors:
9 | # There are a bunch of people that should be here.
10 | # If you want to be on this list, feel free send a PR
11 | # to add yourself.
12 | - jalf
13 | - NTT corp.
14 |
--------------------------------------------------------------------------------
/utils/b64-to-binary.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 | use MIME::Base64;
3 |
4 | for (<>) {
5 | unless (/^'([{}])(\d+)\1(.+?)',$/) {
6 | print;
7 | next;
8 | }
9 |
10 | my ($dir, $amt, $b64) = ($1, $2, $3);
11 |
12 | my $decoded = MIME::Base64::decode($b64) or die "Could not base64-decode line `$_`";
13 |
14 | my $decoded_escaped = join "", map { "\\x$_" } unpack("(H2)*", $decoded);
15 |
16 | print "'${dir}${amt}${dir}${decoded_escaped}',\n";
17 | }
18 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | eslint:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v4
10 | - uses: actions/setup-node@v4
11 | - run: npm install
12 | - run: npm run lint
13 | html:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v4
17 | - uses: actions/setup-node@v4
18 | - run: npm install
19 | - run: git ls-tree --name-only -r HEAD | grep -E "[.](html|css)$" | xargs ./utils/validate
20 |
--------------------------------------------------------------------------------
/tests/test.int.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | const expect = chai.expect;
3 |
4 | import { toUnsigned32bit, toSigned32bit } from '../core/util/int.js';
5 |
6 | describe('Integer casting', function () {
7 | it('should cast unsigned to signed', function () {
8 | let expected = 4294967286;
9 | expect(toUnsigned32bit(-10)).to.equal(expected);
10 | });
11 |
12 | it('should cast signed to unsigned', function () {
13 | let expected = -10;
14 | expect(toSigned32bit(4294967286)).to.equal(expected);
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/utils/README.md:
--------------------------------------------------------------------------------
1 | ## WebSockets Proxy/Bridge
2 |
3 | Websockify has been forked out into its own project. `launch.sh` wil
4 | automatically download it here if it is not already present and not
5 | installed as system-wide.
6 |
7 | For more detailed description and usage information please refer to
8 | the [websockify README](https://github.com/novnc/websockify/blob/master/README.md).
9 |
10 | The other versions of websockify (C, Node.js) and the associated test
11 | programs have been moved to
12 | [websockify](https://github.com/novnc/websockify). Websockify was
13 | formerly named wsproxy.
14 |
15 |
--------------------------------------------------------------------------------
/vendor/pako/lib/zlib/messages.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 2: 'need dictionary', /* Z_NEED_DICT 2 */
3 | 1: 'stream end', /* Z_STREAM_END 1 */
4 | 0: '', /* Z_OK 0 */
5 | '-1': 'file error', /* Z_ERRNO (-1) */
6 | '-2': 'stream error', /* Z_STREAM_ERROR (-2) */
7 | '-3': 'data error', /* Z_DATA_ERROR (-3) */
8 | '-4': 'insufficient memory', /* Z_MEM_ERROR (-4) */
9 | '-5': 'buffer error', /* Z_BUF_ERROR (-5) */
10 | '-6': 'incompatible version' /* Z_VERSION_ERROR (-6) */
11 | };
12 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 |
5 | ---
6 |
7 | **Is your feature request related to a problem? Please describe.**
8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
9 |
10 | **Describe the solution you'd like**
11 | A clear and concise description of what you want to happen.
12 |
13 | **Describe alternatives you've considered**
14 | A clear and concise description of any alternative solutions or features you've considered.
15 |
16 | **Additional context**
17 | Add any other context or screenshots about the feature request here.
18 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | test:
7 | strategy:
8 | matrix:
9 | os:
10 | - ubuntu-latest
11 | - windows-latest
12 | browser:
13 | - ChromeHeadless
14 | - FirefoxHeadless
15 | include:
16 | - os: macos-latest
17 | browser: Safari
18 | - os: windows-latest
19 | browser: EdgeHeadless
20 | fail-fast: false
21 | runs-on: ${{ matrix.os }}
22 | steps:
23 | - uses: actions/checkout@v4
24 | - uses: actions/setup-node@v4
25 | - run: npm install
26 | - run: npm run test
27 | env:
28 | TEST_BROWSER_NAME: ${{ matrix.browser }}
29 |
--------------------------------------------------------------------------------
/core/decoders/copyrect.js:
--------------------------------------------------------------------------------
1 | /*
2 | * noVNC: HTML5 VNC client
3 | * Copyright (C) 2019 The noVNC Authors
4 | * Licensed under MPL 2.0 (see LICENSE.txt)
5 | *
6 | * See README.md for usage and integration instructions.
7 | *
8 | */
9 |
10 | export default class CopyRectDecoder {
11 | decodeRect(x, y, width, height, sock, display, depth) {
12 | if (sock.rQwait("COPYRECT", 4)) {
13 | return false;
14 | }
15 |
16 | let deltaX = sock.rQshift16();
17 | let deltaY = sock.rQshift16();
18 |
19 | if ((width === 0) || (height === 0)) {
20 | return true;
21 | }
22 |
23 | display.copyImage(deltaX, deltaY, x, y, width, height);
24 |
25 | return true;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/vnc_playback.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | VNC Playback
5 |
6 |
7 |
8 |
9 | Iterations:
10 | Perftest:
11 | Realtime:
12 |
13 |
14 |
15 |
16 |
17 | Results:
18 |
19 |
20 |
21 |
22 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/vendor/pako/lib/zlib/adler32.js:
--------------------------------------------------------------------------------
1 | // Note: adler32 takes 12% for level 0 and 2% for level 6.
2 | // It doesn't worth to make additional optimizationa as in original.
3 | // Small size is preferable.
4 |
5 | export default function adler32(adler, buf, len, pos) {
6 | var s1 = (adler & 0xffff) |0,
7 | s2 = ((adler >>> 16) & 0xffff) |0,
8 | n = 0;
9 |
10 | while (len !== 0) {
11 | // Set limit ~ twice less than 5552, to keep
12 | // s2 in 31-bits, because we force signed ints.
13 | // in other case %= will fail.
14 | n = len > 2000 ? 2000 : len;
15 | len -= n;
16 |
17 | do {
18 | s1 = (s1 + buf[pos++]) |0;
19 | s2 = (s2 + s1) |0;
20 | } while (--n);
21 |
22 | s1 %= 65521;
23 | s2 %= 65521;
24 | }
25 |
26 | return (s1 | (s2 << 16)) |0;
27 | }
28 |
--------------------------------------------------------------------------------
/core/decoders/tightpng.js:
--------------------------------------------------------------------------------
1 | /*
2 | * noVNC: HTML5 VNC client
3 | * Copyright (C) 2019 The noVNC Authors
4 | * Licensed under MPL 2.0 (see LICENSE.txt)
5 | *
6 | * See README.md for usage and integration instructions.
7 | *
8 | */
9 |
10 | import TightDecoder from './tight.js';
11 |
12 | export default class TightPNGDecoder extends TightDecoder {
13 | _pngRect(x, y, width, height, sock, display, depth) {
14 | let data = this._readData(sock);
15 | if (data === null) {
16 | return false;
17 | }
18 |
19 | display.imageRect(x, y, width, height, "image/png", data);
20 |
21 | return true;
22 | }
23 |
24 | _basicRect(ctl, x, y, width, height, sock, display, depth) {
25 | throw new Error("BasicCompression received in TightPNG rect");
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/utils/validate:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | RET=0
6 |
7 | OUT=`mktemp`
8 |
9 | for fn in "$@"; do
10 | echo "Validating $fn..."
11 | echo
12 |
13 | case $fn in
14 | *.html)
15 | type="text/html"
16 | ;;
17 | *.css)
18 | type="text/css"
19 | ;;
20 | *)
21 | echo "Unknown format!"
22 | echo
23 | RET=1
24 | continue
25 | ;;
26 | esac
27 |
28 | curl --silent \
29 | --header "Content-Type: ${type}; charset=utf-8" \
30 | --data-binary @${fn} \
31 | https://validator.w3.org/nu/?out=text > $OUT
32 | cat $OUT
33 | echo
34 |
35 | # We don't fail the check for warnings as some warnings are
36 | # not relevant for us, and we don't currently have a way to
37 | # ignore just those
38 | if grep -q -s -E "^Error:" $OUT; then
39 | RET=1
40 | fi
41 | done
42 |
43 | rm $OUT
44 |
45 | exit $RET
46 |
--------------------------------------------------------------------------------
/core/util/strings.js:
--------------------------------------------------------------------------------
1 | /*
2 | * noVNC: HTML5 VNC client
3 | * Copyright (C) 2019 The noVNC Authors
4 | * Licensed under MPL 2.0 (see LICENSE.txt)
5 | *
6 | * See README.md for usage and integration instructions.
7 | */
8 |
9 | // Decode from UTF-8
10 | export function decodeUTF8(utf8string, allowLatin1=false) {
11 | try {
12 | return decodeURIComponent(escape(utf8string));
13 | } catch (e) {
14 | if (e instanceof URIError) {
15 | if (allowLatin1) {
16 | // If we allow Latin1 we can ignore any decoding fails
17 | // and in these cases return the original string
18 | return utf8string;
19 | }
20 | }
21 | throw e;
22 | }
23 | }
24 |
25 | // Encode to UTF-8
26 | export function encodeUTF8(DOMString) {
27 | return unescape(encodeURIComponent(DOMString));
28 | }
29 |
--------------------------------------------------------------------------------
/core/util/element.js:
--------------------------------------------------------------------------------
1 | /*
2 | * noVNC: HTML5 VNC client
3 | * Copyright (C) 2020 The noVNC Authors
4 | * Licensed under MPL 2.0 (see LICENSE.txt)
5 | *
6 | * See README.md for usage and integration instructions.
7 | */
8 |
9 | /*
10 | * HTML element utility functions
11 | */
12 |
13 | export function clientToElement(x, y, elem) {
14 | const bounds = elem.getBoundingClientRect();
15 | let pos = { x: 0, y: 0 };
16 | // Clip to target bounds
17 | if (x < bounds.left) {
18 | pos.x = 0;
19 | } else if (x >= bounds.right) {
20 | pos.x = bounds.width - 1;
21 | } else {
22 | pos.x = x - bounds.left;
23 | }
24 | if (y < bounds.top) {
25 | pos.y = 0;
26 | } else if (y >= bounds.bottom) {
27 | pos.y = bounds.height - 1;
28 | } else {
29 | pos.y = y - bounds.top;
30 | }
31 | return pos;
32 | }
33 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | description = "noVNC is a web-based VNC client";
3 |
4 | inputs = {
5 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
6 | };
7 |
8 | outputs = { self, nixpkgs, flake-utils }:
9 | let
10 | systems = [
11 | "x86_64-linux"
12 | "aarch64-darwin"
13 | ];
14 |
15 | eachSystem = nixpkgs.lib.genAttrs systems;
16 |
17 | mkPkgs = system: import nixpkgs {
18 | inherit system;
19 | };
20 | in
21 | {
22 | packages = eachSystem (system:
23 | let
24 | pkgs = mkPkgs system;
25 | in rec {
26 | devShell = pkgs.mkShell {
27 | packages = [
28 | pkgs.nodejs_20
29 | pkgs.chromedriver
30 | pkgs.chromium
31 | ];
32 | };
33 | });
34 |
35 | devShells = eachSystem (system: {
36 | default = self.packages.${system}.devShell;
37 | });
38 | };
39 | }
40 |
--------------------------------------------------------------------------------
/vendor/pako/lib/zlib/crc32.js:
--------------------------------------------------------------------------------
1 | // Note: we can't get significant speed boost here.
2 | // So write code to minimize size - no pregenerated tables
3 | // and array tools dependencies.
4 |
5 |
6 | // Use ordinary array, since untyped makes no boost here
7 | export default function makeTable() {
8 | var c, table = [];
9 |
10 | for (var n = 0; n < 256; n++) {
11 | c = n;
12 | for (var k = 0; k < 8; k++) {
13 | c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
14 | }
15 | table[n] = c;
16 | }
17 |
18 | return table;
19 | }
20 |
21 | // Create table on load. Just 255 signed longs. Not a problem.
22 | var crcTable = makeTable();
23 |
24 |
25 | function crc32(crc, buf, len, pos) {
26 | var t = crcTable,
27 | end = pos + len;
28 |
29 | crc ^= -1;
30 |
31 | for (var i = pos; i < end; i++) {
32 | crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF];
33 | }
34 |
35 | return (crc ^ (-1)); // >>> 0;
36 | }
37 |
--------------------------------------------------------------------------------
/vendor/pako/lib/zlib/zstream.js:
--------------------------------------------------------------------------------
1 | export default function ZStream() {
2 | /* next input byte */
3 | this.input = null; // JS specific, because we have no pointers
4 | this.next_in = 0;
5 | /* number of bytes available at input */
6 | this.avail_in = 0;
7 | /* total number of input bytes read so far */
8 | this.total_in = 0;
9 | /* next output byte should be put there */
10 | this.output = null; // JS specific, because we have no pointers
11 | this.next_out = 0;
12 | /* remaining free space at output */
13 | this.avail_out = 0;
14 | /* total number of bytes output so far */
15 | this.total_out = 0;
16 | /* last error message, NULL if no error */
17 | this.msg = ''/*Z_NULL*/;
18 | /* not visible by applications */
19 | this.state = null;
20 | /* best guess about the data type: binary or text */
21 | this.data_type = 2/*Z_UNKNOWN*/;
22 | /* adler32 value of the uncompressed data */
23 | this.adler = 0;
24 | }
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 |
5 | ---
6 |
7 | **Describe the bug**
8 | A clear and concise description of what the bug is.
9 |
10 | **To Reproduce**
11 | Steps to reproduce the behavior:
12 | 1. Go to '...'
13 | 2. Click on '....'
14 | 3. Scroll down to '....'
15 | 4. See error
16 |
17 | **Expected behavior**
18 | A clear and concise description of what you expected to happen.
19 |
20 | **Screenshots**
21 | If applicable, add screenshots to help explain your problem.
22 |
23 | **Client (please complete the following information):**
24 | - OS: [e.g. iOS]
25 | - Browser: [e.g. chrome, safari]
26 | - Browser version: [e.g. 22]
27 |
28 | **Server (please complete the following information):**
29 | - noVNC version: [e.g. 1.0.0 or git commit id]
30 | - VNC server: [e.g. QEMU, TigerVNC]
31 | - WebSocket proxy: [e.g. websockify]
32 |
33 | **Additional context**
34 | Add any other context about the problem here.
35 |
--------------------------------------------------------------------------------
/utils/u2x11:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #
3 | # Convert "U+..." commented entries in /usr/include/X11/keysymdef.h
4 | # into JavaScript for use by noVNC. Note this is likely to produce
5 | # a few duplicate properties with clashing values, that will need
6 | # resolving manually.
7 | #
8 | # Colin Dean
9 | #
10 |
11 | regex="^#define[ \t]+XK_[A-Za-z0-9_]+[ \t]+0x([0-9a-fA-F]+)[ \t]+\/\*[ \t]+U\+([0-9a-fA-F]+)[ \t]+[^*]+.[ \t]+\*\/[ \t]*$"
12 | echo "unicodeTable = {"
13 | while read line; do
14 | if echo "${line}" | egrep -qs "${regex}"; then
15 |
16 | x11=$(echo "${line}" | sed -r "s/${regex}/\1/")
17 | vnc=$(echo "${line}" | sed -r "s/${regex}/\2/")
18 |
19 | if echo "${vnc}" | egrep -qs "^00[2-9A-F][0-9A-F]$"; then
20 | : # skip ISO Latin-1 (U+0020 to U+00FF) as 1-to-1 mapping
21 | else
22 | # note 1-to-1 is possible (e.g. for Euro symbol, U+20AC)
23 | echo " 0x${vnc} : 0x${x11},"
24 | fi
25 | fi
26 | done < /usr/include/X11/keysymdef.h | uniq
27 | echo "};"
28 |
29 |
--------------------------------------------------------------------------------
/core/util/eventtarget.js:
--------------------------------------------------------------------------------
1 | /*
2 | * noVNC: HTML5 VNC client
3 | * Copyright (C) 2019 The noVNC Authors
4 | * Licensed under MPL 2.0 (see LICENSE.txt)
5 | *
6 | * See README.md for usage and integration instructions.
7 | */
8 |
9 | export default class EventTargetMixin {
10 | constructor() {
11 | this._listeners = new Map();
12 | }
13 |
14 | addEventListener(type, callback) {
15 | if (!this._listeners.has(type)) {
16 | this._listeners.set(type, new Set());
17 | }
18 | this._listeners.get(type).add(callback);
19 | }
20 |
21 | removeEventListener(type, callback) {
22 | if (this._listeners.has(type)) {
23 | this._listeners.get(type).delete(callback);
24 | }
25 | }
26 |
27 | dispatchEvent(event) {
28 | if (!this._listeners.has(event.type)) {
29 | return true;
30 | }
31 | this._listeners.get(event.type)
32 | .forEach(callback => callback.call(this, event));
33 | return !event.defaultPrevented;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/images/icons/Makefile:
--------------------------------------------------------------------------------
1 | ICONS := \
2 | novnc-16x16.png \
3 | novnc-24x24.png \
4 | novnc-32x32.png \
5 | novnc-48x48.png \
6 | novnc-64x64.png
7 |
8 | ANDROID_LAUNCHER := \
9 | novnc-48x48.png \
10 | novnc-72x72.png \
11 | novnc-96x96.png \
12 | novnc-144x144.png \
13 | novnc-192x192.png
14 |
15 | IPHONE_LAUNCHER := \
16 | novnc-60x60.png \
17 | novnc-120x120.png
18 |
19 | IPAD_LAUNCHER := \
20 | novnc-76x76.png \
21 | novnc-152x152.png
22 |
23 | ALL_ICONS := $(ICONS) $(ANDROID_LAUNCHER) $(IPHONE_LAUNCHER) $(IPAD_LAUNCHER)
24 |
25 | all: $(ALL_ICONS)
26 |
27 | novnc-16x16.png: novnc-icon-sm.svg
28 | convert -density 90 \
29 | -background transparent "$<" "$@"
30 | novnc-24x24.png: novnc-icon-sm.svg
31 | convert -density 135 \
32 | -background transparent "$<" "$@"
33 | novnc-32x32.png: novnc-icon-sm.svg
34 | convert -density 180 \
35 | -background transparent "$<" "$@"
36 |
37 | novnc-%.png: novnc-icon.svg
38 | convert -density $$[`echo $* | cut -d x -f 1` * 90 / 48] \
39 | -background transparent "$<" "$@"
40 |
41 | clean:
42 | rm -f *.png
43 |
--------------------------------------------------------------------------------
/po/Makefile:
--------------------------------------------------------------------------------
1 | all:
2 | .PHONY: update-po update-js update-pot
3 |
4 | LINGUAS := cs de el es ja ko nl pl pt_BR ru sv tr zh_CN zh_TW
5 |
6 | VERSION := $(shell grep '"version"' ../package.json | cut -d '"' -f 4)
7 |
8 | POFILES := $(addsuffix .po,$(LINGUAS))
9 | JSONFILES := $(addprefix ../app/locale/,$(addsuffix .json,$(LINGUAS)))
10 |
11 | update-po: $(POFILES)
12 | update-js: $(JSONFILES)
13 |
14 | %.po: noVNC.pot
15 | msgmerge --update --lang=$* $@ $<
16 | ../app/locale/%.json: %.po
17 | ./po2js $< $@
18 |
19 | update-pot:
20 | xgettext --output=noVNC.js.pot \
21 | --copyright-holder="The noVNC Authors" \
22 | --package-name="noVNC" \
23 | --package-version="$(VERSION)" \
24 | --msgid-bugs-address="novnc@googlegroups.com" \
25 | --add-comments=TRANSLATORS: \
26 | --from-code=UTF-8 \
27 | --sort-by-file \
28 | ../app/*.js \
29 | ../core/*.js \
30 | ../core/input/*.js
31 | ./xgettext-html --output=noVNC.html.pot \
32 | ../vnc.html
33 | msgcat --output-file=noVNC.pot \
34 | --sort-by-file noVNC.js.pot noVNC.html.pot
35 | rm -f noVNC.js.pot noVNC.html.pot
36 |
--------------------------------------------------------------------------------
/vendor/pako/LICENSE:
--------------------------------------------------------------------------------
1 | (The MIT License)
2 |
3 | Copyright (C) 2014-2016 by Vitaly Puzrin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/docs/LIBRARY.md:
--------------------------------------------------------------------------------
1 | # Using the noVNC JavaScript library
2 |
3 | This document describes how to make use of the noVNC JavaScript library for
4 | integration in your own VNC client application. If you wish to embed the more
5 | complete noVNC application with its included user interface then please see
6 | our [embedding documentation](EMBEDDING.md).
7 |
8 | ## API
9 |
10 | The API of noVNC consists of a single object called `RFB`. The formal
11 | documentation for that object can be found in our [API documentation](API.md).
12 |
13 | ## Example
14 |
15 | noVNC includes a small example application called `vnc_lite.html`. This does
16 | not make use of all the features of noVNC, but is a good start to see how to
17 | do things.
18 |
19 | ## Conversion of Modules
20 |
21 | noVNC is written using ECMAScript 6 modules. This is not supported by older
22 | versions of Node.js. To use noVNC with those older versions of Node.js the
23 | library must first be converted.
24 |
25 | Fortunately noVNC includes a script to handle this conversion. Please follow
26 | the following steps:
27 |
28 | 1. Install Node.js
29 | 2. Run `npm install` in the noVNC directory
30 |
31 | The result of the conversion is available in the `lib/` directory.
32 |
--------------------------------------------------------------------------------
/vendor/pako/lib/utils/common.js:
--------------------------------------------------------------------------------
1 | // reduce buffer size, avoiding mem copy
2 | export function shrinkBuf (buf, size) {
3 | if (buf.length === size) { return buf; }
4 | if (buf.subarray) { return buf.subarray(0, size); }
5 | buf.length = size;
6 | return buf;
7 | };
8 |
9 |
10 | export function arraySet (dest, src, src_offs, len, dest_offs) {
11 | if (src.subarray && dest.subarray) {
12 | dest.set(src.subarray(src_offs, src_offs + len), dest_offs);
13 | return;
14 | }
15 | // Fallback to ordinary array
16 | for (var i = 0; i < len; i++) {
17 | dest[dest_offs + i] = src[src_offs + i];
18 | }
19 | }
20 |
21 | // Join array of chunks to single array.
22 | export function flattenChunks (chunks) {
23 | var i, l, len, pos, chunk, result;
24 |
25 | // calculate data length
26 | len = 0;
27 | for (i = 0, l = chunks.length; i < l; i++) {
28 | len += chunks[i].length;
29 | }
30 |
31 | // join chunks
32 | result = new Uint8Array(len);
33 | pos = 0;
34 | for (i = 0, l = chunks.length; i < l; i++) {
35 | chunk = chunks[i];
36 | result.set(chunk, pos);
37 | pos += chunk.length;
38 | }
39 |
40 | return result;
41 | }
42 |
43 | export var Buf8 = Uint8Array;
44 | export var Buf16 = Uint16Array;
45 | export var Buf32 = Int32Array;
46 |
--------------------------------------------------------------------------------
/core/decoders/rre.js:
--------------------------------------------------------------------------------
1 | /*
2 | * noVNC: HTML5 VNC client
3 | * Copyright (C) 2019 The noVNC Authors
4 | * Licensed under MPL 2.0 (see LICENSE.txt)
5 | *
6 | * See README.md for usage and integration instructions.
7 | *
8 | */
9 |
10 | export default class RREDecoder {
11 | constructor() {
12 | this._subrects = 0;
13 | }
14 |
15 | decodeRect(x, y, width, height, sock, display, depth) {
16 | if (this._subrects === 0) {
17 | if (sock.rQwait("RRE", 4 + 4)) {
18 | return false;
19 | }
20 |
21 | this._subrects = sock.rQshift32();
22 |
23 | let color = sock.rQshiftBytes(4); // Background
24 | display.fillRect(x, y, width, height, color);
25 | }
26 |
27 | while (this._subrects > 0) {
28 | if (sock.rQwait("RRE", 4 + 8)) {
29 | return false;
30 | }
31 |
32 | let color = sock.rQshiftBytes(4);
33 | let sx = sock.rQshift16();
34 | let sy = sock.rQshift16();
35 | let swidth = sock.rQshift16();
36 | let sheight = sock.rQshift16();
37 | display.fillRect(x + sx, y + sy, swidth, sheight, color);
38 |
39 | this._subrects--;
40 | }
41 |
42 | return true;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/snap/local/svc_wrapper.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # `snapctl get services` returns a JSON array, example:
4 | #{
5 | #"n6801": {
6 | # "listen": 6801,
7 | # "vnc": "localhost:5901"
8 | #},
9 | #"n6802": {
10 | # "listen": 6802,
11 | # "vnc": "localhost:5902"
12 | #}
13 | #}
14 | snapctl get services | jq -c '.[]' | while read service; do # for each service the user sepcified..
15 | # get the important data for the service (listen port, VNC host:port)
16 | listen_port="$(echo $service | jq --raw-output '.listen')"
17 | vnc_host_port="$(echo $service | jq --raw-output '.vnc')" # --raw-output removes any quotation marks from the output
18 |
19 | # check whether those values are valid
20 | expr "$listen_port" : '^[0-9]\+$' > /dev/null
21 | listen_port_valid=$?
22 | if [ ! $listen_port_valid ] || [ -z "$vnc_host_port" ]; then
23 | # invalid values mean the service is disabled, do nothing except for printing a message (logged in /var/log/system or systemd journal)
24 | echo "novnc: not starting service ${service} with listen_port ${listen_port} and vnc_host_port ${vnc_host_port}"
25 | else
26 | # start (and fork with '&') the service using the specified listen port and VNC host:port
27 | $SNAP/launch.sh --listen $listen_port --vnc $vnc_host_port &
28 | fi
29 | done
30 |
--------------------------------------------------------------------------------
/docs/LICENSE.BSD-2-Clause:
--------------------------------------------------------------------------------
1 | Copyright (c) ,
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | * Redistributions in binary form must reproduce the above copyright
10 | notice, this list of conditions and the following disclaimer in the
11 | documentation and/or other materials provided with the distribution.
12 |
13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY
17 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 |
--------------------------------------------------------------------------------
/tests/test.base64.js:
--------------------------------------------------------------------------------
1 | const expect = chai.expect;
2 |
3 | import Base64 from '../core/base64.js';
4 |
5 | describe('Base64 Tools', function () {
6 | "use strict";
7 |
8 | const BIN_ARR = new Array(256);
9 | for (let i = 0; i < 256; i++) {
10 | BIN_ARR[i] = i;
11 | }
12 |
13 | const B64_STR = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==";
14 |
15 |
16 | describe('encode', function () {
17 | it('should encode a binary string into Base64', function () {
18 | const encoded = Base64.encode(BIN_ARR);
19 | expect(encoded).to.equal(B64_STR);
20 | });
21 | });
22 |
23 | describe('decode', function () {
24 | it('should decode a Base64 string into a normal string', function () {
25 | const decoded = Base64.decode(B64_STR);
26 | expect(decoded).to.deep.equal(BIN_ARR);
27 | });
28 |
29 | it('should throw an error if we have extra characters at the end of the string', function () {
30 | expect(() => Base64.decode(B64_STR+'abcdef')).to.throw(Error);
31 | });
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/vendor/pako/lib/zlib/gzheader.js:
--------------------------------------------------------------------------------
1 | export default function GZheader() {
2 | /* true if compressed data believed to be text */
3 | this.text = 0;
4 | /* modification time */
5 | this.time = 0;
6 | /* extra flags (not used when writing a gzip file) */
7 | this.xflags = 0;
8 | /* operating system */
9 | this.os = 0;
10 | /* pointer to extra field or Z_NULL if none */
11 | this.extra = null;
12 | /* extra field length (valid if extra != Z_NULL) */
13 | this.extra_len = 0; // Actually, we don't need it in JS,
14 | // but leave for few code modifications
15 |
16 | //
17 | // Setup limits is not necessary because in js we should not preallocate memory
18 | // for inflate use constant limit in 65536 bytes
19 | //
20 |
21 | /* space at extra (only when reading header) */
22 | // this.extra_max = 0;
23 | /* pointer to zero-terminated file name or Z_NULL */
24 | this.name = '';
25 | /* space at name (only when reading header) */
26 | // this.name_max = 0;
27 | /* pointer to zero-terminated comment or Z_NULL */
28 | this.comment = '';
29 | /* space at comment (only when reading header) */
30 | // this.comm_max = 0;
31 | /* true if there was or will be a header crc */
32 | this.hcrc = 0;
33 | /* true when done reading gzip header (not used when writing a gzip file) */
34 | this.done = false;
35 | }
36 |
--------------------------------------------------------------------------------
/vendor/pako/lib/zlib/constants.js:
--------------------------------------------------------------------------------
1 | export default {
2 |
3 | /* Allowed flush values; see deflate() and inflate() below for details */
4 | Z_NO_FLUSH: 0,
5 | Z_PARTIAL_FLUSH: 1,
6 | Z_SYNC_FLUSH: 2,
7 | Z_FULL_FLUSH: 3,
8 | Z_FINISH: 4,
9 | Z_BLOCK: 5,
10 | Z_TREES: 6,
11 |
12 | /* Return codes for the compression/decompression functions. Negative values
13 | * are errors, positive values are used for special but normal events.
14 | */
15 | Z_OK: 0,
16 | Z_STREAM_END: 1,
17 | Z_NEED_DICT: 2,
18 | Z_ERRNO: -1,
19 | Z_STREAM_ERROR: -2,
20 | Z_DATA_ERROR: -3,
21 | //Z_MEM_ERROR: -4,
22 | Z_BUF_ERROR: -5,
23 | //Z_VERSION_ERROR: -6,
24 |
25 | /* compression levels */
26 | Z_NO_COMPRESSION: 0,
27 | Z_BEST_SPEED: 1,
28 | Z_BEST_COMPRESSION: 9,
29 | Z_DEFAULT_COMPRESSION: -1,
30 |
31 |
32 | Z_FILTERED: 1,
33 | Z_HUFFMAN_ONLY: 2,
34 | Z_RLE: 3,
35 | Z_FIXED: 4,
36 | Z_DEFAULT_STRATEGY: 0,
37 |
38 | /* Possible values of the data_type field (though see inflate()) */
39 | Z_BINARY: 0,
40 | Z_TEXT: 1,
41 | //Z_ASCII: 1, // = Z_TEXT (deprecated)
42 | Z_UNKNOWN: 2,
43 |
44 | /* The deflate compression method */
45 | Z_DEFLATED: 8
46 | //Z_NULL: null // Use -1 or null inline, depending on var type
47 | };
48 |
--------------------------------------------------------------------------------
/snap/snapcraft.yaml:
--------------------------------------------------------------------------------
1 | name: novnc
2 | base: core18 # the base snap is the execution environment for this snap
3 | version: '@VERSION@'
4 | summary: Open Source VNC client using HTML5 (WebSockets, Canvas)
5 | description: |
6 | Open Source VNC client using HTML5 (WebSockets, Canvas).
7 | noVNC is both a VNC client JavaScript library as well as an
8 | application built on top of that library. noVNC runs well in any
9 | modern browser including mobile browsers (iOS and Android).
10 |
11 | grade: stable
12 | confinement: strict
13 |
14 | parts:
15 | novnc:
16 | source: .
17 | plugin: dump
18 | organize:
19 | utils/launch.sh: /
20 | stage:
21 | - vnc.html
22 | - app
23 | - core/**/*.js
24 | - vendor/**/*.js
25 | - launch.sh
26 | stage-packages:
27 | - bash
28 |
29 | svc-script:
30 | source: snap/local
31 | plugin: dump
32 | stage:
33 | - svc_wrapper.sh
34 | stage-packages:
35 | - bash
36 | - jq
37 |
38 | websockify:
39 | source: https://github.com/novnc/websockify/archive/v0.9.0.tar.gz
40 | plugin: python
41 | stage-packages:
42 | - python3-numpy
43 |
44 | hooks:
45 | configure:
46 | plugs: [network, network-bind]
47 |
48 | apps:
49 | novnc:
50 | command: ./launch.sh
51 | plugs: [network, network-bind]
52 | novncsvc:
53 | command: ./svc_wrapper.sh
54 | daemon: forking
55 | plugs: [network, network-bind]
56 |
--------------------------------------------------------------------------------
/core/util/logging.js:
--------------------------------------------------------------------------------
1 | /*
2 | * noVNC: HTML5 VNC client
3 | * Copyright (C) 2019 The noVNC Authors
4 | * Licensed under MPL 2.0 (see LICENSE.txt)
5 | *
6 | * See README.md for usage and integration instructions.
7 | */
8 |
9 | /*
10 | * Logging/debug routines
11 | */
12 |
13 | let _logLevel = 'warn';
14 |
15 | let Debug = () => {};
16 | let Info = () => {};
17 | let Warn = () => {};
18 | let Error = () => {};
19 |
20 | export function initLogging(level) {
21 | if (typeof level === 'undefined') {
22 | level = _logLevel;
23 | } else {
24 | _logLevel = level;
25 | }
26 |
27 | Debug = Info = Warn = Error = () => {};
28 |
29 | if (typeof window.console !== "undefined") {
30 | /* eslint-disable no-console, no-fallthrough */
31 | switch (level) {
32 | case 'debug':
33 | Debug = console.debug.bind(window.console);
34 | case 'info':
35 | Info = console.info.bind(window.console);
36 | case 'warn':
37 | Warn = console.warn.bind(window.console);
38 | case 'error':
39 | Error = console.error.bind(window.console);
40 | case 'none':
41 | break;
42 | default:
43 | throw new window.Error("invalid logging type '" + level + "'");
44 | }
45 | /* eslint-enable no-console, no-fallthrough */
46 | }
47 | }
48 |
49 | export function getLogging() {
50 | return _logLevel;
51 | }
52 |
53 | export { Debug, Info, Warn, Error };
54 |
55 | // Initialize logging level
56 | initLogging();
57 |
--------------------------------------------------------------------------------
/docs/LICENSE.BSD-3-Clause:
--------------------------------------------------------------------------------
1 | Copyright (c) ,
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 | * Redistributions of source code must retain the above copyright
7 | notice, this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above copyright
9 | notice, this list of conditions and the following disclaimer in the
10 | documentation and/or other materials provided with the distribution.
11 | * Neither the name of the nor the
12 | names of its contributors may be used to endorse or promote products
13 | derived from this software without specific prior written permission.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY
19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |
--------------------------------------------------------------------------------
/po/po2js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | /*
3 | * ps2js: gettext .po to noVNC .js converter
4 | * Copyright (C) 2018 The noVNC Authors
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 |
20 | const getopt = require('node-getopt');
21 | const fs = require('fs');
22 | const po2json = require("po2json");
23 |
24 | const opt = getopt.create([
25 | ['h', 'help', 'display this help'],
26 | ]).bindHelp().parseSystem();
27 |
28 | if (opt.argv.length != 2) {
29 | console.error("Incorrect number of arguments given");
30 | process.exit(1);
31 | }
32 |
33 | const data = po2json.parseFileSync(opt.argv[0]);
34 |
35 | const bodyPart = Object.keys(data).filter(msgid => msgid !== "").map((msgid) => {
36 | if (msgid === "") return;
37 | const msgstr = data[msgid][1];
38 | return " " + JSON.stringify(msgid) + ": " + JSON.stringify(msgstr);
39 | }).join(",\n");
40 |
41 | const output = "{\n" + bodyPart + "\n}";
42 |
43 | fs.writeFileSync(opt.argv[1], output);
44 |
--------------------------------------------------------------------------------
/core/encodings.js:
--------------------------------------------------------------------------------
1 | /*
2 | * noVNC: HTML5 VNC client
3 | * Copyright (C) 2019 The noVNC Authors
4 | * Licensed under MPL 2.0 (see LICENSE.txt)
5 | *
6 | * See README.md for usage and integration instructions.
7 | */
8 |
9 | export const encodings = {
10 | encodingRaw: 0,
11 | encodingCopyRect: 1,
12 | encodingRRE: 2,
13 | encodingHextile: 5,
14 | encodingTight: 7,
15 | encodingTightPNG: -260,
16 |
17 | pseudoEncodingQualityLevel9: -23,
18 | pseudoEncodingQualityLevel0: -32,
19 | pseudoEncodingDesktopSize: -223,
20 | pseudoEncodingLastRect: -224,
21 | pseudoEncodingCursor: -239,
22 | pseudoEncodingQEMUExtendedKeyEvent: -258,
23 | pseudoEncodingDesktopName: -307,
24 | pseudoEncodingExtendedDesktopSize: -308,
25 | pseudoEncodingXvp: -309,
26 | pseudoEncodingFence: -312,
27 | pseudoEncodingContinuousUpdates: -313,
28 | pseudoEncodingCompressLevel9: -247,
29 | pseudoEncodingCompressLevel0: -256,
30 | pseudoEncodingReplitAudio: 0x52706c41,
31 | pseudoEncodingVMwareCursor: 0x574d5664,
32 | pseudoEncodingExtendedClipboard: 0xc0a1e5ce
33 | };
34 |
35 | export function encodingName(num) {
36 | switch (num) {
37 | case encodings.encodingRaw: return "Raw";
38 | case encodings.encodingCopyRect: return "CopyRect";
39 | case encodings.encodingRRE: return "RRE";
40 | case encodings.encodingHextile: return "Hextile";
41 | case encodings.encodingTight: return "Tight";
42 | case encodings.encodingTightPNG: return "TightPNG";
43 | default: return "[unknown encoding " + num + "]";
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "flake-utils": {
4 | "inputs": {
5 | "systems": "systems"
6 | },
7 | "locked": {
8 | "lastModified": 1731533236,
9 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
10 | "owner": "numtide",
11 | "repo": "flake-utils",
12 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
13 | "type": "github"
14 | },
15 | "original": {
16 | "id": "flake-utils",
17 | "type": "indirect"
18 | }
19 | },
20 | "nixpkgs": {
21 | "locked": {
22 | "lastModified": 1739758141,
23 | "narHash": "sha256-uq6A2L7o1/tR6VfmYhZWoVAwb3gTy7j4Jx30MIrH0rE=",
24 | "owner": "NixOS",
25 | "repo": "nixpkgs",
26 | "rev": "c618e28f70257593de75a7044438efc1c1fc0791",
27 | "type": "github"
28 | },
29 | "original": {
30 | "owner": "NixOS",
31 | "ref": "nixos-24.11",
32 | "repo": "nixpkgs",
33 | "type": "github"
34 | }
35 | },
36 | "root": {
37 | "inputs": {
38 | "flake-utils": "flake-utils",
39 | "nixpkgs": "nixpkgs"
40 | }
41 | },
42 | "systems": {
43 | "locked": {
44 | "lastModified": 1681028828,
45 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
46 | "owner": "nix-systems",
47 | "repo": "default",
48 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
49 | "type": "github"
50 | },
51 | "original": {
52 | "owner": "nix-systems",
53 | "repo": "default",
54 | "type": "github"
55 | }
56 | }
57 | },
58 | "root": "root",
59 | "version": 7
60 | }
61 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Publish
2 |
3 | on:
4 | push:
5 | pull_request:
6 | release:
7 | types: [published]
8 |
9 | jobs:
10 | npm:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v4
14 | - uses: actions/setup-node@v4
15 | with:
16 | # Needs to be explicitly specified for auth to work
17 | registry-url: 'https://registry.npmjs.org'
18 | - run: npm install
19 | - uses: actions/upload-artifact@v2
20 | with:
21 | name: npm
22 | path: lib
23 | - run: npm publish --access public
24 | env:
25 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
26 | if: ${{ github.event_name == 'release' && !github.event.release.prerelease }}
27 | - run: npm publish --access public --tag beta
28 | env:
29 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
30 | if: ${{ github.event_name == 'release' && github.event.release.prerelease }}
31 | snap:
32 | runs-on: ubuntu-latest
33 | steps:
34 | - uses: actions/checkout@v4
35 | - run: |
36 | VERSION=$(grep '"version"' package.json | cut -d '"' -f 4)
37 | echo $VERSION
38 | sed -i "s/@VERSION@/$VERSION/g" snap/snapcraft.yaml
39 | - uses: snapcore/action-build@v1
40 | id: snapcraft
41 | - uses: actions/upload-artifact@v2
42 | with:
43 | name: snap
44 | path: ${{ steps.snapcraft.outputs.snap }}
45 | - uses: snapcore/action-publish@v1
46 | with:
47 | store_login: ${{ secrets.SNAPCRAFT_LOGIN }}
48 | snap: ${{ steps.build.outputs.snap }}
49 | release: stable
50 | if: ${{ github.event_name == 'release' && !github.event.release.prerelease }}
51 | - uses: snapcore/action-publish@v1
52 | with:
53 | store_login: ${{ secrets.SNAPCRAFT_LOGIN }}
54 | snap: ${{ steps.build.outputs.snap }}
55 | release: beta
56 | if: ${{ github.event_name == 'release' && github.event.release.prerelease }}
57 |
--------------------------------------------------------------------------------
/core/decoders/raw.js:
--------------------------------------------------------------------------------
1 | /*
2 | * noVNC: HTML5 VNC client
3 | * Copyright (C) 2019 The noVNC Authors
4 | * Licensed under MPL 2.0 (see LICENSE.txt)
5 | *
6 | * See README.md for usage and integration instructions.
7 | *
8 | */
9 |
10 | export default class RawDecoder {
11 | constructor() {
12 | this._lines = 0;
13 | }
14 |
15 | decodeRect(x, y, width, height, sock, display, depth) {
16 | if ((width === 0) || (height === 0)) {
17 | return true;
18 | }
19 |
20 | if (this._lines === 0) {
21 | this._lines = height;
22 | }
23 |
24 | const pixelSize = depth == 8 ? 1 : 4;
25 | const bytesPerLine = width * pixelSize;
26 |
27 | if (sock.rQwait("RAW", bytesPerLine)) {
28 | return false;
29 | }
30 |
31 | const curY = y + (height - this._lines);
32 | const currHeight = Math.min(this._lines,
33 | Math.floor(sock.rQlen / bytesPerLine));
34 | const pixels = width * currHeight;
35 |
36 | let data = sock.rQ;
37 | let index = sock.rQi;
38 |
39 | // Convert data if needed
40 | if (depth == 8) {
41 | const newdata = new Uint8Array(pixels * 4);
42 | for (let i = 0; i < pixels; i++) {
43 | newdata[i * 4 + 0] = ((data[index + i] >> 0) & 0x3) * 255 / 3;
44 | newdata[i * 4 + 1] = ((data[index + i] >> 2) & 0x3) * 255 / 3;
45 | newdata[i * 4 + 2] = ((data[index + i] >> 4) & 0x3) * 255 / 3;
46 | newdata[i * 4 + 3] = 255;
47 | }
48 | data = newdata;
49 | index = 0;
50 | }
51 |
52 | // Max sure the image is fully opaque
53 | for (let i = 0; i < pixels; i++) {
54 | data[i * 4 + 3] = 255;
55 | }
56 |
57 | display.blitImage(x, curY, width, currHeight, data, index);
58 | sock.rQskipBytes(currHeight * bytesPerLine);
59 | this._lines -= currHeight;
60 | if (this._lines > 0) {
61 | return false;
62 | }
63 |
64 | return true;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/core/inflator.js:
--------------------------------------------------------------------------------
1 | /*
2 | * noVNC: HTML5 VNC client
3 | * Copyright (C) 2020 The noVNC Authors
4 | * Licensed under MPL 2.0 (see LICENSE.txt)
5 | *
6 | * See README.md for usage and integration instructions.
7 | */
8 |
9 | import { inflateInit, inflate, inflateReset } from "../vendor/pako/lib/zlib/inflate.js";
10 | import ZStream from "../vendor/pako/lib/zlib/zstream.js";
11 |
12 | export default class Inflate {
13 | constructor() {
14 | this.strm = new ZStream();
15 | this.chunkSize = 1024 * 10 * 10;
16 | this.strm.output = new Uint8Array(this.chunkSize);
17 | this.windowBits = 5;
18 |
19 | inflateInit(this.strm, this.windowBits);
20 | }
21 |
22 | setInput(data) {
23 | if (!data) {
24 | //FIXME: flush remaining data.
25 | /* eslint-disable camelcase */
26 | this.strm.input = null;
27 | this.strm.avail_in = 0;
28 | this.strm.next_in = 0;
29 | } else {
30 | this.strm.input = data;
31 | this.strm.avail_in = this.strm.input.length;
32 | this.strm.next_in = 0;
33 | /* eslint-enable camelcase */
34 | }
35 | }
36 |
37 | inflate(expected) {
38 | // resize our output buffer if it's too small
39 | // (we could just use multiple chunks, but that would cause an extra
40 | // allocation each time to flatten the chunks)
41 | if (expected > this.chunkSize) {
42 | this.chunkSize = expected;
43 | this.strm.output = new Uint8Array(this.chunkSize);
44 | }
45 |
46 | /* eslint-disable camelcase */
47 | this.strm.next_out = 0;
48 | this.strm.avail_out = expected;
49 | /* eslint-enable camelcase */
50 |
51 | let ret = inflate(this.strm, 0); // Flush argument not used.
52 | if (ret < 0) {
53 | throw new Error("zlib inflate failed");
54 | }
55 |
56 | if (this.strm.next_out != expected) {
57 | throw new Error("Incomplete zlib block");
58 | }
59 |
60 | return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
61 | }
62 |
63 | reset() {
64 | inflateReset(this.strm);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/locale/zh_CN.json:
--------------------------------------------------------------------------------
1 | {
2 | "Connecting...": "连接中...",
3 | "Disconnecting...": "正在断开连接...",
4 | "Reconnecting...": "重新连接中...",
5 | "Internal error": "内部错误",
6 | "Must set host": "请提供主机名",
7 | "Connected (encrypted) to ": "已连接到(加密)",
8 | "Connected (unencrypted) to ": "已连接到(未加密)",
9 | "Something went wrong, connection is closed": "发生错误,连接已关闭",
10 | "Failed to connect to server": "无法连接到服务器",
11 | "Disconnected": "已断开连接",
12 | "New connection has been rejected with reason: ": "连接被拒绝,原因:",
13 | "New connection has been rejected": "连接被拒绝",
14 | "Password is required": "请提供密码",
15 | "noVNC encountered an error:": "noVNC 遇到一个错误:",
16 | "Hide/Show the control bar": "显示/隐藏控制栏",
17 | "Move/Drag Viewport": "拖放显示范围",
18 | "viewport drag": "显示范围拖放",
19 | "Active Mouse Button": "启动鼠标按鍵",
20 | "No mousebutton": "禁用鼠标按鍵",
21 | "Left mousebutton": "鼠标左鍵",
22 | "Middle mousebutton": "鼠标中鍵",
23 | "Right mousebutton": "鼠标右鍵",
24 | "Keyboard": "键盘",
25 | "Show Keyboard": "显示键盘",
26 | "Extra keys": "额外按键",
27 | "Show Extra Keys": "显示额外按键",
28 | "Ctrl": "Ctrl",
29 | "Toggle Ctrl": "切换 Ctrl",
30 | "Alt": "Alt",
31 | "Toggle Alt": "切换 Alt",
32 | "Send Tab": "发送 Tab 键",
33 | "Tab": "Tab",
34 | "Esc": "Esc",
35 | "Send Escape": "发送 Escape 键",
36 | "Ctrl+Alt+Del": "Ctrl-Alt-Del",
37 | "Send Ctrl-Alt-Del": "发送 Ctrl-Alt-Del 键",
38 | "Shutdown/Reboot": "关机/重新启动",
39 | "Shutdown/Reboot...": "关机/重新启动...",
40 | "Power": "电源",
41 | "Shutdown": "关机",
42 | "Reboot": "重新启动",
43 | "Reset": "重置",
44 | "Clipboard": "剪贴板",
45 | "Clear": "清除",
46 | "Fullscreen": "全屏",
47 | "Settings": "设置",
48 | "Shared Mode": "分享模式",
49 | "View Only": "仅查看",
50 | "Clip to Window": "限制/裁切窗口大小",
51 | "Scaling Mode:": "缩放模式:",
52 | "None": "无",
53 | "Local Scaling": "本地缩放",
54 | "Remote Resizing": "远程调整大小",
55 | "Advanced": "高级",
56 | "Repeater ID:": "中继站 ID",
57 | "WebSocket": "WebSocket",
58 | "Encrypt": "加密",
59 | "Host:": "主机:",
60 | "Port:": "端口:",
61 | "Path:": "路径:",
62 | "Automatic Reconnect": "自动重新连接",
63 | "Reconnect Delay (ms):": "重新连接间隔 (ms):",
64 | "Logging:": "日志级别:",
65 | "Disconnect": "中断连接",
66 | "Connect": "连接",
67 | "Password:": "密码:",
68 | "Cancel": "取消"
69 | }
--------------------------------------------------------------------------------
/app/locale/zh_TW.json:
--------------------------------------------------------------------------------
1 | {
2 | "Connecting...": "連線中...",
3 | "Disconnecting...": "正在中斷連線...",
4 | "Reconnecting...": "重新連線中...",
5 | "Internal error": "內部錯誤",
6 | "Must set host": "請提供主機資訊",
7 | "Connected (encrypted) to ": "已加密連線到",
8 | "Connected (unencrypted) to ": "未加密連線到",
9 | "Something went wrong, connection is closed": "發生錯誤,連線已關閉",
10 | "Failed to connect to server": "無法連線到伺服器",
11 | "Disconnected": "連線已中斷",
12 | "New connection has been rejected with reason: ": "連線被拒絕,原因:",
13 | "New connection has been rejected": "連線被拒絕",
14 | "Password is required": "請提供密碼",
15 | "noVNC encountered an error:": "noVNC 遇到一個錯誤:",
16 | "Hide/Show the control bar": "顯示/隱藏控制列",
17 | "Move/Drag Viewport": "拖放顯示範圍",
18 | "viewport drag": "顯示範圍拖放",
19 | "Active Mouse Button": "啟用滑鼠按鍵",
20 | "No mousebutton": "無滑鼠按鍵",
21 | "Left mousebutton": "滑鼠左鍵",
22 | "Middle mousebutton": "滑鼠中鍵",
23 | "Right mousebutton": "滑鼠右鍵",
24 | "Keyboard": "鍵盤",
25 | "Show Keyboard": "顯示鍵盤",
26 | "Extra keys": "額外按鍵",
27 | "Show Extra Keys": "顯示額外按鍵",
28 | "Ctrl": "Ctrl",
29 | "Toggle Ctrl": "切換 Ctrl",
30 | "Alt": "Alt",
31 | "Toggle Alt": "切換 Alt",
32 | "Send Tab": "送出 Tab 鍵",
33 | "Tab": "Tab",
34 | "Esc": "Esc",
35 | "Send Escape": "送出 Escape 鍵",
36 | "Ctrl+Alt+Del": "Ctrl-Alt-Del",
37 | "Send Ctrl-Alt-Del": "送出 Ctrl-Alt-Del 快捷鍵",
38 | "Shutdown/Reboot": "關機/重新啟動",
39 | "Shutdown/Reboot...": "關機/重新啟動...",
40 | "Power": "電源",
41 | "Shutdown": "關機",
42 | "Reboot": "重新啟動",
43 | "Reset": "重設",
44 | "Clipboard": "剪貼簿",
45 | "Clear": "清除",
46 | "Fullscreen": "全螢幕",
47 | "Settings": "設定",
48 | "Shared Mode": "分享模式",
49 | "View Only": "僅檢視",
50 | "Clip to Window": "限制/裁切視窗大小",
51 | "Scaling Mode:": "縮放模式:",
52 | "None": "無",
53 | "Local Scaling": "本機縮放",
54 | "Remote Resizing": "遠端調整大小",
55 | "Advanced": "進階",
56 | "Repeater ID:": "中繼站 ID",
57 | "WebSocket": "WebSocket",
58 | "Encrypt": "加密",
59 | "Host:": "主機:",
60 | "Port:": "連接埠:",
61 | "Path:": "路徑:",
62 | "Automatic Reconnect": "自動重新連線",
63 | "Reconnect Delay (ms):": "重新連線間隔 (ms):",
64 | "Logging:": "日誌級別:",
65 | "Disconnect": "中斷連線",
66 | "Connect": "連線",
67 | "Password:": "密碼:",
68 | "Cancel": "取消"
69 | }
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | noVNC is Copyright (C) 2019 The noVNC Authors
2 | (./AUTHORS)
3 |
4 | The noVNC core library files are licensed under the MPL 2.0 (Mozilla
5 | Public License 2.0). The noVNC core library is composed of the
6 | Javascript code necessary for full noVNC operation. This includes (but
7 | is not limited to):
8 |
9 | core/**/*.js
10 | app/*.js
11 | test/playback.js
12 |
13 | The HTML, CSS, font and images files that included with the noVNC
14 | source distibution (or repository) are not considered part of the
15 | noVNC core library and are licensed under more permissive licenses.
16 | The intent is to allow easy integration of noVNC into existing web
17 | sites and web applications.
18 |
19 | The HTML, CSS, font and image files are licensed as follows:
20 |
21 | *.html : 2-Clause BSD license
22 |
23 | app/styles/*.css : 2-Clause BSD license
24 |
25 | app/styles/Orbitron* : SIL Open Font License 1.1
26 | (Copyright 2009 Matt McInerney)
27 |
28 | app/images/ : Creative Commons Attribution-ShareAlike
29 | http://creativecommons.org/licenses/by-sa/3.0/
30 |
31 | Some portions of noVNC are copyright to their individual authors.
32 | Please refer to the individual source files and/or to the noVNC commit
33 | history: https://github.com/novnc/noVNC/commits/master
34 |
35 | The are several files and projects that have been incorporated into
36 | the noVNC core library. Here is a list of those files and the original
37 | licenses (all MPL 2.0 compatible):
38 |
39 | core/base64.js : MPL 2.0
40 |
41 | core/des.js : Various BSD style licenses
42 |
43 | vendor/pako/ : MIT
44 |
45 | Any other files not mentioned above are typically marked with
46 | a copyright/license header at the top of the file. The default noVNC
47 | license is MPL-2.0.
48 |
49 | The following license texts are included:
50 |
51 | docs/LICENSE.MPL-2.0
52 | docs/LICENSE.OFL-1.1
53 | docs/LICENSE.BSD-3-Clause (New BSD)
54 | docs/LICENSE.BSD-2-Clause (Simplified BSD / FreeBSD)
55 | vendor/pako/LICENSE (MIT)
56 |
57 | Or alternatively the license texts may be found here:
58 |
59 | http://www.mozilla.org/MPL/2.0/
60 | http://scripts.sil.org/OFL
61 | http://en.wikipedia.org/wiki/BSD_licenses
62 | https://opensource.org/licenses/MIT
63 |
--------------------------------------------------------------------------------
/app/locale/ko.json:
--------------------------------------------------------------------------------
1 | {
2 | "Connecting...": "연결중...",
3 | "Disconnecting...": "연결 해제중...",
4 | "Reconnecting...": "재연결중...",
5 | "Internal error": "내부 오류",
6 | "Must set host": "호스트는 설정되어야 합니다.",
7 | "Connected (encrypted) to ": "다음과 (암호화되어) 연결되었습니다:",
8 | "Connected (unencrypted) to ": "다음과 (암호화 없이) 연결되었습니다:",
9 | "Something went wrong, connection is closed": "무언가 잘못되었습니다, 연결이 닫혔습니다.",
10 | "Failed to connect to server": "서버에 연결하지 못했습니다.",
11 | "Disconnected": "연결이 해제되었습니다.",
12 | "New connection has been rejected with reason: ": "새 연결이 다음 이유로 거부되었습니다:",
13 | "New connection has been rejected": "새 연결이 거부되었습니다.",
14 | "Password is required": "비밀번호가 필요합니다.",
15 | "noVNC encountered an error:": "noVNC에 오류가 발생했습니다:",
16 | "Hide/Show the control bar": "컨트롤 바 숨기기/보이기",
17 | "Move/Drag Viewport": "움직이기/드래그 뷰포트",
18 | "viewport drag": "뷰포트 드래그",
19 | "Active Mouse Button": "마우스 버튼 활성화",
20 | "No mousebutton": "마우스 버튼 없음",
21 | "Left mousebutton": "왼쪽 마우스 버튼",
22 | "Middle mousebutton": "중간 마우스 버튼",
23 | "Right mousebutton": "오른쪽 마우스 버튼",
24 | "Keyboard": "키보드",
25 | "Show Keyboard": "키보드 보이기",
26 | "Extra keys": "기타 키들",
27 | "Show Extra Keys": "기타 키들 보이기",
28 | "Ctrl": "Ctrl",
29 | "Toggle Ctrl": "Ctrl 켜기/끄기",
30 | "Alt": "Alt",
31 | "Toggle Alt": "Alt 켜기/끄기",
32 | "Send Tab": "Tab 보내기",
33 | "Tab": "Tab",
34 | "Esc": "Esc",
35 | "Send Escape": "Esc 보내기",
36 | "Ctrl+Alt+Del": "Ctrl+Alt+Del",
37 | "Send Ctrl-Alt-Del": "Ctrl+Alt+Del 보내기",
38 | "Shutdown/Reboot": "셧다운/리붓",
39 | "Shutdown/Reboot...": "셧다운/리붓...",
40 | "Power": "전원",
41 | "Shutdown": "셧다운",
42 | "Reboot": "리붓",
43 | "Reset": "리셋",
44 | "Clipboard": "클립보드",
45 | "Clear": "지우기",
46 | "Fullscreen": "전체화면",
47 | "Settings": "설정",
48 | "Shared Mode": "공유 모드",
49 | "View Only": "보기 전용",
50 | "Clip to Window": "창에 클립",
51 | "Scaling Mode:": "스케일링 모드:",
52 | "None": "없음",
53 | "Local Scaling": "로컬 스케일링",
54 | "Remote Resizing": "원격 크기 조절",
55 | "Advanced": "고급",
56 | "Repeater ID:": "중계 ID",
57 | "WebSocket": "웹소켓",
58 | "Encrypt": "암호화",
59 | "Host:": "호스트:",
60 | "Port:": "포트:",
61 | "Path:": "위치:",
62 | "Automatic Reconnect": "자동 재연결",
63 | "Reconnect Delay (ms):": "재연결 지연 시간 (ms)",
64 | "Logging:": "로깅",
65 | "Disconnect": "연결 해제",
66 | "Connect": "연결",
67 | "Password:": "비밀번호:",
68 | "Send Password": "비밀번호 전송",
69 | "Cancel": "취소"
70 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@replit/novnc",
3 | "version": "2.2.0",
4 | "description": "An HTML5 VNC client",
5 | "browser": "lib/rfb",
6 | "directories": {
7 | "lib": "lib",
8 | "doc": "docs",
9 | "test": "tests"
10 | },
11 | "files": [
12 | "lib",
13 | "AUTHORS",
14 | "VERSION",
15 | "docs/API.md",
16 | "docs/LIBRARY.md",
17 | "docs/LICENSE*",
18 | "core",
19 | "vendor/pako"
20 | ],
21 | "scripts": {
22 | "lint": "eslint app core po/po2js po/xgettext-html tests utils",
23 | "test": "karma start karma.conf.js",
24 | "prepublish": "node ./utils/use_require.js --clean"
25 | },
26 | "repository": {
27 | "type": "git",
28 | "url": "git+https://github.com/novnc/noVNC.git"
29 | },
30 | "author": "Joel Martin (https://github.com/kanaka)",
31 | "contributors": [
32 | "Solly Ross (https://github.com/directxman12)",
33 | "Peter Åstrand (https://github.com/astrand)",
34 | "Samuel Mannehed (https://github.com/samhed)",
35 | "Pierre Ossman (https://github.com/CendioOssman)"
36 | ],
37 | "license": "MPL-2.0",
38 | "bugs": {
39 | "url": "https://github.com/novnc/noVNC/issues"
40 | },
41 | "homepage": "https://github.com/novnc/noVNC",
42 | "devDependencies": {
43 | "@babel/core": "*",
44 | "@babel/plugin-syntax-dynamic-import": "*",
45 | "@babel/plugin-transform-modules-commonjs": "*",
46 | "@babel/preset-env": "*",
47 | "@babel/cli": "*",
48 | "babel-plugin-import-redirect": "*",
49 | "browserify": "*",
50 | "babelify": "*",
51 | "core-js": "*",
52 | "chai": "*",
53 | "commander": "*",
54 | "es-module-loader": "*",
55 | "eslint": "*",
56 | "fs-extra": "*",
57 | "jsdom": "*",
58 | "karma": "*",
59 | "karma-mocha": "*",
60 | "karma-chrome-launcher": "*",
61 | "@chiragrupani/karma-chromium-edge-launcher": "*",
62 | "karma-firefox-launcher": "*",
63 | "karma-ie-launcher": "*",
64 | "karma-mocha-reporter": "*",
65 | "karma-safari-launcher": "*",
66 | "karma-script-launcher": "*",
67 | "karma-sinon-chai": "*",
68 | "mocha": "*",
69 | "node-getopt": "*",
70 | "po2json": "*",
71 | "requirejs": "*",
72 | "rollup": "*",
73 | "rollup-plugin-node-resolve": "*",
74 | "sinon": "*",
75 | "sinon-chai": "*"
76 | },
77 | "dependencies": {},
78 | "keywords": [
79 | "vnc",
80 | "rfb",
81 | "novnc",
82 | "websockify"
83 | ]
84 | }
85 |
--------------------------------------------------------------------------------
/docs/links:
--------------------------------------------------------------------------------
1 | New tight PNG protocol:
2 | http://wiki.qemu.org/VNC_Tight_PNG
3 | http://xf.iksaif.net/blog/index.php?post/2010/06/14/QEMU:-Tight-PNG-and-some-profiling
4 |
5 | RFB protocol and extensions:
6 | http://tigervnc.org/cgi-bin/rfbproto
7 |
8 | Canvas Browser Compatibility:
9 | http://philip.html5.org/tests/canvas/suite/tests/results.html
10 |
11 | WebSockets API standard:
12 | http://www.whatwg.org/specs/web-apps/current-work/complete.html#websocket
13 | http://dev.w3.org/html5/websockets/
14 | http://www.ietf.org/id/draft-ietf-hybi-thewebsocketprotocol-00.txt
15 |
16 | Browser Keyboard Events detailed:
17 | http://unixpapa.com/js/key.html
18 |
19 | ActionScript (Flash) WebSocket implementation:
20 | http://github.com/gimite/web-socket-js
21 |
22 | ActionScript (Flash) crypto/TLS library:
23 | http://code.google.com/p/as3crypto
24 | http://github.com/lyokato/as3crypto_patched
25 |
26 | TLS Protocol:
27 | http://en.wikipedia.org/wiki/Transport_Layer_Security
28 |
29 | Generate self-signed certificate:
30 | http://docs.python.org/dev/library/ssl.html#certificates
31 |
32 | Cursor appearance/style (for Cursor pseudo-encoding):
33 | http://en.wikipedia.org/wiki/ICO_(file_format)
34 | http://www.daubnet.com/en/file-format-cur
35 | https://developer.mozilla.org/en/Using_URL_values_for_the_cursor_property
36 | http://www.fileformat.info/format/bmp/egff.htm
37 |
38 | Icon/Cursor file format:
39 | http://msdn.microsoft.com/en-us/library/ms997538
40 | http://msdn.microsoft.com/en-us/library/aa921550.aspx
41 | http://msdn.microsoft.com/en-us/library/aa930622.aspx
42 |
43 |
44 | RDP Protocol specification:
45 | http://msdn.microsoft.com/en-us/library/cc240445(v=PROT.10).aspx
46 |
47 |
48 | Related projects:
49 |
50 | guacamole: http://guacamole.sourceforge.net/
51 |
52 | - Web client, but Java servlet does pre-processing
53 |
54 | jsvnc: http://code.google.com/p/jsvnc/
55 |
56 | - No releases
57 |
58 | webvnc: http://code.google.com/p/webvnc/
59 |
60 | - Jetty web server gateway, no updates since April 2008.
61 |
62 | RealVNC Java applet: http://www.realvnc.com/support/javavncviewer.html
63 |
64 | - Java applet
65 |
66 | Flashlight-VNC: http://www.wizhelp.com/flashlight-vnc/
67 |
68 | - Adobe Flash implementation
69 |
70 | FVNC: http://osflash.org/fvnc
71 |
72 | - Adbove Flash implementation
73 |
74 | CanVNC: http://canvnc.sourceforge.net/
75 |
76 | - HTML client with REST to VNC python proxy. Mostly vapor.
77 |
--------------------------------------------------------------------------------
/app/locale/ja.json:
--------------------------------------------------------------------------------
1 | {
2 | "Connecting...": "接続しています...",
3 | "Disconnecting...": "切断しています...",
4 | "Reconnecting...": "再接続しています...",
5 | "Internal error": "内部エラー",
6 | "Must set host": "ホストを設定する必要があります",
7 | "Connected (encrypted) to ": "接続しました (暗号化済み): ",
8 | "Connected (unencrypted) to ": "接続しました (暗号化されていません): ",
9 | "Something went wrong, connection is closed": "何らかの問題で、接続が閉じられました",
10 | "Failed to connect to server": "サーバーへの接続に失敗しました",
11 | "Disconnected": "切断しました",
12 | "New connection has been rejected with reason: ": "新規接続は次の理由で拒否されました: ",
13 | "New connection has been rejected": "新規接続は拒否されました",
14 | "Credentials are required": "資格情報が必要です",
15 | "noVNC encountered an error:": "noVNC でエラーが発生しました:",
16 | "Hide/Show the control bar": "コントロールバーを隠す/表示する",
17 | "Drag": "ドラッグ",
18 | "Move/Drag Viewport": "ビューポートを移動/ドラッグ",
19 | "Keyboard": "キーボード",
20 | "Show Keyboard": "キーボードを表示",
21 | "Extra keys": "追加キー",
22 | "Show Extra Keys": "追加キーを表示",
23 | "Ctrl": "Ctrl",
24 | "Toggle Ctrl": "Ctrl キーを切り替え",
25 | "Alt": "Alt",
26 | "Toggle Alt": "Alt キーを切り替え",
27 | "Toggle Windows": "Windows キーを切り替え",
28 | "Windows": "Windows",
29 | "Send Tab": "Tab キーを送信",
30 | "Tab": "Tab",
31 | "Esc": "Esc",
32 | "Send Escape": "Escape キーを送信",
33 | "Ctrl+Alt+Del": "Ctrl+Alt+Del",
34 | "Send Ctrl-Alt-Del": "Ctrl-Alt-Del を送信",
35 | "Shutdown/Reboot": "シャットダウン/再起動",
36 | "Shutdown/Reboot...": "シャットダウン/再起動...",
37 | "Power": "電源",
38 | "Shutdown": "シャットダウン",
39 | "Reboot": "再起動",
40 | "Reset": "リセット",
41 | "Clipboard": "クリップボード",
42 | "Clear": "クリア",
43 | "Fullscreen": "全画面表示",
44 | "Settings": "設定",
45 | "Shared Mode": "共有モード",
46 | "View Only": "表示のみ",
47 | "Clip to Window": "ウィンドウにクリップ",
48 | "Scaling Mode:": "スケーリングモード:",
49 | "None": "なし",
50 | "Local Scaling": "ローカルスケーリング",
51 | "Remote Resizing": "リモートでリサイズ",
52 | "Advanced": "高度",
53 | "Quality:": "品質:",
54 | "Compression level:": "圧縮レベル:",
55 | "Repeater ID:": "リピーター ID:",
56 | "WebSocket": "WebSocket",
57 | "Encrypt": "暗号化",
58 | "Host:": "ホスト:",
59 | "Port:": "ポート:",
60 | "Path:": "パス:",
61 | "Automatic Reconnect": "自動再接続",
62 | "Reconnect Delay (ms):": "再接続する遅延 (ミリ秒):",
63 | "Show Dot when No Cursor": "カーソルがないときにドットを表示",
64 | "Logging:": "ロギング:",
65 | "Version:": "バージョン:",
66 | "Disconnect": "切断",
67 | "Connect": "接続",
68 | "Username:": "ユーザー名:",
69 | "Password:": "パスワード:",
70 | "Send Credentials": "資格情報を送信",
71 | "Cancel": "キャンセル"
72 | }
--------------------------------------------------------------------------------
/app/error-handler.js:
--------------------------------------------------------------------------------
1 | /*
2 | * noVNC: HTML5 VNC client
3 | * Copyright (C) 2019 The noVNC Authors
4 | * Licensed under MPL 2.0 (see LICENSE.txt)
5 | *
6 | * See README.md for usage and integration instructions.
7 | */
8 |
9 | // NB: this should *not* be included as a module until we have
10 | // native support in the browsers, so that our error handler
11 | // can catch script-loading errors.
12 |
13 | // No ES6 can be used in this file since it's used for the translation
14 | /* eslint-disable prefer-arrow-callback */
15 |
16 | (function _scope() {
17 | "use strict";
18 |
19 | // Fallback for all uncought errors
20 | function handleError(event, err) {
21 | try {
22 | const msg = document.getElementById('noVNC_fallback_errormsg');
23 |
24 | // Only show the initial error
25 | if (msg.hasChildNodes()) {
26 | return false;
27 | }
28 |
29 | let div = document.createElement("div");
30 | div.classList.add('noVNC_message');
31 | div.appendChild(document.createTextNode(event.message));
32 | msg.appendChild(div);
33 |
34 | if (event.filename) {
35 | div = document.createElement("div");
36 | div.className = 'noVNC_location';
37 | let text = event.filename;
38 | if (event.lineno !== undefined) {
39 | text += ":" + event.lineno;
40 | if (event.colno !== undefined) {
41 | text += ":" + event.colno;
42 | }
43 | }
44 | div.appendChild(document.createTextNode(text));
45 | msg.appendChild(div);
46 | }
47 |
48 | if (err && err.stack) {
49 | div = document.createElement("div");
50 | div.className = 'noVNC_stack';
51 | div.appendChild(document.createTextNode(err.stack));
52 | msg.appendChild(div);
53 | }
54 |
55 | document.getElementById('noVNC_fallback_error')
56 | .classList.add("noVNC_open");
57 | } catch (exc) {
58 | document.write("noVNC encountered an error.");
59 | }
60 | // Don't return true since this would prevent the error
61 | // from being printed to the browser console.
62 | return false;
63 | }
64 | window.addEventListener('error', function onerror(evt) { handleError(evt, evt.error); });
65 | window.addEventListener('unhandledrejection', function onreject(evt) { handleError(evt.reason, evt.reason); });
66 | })();
67 |
--------------------------------------------------------------------------------
/app/images/windows.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/tests/fake.websocket.js:
--------------------------------------------------------------------------------
1 | import Base64 from '../core/base64.js';
2 |
3 | export default class FakeWebSocket {
4 | constructor(uri, protocols) {
5 | this.url = uri;
6 | this.binaryType = "arraybuffer";
7 | this.extensions = "";
8 |
9 | this.onerror = null;
10 | this.onmessage = null;
11 | this.onopen = null;
12 |
13 | if (!protocols || typeof protocols === 'string') {
14 | this.protocol = protocols;
15 | } else {
16 | this.protocol = protocols[0];
17 | }
18 |
19 | this._sendQueue = new Uint8Array(20000);
20 |
21 | this.readyState = FakeWebSocket.CONNECTING;
22 | this.bufferedAmount = 0;
23 |
24 | this._isFake = true;
25 | }
26 |
27 | close(code, reason) {
28 | this.readyState = FakeWebSocket.CLOSED;
29 | if (this.onclose) {
30 | this.onclose(new CloseEvent("close", { 'code': code, 'reason': reason, 'wasClean': true }));
31 | }
32 | }
33 |
34 | send(data) {
35 | if (this.protocol == 'base64') {
36 | data = Base64.decode(data);
37 | } else {
38 | data = new Uint8Array(data);
39 | }
40 | this._sendQueue.set(data, this.bufferedAmount);
41 | this.bufferedAmount += data.length;
42 | }
43 |
44 | _getSentData() {
45 | const res = new Uint8Array(this._sendQueue.buffer, 0, this.bufferedAmount);
46 | this.bufferedAmount = 0;
47 | return res;
48 | }
49 |
50 | _open() {
51 | this.readyState = FakeWebSocket.OPEN;
52 | if (this.onopen) {
53 | this.onopen(new Event('open'));
54 | }
55 | }
56 |
57 | _receiveData(data) {
58 | // Break apart the data to expose bugs where we assume data is
59 | // neatly packaged
60 | for (let i = 0;i < data.length;i++) {
61 | let buf = data.subarray(i, i+1);
62 | this.onmessage(new MessageEvent("message", { 'data': buf }));
63 | }
64 | }
65 | }
66 |
67 | FakeWebSocket.OPEN = WebSocket.OPEN;
68 | FakeWebSocket.CONNECTING = WebSocket.CONNECTING;
69 | FakeWebSocket.CLOSING = WebSocket.CLOSING;
70 | FakeWebSocket.CLOSED = WebSocket.CLOSED;
71 |
72 | FakeWebSocket._isFake = true;
73 |
74 | FakeWebSocket.replace = () => {
75 | if (!WebSocket._isFake) {
76 | const realVersion = WebSocket;
77 | // eslint-disable-next-line no-global-assign
78 | WebSocket = FakeWebSocket;
79 | FakeWebSocket._realVersion = realVersion;
80 | }
81 | };
82 |
83 | FakeWebSocket.restore = () => {
84 | if (WebSocket._isFake) {
85 | // eslint-disable-next-line no-global-assign
86 | WebSocket = WebSocket._realVersion;
87 | }
88 | };
89 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 |
3 | // The Safari launcher is broken, so construct our own
4 | function SafariBrowser(id, baseBrowserDecorator, args) {
5 | baseBrowserDecorator(this);
6 |
7 | this._start = function(url) {
8 | this._execCommand('/usr/bin/open', ['-W', '-n', '-a', 'Safari', url]);
9 | }
10 | }
11 |
12 | SafariBrowser.prototype = {
13 | name: 'Safari'
14 | }
15 |
16 | module.exports = (config) => {
17 | let browsers = [];
18 |
19 | if (process.env.TEST_BROWSER_NAME) {
20 | browsers = process.env.TEST_BROWSER_NAME.split(',');
21 | }
22 |
23 | const my_conf = {
24 |
25 | // base path that will be used to resolve all patterns (eg. files, exclude)
26 | basePath: '',
27 |
28 | // frameworks to use
29 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
30 | frameworks: ['mocha', 'sinon-chai'],
31 |
32 | // list of files / patterns to load in the browser (loaded in order)
33 | files: [
34 | { pattern: 'app/localization.js', included: false, type: 'module' },
35 | { pattern: 'app/webutil.js', included: false, type: 'module' },
36 | { pattern: 'core/**/*.js', included: false, type: 'module' },
37 | { pattern: 'vendor/pako/**/*.js', included: false, type: 'module' },
38 | { pattern: 'tests/test.*.js', type: 'module' },
39 | { pattern: 'tests/fake.*.js', included: false, type: 'module' },
40 | { pattern: 'tests/assertions.js', type: 'module' },
41 | ],
42 |
43 | client: {
44 | mocha: {
45 | // replace Karma debug page with mocha display
46 | 'reporter': 'html',
47 | 'ui': 'bdd'
48 | }
49 | },
50 |
51 | // list of files to exclude
52 | exclude: [
53 | ],
54 |
55 | plugins: [
56 | 'karma-*',
57 | '@chiragrupani/karma-chromium-edge-launcher',
58 | { 'launcher:Safari': [ 'type', SafariBrowser ] },
59 | ],
60 |
61 | // start these browsers
62 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
63 | browsers: browsers,
64 |
65 | // test results reporter to use
66 | // possible values: 'dots', 'progress'
67 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
68 | reporters: ['mocha'],
69 |
70 |
71 | // level of logging
72 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
73 | logLevel: config.LOG_INFO,
74 |
75 |
76 | // enable / disable watching file and executing tests whenever any file changes
77 | autoWatch: false,
78 |
79 | // Continuous Integration mode
80 | // if true, Karma captures browsers, runs the tests and exits
81 | singleRun: true,
82 | };
83 |
84 | config.set(my_conf);
85 | };
86 |
--------------------------------------------------------------------------------
/tests/test.deflator.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | const expect = chai.expect;
3 |
4 | import { inflateInit, inflate } from "../vendor/pako/lib/zlib/inflate.js";
5 | import ZStream from "../vendor/pako/lib/zlib/zstream.js";
6 | import Deflator from "../core/deflator.js";
7 |
8 | function _inflator(compText, expected) {
9 | let strm = new ZStream();
10 | let chunkSize = 1024 * 10 * 10;
11 | strm.output = new Uint8Array(chunkSize);
12 |
13 | inflateInit(strm, 5);
14 |
15 | if (expected > chunkSize) {
16 | chunkSize = expected;
17 | strm.output = new Uint8Array(chunkSize);
18 | }
19 |
20 | /* eslint-disable camelcase */
21 | strm.input = compText;
22 | strm.avail_in = strm.input.length;
23 | strm.next_in = 0;
24 |
25 | strm.next_out = 0;
26 | strm.avail_out = expected.length;
27 | /* eslint-enable camelcase */
28 |
29 | let ret = inflate(strm, 0);
30 |
31 | // Check that return code is not an error
32 | expect(ret).to.be.greaterThan(-1);
33 |
34 | return new Uint8Array(strm.output.buffer, 0, strm.next_out);
35 | }
36 |
37 | describe('Deflate data', function () {
38 |
39 | it('should be able to deflate messages', function () {
40 | let deflator = new Deflator();
41 |
42 | let text = "123asdf";
43 | let preText = new Uint8Array(text.length);
44 | for (let i = 0; i < preText.length; i++) {
45 | preText[i] = text.charCodeAt(i);
46 | }
47 |
48 | let compText = deflator.deflate(preText);
49 |
50 | let inflatedText = _inflator(compText, text.length);
51 | expect(inflatedText).to.array.equal(preText);
52 |
53 | });
54 |
55 | it('should be able to deflate large messages', function () {
56 | let deflator = new Deflator();
57 |
58 | /* Generate a big string with random characters. Used because
59 | repetition of letters might be deflated more effectively than
60 | random ones. */
61 | let text = "";
62 | let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
63 | for (let i = 0; i < 300000; i++) {
64 | text += characters.charAt(Math.floor(Math.random() * characters.length));
65 | }
66 |
67 | let preText = new Uint8Array(text.length);
68 | for (let i = 0; i < preText.length; i++) {
69 | preText[i] = text.charCodeAt(i);
70 | }
71 |
72 | let compText = deflator.deflate(preText);
73 |
74 | //Check that the compressed size is expected size
75 | expect(compText.length).to.be.greaterThan((1024 * 10 * 10) * 2);
76 |
77 | let inflatedText = _inflator(compText, text.length);
78 |
79 | expect(inflatedText).to.array.equal(preText);
80 |
81 | });
82 | });
83 |
--------------------------------------------------------------------------------
/app/images/handle.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
83 |
--------------------------------------------------------------------------------
/app/locale/es.json:
--------------------------------------------------------------------------------
1 | {
2 | "Connecting...": "Conectando...",
3 | "Connected (encrypted) to ": "Conectado (con encriptación) a",
4 | "Connected (unencrypted) to ": "Conectado (sin encriptación) a",
5 | "Disconnecting...": "Desconectando...",
6 | "Disconnected": "Desconectado",
7 | "Must set host": "Debes configurar el host",
8 | "Reconnecting...": "Reconectando...",
9 | "Password is required": "Contraseña es obligatoria",
10 | "Disconnect timeout": "Tiempo de desconexión agotado",
11 | "noVNC encountered an error:": "noVNC ha encontrado un error:",
12 | "Hide/Show the control bar": "Ocultar/Mostrar la barra de control",
13 | "Move/Drag Viewport": "Mover/Arrastrar la ventana",
14 | "viewport drag": "Arrastrar la ventana",
15 | "Active Mouse Button": "Botón activo del ratón",
16 | "No mousebutton": "Ningún botón del ratón",
17 | "Left mousebutton": "Botón izquierdo del ratón",
18 | "Middle mousebutton": "Botón central del ratón",
19 | "Right mousebutton": "Botón derecho del ratón",
20 | "Keyboard": "Teclado",
21 | "Show Keyboard": "Mostrar teclado",
22 | "Extra keys": "Teclas adicionales",
23 | "Show Extra Keys": "Mostrar Teclas Adicionales",
24 | "Ctrl": "Ctrl",
25 | "Toggle Ctrl": "Pulsar/Soltar Ctrl",
26 | "Alt": "Alt",
27 | "Toggle Alt": "Pulsar/Soltar Alt",
28 | "Send Tab": "Enviar Tabulación",
29 | "Tab": "Tabulación",
30 | "Esc": "Esc",
31 | "Send Escape": "Enviar Escape",
32 | "Ctrl+Alt+Del": "Ctrl+Alt+Del",
33 | "Send Ctrl-Alt-Del": "Enviar Ctrl+Alt+Del",
34 | "Shutdown/Reboot": "Apagar/Reiniciar",
35 | "Shutdown/Reboot...": "Apagar/Reiniciar...",
36 | "Power": "Encender",
37 | "Shutdown": "Apagar",
38 | "Reboot": "Reiniciar",
39 | "Reset": "Restablecer",
40 | "Clipboard": "Portapapeles",
41 | "Clear": "Vaciar",
42 | "Fullscreen": "Pantalla Completa",
43 | "Settings": "Configuraciones",
44 | "Shared Mode": "Modo Compartido",
45 | "View Only": "Solo visualización",
46 | "Clip to Window": "Recortar al tamaño de la ventana",
47 | "Scaling Mode:": "Modo de escalado:",
48 | "None": "Ninguno",
49 | "Local Scaling": "Escalado Local",
50 | "Local Downscaling": "Reducción de escala local",
51 | "Remote Resizing": "Cambio de tamaño remoto",
52 | "Advanced": "Avanzado",
53 | "Local Cursor": "Cursor Local",
54 | "Repeater ID:": "ID del Repetidor",
55 | "WebSocket": "WebSocket",
56 | "Encrypt": "",
57 | "Host:": "Host",
58 | "Port:": "Puesto",
59 | "Path:": "Ruta",
60 | "Automatic Reconnect": "Reconexión automática",
61 | "Reconnect Delay (ms):": "Retraso en la reconexión (ms)",
62 | "Logging:": "Logging",
63 | "Disconnect": "Desconectar",
64 | "Connect": "Conectar",
65 | "Password:": "Contraseña",
66 | "Cancel": "Cancelar",
67 | "Canvas not supported.": "Canvas no está soportado"
68 | }
--------------------------------------------------------------------------------
/core/deflator.js:
--------------------------------------------------------------------------------
1 | /*
2 | * noVNC: HTML5 VNC client
3 | * Copyright (C) 2020 The noVNC Authors
4 | * Licensed under MPL 2.0 (see LICENSE.txt)
5 | *
6 | * See README.md for usage and integration instructions.
7 | */
8 |
9 | import { deflateInit, deflate } from "../vendor/pako/lib/zlib/deflate.js";
10 | import { Z_FULL_FLUSH } from "../vendor/pako/lib/zlib/deflate.js";
11 | import ZStream from "../vendor/pako/lib/zlib/zstream.js";
12 |
13 | export default class Deflator {
14 | constructor() {
15 | this.strm = new ZStream();
16 | this.chunkSize = 1024 * 10 * 10;
17 | this.outputBuffer = new Uint8Array(this.chunkSize);
18 | this.windowBits = 5;
19 |
20 | deflateInit(this.strm, this.windowBits);
21 | }
22 |
23 | deflate(inData) {
24 | /* eslint-disable camelcase */
25 | this.strm.input = inData;
26 | this.strm.avail_in = this.strm.input.length;
27 | this.strm.next_in = 0;
28 | this.strm.output = this.outputBuffer;
29 | this.strm.avail_out = this.chunkSize;
30 | this.strm.next_out = 0;
31 | /* eslint-enable camelcase */
32 |
33 | let lastRet = deflate(this.strm, Z_FULL_FLUSH);
34 | let outData = new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
35 |
36 | if (lastRet < 0) {
37 | throw new Error("zlib deflate failed");
38 | }
39 |
40 | if (this.strm.avail_in > 0) {
41 | // Read chunks until done
42 |
43 | let chunks = [outData];
44 | let totalLen = outData.length;
45 | do {
46 | /* eslint-disable camelcase */
47 | this.strm.output = new Uint8Array(this.chunkSize);
48 | this.strm.next_out = 0;
49 | this.strm.avail_out = this.chunkSize;
50 | /* eslint-enable camelcase */
51 |
52 | lastRet = deflate(this.strm, Z_FULL_FLUSH);
53 |
54 | if (lastRet < 0) {
55 | throw new Error("zlib deflate failed");
56 | }
57 |
58 | let chunk = new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
59 | totalLen += chunk.length;
60 | chunks.push(chunk);
61 | } while (this.strm.avail_in > 0);
62 |
63 | // Combine chunks into a single data
64 |
65 | let newData = new Uint8Array(totalLen);
66 | let offset = 0;
67 |
68 | for (let i = 0; i < chunks.length; i++) {
69 | newData.set(chunks[i], offset);
70 | offset += chunks[i].length;
71 | }
72 |
73 | outData = newData;
74 | }
75 |
76 | /* eslint-disable camelcase */
77 | this.strm.input = null;
78 | this.strm.avail_in = 0;
79 | this.strm.next_in = 0;
80 | /* eslint-enable camelcase */
81 |
82 | return outData;
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/app/locale/tr.json:
--------------------------------------------------------------------------------
1 | {
2 | "Connecting...": "Bağlanıyor...",
3 | "Disconnecting...": "Bağlantı kesiliyor...",
4 | "Reconnecting...": "Yeniden bağlantı kuruluyor...",
5 | "Internal error": "İç hata",
6 | "Must set host": "Sunucuyu kur",
7 | "Connected (encrypted) to ": "Bağlı (şifrelenmiş)",
8 | "Connected (unencrypted) to ": "Bağlandı (şifrelenmemiş)",
9 | "Something went wrong, connection is closed": "Bir şeyler ters gitti, bağlantı kesildi",
10 | "Disconnected": "Bağlantı kesildi",
11 | "New connection has been rejected with reason: ": "Bağlantı aşağıdaki nedenlerden dolayı reddedildi: ",
12 | "New connection has been rejected": "Bağlantı reddedildi",
13 | "Password is required": "Şifre gerekli",
14 | "noVNC encountered an error:": "Bir hata oluştu:",
15 | "Hide/Show the control bar": "Denetim masasını Gizle/Göster",
16 | "Move/Drag Viewport": "Görünümü Taşı/Sürükle",
17 | "viewport drag": "Görüntü penceresini sürükle",
18 | "Active Mouse Button": "Aktif Fare Düğmesi",
19 | "No mousebutton": "Fare düğmesi yok",
20 | "Left mousebutton": "Farenin sol düğmesi",
21 | "Middle mousebutton": "Farenin orta düğmesi",
22 | "Right mousebutton": "Farenin sağ düğmesi",
23 | "Keyboard": "Klavye",
24 | "Show Keyboard": "Klavye Düzenini Göster",
25 | "Extra keys": "Ekstra tuşlar",
26 | "Show Extra Keys": "Ekstra tuşları göster",
27 | "Ctrl": "Ctrl",
28 | "Toggle Ctrl": "Ctrl Değiştir ",
29 | "Alt": "Alt",
30 | "Toggle Alt": "Alt Değiştir",
31 | "Send Tab": "Sekme Gönder",
32 | "Tab": "Sekme",
33 | "Esc": "Esc",
34 | "Send Escape": "Boşluk Gönder",
35 | "Ctrl+Alt+Del": "Ctrl + Alt + Del",
36 | "Send Ctrl-Alt-Del": "Ctrl-Alt-Del Gönder",
37 | "Shutdown/Reboot": "Kapat/Yeniden Başlat",
38 | "Shutdown/Reboot...": "Kapat/Yeniden Başlat...",
39 | "Power": "Güç",
40 | "Shutdown": "Kapat",
41 | "Reboot": "Yeniden Başlat",
42 | "Reset": "Sıfırla",
43 | "Clipboard": "Pano",
44 | "Clear": "Temizle",
45 | "Fullscreen": "Tam Ekran",
46 | "Settings": "Ayarlar",
47 | "Shared Mode": "Paylaşım Modu",
48 | "View Only": "Sadece Görüntüle",
49 | "Clip to Window": "Pencereye Tıkla",
50 | "Scaling Mode:": "Ölçekleme Modu:",
51 | "None": "Bilinmeyen",
52 | "Local Scaling": "Yerel Ölçeklendirme",
53 | "Remote Resizing": "Uzaktan Yeniden Boyutlandırma",
54 | "Advanced": "Gelişmiş",
55 | "Repeater ID:": "Tekralayıcı ID:",
56 | "WebSocket": "WebSocket",
57 | "Encrypt": "Şifrele",
58 | "Host:": "Ana makine:",
59 | "Port:": "Port:",
60 | "Path:": "Yol:",
61 | "Automatic Reconnect": "Otomatik Yeniden Bağlan",
62 | "Reconnect Delay (ms):": "Yeniden Bağlanma Süreci (ms):",
63 | "Logging:": "Giriş yapılıyor:",
64 | "Disconnect": "Bağlantıyı Kes",
65 | "Connect": "Bağlan",
66 | "Password:": "Parola:",
67 | "Cancel": "Vazgeç",
68 | "Canvas not supported.": "Tuval desteklenmiyor."
69 | }
--------------------------------------------------------------------------------
/app/locale/sv.json:
--------------------------------------------------------------------------------
1 | {
2 | "Connecting...": "Ansluter...",
3 | "Disconnecting...": "Kopplar ner...",
4 | "Reconnecting...": "Återansluter...",
5 | "Internal error": "Internt fel",
6 | "Must set host": "Du måste specifiera en värd",
7 | "Connected (encrypted) to ": "Ansluten (krypterat) till ",
8 | "Connected (unencrypted) to ": "Ansluten (okrypterat) till ",
9 | "Something went wrong, connection is closed": "Något gick fel, anslutningen avslutades",
10 | "Failed to connect to server": "Misslyckades att ansluta till servern",
11 | "Disconnected": "Frånkopplad",
12 | "New connection has been rejected with reason: ": "Ny anslutning har blivit nekad med följande skäl: ",
13 | "New connection has been rejected": "Ny anslutning har blivit nekad",
14 | "Credentials are required": "Användaruppgifter krävs",
15 | "noVNC encountered an error:": "noVNC stötte på ett problem:",
16 | "Hide/Show the control bar": "Göm/Visa kontrollbaren",
17 | "Drag": "Dra",
18 | "Move/Drag Viewport": "Flytta/Dra Vyn",
19 | "Keyboard": "Tangentbord",
20 | "Show Keyboard": "Visa Tangentbord",
21 | "Extra keys": "Extraknappar",
22 | "Show Extra Keys": "Visa Extraknappar",
23 | "Ctrl": "Ctrl",
24 | "Toggle Ctrl": "Växla Ctrl",
25 | "Alt": "Alt",
26 | "Toggle Alt": "Växla Alt",
27 | "Toggle Windows": "Växla Windows",
28 | "Windows": "Windows",
29 | "Send Tab": "Skicka Tab",
30 | "Tab": "Tab",
31 | "Esc": "Esc",
32 | "Send Escape": "Skicka Escape",
33 | "Ctrl+Alt+Del": "Ctrl+Alt+Del",
34 | "Send Ctrl-Alt-Del": "Skicka Ctrl-Alt-Del",
35 | "Shutdown/Reboot": "Stäng av/Boota om",
36 | "Shutdown/Reboot...": "Stäng av/Boota om...",
37 | "Power": "Ström",
38 | "Shutdown": "Stäng av",
39 | "Reboot": "Boota om",
40 | "Reset": "Återställ",
41 | "Clipboard": "Urklipp",
42 | "Clear": "Rensa",
43 | "Fullscreen": "Fullskärm",
44 | "Settings": "Inställningar",
45 | "Shared Mode": "Delat Läge",
46 | "View Only": "Endast Visning",
47 | "Clip to Window": "Begränsa till Fönster",
48 | "Scaling Mode:": "Skalningsläge:",
49 | "None": "Ingen",
50 | "Local Scaling": "Lokal Skalning",
51 | "Remote Resizing": "Ändra Storlek",
52 | "Advanced": "Avancerat",
53 | "Quality:": "Kvalitet:",
54 | "Compression level:": "Kompressionsnivå:",
55 | "Repeater ID:": "Repeater-ID:",
56 | "WebSocket": "WebSocket",
57 | "Encrypt": "Kryptera",
58 | "Host:": "Värd:",
59 | "Port:": "Port:",
60 | "Path:": "Sökväg:",
61 | "Automatic Reconnect": "Automatisk Återanslutning",
62 | "Reconnect Delay (ms):": "Fördröjning (ms):",
63 | "Show Dot when No Cursor": "Visa prick när ingen muspekare finns",
64 | "Logging:": "Loggning:",
65 | "Version:": "Version:",
66 | "Disconnect": "Koppla från",
67 | "Connect": "Anslut",
68 | "Username:": "Användarnamn:",
69 | "Password:": "Lösenord:",
70 | "Send Credentials": "Skicka Användaruppgifter",
71 | "Cancel": "Avbryt"
72 | }
--------------------------------------------------------------------------------
/app/locale/pl.json:
--------------------------------------------------------------------------------
1 | {
2 | "Connecting...": "Łączenie...",
3 | "Disconnecting...": "Rozłączanie...",
4 | "Reconnecting...": "Łączenie...",
5 | "Internal error": "Błąd wewnętrzny",
6 | "Must set host": "Host i port są wymagane",
7 | "Connected (encrypted) to ": "Połączenie (szyfrowane) z ",
8 | "Connected (unencrypted) to ": "Połączenie (nieszyfrowane) z ",
9 | "Something went wrong, connection is closed": "Coś poszło źle, połączenie zostało zamknięte",
10 | "Disconnected": "Rozłączony",
11 | "New connection has been rejected with reason: ": "Nowe połączenie zostało odrzucone z powodu: ",
12 | "New connection has been rejected": "Nowe połączenie zostało odrzucone",
13 | "Password is required": "Hasło jest wymagane",
14 | "noVNC encountered an error:": "noVNC napotkało błąd:",
15 | "Hide/Show the control bar": "Pokaż/Ukryj pasek ustawień",
16 | "Move/Drag Viewport": "Ruszaj/Przeciągaj Viewport",
17 | "viewport drag": "przeciągnij viewport",
18 | "Active Mouse Button": "Aktywny Przycisk Myszy",
19 | "No mousebutton": "Brak przycisku myszy",
20 | "Left mousebutton": "Lewy przycisk myszy",
21 | "Middle mousebutton": "Środkowy przycisk myszy",
22 | "Right mousebutton": "Prawy przycisk myszy",
23 | "Keyboard": "Klawiatura",
24 | "Show Keyboard": "Pokaż klawiaturę",
25 | "Extra keys": "Przyciski dodatkowe",
26 | "Show Extra Keys": "Pokaż przyciski dodatkowe",
27 | "Ctrl": "Ctrl",
28 | "Toggle Ctrl": "Przełącz Ctrl",
29 | "Alt": "Alt",
30 | "Toggle Alt": "Przełącz Alt",
31 | "Send Tab": "Wyślij Tab",
32 | "Tab": "Tab",
33 | "Esc": "Esc",
34 | "Send Escape": "Wyślij Escape",
35 | "Ctrl+Alt+Del": "Ctrl+Alt+Del",
36 | "Send Ctrl-Alt-Del": "Wyślij Ctrl-Alt-Del",
37 | "Shutdown/Reboot": "Wyłącz/Uruchom ponownie",
38 | "Shutdown/Reboot...": "Wyłącz/Uruchom ponownie...",
39 | "Power": "Włączony",
40 | "Shutdown": "Wyłącz",
41 | "Reboot": "Uruchom ponownie",
42 | "Reset": "Resetuj",
43 | "Clipboard": "Schowek",
44 | "Clear": "Wyczyść",
45 | "Fullscreen": "Pełny ekran",
46 | "Settings": "Ustawienia",
47 | "Shared Mode": "Tryb Współdzielenia",
48 | "View Only": "Tylko Podgląd",
49 | "Clip to Window": "Przytnij do Okna",
50 | "Scaling Mode:": "Tryb Skalowania:",
51 | "None": "Brak",
52 | "Local Scaling": "Skalowanie lokalne",
53 | "Remote Resizing": "Skalowanie zdalne",
54 | "Advanced": "Zaawansowane",
55 | "Repeater ID:": "ID Repeatera:",
56 | "WebSocket": "WebSocket",
57 | "Encrypt": "Szyfrowanie",
58 | "Host:": "Host:",
59 | "Port:": "Port:",
60 | "Path:": "Ścieżka:",
61 | "Automatic Reconnect": "Automatycznie wznawiaj połączenie",
62 | "Reconnect Delay (ms):": "Opóźnienie wznawiania (ms):",
63 | "Logging:": "Poziom logowania:",
64 | "Disconnect": "Rozłącz",
65 | "Connect": "Połącz",
66 | "Password:": "Hasło:",
67 | "Cancel": "Anuluj",
68 | "Canvas not supported.": "Element Canvas nie jest wspierany."
69 | }
--------------------------------------------------------------------------------
/app/locale/cs.json:
--------------------------------------------------------------------------------
1 | {
2 | "Connecting...": "Připojení...",
3 | "Disconnecting...": "Odpojení...",
4 | "Reconnecting...": "Obnova připojení...",
5 | "Internal error": "Vnitřní chyba",
6 | "Must set host": "Hostitel musí být nastavení",
7 | "Connected (encrypted) to ": "Připojení (šifrované) k ",
8 | "Connected (unencrypted) to ": "Připojení (nešifrované) k ",
9 | "Something went wrong, connection is closed": "Něco se pokazilo, odpojeno",
10 | "Failed to connect to server": "Chyba připojení k serveru",
11 | "Disconnected": "Odpojeno",
12 | "New connection has been rejected with reason: ": "Nové připojení bylo odmítnuto s odůvodněním: ",
13 | "New connection has been rejected": "Nové připojení bylo odmítnuto",
14 | "Password is required": "Je vyžadováno heslo",
15 | "noVNC encountered an error:": "noVNC narazilo na chybu:",
16 | "Hide/Show the control bar": "Skrýt/zobrazit ovládací panel",
17 | "Move/Drag Viewport": "Přesunout/přetáhnout výřez",
18 | "viewport drag": "přesun výřezu",
19 | "Active Mouse Button": "Aktivní tlačítka myši",
20 | "No mousebutton": "Žádné",
21 | "Left mousebutton": "Levé tlačítko myši",
22 | "Middle mousebutton": "Prostřední tlačítko myši",
23 | "Right mousebutton": "Pravé tlačítko myši",
24 | "Keyboard": "Klávesnice",
25 | "Show Keyboard": "Zobrazit klávesnici",
26 | "Extra keys": "Extra klávesy",
27 | "Show Extra Keys": "Zobrazit extra klávesy",
28 | "Ctrl": "Ctrl",
29 | "Toggle Ctrl": "Přepnout Ctrl",
30 | "Alt": "Alt",
31 | "Toggle Alt": "Přepnout Alt",
32 | "Send Tab": "Odeslat tabulátor",
33 | "Tab": "Tab",
34 | "Esc": "Esc",
35 | "Send Escape": "Odeslat Esc",
36 | "Ctrl+Alt+Del": "Ctrl+Alt+Del",
37 | "Send Ctrl-Alt-Del": "Poslat Ctrl-Alt-Del",
38 | "Shutdown/Reboot": "Vypnutí/Restart",
39 | "Shutdown/Reboot...": "Vypnutí/Restart...",
40 | "Power": "Napájení",
41 | "Shutdown": "Vypnout",
42 | "Reboot": "Restart",
43 | "Reset": "Reset",
44 | "Clipboard": "Schránka",
45 | "Clear": "Vymazat",
46 | "Fullscreen": "Celá obrazovka",
47 | "Settings": "Nastavení",
48 | "Shared Mode": "Sdílený režim",
49 | "View Only": "Pouze prohlížení",
50 | "Clip to Window": "Přizpůsobit oknu",
51 | "Scaling Mode:": "Přizpůsobení velikosti",
52 | "None": "Žádné",
53 | "Local Scaling": "Místní",
54 | "Remote Resizing": "Vzdálené",
55 | "Advanced": "Pokročilé",
56 | "Repeater ID:": "ID opakovače",
57 | "WebSocket": "WebSocket",
58 | "Encrypt": "Šifrování:",
59 | "Host:": "Hostitel:",
60 | "Port:": "Port:",
61 | "Path:": "Cesta",
62 | "Automatic Reconnect": "Automatická obnova připojení",
63 | "Reconnect Delay (ms):": "Zpoždění připojení (ms)",
64 | "Show Dot when No Cursor": "Tečka místo chybějícího kurzoru myši",
65 | "Logging:": "Logování:",
66 | "Disconnect": "Odpojit",
67 | "Connect": "Připojit",
68 | "Password:": "Heslo",
69 | "Send Password": "Odeslat heslo",
70 | "Cancel": "Zrušit"
71 | }
--------------------------------------------------------------------------------
/tests/test.localization.js:
--------------------------------------------------------------------------------
1 | const expect = chai.expect;
2 | import { l10n } from '../app/localization.js';
3 |
4 | describe('Localization', function () {
5 | "use strict";
6 |
7 | describe('language selection', function () {
8 | let origNavigator;
9 | beforeEach(function () {
10 | // window.navigator is a protected read-only property in many
11 | // environments, so we need to redefine it whilst running these
12 | // tests.
13 | origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
14 |
15 | Object.defineProperty(window, "navigator", {value: {}});
16 | if (window.navigator.languages !== undefined) {
17 | // Object.defineProperty() doesn't work properly in old
18 | // versions of Chrome
19 | this.skip();
20 | }
21 |
22 | window.navigator.languages = [];
23 | });
24 | afterEach(function () {
25 | if (origNavigator !== undefined) {
26 | Object.defineProperty(window, "navigator", origNavigator);
27 | }
28 | });
29 |
30 | it('should use English by default', function () {
31 | expect(l10n.language).to.equal('en');
32 | });
33 | it('should use English if no user language matches', function () {
34 | window.navigator.languages = ["nl", "de"];
35 | l10n.setup(["es", "fr"]);
36 | expect(l10n.language).to.equal('en');
37 | });
38 | it('should use the most preferred user language', function () {
39 | window.navigator.languages = ["nl", "de", "fr"];
40 | l10n.setup(["es", "fr", "de"]);
41 | expect(l10n.language).to.equal('de');
42 | });
43 | it('should prefer sub-languages languages', function () {
44 | window.navigator.languages = ["pt-BR"];
45 | l10n.setup(["pt", "pt-BR"]);
46 | expect(l10n.language).to.equal('pt-BR');
47 | });
48 | it('should fall back to language "parents"', function () {
49 | window.navigator.languages = ["pt-BR"];
50 | l10n.setup(["fr", "pt", "de"]);
51 | expect(l10n.language).to.equal('pt');
52 | });
53 | it('should not use specific language when user asks for a generic language', function () {
54 | window.navigator.languages = ["pt", "de"];
55 | l10n.setup(["fr", "pt-BR", "de"]);
56 | expect(l10n.language).to.equal('de');
57 | });
58 | it('should handle underscore as a separator', function () {
59 | window.navigator.languages = ["pt-BR"];
60 | l10n.setup(["pt_BR"]);
61 | expect(l10n.language).to.equal('pt_BR');
62 | });
63 | it('should handle difference in case', function () {
64 | window.navigator.languages = ["pt-br"];
65 | l10n.setup(["pt-BR"]);
66 | expect(l10n.language).to.equal('pt-BR');
67 | });
68 | });
69 | });
70 |
--------------------------------------------------------------------------------
/app/locale/de.json:
--------------------------------------------------------------------------------
1 | {
2 | "Connecting...": "Verbinden...",
3 | "Disconnecting...": "Verbindung trennen...",
4 | "Reconnecting...": "Verbindung wiederherstellen...",
5 | "Internal error": "Interner Fehler",
6 | "Must set host": "Richten Sie den Server ein",
7 | "Connected (encrypted) to ": "Verbunden mit (verschlüsselt) ",
8 | "Connected (unencrypted) to ": "Verbunden mit (unverschlüsselt) ",
9 | "Something went wrong, connection is closed": "Etwas lief schief, Verbindung wurde getrennt",
10 | "Disconnected": "Verbindung zum Server getrennt",
11 | "New connection has been rejected with reason: ": "Verbindung wurde aus folgendem Grund abgelehnt: ",
12 | "New connection has been rejected": "Verbindung wurde abgelehnt",
13 | "Password is required": "Passwort ist erforderlich",
14 | "noVNC encountered an error:": "Ein Fehler ist aufgetreten:",
15 | "Hide/Show the control bar": "Kontrollleiste verstecken/anzeigen",
16 | "Move/Drag Viewport": "Ansichtsfenster verschieben/ziehen",
17 | "viewport drag": "Ansichtsfenster ziehen",
18 | "Active Mouse Button": "Aktive Maustaste",
19 | "No mousebutton": "Keine Maustaste",
20 | "Left mousebutton": "Linke Maustaste",
21 | "Middle mousebutton": "Mittlere Maustaste",
22 | "Right mousebutton": "Rechte Maustaste",
23 | "Keyboard": "Tastatur",
24 | "Show Keyboard": "Tastatur anzeigen",
25 | "Extra keys": "Zusatztasten",
26 | "Show Extra Keys": "Zusatztasten anzeigen",
27 | "Ctrl": "Strg",
28 | "Toggle Ctrl": "Strg umschalten",
29 | "Alt": "Alt",
30 | "Toggle Alt": "Alt umschalten",
31 | "Send Tab": "Tab senden",
32 | "Tab": "Tab",
33 | "Esc": "Esc",
34 | "Send Escape": "Escape senden",
35 | "Ctrl+Alt+Del": "Strg+Alt+Entf",
36 | "Send Ctrl-Alt-Del": "Strg+Alt+Entf senden",
37 | "Shutdown/Reboot": "Herunterfahren/Neustarten",
38 | "Shutdown/Reboot...": "Herunterfahren/Neustarten...",
39 | "Power": "Energie",
40 | "Shutdown": "Herunterfahren",
41 | "Reboot": "Neustarten",
42 | "Reset": "Zurücksetzen",
43 | "Clipboard": "Zwischenablage",
44 | "Clear": "Löschen",
45 | "Fullscreen": "Vollbild",
46 | "Settings": "Einstellungen",
47 | "Shared Mode": "Geteilter Modus",
48 | "View Only": "Nur betrachten",
49 | "Clip to Window": "Auf Fenster begrenzen",
50 | "Scaling Mode:": "Skalierungsmodus:",
51 | "None": "Keiner",
52 | "Local Scaling": "Lokales skalieren",
53 | "Remote Resizing": "Serverseitiges skalieren",
54 | "Advanced": "Erweitert",
55 | "Repeater ID:": "Repeater ID:",
56 | "WebSocket": "WebSocket",
57 | "Encrypt": "Verschlüsselt",
58 | "Host:": "Server:",
59 | "Port:": "Port:",
60 | "Path:": "Pfad:",
61 | "Automatic Reconnect": "Automatisch wiederverbinden",
62 | "Reconnect Delay (ms):": "Wiederverbindungsverzögerung (ms):",
63 | "Logging:": "Protokollierung:",
64 | "Disconnect": "Verbindung trennen",
65 | "Connect": "Verbinden",
66 | "Password:": "Passwort:",
67 | "Cancel": "Abbrechen",
68 | "Canvas not supported.": "Canvas nicht unterstützt."
69 | }
--------------------------------------------------------------------------------
/core/input/vkeys.js:
--------------------------------------------------------------------------------
1 | /*
2 | * noVNC: HTML5 VNC client
3 | * Copyright (C) 2018 The noVNC Authors
4 | * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
5 | */
6 |
7 | /*
8 | * Mapping between Microsoft® Windows® Virtual-Key codes and
9 | * HTML key codes.
10 | */
11 |
12 | export default {
13 | 0x08: 'Backspace',
14 | 0x09: 'Tab',
15 | 0x0a: 'NumpadClear',
16 | 0x0d: 'Enter',
17 | 0x10: 'ShiftLeft',
18 | 0x11: 'ControlLeft',
19 | 0x12: 'AltLeft',
20 | 0x13: 'Pause',
21 | 0x14: 'CapsLock',
22 | 0x15: 'Lang1',
23 | 0x19: 'Lang2',
24 | 0x1b: 'Escape',
25 | 0x1c: 'Convert',
26 | 0x1d: 'NonConvert',
27 | 0x20: 'Space',
28 | 0x21: 'PageUp',
29 | 0x22: 'PageDown',
30 | 0x23: 'End',
31 | 0x24: 'Home',
32 | 0x25: 'ArrowLeft',
33 | 0x26: 'ArrowUp',
34 | 0x27: 'ArrowRight',
35 | 0x28: 'ArrowDown',
36 | 0x29: 'Select',
37 | 0x2c: 'PrintScreen',
38 | 0x2d: 'Insert',
39 | 0x2e: 'Delete',
40 | 0x2f: 'Help',
41 | 0x30: 'Digit0',
42 | 0x31: 'Digit1',
43 | 0x32: 'Digit2',
44 | 0x33: 'Digit3',
45 | 0x34: 'Digit4',
46 | 0x35: 'Digit5',
47 | 0x36: 'Digit6',
48 | 0x37: 'Digit7',
49 | 0x38: 'Digit8',
50 | 0x39: 'Digit9',
51 | 0x5b: 'MetaLeft',
52 | 0x5c: 'MetaRight',
53 | 0x5d: 'ContextMenu',
54 | 0x5f: 'Sleep',
55 | 0x60: 'Numpad0',
56 | 0x61: 'Numpad1',
57 | 0x62: 'Numpad2',
58 | 0x63: 'Numpad3',
59 | 0x64: 'Numpad4',
60 | 0x65: 'Numpad5',
61 | 0x66: 'Numpad6',
62 | 0x67: 'Numpad7',
63 | 0x68: 'Numpad8',
64 | 0x69: 'Numpad9',
65 | 0x6a: 'NumpadMultiply',
66 | 0x6b: 'NumpadAdd',
67 | 0x6c: 'NumpadDecimal',
68 | 0x6d: 'NumpadSubtract',
69 | 0x6e: 'NumpadDecimal', // Duplicate, because buggy on Windows
70 | 0x6f: 'NumpadDivide',
71 | 0x70: 'F1',
72 | 0x71: 'F2',
73 | 0x72: 'F3',
74 | 0x73: 'F4',
75 | 0x74: 'F5',
76 | 0x75: 'F6',
77 | 0x76: 'F7',
78 | 0x77: 'F8',
79 | 0x78: 'F9',
80 | 0x79: 'F10',
81 | 0x7a: 'F11',
82 | 0x7b: 'F12',
83 | 0x7c: 'F13',
84 | 0x7d: 'F14',
85 | 0x7e: 'F15',
86 | 0x7f: 'F16',
87 | 0x80: 'F17',
88 | 0x81: 'F18',
89 | 0x82: 'F19',
90 | 0x83: 'F20',
91 | 0x84: 'F21',
92 | 0x85: 'F22',
93 | 0x86: 'F23',
94 | 0x87: 'F24',
95 | 0x90: 'NumLock',
96 | 0x91: 'ScrollLock',
97 | 0xa6: 'BrowserBack',
98 | 0xa7: 'BrowserForward',
99 | 0xa8: 'BrowserRefresh',
100 | 0xa9: 'BrowserStop',
101 | 0xaa: 'BrowserSearch',
102 | 0xab: 'BrowserFavorites',
103 | 0xac: 'BrowserHome',
104 | 0xad: 'AudioVolumeMute',
105 | 0xae: 'AudioVolumeDown',
106 | 0xaf: 'AudioVolumeUp',
107 | 0xb0: 'MediaTrackNext',
108 | 0xb1: 'MediaTrackPrevious',
109 | 0xb2: 'MediaStop',
110 | 0xb3: 'MediaPlayPause',
111 | 0xb4: 'LaunchMail',
112 | 0xb5: 'MediaSelect',
113 | 0xb6: 'LaunchApp1',
114 | 0xb7: 'LaunchApp2',
115 | 0xe1: 'AltRight', // Only when it is AltGraph
116 | };
117 |
--------------------------------------------------------------------------------
/app/locale/pt_BR.json:
--------------------------------------------------------------------------------
1 | {
2 | "Connecting...": "Conectando...",
3 | "Disconnecting...": "Desconectando...",
4 | "Reconnecting...": "Reconectando...",
5 | "Internal error": "Erro interno",
6 | "Must set host": "É necessário definir o host",
7 | "Connected (encrypted) to ": "Conectado (com criptografia) a ",
8 | "Connected (unencrypted) to ": "Conectado (sem criptografia) a ",
9 | "Something went wrong, connection is closed": "Algo deu errado. A conexão foi encerrada.",
10 | "Failed to connect to server": "Falha ao conectar-se ao servidor",
11 | "Disconnected": "Desconectado",
12 | "New connection has been rejected with reason: ": "A nova conexão foi rejeitada pelo motivo: ",
13 | "New connection has been rejected": "A nova conexão foi rejeitada",
14 | "Credentials are required": "Credenciais são obrigatórias",
15 | "noVNC encountered an error:": "O noVNC encontrou um erro:",
16 | "Hide/Show the control bar": "Esconder/mostrar a barra de controles",
17 | "Drag": "Arrastar",
18 | "Move/Drag Viewport": "Mover/arrastar a janela",
19 | "Keyboard": "Teclado",
20 | "Show Keyboard": "Mostrar teclado",
21 | "Extra keys": "Teclas adicionais",
22 | "Show Extra Keys": "Mostar teclas adicionais",
23 | "Ctrl": "Ctrl",
24 | "Toggle Ctrl": "Pressionar/soltar Ctrl",
25 | "Alt": "Alt",
26 | "Toggle Alt": "Pressionar/soltar Alt",
27 | "Toggle Windows": "Pressionar/soltar Windows",
28 | "Windows": "Windows",
29 | "Send Tab": "Enviar Tab",
30 | "Tab": "Tab",
31 | "Esc": "Esc",
32 | "Send Escape": "Enviar Esc",
33 | "Ctrl+Alt+Del": "Ctrl+Alt+Del",
34 | "Send Ctrl-Alt-Del": "Enviar Ctrl-Alt-Del",
35 | "Shutdown/Reboot": "Desligar/reiniciar",
36 | "Shutdown/Reboot...": "Desligar/reiniciar...",
37 | "Power": "Ligar",
38 | "Shutdown": "Desligar",
39 | "Reboot": "Reiniciar",
40 | "Reset": "Reiniciar (forçado)",
41 | "Clipboard": "Área de transferência",
42 | "Clear": "Limpar",
43 | "Fullscreen": "Tela cheia",
44 | "Settings": "Configurações",
45 | "Shared Mode": "Modo compartilhado",
46 | "View Only": "Apenas visualizar",
47 | "Clip to Window": "Recortar à janela",
48 | "Scaling Mode:": "Modo de dimensionamento:",
49 | "None": "Nenhum",
50 | "Local Scaling": "Local",
51 | "Remote Resizing": "Remoto",
52 | "Advanced": "Avançado",
53 | "Quality:": "Qualidade:",
54 | "Compression level:": "Nível de compressão:",
55 | "Repeater ID:": "ID do repetidor:",
56 | "WebSocket": "WebSocket",
57 | "Encrypt": "Criptografar",
58 | "Host:": "Host:",
59 | "Port:": "Porta:",
60 | "Path:": "Caminho:",
61 | "Automatic Reconnect": "Reconexão automática",
62 | "Reconnect Delay (ms):": "Atraso da reconexão (ms)",
63 | "Show Dot when No Cursor": "Mostrar ponto quando não há cursor",
64 | "Logging:": "Registros:",
65 | "Version:": "Versão:",
66 | "Disconnect": "Desconectar",
67 | "Connect": "Conectar",
68 | "Username:": "Nome de usuário:",
69 | "Password:": "Senha:",
70 | "Send Credentials": "Enviar credenciais",
71 | "Cancel": "Cancelar"
72 | }
--------------------------------------------------------------------------------
/app/locale/el.json:
--------------------------------------------------------------------------------
1 | {
2 | "Connecting...": "Συνδέεται...",
3 | "Disconnecting...": "Aποσυνδέεται...",
4 | "Reconnecting...": "Επανασυνδέεται...",
5 | "Internal error": "Εσωτερικό σφάλμα",
6 | "Must set host": "Πρέπει να οριστεί ο διακομιστής",
7 | "Connected (encrypted) to ": "Συνδέθηκε (κρυπτογραφημένα) με το ",
8 | "Connected (unencrypted) to ": "Συνδέθηκε (μη κρυπτογραφημένα) με το ",
9 | "Something went wrong, connection is closed": "Κάτι πήγε στραβά, η σύνδεση διακόπηκε",
10 | "Disconnected": "Αποσυνδέθηκε",
11 | "New connection has been rejected with reason: ": "Η νέα σύνδεση απορρίφθηκε διότι: ",
12 | "New connection has been rejected": "Η νέα σύνδεση απορρίφθηκε ",
13 | "Password is required": "Απαιτείται ο κωδικός πρόσβασης",
14 | "noVNC encountered an error:": "το noVNC αντιμετώπισε ένα σφάλμα:",
15 | "Hide/Show the control bar": "Απόκρυψη/Εμφάνιση γραμμής ελέγχου",
16 | "Move/Drag Viewport": "Μετακίνηση/Σύρσιμο Θεατού πεδίου",
17 | "viewport drag": "σύρσιμο θεατού πεδίου",
18 | "Active Mouse Button": "Ενεργό Πλήκτρο Ποντικιού",
19 | "No mousebutton": "Χωρίς Πλήκτρο Ποντικιού",
20 | "Left mousebutton": "Αριστερό Πλήκτρο Ποντικιού",
21 | "Middle mousebutton": "Μεσαίο Πλήκτρο Ποντικιού",
22 | "Right mousebutton": "Δεξί Πλήκτρο Ποντικιού",
23 | "Keyboard": "Πληκτρολόγιο",
24 | "Show Keyboard": "Εμφάνιση Πληκτρολογίου",
25 | "Extra keys": "Επιπλέον πλήκτρα",
26 | "Show Extra Keys": "Εμφάνιση Επιπλέον Πλήκτρων",
27 | "Ctrl": "Ctrl",
28 | "Toggle Ctrl": "Εναλλαγή Ctrl",
29 | "Alt": "Alt",
30 | "Toggle Alt": "Εναλλαγή Alt",
31 | "Send Tab": "Αποστολή Tab",
32 | "Tab": "Tab",
33 | "Esc": "Esc",
34 | "Send Escape": "Αποστολή Escape",
35 | "Ctrl+Alt+Del": "Ctrl+Alt+Del",
36 | "Send Ctrl-Alt-Del": "Αποστολή Ctrl-Alt-Del",
37 | "Shutdown/Reboot": "Κλείσιμο/Επανεκκίνηση",
38 | "Shutdown/Reboot...": "Κλείσιμο/Επανεκκίνηση...",
39 | "Power": "Απενεργοποίηση",
40 | "Shutdown": "Κλείσιμο",
41 | "Reboot": "Επανεκκίνηση",
42 | "Reset": "Επαναφορά",
43 | "Clipboard": "Πρόχειρο",
44 | "Clear": "Καθάρισμα",
45 | "Fullscreen": "Πλήρης Οθόνη",
46 | "Settings": "Ρυθμίσεις",
47 | "Shared Mode": "Κοινόχρηστη Λειτουργία",
48 | "View Only": "Μόνο Θέαση",
49 | "Clip to Window": "Αποκοπή στο όριο του Παράθυρου",
50 | "Scaling Mode:": "Λειτουργία Κλιμάκωσης:",
51 | "None": "Καμία",
52 | "Local Scaling": "Τοπική Κλιμάκωση",
53 | "Remote Resizing": "Απομακρυσμένη Αλλαγή μεγέθους",
54 | "Advanced": "Για προχωρημένους",
55 | "Repeater ID:": "Repeater ID:",
56 | "WebSocket": "WebSocket",
57 | "Encrypt": "Κρυπτογράφηση",
58 | "Host:": "Όνομα διακομιστή:",
59 | "Port:": "Πόρτα διακομιστή:",
60 | "Path:": "Διαδρομή:",
61 | "Automatic Reconnect": "Αυτόματη επανασύνδεση",
62 | "Reconnect Delay (ms):": "Καθυστέρηση επανασύνδεσης (ms):",
63 | "Logging:": "Καταγραφή:",
64 | "Disconnect": "Αποσύνδεση",
65 | "Connect": "Σύνδεση",
66 | "Password:": "Κωδικός Πρόσβασης:",
67 | "Cancel": "Ακύρωση",
68 | "Canvas not supported.": "Δεν υποστηρίζεται το στοιχείο Canvas"
69 | }
--------------------------------------------------------------------------------
/app/locale/ru.json:
--------------------------------------------------------------------------------
1 | {
2 | "Connecting...": "Подключение...",
3 | "Disconnecting...": "Отключение...",
4 | "Reconnecting...": "Переподключение...",
5 | "Internal error": "Внутренняя ошибка",
6 | "Must set host": "Задайте имя сервера или IP",
7 | "Connected (encrypted) to ": "Подключено (с шифрованием) к ",
8 | "Connected (unencrypted) to ": "Подключено (без шифрования) к ",
9 | "Something went wrong, connection is closed": "Что-то пошло не так, подключение разорвано",
10 | "Failed to connect to server": "Ошибка подключения к серверу",
11 | "Disconnected": "Отключено",
12 | "New connection has been rejected with reason: ": "Подключиться не удалось: ",
13 | "New connection has been rejected": "Подключиться не удалось",
14 | "Password is required": "Требуется пароль",
15 | "noVNC encountered an error:": "Ошибка noVNC: ",
16 | "Hide/Show the control bar": "Скрыть/Показать контрольную панель",
17 | "Move/Drag Viewport": "Переместить окно",
18 | "viewport drag": "Переместить окно",
19 | "Active Mouse Button": "Активировать кнопки мыши",
20 | "No mousebutton": "Отключить кнопки мыши",
21 | "Left mousebutton": "Левая кнопка мыши",
22 | "Middle mousebutton": "Средняя кнопка мыши",
23 | "Right mousebutton": "Правая кнопка мыши",
24 | "Keyboard": "Клавиатура",
25 | "Show Keyboard": "Показать клавиатуру",
26 | "Extra keys": "Доп. кнопки",
27 | "Show Extra Keys": "Показать дополнительные кнопки",
28 | "Ctrl": "Ctrl",
29 | "Toggle Ctrl": "Передать нажатие Ctrl",
30 | "Alt": "Alt",
31 | "Toggle Alt": "Передать нажатие Alt",
32 | "Toggle Windows": "Переключение вкладок",
33 | "Windows": "Вкладка",
34 | "Send Tab": "Передать нажатие Tab",
35 | "Tab": "Tab",
36 | "Esc": "Esc",
37 | "Send Escape": "Передать нажатие Escape",
38 | "Ctrl+Alt+Del": "Ctrl+Alt+Del",
39 | "Send Ctrl-Alt-Del": "Передать нажатие Ctrl-Alt-Del",
40 | "Shutdown/Reboot": "Выключить/Перезагрузить",
41 | "Shutdown/Reboot...": "Выключить/Перезагрузить...",
42 | "Power": "Питание",
43 | "Shutdown": "Выключить",
44 | "Reboot": "Перезагрузить",
45 | "Reset": "Сброс",
46 | "Clipboard": "Буфер обмена",
47 | "Clear": "Очистить",
48 | "Fullscreen": "Во весь экран",
49 | "Settings": "Настройки",
50 | "Shared Mode": "Общий режим",
51 | "View Only": "Просмотр",
52 | "Clip to Window": "В окно",
53 | "Scaling Mode:": "Масштаб:",
54 | "None": "Нет",
55 | "Local Scaling": "Локльный масштаб",
56 | "Remote Resizing": "Удаленный масштаб",
57 | "Advanced": "Дополнительно",
58 | "Repeater ID:": "Идентификатор ID:",
59 | "WebSocket": "WebSocket",
60 | "Encrypt": "Шифрование",
61 | "Host:": "Сервер:",
62 | "Port:": "Порт:",
63 | "Path:": "Путь:",
64 | "Automatic Reconnect": "Автоматическое переподключение",
65 | "Reconnect Delay (ms):": "Задержка переподключения (мс):",
66 | "Show Dot when No Cursor": "Показать точку вместо курсора",
67 | "Logging:": "Лог:",
68 | "Disconnect": "Отключение",
69 | "Connect": "Подключение",
70 | "Password:": "Пароль:",
71 | "Send Password": "Пароль: ",
72 | "Cancel": "Выход"
73 | }
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import globals from "globals";
2 | import path from "node:path";
3 | import { fileURLToPath } from "node:url";
4 | import js from "@eslint/js";
5 | import { FlatCompat } from "@eslint/eslintrc";
6 |
7 | const __filename = fileURLToPath(import.meta.url);
8 | const __dirname = path.dirname(__filename);
9 | const compat = new FlatCompat({
10 | baseDirectory: __dirname,
11 | recommendedConfig: js.configs.recommended,
12 | allConfig: js.configs.all
13 | });
14 |
15 | export default [{
16 | ignores: ["**/xtscancodes.js"],
17 | }, ...compat.extends("eslint:recommended"), {
18 | languageOptions: {
19 | globals: {
20 | ...globals.browser,
21 | },
22 |
23 | ecmaVersion: 5,
24 | sourceType: "module",
25 | },
26 |
27 | rules: {
28 | "no-unused-vars": ["error", {
29 | vars: "all",
30 | args: "none",
31 | ignoreRestSiblings: true,
32 | }],
33 |
34 | "no-constant-condition": ["error", {
35 | checkLoops: false,
36 | }],
37 |
38 | "no-var": "error",
39 | "no-useless-constructor": "error",
40 |
41 | "object-shorthand": ["error", "methods", {
42 | avoidQuotes: true,
43 | }],
44 |
45 | "prefer-arrow-callback": "error",
46 |
47 | "arrow-body-style": ["error", "as-needed", {
48 | requireReturnForObjectLiteral: false,
49 | }],
50 |
51 | "arrow-parens": ["error", "as-needed", {
52 | requireForBlockBody: true,
53 | }],
54 |
55 | "arrow-spacing": ["error"],
56 |
57 | "no-confusing-arrow": ["error", {
58 | allowParens: true,
59 | }],
60 |
61 | "brace-style": ["error", "1tbs", {
62 | allowSingleLine: true,
63 | }],
64 |
65 | indent: ["error", 4, {
66 | SwitchCase: 1,
67 |
68 | FunctionDeclaration: {
69 | parameters: "first",
70 | },
71 |
72 | CallExpression: {
73 | arguments: "first",
74 | },
75 |
76 | ArrayExpression: "first",
77 | ObjectExpression: "first",
78 | ignoreComments: true,
79 | }],
80 |
81 | "comma-spacing": ["error"],
82 | "comma-style": ["error"],
83 | curly: ["error", "multi-line"],
84 | "func-call-spacing": ["error"],
85 | "func-names": ["error"],
86 |
87 | "func-style": ["error", "declaration", {
88 | allowArrowFunctions: true,
89 | }],
90 |
91 | "key-spacing": ["error"],
92 | "keyword-spacing": ["error"],
93 | "no-trailing-spaces": ["error"],
94 | semi: ["error"],
95 | "space-before-blocks": ["error"],
96 |
97 | "space-before-function-paren": ["error", {
98 | anonymous: "always",
99 | named: "never",
100 | asyncArrow: "always",
101 | }],
102 |
103 | "switch-colon-spacing": ["error"],
104 |
105 | camelcase: ["error", {
106 | allow: ["^XK_", "^XF86XK_"],
107 | }],
108 | },
109 | }];
--------------------------------------------------------------------------------
/app/images/tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
87 |
--------------------------------------------------------------------------------
/app/locale/nl.json:
--------------------------------------------------------------------------------
1 | {
2 | "Connecting...": "Verbinden...",
3 | "Disconnecting...": "Verbinding verbreken...",
4 | "Reconnecting...": "Opnieuw verbinding maken...",
5 | "Internal error": "Interne fout",
6 | "Must set host": "Host moeten worden ingesteld",
7 | "Connected (encrypted) to ": "Verbonden (versleuteld) met ",
8 | "Connected (unencrypted) to ": "Verbonden (onversleuteld) met ",
9 | "Something went wrong, connection is closed": "Er iets fout gelopen, verbinding werd verbroken",
10 | "Failed to connect to server": "Verbinding maken met server is mislukt",
11 | "Disconnected": "Verbinding verbroken",
12 | "New connection has been rejected with reason: ": "Nieuwe verbinding is geweigerd omwille van de volgende reden: ",
13 | "New connection has been rejected": "Nieuwe verbinding is geweigerd",
14 | "Password is required": "Wachtwoord is vereist",
15 | "noVNC encountered an error:": "noVNC heeft een fout bemerkt:",
16 | "Hide/Show the control bar": "Verberg/Toon de bedieningsbalk",
17 | "Move/Drag Viewport": "Verplaats/Versleep Kijkvenster",
18 | "viewport drag": "kijkvenster slepen",
19 | "Active Mouse Button": "Actieve Muisknop",
20 | "No mousebutton": "Geen muisknop",
21 | "Left mousebutton": "Linker muisknop",
22 | "Middle mousebutton": "Middelste muisknop",
23 | "Right mousebutton": "Rechter muisknop",
24 | "Keyboard": "Toetsenbord",
25 | "Show Keyboard": "Toon Toetsenbord",
26 | "Extra keys": "Extra toetsen",
27 | "Show Extra Keys": "Toon Extra Toetsen",
28 | "Ctrl": "Ctrl",
29 | "Toggle Ctrl": "Ctrl omschakelen",
30 | "Alt": "Alt",
31 | "Toggle Alt": "Alt omschakelen",
32 | "Toggle Windows": "Windows omschakelen",
33 | "Windows": "Windows",
34 | "Send Tab": "Tab Sturen",
35 | "Tab": "Tab",
36 | "Esc": "Esc",
37 | "Send Escape": "Escape Sturen",
38 | "Ctrl+Alt+Del": "Ctrl-Alt-Del",
39 | "Send Ctrl-Alt-Del": "Ctrl-Alt-Del Sturen",
40 | "Shutdown/Reboot": "Uitschakelen/Herstarten",
41 | "Shutdown/Reboot...": "Uitschakelen/Herstarten...",
42 | "Power": "Systeem",
43 | "Shutdown": "Uitschakelen",
44 | "Reboot": "Herstarten",
45 | "Reset": "Resetten",
46 | "Clipboard": "Klembord",
47 | "Clear": "Wissen",
48 | "Fullscreen": "Volledig Scherm",
49 | "Settings": "Instellingen",
50 | "Shared Mode": "Gedeelde Modus",
51 | "View Only": "Alleen Kijken",
52 | "Clip to Window": "Randen buiten venster afsnijden",
53 | "Scaling Mode:": "Schaalmodus:",
54 | "None": "Geen",
55 | "Local Scaling": "Lokaal Schalen",
56 | "Remote Resizing": "Op Afstand Formaat Wijzigen",
57 | "Advanced": "Geavanceerd",
58 | "Repeater ID:": "Repeater ID:",
59 | "WebSocket": "WebSocket",
60 | "Encrypt": "Versleutelen",
61 | "Host:": "Host:",
62 | "Port:": "Poort:",
63 | "Path:": "Pad:",
64 | "Automatic Reconnect": "Automatisch Opnieuw Verbinden",
65 | "Reconnect Delay (ms):": "Vertraging voor Opnieuw Verbinden (ms):",
66 | "Show Dot when No Cursor": "Geef stip weer indien geen cursor",
67 | "Logging:": "Logmeldingen:",
68 | "Disconnect": "Verbinding verbreken",
69 | "Connect": "Verbinden",
70 | "Password:": "Wachtwoord:",
71 | "Send Password": "Verzend Wachtwoord:",
72 | "Cancel": "Annuleren"
73 | }
--------------------------------------------------------------------------------
/app/images/expander.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
70 |
--------------------------------------------------------------------------------
/tests/test.copyrect.js:
--------------------------------------------------------------------------------
1 | const expect = chai.expect;
2 |
3 | import Websock from '../core/websock.js';
4 | import Display from '../core/display.js';
5 |
6 | import CopyRectDecoder from '../core/decoders/copyrect.js';
7 |
8 | import FakeWebSocket from './fake.websocket.js';
9 |
10 | function testDecodeRect(decoder, x, y, width, height, data, display, depth) {
11 | let sock;
12 |
13 | sock = new Websock;
14 | sock.open("ws://example.com");
15 |
16 | sock.on('message', () => {
17 | decoder.decodeRect(x, y, width, height, sock, display, depth);
18 | });
19 |
20 | // Empty messages are filtered at multiple layers, so we need to
21 | // do a direct call
22 | if (data.length === 0) {
23 | decoder.decodeRect(x, y, width, height, sock, display, depth);
24 | } else {
25 | sock._websocket._receiveData(new Uint8Array(data));
26 | }
27 |
28 | display.flip();
29 | }
30 |
31 | describe('CopyRect Decoder', function () {
32 | let decoder;
33 | let display;
34 |
35 | before(FakeWebSocket.replace);
36 | after(FakeWebSocket.restore);
37 |
38 | beforeEach(function () {
39 | decoder = new CopyRectDecoder();
40 | display = new Display(document.createElement('canvas'));
41 | display.resize(4, 4);
42 | });
43 |
44 | it('should handle the CopyRect encoding', function () {
45 | // seed some initial data to copy
46 | display.fillRect(0, 0, 4, 4, [ 0x11, 0x22, 0x33 ]);
47 | display.fillRect(0, 0, 2, 2, [ 0x00, 0x00, 0xff ]);
48 | display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]);
49 |
50 | testDecodeRect(decoder, 0, 2, 2, 2,
51 | [0x00, 0x02, 0x00, 0x00],
52 | display, 24);
53 | testDecodeRect(decoder, 2, 2, 2, 2,
54 | [0x00, 0x00, 0x00, 0x00],
55 | display, 24);
56 |
57 | let targetData = new Uint8Array([
58 | 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
59 | 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
60 | 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
61 | 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
62 | ]);
63 |
64 | expect(display).to.have.displayed(targetData);
65 | });
66 |
67 | it('should handle empty rects', function () {
68 | display.fillRect(0, 0, 4, 4, [ 0x00, 0x00, 0xff ]);
69 | display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]);
70 | display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]);
71 |
72 | testDecodeRect(decoder, 1, 2, 0, 0, [0x00, 0x00, 0x00, 0x00], display, 24);
73 |
74 | let targetData = new Uint8Array([
75 | 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
76 | 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
77 | 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
78 | 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
79 | ]);
80 |
81 | expect(display).to.have.displayed(targetData);
82 | });
83 | });
84 |
--------------------------------------------------------------------------------
/app/images/settings.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
77 |
--------------------------------------------------------------------------------
/app/images/error.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
82 |
--------------------------------------------------------------------------------
/app/images/fullscreen.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
94 |
--------------------------------------------------------------------------------
/app/images/info.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
82 |
--------------------------------------------------------------------------------
/tests/test.util.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | const expect = chai.expect;
3 |
4 | import * as Log from '../core/util/logging.js';
5 | import { encodeUTF8, decodeUTF8 } from '../core/util/strings.js';
6 |
7 | describe('Utils', function () {
8 | "use strict";
9 |
10 | describe('logging functions', function () {
11 | beforeEach(function () {
12 | sinon.spy(console, 'log');
13 | sinon.spy(console, 'debug');
14 | sinon.spy(console, 'warn');
15 | sinon.spy(console, 'error');
16 | sinon.spy(console, 'info');
17 | });
18 |
19 | afterEach(function () {
20 | console.log.restore();
21 | console.debug.restore();
22 | console.warn.restore();
23 | console.error.restore();
24 | console.info.restore();
25 | Log.initLogging();
26 | });
27 |
28 | it('should use noop for levels lower than the min level', function () {
29 | Log.initLogging('warn');
30 | Log.Debug('hi');
31 | Log.Info('hello');
32 | expect(console.log).to.not.have.been.called;
33 | });
34 |
35 | it('should use console.debug for Debug', function () {
36 | Log.initLogging('debug');
37 | Log.Debug('dbg');
38 | expect(console.debug).to.have.been.calledWith('dbg');
39 | });
40 |
41 | it('should use console.info for Info', function () {
42 | Log.initLogging('debug');
43 | Log.Info('inf');
44 | expect(console.info).to.have.been.calledWith('inf');
45 | });
46 |
47 | it('should use console.warn for Warn', function () {
48 | Log.initLogging('warn');
49 | Log.Warn('wrn');
50 | expect(console.warn).to.have.been.called;
51 | expect(console.warn).to.have.been.calledWith('wrn');
52 | });
53 |
54 | it('should use console.error for Error', function () {
55 | Log.initLogging('error');
56 | Log.Error('err');
57 | expect(console.error).to.have.been.called;
58 | expect(console.error).to.have.been.calledWith('err');
59 | });
60 | });
61 |
62 | describe('string functions', function () {
63 | it('should decode UTF-8 to DOMString correctly', function () {
64 | const utf8string = '\xd0\x9f';
65 | const domstring = decodeUTF8(utf8string);
66 | expect(domstring).to.equal("П");
67 | });
68 |
69 | it('should encode DOMString to UTF-8 correctly', function () {
70 | const domstring = "åäöa";
71 | const utf8string = encodeUTF8(domstring);
72 | expect(utf8string).to.equal('\xc3\xa5\xc3\xa4\xc3\xb6\x61');
73 | });
74 |
75 | it('should allow Latin-1 strings if allowLatin1 is set when decoding', function () {
76 | const latin1string = '\xe5\xe4\xf6';
77 | expect(() => decodeUTF8(latin1string)).to.throw(Error);
78 | expect(decodeUTF8(latin1string, true)).to.equal('åäö');
79 | });
80 | });
81 |
82 | // TODO(directxman12): test the conf_default and conf_defaults methods
83 | // TODO(directxman12): test the event methods (addEvent, removeEvent, stopEvent)
84 | // TODO(directxman12): figure out a good way to test getPosition and getEventPosition
85 | // TODO(directxman12): figure out how to test the browser detection functions properly
86 | // (we can't really test them against the browsers, except for Gecko
87 | // via PhantomJS, the default test driver)
88 | });
89 | /* eslint-enable no-console */
90 |
--------------------------------------------------------------------------------
/app/images/ctrlaltdel.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
101 |
--------------------------------------------------------------------------------
/app/images/connect.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
97 |
--------------------------------------------------------------------------------
/utils/genkeysymdef.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | /*
3 | * genkeysymdef: X11 keysymdef.h to JavaScript converter
4 | * Copyright (C) 2018 The noVNC Authors
5 | * Licensed under MPL 2.0 (see LICENSE.txt)
6 | */
7 |
8 | "use strict";
9 |
10 | const fs = require('fs');
11 |
12 | let showHelp = process.argv.length === 2;
13 | let filename;
14 |
15 | for (let i = 2; i < process.argv.length; ++i) {
16 | switch (process.argv[i]) {
17 | case "--help":
18 | case "-h":
19 | showHelp = true;
20 | break;
21 | case "--file":
22 | case "-f":
23 | default:
24 | filename = process.argv[i];
25 | }
26 | }
27 |
28 | if (!filename) {
29 | showHelp = true;
30 | console.log("Error: No filename specified\n");
31 | }
32 |
33 | if (showHelp) {
34 | console.log("Parses a *nix keysymdef.h to generate Unicode code point mappings");
35 | console.log("Usage: node parse.js [options] filename:");
36 | console.log(" -h [ --help ] Produce this help message");
37 | console.log(" filename The keysymdef.h file to parse");
38 | process.exit(0);
39 | }
40 |
41 | const buf = fs.readFileSync(filename);
42 | const str = buf.toString('utf8');
43 |
44 | const re = /^#define XK_([a-zA-Z_0-9]+)\s+0x([0-9a-fA-F]+)\s*(\/\*\s*(.*)\s*\*\/)?\s*$/m;
45 |
46 | const arr = str.split('\n');
47 |
48 | const codepoints = {};
49 |
50 | for (let i = 0; i < arr.length; ++i) {
51 | const result = re.exec(arr[i]);
52 | if (result) {
53 | const keyname = result[1];
54 | const keysym = parseInt(result[2], 16);
55 | const remainder = result[3];
56 |
57 | const unicodeRes = /U\+([0-9a-fA-F]+)/.exec(remainder);
58 | if (unicodeRes) {
59 | const unicode = parseInt(unicodeRes[1], 16);
60 | // The first entry is the preferred one
61 | if (!codepoints[unicode]) {
62 | codepoints[unicode] = { keysym: keysym, name: keyname };
63 | }
64 | }
65 | }
66 | }
67 |
68 | let out =
69 | "/*\n" +
70 | " * Mapping from Unicode codepoints to X11/RFB keysyms\n" +
71 | " *\n" +
72 | " * This file was automatically generated from keysymdef.h\n" +
73 | " * DO NOT EDIT!\n" +
74 | " */\n" +
75 | "\n" +
76 | "/* Functions at the bottom */\n" +
77 | "\n" +
78 | "const codepoints = {\n";
79 |
80 | function toHex(num) {
81 | let s = num.toString(16);
82 | if (s.length < 4) {
83 | s = ("0000" + s).slice(-4);
84 | }
85 | return "0x" + s;
86 | }
87 |
88 | for (let codepoint in codepoints) {
89 | codepoint = parseInt(codepoint);
90 |
91 | // Latin-1?
92 | if ((codepoint >= 0x20) && (codepoint <= 0xff)) {
93 | continue;
94 | }
95 |
96 | // Handled by the general Unicode mapping?
97 | if ((codepoint | 0x01000000) === codepoints[codepoint].keysym) {
98 | continue;
99 | }
100 |
101 | out += " " + toHex(codepoint) + ": " +
102 | toHex(codepoints[codepoint].keysym) +
103 | ", // XK_" + codepoints[codepoint].name + "\n";
104 | }
105 |
106 | out +=
107 | "};\n" +
108 | "\n" +
109 | "export default {\n" +
110 | " lookup(u) {\n" +
111 | " // Latin-1 is one-to-one mapping\n" +
112 | " if ((u >= 0x20) && (u <= 0xff)) {\n" +
113 | " return u;\n" +
114 | " }\n" +
115 | "\n" +
116 | " // Lookup table (fairly random)\n" +
117 | " const keysym = codepoints[u];\n" +
118 | " if (keysym !== undefined) {\n" +
119 | " return keysym;\n" +
120 | " }\n" +
121 | "\n" +
122 | " // General mapping as final fallback\n" +
123 | " return 0x01000000 | u;\n" +
124 | " },\n" +
125 | "};";
126 |
127 | console.log(out);
128 |
--------------------------------------------------------------------------------
/app/images/alt.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
93 |
--------------------------------------------------------------------------------
/po/xgettext-html:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | /*
3 | * xgettext-html: HTML gettext parser
4 | * Copyright (C) 2018 The noVNC Authors
5 | * Licensed under MPL 2.0 (see LICENSE.txt)
6 | */
7 |
8 | const getopt = require('node-getopt');
9 | const jsdom = require("jsdom");
10 | const fs = require("fs");
11 |
12 | const opt = getopt.create([
13 | ['o', 'output=FILE', 'write output to specified file'],
14 | ['h', 'help', 'display this help'],
15 | ]).bindHelp().parseSystem();
16 |
17 | const strings = {};
18 |
19 | function addString(str, location) {
20 | if (str.length == 0) {
21 | return;
22 | }
23 |
24 | if (strings[str] === undefined) {
25 | strings[str] = {};
26 | }
27 | strings[str][location] = null;
28 | }
29 |
30 | // See https://html.spec.whatwg.org/multipage/dom.html#attr-translate
31 | function process(elem, locator, enabled) {
32 | function isAnyOf(searchElement, items) {
33 | return items.indexOf(searchElement) !== -1;
34 | }
35 |
36 | if (elem.hasAttribute("translate")) {
37 | if (isAnyOf(elem.getAttribute("translate"), ["", "yes"])) {
38 | enabled = true;
39 | } else if (isAnyOf(elem.getAttribute("translate"), ["no"])) {
40 | enabled = false;
41 | }
42 | }
43 |
44 | if (enabled) {
45 | if (elem.hasAttribute("abbr") &&
46 | elem.tagName === "TH") {
47 | addString(elem.getAttribute("abbr"), locator(elem));
48 | }
49 | if (elem.hasAttribute("alt") &&
50 | isAnyOf(elem.tagName, ["AREA", "IMG", "INPUT"])) {
51 | addString(elem.getAttribute("alt"), locator(elem));
52 | }
53 | if (elem.hasAttribute("download") &&
54 | isAnyOf(elem.tagName, ["A", "AREA"])) {
55 | addString(elem.getAttribute("download"), locator(elem));
56 | }
57 | if (elem.hasAttribute("label") &&
58 | isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP",
59 | "OPTION", "TRACK"])) {
60 | addString(elem.getAttribute("label"), locator(elem));
61 | }
62 | if (elem.hasAttribute("placeholder") &&
63 | isAnyOf(elem.tagName in ["INPUT", "TEXTAREA"])) {
64 | addString(elem.getAttribute("placeholder"), locator(elem));
65 | }
66 | if (elem.hasAttribute("title")) {
67 | addString(elem.getAttribute("title"), locator(elem));
68 | }
69 | if (elem.hasAttribute("value") &&
70 | elem.tagName === "INPUT" &&
71 | isAnyOf(elem.getAttribute("type"), ["reset", "button", "submit"])) {
72 | addString(elem.getAttribute("value"), locator(elem));
73 | }
74 | }
75 |
76 | for (let i = 0; i < elem.childNodes.length; i++) {
77 | let node = elem.childNodes[i];
78 | if (node.nodeType === node.ELEMENT_NODE) {
79 | process(node, locator, enabled);
80 | } else if (node.nodeType === node.TEXT_NODE && enabled) {
81 | addString(node.data.trim(), locator(node));
82 | }
83 | }
84 | }
85 |
86 | for (let i = 0; i < opt.argv.length; i++) {
87 | const fn = opt.argv[i];
88 | const file = fs.readFileSync(fn, "utf8");
89 | const dom = new jsdom.JSDOM(file, { includeNodeLocations: true });
90 | const body = dom.window.document.body;
91 |
92 | let locator = (elem) => {
93 | const offset = dom.nodeLocation(elem).startOffset;
94 | const line = file.slice(0, offset).split("\n").length;
95 | return fn + ":" + line;
96 | };
97 |
98 | process(body, locator, true);
99 | }
100 |
101 | let output = "";
102 |
103 | for (let str in strings) {
104 | output += "#:";
105 | for (location in strings[str]) {
106 | output += " " + location;
107 | }
108 | output += "\n";
109 |
110 | output += "msgid " + JSON.stringify(str) + "\n";
111 | output += "msgstr \"\"\n";
112 | output += "\n";
113 | }
114 |
115 | fs.writeFileSync(opt.options.output, output);
116 |
--------------------------------------------------------------------------------
/tests/test.rre.js:
--------------------------------------------------------------------------------
1 | const expect = chai.expect;
2 |
3 | import Websock from '../core/websock.js';
4 | import Display from '../core/display.js';
5 |
6 | import RREDecoder from '../core/decoders/rre.js';
7 |
8 | import FakeWebSocket from './fake.websocket.js';
9 |
10 | function testDecodeRect(decoder, x, y, width, height, data, display, depth) {
11 | let sock;
12 |
13 | sock = new Websock;
14 | sock.open("ws://example.com");
15 |
16 | sock.on('message', () => {
17 | decoder.decodeRect(x, y, width, height, sock, display, depth);
18 | });
19 |
20 | // Empty messages are filtered at multiple layers, so we need to
21 | // do a direct call
22 | if (data.length === 0) {
23 | decoder.decodeRect(x, y, width, height, sock, display, depth);
24 | } else {
25 | sock._websocket._receiveData(new Uint8Array(data));
26 | }
27 |
28 | display.flip();
29 | }
30 |
31 | function push16(arr, num) {
32 | arr.push((num >> 8) & 0xFF,
33 | num & 0xFF);
34 | }
35 |
36 | function push32(arr, num) {
37 | arr.push((num >> 24) & 0xFF,
38 | (num >> 16) & 0xFF,
39 | (num >> 8) & 0xFF,
40 | num & 0xFF);
41 | }
42 |
43 | describe('RRE Decoder', function () {
44 | let decoder;
45 | let display;
46 |
47 | before(FakeWebSocket.replace);
48 | after(FakeWebSocket.restore);
49 |
50 | beforeEach(function () {
51 | decoder = new RREDecoder();
52 | display = new Display(document.createElement('canvas'));
53 | display.resize(4, 4);
54 | });
55 |
56 | // TODO(directxman12): test rre_chunk_sz?
57 |
58 | it('should handle the RRE encoding', function () {
59 | let data = [];
60 | push32(data, 2); // 2 subrects
61 | push32(data, 0x00ff0000); // becomes 00ff0000 --> #00FF00 bg color
62 | data.push(0x00); // becomes 0000ff00 --> #0000FF fg color
63 | data.push(0x00);
64 | data.push(0xff);
65 | data.push(0x00);
66 | push16(data, 0); // x: 0
67 | push16(data, 0); // y: 0
68 | push16(data, 2); // width: 2
69 | push16(data, 2); // height: 2
70 | data.push(0x00); // becomes 0000ff00 --> #0000FF fg color
71 | data.push(0x00);
72 | data.push(0xff);
73 | data.push(0x00);
74 | push16(data, 2); // x: 2
75 | push16(data, 2); // y: 2
76 | push16(data, 2); // width: 2
77 | push16(data, 2); // height: 2
78 |
79 | testDecodeRect(decoder, 0, 0, 4, 4, data, display, 24);
80 |
81 | let targetData = new Uint8Array([
82 | 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
83 | 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
84 | 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
85 | 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
86 | ]);
87 |
88 | expect(display).to.have.displayed(targetData);
89 | });
90 |
91 | it('should handle empty rects', function () {
92 | display.fillRect(0, 0, 4, 4, [ 0x00, 0x00, 0xff ]);
93 | display.fillRect(2, 0, 2, 2, [ 0x00, 0xff, 0x00 ]);
94 | display.fillRect(0, 2, 2, 2, [ 0x00, 0xff, 0x00 ]);
95 |
96 | testDecodeRect(decoder, 1, 2, 0, 0, [ 0x00, 0xff, 0xff, 0xff, 0xff ], display, 24);
97 |
98 | let targetData = new Uint8Array([
99 | 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
100 | 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
101 | 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
102 | 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
103 | ]);
104 |
105 | expect(display).to.have.displayed(targetData);
106 | });
107 | });
108 |
--------------------------------------------------------------------------------
/docs/EMBEDDING.md:
--------------------------------------------------------------------------------
1 | # Embedding and Deploying noVNC Application
2 |
3 | This document describes how to embed and deploy the noVNC application, which
4 | includes settings and a full user interface. If you are looking for
5 | documentation on how to use the core noVNC library in your own application,
6 | then please see our [library documentation](LIBRARY.md).
7 |
8 | ## Files
9 |
10 | The noVNC application consists of the following files and directories:
11 |
12 | * `vnc.html` - The main page for the application and where users should go. It
13 | is possible to rename this file.
14 |
15 | * `app/` - Support files for the application. Contains code, images, styles and
16 | translations.
17 |
18 | * `core/` - The core noVNC library.
19 |
20 | * `vendor/` - Third party support libraries used by the application and the
21 | core library.
22 |
23 | The most basic deployment consists of simply serving these files from a web
24 | server and setting up a WebSocket proxy to the VNC server.
25 |
26 | ## Parameters
27 |
28 | The noVNC application can be controlled by including certain settings in the
29 | query string. Currently the following options are available:
30 |
31 | * `autoconnect` - Automatically connect as soon as the page has finished
32 | loading.
33 |
34 | * `reconnect` - If noVNC should automatically reconnect if the connection is
35 | dropped.
36 |
37 | * `reconnect_delay` - How long to wait in milliseconds before attempting to
38 | reconnect.
39 |
40 | * `host` - The WebSocket host to connect to.
41 |
42 | * `port` - The WebSocket port to connect to.
43 |
44 | * `encrypt` - If TLS should be used for the WebSocket connection.
45 |
46 | * `path` - The WebSocket path to use.
47 |
48 | * `password` - The password sent to the server, if required.
49 |
50 | * `repeaterID` - The repeater ID to use if a VNC repeater is detected.
51 |
52 | * `shared` - If other VNC clients should be disconnected when noVNC connects.
53 |
54 | * `bell` - If the keyboard bell should be enabled or not.
55 |
56 | * `view_only` - If the remote session should be in non-interactive mode.
57 |
58 | * `view_clip` - If the remote session should be clipped or use scrollbars if
59 | it cannot fit in the browser.
60 |
61 | * `resize` - How to resize the remote session if it is not the same size as
62 | the browser window. Can be one of `off`, `scale` and `remote`.
63 |
64 | * `quality` - The session JPEG quality level. Can be `0` to `9`.
65 |
66 | * `compression` - The session compression level. Can be `0` to `9`.
67 |
68 | * `show_dot` - If a dot cursor should be shown when the remote server provides
69 | no local cursor, or provides a fully-transparent (invisible) cursor.
70 |
71 | * `logging` - The console log level. Can be one of `error`, `warn`, `info` or
72 | `debug`.
73 |
74 | ## HTTP Serving Considerations
75 | ### Browser Cache Issue
76 |
77 | If you serve noVNC files using a web server that provides an ETag header, and
78 | include any options in the query string, a nasty browser cache issue can bite
79 | you on upgrade, resulting in a red error box. The issue is caused by a mismatch
80 | between the new vnc.html (which is reloaded because the user has used it with
81 | new query string after the upgrade) and the old javascript files (that the
82 | browser reuses from its cache). To avoid this issue, the browser must be told
83 | to always revalidate cached files using conditional requests. The correct
84 | semantics are achieved via the (confusingly named) `Cache-Control: no-cache`
85 | header that needs to be provided in the web server responses.
86 |
87 | ### Example Server Configurations
88 |
89 | Apache:
90 |
91 | ```
92 | # In the main configuration file
93 | # (Debian/Ubuntu users: use "a2enmod headers" instead)
94 | LoadModule headers_module modules/mod_headers.so
95 |
96 | # In the or block related to noVNC
97 | Header set Cache-Control "no-cache"
98 | ```
99 |
100 | Nginx:
101 |
102 | ```
103 | # In the location block related to noVNC
104 | add_header Cache-Control no-cache;
105 | ```
106 |
--------------------------------------------------------------------------------
/app/images/warning.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
82 |
--------------------------------------------------------------------------------
/docs/rfb_notes:
--------------------------------------------------------------------------------
1 | 5.1.1 ProtocolVersion: 12, 12 bytes
2 |
3 | - Sent by server, max supported
4 | 12 ascii - "RFB 003.008\n"
5 | - Response by client, version to use
6 | 12 ascii - "RFB 003.003\n"
7 |
8 | 5.1.2 Authentication: >=4, [16, 4] bytes
9 |
10 | - Sent by server
11 | CARD32 - authentication-scheme
12 | 0 - connection failed
13 | CARD32 - length
14 | length - reason
15 | 1 - no authentication
16 |
17 | 2 - VNC authentication
18 | 16 CARD8 - challenge (random bytes)
19 |
20 | - Response by client (if VNC authentication)
21 | 16 CARD8 - client encrypts the challenge with DES, using user
22 | password as key, sends resulting 16 byte response
23 |
24 | - Response by server (if VNC authentication)
25 | CARD32 - 0 - OK
26 | 1 - failed
27 | 2 - too-many
28 |
29 | 5.1.3 ClientInitialisation: 1 byte
30 | - Sent by client
31 | CARD8 - shared-flag, 0 exclusive, non-zero shared
32 |
33 | 5.1.4 ServerInitialisation: >=24 bytes
34 | - Sent by server
35 | CARD16 - framebuffer-width
36 | CARD16 - framebuffer-height
37 | 16 byte PIXEL_FORMAT - server-pixel-format
38 | CARD8 - bits-per-pixel
39 | CARD8 - depth
40 | CARD8 - big-endian-flag, non-zero is big endian
41 | CARD8 - true-color-flag, non-zero then next 6 apply
42 | CARD16 - red-max
43 | CARD16 - green-max
44 | CARD16 - blue-max
45 | CARD8 - red-shift
46 | CARD8 - green-shift
47 | CARD8 - blue-shift
48 | 3 bytes - padding
49 | CARD32 - name-length
50 |
51 | CARD8[length] - name-string
52 |
53 |
54 |
55 | Client to Server Messages:
56 |
57 | 5.2.1 SetPixelFormat: 20 bytes
58 | CARD8: 0 - message-type
59 | ...
60 |
61 | 5.2.2 FixColourMapEntries: >=6 bytes
62 | CARD8: 1 - message-type
63 | ...
64 |
65 | 5.2.3 SetEncodings: >=8 bytes
66 | CARD8: 2 - message-type
67 | CARD8 - padding
68 | CARD16 - numer-of-encodings
69 |
70 | CARD32 - encoding-type in preference order
71 | 0 - raw
72 | 1 - copy-rectangle
73 | 2 - RRE
74 | 4 - CoRRE
75 | 5 - hextile
76 |
77 | 5.2.4 FramebufferUpdateRequest (10 bytes)
78 | CARD8: 3 - message-type
79 | CARD8 - incremental (0 for full-update, non-zero for incremental)
80 | CARD16 - x-position
81 | CARD16 - y-position
82 | CARD16 - width
83 | CARD16 - height
84 |
85 |
86 | 5.2.5 KeyEvent: 8 bytes
87 | CARD8: 4 - message-type
88 | CARD8 - down-flag
89 | 2 bytes - padding
90 | CARD32 - key (X-Windows keysym values)
91 |
92 | 5.2.6 PointerEvent: 6 bytes
93 | CARD8: 5 - message-type
94 | CARD8 - button-mask
95 | CARD16 - x-position
96 | CARD16 - y-position
97 |
98 | 5.2.7 ClientCutText: >=9 bytes
99 | CARD8: 6 - message-type
100 | ...
101 |
102 |
103 | Server to Client Messages:
104 |
105 | 5.3.1 FramebufferUpdate
106 | CARD8: 0 - message-type
107 | 1 byte - padding
108 | CARD16 - number-of-rectangles
109 |
110 | CARD16 - x-position
111 | CARD16 - y-position
112 | CARD16 - width
113 | CARD16 - height
114 | CARD16 - encoding-type:
115 | 0 - raw
116 | 1 - copy rectangle
117 | 2 - RRE
118 | 4 - CoRRE
119 | 5 - hextile
120 |
121 | raw:
122 | - width x height pixel values
123 |
124 | copy rectangle:
125 | CARD16 - src-x-position
126 | CARD16 - src-y-position
127 |
128 | RRE:
129 | CARD32 - N number-of-subrectangles
130 | Nxd bytes - background-pixel-value (d bits-per-pixel)
131 |
132 | ...
133 |
134 | 5.3.2 SetColourMapEntries (no support)
135 | CARD8: 1 - message-type
136 | ...
137 |
138 | 5.3.3 Bell
139 | CARD8: 2 - message-type
140 |
141 | 5.3.4 ServerCutText
142 | CARD8: 3 - message-type
143 |
144 |
145 |
146 |
147 |
148 |
--------------------------------------------------------------------------------
/core/util/browser.js:
--------------------------------------------------------------------------------
1 | /*
2 | * noVNC: HTML5 VNC client
3 | * Copyright (C) 2019 The noVNC Authors
4 | * Licensed under MPL 2.0 (see LICENSE.txt)
5 | *
6 | * See README.md for usage and integration instructions.
7 | *
8 | * Browser feature support detection
9 | */
10 |
11 | import * as Log from './logging.js';
12 |
13 | // Touch detection
14 | export let isTouchDevice = ('ontouchstart' in document.documentElement) ||
15 | // requried for Chrome debugger
16 | (document.ontouchstart !== undefined) ||
17 | // required for MS Surface
18 | (navigator.maxTouchPoints > 0) ||
19 | (navigator.msMaxTouchPoints > 0);
20 | window.addEventListener('touchstart', function onFirstTouch() {
21 | isTouchDevice = true;
22 | window.removeEventListener('touchstart', onFirstTouch, false);
23 | }, false);
24 |
25 |
26 | // The goal is to find a certain physical width, the devicePixelRatio
27 | // brings us a bit closer but is not optimal.
28 | export let dragThreshold = 10 * (window.devicePixelRatio || 1);
29 |
30 | let _supportsCursorURIs = false;
31 |
32 | try {
33 | const target = document.createElement('canvas');
34 | target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
35 |
36 | if (target.style.cursor.indexOf("url") === 0) {
37 | Log.Info("Data URI scheme cursor supported");
38 | _supportsCursorURIs = true;
39 | } else {
40 | Log.Warn("Data URI scheme cursor not supported");
41 | }
42 | } catch (exc) {
43 | Log.Error("Data URI scheme cursor test exception: " + exc);
44 | }
45 |
46 | export const supportsCursorURIs = _supportsCursorURIs;
47 |
48 | let _hasScrollbarGutter = true;
49 | try {
50 | // Create invisible container
51 | const container = document.createElement('div');
52 | container.style.visibility = 'hidden';
53 | container.style.overflow = 'scroll'; // forcing scrollbars
54 | document.body.appendChild(container);
55 |
56 | // Create a div and place it in the container
57 | const child = document.createElement('div');
58 | container.appendChild(child);
59 |
60 | // Calculate the difference between the container's full width
61 | // and the child's width - the difference is the scrollbars
62 | const scrollbarWidth = (container.offsetWidth - child.offsetWidth);
63 |
64 | // Clean up
65 | container.parentNode.removeChild(container);
66 |
67 | _hasScrollbarGutter = scrollbarWidth != 0;
68 | } catch (exc) {
69 | Log.Error("Scrollbar test exception: " + exc);
70 | }
71 | export const hasScrollbarGutter = _hasScrollbarGutter;
72 |
73 | /*
74 | * The functions for detection of platforms and browsers below are exported
75 | * but the use of these should be minimized as much as possible.
76 | *
77 | * It's better to use feature detection than platform detection.
78 | */
79 |
80 | export function isMac() {
81 | return navigator && !!(/mac/i).exec(navigator.platform);
82 | }
83 |
84 | export function isWindows() {
85 | return navigator && !!(/win/i).exec(navigator.platform);
86 | }
87 |
88 | export function isIOS() {
89 | return navigator &&
90 | (!!(/ipad/i).exec(navigator.platform) ||
91 | !!(/iphone/i).exec(navigator.platform) ||
92 | !!(/ipod/i).exec(navigator.platform));
93 | }
94 |
95 | export function isSafari() {
96 | return navigator && (navigator.userAgent.indexOf('Safari') !== -1 &&
97 | navigator.userAgent.indexOf('Chrome') === -1);
98 | }
99 |
100 | export function isFirefox() {
101 | return navigator && !!(/firefox/i).exec(navigator.userAgent);
102 | }
103 |
104 |
--------------------------------------------------------------------------------
/app/images/power.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
88 |
--------------------------------------------------------------------------------
/docs/API-internal.md:
--------------------------------------------------------------------------------
1 | # 1. Internal Modules
2 |
3 | The noVNC client is composed of several internal modules that handle
4 | rendering, input, networking, etc. Each of the modules is designed to
5 | be cross-browser and independent from each other.
6 |
7 | Note however that the API of these modules is not guaranteed to be
8 | stable, and this documentation is not maintained as well as the
9 | official external API.
10 |
11 |
12 | ## 1.1 Module List
13 |
14 | * __Keyboard__ (core/input/keyboard.js): Keyboard input event handler with
15 | non-US keyboard support. Translates keyDown and keyUp events to X11
16 | keysym values.
17 |
18 | * __Display__ (core/display.js): Efficient 2D rendering abstraction
19 | layered on the HTML5 canvas element.
20 |
21 | * __Websock__ (core/websock.js): Websock client from websockify
22 | with transparent binary data support.
23 | [Websock API](https://github.com/novnc/websockify-js/wiki/websock.js) wiki page.
24 |
25 |
26 | ## 1.2 Callbacks
27 |
28 | For the Mouse, Keyboard and Display objects the callback functions are
29 | assigned to configuration attributes, just as for the RFB object. The
30 | WebSock module has a method named 'on' that takes two parameters: the
31 | callback event name, and the callback function.
32 |
33 | ## 2. Modules
34 |
35 | ## 2.1 Keyboard Module
36 |
37 | ### 2.1.1 Configuration Attributes
38 |
39 | None
40 |
41 | ### 2.1.2 Methods
42 |
43 | | name | parameters | description
44 | | ------ | ---------- | ------------
45 | | grab | () | Begin capturing keyboard events
46 | | ungrab | () | Stop capturing keyboard events
47 |
48 | ### 2.1.3 Callbacks
49 |
50 | | name | parameters | description
51 | | ---------- | -------------------- | ------------
52 | | onkeypress | (keysym, code, down) | Handler for key press/release
53 |
54 |
55 | ## 2.2 Display Module
56 |
57 | ### 2.2.1 Configuration Attributes
58 |
59 | | name | type | mode | default | description
60 | | ------------ | ----- | ---- | ------- | ------------
61 | | scale | float | RW | 1.0 | Display area scale factor 0.0 - 1.0
62 | | clipViewport | bool | RW | false | Use viewport clipping
63 | | width | int | RO | | Display area width
64 | | height | int | RO | | Display area height
65 |
66 | ### 2.2.2 Methods
67 |
68 | | name | parameters | description
69 | | ------------------ | ------------------------------------------------------- | ------------
70 | | viewportChangePos | (deltaX, deltaY) | Move the viewport relative to the current location
71 | | viewportChangeSize | (width, height) | Change size of the viewport
72 | | absX | (x) | Return X relative to the remote display
73 | | absY | (y) | Return Y relative to the remote display
74 | | resize | (width, height) | Set width and height
75 | | flip | (from_queue) | Update the visible canvas with the contents of the rendering canvas
76 | | pending | () | Check if there are waiting items in the render queue
77 | | flush | () | Resume processing the render queue unless it's empty
78 | | fillRect | (x, y, width, height, color, from_queue) | Draw a filled in rectangle
79 | | copyImage | (old_x, old_y, new_x, new_y, width, height, from_queue) | Copy a rectangular area
80 | | imageRect | (x, y, width, height, mime, arr) | Draw a rectangle with an image
81 | | blitImage | (x, y, width, height, arr, offset, from_queue) | Blit pixels (of R,G,B,A) to the display
82 | | drawImage | (img, x, y) | Draw image and track damage
83 | | autoscale | (containerWidth, containerHeight) | Scale the display
84 |
85 | ### 2.2.3 Callbacks
86 |
87 | | name | parameters | description
88 | | ------- | ---------- | ------------
89 | | onflush | () | A display flush has been requested and we are now ready to resume FBU processing
90 |
--------------------------------------------------------------------------------
/core/input/fixedkeys.js:
--------------------------------------------------------------------------------
1 | /*
2 | * noVNC: HTML5 VNC client
3 | * Copyright (C) 2018 The noVNC Authors
4 | * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
5 | */
6 |
7 | /*
8 | * Fallback mapping between HTML key codes (physical keys) and
9 | * HTML key values. This only works for keys that don't vary
10 | * between layouts. We also omit those who manage fine by mapping the
11 | * Unicode representation.
12 | *
13 | * See https://www.w3.org/TR/uievents-code/ for possible codes.
14 | * See https://www.w3.org/TR/uievents-key/ for possible values.
15 | */
16 |
17 | /* eslint-disable key-spacing */
18 |
19 | export default {
20 |
21 | // 3.1.1.1. Writing System Keys
22 |
23 | 'Backspace': 'Backspace',
24 |
25 | // 3.1.1.2. Functional Keys
26 |
27 | 'AltLeft': 'Alt',
28 | 'AltRight': 'Alt', // This could also be 'AltGraph'
29 | 'CapsLock': 'CapsLock',
30 | 'ContextMenu': 'ContextMenu',
31 | 'ControlLeft': 'Control',
32 | 'ControlRight': 'Control',
33 | 'Enter': 'Enter',
34 | 'MetaLeft': 'Meta',
35 | 'MetaRight': 'Meta',
36 | 'ShiftLeft': 'Shift',
37 | 'ShiftRight': 'Shift',
38 | 'Tab': 'Tab',
39 | // FIXME: Japanese/Korean keys
40 |
41 | // 3.1.2. Control Pad Section
42 |
43 | 'Delete': 'Delete',
44 | 'End': 'End',
45 | 'Help': 'Help',
46 | 'Home': 'Home',
47 | 'Insert': 'Insert',
48 | 'PageDown': 'PageDown',
49 | 'PageUp': 'PageUp',
50 |
51 | // 3.1.3. Arrow Pad Section
52 |
53 | 'ArrowDown': 'ArrowDown',
54 | 'ArrowLeft': 'ArrowLeft',
55 | 'ArrowRight': 'ArrowRight',
56 | 'ArrowUp': 'ArrowUp',
57 |
58 | // 3.1.4. Numpad Section
59 |
60 | 'NumLock': 'NumLock',
61 | 'NumpadBackspace': 'Backspace',
62 | 'NumpadClear': 'Clear',
63 |
64 | // 3.1.5. Function Section
65 |
66 | 'Escape': 'Escape',
67 | 'F1': 'F1',
68 | 'F2': 'F2',
69 | 'F3': 'F3',
70 | 'F4': 'F4',
71 | 'F5': 'F5',
72 | 'F6': 'F6',
73 | 'F7': 'F7',
74 | 'F8': 'F8',
75 | 'F9': 'F9',
76 | 'F10': 'F10',
77 | 'F11': 'F11',
78 | 'F12': 'F12',
79 | 'F13': 'F13',
80 | 'F14': 'F14',
81 | 'F15': 'F15',
82 | 'F16': 'F16',
83 | 'F17': 'F17',
84 | 'F18': 'F18',
85 | 'F19': 'F19',
86 | 'F20': 'F20',
87 | 'F21': 'F21',
88 | 'F22': 'F22',
89 | 'F23': 'F23',
90 | 'F24': 'F24',
91 | 'F25': 'F25',
92 | 'F26': 'F26',
93 | 'F27': 'F27',
94 | 'F28': 'F28',
95 | 'F29': 'F29',
96 | 'F30': 'F30',
97 | 'F31': 'F31',
98 | 'F32': 'F32',
99 | 'F33': 'F33',
100 | 'F34': 'F34',
101 | 'F35': 'F35',
102 | 'PrintScreen': 'PrintScreen',
103 | 'ScrollLock': 'ScrollLock',
104 | 'Pause': 'Pause',
105 |
106 | // 3.1.6. Media Keys
107 |
108 | 'BrowserBack': 'BrowserBack',
109 | 'BrowserFavorites': 'BrowserFavorites',
110 | 'BrowserForward': 'BrowserForward',
111 | 'BrowserHome': 'BrowserHome',
112 | 'BrowserRefresh': 'BrowserRefresh',
113 | 'BrowserSearch': 'BrowserSearch',
114 | 'BrowserStop': 'BrowserStop',
115 | 'Eject': 'Eject',
116 | 'LaunchApp1': 'LaunchMyComputer',
117 | 'LaunchApp2': 'LaunchCalendar',
118 | 'LaunchMail': 'LaunchMail',
119 | 'MediaPlayPause': 'MediaPlay',
120 | 'MediaStop': 'MediaStop',
121 | 'MediaTrackNext': 'MediaTrackNext',
122 | 'MediaTrackPrevious': 'MediaTrackPrevious',
123 | 'Power': 'Power',
124 | 'Sleep': 'Sleep',
125 | 'AudioVolumeDown': 'AudioVolumeDown',
126 | 'AudioVolumeMute': 'AudioVolumeMute',
127 | 'AudioVolumeUp': 'AudioVolumeUp',
128 | 'WakeUp': 'WakeUp',
129 | };
130 |
--------------------------------------------------------------------------------
/tests/assertions.js:
--------------------------------------------------------------------------------
1 | // noVNC specific assertions
2 | chai.use(function (_chai, utils) {
3 | function _equal(a, b) {
4 | return a === b;
5 | }
6 | _chai.Assertion.addMethod('displayed', function (targetData, cmp=_equal) {
7 | const obj = this._obj;
8 | const ctx = obj._target.getContext('2d');
9 | const data = ctx.getImageData(0, 0, obj._target.width, obj._target.height).data;
10 | const len = data.length;
11 | new chai.Assertion(len).to.be.equal(targetData.length, "unexpected display size");
12 | let same = true;
13 | for (let i = 0; i < len; i++) {
14 | if (!cmp(data[i], targetData[i])) {
15 | same = false;
16 | break;
17 | }
18 | }
19 | if (!same) {
20 | // eslint-disable-next-line no-console
21 | console.log("expected data: %o, actual data: %o", targetData, data);
22 | }
23 | this.assert(same,
24 | "expected #{this} to have displayed the image #{exp}, but instead it displayed #{act}",
25 | "expected #{this} not to have displayed the image #{act}",
26 | targetData,
27 | data);
28 | });
29 |
30 | _chai.Assertion.addMethod('sent', function (targetData) {
31 | const obj = this._obj;
32 | obj.inspect = () => {
33 | const res = { _websocket: obj._websocket, rQi: obj._rQi, _rQ: new Uint8Array(obj._rQ.buffer, 0, obj._rQlen),
34 | _sQ: new Uint8Array(obj._sQ.buffer, 0, obj._sQlen) };
35 | res.prototype = obj;
36 | return res;
37 | };
38 | const data = obj._websocket._getSentData();
39 | let same = true;
40 | if (data.length != targetData.length) {
41 | same = false;
42 | } else {
43 | for (let i = 0; i < data.length; i++) {
44 | if (data[i] != targetData[i]) {
45 | same = false;
46 | break;
47 | }
48 | }
49 | }
50 | if (!same) {
51 | // eslint-disable-next-line no-console
52 | console.log("expected data: %o, actual data: %o", targetData, data);
53 | }
54 | this.assert(same,
55 | "expected #{this} to have sent the data #{exp}, but it actually sent #{act}",
56 | "expected #{this} not to have sent the data #{act}",
57 | Array.prototype.slice.call(targetData),
58 | Array.prototype.slice.call(data));
59 | });
60 |
61 | _chai.Assertion.addProperty('array', function () {
62 | utils.flag(this, 'array', true);
63 | });
64 |
65 | _chai.Assertion.overwriteMethod('equal', function (_super) {
66 | return function assertArrayEqual(target) {
67 | if (utils.flag(this, 'array')) {
68 | const obj = this._obj;
69 |
70 | let same = true;
71 |
72 | if (utils.flag(this, 'deep')) {
73 | for (let i = 0; i < obj.length; i++) {
74 | if (!utils.eql(obj[i], target[i])) {
75 | same = false;
76 | break;
77 | }
78 | }
79 |
80 | this.assert(same,
81 | "expected #{this} to have elements deeply equal to #{exp}",
82 | "expected #{this} not to have elements deeply equal to #{exp}",
83 | Array.prototype.slice.call(target));
84 | } else {
85 | for (let i = 0; i < obj.length; i++) {
86 | if (obj[i] != target[i]) {
87 | same = false;
88 | break;
89 | }
90 | }
91 |
92 | this.assert(same,
93 | "expected #{this} to have elements equal to #{exp}",
94 | "expected #{this} not to have elements equal to #{exp}",
95 | Array.prototype.slice.call(target));
96 | }
97 | } else {
98 | _super.apply(this, arguments);
99 | }
100 | };
101 | });
102 | });
103 |
--------------------------------------------------------------------------------
/app/images/clipboard.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
107 |
--------------------------------------------------------------------------------