6 Commits

Author SHA1 Message Date
Joel Martin 876a691d2a wasm: use port 7080, use kanaka/rust-wasm image 2018-07-31 11:02:37 -05:00
Joel Martin 5372d169cc wasm: simplify down to just use MODE 3 (draw3).
draw3 was the pretty clear winner performance-wise.
2018-07-26 16:05:55 +09:00
Joel Martin af54d4b95e wasm: use requestAnimationFrame and report FPS. 2018-07-26 15:38:33 +09:00
Joel Martin 82b8ff1c6a wasm: tests/results for 3 modes.
- mode 1: call putImageData from rust/wasm. This involves copying the
  image data via the wasm-bindgen API.
- mode 2: allocating memory in rust/wasm and returning reference to it
  for JS code. JS code calls draw2 using the reference so now image
  data is copying in either direction.
- mode 3: same as mode 2 but the draw3 function treats the memory as
  a vector of u32 instead of a vector of u8.
2018-07-26 14:19:01 +09:00
Joel Martin 9efc362c82 wasm: draw/fill a canvas with solid color. 2018-07-25 16:48:48 +09:00
Joel Martin 62ad00fedf wasm: hello world with rust, wasm-bindgen, webpack 2018-07-25 16:02:40 +09:00
180 changed files with 17445 additions and 29817 deletions
-1
View File
@@ -1 +0,0 @@
**/xtscancodes.js
-50
View File
@@ -1,50 +0,0 @@
{
"env": {
"browser": true,
"es6": true
},
"parserOptions": {
"sourceType": "module"
},
"extends": "eslint:recommended",
"rules": {
// Unsafe or confusing stuff that we forbid
"no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": true }],
"no-constant-condition": ["error", { "checkLoops": false }],
"no-var": "error",
"no-useless-constructor": "error",
"object-shorthand": ["error", "methods", { "avoidQuotes": true }],
"prefer-arrow-callback": "error",
"arrow-body-style": ["error", "as-needed", { "requireReturnForObjectLiteral": false } ],
"arrow-parens": ["error", "as-needed", { "requireForBlockBody": true }],
"arrow-spacing": ["error"],
"no-confusing-arrow": ["error", { "allowParens": true }],
// Enforced coding style
"brace-style": ["error", "1tbs", { "allowSingleLine": true }],
"indent": ["error", 4, { "SwitchCase": 1,
"FunctionDeclaration": { "parameters": "first" },
"CallExpression": { "arguments": "first" },
"ArrayExpression": "first",
"ObjectExpression": "first",
"ignoreComments": true }],
"comma-spacing": ["error"],
"comma-style": ["error"],
"curly": ["error", "multi-line"],
"func-call-spacing": ["error"],
"func-names": ["error"],
"func-style": ["error", "declaration", { "allowArrowFunctions": true }],
"key-spacing": ["error"],
"keyword-spacing": ["error"],
"no-trailing-spaces": ["error"],
"semi": ["error"],
"space-before-blocks": ["error"],
"space-before-function-paren": ["error", { "anonymous": "always",
"named": "never",
"asyncArrow": "always" }],
"switch-colon-spacing": ["error"],
"camelcase": ["error", { allow: ["^XK_", "^XF86XK_"] }],
}
}
-34
View File
@@ -1,34 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Client (please complete the following information):**
- OS: [e.g. iOS]
- Browser: [e.g. chrome, safari]
- Browser version: [e.g. 22]
**Server (please complete the following information):**
- noVNC version: [e.g. 1.0.0 or git commit id]
- VNC server: [e.g. QEMU, TigerVNC]
- WebSocket proxy: [e.g. websockify]
**Additional context**
Add any other context about the problem here.
-5
View File
@@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Question or discussion
url: https://groups.google.com/forum/?fromgroups#!forum/novnc
about: Ask a question or start a discussion
-17
View File
@@ -1,17 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
-56
View File
@@ -1,56 +0,0 @@
name: Publish
on:
push:
pull_request:
release:
types: [published]
jobs:
npm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
# Needs to be explicitly specified for auth to work
registry-url: 'https://registry.npmjs.org'
- run: npm install
- uses: actions/upload-artifact@v2
with:
name: npm
path: lib
- run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
if: ${{ github.event_name == 'release' && !github.event.release.prerelease }}
- run: npm publish --access public --tag beta
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
if: ${{ github.event_name == 'release' && github.event.release.prerelease }}
snap:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: |
VERSION=$(grep '"version"' package.json | cut -d '"' -f 4)
echo $VERSION
sed -i "s/@VERSION@/$VERSION/g" snap/snapcraft.yaml
- uses: snapcore/action-build@v1
id: snapcraft
- uses: actions/upload-artifact@v2
with:
name: snap
path: ${{ steps.snapcraft.outputs.snap }}
- uses: snapcore/action-publish@v1
with:
store_login: ${{ secrets.SNAPCRAFT_LOGIN }}
snap: ${{ steps.build.outputs.snap }}
release: stable
if: ${{ github.event_name == 'release' && !github.event.release.prerelease }}
- uses: snapcore/action-publish@v1
with:
store_login: ${{ secrets.SNAPCRAFT_LOGIN }}
snap: ${{ steps.build.outputs.snap }}
release: beta
if: ${{ github.event_name == 'release' && github.event.release.prerelease }}
-19
View File
@@ -1,19 +0,0 @@
name: Lint
on: [push, pull_request]
jobs:
eslint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- run: npm install
- run: npm run lint
html:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- run: npm install
- run: git ls-tree --name-only -r HEAD | grep -E "[.](html|css)$" | xargs ./utils/validate
-28
View File
@@ -1,28 +0,0 @@
name: Test
on: [push, pull_request]
jobs:
test:
strategy:
matrix:
os:
- ubuntu-latest
- windows-latest
browser:
- ChromeHeadless
- FirefoxHeadless
include:
- os: macos-latest
browser: Safari
- os: windows-latest
browser: EdgeHeadless
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- run: npm install
- run: npm run test
env:
TEST_BROWSER_NAME: ${{ matrix.browser }}
+3 -6
View File
@@ -3,10 +3,7 @@
tests/data_*.js
utils/rebind.so
utils/websockify
/node_modules
/build
/lib
node_modules
build
lib
recordings
*.swp
*~
noVNC-*.tgz
+20
View File
@@ -0,0 +1,20 @@
app
core
.gitmodules
node_modules
.*
*~
*.swp
*.swo
tests
.travis.yml
utils
docs/notes
docs/links
docs/release.txt
docs/rfb_notes
docs/*.pdf
vnc.html
vnc_auto.html
karma.conf.js
docs/flash_policy.txt
+20
View File
@@ -0,0 +1,20 @@
language: node_js
sudo: false
cache:
directories:
- node_modules
node_js:
- '6.1'
env:
matrix:
- TEST_BROWSER_NAME=PhantomJS
- TEST_BROWSER_NAME=chrome TEST_BROWSER_OS='Windows 10,Linux,OS X 10.11'
- TEST_BROWSER_NAME=firefox TEST_BROWSER_OS='Windows 10,Linux,OS X 10.11'
- TEST_BROWSER_NAME='internet explorer' TEST_BROWSER_OS='Windows 10'
- TEST_BROWSER_NAME=safari TEST_BROWSER_OS='OS X 10.11'
global:
- secure: QE5GqGd2hrpQsIgd8dlv3oRUUHqZayomzzQjNXOB81VQi241uz/ru+3GtBZLB5WLZCq/Gj89vbLnR0LN4ixlmPaWv3/WJQGyDGuRD/vMnccVl+rBUP/Hh2zdYwiISIGcrywNAE+KLus/lyt/ahVgzbaRaDSzrM1HaZFT/rndGck=
- secure: g75sdctEwj0hoLW0Y08Tdv8s5scNzplB6a9EtaJ2vJD9S/bK+AsPqbWesGv1UlrFPCWdbV7Vg61vkmoUjcmb5xhqFIjcM9TlYJoKWeOTsOmnQoSIkIq6gMF1k02+LmKInbPgIzrp3m3jluS1qaOs/EzFpDnJp9hWBiAfXa12Jxk=
before_script: npm install -g karma-cli
addons:
sauce_connect: true
-13
View File
@@ -1,13 +0,0 @@
maintainers:
- Joel Martin (@kanaka)
- Solly Ross (@directxman12)
- Samuel Mannehed for Cendio AB (@samhed)
- Pierre Ossman for Cendio AB (@CendioOssman)
maintainersEmeritus:
- @astrand
contributors:
# There are a bunch of people that should be here.
# If you want to be on this list, feel free send a PR
# to add yourself.
- jalf <git@jalf.dk>
- NTT corp.
+54
View File
@@ -0,0 +1,54 @@
How to contribute to noVNC
==========================
We accept code via pull requests on GitHub. There are several guidelines that
we expect contributors submitting code requests to follow. If you have issues
following any of these guidelines, feel free to drop us a line by leaving a
comment in the code request or sending us an email.
Contributing Guidelines
-----------------------
* While we don't have an official coding style guide, please try to follow
the general coding style of the existing code.
** Use four spaces instead of tabs
** prefix private variables and functions with an `_`
* Please try to include unit tests for your code. For instance, if you
introduce a new encoding, add a test to `tests/test.rfb.js` under the
"Encoding Handlers" section (basically, input a small pattern in your
encoding and make sure the pattern gets displayed correctly). If you
fix a bug, try to add a unit test that would have caught that bug
(if possible -- some bugs, especially visual ones, are hard to test for).
* Squash your commits down in to a clean commit history. For instance, there
should not be "cleanup" commits where you fix issues in previous commits in
the same pull request. Before you go to commit, use `git rebase -i` to
squash these changes into the relevant commits. For instance, a good commit
history might look like "Added support for FOO encoding, Added support for
BAR message, Placed Button in UI to Trigger BAR" (where each comma denotes
a separate commit).
* Add both a title and description to your commit, if possible. Place more
detail on what you did in the description.
Running the unit tests
----------------------
There are two ways to run the unit tests. For both ways, you should first run
`npm install` (not as root).
The first way to run the tests is to run `npm test`. This will run all the
tests in the headless PhantomJS browser (which uses WebKit).
The second way to run the tests is using the `tests/run_from_console.js` file.
This way is a bit more flexible, and can provide more information about what
went wrong. To run all the tests, simply run `tests/run_from_console.js`.
To run a specific test file, you can use the `-t path/to/test/file.js` option.
If you wish to simply generate the HTML for the test, use the `-g` option, and
the path to the temporary HTML file will be written to standard out. To open
this file in your default browser automatically, pass the `-o` option as well.
More information can be found by passing the `--help` or `-h` option.
Thanks, and happy coding!
+26 -9
View File
@@ -1,14 +1,23 @@
noVNC is Copyright (C) 2019 The noVNC Authors
(./AUTHORS)
noVNC is Copyright (C) 2011 Joel Martin <github@martintribe.org>
The noVNC core library files are licensed under the MPL 2.0 (Mozilla
Public License 2.0). The noVNC core library is composed of the
Javascript code necessary for full noVNC operation. This includes (but
is not limited to):
core/**/*.js
app/*.js
test/playback.js
core/base64.js
core/des.js
core/display.js
core/input/devices.js
core/input/keysym.js
core/logo.js
core/playback.js
core/rfb.js
app/ui.js
core/util.js
core/websock.js
app/webutil.js
core/input/xtscancodes.js
The HTML, CSS, font and images files that included with the noVNC
source distibution (or repository) are not considered part of the
@@ -30,7 +39,7 @@ The HTML, CSS, font and image files are licensed as follows:
Some portions of noVNC are copyright to their individual authors.
Please refer to the individual source files and/or to the noVNC commit
history: https://github.com/novnc/noVNC/commits/master
history: https://github.com/kanaka/noVNC/commits/master
The are several files and projects that have been incorporated into
the noVNC core library. Here is a list of those files and the original
@@ -40,7 +49,8 @@ licenses (all MPL 2.0 compatible):
core/des.js : Various BSD style licenses
vendor/pako/ : MIT
utils/inflator.mod.js
include/inflator.js : MIT (for pako)
Any other files not mentioned above are typically marked with
a copyright/license header at the top of the file. The default noVNC
@@ -49,14 +59,21 @@ license is MPL-2.0.
The following license texts are included:
docs/LICENSE.MPL-2.0
docs/LICENSE.LGPL-3 and
docs/LICENSE.GPL-3
docs/LICENSE.OFL-1.1
docs/LICENSE.BSD-3-Clause (New BSD)
docs/LICENSE.BSD-2-Clause (Simplified BSD / FreeBSD)
vendor/pako/LICENSE (MIT)
docs/LICENSE.zlib
docs/LICENSE.Apache-2.0
docs/LICENSE.pako
Or alternatively the license texts may be found here:
http://www.mozilla.org/MPL/2.0/
http://www.gnu.org/licenses/lgpl.html and
http://www.gnu.org/licenses/gpl.html
http://scripts.sil.org/OFL
http://en.wikipedia.org/wiki/BSD_licenses
https://opensource.org/licenses/MIT
http://www.gzip.org/zlib/zlib_license.html
http://www.apache.org/licenses/LICENSE-2.0.html
+61 -125
View File
@@ -1,15 +1,14 @@
## noVNC: HTML VNC Client Library and Application
## noVNC: HTML5 VNC Client
[![Test Status](https://github.com/novnc/noVNC/workflows/Test/badge.svg)](https://github.com/novnc/noVNC/actions?query=workflow%3ATest)
[![Lint Status](https://github.com/novnc/noVNC/workflows/Lint/badge.svg)](https://github.com/novnc/noVNC/actions?query=workflow%3ALint)
[![Build Status](https://travis-ci.org/novnc/noVNC.svg?branch=master)](https://travis-ci.org/novnc/noVNC)
### Description
noVNC is both a HTML VNC client JavaScript library and an application built on
top of that library. noVNC runs well in any modern browser including mobile
browsers (iOS and Android).
noVNC is a HTML5 VNC client that runs well in any modern browser including
mobile browsers (iOS and Android).
Many companies, projects and products have integrated noVNC including
[Ganeti Web Manager](http://code.osuosl.org/projects/ganeti-webmgr),
[OpenStack](http://www.openstack.org),
[OpenNebula](http://opennebula.org/),
[LibVNCServer](http://libvncserver.sourceforge.net), and
@@ -17,39 +16,20 @@ Many companies, projects and products have integrated noVNC including
[the Projects and Companies wiki page](https://github.com/novnc/noVNC/wiki/Projects-and-companies-using-noVNC)
for a more complete list with additional info and links.
### Table of Contents
- [News/help/contact](#newshelpcontact)
- [Features](#features)
- [Screenshots](#screenshots)
- [Browser Requirements](#browser-requirements)
- [Server Requirements](#server-requirements)
- [Quick Start](#quick-start)
- [Installation from Snap Package](#installation-from-snap-package)
- [Integration and Deployment](#integration-and-deployment)
- [Authors/Contributors](#authorscontributors)
### News/help/contact
The project website is found at [novnc.com](http://novnc.com).
Notable commits, announcements and news are posted to
[@noVNC](http://www.twitter.com/noVNC).
<a href="http://www.twitter.com/noVNC">@noVNC</a>.
If you are a noVNC developer/integrator/user (or want to be) please join the
[noVNC discussion group](https://groups.google.com/forum/?fromgroups#!forum/novnc).
<a href="https://groups.google.com/forum/?fromgroups#!forum/novnc">
noVNC discussion group</a>.
Bugs and feature requests can be submitted via
[github issues](https://github.com/novnc/noVNC/issues). If you have questions
about using noVNC then please first use the
[discussion group](https://groups.google.com/forum/?fromgroups#!forum/novnc).
We also have a [wiki](https://github.com/novnc/noVNC/wiki/) with lots of
helpful information.
[github issues](https://github.com/novnc/noVNC/issues).
If you are looking for a place to start contributing to noVNC, a good place to
start would be the issues that are marked as
["patchwelcome"](https://github.com/novnc/noVNC/issues?labels=patchwelcome).
Please check our
[contribution guide](https://github.com/novnc/noVNC/wiki/Contributing) though.
If you want to show appreciation for noVNC you could donate to a great non-
profits such as:
@@ -59,143 +39,100 @@ profits such as:
[Electronic Frontier Foundation](https://www.eff.org/),
[Against Malaria Foundation](http://www.againstmalaria.com/),
[Nothing But Nets](http://www.nothingbutnets.net/), etc.
Please tweet [@noVNC](http://www.twitter.com/noVNC) if you do.
Please tweet <a href="http://www.twitter.com/noVNC">@noVNC</a> if you do.
### Features
* Supports all modern browsers including mobile (iOS, Android)
* Supported VNC encodings: raw, copyrect, rre, hextile, tight, tightPNG
* Supports scaling, clipping and resizing the desktop
* Local cursor rendering
* WebSocket SSL/TLS encryption (i.e. "wss://") support
* 24-bit true color and 8 bit colour mapped
* Supports desktop resize notification/pseudo-encoding
* Local or remote cursor
* Clipboard copy/paste
* Translations
* Touch gestures for emulating common mouse actions
* Licensed mainly under the [MPL 2.0](http://www.mozilla.org/MPL/2.0/), see
[the license document](LICENSE.txt) for details
* Clipping or scolling modes for large remote screens
* Easy site integration and theming (3 example themes included)
* Licensed under the [MPL 2.0](http://www.mozilla.org/MPL/2.0/)
### Screenshots
Running in Firefox before and after connecting:
Running in Chrome before and after connecting:
<img src="http://novnc.com/img/noVNC-1-login.png" width=400>&nbsp;
<img src="http://novnc.com/img/noVNC-3-connected.png" width=400>
<img src="http://novnc.com/img/noVNC-5.png" width=400>&nbsp;
<img src="http://novnc.com/img/noVNC-7.jpg" width=400>
See more screenshots
[here](http://novnc.com/screenshots.html).
<a href="http://novnc.com/screenshots.html">here</a>.
### Browser Requirements
noVNC uses many modern web technologies so a formal requirement list is
not available. However these are the minimum versions we are currently
aware of:
* Chrome 8, Firefox 4, Safari 6, Opera 12, IE 11, Edge 12, etc.
* Chrome 64, Firefox 79, Safari 13.4, Opera 51, Edge 79
* HTML5 Canvas, WebSockets and Typed Arrays
* Fast Javascript Engine: this is not strictly a requirement, but without a
fast Javascript engine, noVNC might be painfully slow.
* See the more detailed
[browser compatibility wiki page](https://github.com/novnc/noVNC/wiki/Browser-support).
### Server Requirements
noVNC follows the standard VNC protocol, but unlike other VNC clients it does
require WebSockets support. Many servers include support (e.g.
[x11vnc/libvncserver](http://libvncserver.sourceforge.net/),
[QEMU](http://www.qemu.org/), and
[MobileVNC](http://www.smartlab.at/mobilevnc/)), but for the others you need to
use a WebSockets to TCP socket proxy. noVNC has a sister project
[websockify](https://github.com/novnc/websockify) that provides a simple such
proxy.
Unless you are using a VNC server with support for WebSockets connections (such
as [x11vnc/libvncserver](http://libvncserver.sourceforge.net/),
[QEMU](http://www.qemu.org/), or
[MobileVNC](http://www.smartlab.at/mobilevnc/)), you need to use a
WebSockets to TCP socket proxy. There is a python proxy included
('websockify').
### Quick Start
* Use the `novnc_proxy` script to automatically download and start websockify, which
includes a mini-webserver and the WebSockets proxy. The `--vnc` option is
used to specify the location of a running VNC server:
* Use the launch script to start a mini-webserver and the WebSockets proxy
(websockify). The `--vnc` option is used to specify the location of a running
VNC server:
`./utils/novnc_proxy --vnc localhost:5901`
`./utils/launch.sh --vnc localhost:5901`
* Point your browser to the cut-and-paste URL that is output by the `novnc_procy`
script. Hit the Connect button, enter a password if the VNC server has one
configured, and enjoy!
* Point your browser to the cut-and-paste URL that is output by the launch
script. Enter a password if the VNC server has one configured. Hit the
Connect button and enjoy!
### Installation from Snap Package
Running the command below will install the latest release of noVNC from Snap:
`sudo snap install novnc`
### Other Pages
#### Running noVNC
* [Modules/API](https://github.com/novnc/noVNC/wiki/Modules-API) - The library
modules and their Javascript API.
You can run the Snap-package installed novnc directly with, for example:
* [Integration](https://github.com/novnc/noVNC/wiki/Integration) - Get noVNC
to work in existing projects.
`novnc --listen 6081 --vnc localhost:5901 # /snap/bin/novnc if /snap/bin is not in your PATH`
* [Troubleshooting](https://github.com/novnc/noVNC/wiki/Troubleshooting) - How
to troubleshoot problems.
#### Running as a Service (Daemon)
The Snap package also has the capability to run a 'novnc' service which can be
configured to listen on multiple ports connecting to multiple VNC servers
(effectively a service runing multiple instances of novnc).
Instructions (with example values):
* [Encrypted Connections](https://github.com/novnc/websockify/wiki/Encrypted-Connections) -
Setup websockify so that you can use encrypted connections from noVNC.
List current services (out-of-box this will be blank):
* [Advanced Usage](https://github.com/novnc/noVNC/wiki/Advanced-usage) -
Generating an SSL certificate, starting a VNC server, advanced websockify
usage, etc.
```
sudo snap get novnc services
Key Value
services.n6080 {...}
services.n6081 {...}
```
* [Testing](https://github.com/novnc/noVNC/wiki/Testing) - Run and write
tests.
Create a new service that listens on port 6082 and connects to the VNC server
running on port 5902 on localhost:
`sudo snap set novnc services.n6082.listen=6082 services.n6082.vnc=localhost:5902`
(Any services you define with 'snap set' will be automatically started)
Note that the name of the service, 'n6082' in this example, can be anything
as long as it doesn't start with a number or contain spaces/special characters.
View the configuration of the service just created:
```
sudo snap get novnc services.n6082
Key Value
services.n6082.listen 6082
services.n6082.vnc localhost:5902
```
Disable a service (note that because of a limitation in Snap it's currently not
possible to unset config variables, setting them to blank values is the way
to disable a service):
`sudo snap set novnc services.n6082.listen='' services.n6082.vnc=''`
(Any services you set to blank with 'snap set' like this will be automatically stopped)
Verify that the service is disabled (blank values):
```
sudo snap get novnc services.n6082
Key Value
services.n6082.listen
services.n6082.vnc
```
### Integration and Deployment
Please see our other documents for how to integrate noVNC in your own software,
or deploying the noVNC application in production environments:
* [Embedding](docs/EMBEDDING.md) - For the noVNC application
* [Library](docs/LIBRARY.md) - For the noVNC JavaScript library
* [Translations](https://github.com/novnc/noVNC/wiki/Translations) - Add and
modify localization for JavaScript and HTML.
### Authors/Contributors
See [AUTHORS](AUTHORS) for a (full-ish) list of authors. If you're not on
that list and you think you should be, feel free to send a PR to fix that.
* Core team:
* [Joel Martin](https://github.com/kanaka)
* [Samuel Mannehed](https://github.com/samhed) (Cendio)
* [Peter Åstrand](https://github.com/astrand) (Cendio)
* [Solly Ross](https://github.com/DirectXMan12) (Red Hat / OpenStack)
* [Pierre Ossman](https://github.com/CendioOssman) (Cendio)
@@ -205,10 +142,9 @@ that list and you think you should be, feel free to send a PR to fix that.
* tight encoding : Michael Tinglof (Mercuri.ca)
* Included libraries:
* as3crypto : Henri Torgemane (code.google.com/p/as3crypto)
* base64 : Martijn Pieters (Digital Creations 2), Samuel Sieb (sieb.net)
* DES : Dave Zimmerman (Widget Workshop), Jef Poskanzer (ACME Labs)
* Pako : Vitaly Puzrin (https://github.com/nodeca/pako)
Do you want to be on this list? Check out our
[contribution guide](https://github.com/novnc/noVNC/wiki/Contributing) and
start hacking!
* [Contribution guide](https://github.com/novnc/noVNC/wiki/Contributing)
-66
View File
@@ -1,66 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
// NB: this should *not* be included as a module until we have
// native support in the browsers, so that our error handler
// can catch script-loading errors.
// No ES6 can be used in this file since it's used for the translation
/* eslint-disable prefer-arrow-callback */
(function _scope() {
"use strict";
// Fallback for all uncought errors
function handleError(event, err) {
try {
const msg = document.getElementById('noVNC_fallback_errormsg');
// Only show the initial error
if (msg.hasChildNodes()) {
return false;
}
let div = document.createElement("div");
div.classList.add('noVNC_message');
div.appendChild(document.createTextNode(event.message));
msg.appendChild(div);
if (event.filename) {
div = document.createElement("div");
div.className = 'noVNC_location';
let text = event.filename;
if (event.lineno !== undefined) {
text += ":" + event.lineno;
if (event.colno !== undefined) {
text += ":" + event.colno;
}
}
div.appendChild(document.createTextNode(text));
msg.appendChild(div);
}
if (err && err.stack) {
div = document.createElement("div");
div.className = 'noVNC_stack';
div.appendChild(document.createTextNode(err.stack));
msg.appendChild(div);
}
document.getElementById('noVNC_fallback_error')
.classList.add("noVNC_open");
} catch (exc) {
document.write("noVNC encountered an error.");
}
// Don't return true since this would prevent the error
// from being printed to the browser console.
return false;
}
window.addEventListener('error', function onerror(evt) { handleError(evt, evt.error); });
window.addEventListener('unhandledrejection', function onreject(evt) { handleError(evt.reason, evt.reason); });
})();
+2 -1
View File
@@ -10,7 +10,8 @@ ANDROID_LAUNCHER := \
novnc-72x72.png \
novnc-96x96.png \
novnc-144x144.png \
novnc-192x192.png
novnc-192x192.png \
novnc-512x512.png
IPHONE_LAUNCHER := \
novnc-60x60.png \
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

+92
View File
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="25"
height="25"
viewBox="0 0 25 25"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="mouse_left.svg"
inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#959595"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="11.313708"
inkscape:cx="15.551515"
inkscape:cy="12.205592"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:object-paths="true"
showguides="true"
inkscape:window-width="1920"
inkscape:window-height="1136"
inkscape:window-x="1920"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:snap-smooth-nodes="true"
inkscape:object-nodes="true"
inkscape:snap-intersection-paths="true"
inkscape:snap-nodes="true"
inkscape:snap-global="true">
<inkscape:grid
type="xygrid"
id="grid4136" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1027.3622)">
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0068f6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 8,1030.3622 c -2.1987124,0 -4,1.8013 -4,4 l 0,2 5,0 0,-2 c 0,-1.4738 1.090393,-2.7071 2.5,-2.9492 l 0,-1.0508 -3.5,0 z"
id="path6219" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 13.5,1030.3622 0,1.0508 c 1.409607,0.2421 2.5,1.4754 2.5,2.9492 l 0,2 5,0 0,-2 c 0,-2.1987 -1.801288,-4 -4,-4 l -3.5,0 z"
id="path6217" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 12,1033.3622 c -0.571311,0 -1,0.4287 -1,1 l 0,5 c 0,0.5713 0.428689,1 1,1 l 1,0 c 0.571311,0 1,-0.4287 1,-1 l 0,-5 c 0,-0.5713 -0.428689,-1 -1,-1 l -1,0 z"
id="path6215" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 4,1038.3622 0,3.5 c 0,4.1377 3.362302,7.5 7.5,7.5 l 2,0 c 4.137698,0 7.5,-3.3623 7.5,-7.5 l 0,-3.5 -5,0 0,1 c 0,1.6447 -1.355293,3 -3,3 l -1,0 c -1.644707,0 -3,-1.3553 -3,-3 l 0,-1 -5,0 z"
id="rect6178" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

+92
View File
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="25"
height="25"
viewBox="0 0 25 25"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="mouse_middle.svg"
inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#959595"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="11.313708"
inkscape:cx="15.551515"
inkscape:cy="12.205592"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:object-paths="true"
showguides="true"
inkscape:window-width="1920"
inkscape:window-height="1136"
inkscape:window-x="1920"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:snap-smooth-nodes="true"
inkscape:object-nodes="true"
inkscape:snap-intersection-paths="true"
inkscape:snap-nodes="true"
inkscape:snap-global="true">
<inkscape:grid
type="xygrid"
id="grid4136" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1027.3622)">
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 8,1030.3622 c -2.1987124,0 -4,1.8013 -4,4 l 0,2 5,0 0,-2 c 0,-1.4738 1.090393,-2.7071 2.5,-2.9492 l 0,-1.0508 -3.5,0 z"
id="path6219" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 13.5,1030.3622 0,1.0508 c 1.409607,0.2421 2.5,1.4754 2.5,2.9492 l 0,2 5,0 0,-2 c 0,-2.1987 -1.801288,-4 -4,-4 l -3.5,0 z"
id="path6217" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0068f6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 12,1033.3622 c -0.571311,0 -1,0.4287 -1,1 l 0,5 c 0,0.5713 0.428689,1 1,1 l 1,0 c 0.571311,0 1,-0.4287 1,-1 l 0,-5 c 0,-0.5713 -0.428689,-1 -1,-1 l -1,0 z"
id="path6215" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 4,1038.3622 0,3.5 c 0,4.1377 3.362302,7.5 7.5,7.5 l 2,0 c 4.137698,0 7.5,-3.3623 7.5,-7.5 l 0,-3.5 -5,0 0,1 c 0,1.6447 -1.355293,3 -3,3 l -1,0 c -1.644707,0 -3,-1.3553 -3,-3 l 0,-1 -5,0 z"
id="rect6178" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

+92
View File
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="25"
height="25"
viewBox="0 0 25 25"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="mouse_none.svg"
inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#959595"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="16"
inkscape:cx="23.160825"
inkscape:cy="13.208262"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:object-paths="true"
showguides="true"
inkscape:window-width="1920"
inkscape:window-height="1136"
inkscape:window-x="1920"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:snap-smooth-nodes="true"
inkscape:object-nodes="true"
inkscape:snap-intersection-paths="true"
inkscape:snap-nodes="true"
inkscape:snap-global="true">
<inkscape:grid
type="xygrid"
id="grid4136" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1027.3622)">
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 8,1030.3622 c -2.1987124,0 -4,1.8013 -4,4 l 0,2 5,0 0,-2 c 0,-1.4738 1.090393,-2.7071 2.5,-2.9492 l 0,-1.0508 -3.5,0 z"
id="path6219" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 13.5,1030.3622 0,1.0508 c 1.409607,0.2421 2.5,1.4754 2.5,2.9492 l 0,2 5,0 0,-2 c 0,-2.1987 -1.801288,-4 -4,-4 l -3.5,0 z"
id="path6217" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 12,1033.3622 c -0.571311,0 -1,0.4287 -1,1 l 0,5 c 0,0.5713 0.428689,1 1,1 l 1,0 c 0.571311,0 1,-0.4287 1,-1 l 0,-5 c 0,-0.5713 -0.428689,-1 -1,-1 l -1,0 z"
id="path6215" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 4,1038.3622 0,3.5 c 0,4.1377 3.362302,7.5 7.5,7.5 l 2,0 c 4.137698,0 7.5,-3.3623 7.5,-7.5 l 0,-3.5 -5,0 0,1 c 0,1.6447 -1.355293,3 -3,3 l -1,0 c -1.644707,0 -3,-1.3553 -3,-3 l 0,-1 -5,0 z"
id="rect6178" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

+92
View File
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="25"
height="25"
viewBox="0 0 25 25"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="mouse_right.svg"
inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#959595"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="11.313708"
inkscape:cx="15.551515"
inkscape:cy="12.205592"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:object-paths="true"
showguides="true"
inkscape:window-width="1920"
inkscape:window-height="1136"
inkscape:window-x="1920"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:snap-smooth-nodes="true"
inkscape:object-nodes="true"
inkscape:snap-intersection-paths="true"
inkscape:snap-nodes="true"
inkscape:snap-global="true">
<inkscape:grid
type="xygrid"
id="grid4136" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1027.3622)">
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 8,1030.3622 c -2.1987124,0 -4,1.8013 -4,4 l 0,2 5,0 0,-2 c 0,-1.4738 1.090393,-2.7071 2.5,-2.9492 l 0,-1.0508 -3.5,0 z"
id="path6219" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0068f6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 13.5,1030.3622 0,1.0508 c 1.409607,0.2421 2.5,1.4754 2.5,2.9492 l 0,2 5,0 0,-2 c 0,-2.1987 -1.801288,-4 -4,-4 l -3.5,0 z"
id="path6217" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 12,1033.3622 c -0.571311,0 -1,0.4287 -1,1 l 0,5 c 0,0.5713 0.428689,1 1,1 l 1,0 c 0.571311,0 1,-0.4287 1,-1 l 0,-5 c 0,-0.5713 -0.428689,-1 -1,-1 l -1,0 z"
id="path6215" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 4,1038.3622 0,3.5 c 0,4.1377 3.362302,7.5 7.5,7.5 l 2,0 c 4.137698,0 7.5,-3.3623 7.5,-7.5 l 0,-3.5 -5,0 0,1 c 0,1.6447 -1.355293,3 -3,3 l -1,0 c -1.644707,0 -3,-1.3553 -3,-3 l 0,-1 -5,0 z"
id="rect6178" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

-65
View File
@@ -1,65 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="svg2"
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
sodipodi:docname="windows.svg"
inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
inkscape:version="0.92.4 (unknown)"
x="0px"
y="0px"
viewBox="-293 384 25 25"
xml:space="preserve"
width="25"
height="25"><metadata
id="metadata21"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs19" /><sodipodi:namedview
pagecolor="#959595"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1136"
id="namedview17"
showgrid="true"
inkscape:pagecheckerboard="false"
inkscape:zoom="32"
inkscape:cx="3.926913"
inkscape:cy="13.255959"
inkscape:window-x="1920"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"><inkscape:grid
type="xygrid"
id="grid818" /></sodipodi:namedview>
<style
type="text/css"
id="style2">
.st0{fill:#FFFFFF;}
</style>
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="M 21 4 L 11 5.1757812 L 11 12 L 21 12 L 21 4 z M 10 5.2949219 L 4 6 L 4 12 L 10 12 L 10 5.2949219 z "
transform="translate(-293,384)"
id="path853" /><path
id="path858"
d="m -272,405 -10,-1.17578 V 397 h 10 z M -283,403.70508 -289,403 v -6 h 6 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" /></svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

-1
View File
@@ -1 +0,0 @@
DO NOT MODIFY THE FILES IN THIS FOLDER, THEY ARE AUTOMATICALLY GENERATED FROM THE PO-FILES.
-71
View File
@@ -1,71 +0,0 @@
{
"Connecting...": "Připojení...",
"Disconnecting...": "Odpojení...",
"Reconnecting...": "Obnova připojení...",
"Internal error": "Vnitřní chyba",
"Must set host": "Hostitel musí být nastavení",
"Connected (encrypted) to ": "Připojení (šifrované) k ",
"Connected (unencrypted) to ": "Připojení (nešifrované) k ",
"Something went wrong, connection is closed": "Něco se pokazilo, odpojeno",
"Failed to connect to server": "Chyba připojení k serveru",
"Disconnected": "Odpojeno",
"New connection has been rejected with reason: ": "Nové připojení bylo odmítnuto s odůvodněním: ",
"New connection has been rejected": "Nové připojení bylo odmítnuto",
"Password is required": "Je vyžadováno heslo",
"noVNC encountered an error:": "noVNC narazilo na chybu:",
"Hide/Show the control bar": "Skrýt/zobrazit ovládací panel",
"Move/Drag Viewport": "Přesunout/přetáhnout výřez",
"viewport drag": "přesun výřezu",
"Active Mouse Button": "Aktivní tlačítka myši",
"No mousebutton": "Žádné",
"Left mousebutton": "Levé tlačítko myši",
"Middle mousebutton": "Prostřední tlačítko myši",
"Right mousebutton": "Pravé tlačítko myši",
"Keyboard": "Klávesnice",
"Show Keyboard": "Zobrazit klávesnici",
"Extra keys": "Extra klávesy",
"Show Extra Keys": "Zobrazit extra klávesy",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Přepnout Ctrl",
"Alt": "Alt",
"Toggle Alt": "Přepnout Alt",
"Send Tab": "Odeslat tabulátor",
"Tab": "Tab",
"Esc": "Esc",
"Send Escape": "Odeslat Esc",
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
"Send Ctrl-Alt-Del": "Poslat Ctrl-Alt-Del",
"Shutdown/Reboot": "Vypnutí/Restart",
"Shutdown/Reboot...": "Vypnutí/Restart...",
"Power": "Napájení",
"Shutdown": "Vypnout",
"Reboot": "Restart",
"Reset": "Reset",
"Clipboard": "Schránka",
"Clear": "Vymazat",
"Fullscreen": "Celá obrazovka",
"Settings": "Nastavení",
"Shared Mode": "Sdílený režim",
"View Only": "Pouze prohlížení",
"Clip to Window": "Přizpůsobit oknu",
"Scaling Mode:": "Přizpůsobení velikosti",
"None": "Žádné",
"Local Scaling": "Místní",
"Remote Resizing": "Vzdálené",
"Advanced": "Pokročilé",
"Repeater ID:": "ID opakovače",
"WebSocket": "WebSocket",
"Encrypt": "Šifrování:",
"Host:": "Hostitel:",
"Port:": "Port:",
"Path:": "Cesta",
"Automatic Reconnect": "Automatická obnova připojení",
"Reconnect Delay (ms):": "Zpoždění připojení (ms)",
"Show Dot when No Cursor": "Tečka místo chybějícího kurzoru myši",
"Logging:": "Logování:",
"Disconnect": "Odpojit",
"Connect": "Připojit",
"Password:": "Heslo",
"Send Password": "Odeslat heslo",
"Cancel": "Zrušit"
}
+18
View File
@@ -0,0 +1,18 @@
/*
* Translations for de
*
* This file was autotomatically generated from de.po
* DO NOT EDIT!
*/
Language = {
"Connecting...": "Verbunden...",
"Connected (encrypted) to ": "Verbunden mit (verschlüsselt) ",
"Connected (unencrypted) to ": "Verbunden mit (unverschlüsselt) ",
"Disconnecting...": "Verbindung trennen...",
"Disconnected": "Verbindung zum Server getrennt",
"Must set host and port": "Richten Sie Host und Port ein",
"Password is required": "Passwort ist erforderlich",
"Forcing clipping mode since scrollbars aren't supported by IE in fullscreen": "'Clipping-Modus' aktiviert, Scrollbalken in 'IE-Vollbildmodus' werden nicht unterstützt",
"Disconnect timeout": "Timeout beim trennen",
};
-69
View File
@@ -1,69 +0,0 @@
{
"Connecting...": "Verbinden...",
"Disconnecting...": "Verbindung trennen...",
"Reconnecting...": "Verbindung wiederherstellen...",
"Internal error": "Interner Fehler",
"Must set host": "Richten Sie den Server ein",
"Connected (encrypted) to ": "Verbunden mit (verschlüsselt) ",
"Connected (unencrypted) to ": "Verbunden mit (unverschlüsselt) ",
"Something went wrong, connection is closed": "Etwas lief schief, Verbindung wurde getrennt",
"Disconnected": "Verbindung zum Server getrennt",
"New connection has been rejected with reason: ": "Verbindung wurde aus folgendem Grund abgelehnt: ",
"New connection has been rejected": "Verbindung wurde abgelehnt",
"Password is required": "Passwort ist erforderlich",
"noVNC encountered an error:": "Ein Fehler ist aufgetreten:",
"Hide/Show the control bar": "Kontrollleiste verstecken/anzeigen",
"Move/Drag Viewport": "Ansichtsfenster verschieben/ziehen",
"viewport drag": "Ansichtsfenster ziehen",
"Active Mouse Button": "Aktive Maustaste",
"No mousebutton": "Keine Maustaste",
"Left mousebutton": "Linke Maustaste",
"Middle mousebutton": "Mittlere Maustaste",
"Right mousebutton": "Rechte Maustaste",
"Keyboard": "Tastatur",
"Show Keyboard": "Tastatur anzeigen",
"Extra keys": "Zusatztasten",
"Show Extra Keys": "Zusatztasten anzeigen",
"Ctrl": "Strg",
"Toggle Ctrl": "Strg umschalten",
"Alt": "Alt",
"Toggle Alt": "Alt umschalten",
"Send Tab": "Tab senden",
"Tab": "Tab",
"Esc": "Esc",
"Send Escape": "Escape senden",
"Ctrl+Alt+Del": "Strg+Alt+Entf",
"Send Ctrl-Alt-Del": "Strg+Alt+Entf senden",
"Shutdown/Reboot": "Herunterfahren/Neustarten",
"Shutdown/Reboot...": "Herunterfahren/Neustarten...",
"Power": "Energie",
"Shutdown": "Herunterfahren",
"Reboot": "Neustarten",
"Reset": "Zurücksetzen",
"Clipboard": "Zwischenablage",
"Clear": "Löschen",
"Fullscreen": "Vollbild",
"Settings": "Einstellungen",
"Shared Mode": "Geteilter Modus",
"View Only": "Nur betrachten",
"Clip to Window": "Auf Fenster begrenzen",
"Scaling Mode:": "Skalierungsmodus:",
"None": "Keiner",
"Local Scaling": "Lokales skalieren",
"Remote Resizing": "Serverseitiges skalieren",
"Advanced": "Erweitert",
"Repeater ID:": "Repeater ID:",
"WebSocket": "WebSocket",
"Encrypt": "Verschlüsselt",
"Host:": "Server:",
"Port:": "Port:",
"Path:": "Pfad:",
"Automatic Reconnect": "Automatisch wiederverbinden",
"Reconnect Delay (ms):": "Wiederverbindungsverzögerung (ms):",
"Logging:": "Protokollierung:",
"Disconnect": "Verbindung trennen",
"Connect": "Verbinden",
"Password:": "Passwort:",
"Cancel": "Abbrechen",
"Canvas not supported.": "Canvas nicht unterstützt."
}
+27 -22
View File
@@ -1,17 +1,21 @@
{
/*
* Translations for el
*
* This file was autotomatically generated from el.po
* DO NOT EDIT!
*/
Language = {
"Connecting...": "Συνδέεται...",
"Disconnecting...": "Aποσυνδέεται...",
"Reconnecting...": "Επανασυνδέεται...",
"Internal error": "Εσωτερικό σφάλμα",
"Must set host": "Πρέπει να οριστεί ο διακομιστής",
"Connected (encrypted) to ": "Συνδέθηκε (κρυπτογραφημένα) με το ",
"Connected (unencrypted) to ": "Συνδέθηκε (μη κρυπτογραφημένα) με το ",
"Something went wrong, connection is closed": "Κάτι πήγε στραβά, η σύνδεση διακόπηκε",
"Disconnecting...": "Aποσυνδέεται...",
"Disconnected": "Αποσυνδέθηκε",
"New connection has been rejected with reason: ": "Η νέα σύνδεση απορρίφθηκε διότι: ",
"New connection has been rejected": "Η νέα σύνδεση απορρίφθηκε ",
"Must set host and port": "Πρέπει να οριστεί το όνομα και η πόρτα του διακομιστή",
"Password is required": "Απαιτείται ο κωδικός πρόσβασης",
"noVNC encountered an error:": "το noVNC αντιμετώπισε ένα σφάλμα:",
"Forcing clipping mode since scrollbars aren't supported by IE in fullscreen": "Εφαρμογή λειτουργίας αποκοπής αφού δεν υποστηρίζονται οι λωρίδες κύλισης σε πλήρη οθόνη στον IE",
"Disconnect timeout": "Παρέλευση χρονικού ορίου αποσύνδεσης",
"noVNC encountered an error:": "το noVNC αντιμετώπισε ένα σφάλμα",
"Hide/Show the control bar": "Απόκρυψη/Εμφάνιση γραμμής ελέγχου",
"Move/Drag Viewport": "Μετακίνηση/Σύρσιμο Θεατού πεδίου",
"viewport drag": "σύρσιμο θεατού πεδίου",
@@ -44,26 +48,27 @@
"Clear": "Καθάρισμα",
"Fullscreen": "Πλήρης Οθόνη",
"Settings": "Ρυθμίσεις",
"Encrypt": "Κρυπτογράφηση",
"True Color": "Πραγματικά Χρώματα",
"Local Cursor": "Τοπικός Δρομέας",
"Clip to Window": "Αποκοπή στο όριο του Παράθυρου",
"Shared Mode": "Κοινόχρηστη Λειτουργία",
"View Only": "Μόνο Θέαση",
"Clip to Window": "Αποκοπή στο όριο του Παράθυρου",
"Path:": "Διαδρομή:",
"Scaling Mode:": "Λειτουργία Κλιμάκωσης:",
"None": "Καμία",
"Local Scaling": "Τοπική Κλιμάκωση",
"Local Downscaling": "Τοπική Συρρίκνωση",
"Remote Resizing": "Απομακρυσμένη Αλλαγή μεγέθους",
"Advanced": "Για προχωρημένους",
"Repeater ID:": "Repeater ID:",
"WebSocket": "WebSocket",
"Encrypt": "Κρυπτογράφηση",
"Style:": "Στυλ:",
"default": "προεπιλεγμένο",
"Logging:": "Καταγραφή:",
"Apply": "Εφαρμογή",
"Host:": "Όνομα διακομιστή:",
"Port:": "Πόρτα διακομιστή:",
"Path:": "Διαδρομή:",
"Automatic Reconnect": "Αυτόματη επανασύνδεση",
"Reconnect Delay (ms):": "Καθυστέρηση επανασύνδεσης (ms):",
"Logging:": "Καταγραφή:",
"Disconnect": "Αποσύνδεση",
"Connect": "Σύνδεση",
"Password:": "Κωδικός Πρόσβασης:",
"Cancel": "Ακύρωση",
"Canvas not supported.": "Δεν υποστηρίζεται το στοιχείο Canvas"
}
"Token:": "Διακριτικό:",
"Send Password": "Αποστολή Κωδικού Πρόσβασης",
"Canvas not supported.": "Δεν υποστηρίζεται το στοιχείο Canvas",
};
-68
View File
@@ -1,68 +0,0 @@
{
"Connecting...": "Conectando...",
"Connected (encrypted) to ": "Conectado (con encriptación) a",
"Connected (unencrypted) to ": "Conectado (sin encriptación) a",
"Disconnecting...": "Desconectando...",
"Disconnected": "Desconectado",
"Must set host": "Debes configurar el host",
"Reconnecting...": "Reconectando...",
"Password is required": "Contraseña es obligatoria",
"Disconnect timeout": "Tiempo de desconexión agotado",
"noVNC encountered an error:": "noVNC ha encontrado un error:",
"Hide/Show the control bar": "Ocultar/Mostrar la barra de control",
"Move/Drag Viewport": "Mover/Arrastrar la ventana",
"viewport drag": "Arrastrar la ventana",
"Active Mouse Button": "Botón activo del ratón",
"No mousebutton": "Ningún botón del ratón",
"Left mousebutton": "Botón izquierdo del ratón",
"Middle mousebutton": "Botón central del ratón",
"Right mousebutton": "Botón derecho del ratón",
"Keyboard": "Teclado",
"Show Keyboard": "Mostrar teclado",
"Extra keys": "Teclas adicionales",
"Show Extra Keys": "Mostrar Teclas Adicionales",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Pulsar/Soltar Ctrl",
"Alt": "Alt",
"Toggle Alt": "Pulsar/Soltar Alt",
"Send Tab": "Enviar Tabulación",
"Tab": "Tabulación",
"Esc": "Esc",
"Send Escape": "Enviar Escape",
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
"Send Ctrl-Alt-Del": "Enviar Ctrl+Alt+Del",
"Shutdown/Reboot": "Apagar/Reiniciar",
"Shutdown/Reboot...": "Apagar/Reiniciar...",
"Power": "Encender",
"Shutdown": "Apagar",
"Reboot": "Reiniciar",
"Reset": "Restablecer",
"Clipboard": "Portapapeles",
"Clear": "Vaciar",
"Fullscreen": "Pantalla Completa",
"Settings": "Configuraciones",
"Shared Mode": "Modo Compartido",
"View Only": "Solo visualización",
"Clip to Window": "Recortar al tamaño de la ventana",
"Scaling Mode:": "Modo de escalado:",
"None": "Ninguno",
"Local Scaling": "Escalado Local",
"Local Downscaling": "Reducción de escala local",
"Remote Resizing": "Cambio de tamaño remoto",
"Advanced": "Avanzado",
"Local Cursor": "Cursor Local",
"Repeater ID:": "ID del Repetidor",
"WebSocket": "WebSocket",
"Encrypt": "",
"Host:": "Host",
"Port:": "Puesto",
"Path:": "Ruta",
"Automatic Reconnect": "Reconexión automática",
"Reconnect Delay (ms):": "Retraso en la reconexión (ms)",
"Logging:": "Logging",
"Disconnect": "Desconectar",
"Connect": "Conectar",
"Password:": "Contraseña",
"Cancel": "Cancelar",
"Canvas not supported.": "Canvas no está soportado"
}
-72
View File
@@ -1,72 +0,0 @@
{
"Connecting...": "接続しています...",
"Disconnecting...": "切断しています...",
"Reconnecting...": "再接続しています...",
"Internal error": "内部エラー",
"Must set host": "ホストを設定する必要があります",
"Connected (encrypted) to ": "接続しました (暗号化済み): ",
"Connected (unencrypted) to ": "接続しました (暗号化されていません): ",
"Something went wrong, connection is closed": "何らかの問題で、接続が閉じられました",
"Failed to connect to server": "サーバーへの接続に失敗しました",
"Disconnected": "切断しました",
"New connection has been rejected with reason: ": "新規接続は次の理由で拒否されました: ",
"New connection has been rejected": "新規接続は拒否されました",
"Credentials are required": "資格情報が必要です",
"noVNC encountered an error:": "noVNC でエラーが発生しました:",
"Hide/Show the control bar": "コントロールバーを隠す/表示する",
"Drag": "ドラッグ",
"Move/Drag Viewport": "ビューポートを移動/ドラッグ",
"Keyboard": "キーボード",
"Show Keyboard": "キーボードを表示",
"Extra keys": "追加キー",
"Show Extra Keys": "追加キーを表示",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Ctrl キーを切り替え",
"Alt": "Alt",
"Toggle Alt": "Alt キーを切り替え",
"Toggle Windows": "Windows キーを切り替え",
"Windows": "Windows",
"Send Tab": "Tab キーを送信",
"Tab": "Tab",
"Esc": "Esc",
"Send Escape": "Escape キーを送信",
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
"Send Ctrl-Alt-Del": "Ctrl-Alt-Del を送信",
"Shutdown/Reboot": "シャットダウン/再起動",
"Shutdown/Reboot...": "シャットダウン/再起動...",
"Power": "電源",
"Shutdown": "シャットダウン",
"Reboot": "再起動",
"Reset": "リセット",
"Clipboard": "クリップボード",
"Clear": "クリア",
"Fullscreen": "全画面表示",
"Settings": "設定",
"Shared Mode": "共有モード",
"View Only": "表示のみ",
"Clip to Window": "ウィンドウにクリップ",
"Scaling Mode:": "スケーリングモード:",
"None": "なし",
"Local Scaling": "ローカルスケーリング",
"Remote Resizing": "リモートでリサイズ",
"Advanced": "高度",
"Quality:": "品質:",
"Compression level:": "圧縮レベル:",
"Repeater ID:": "リピーター ID:",
"WebSocket": "WebSocket",
"Encrypt": "暗号化",
"Host:": "ホスト:",
"Port:": "ポート:",
"Path:": "パス:",
"Automatic Reconnect": "自動再接続",
"Reconnect Delay (ms):": "再接続する遅延 (ミリ秒):",
"Show Dot when No Cursor": "カーソルがないときにドットを表示",
"Logging:": "ロギング:",
"Version:": "バージョン:",
"Disconnect": "切断",
"Connect": "接続",
"Username:": "ユーザー名:",
"Password:": "パスワード:",
"Send Credentials": "資格情報を送信",
"Cancel": "キャンセル"
}
-70
View File
@@ -1,70 +0,0 @@
{
"Connecting...": "연결중...",
"Disconnecting...": "연결 해제중...",
"Reconnecting...": "재연결중...",
"Internal error": "내부 오류",
"Must set host": "호스트는 설정되어야 합니다.",
"Connected (encrypted) to ": "다음과 (암호화되어) 연결되었습니다:",
"Connected (unencrypted) to ": "다음과 (암호화 없이) 연결되었습니다:",
"Something went wrong, connection is closed": "무언가 잘못되었습니다, 연결이 닫혔습니다.",
"Failed to connect to server": "서버에 연결하지 못했습니다.",
"Disconnected": "연결이 해제되었습니다.",
"New connection has been rejected with reason: ": "새 연결이 다음 이유로 거부되었습니다:",
"New connection has been rejected": "새 연결이 거부되었습니다.",
"Password is required": "비밀번호가 필요합니다.",
"noVNC encountered an error:": "noVNC에 오류가 발생했습니다:",
"Hide/Show the control bar": "컨트롤 바 숨기기/보이기",
"Move/Drag Viewport": "움직이기/드래그 뷰포트",
"viewport drag": "뷰포트 드래그",
"Active Mouse Button": "마우스 버튼 활성화",
"No mousebutton": "마우스 버튼 없음",
"Left mousebutton": "왼쪽 마우스 버튼",
"Middle mousebutton": "중간 마우스 버튼",
"Right mousebutton": "오른쪽 마우스 버튼",
"Keyboard": "키보드",
"Show Keyboard": "키보드 보이기",
"Extra keys": "기타 키들",
"Show Extra Keys": "기타 키들 보이기",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Ctrl 켜기/끄기",
"Alt": "Alt",
"Toggle Alt": "Alt 켜기/끄기",
"Send Tab": "Tab 보내기",
"Tab": "Tab",
"Esc": "Esc",
"Send Escape": "Esc 보내기",
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
"Send Ctrl-Alt-Del": "Ctrl+Alt+Del 보내기",
"Shutdown/Reboot": "셧다운/리붓",
"Shutdown/Reboot...": "셧다운/리붓...",
"Power": "전원",
"Shutdown": "셧다운",
"Reboot": "리붓",
"Reset": "리셋",
"Clipboard": "클립보드",
"Clear": "지우기",
"Fullscreen": "전체화면",
"Settings": "설정",
"Shared Mode": "공유 모드",
"View Only": "보기 전용",
"Clip to Window": "창에 클립",
"Scaling Mode:": "스케일링 모드:",
"None": "없음",
"Local Scaling": "로컬 스케일링",
"Remote Resizing": "원격 크기 조절",
"Advanced": "고급",
"Repeater ID:": "중계 ID",
"WebSocket": "웹소켓",
"Encrypt": "암호화",
"Host:": "호스트:",
"Port:": "포트:",
"Path:": "위치:",
"Automatic Reconnect": "자동 재연결",
"Reconnect Delay (ms):": "재연결 지연 시간 (ms)",
"Logging:": "로깅",
"Disconnect": "연결 해제",
"Connect": "연결",
"Password:": "비밀번호:",
"Send Password": "비밀번호 전송",
"Cancel": "취소"
}
+18
View File
@@ -0,0 +1,18 @@
/*
* Translations for nl
*
* This file was autotomatically generated from nl.po
* DO NOT EDIT!
*/
Language = {
"Connecting...": "Verbinden...",
"Connected (encrypted) to ": "Verbonden (versleuteld) met ",
"Connected (unencrypted) to ": "Verbonden (onversleuteld) met ",
"Disconnecting...": "Verbinding verbreken...",
"Disconnected": "Verbinding verbroken",
"Must set host and port": "Host en poort moeten worden ingesteld",
"Password is required": "Wachtwoord is vereist",
"Forcing clipping mode since scrollbars aren't supported by IE in fullscreen": "''Clipping mode' ingeschakeld, omdat schuifbalken in volledige-scherm-modus in IE niet worden ondersteund",
"Disconnect timeout": "Timeout tijdens verbreken van verbinding",
};
-73
View File
@@ -1,73 +0,0 @@
{
"Connecting...": "Verbinden...",
"Disconnecting...": "Verbinding verbreken...",
"Reconnecting...": "Opnieuw verbinding maken...",
"Internal error": "Interne fout",
"Must set host": "Host moeten worden ingesteld",
"Connected (encrypted) to ": "Verbonden (versleuteld) met ",
"Connected (unencrypted) to ": "Verbonden (onversleuteld) met ",
"Something went wrong, connection is closed": "Er iets fout gelopen, verbinding werd verbroken",
"Failed to connect to server": "Verbinding maken met server is mislukt",
"Disconnected": "Verbinding verbroken",
"New connection has been rejected with reason: ": "Nieuwe verbinding is geweigerd omwille van de volgende reden: ",
"New connection has been rejected": "Nieuwe verbinding is geweigerd",
"Password is required": "Wachtwoord is vereist",
"noVNC encountered an error:": "noVNC heeft een fout bemerkt:",
"Hide/Show the control bar": "Verberg/Toon de bedieningsbalk",
"Move/Drag Viewport": "Verplaats/Versleep Kijkvenster",
"viewport drag": "kijkvenster slepen",
"Active Mouse Button": "Actieve Muisknop",
"No mousebutton": "Geen muisknop",
"Left mousebutton": "Linker muisknop",
"Middle mousebutton": "Middelste muisknop",
"Right mousebutton": "Rechter muisknop",
"Keyboard": "Toetsenbord",
"Show Keyboard": "Toon Toetsenbord",
"Extra keys": "Extra toetsen",
"Show Extra Keys": "Toon Extra Toetsen",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Ctrl omschakelen",
"Alt": "Alt",
"Toggle Alt": "Alt omschakelen",
"Toggle Windows": "Windows omschakelen",
"Windows": "Windows",
"Send Tab": "Tab Sturen",
"Tab": "Tab",
"Esc": "Esc",
"Send Escape": "Escape Sturen",
"Ctrl+Alt+Del": "Ctrl-Alt-Del",
"Send Ctrl-Alt-Del": "Ctrl-Alt-Del Sturen",
"Shutdown/Reboot": "Uitschakelen/Herstarten",
"Shutdown/Reboot...": "Uitschakelen/Herstarten...",
"Power": "Systeem",
"Shutdown": "Uitschakelen",
"Reboot": "Herstarten",
"Reset": "Resetten",
"Clipboard": "Klembord",
"Clear": "Wissen",
"Fullscreen": "Volledig Scherm",
"Settings": "Instellingen",
"Shared Mode": "Gedeelde Modus",
"View Only": "Alleen Kijken",
"Clip to Window": "Randen buiten venster afsnijden",
"Scaling Mode:": "Schaalmodus:",
"None": "Geen",
"Local Scaling": "Lokaal Schalen",
"Remote Resizing": "Op Afstand Formaat Wijzigen",
"Advanced": "Geavanceerd",
"Repeater ID:": "Repeater ID:",
"WebSocket": "WebSocket",
"Encrypt": "Versleutelen",
"Host:": "Host:",
"Port:": "Poort:",
"Path:": "Pad:",
"Automatic Reconnect": "Automatisch Opnieuw Verbinden",
"Reconnect Delay (ms):": "Vertraging voor Opnieuw Verbinden (ms):",
"Show Dot when No Cursor": "Geef stip weer indien geen cursor",
"Logging:": "Logmeldingen:",
"Disconnect": "Verbinding verbreken",
"Connect": "Verbinden",
"Password:": "Wachtwoord:",
"Send Password": "Verzend Wachtwoord:",
"Cancel": "Annuleren"
}
-69
View File
@@ -1,69 +0,0 @@
{
"Connecting...": "Łączenie...",
"Disconnecting...": "Rozłączanie...",
"Reconnecting...": "Łączenie...",
"Internal error": "Błąd wewnętrzny",
"Must set host": "Host i port są wymagane",
"Connected (encrypted) to ": "Połączenie (szyfrowane) z ",
"Connected (unencrypted) to ": "Połączenie (nieszyfrowane) z ",
"Something went wrong, connection is closed": "Coś poszło źle, połączenie zostało zamknięte",
"Disconnected": "Rozłączony",
"New connection has been rejected with reason: ": "Nowe połączenie zostało odrzucone z powodu: ",
"New connection has been rejected": "Nowe połączenie zostało odrzucone",
"Password is required": "Hasło jest wymagane",
"noVNC encountered an error:": "noVNC napotkało błąd:",
"Hide/Show the control bar": "Pokaż/Ukryj pasek ustawień",
"Move/Drag Viewport": "Ruszaj/Przeciągaj Viewport",
"viewport drag": "przeciągnij viewport",
"Active Mouse Button": "Aktywny Przycisk Myszy",
"No mousebutton": "Brak przycisku myszy",
"Left mousebutton": "Lewy przycisk myszy",
"Middle mousebutton": "Środkowy przycisk myszy",
"Right mousebutton": "Prawy przycisk myszy",
"Keyboard": "Klawiatura",
"Show Keyboard": "Pokaż klawiaturę",
"Extra keys": "Przyciski dodatkowe",
"Show Extra Keys": "Pokaż przyciski dodatkowe",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Przełącz Ctrl",
"Alt": "Alt",
"Toggle Alt": "Przełącz Alt",
"Send Tab": "Wyślij Tab",
"Tab": "Tab",
"Esc": "Esc",
"Send Escape": "Wyślij Escape",
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
"Send Ctrl-Alt-Del": "Wyślij Ctrl-Alt-Del",
"Shutdown/Reboot": "Wyłącz/Uruchom ponownie",
"Shutdown/Reboot...": "Wyłącz/Uruchom ponownie...",
"Power": "Włączony",
"Shutdown": "Wyłącz",
"Reboot": "Uruchom ponownie",
"Reset": "Resetuj",
"Clipboard": "Schowek",
"Clear": "Wyczyść",
"Fullscreen": "Pełny ekran",
"Settings": "Ustawienia",
"Shared Mode": "Tryb Współdzielenia",
"View Only": "Tylko Podgląd",
"Clip to Window": "Przytnij do Okna",
"Scaling Mode:": "Tryb Skalowania:",
"None": "Brak",
"Local Scaling": "Skalowanie lokalne",
"Remote Resizing": "Skalowanie zdalne",
"Advanced": "Zaawansowane",
"Repeater ID:": "ID Repeatera:",
"WebSocket": "WebSocket",
"Encrypt": "Szyfrowanie",
"Host:": "Host:",
"Port:": "Port:",
"Path:": "Ścieżka:",
"Automatic Reconnect": "Automatycznie wznawiaj połączenie",
"Reconnect Delay (ms):": "Opóźnienie wznawiania (ms):",
"Logging:": "Poziom logowania:",
"Disconnect": "Rozłącz",
"Connect": "Połącz",
"Password:": "Hasło:",
"Cancel": "Anuluj",
"Canvas not supported.": "Element Canvas nie jest wspierany."
}
-72
View File
@@ -1,72 +0,0 @@
{
"Connecting...": "Conectando...",
"Disconnecting...": "Desconectando...",
"Reconnecting...": "Reconectando...",
"Internal error": "Erro interno",
"Must set host": "É necessário definir o host",
"Connected (encrypted) to ": "Conectado (com criptografia) a ",
"Connected (unencrypted) to ": "Conectado (sem criptografia) a ",
"Something went wrong, connection is closed": "Algo deu errado. A conexão foi encerrada.",
"Failed to connect to server": "Falha ao conectar-se ao servidor",
"Disconnected": "Desconectado",
"New connection has been rejected with reason: ": "A nova conexão foi rejeitada pelo motivo: ",
"New connection has been rejected": "A nova conexão foi rejeitada",
"Credentials are required": "Credenciais são obrigatórias",
"noVNC encountered an error:": "O noVNC encontrou um erro:",
"Hide/Show the control bar": "Esconder/mostrar a barra de controles",
"Drag": "Arrastar",
"Move/Drag Viewport": "Mover/arrastar a janela",
"Keyboard": "Teclado",
"Show Keyboard": "Mostrar teclado",
"Extra keys": "Teclas adicionais",
"Show Extra Keys": "Mostar teclas adicionais",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Pressionar/soltar Ctrl",
"Alt": "Alt",
"Toggle Alt": "Pressionar/soltar Alt",
"Toggle Windows": "Pressionar/soltar Windows",
"Windows": "Windows",
"Send Tab": "Enviar Tab",
"Tab": "Tab",
"Esc": "Esc",
"Send Escape": "Enviar Esc",
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
"Send Ctrl-Alt-Del": "Enviar Ctrl-Alt-Del",
"Shutdown/Reboot": "Desligar/reiniciar",
"Shutdown/Reboot...": "Desligar/reiniciar...",
"Power": "Ligar",
"Shutdown": "Desligar",
"Reboot": "Reiniciar",
"Reset": "Reiniciar (forçado)",
"Clipboard": "Área de transferência",
"Clear": "Limpar",
"Fullscreen": "Tela cheia",
"Settings": "Configurações",
"Shared Mode": "Modo compartilhado",
"View Only": "Apenas visualizar",
"Clip to Window": "Recortar à janela",
"Scaling Mode:": "Modo de dimensionamento:",
"None": "Nenhum",
"Local Scaling": "Local",
"Remote Resizing": "Remoto",
"Advanced": "Avançado",
"Quality:": "Qualidade:",
"Compression level:": "Nível de compressão:",
"Repeater ID:": "ID do repetidor:",
"WebSocket": "WebSocket",
"Encrypt": "Criptografar",
"Host:": "Host:",
"Port:": "Porta:",
"Path:": "Caminho:",
"Automatic Reconnect": "Reconexão automática",
"Reconnect Delay (ms):": "Atraso da reconexão (ms)",
"Show Dot when No Cursor": "Mostrar ponto quando não há cursor",
"Logging:": "Registros:",
"Version:": "Versão:",
"Disconnect": "Desconectar",
"Connect": "Conectar",
"Username:": "Nome de usuário:",
"Password:": "Senha:",
"Send Credentials": "Enviar credenciais",
"Cancel": "Cancelar"
}
-73
View File
@@ -1,73 +0,0 @@
{
"Connecting...": "Подключение...",
"Disconnecting...": "Отключение...",
"Reconnecting...": "Переподключение...",
"Internal error": "Внутренняя ошибка",
"Must set host": "Задайте имя сервера или IP",
"Connected (encrypted) to ": "Подключено (с шифрованием) к ",
"Connected (unencrypted) to ": "Подключено (без шифрования) к ",
"Something went wrong, connection is closed": "Что-то пошло не так, подключение разорвано",
"Failed to connect to server": "Ошибка подключения к серверу",
"Disconnected": "Отключено",
"New connection has been rejected with reason: ": "Подключиться не удалось: ",
"New connection has been rejected": "Подключиться не удалось",
"Password is required": "Требуется пароль",
"noVNC encountered an error:": "Ошибка noVNC: ",
"Hide/Show the control bar": "Скрыть/Показать контрольную панель",
"Move/Drag Viewport": "Переместить окно",
"viewport drag": "Переместить окно",
"Active Mouse Button": "Активировать кнопки мыши",
"No mousebutton": "Отключить кнопки мыши",
"Left mousebutton": "Левая кнопка мыши",
"Middle mousebutton": "Средняя кнопка мыши",
"Right mousebutton": "Правая кнопка мыши",
"Keyboard": "Клавиатура",
"Show Keyboard": "Показать клавиатуру",
"Extra keys": "Доп. кнопки",
"Show Extra Keys": "Показать дополнительные кнопки",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Передать нажатие Ctrl",
"Alt": "Alt",
"Toggle Alt": "Передать нажатие Alt",
"Toggle Windows": "Переключение вкладок",
"Windows": "Вкладка",
"Send Tab": "Передать нажатие Tab",
"Tab": "Tab",
"Esc": "Esc",
"Send Escape": "Передать нажатие Escape",
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
"Send Ctrl-Alt-Del": "Передать нажатие Ctrl-Alt-Del",
"Shutdown/Reboot": "Выключить/Перезагрузить",
"Shutdown/Reboot...": "Выключить/Перезагрузить...",
"Power": "Питание",
"Shutdown": "Выключить",
"Reboot": "Перезагрузить",
"Reset": "Сброс",
"Clipboard": "Буфер обмена",
"Clear": "Очистить",
"Fullscreen": "Во весь экран",
"Settings": "Настройки",
"Shared Mode": "Общий режим",
"View Only": "Просмотр",
"Clip to Window": "В окно",
"Scaling Mode:": "Масштаб:",
"None": "Нет",
"Local Scaling": "Локльный масштаб",
"Remote Resizing": "Удаленный масштаб",
"Advanced": "Дополнительно",
"Repeater ID:": "Идентификатор ID:",
"WebSocket": "WebSocket",
"Encrypt": "Шифрование",
"Host:": "Сервер:",
"Port:": "Порт:",
"Path:": "Путь:",
"Automatic Reconnect": "Автоматическое переподключение",
"Reconnect Delay (ms):": "Задержка переподключения (мс):",
"Show Dot when No Cursor": "Показать точку вместо курсора",
"Logging:": "Лог:",
"Disconnect": "Отключение",
"Connect": "Подключение",
"Password:": "Пароль:",
"Send Password": "Пароль: ",
"Cancel": "Выход"
}
+36 -31
View File
@@ -1,21 +1,29 @@
{
/*
* Translations for sv
*
* This file was autotomatically generated from sv.po
* DO NOT EDIT!
*/
Language = {
"Connecting...": "Ansluter...",
"Disconnecting...": "Kopplar ner...",
"Reconnecting...": "Återansluter...",
"Internal error": "Internt fel",
"Must set host": "Du måste specifiera en värd",
"Connected (encrypted) to ": "Ansluten (krypterat) till ",
"Connected (unencrypted) to ": "Ansluten (okrypterat) till ",
"Something went wrong, connection is closed": "Något gick fel, anslutningen avslutades",
"Failed to connect to server": "Misslyckades att ansluta till servern",
"Disconnecting...": "Kopplar ner...",
"Disconnected": "Frånkopplad",
"New connection has been rejected with reason: ": "Ny anslutning har blivit nekad med följande skäl: ",
"New connection has been rejected": "Ny anslutning har blivit nekad",
"Credentials are required": "Användaruppgifter krävs",
"Must set host and port": "Du måste specifiera en host och port",
"Password is required": "Lösenord krävs",
"Forcing clipping mode since scrollbars aren't supported by IE in fullscreen": "Tvingar 'Clipping mode' eftersom skrollning inte stödjs av IE i fullskärm",
"Disconnect timeout": "Det tog för lång tid att koppla ner",
"noVNC encountered an error:": "noVNC stötte på ett problem:",
"Hide/Show the control bar": "Göm/Visa kontrollbaren",
"Drag": "Dra",
"Move/Drag Viewport": "Flytta/Dra Vyn",
"viewport drag": "dra vy",
"Active Mouse Button": "Aktiv musknapp",
"No mousebutton": "Ingen musknapp",
"Left mousebutton": "Vänster musknapp",
"Middle mousebutton": "Mitten-musknapp",
"Right mousebutton": "Höger musknapp",
"Keyboard": "Tangentbord",
"Show Keyboard": "Visa Tangentbord",
"Extra keys": "Extraknappar",
@@ -24,8 +32,6 @@
"Toggle Ctrl": "Växla Ctrl",
"Alt": "Alt",
"Toggle Alt": "Växla Alt",
"Toggle Windows": "Växla Windows",
"Windows": "Windows",
"Send Tab": "Skicka Tab",
"Tab": "Tab",
"Esc": "Esc",
@@ -42,31 +48,30 @@
"Clear": "Rensa",
"Fullscreen": "Fullskärm",
"Settings": "Inställningar",
"Encrypt": "Kryptera",
"True Color": "Fullfärg",
"Local Cursor": "Lokal Muspekare",
"Clip to Window": "Begränsa till Fönster",
"Shared Mode": "Delat Läge",
"View Only": "Endast Visning",
"Clip to Window": "Begränsa till Fönster",
"Path:": "Sökväg:",
"Scaling Mode:": "Skalningsläge:",
"None": "Ingen",
"Local Scaling": "Lokal Skalning",
"Local Downscaling": "Lokal Nedskalning",
"Remote Resizing": "Ändra Storlek",
"Advanced": "Avancerat",
"Quality:": "Kvalitet:",
"Compression level:": "Kompressionsnivå:",
"Repeater ID:": "Repeater-ID:",
"WebSocket": "WebSocket",
"Encrypt": "Kryptera",
"Style:": "Stil:",
"default": "standard",
"Logging:": "Loggning:",
"Apply": "Verkställ",
"Connect": "Anslut",
"Disconnect": "Koppla från",
"Connection": "Uppkoppling",
"Host:": "Värd:",
"Port:": "Port:",
"Path:": "Sökväg:",
"Automatic Reconnect": "Automatisk Återanslutning",
"Reconnect Delay (ms):": "Fördröjning (ms):",
"Show Dot when No Cursor": "Visa prick när ingen muspekare finns",
"Logging:": "Loggning:",
"Version:": "Version:",
"Disconnect": "Koppla från",
"Connect": "Anslut",
"Username:": "Användarnamn:",
"Password:": "Lösenord:",
"Send Credentials": "Skicka Användaruppgifter",
"Cancel": "Avbryt"
}
"Token:": "Token:",
"Send Password": "Skicka Lösenord",
"Canvas not supported.": "Canvas stöds ej",
};
-69
View File
@@ -1,69 +0,0 @@
{
"Connecting...": "Bağlanıyor...",
"Disconnecting...": "Bağlantı kesiliyor...",
"Reconnecting...": "Yeniden bağlantı kuruluyor...",
"Internal error": "İç hata",
"Must set host": "Sunucuyu kur",
"Connected (encrypted) to ": "Bağlı (şifrelenmiş)",
"Connected (unencrypted) to ": "Bağlandı (şifrelenmemiş)",
"Something went wrong, connection is closed": "Bir şeyler ters gitti, bağlantı kesildi",
"Disconnected": "Bağlantı kesildi",
"New connection has been rejected with reason: ": "Bağlantı aşağıdaki nedenlerden dolayı reddedildi: ",
"New connection has been rejected": "Bağlantı reddedildi",
"Password is required": "Şifre gerekli",
"noVNC encountered an error:": "Bir hata oluştu:",
"Hide/Show the control bar": "Denetim masasını Gizle/Göster",
"Move/Drag Viewport": "Görünümü Taşı/Sürükle",
"viewport drag": "Görüntü penceresini sürükle",
"Active Mouse Button": "Aktif Fare Düğmesi",
"No mousebutton": "Fare düğmesi yok",
"Left mousebutton": "Farenin sol düğmesi",
"Middle mousebutton": "Farenin orta düğmesi",
"Right mousebutton": "Farenin sağ düğmesi",
"Keyboard": "Klavye",
"Show Keyboard": "Klavye Düzenini Göster",
"Extra keys": "Ekstra tuşlar",
"Show Extra Keys": "Ekstra tuşları göster",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Ctrl Değiştir ",
"Alt": "Alt",
"Toggle Alt": "Alt Değiştir",
"Send Tab": "Sekme Gönder",
"Tab": "Sekme",
"Esc": "Esc",
"Send Escape": "Boşluk Gönder",
"Ctrl+Alt+Del": "Ctrl + Alt + Del",
"Send Ctrl-Alt-Del": "Ctrl-Alt-Del Gönder",
"Shutdown/Reboot": "Kapat/Yeniden Başlat",
"Shutdown/Reboot...": "Kapat/Yeniden Başlat...",
"Power": "Güç",
"Shutdown": "Kapat",
"Reboot": "Yeniden Başlat",
"Reset": "Sıfırla",
"Clipboard": "Pano",
"Clear": "Temizle",
"Fullscreen": "Tam Ekran",
"Settings": "Ayarlar",
"Shared Mode": "Paylaşım Modu",
"View Only": "Sadece Görüntüle",
"Clip to Window": "Pencereye Tıkla",
"Scaling Mode:": "Ölçekleme Modu:",
"None": "Bilinmeyen",
"Local Scaling": "Yerel Ölçeklendirme",
"Remote Resizing": "Uzaktan Yeniden Boyutlandırma",
"Advanced": "Gelişmiş",
"Repeater ID:": "Tekralayıcı ID:",
"WebSocket": "WebSocket",
"Encrypt": "Şifrele",
"Host:": "Ana makine:",
"Port:": "Port:",
"Path:": "Yol:",
"Automatic Reconnect": "Otomatik Yeniden Bağlan",
"Reconnect Delay (ms):": "Yeniden Bağlanma Süreci (ms):",
"Logging:": "Giriş yapılıyor:",
"Disconnect": "Bağlantıyı Kes",
"Connect": "Bağlan",
"Password:": "Parola:",
"Cancel": "Vazgeç",
"Canvas not supported.": "Tuval desteklenmiyor."
}
-69
View File
@@ -1,69 +0,0 @@
{
"Connecting...": "连接中...",
"Disconnecting...": "正在断开连接...",
"Reconnecting...": "重新连接中...",
"Internal error": "内部错误",
"Must set host": "请提供主机名",
"Connected (encrypted) to ": "已连接到(加密)",
"Connected (unencrypted) to ": "已连接到(未加密)",
"Something went wrong, connection is closed": "发生错误,连接已关闭",
"Failed to connect to server": "无法连接到服务器",
"Disconnected": "已断开连接",
"New connection has been rejected with reason: ": "连接被拒绝,原因:",
"New connection has been rejected": "连接被拒绝",
"Password is required": "请提供密码",
"noVNC encountered an error:": "noVNC 遇到一个错误:",
"Hide/Show the control bar": "显示/隐藏控制栏",
"Move/Drag Viewport": "拖放显示范围",
"viewport drag": "显示范围拖放",
"Active Mouse Button": "启动鼠标按鍵",
"No mousebutton": "禁用鼠标按鍵",
"Left mousebutton": "鼠标左鍵",
"Middle mousebutton": "鼠标中鍵",
"Right mousebutton": "鼠标右鍵",
"Keyboard": "键盘",
"Show Keyboard": "显示键盘",
"Extra keys": "额外按键",
"Show Extra Keys": "显示额外按键",
"Ctrl": "Ctrl",
"Toggle Ctrl": "切换 Ctrl",
"Alt": "Alt",
"Toggle Alt": "切换 Alt",
"Send Tab": "发送 Tab 键",
"Tab": "Tab",
"Esc": "Esc",
"Send Escape": "发送 Escape 键",
"Ctrl+Alt+Del": "Ctrl-Alt-Del",
"Send Ctrl-Alt-Del": "发送 Ctrl-Alt-Del 键",
"Shutdown/Reboot": "关机/重新启动",
"Shutdown/Reboot...": "关机/重新启动...",
"Power": "电源",
"Shutdown": "关机",
"Reboot": "重新启动",
"Reset": "重置",
"Clipboard": "剪贴板",
"Clear": "清除",
"Fullscreen": "全屏",
"Settings": "设置",
"Shared Mode": "分享模式",
"View Only": "仅查看",
"Clip to Window": "限制/裁切窗口大小",
"Scaling Mode:": "缩放模式:",
"None": "无",
"Local Scaling": "本地缩放",
"Remote Resizing": "远程调整大小",
"Advanced": "高级",
"Repeater ID:": "中继站 ID",
"WebSocket": "WebSocket",
"Encrypt": "加密",
"Host:": "主机:",
"Port:": "端口:",
"Path:": "路径:",
"Automatic Reconnect": "自动重新连接",
"Reconnect Delay (ms):": "重新连接间隔 (ms)",
"Logging:": "日志级别:",
"Disconnect": "中断连接",
"Connect": "连接",
"Password:": "密码:",
"Cancel": "取消"
}
-69
View File
@@ -1,69 +0,0 @@
{
"Connecting...": "連線中...",
"Disconnecting...": "正在中斷連線...",
"Reconnecting...": "重新連線中...",
"Internal error": "內部錯誤",
"Must set host": "請提供主機資訊",
"Connected (encrypted) to ": "已加密連線到",
"Connected (unencrypted) to ": "未加密連線到",
"Something went wrong, connection is closed": "發生錯誤,連線已關閉",
"Failed to connect to server": "無法連線到伺服器",
"Disconnected": "連線已中斷",
"New connection has been rejected with reason: ": "連線被拒絕,原因:",
"New connection has been rejected": "連線被拒絕",
"Password is required": "請提供密碼",
"noVNC encountered an error:": "noVNC 遇到一個錯誤:",
"Hide/Show the control bar": "顯示/隱藏控制列",
"Move/Drag Viewport": "拖放顯示範圍",
"viewport drag": "顯示範圍拖放",
"Active Mouse Button": "啟用滑鼠按鍵",
"No mousebutton": "無滑鼠按鍵",
"Left mousebutton": "滑鼠左鍵",
"Middle mousebutton": "滑鼠中鍵",
"Right mousebutton": "滑鼠右鍵",
"Keyboard": "鍵盤",
"Show Keyboard": "顯示鍵盤",
"Extra keys": "額外按鍵",
"Show Extra Keys": "顯示額外按鍵",
"Ctrl": "Ctrl",
"Toggle Ctrl": "切換 Ctrl",
"Alt": "Alt",
"Toggle Alt": "切換 Alt",
"Send Tab": "送出 Tab 鍵",
"Tab": "Tab",
"Esc": "Esc",
"Send Escape": "送出 Escape 鍵",
"Ctrl+Alt+Del": "Ctrl-Alt-Del",
"Send Ctrl-Alt-Del": "送出 Ctrl-Alt-Del 快捷鍵",
"Shutdown/Reboot": "關機/重新啟動",
"Shutdown/Reboot...": "關機/重新啟動...",
"Power": "電源",
"Shutdown": "關機",
"Reboot": "重新啟動",
"Reset": "重設",
"Clipboard": "剪貼簿",
"Clear": "清除",
"Fullscreen": "全螢幕",
"Settings": "設定",
"Shared Mode": "分享模式",
"View Only": "僅檢視",
"Clip to Window": "限制/裁切視窗大小",
"Scaling Mode:": "縮放模式:",
"None": "無",
"Local Scaling": "本機縮放",
"Remote Resizing": "遠端調整大小",
"Advanced": "進階",
"Repeater ID:": "中繼站 ID",
"WebSocket": "WebSocket",
"Encrypt": "加密",
"Host:": "主機:",
"Port:": "連接埠:",
"Path:": "路徑:",
"Automatic Reconnect": "自動重新連線",
"Reconnect Delay (ms):": "重新連線間隔 (ms)",
"Logging:": "日誌級別:",
"Disconnect": "中斷連線",
"Connect": "連線",
"Password:": "密碼:",
"Cancel": "取消"
}
-172
View File
@@ -1,172 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
/*
* Localization Utilities
*/
export class Localizer {
constructor() {
// Currently configured language
this.language = 'en';
// Current dictionary of translations
this.dictionary = undefined;
}
// Configure suitable language based on user preferences
setup(supportedLanguages) {
this.language = 'en'; // Default: US English
/*
* Navigator.languages only available in Chrome (32+) and FireFox (32+)
* Fall back to navigator.language for other browsers
*/
let userLanguages;
if (typeof window.navigator.languages == 'object') {
userLanguages = window.navigator.languages;
} else {
userLanguages = [navigator.language || navigator.userLanguage];
}
for (let i = 0;i < userLanguages.length;i++) {
const userLang = userLanguages[i]
.toLowerCase()
.replace("_", "-")
.split("-");
// Built-in default?
if ((userLang[0] === 'en') &&
((userLang[1] === undefined) || (userLang[1] === 'us'))) {
return;
}
// First pass: perfect match
for (let j = 0; j < supportedLanguages.length; j++) {
const supLang = supportedLanguages[j]
.toLowerCase()
.replace("_", "-")
.split("-");
if (userLang[0] !== supLang[0]) {
continue;
}
if (userLang[1] !== supLang[1]) {
continue;
}
this.language = supportedLanguages[j];
return;
}
// Second pass: fallback
for (let j = 0;j < supportedLanguages.length;j++) {
const supLang = supportedLanguages[j]
.toLowerCase()
.replace("_", "-")
.split("-");
if (userLang[0] !== supLang[0]) {
continue;
}
if (supLang[1] !== undefined) {
continue;
}
this.language = supportedLanguages[j];
return;
}
}
}
// Retrieve localised text
get(id) {
if (typeof this.dictionary !== 'undefined' && this.dictionary[id]) {
return this.dictionary[id];
} else {
return id;
}
}
// Traverses the DOM and translates relevant fields
// See https://html.spec.whatwg.org/multipage/dom.html#attr-translate
translateDOM() {
const self = this;
function process(elem, enabled) {
function isAnyOf(searchElement, items) {
return items.indexOf(searchElement) !== -1;
}
function translateAttribute(elem, attr) {
const str = self.get(elem.getAttribute(attr));
elem.setAttribute(attr, str);
}
function translateTextNode(node) {
const str = self.get(node.data.trim());
node.data = str;
}
if (elem.hasAttribute("translate")) {
if (isAnyOf(elem.getAttribute("translate"), ["", "yes"])) {
enabled = true;
} else if (isAnyOf(elem.getAttribute("translate"), ["no"])) {
enabled = false;
}
}
if (enabled) {
if (elem.hasAttribute("abbr") &&
elem.tagName === "TH") {
translateAttribute(elem, "abbr");
}
if (elem.hasAttribute("alt") &&
isAnyOf(elem.tagName, ["AREA", "IMG", "INPUT"])) {
translateAttribute(elem, "alt");
}
if (elem.hasAttribute("download") &&
isAnyOf(elem.tagName, ["A", "AREA"])) {
translateAttribute(elem, "download");
}
if (elem.hasAttribute("label") &&
isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP",
"OPTION", "TRACK"])) {
translateAttribute(elem, "label");
}
// FIXME: Should update "lang"
if (elem.hasAttribute("placeholder") &&
isAnyOf(elem.tagName, ["INPUT", "TEXTAREA"])) {
translateAttribute(elem, "placeholder");
}
if (elem.hasAttribute("title")) {
translateAttribute(elem, "title");
}
if (elem.hasAttribute("value") &&
elem.tagName === "INPUT" &&
isAnyOf(elem.getAttribute("type"), ["reset", "button", "submit"])) {
translateAttribute(elem, "value");
}
}
for (let i = 0; i < elem.childNodes.length; i++) {
const node = elem.childNodes[i];
if (node.nodeType === node.ELEMENT_NODE) {
process(node, enabled);
} else if (node.nodeType === node.TEXT_NODE && enabled) {
translateTextNode(node);
}
}
}
process(document.body, true);
}
}
export const l10n = new Localizer();
export default l10n.get.bind(l10n);
+89
View File
@@ -0,0 +1,89 @@
/*
* noVNC auto CSS
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2016 Samuel Mannehed for Cendio AB
* noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
*/
body {
margin:0;
padding:0;
font-family: Helvetica;
/*Background image with light grey curve.*/
background-color:#494949;
background-repeat:no-repeat;
background-position:right bottom;
height:100%;
}
html {
height:100%;
}
#noVNC_container {
display: table;
width:100%;
height:100%;
background-color:#313131;
border-bottom-right-radius: 800px 600px;
/*border-top-left-radius: 800px 600px;*/
}
#noVNC_status {
font-size: 12px;
padding-top: 4px;
height:32px;
text-align: center;
font-weight: bold;
color: #fff;
z-index: 0;
position: absolute;
width: 100%;
margin-left: 0px;
}
.noVNC_status_normal {
background: #b2bdcd; /* Old browsers */
background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
}
.noVNC_status_error {
background: #f04040; /* Old browsers */
background: -moz-linear-gradient(top, #f04040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f04040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
background: linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
}
.noVNC_status_warn {
background: #f0f040; /* Old browsers */
background: -moz-linear-gradient(top, #f0f040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f0f040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
background: linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
}
#noVNC_buttons {
white-space: nowrap;
}
/* Do not set width/height for VNC_canvas or incorrect
* scaling will occur. Canvas size depends on remote VNC
* settings and noVNC settings. */
#noVNC_canvas {
position: absolute;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
}
+57 -156
View File
@@ -1,6 +1,8 @@
/*
* noVNC base CSS
* Copyright (C) 2019 The noVNC Authors
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2016 Samuel Mannehed for Cendio AB
* Copyright (C) 2016 Pierre Ossman for Cendio AB
* noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
*/
@@ -39,10 +41,6 @@ html {
display: none;
}
.noVNC_disabled {
color: rgb(128, 128, 128);
}
/* ----------------------------------------
* Spinner
* ----------------------------------------
@@ -55,7 +53,6 @@ html {
width: 10px;
height: 10px;
border-radius: 2px;
box-shadow: -60px 10px 0 rgba(255, 255, 255, 0);
animation: noVNC_spinner 1.0s linear infinite;
}
.noVNC_spinner::before {
@@ -83,20 +80,7 @@ html {
* ----------------------------------------
*/
input:not([type]),
input[type=date],
input[type=datetime-local],
input[type=email],
input[type=month],
input[type=number],
input[type=password],
input[type=search],
input[type=tel],
input[type=text],
input[type=time],
input[type=url],
input[type=week],
textarea {
input[type=input], input[type=password], input:not([type]), textarea {
/* Disable default rendering */
-webkit-appearance: none;
-moz-appearance: none;
@@ -110,11 +94,7 @@ textarea {
background: linear-gradient(to top, rgb(255, 255, 255) 80%, rgb(240, 240, 240));
}
input[type=button],
input[type=color],
input[type=reset],
input[type=submit],
select {
input[type=button], input[type=submit], select {
/* Disable default rendering */
-webkit-appearance: none;
-moz-appearance: none;
@@ -132,10 +112,7 @@ select {
vertical-align: middle;
}
input[type=button],
input[type=color],
input[type=reset],
input[type=submit] {
input[type=button], input[type=submit] {
padding-left: 20px;
padding-right: 20px;
}
@@ -145,72 +122,35 @@ option {
background: white;
}
input:not([type]):focus,
input[type=button]:focus,
input[type=color]:focus,
input[type=date]:focus,
input[type=datetime-local]:focus,
input[type=email]:focus,
input[type=month]:focus,
input[type=number]:focus,
input[type=password]:focus,
input[type=reset]:focus,
input[type=search]:focus,
input[type=input]:focus, input[type=password]:focus,
input:not([type]):focus, input[type=button]:focus,
input[type=submit]:focus,
input[type=tel]:focus,
input[type=text]:focus,
input[type=time]:focus,
input[type=url]:focus,
input[type=week]:focus,
select:focus,
textarea:focus {
textarea:focus, select:focus {
box-shadow: 0px 0px 3px rgba(74, 144, 217, 0.5);
border-color: rgb(74, 144, 217);
outline: none;
}
input[type=button]::-moz-focus-inner,
input[type=color]::-moz-focus-inner,
input[type=reset]::-moz-focus-inner,
input[type=submit]::-moz-focus-inner {
border: none;
}
input:not([type]):disabled,
input[type=button]:disabled,
input[type=color]:disabled,
input[type=date]:disabled,
input[type=datetime-local]:disabled,
input[type=email]:disabled,
input[type=month]:disabled,
input[type=number]:disabled,
input[type=password]:disabled,
input[type=reset]:disabled,
input[type=search]:disabled,
input[type=input]:disabled, input[type=password]:disabled,
input:not([type]):disabled, input[type=button]:disabled,
input[type=submit]:disabled,
input[type=tel]:disabled,
input[type=text]:disabled,
input[type=time]:disabled,
input[type=url]:disabled,
input[type=week]:disabled,
select:disabled,
textarea:disabled {
textarea:disabled, select:disabled {
color: rgb(128, 128, 128);
background: rgb(240, 240, 240);
}
input[type=button]:active,
input[type=color]:active,
input[type=reset]:active,
input[type=submit]:active,
input[type=button]:active, input[type=submit]:active,
select:active {
border-bottom-width: 1px;
margin-top: 3px;
}
:root:not(.noVNC_touch) input[type=button]:hover:not(:disabled),
:root:not(.noVNC_touch) input[type=color]:hover:not(:disabled),
:root:not(.noVNC_touch) input[type=reset]:hover:not(:disabled),
:root:not(.noVNC_touch) input[type=submit]:hover:not(:disabled),
:root:not(.noVNC_touch) select:hover:not(:disabled) {
background: linear-gradient(to top, rgb(255, 255, 255), rgb(250, 250, 250));
@@ -270,32 +210,31 @@ select:active {
*/
#noVNC_fallback_error {
position: fixed;
z-index: 1000;
visibility: hidden;
}
#noVNC_fallback_error.noVNC_open {
visibility: visible;
}
#noVNC_fallback_error > div {
max-width: 90%;
padding: 15px;
left: 50%;
transform: translate(-50%, -50px);
transition: 0.5s ease-in-out;
transform: translateY(-50px);
visibility: hidden;
opacity: 0;
top: 60px;
padding: 15px;
width: auto;
text-align: center;
font-weight: bold;
word-wrap: break-word;
color: #fff;
border-radius: 10px;
box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5);
background: rgba(200,55,55,0.8);
}
#noVNC_fallback_error.noVNC_open > div {
transform: translateY(0);
#noVNC_fallback_error.noVNC_open {
transform: translate(-50%, 0);
visibility: visible;
opacity: 1;
}
@@ -303,13 +242,6 @@ select:active {
font-weight: normal;
}
#noVNC_fallback_errormsg .noVNC_message {
display: inline-block;
text-align: left;
font-family: monospace;
white-space: pre-wrap;
}
#noVNC_fallback_error .noVNC_location {
font-style: italic;
font-size: 0.8em;
@@ -317,16 +249,13 @@ select:active {
}
#noVNC_fallback_error .noVNC_stack {
max-height: 50vh;
padding: 10px;
margin: 10px;
font-size: 0.8em;
text-align: left;
font-family: monospace;
white-space: pre;
border: 1px solid rgba(0, 0, 0, 0.5);
background: rgba(0, 0, 0, 0.2);
overflow: auto;
}
/* ----------------------------------------
@@ -458,36 +387,6 @@ select:active {
padding: 0 5px 0 10px;
}
/* Control bar hint */
#noVNC_control_bar_hint {
position: fixed;
left: calc(100vw - 50px);
right: auto;
top: 50%;
transform: translateY(-50%) scale(0);
width: 100px;
height: 50%;
max-height: 600px;
visibility: hidden;
opacity: 0;
transition: 0.2s ease-in-out;
background: transparent;
box-shadow: 0 0 10px black, inset 0 0 10px 10px rgba(110, 132, 163, 0.8);
border-radius: 10px;
transition-delay: 0s;
}
#noVNC_control_bar_anchor.noVNC_right #noVNC_control_bar_hint{
left: auto;
right: calc(100vw - 50px);
}
#noVNC_control_bar_hint.noVNC_active {
visibility: visible;
opacity: 1;
transition-delay: 0.2s;
transform: translateY(-50%) scale(1);
}
/* General button style */
.noVNC_button {
display: block;
@@ -511,15 +410,11 @@ select:active {
padding-top: 5px;
padding-bottom: 3px;
}
/* Android browsers don't properly update hover state if touch events
* are intercepted, but focus should be safe to display */
:root:not(.noVNC_touch) .noVNC_button.noVNC_selected:hover,
.noVNC_button.noVNC_selected:focus {
:root:not(.noVNC_touch) .noVNC_button.noVNC_selected:hover {
border-color: rgba(0, 0, 0, 0.4);
background: rgba(0, 0, 0, 0.2);
}
:root:not(.noVNC_touch) .noVNC_button:hover,
.noVNC_button:focus {
:root:not(.noVNC_touch) .noVNC_button:hover {
background: rgba(255, 255, 255, 0.2);
}
.noVNC_button.noVNC_hidden {
@@ -635,7 +530,7 @@ select:active {
}
/* Extra manual keys */
:root:not(.noVNC_connected) #noVNC_toggle_extra_keys_button {
:root:not(.noVNC_connected) #noVNC_extra_keys {
display: none;
}
@@ -645,17 +540,17 @@ select:active {
padding: 0 10px;
}
/* Shutdown/Reboot */
:root:not(.noVNC_connected) #noVNC_power_button {
/* XVP Shutdown/Reboot */
:root:not(.noVNC_connected) #noVNC_xvp_button {
display: none;
}
#noVNC_power {
#noVNC_xvp {
}
#noVNC_power_buttons {
#noVNC_xvp_buttons {
display: none;
}
#noVNC_power input[type=button] {
#noVNC_xvp input[type=button] {
width: 100%;
}
@@ -687,16 +582,6 @@ select:active {
width: 100px;
}
/* Version */
.noVNC_version_wrapper {
font-size: small;
}
.noVNC_version {
margin-left: 1rem;
}
/* Connection Controls */
:root:not(.noVNC_connected) #noVNC_disconnect_button {
display: none;
@@ -846,23 +731,19 @@ select:active {
* ----------------------------------------
*/
#noVNC_credentials_dlg {
#noVNC_password_dlg {
position: relative;
transform: translateY(-50px);
}
#noVNC_credentials_dlg.noVNC_open {
#noVNC_password_dlg.noVNC_open {
transform: translateY(0);
}
#noVNC_credentials_dlg ul {
#noVNC_password_dlg ul {
list-style: none;
margin: 0px;
padding: 0px;
}
.noVNC_hidden {
display: none;
}
/* ----------------------------------------
* Main Area
@@ -888,7 +769,6 @@ select:active {
justify-content: center;
flex-direction: column;
}
:root.noVNC_loading #noVNC_transition,
:root.noVNC_connecting #noVNC_transition,
:root.noVNC_disconnecting #noVNC_transition,
:root.noVNC_reconnecting #noVNC_transition {
@@ -922,6 +802,27 @@ select:active {
ime-mode: disabled;
}
/* HTML5 Canvas */
#noVNC_screen {
display: flex;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgb(40, 40, 40);
}
:root:not(.noVNC_connected) #noVNC_screen {
display: none;
}
/* Do not set width/height for VNC_canvas or incorrect
* scaling will occur. Canvas size depends on remote VNC
* settings and noVNC settings. */
#noVNC_canvas {
margin: auto;
/* IE miscalculates width without this :( */
flex-shrink: 0;
}
/*Default noVNC logo.*/
/* From: http://fonts.googleapis.com/css?family=Orbitron:700 */
@font-face {
+1425 -1433
View File
File diff suppressed because it is too large Load Diff
+218 -93
View File
@@ -1,82 +1,114 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2013 NTT corp.
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
import { initLogging as mainInitLogging } from '../core/util/logging.js';
/*jslint bitwise: false, white: false, browser: true, devel: true */
/*global Util, window, document */
/* [module]
* import Util from "../core/util";
*/
// Globals defined here
var WebUtil = {};
/*
* ------------------------------------------------------
* Namespaced in WebUtil
* ------------------------------------------------------
*/
// init log level reading the logging HTTP param
export function initLogging(level) {
WebUtil.init_logging = function (level) {
"use strict";
if (typeof level !== "undefined") {
mainInitLogging(level);
Util._log_level = level;
} else {
const param = document.location.href.match(/logging=([A-Za-z0-9._-]*)/);
mainInitLogging(param || undefined);
var param = document.location.href.match(/logging=([A-Za-z0-9\._\-]*)/);
Util._log_level = (param || ['', Util._log_level])[1];
}
}
Util.init_logging();
};
WebUtil.dirObj = function (obj, depth, parent) {
"use strict";
if (! depth) { depth = 2; }
if (! parent) { parent = ""; }
// Print the properties of the passed-in object
var msg = "";
for (var i in obj) {
if ((depth > 1) && (typeof obj[i] === "object")) {
// Recurse attributes that are objects
msg += WebUtil.dirObj(obj[i], depth - 1, parent + "." + i);
} else {
//val = new String(obj[i]).replace("\n", " ");
var val = "";
if (typeof(obj[i]) === "undefined") {
val = "undefined";
} else {
val = obj[i].toString().replace("\n", " ");
}
if (val.length > 30) {
val = val.substr(0, 30) + "...";
}
msg += parent + "." + i + ": " + val + "\n";
}
}
return msg;
};
// Read a query string variable
// A URL with a query parameter can look like this (But will most probably get logged on the http server):
// https://www.example.com?myqueryparam=myvalue
//
// For privacy (Using a hastag #, the parameters will not be sent to the server)
// the url can be requested in the following way:
// https://www.example.com#myqueryparam=myvalue&password=secreatvalue
//
// Even Mixing public and non public parameters will work:
// https://www.example.com?nonsecretparam=example.com#password=secreatvalue
export function getQueryVar(name, defVal) {
WebUtil.getQueryVar = function (name, defVal) {
"use strict";
const re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
match = ''.concat(document.location.href, window.location.hash).match(re);
var re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
match = document.location.href.match(re);
if (typeof defVal === 'undefined') { defVal = null; }
if (match) {
return decodeURIComponent(match[1]);
} else {
return defVal;
}
return defVal;
}
};
// Read a hash fragment variable
export function getHashVar(name, defVal) {
WebUtil.getHashVar = function (name, defVal) {
"use strict";
const re = new RegExp('.*[&#]' + name + '=([^&]*)'),
var re = new RegExp('.*[&#]' + name + '=([^&]*)'),
match = document.location.hash.match(re);
if (typeof defVal === 'undefined') { defVal = null; }
if (match) {
return decodeURIComponent(match[1]);
} else {
return defVal;
}
return defVal;
}
};
// Read a variable from the fragment or the query string
// Fragment takes precedence
export function getConfigVar(name, defVal) {
WebUtil.getConfigVar = function (name, defVal) {
"use strict";
const val = getHashVar(name);
var val = WebUtil.getHashVar(name);
if (val === null) {
return getQueryVar(name, defVal);
val = WebUtil.getQueryVar(name, defVal);
}
return val;
}
};
/*
* Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
*/
// No days means only for this browser session
export function createCookie(name, value, days) {
WebUtil.createCookie = function (name, value, days) {
"use strict";
let date, expires;
var date, expires;
if (days) {
date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
@@ -85,102 +117,195 @@ export function createCookie(name, value, days) {
expires = "";
}
let secure;
var secure;
if (document.location.protocol === "https:") {
secure = "; secure";
} else {
secure = "";
}
document.cookie = name + "=" + value + expires + "; path=/" + secure;
}
};
export function readCookie(name, defaultValue) {
WebUtil.readCookie = function (name, defaultValue) {
"use strict";
const nameEQ = name + "=";
const ca = document.cookie.split(';');
var nameEQ = name + "=",
ca = document.cookie.split(';');
for (let i = 0; i < ca.length; i += 1) {
let c = ca[i];
while (c.charAt(0) === ' ') {
c = c.substring(1, c.length);
}
if (c.indexOf(nameEQ) === 0) {
return c.substring(nameEQ.length, c.length);
}
for (var i = 0; i < ca.length; i += 1) {
var c = ca[i];
while (c.charAt(0) === ' ') { c = c.substring(1, c.length); }
if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length, c.length); }
}
return (typeof defaultValue !== 'undefined') ? defaultValue : null;
}
};
export function eraseCookie(name) {
WebUtil.eraseCookie = function (name) {
"use strict";
createCookie(name, "", -1);
}
WebUtil.createCookie(name, "", -1);
};
/*
* Setting handling.
*/
let settings = {};
export function initSettings() {
if (!window.chrome || !window.chrome.storage) {
settings = {};
return Promise.resolve();
WebUtil.initSettings = function (callback /*, ...callbackArgs */) {
"use strict";
var callbackArgs = Array.prototype.slice.call(arguments, 1);
if (window.chrome && window.chrome.storage) {
window.chrome.storage.sync.get(function (cfg) {
WebUtil.settings = cfg;
console.log(WebUtil.settings);
if (callback) {
callback.apply(this, callbackArgs);
}
});
} else {
// No-op
if (callback) {
callback.apply(this, callbackArgs);
}
}
return new Promise(resolve => window.chrome.storage.sync.get(resolve))
.then((cfg) => { settings = cfg; });
}
// Update the settings cache, but do not write to permanent storage
export function setSetting(name, value) {
settings[name] = value;
}
};
// No days means only for this browser session
export function writeSetting(name, value) {
WebUtil.writeSetting = function (name, value) {
"use strict";
if (settings[name] === value) return;
settings[name] = value;
if (window.chrome && window.chrome.storage) {
window.chrome.storage.sync.set(settings);
//console.log("writeSetting:", name, value);
if (WebUtil.settings[name] !== value) {
WebUtil.settings[name] = value;
window.chrome.storage.sync.set(WebUtil.settings);
}
} else {
localStorage.setItem(name, value);
}
}
};
export function readSetting(name, defaultValue) {
WebUtil.readSetting = function (name, defaultValue) {
"use strict";
let value;
if ((name in settings) || (window.chrome && window.chrome.storage)) {
value = settings[name];
var value;
if (window.chrome && window.chrome.storage) {
value = WebUtil.settings[name];
} else {
value = localStorage.getItem(name);
settings[name] = value;
}
if (typeof value === "undefined") {
value = null;
}
if (value === null && typeof defaultValue !== "undefined") {
if (value === null && typeof defaultValue !== undefined) {
return defaultValue;
} else {
return value;
}
};
return value;
}
export function eraseSetting(name) {
WebUtil.eraseSetting = function (name) {
"use strict";
// Deleting here means that next time the setting is read when using local
// storage, it will be pulled from local storage again.
// If the setting in local storage is changed (e.g. in another tab)
// between this delete and the next read, it could lead to an unexpected
// value change.
delete settings[name];
if (window.chrome && window.chrome.storage) {
window.chrome.storage.sync.remove(name);
delete WebUtil.settings[name];
} else {
localStorage.removeItem(name);
}
}
};
WebUtil.injectParamIfMissing = function (path, param, value) {
// force pretend that we're dealing with a relative path
// (assume that we wanted an extra if we pass one in)
path = "/" + path;
var elem = document.createElement('a');
elem.href = path;
var param_eq = encodeURIComponent(param) + "=";
var query;
if (elem.search) {
query = elem.search.slice(1).split('&');
} else {
query = [];
}
if (!query.some(function (v) { return v.startsWith(param_eq); })) {
query.push(param_eq + encodeURIComponent(value));
elem.search = "?" + query.join("&");
}
// some browsers (e.g. IE11) may occasionally omit the leading slash
// in the elem.pathname string. Handle that case gracefully.
if (elem.pathname.charAt(0) == "/") {
return elem.pathname.slice(1) + elem.search + elem.hash;
} else {
return elem.pathname + elem.search + elem.hash;
}
};
// Dynamically load scripts without using document.write()
// Reference: http://unixpapa.com/js/dyna.html
//
// Handles the case where load_scripts is invoked from a script that
// itself is loaded via load_scripts. Once all scripts are loaded the
// window.onscriptsloaded handler is called (if set).
WebUtil.get_include_uri = function (root_dir) {
return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI + root_dir + '/' : root_dir + '/';
};
WebUtil._loading_scripts = [];
WebUtil._pending_scripts = [];
WebUtil.load_scripts = function (files_by_dir) {
"use strict";
var head = document.getElementsByTagName('head')[0], script,
ls = WebUtil._loading_scripts, ps = WebUtil._pending_scripts;
var loadFunc = function (e) {
while (ls.length > 0 && (ls[0].readyState === 'loaded' ||
ls[0].readyState === 'complete')) {
// For IE, append the script to trigger execution
var s = ls.shift();
//console.log("loaded script: " + s.src);
head.appendChild(s);
}
if (!this.readyState ||
(Util.Engine.presto && this.readyState === 'loaded') ||
this.readyState === 'complete') {
if (ps.indexOf(this) >= 0) {
this.onload = this.onreadystatechange = null;
//console.log("completed script: " + this.src);
ps.splice(ps.indexOf(this), 1);
// Call window.onscriptsload after last script loads
if (ps.length === 0 && window.onscriptsload) {
window.onscriptsload();
}
}
}
};
var root_dirs = Object.keys(files_by_dir);
for (var d = 0; d < root_dirs.length; d++) {
var root_dir = root_dirs[d];
var files = files_by_dir[root_dir];
for (var f = 0; f < files.length; f++) {
script = document.createElement('script');
script.type = 'text/javascript';
script.src = WebUtil.get_include_uri(root_dir) + files[f];
//console.log("loading script: " + script.src);
script.onload = script.onreadystatechange = loadFunc;
// In-order script execution tricks
if (Util.Engine.trident) {
// For IE wait until readyState is 'loaded' before
// appending it which will trigger execution
// http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
ls.push(script);
} else {
// For webkit and firefox set async=false and append now
// https://developer.mozilla.org/en-US/docs/HTML/Element/script
script.async = false;
head.appendChild(script);
}
ps.push(script);
}
}
};
/* [module] export default WebUtil; */
+49 -38
View File
@@ -4,47 +4,51 @@
// From: http://hg.mozilla.org/mozilla-central/raw-file/ec10630b1a54/js/src/devtools/jint/sunspider/string-base64.js
import * as Log from './util/logging.js';
/*jslint white: false */
/*global console */
export default {
var Base64 = {
/* Convert data (an array of integers) to a Base64 string. */
toBase64Table: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
base64Pad: '=',
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
base64Pad : '=',
encode(data) {
encode: function (data) {
"use strict";
let result = '';
const length = data.length;
const lengthpad = (length % 3);
var result = '';
var toBase64Table = Base64.toBase64Table;
var length = data.length;
var lengthpad = (length % 3);
// Convert every three bytes to 4 ascii characters.
for (let i = 0; i < (length - 2); i += 3) {
result += this.toBase64Table[data[i] >> 2];
result += this.toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
result += this.toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
result += this.toBase64Table[data[i + 2] & 0x3f];
for (var i = 0; i < (length - 2); i += 3) {
result += toBase64Table[data[i] >> 2];
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
result += toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
result += toBase64Table[data[i + 2] & 0x3f];
}
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
const j = length - lengthpad;
var j = 0;
if (lengthpad === 2) {
result += this.toBase64Table[data[j] >> 2];
result += this.toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)];
result += this.toBase64Table[(data[j + 1] & 0x0f) << 2];
result += this.toBase64Table[64];
j = length - lengthpad;
result += toBase64Table[data[j] >> 2];
result += toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)];
result += toBase64Table[(data[j + 1] & 0x0f) << 2];
result += toBase64Table[64];
} else if (lengthpad === 1) {
result += this.toBase64Table[data[j] >> 2];
result += this.toBase64Table[(data[j] & 0x03) << 4];
result += this.toBase64Table[64];
result += this.toBase64Table[64];
j = length - lengthpad;
result += toBase64Table[data[j] >> 2];
result += toBase64Table[(data[j] & 0x03) << 4];
result += toBase64Table[64];
result += toBase64Table[64];
}
return result;
},
/* Convert Base64 data to a string */
/* eslint-disable comma-spacing */
toBinaryTable: [
/* jshint -W013 */
toBinaryTable : [
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
@@ -54,26 +58,31 @@ export default {
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
],
/* eslint-enable comma-spacing */
/* jshint +W013 */
decode(data, offset = 0) {
let dataLength = data.indexOf('=') - offset;
if (dataLength < 0) { dataLength = data.length - offset; }
decode: function (data, offset) {
"use strict";
offset = typeof(offset) !== 'undefined' ? offset : 0;
var toBinaryTable = Base64.toBinaryTable;
var base64Pad = Base64.base64Pad;
var result, result_length;
var leftbits = 0; // number of bits decoded, but yet to be appended
var leftdata = 0; // bits decoded, but yet to be appended
var data_length = data.indexOf('=') - offset;
if (data_length < 0) { data_length = data.length - offset; }
/* Every four characters is 3 resulting numbers */
const resultLength = (dataLength >> 2) * 3 + Math.floor((dataLength % 4) / 1.5);
const result = new Array(resultLength);
result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5);
result = new Array(result_length);
// Convert one by one.
let leftbits = 0; // number of bits decoded, but yet to be appended
let leftdata = 0; // bits decoded, but yet to be appended
for (let idx = 0, i = offset; i < data.length; i++) {
const c = this.toBinaryTable[data.charCodeAt(i) & 0x7f];
const padding = (data.charAt(i) === this.base64Pad);
for (var idx = 0, i = offset; i < data.length; i++) {
var c = toBinaryTable[data.charCodeAt(i) & 0x7f];
var padding = (data.charAt(i) === base64Pad);
// Skip illegal characters and whitespace
if (c === -1) {
Log.Error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
continue;
}
@@ -94,7 +103,7 @@ export default {
// If there are any bits left, the base64 string was corrupted
if (leftbits) {
const err = new Error('Corrupted base64 string');
err = new Error('Corrupted base64 string');
err.name = 'Base64-Error';
throw err;
}
@@ -102,3 +111,5 @@ export default {
return result;
}
}; /* End of Base64 namespace */
/* [module] export default Base64; */
-27
View File
@@ -1,27 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
export default class CopyRectDecoder {
decodeRect(x, y, width, height, sock, display, depth) {
if (sock.rQwait("COPYRECT", 4)) {
return false;
}
let deltaX = sock.rQshift16();
let deltaY = sock.rQshift16();
if ((width === 0) || (height === 0)) {
return true;
}
display.copyImage(deltaX, deltaY, x, y, width, height);
return true;
}
}
-191
View File
@@ -1,191 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
import * as Log from '../util/logging.js';
export default class HextileDecoder {
constructor() {
this._tiles = 0;
this._lastsubencoding = 0;
this._tileBuffer = new Uint8Array(16 * 16 * 4);
}
decodeRect(x, y, width, height, sock, display, depth) {
if (this._tiles === 0) {
this._tilesX = Math.ceil(width / 16);
this._tilesY = Math.ceil(height / 16);
this._totalTiles = this._tilesX * this._tilesY;
this._tiles = this._totalTiles;
}
while (this._tiles > 0) {
let bytes = 1;
if (sock.rQwait("HEXTILE", bytes)) {
return false;
}
let rQ = sock.rQ;
let rQi = sock.rQi;
let subencoding = rQ[rQi]; // Peek
if (subencoding > 30) { // Raw
throw new Error("Illegal hextile subencoding (subencoding: " +
subencoding + ")");
}
const currTile = this._totalTiles - this._tiles;
const tileX = currTile % this._tilesX;
const tileY = Math.floor(currTile / this._tilesX);
const tx = x + tileX * 16;
const ty = y + tileY * 16;
const tw = Math.min(16, (x + width) - tx);
const th = Math.min(16, (y + height) - ty);
// Figure out how much we are expecting
if (subencoding & 0x01) { // Raw
bytes += tw * th * 4;
} else {
if (subencoding & 0x02) { // Background
bytes += 4;
}
if (subencoding & 0x04) { // Foreground
bytes += 4;
}
if (subencoding & 0x08) { // AnySubrects
bytes++; // Since we aren't shifting it off
if (sock.rQwait("HEXTILE", bytes)) {
return false;
}
let subrects = rQ[rQi + bytes - 1]; // Peek
if (subencoding & 0x10) { // SubrectsColoured
bytes += subrects * (4 + 2);
} else {
bytes += subrects * 2;
}
}
}
if (sock.rQwait("HEXTILE", bytes)) {
return false;
}
// We know the encoding and have a whole tile
rQi++;
if (subencoding === 0) {
if (this._lastsubencoding & 0x01) {
// Weird: ignore blanks are RAW
Log.Debug(" Ignoring blank after RAW");
} else {
display.fillRect(tx, ty, tw, th, this._background);
}
} else if (subencoding & 0x01) { // Raw
let pixels = tw * th;
// Max sure the image is fully opaque
for (let i = 0;i < pixels;i++) {
rQ[rQi + i * 4 + 3] = 255;
}
display.blitImage(tx, ty, tw, th, rQ, rQi);
rQi += bytes - 1;
} else {
if (subencoding & 0x02) { // Background
this._background = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
rQi += 4;
}
if (subencoding & 0x04) { // Foreground
this._foreground = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
rQi += 4;
}
this._startTile(tx, ty, tw, th, this._background);
if (subencoding & 0x08) { // AnySubrects
let subrects = rQ[rQi];
rQi++;
for (let s = 0; s < subrects; s++) {
let color;
if (subencoding & 0x10) { // SubrectsColoured
color = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
rQi += 4;
} else {
color = this._foreground;
}
const xy = rQ[rQi];
rQi++;
const sx = (xy >> 4);
const sy = (xy & 0x0f);
const wh = rQ[rQi];
rQi++;
const sw = (wh >> 4) + 1;
const sh = (wh & 0x0f) + 1;
this._subTile(sx, sy, sw, sh, color);
}
}
this._finishTile(display);
}
sock.rQi = rQi;
this._lastsubencoding = subencoding;
this._tiles--;
}
return true;
}
// start updating a tile
_startTile(x, y, width, height, color) {
this._tileX = x;
this._tileY = y;
this._tileW = width;
this._tileH = height;
const red = color[0];
const green = color[1];
const blue = color[2];
const data = this._tileBuffer;
for (let i = 0; i < width * height * 4; i += 4) {
data[i] = red;
data[i + 1] = green;
data[i + 2] = blue;
data[i + 3] = 255;
}
}
// update sub-rectangle of the current tile
_subTile(x, y, w, h, color) {
const red = color[0];
const green = color[1];
const blue = color[2];
const xend = x + w;
const yend = y + h;
const data = this._tileBuffer;
const width = this._tileW;
for (let j = y; j < yend; j++) {
for (let i = x; i < xend; i++) {
const p = (i + (j * width)) * 4;
data[p] = red;
data[p + 1] = green;
data[p + 2] = blue;
data[p + 3] = 255;
}
}
}
// draw the current tile to the screen
_finishTile(display) {
display.blitImage(this._tileX, this._tileY,
this._tileW, this._tileH,
this._tileBuffer, 0);
}
}
-66
View File
@@ -1,66 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
export default class RawDecoder {
constructor() {
this._lines = 0;
}
decodeRect(x, y, width, height, sock, display, depth) {
if ((width === 0) || (height === 0)) {
return true;
}
if (this._lines === 0) {
this._lines = height;
}
const pixelSize = depth == 8 ? 1 : 4;
const bytesPerLine = width * pixelSize;
if (sock.rQwait("RAW", bytesPerLine)) {
return false;
}
const curY = y + (height - this._lines);
const currHeight = Math.min(this._lines,
Math.floor(sock.rQlen / bytesPerLine));
const pixels = width * currHeight;
let data = sock.rQ;
let index = sock.rQi;
// Convert data if needed
if (depth == 8) {
const newdata = new Uint8Array(pixels * 4);
for (let i = 0; i < pixels; i++) {
newdata[i * 4 + 0] = ((data[index + i] >> 0) & 0x3) * 255 / 3;
newdata[i * 4 + 1] = ((data[index + i] >> 2) & 0x3) * 255 / 3;
newdata[i * 4 + 2] = ((data[index + i] >> 4) & 0x3) * 255 / 3;
newdata[i * 4 + 3] = 255;
}
data = newdata;
index = 0;
}
// Max sure the image is fully opaque
for (let i = 0; i < pixels; i++) {
data[i * 4 + 3] = 255;
}
display.blitImage(x, curY, width, currHeight, data, index);
sock.rQskipBytes(currHeight * bytesPerLine);
this._lines -= currHeight;
if (this._lines > 0) {
return false;
}
return true;
}
}
-44
View File
@@ -1,44 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
export default class RREDecoder {
constructor() {
this._subrects = 0;
}
decodeRect(x, y, width, height, sock, display, depth) {
if (this._subrects === 0) {
if (sock.rQwait("RRE", 4 + 4)) {
return false;
}
this._subrects = sock.rQshift32();
let color = sock.rQshiftBytes(4); // Background
display.fillRect(x, y, width, height, color);
}
while (this._subrects > 0) {
if (sock.rQwait("RRE", 4 + 8)) {
return false;
}
let color = sock.rQshiftBytes(4);
let sx = sock.rQshift16();
let sy = sock.rQshift16();
let swidth = sock.rQshift16();
let sheight = sock.rQshift16();
display.fillRect(x + sx, y + sy, swidth, sheight, color);
this._subrects--;
}
return true;
}
}
-331
View File
@@ -1,331 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
import * as Log from '../util/logging.js';
import Inflator from "../inflator.js";
export default class TightDecoder {
constructor() {
this._ctl = null;
this._filter = null;
this._numColors = 0;
this._palette = new Uint8Array(1024); // 256 * 4 (max palette size * max bytes-per-pixel)
this._len = 0;
this._zlibs = [];
for (let i = 0; i < 4; i++) {
this._zlibs[i] = new Inflator();
}
}
decodeRect(x, y, width, height, sock, display, depth) {
if (this._ctl === null) {
if (sock.rQwait("TIGHT compression-control", 1)) {
return false;
}
this._ctl = sock.rQshift8();
// Reset streams if the server requests it
for (let i = 0; i < 4; i++) {
if ((this._ctl >> i) & 1) {
this._zlibs[i].reset();
Log.Info("Reset zlib stream " + i);
}
}
// Figure out filter
this._ctl = this._ctl >> 4;
}
let ret;
if (this._ctl === 0x08) {
ret = this._fillRect(x, y, width, height,
sock, display, depth);
} else if (this._ctl === 0x09) {
ret = this._jpegRect(x, y, width, height,
sock, display, depth);
} else if (this._ctl === 0x0A) {
ret = this._pngRect(x, y, width, height,
sock, display, depth);
} else if ((this._ctl & 0x08) == 0) {
ret = this._basicRect(this._ctl, x, y, width, height,
sock, display, depth);
} else {
throw new Error("Illegal tight compression received (ctl: " +
this._ctl + ")");
}
if (ret) {
this._ctl = null;
}
return ret;
}
_fillRect(x, y, width, height, sock, display, depth) {
if (sock.rQwait("TIGHT", 3)) {
return false;
}
const rQi = sock.rQi;
const rQ = sock.rQ;
display.fillRect(x, y, width, height,
[rQ[rQi], rQ[rQi + 1], rQ[rQi + 2]], false);
sock.rQskipBytes(3);
return true;
}
_jpegRect(x, y, width, height, sock, display, depth) {
let data = this._readData(sock);
if (data === null) {
return false;
}
display.imageRect(x, y, width, height, "image/jpeg", data);
return true;
}
_pngRect(x, y, width, height, sock, display, depth) {
throw new Error("PNG received in standard Tight rect");
}
_basicRect(ctl, x, y, width, height, sock, display, depth) {
if (this._filter === null) {
if (ctl & 0x4) {
if (sock.rQwait("TIGHT", 1)) {
return false;
}
this._filter = sock.rQshift8();
} else {
// Implicit CopyFilter
this._filter = 0;
}
}
let streamId = ctl & 0x3;
let ret;
switch (this._filter) {
case 0: // CopyFilter
ret = this._copyFilter(streamId, x, y, width, height,
sock, display, depth);
break;
case 1: // PaletteFilter
ret = this._paletteFilter(streamId, x, y, width, height,
sock, display, depth);
break;
case 2: // GradientFilter
ret = this._gradientFilter(streamId, x, y, width, height,
sock, display, depth);
break;
default:
throw new Error("Illegal tight filter received (ctl: " +
this._filter + ")");
}
if (ret) {
this._filter = null;
}
return ret;
}
_copyFilter(streamId, x, y, width, height, sock, display, depth) {
const uncompressedSize = width * height * 3;
let data;
if (uncompressedSize === 0) {
return true;
}
if (uncompressedSize < 12) {
if (sock.rQwait("TIGHT", uncompressedSize)) {
return false;
}
data = sock.rQshiftBytes(uncompressedSize);
} else {
data = this._readData(sock);
if (data === null) {
return false;
}
this._zlibs[streamId].setInput(data);
data = this._zlibs[streamId].inflate(uncompressedSize);
this._zlibs[streamId].setInput(null);
}
let rgbx = new Uint8Array(width * height * 4);
for (let i = 0, j = 0; i < width * height * 4; i += 4, j += 3) {
rgbx[i] = data[j];
rgbx[i + 1] = data[j + 1];
rgbx[i + 2] = data[j + 2];
rgbx[i + 3] = 255; // Alpha
}
display.blitImage(x, y, width, height, rgbx, 0, false);
return true;
}
_paletteFilter(streamId, x, y, width, height, sock, display, depth) {
if (this._numColors === 0) {
if (sock.rQwait("TIGHT palette", 1)) {
return false;
}
const numColors = sock.rQpeek8() + 1;
const paletteSize = numColors * 3;
if (sock.rQwait("TIGHT palette", 1 + paletteSize)) {
return false;
}
this._numColors = numColors;
sock.rQskipBytes(1);
sock.rQshiftTo(this._palette, paletteSize);
}
const bpp = (this._numColors <= 2) ? 1 : 8;
const rowSize = Math.floor((width * bpp + 7) / 8);
const uncompressedSize = rowSize * height;
let data;
if (uncompressedSize === 0) {
return true;
}
if (uncompressedSize < 12) {
if (sock.rQwait("TIGHT", uncompressedSize)) {
return false;
}
data = sock.rQshiftBytes(uncompressedSize);
} else {
data = this._readData(sock);
if (data === null) {
return false;
}
this._zlibs[streamId].setInput(data);
data = this._zlibs[streamId].inflate(uncompressedSize);
this._zlibs[streamId].setInput(null);
}
// Convert indexed (palette based) image data to RGB
if (this._numColors == 2) {
this._monoRect(x, y, width, height, data, this._palette, display);
} else {
this._paletteRect(x, y, width, height, data, this._palette, display);
}
this._numColors = 0;
return true;
}
_monoRect(x, y, width, height, data, palette, display) {
// Convert indexed (palette based) image data to RGB
// TODO: reduce number of calculations inside loop
const dest = this._getScratchBuffer(width * height * 4);
const w = Math.floor((width + 7) / 8);
const w1 = Math.floor(width / 8);
for (let y = 0; y < height; y++) {
let dp, sp, x;
for (x = 0; x < w1; x++) {
for (let b = 7; b >= 0; b--) {
dp = (y * width + x * 8 + 7 - b) * 4;
sp = (data[y * w + x] >> b & 1) * 3;
dest[dp] = palette[sp];
dest[dp + 1] = palette[sp + 1];
dest[dp + 2] = palette[sp + 2];
dest[dp + 3] = 255;
}
}
for (let b = 7; b >= 8 - width % 8; b--) {
dp = (y * width + x * 8 + 7 - b) * 4;
sp = (data[y * w + x] >> b & 1) * 3;
dest[dp] = palette[sp];
dest[dp + 1] = palette[sp + 1];
dest[dp + 2] = palette[sp + 2];
dest[dp + 3] = 255;
}
}
display.blitImage(x, y, width, height, dest, 0, false);
}
_paletteRect(x, y, width, height, data, palette, display) {
// Convert indexed (palette based) image data to RGB
const dest = this._getScratchBuffer(width * height * 4);
const total = width * height * 4;
for (let i = 0, j = 0; i < total; i += 4, j++) {
const sp = data[j] * 3;
dest[i] = palette[sp];
dest[i + 1] = palette[sp + 1];
dest[i + 2] = palette[sp + 2];
dest[i + 3] = 255;
}
display.blitImage(x, y, width, height, dest, 0, false);
}
_gradientFilter(streamId, x, y, width, height, sock, display, depth) {
throw new Error("Gradient filter not implemented");
}
_readData(sock) {
if (this._len === 0) {
if (sock.rQwait("TIGHT", 3)) {
return null;
}
let byte;
byte = sock.rQshift8();
this._len = byte & 0x7f;
if (byte & 0x80) {
byte = sock.rQshift8();
this._len |= (byte & 0x7f) << 7;
if (byte & 0x80) {
byte = sock.rQshift8();
this._len |= byte << 14;
}
}
}
if (sock.rQwait("TIGHT", this._len)) {
return null;
}
let data = sock.rQshiftBytes(this._len);
this._len = 0;
return data;
}
_getScratchBuffer(size) {
if (!this._scratchBuffer || (this._scratchBuffer.length < size)) {
this._scratchBuffer = new Uint8Array(size);
}
return this._scratchBuffer;
}
}
-27
View File
@@ -1,27 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
import TightDecoder from './tight.js';
export default class TightPNGDecoder extends TightDecoder {
_pngRect(x, y, width, height, sock, display, depth) {
let data = this._readData(sock);
if (data === null) {
return false;
}
display.imageRect(x, y, width, height, "image/png", data);
return true;
}
_basicRect(ctl, x, y, width, height, sock, display, depth) {
throw new Error("BasicCompression received in TightPNG rect");
}
}
-85
View File
@@ -1,85 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2020 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
import { deflateInit, deflate } from "../vendor/pako/lib/zlib/deflate.js";
import { Z_FULL_FLUSH } from "../vendor/pako/lib/zlib/deflate.js";
import ZStream from "../vendor/pako/lib/zlib/zstream.js";
export default class Deflator {
constructor() {
this.strm = new ZStream();
this.chunkSize = 1024 * 10 * 10;
this.outputBuffer = new Uint8Array(this.chunkSize);
this.windowBits = 5;
deflateInit(this.strm, this.windowBits);
}
deflate(inData) {
/* eslint-disable camelcase */
this.strm.input = inData;
this.strm.avail_in = this.strm.input.length;
this.strm.next_in = 0;
this.strm.output = this.outputBuffer;
this.strm.avail_out = this.chunkSize;
this.strm.next_out = 0;
/* eslint-enable camelcase */
let lastRet = deflate(this.strm, Z_FULL_FLUSH);
let outData = new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
if (lastRet < 0) {
throw new Error("zlib deflate failed");
}
if (this.strm.avail_in > 0) {
// Read chunks until done
let chunks = [outData];
let totalLen = outData.length;
do {
/* eslint-disable camelcase */
this.strm.output = new Uint8Array(this.chunkSize);
this.strm.next_out = 0;
this.strm.avail_out = this.chunkSize;
/* eslint-enable camelcase */
lastRet = deflate(this.strm, Z_FULL_FLUSH);
if (lastRet < 0) {
throw new Error("zlib deflate failed");
}
let chunk = new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
totalLen += chunk.length;
chunks.push(chunk);
} while (this.strm.avail_in > 0);
// Combine chunks into a single data
let newData = new Uint8Array(totalLen);
let offset = 0;
for (let i = 0; i < chunks.length; i++) {
newData.set(chunks[i], offset);
offset += chunks[i].length;
}
outData = newData;
}
/* eslint-disable camelcase */
this.strm.input = null;
this.strm.avail_in = 0;
this.strm.next_in = 0;
/* eslint-enable camelcase */
return outData;
}
}
+99 -89
View File
@@ -75,83 +75,89 @@
* fine Java utilities: http://www.acme.com/java/
*/
/* eslint-disable comma-spacing */
/* jslint white: false */
// Tables, permutations, S-boxes, etc.
const PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,
25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,
50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],
totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28];
/* [module] export default */ function DES(passwd) {
"use strict";
const z = 0x0;
let a,b,c,d,e,f;
a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e;
const SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d,
z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z,
a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f,
c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d];
a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e;
const SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d,
a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f,
z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z,
z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e];
a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e;
const SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f,
b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z,
c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d,
b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e];
a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e;
const SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d,
z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f,
b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e,
c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e];
a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e;
const SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z,
a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f,
z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e,
c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d];
a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e;
const SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f,
z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z,
b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z,
a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f];
a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e;
const SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f,
b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e,
b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e,
z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d];
a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e;
const SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d,
c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z,
a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f,
z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e];
// Tables, permutations, S-boxes, etc.
// jshint -W013
var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,
25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,
50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],
totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28],
z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8,
keys = [];
/* eslint-enable comma-spacing */
// jshint -W015
a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e;
SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d,
z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z,
a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f,
c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d];
a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e;
SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d,
a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f,
z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z,
z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e];
a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e;
SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f,
b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z,
c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d,
b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e];
a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e;
SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d,
z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f,
b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e,
c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e];
a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e;
SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z,
a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f,
z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e,
c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d];
a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e;
SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f,
z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z,
b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z,
a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f];
a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e;
SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f,
b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e,
b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e,
z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d];
a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e;
SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d,
c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z,
a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f,
z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e];
// jshint +W013,+W015
export default class DES {
constructor(password) {
this.keys = [];
// Set the key.
function setKeys(keyBlock) {
var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [],
raw0, raw1, rawi, KnLi;
// Set the key.
const pc1m = [], pcr = [], kn = [];
for (let j = 0, l = 56; j < 56; ++j, l -= 8) {
for (j = 0, l = 56; j < 56; ++j, l -= 8) {
l += l < -5 ? 65 : l < -3 ? 31 : l < -1 ? 63 : l === 27 ? 35 : 0; // PC1
const m = l & 0x7;
pc1m[j] = ((password[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
m = l & 0x7;
pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
}
for (let i = 0; i < 16; ++i) {
const m = i << 1;
const n = m + 1;
for (i = 0; i < 16; ++i) {
m = i << 1;
n = m + 1;
kn[m] = kn[n] = 0;
for (let o = 28; o < 59; o += 28) {
for (let j = o - 28; j < o; ++j) {
const l = j + totrot[i];
pcr[j] = l < o ? pc1m[l] : pc1m[l - 28];
for (o = 28; o < 59; o += 28) {
for (j = o - 28; j < o; ++j) {
l = j + totrot[i];
if (l < o) {
pcr[j] = pc1m[l];
} else {
pcr[j] = pc1m[l - 28];
}
}
}
for (let j = 0; j < 24; ++j) {
for (j = 0; j < 24; ++j) {
if (pcr[PC2[j]] !== 0) {
kn[m] |= 1 << (23 - j);
}
@@ -162,26 +168,26 @@ export default class DES {
}
// cookey
for (let i = 0, rawi = 0, KnLi = 0; i < 16; ++i) {
const raw0 = kn[rawi++];
const raw1 = kn[rawi++];
this.keys[KnLi] = (raw0 & 0x00fc0000) << 6;
this.keys[KnLi] |= (raw0 & 0x00000fc0) << 10;
this.keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
this.keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6;
for (i = 0, rawi = 0, KnLi = 0; i < 16; ++i) {
raw0 = kn[rawi++];
raw1 = kn[rawi++];
keys[KnLi] = (raw0 & 0x00fc0000) << 6;
keys[KnLi] |= (raw0 & 0x00000fc0) << 10;
keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6;
++KnLi;
this.keys[KnLi] = (raw0 & 0x0003f000) << 12;
this.keys[KnLi] |= (raw0 & 0x0000003f) << 16;
this.keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;
this.keys[KnLi] |= (raw1 & 0x0000003f);
keys[KnLi] = (raw0 & 0x0003f000) << 12;
keys[KnLi] |= (raw0 & 0x0000003f) << 16;
keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;
keys[KnLi] |= (raw1 & 0x0000003f);
++KnLi;
}
}
// Encrypt 8 bytes of text
enc8(text) {
const b = text.slice();
let i = 0, l, r, x; // left, right, accumulator
function enc8(text) {
var i = 0, b = text.slice(), fval, keysi = 0,
l, r, x; // left, right, accumulator
// Squash 8 bytes to 2 ints
l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
@@ -205,26 +211,26 @@ export default class DES {
r ^= x;
l = (l << 1) | ((l >>> 31) & 1);
for (let i = 0, keysi = 0; i < 8; ++i) {
for (i = 0; i < 8; ++i) {
x = (r << 28) | (r >>> 4);
x ^= this.keys[keysi++];
let fval = SP7[x & 0x3f];
x ^= keys[keysi++];
fval = SP7[x & 0x3f];
fval |= SP5[(x >>> 8) & 0x3f];
fval |= SP3[(x >>> 16) & 0x3f];
fval |= SP1[(x >>> 24) & 0x3f];
x = r ^ this.keys[keysi++];
x = r ^ keys[keysi++];
fval |= SP8[x & 0x3f];
fval |= SP6[(x >>> 8) & 0x3f];
fval |= SP4[(x >>> 16) & 0x3f];
fval |= SP2[(x >>> 24) & 0x3f];
l ^= fval;
x = (l << 28) | (l >>> 4);
x ^= this.keys[keysi++];
x ^= keys[keysi++];
fval = SP7[x & 0x3f];
fval |= SP5[(x >>> 8) & 0x3f];
fval |= SP3[(x >>> 16) & 0x3f];
fval |= SP1[(x >>> 24) & 0x3f];
x = l ^ this.keys[keysi++];
x = l ^ keys[keysi++];
fval |= SP8[x & 0x0000003f];
fval |= SP6[(x >>> 8) & 0x3f];
fval |= SP4[(x >>> 16) & 0x3f];
@@ -260,7 +266,11 @@ export default class DES {
}
// Encrypt 16 bytes of text using passwd as key
encrypt(t) {
return this.enc8(t.slice(0, 8)).concat(this.enc8(t.slice(8, 16)));
function encrypt(t) {
return enc8(t.slice(0, 8)).concat(enc8(t.slice(8, 16)));
}
}
setKeys(passwd); // Setup keys
return {'encrypt': encrypt}; // Public interface
}; // function DES
+780 -421
View File
File diff suppressed because it is too large Load Diff
-44
View File
@@ -1,44 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
export const encodings = {
encodingRaw: 0,
encodingCopyRect: 1,
encodingRRE: 2,
encodingHextile: 5,
encodingTight: 7,
encodingTightPNG: -260,
pseudoEncodingQualityLevel9: -23,
pseudoEncodingQualityLevel0: -32,
pseudoEncodingDesktopSize: -223,
pseudoEncodingLastRect: -224,
pseudoEncodingCursor: -239,
pseudoEncodingQEMUExtendedKeyEvent: -258,
pseudoEncodingDesktopName: -307,
pseudoEncodingExtendedDesktopSize: -308,
pseudoEncodingXvp: -309,
pseudoEncodingFence: -312,
pseudoEncodingContinuousUpdates: -313,
pseudoEncodingCompressLevel9: -247,
pseudoEncodingCompressLevel0: -256,
pseudoEncodingVMwareCursor: 0x574d5664,
pseudoEncodingExtendedClipboard: 0xc0a1e5ce
};
export function encodingName(num) {
switch (num) {
case encodings.encodingRaw: return "Raw";
case encodings.encodingCopyRect: return "CopyRect";
case encodings.encodingRRE: return "RRE";
case encodings.encodingHextile: return "Hextile";
case encodings.encodingTight: return "Tight";
case encodings.encodingTightPNG: return "TightPNG";
default: return "[unknown encoding " + num + "]";
}
}
+2434 -47
View File
File diff suppressed because it is too large Load Diff
+40
View File
@@ -0,0 +1,40 @@
var zlib = require('pako/lib/zlib/inflate.js');
var ZStream = require('pako/lib/zlib/zstream.js');
function Inflate() {
this.strm = new ZStream();
this.chunkSize = 1024 * 10 * 10;
this.strm.output = new Uint8Array(this.chunkSize);
this.windowBits = 5;
zlib.inflateInit(this.strm, this.windowBits);
};
Inflate.prototype = {
inflate: function (data, flush, expected) {
this.strm.input = data;
this.strm.avail_in = this.strm.input.length;
this.strm.next_in = 0;
this.strm.next_out = 0;
// resize our output buffer if it's too small
// (we could just use multiple chunks, but that would cause an extra
// allocation each time to flatten the chunks)
if (expected > this.chunkSize) {
this.chunkSize = expected;
this.strm.output = new Uint8Array(this.chunkSize);
}
this.strm.avail_out = this.chunkSize;
zlib.inflate(this.strm, flush);
return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
},
reset: function () {
zlib.inflateReset(this.strm);
}
};
module.exports = { Inflate: Inflate };
+403
View File
@@ -0,0 +1,403 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2013 Samuel Mannehed for Cendio AB
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/
/*jslint browser: true, white: false */
/*global window, Util */
/* [module]
* import Util from "../util";
* import KeyboardUtil from "./util";
*/
/* [module] export */ var Keyboard;
(function () {
"use strict";
//
// Keyboard event handler
//
Keyboard = function (defaults) {
this._keyDownList = []; // List of depressed keys
// (even if they are happy)
Util.set_defaults(this, defaults, {
'target': document,
'focused': true
});
// create the keyboard handler
this._handler = new KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(),
KeyboardUtil.VerifyCharModifier( /* jshint newcap: false */
KeyboardUtil.TrackKeyState(
KeyboardUtil.EscapeModifiers(this._handleRfbEvent.bind(this))
)
)
); /* jshint newcap: true */
// keep these here so we can refer to them later
this._eventHandlers = {
'keyup': this._handleKeyUp.bind(this),
'keydown': this._handleKeyDown.bind(this),
'keypress': this._handleKeyPress.bind(this),
'blur': this._allKeysUp.bind(this)
};
};
Keyboard.prototype = {
// private methods
_handleRfbEvent: function (e) {
if (this._onKeyPress) {
Util.Debug("onKeyPress " + (e.type == 'keydown' ? "down" : "up") +
", keysym: " + e.keysym.keysym + "(" + e.keysym.keyname + ")");
this._onKeyPress(e);
}
},
setQEMUVNCKeyboardHandler: function () {
this._handler = new KeyboardUtil.QEMUKeyEventDecoder(KeyboardUtil.ModifierSync(),
KeyboardUtil.TrackQEMUKeyState(
this._handleRfbEvent.bind(this)
)
);
},
_handleKeyDown: function (e) {
if (!this._focused) { return; }
if (this._handler.keydown(e)) {
// Suppress bubbling/default actions
Util.stopEvent(e);
} else {
// Allow the event to bubble and become a keyPress event which
// will have the character code translated
}
},
_handleKeyPress: function (e) {
if (!this._focused) { return; }
if (this._handler.keypress(e)) {
// Suppress bubbling/default actions
Util.stopEvent(e);
}
},
_handleKeyUp: function (e) {
if (!this._focused) { return; }
if (this._handler.keyup(e)) {
// Suppress bubbling/default actions
Util.stopEvent(e);
}
},
_allKeysUp: function () {
Util.Debug(">> Keyboard.allKeysUp");
this._handler.releaseAll();
Util.Debug("<< Keyboard.allKeysUp");
},
// Public methods
grab: function () {
//Util.Debug(">> Keyboard.grab");
var c = this._target;
c.addEventListener('keydown', this._eventHandlers.keydown);
c.addEventListener('keyup', this._eventHandlers.keyup);
c.addEventListener('keypress', this._eventHandlers.keypress);
// Release (key up) if window loses focus
window.addEventListener('blur', this._eventHandlers.blur);
//Util.Debug("<< Keyboard.grab");
},
ungrab: function () {
//Util.Debug(">> Keyboard.ungrab");
var c = this._target;
c.removeEventListener('keydown', this._eventHandlers.keydown);
c.removeEventListener('keyup', this._eventHandlers.keyup);
c.removeEventListener('keypress', this._eventHandlers.keypress);
window.removeEventListener('blur', this._eventHandlers.blur);
// Release (key up) all keys that are in a down state
this._allKeysUp();
//Util.Debug(">> Keyboard.ungrab");
},
sync: function (e) {
this._handler.syncModifiers(e);
}
};
Util.make_properties(Keyboard, [
['target', 'wo', 'dom'], // DOM element that captures keyboard input
['focused', 'rw', 'bool'], // Capture and send key events
['onKeyPress', 'rw', 'func'] // Handler for key press/release
]);
})();
/* [module] export */ var Mouse;
(function () {
Mouse = function (defaults) {
this._mouseCaptured = false;
this._doubleClickTimer = null;
this._lastTouchPos = null;
// Configuration attributes
Util.set_defaults(this, defaults, {
'target': document,
'focused': true,
'touchButton': 1
});
this._eventHandlers = {
'mousedown': this._handleMouseDown.bind(this),
'mouseup': this._handleMouseUp.bind(this),
'mousemove': this._handleMouseMove.bind(this),
'mousewheel': this._handleMouseWheel.bind(this),
'mousedisable': this._handleMouseDisable.bind(this)
};
};
Mouse.prototype = {
// private methods
_captureMouse: function () {
// capturing the mouse ensures we get the mouseup event
Util.setCapture(this._target);
// some browsers give us mouseup events regardless,
// so if we never captured the mouse, we can disregard the event
this._mouseCaptured = true;
},
_releaseMouse: function () {
Util.releaseCapture();
this._mouseCaptured = false;
},
_resetDoubleClickTimer: function () {
this._doubleClickTimer = null;
},
_handleMouseButton: function (e, down) {
if (!this._focused) { return; }
if (this._notify) {
this._notify(e);
}
var pos = this._getMousePosition(e);
var bmask;
if (e.touches || e.changedTouches) {
// Touch device
// When two touches occur within 500 ms of each other and are
// close enough together a double click is triggered.
if (down == 1) {
if (this._doubleClickTimer === null) {
this._lastTouchPos = pos;
} else {
clearTimeout(this._doubleClickTimer);
// When the distance between the two touches is small enough
// force the position of the latter touch to the position of
// the first.
var xs = this._lastTouchPos.x - pos.x;
var ys = this._lastTouchPos.y - pos.y;
var d = Math.sqrt((xs * xs) + (ys * ys));
// The goal is to trigger on a certain physical width, the
// devicePixelRatio brings us a bit closer but is not optimal.
var threshold = 20 * (window.devicePixelRatio || 1);
if (d < threshold) {
pos = this._lastTouchPos;
}
}
this._doubleClickTimer = setTimeout(this._resetDoubleClickTimer.bind(this), 500);
}
bmask = this._touchButton;
// If bmask is set
} else if (e.which) {
/* everything except IE */
bmask = 1 << e.button;
} else {
/* IE including 9 */
bmask = (e.button & 0x1) + // Left
(e.button & 0x2) * 2 + // Right
(e.button & 0x4) / 2; // Middle
}
if (this._onMouseButton) {
Util.Debug("onMouseButton " + (down ? "down" : "up") +
", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
this._onMouseButton(pos.x, pos.y, down, bmask);
}
Util.stopEvent(e);
},
_handleMouseDown: function (e) {
this._captureMouse();
this._handleMouseButton(e, 1);
},
_handleMouseUp: function (e) {
if (!this._mouseCaptured) { return; }
this._handleMouseButton(e, 0);
this._releaseMouse();
},
_handleMouseWheel: function (e) {
if (!this._focused) { return; }
if (this._notify) {
this._notify(e);
}
var pos = this._getMousePosition(e);
if (this._onMouseButton) {
if (e.deltaX < 0) {
this._onMouseButton(pos.x, pos.y, 1, 1 << 5);
this._onMouseButton(pos.x, pos.y, 0, 1 << 5);
} else if (e.deltaX > 0) {
this._onMouseButton(pos.x, pos.y, 1, 1 << 6);
this._onMouseButton(pos.x, pos.y, 0, 1 << 6);
}
if (e.deltaY < 0) {
this._onMouseButton(pos.x, pos.y, 1, 1 << 3);
this._onMouseButton(pos.x, pos.y, 0, 1 << 3);
} else if (e.deltaY > 0) {
this._onMouseButton(pos.x, pos.y, 1, 1 << 4);
this._onMouseButton(pos.x, pos.y, 0, 1 << 4);
}
}
Util.stopEvent(e);
},
_handleMouseMove: function (e) {
if (! this._focused) { return; }
if (this._notify) {
this._notify(e);
}
var pos = this._getMousePosition(e);
if (this._onMouseMove) {
this._onMouseMove(pos.x, pos.y);
}
Util.stopEvent(e);
},
_handleMouseDisable: function (e) {
if (!this._focused) { return; }
/*
* Stop propagation if inside canvas area
* Note: This is only needed for the 'click' event as it fails
* to fire properly for the target element so we have
* to listen on the document element instead.
*/
if (e.target == this._target) {
//Util.Debug("mouse event disabled");
Util.stopEvent(e);
}
},
// Return coordinates relative to target
_getMousePosition: function(e) {
e = Util.getPointerEvent(e);
var bounds = this._target.getBoundingClientRect();
var x, y;
// Clip to target bounds
if (e.clientX < bounds.left) {
x = 0;
} else if (e.clientX >= bounds.right) {
x = bounds.width - 1;
} else {
x = e.clientX - bounds.left;
}
if (e.clientY < bounds.top) {
y = 0;
} else if (e.clientY >= bounds.bottom) {
y = bounds.height - 1;
} else {
y = e.clientY - bounds.top;
}
return {x:x, y:y};
},
// Public methods
grab: function () {
var c = this._target;
if (Util.isTouchDevice) {
c.addEventListener('touchstart', this._eventHandlers.mousedown);
window.addEventListener('touchend', this._eventHandlers.mouseup);
c.addEventListener('touchend', this._eventHandlers.mouseup);
c.addEventListener('touchmove', this._eventHandlers.mousemove);
}
c.addEventListener('mousedown', this._eventHandlers.mousedown);
window.addEventListener('mouseup', this._eventHandlers.mouseup);
c.addEventListener('mouseup', this._eventHandlers.mouseup);
c.addEventListener('mousemove', this._eventHandlers.mousemove);
c.addEventListener('wheel', this._eventHandlers.mousewheel);
/* Prevent middle-click pasting (see above for why we bind to document) */
document.addEventListener('click', this._eventHandlers.mousedisable);
/* preventDefault() on mousedown doesn't stop this event for some
reason so we have to explicitly block it */
c.addEventListener('contextmenu', this._eventHandlers.mousedisable);
},
ungrab: function () {
var c = this._target;
if (Util.isTouchDevice) {
c.removeEventListener('touchstart', this._eventHandlers.mousedown);
window.removeEventListener('touchend', this._eventHandlers.mouseup);
c.removeEventListener('touchend', this._eventHandlers.mouseup);
c.removeEventListener('touchmove', this._eventHandlers.mousemove);
}
c.removeEventListener('mousedown', this._eventHandlers.mousedown);
window.removeEventListener('mouseup', this._eventHandlers.mouseup);
c.removeEventListener('mouseup', this._eventHandlers.mouseup);
c.removeEventListener('mousemove', this._eventHandlers.mousemove);
c.removeEventListener('wheel', this._eventHandlers.mousewheel);
document.removeEventListener('click', this._eventHandlers.mousedisable);
c.removeEventListener('contextmenu', this._eventHandlers.mousedisable);
}
};
Util.make_properties(Mouse, [
['target', 'ro', 'dom'], // DOM element that captures mouse input
['notify', 'ro', 'func'], // Function to call to notify whenever a mouse event is received
['focused', 'rw', 'bool'], // Capture and send mouse clicks/movement
['onMouseButton', 'rw', 'func'], // Handler for mouse button click/release
['onMouseMove', 'rw', 'func'], // Handler for mouse movement
['touchButton', 'rw', 'int'] // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
]);
})();
-311
View File
@@ -1,311 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/
import KeyTable from "./keysym.js";
/*
* Mapping between HTML key values and VNC/X11 keysyms for "special"
* keys that cannot be handled via their Unicode codepoint.
*
* See https://www.w3.org/TR/uievents-key/ for possible values.
*/
const DOMKeyTable = {};
function addStandard(key, standard) {
if (standard === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
if (key in DOMKeyTable) throw new Error("Duplicate entry for key \"" + key + "\"");
DOMKeyTable[key] = [standard, standard, standard, standard];
}
function addLeftRight(key, left, right) {
if (left === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
if (right === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
if (key in DOMKeyTable) throw new Error("Duplicate entry for key \"" + key + "\"");
DOMKeyTable[key] = [left, left, right, left];
}
function addNumpad(key, standard, numpad) {
if (standard === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
if (numpad === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
if (key in DOMKeyTable) throw new Error("Duplicate entry for key \"" + key + "\"");
DOMKeyTable[key] = [standard, standard, standard, numpad];
}
// 3.2. Modifier Keys
addLeftRight("Alt", KeyTable.XK_Alt_L, KeyTable.XK_Alt_R);
addStandard("AltGraph", KeyTable.XK_ISO_Level3_Shift);
addStandard("CapsLock", KeyTable.XK_Caps_Lock);
addLeftRight("Control", KeyTable.XK_Control_L, KeyTable.XK_Control_R);
// - Fn
// - FnLock
addLeftRight("Meta", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
addStandard("NumLock", KeyTable.XK_Num_Lock);
addStandard("ScrollLock", KeyTable.XK_Scroll_Lock);
addLeftRight("Shift", KeyTable.XK_Shift_L, KeyTable.XK_Shift_R);
// - Symbol
// - SymbolLock
// - Hyper
// - Super
// 3.3. Whitespace Keys
addNumpad("Enter", KeyTable.XK_Return, KeyTable.XK_KP_Enter);
addStandard("Tab", KeyTable.XK_Tab);
addNumpad(" ", KeyTable.XK_space, KeyTable.XK_KP_Space);
// 3.4. Navigation Keys
addNumpad("ArrowDown", KeyTable.XK_Down, KeyTable.XK_KP_Down);
addNumpad("ArrowLeft", KeyTable.XK_Left, KeyTable.XK_KP_Left);
addNumpad("ArrowRight", KeyTable.XK_Right, KeyTable.XK_KP_Right);
addNumpad("ArrowUp", KeyTable.XK_Up, KeyTable.XK_KP_Up);
addNumpad("End", KeyTable.XK_End, KeyTable.XK_KP_End);
addNumpad("Home", KeyTable.XK_Home, KeyTable.XK_KP_Home);
addNumpad("PageDown", KeyTable.XK_Next, KeyTable.XK_KP_Next);
addNumpad("PageUp", KeyTable.XK_Prior, KeyTable.XK_KP_Prior);
// 3.5. Editing Keys
addStandard("Backspace", KeyTable.XK_BackSpace);
// Browsers send "Clear" for the numpad 5 without NumLock because
// Windows uses VK_Clear for that key. But Unix expects KP_Begin for
// that scenario.
addNumpad("Clear", KeyTable.XK_Clear, KeyTable.XK_KP_Begin);
addStandard("Copy", KeyTable.XF86XK_Copy);
// - CrSel
addStandard("Cut", KeyTable.XF86XK_Cut);
addNumpad("Delete", KeyTable.XK_Delete, KeyTable.XK_KP_Delete);
// - EraseEof
// - ExSel
addNumpad("Insert", KeyTable.XK_Insert, KeyTable.XK_KP_Insert);
addStandard("Paste", KeyTable.XF86XK_Paste);
addStandard("Redo", KeyTable.XK_Redo);
addStandard("Undo", KeyTable.XK_Undo);
// 3.6. UI Keys
// - Accept
// - Again (could just be XK_Redo)
// - Attn
addStandard("Cancel", KeyTable.XK_Cancel);
addStandard("ContextMenu", KeyTable.XK_Menu);
addStandard("Escape", KeyTable.XK_Escape);
addStandard("Execute", KeyTable.XK_Execute);
addStandard("Find", KeyTable.XK_Find);
addStandard("Help", KeyTable.XK_Help);
addStandard("Pause", KeyTable.XK_Pause);
// - Play
// - Props
addStandard("Select", KeyTable.XK_Select);
addStandard("ZoomIn", KeyTable.XF86XK_ZoomIn);
addStandard("ZoomOut", KeyTable.XF86XK_ZoomOut);
// 3.7. Device Keys
addStandard("BrightnessDown", KeyTable.XF86XK_MonBrightnessDown);
addStandard("BrightnessUp", KeyTable.XF86XK_MonBrightnessUp);
addStandard("Eject", KeyTable.XF86XK_Eject);
addStandard("LogOff", KeyTable.XF86XK_LogOff);
addStandard("Power", KeyTable.XF86XK_PowerOff);
addStandard("PowerOff", KeyTable.XF86XK_PowerDown);
addStandard("PrintScreen", KeyTable.XK_Print);
addStandard("Hibernate", KeyTable.XF86XK_Hibernate);
addStandard("Standby", KeyTable.XF86XK_Standby);
addStandard("WakeUp", KeyTable.XF86XK_WakeUp);
// 3.8. IME and Composition Keys
addStandard("AllCandidates", KeyTable.XK_MultipleCandidate);
addStandard("Alphanumeric", KeyTable.XK_Eisu_toggle);
addStandard("CodeInput", KeyTable.XK_Codeinput);
addStandard("Compose", KeyTable.XK_Multi_key);
addStandard("Convert", KeyTable.XK_Henkan);
// - Dead
// - FinalMode
addStandard("GroupFirst", KeyTable.XK_ISO_First_Group);
addStandard("GroupLast", KeyTable.XK_ISO_Last_Group);
addStandard("GroupNext", KeyTable.XK_ISO_Next_Group);
addStandard("GroupPrevious", KeyTable.XK_ISO_Prev_Group);
// - ModeChange (XK_Mode_switch is often used for AltGr)
// - NextCandidate
addStandard("NonConvert", KeyTable.XK_Muhenkan);
addStandard("PreviousCandidate", KeyTable.XK_PreviousCandidate);
// - Process
addStandard("SingleCandidate", KeyTable.XK_SingleCandidate);
addStandard("HangulMode", KeyTable.XK_Hangul);
addStandard("HanjaMode", KeyTable.XK_Hangul_Hanja);
addStandard("JunjaMode", KeyTable.XK_Hangul_Jeonja);
addStandard("Eisu", KeyTable.XK_Eisu_toggle);
addStandard("Hankaku", KeyTable.XK_Hankaku);
addStandard("Hiragana", KeyTable.XK_Hiragana);
addStandard("HiraganaKatakana", KeyTable.XK_Hiragana_Katakana);
addStandard("KanaMode", KeyTable.XK_Kana_Shift); // could also be _Kana_Lock
addStandard("KanjiMode", KeyTable.XK_Kanji);
addStandard("Katakana", KeyTable.XK_Katakana);
addStandard("Romaji", KeyTable.XK_Romaji);
addStandard("Zenkaku", KeyTable.XK_Zenkaku);
addStandard("ZenkakuHankaku", KeyTable.XK_Zenkaku_Hankaku);
// 3.9. General-Purpose Function Keys
addStandard("F1", KeyTable.XK_F1);
addStandard("F2", KeyTable.XK_F2);
addStandard("F3", KeyTable.XK_F3);
addStandard("F4", KeyTable.XK_F4);
addStandard("F5", KeyTable.XK_F5);
addStandard("F6", KeyTable.XK_F6);
addStandard("F7", KeyTable.XK_F7);
addStandard("F8", KeyTable.XK_F8);
addStandard("F9", KeyTable.XK_F9);
addStandard("F10", KeyTable.XK_F10);
addStandard("F11", KeyTable.XK_F11);
addStandard("F12", KeyTable.XK_F12);
addStandard("F13", KeyTable.XK_F13);
addStandard("F14", KeyTable.XK_F14);
addStandard("F15", KeyTable.XK_F15);
addStandard("F16", KeyTable.XK_F16);
addStandard("F17", KeyTable.XK_F17);
addStandard("F18", KeyTable.XK_F18);
addStandard("F19", KeyTable.XK_F19);
addStandard("F20", KeyTable.XK_F20);
addStandard("F21", KeyTable.XK_F21);
addStandard("F22", KeyTable.XK_F22);
addStandard("F23", KeyTable.XK_F23);
addStandard("F24", KeyTable.XK_F24);
addStandard("F25", KeyTable.XK_F25);
addStandard("F26", KeyTable.XK_F26);
addStandard("F27", KeyTable.XK_F27);
addStandard("F28", KeyTable.XK_F28);
addStandard("F29", KeyTable.XK_F29);
addStandard("F30", KeyTable.XK_F30);
addStandard("F31", KeyTable.XK_F31);
addStandard("F32", KeyTable.XK_F32);
addStandard("F33", KeyTable.XK_F33);
addStandard("F34", KeyTable.XK_F34);
addStandard("F35", KeyTable.XK_F35);
// - Soft1...
// 3.10. Multimedia Keys
// - ChannelDown
// - ChannelUp
addStandard("Close", KeyTable.XF86XK_Close);
addStandard("MailForward", KeyTable.XF86XK_MailForward);
addStandard("MailReply", KeyTable.XF86XK_Reply);
addStandard("MailSend", KeyTable.XF86XK_Send);
// - MediaClose
addStandard("MediaFastForward", KeyTable.XF86XK_AudioForward);
addStandard("MediaPause", KeyTable.XF86XK_AudioPause);
addStandard("MediaPlay", KeyTable.XF86XK_AudioPlay);
// - MediaPlayPause
addStandard("MediaRecord", KeyTable.XF86XK_AudioRecord);
addStandard("MediaRewind", KeyTable.XF86XK_AudioRewind);
addStandard("MediaStop", KeyTable.XF86XK_AudioStop);
addStandard("MediaTrackNext", KeyTable.XF86XK_AudioNext);
addStandard("MediaTrackPrevious", KeyTable.XF86XK_AudioPrev);
addStandard("New", KeyTable.XF86XK_New);
addStandard("Open", KeyTable.XF86XK_Open);
addStandard("Print", KeyTable.XK_Print);
addStandard("Save", KeyTable.XF86XK_Save);
addStandard("SpellCheck", KeyTable.XF86XK_Spell);
// 3.11. Multimedia Numpad Keys
// - Key11
// - Key12
// 3.12. Audio Keys
// - AudioBalanceLeft
// - AudioBalanceRight
// - AudioBassBoostDown
// - AudioBassBoostToggle
// - AudioBassBoostUp
// - AudioFaderFront
// - AudioFaderRear
// - AudioSurroundModeNext
// - AudioTrebleDown
// - AudioTrebleUp
addStandard("AudioVolumeDown", KeyTable.XF86XK_AudioLowerVolume);
addStandard("AudioVolumeUp", KeyTable.XF86XK_AudioRaiseVolume);
addStandard("AudioVolumeMute", KeyTable.XF86XK_AudioMute);
// - MicrophoneToggle
// - MicrophoneVolumeDown
// - MicrophoneVolumeUp
addStandard("MicrophoneVolumeMute", KeyTable.XF86XK_AudioMicMute);
// 3.13. Speech Keys
// - SpeechCorrectionList
// - SpeechInputToggle
// 3.14. Application Keys
addStandard("LaunchApplication1", KeyTable.XF86XK_MyComputer);
addStandard("LaunchApplication2", KeyTable.XF86XK_Calculator);
addStandard("LaunchCalendar", KeyTable.XF86XK_Calendar);
// - LaunchContacts
addStandard("LaunchMail", KeyTable.XF86XK_Mail);
addStandard("LaunchMediaPlayer", KeyTable.XF86XK_AudioMedia);
addStandard("LaunchMusicPlayer", KeyTable.XF86XK_Music);
addStandard("LaunchPhone", KeyTable.XF86XK_Phone);
addStandard("LaunchScreenSaver", KeyTable.XF86XK_ScreenSaver);
addStandard("LaunchSpreadsheet", KeyTable.XF86XK_Excel);
addStandard("LaunchWebBrowser", KeyTable.XF86XK_WWW);
addStandard("LaunchWebCam", KeyTable.XF86XK_WebCam);
addStandard("LaunchWordProcessor", KeyTable.XF86XK_Word);
// 3.15. Browser Keys
addStandard("BrowserBack", KeyTable.XF86XK_Back);
addStandard("BrowserFavorites", KeyTable.XF86XK_Favorites);
addStandard("BrowserForward", KeyTable.XF86XK_Forward);
addStandard("BrowserHome", KeyTable.XF86XK_HomePage);
addStandard("BrowserRefresh", KeyTable.XF86XK_Refresh);
addStandard("BrowserSearch", KeyTable.XF86XK_Search);
addStandard("BrowserStop", KeyTable.XF86XK_Stop);
// 3.16. Mobile Phone Keys
// - A whole bunch...
// 3.17. TV Keys
// - A whole bunch...
// 3.18. Media Controller Keys
// - A whole bunch...
addStandard("Dimmer", KeyTable.XF86XK_BrightnessAdjust);
addStandard("MediaAudioTrack", KeyTable.XF86XK_AudioCycleTrack);
addStandard("RandomToggle", KeyTable.XF86XK_AudioRandomPlay);
addStandard("SplitScreenToggle", KeyTable.XF86XK_SplitScreen);
addStandard("Subtitle", KeyTable.XF86XK_Subtitle);
addStandard("VideoModeNext", KeyTable.XF86XK_Next_VMode);
// Extra: Numpad
addNumpad("=", KeyTable.XK_equal, KeyTable.XK_KP_Equal);
addNumpad("+", KeyTable.XK_plus, KeyTable.XK_KP_Add);
addNumpad("-", KeyTable.XK_minus, KeyTable.XK_KP_Subtract);
addNumpad("*", KeyTable.XK_asterisk, KeyTable.XK_KP_Multiply);
addNumpad("/", KeyTable.XK_slash, KeyTable.XK_KP_Divide);
addNumpad(".", KeyTable.XK_period, KeyTable.XK_KP_Decimal);
addNumpad(",", KeyTable.XK_comma, KeyTable.XK_KP_Separator);
addNumpad("0", KeyTable.XK_0, KeyTable.XK_KP_0);
addNumpad("1", KeyTable.XK_1, KeyTable.XK_KP_1);
addNumpad("2", KeyTable.XK_2, KeyTable.XK_KP_2);
addNumpad("3", KeyTable.XK_3, KeyTable.XK_KP_3);
addNumpad("4", KeyTable.XK_4, KeyTable.XK_KP_4);
addNumpad("5", KeyTable.XK_5, KeyTable.XK_KP_5);
addNumpad("6", KeyTable.XK_6, KeyTable.XK_KP_6);
addNumpad("7", KeyTable.XK_7, KeyTable.XK_KP_7);
addNumpad("8", KeyTable.XK_8, KeyTable.XK_KP_8);
addNumpad("9", KeyTable.XK_9, KeyTable.XK_KP_9);
export default DOMKeyTable;
-129
View File
@@ -1,129 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/
/*
* Fallback mapping between HTML key codes (physical keys) and
* HTML key values. This only works for keys that don't vary
* between layouts. We also omit those who manage fine by mapping the
* Unicode representation.
*
* See https://www.w3.org/TR/uievents-code/ for possible codes.
* See https://www.w3.org/TR/uievents-key/ for possible values.
*/
/* eslint-disable key-spacing */
export default {
// 3.1.1.1. Writing System Keys
'Backspace': 'Backspace',
// 3.1.1.2. Functional Keys
'AltLeft': 'Alt',
'AltRight': 'Alt', // This could also be 'AltGraph'
'CapsLock': 'CapsLock',
'ContextMenu': 'ContextMenu',
'ControlLeft': 'Control',
'ControlRight': 'Control',
'Enter': 'Enter',
'MetaLeft': 'Meta',
'MetaRight': 'Meta',
'ShiftLeft': 'Shift',
'ShiftRight': 'Shift',
'Tab': 'Tab',
// FIXME: Japanese/Korean keys
// 3.1.2. Control Pad Section
'Delete': 'Delete',
'End': 'End',
'Help': 'Help',
'Home': 'Home',
'Insert': 'Insert',
'PageDown': 'PageDown',
'PageUp': 'PageUp',
// 3.1.3. Arrow Pad Section
'ArrowDown': 'ArrowDown',
'ArrowLeft': 'ArrowLeft',
'ArrowRight': 'ArrowRight',
'ArrowUp': 'ArrowUp',
// 3.1.4. Numpad Section
'NumLock': 'NumLock',
'NumpadBackspace': 'Backspace',
'NumpadClear': 'Clear',
// 3.1.5. Function Section
'Escape': 'Escape',
'F1': 'F1',
'F2': 'F2',
'F3': 'F3',
'F4': 'F4',
'F5': 'F5',
'F6': 'F6',
'F7': 'F7',
'F8': 'F8',
'F9': 'F9',
'F10': 'F10',
'F11': 'F11',
'F12': 'F12',
'F13': 'F13',
'F14': 'F14',
'F15': 'F15',
'F16': 'F16',
'F17': 'F17',
'F18': 'F18',
'F19': 'F19',
'F20': 'F20',
'F21': 'F21',
'F22': 'F22',
'F23': 'F23',
'F24': 'F24',
'F25': 'F25',
'F26': 'F26',
'F27': 'F27',
'F28': 'F28',
'F29': 'F29',
'F30': 'F30',
'F31': 'F31',
'F32': 'F32',
'F33': 'F33',
'F34': 'F34',
'F35': 'F35',
'PrintScreen': 'PrintScreen',
'ScrollLock': 'ScrollLock',
'Pause': 'Pause',
// 3.1.6. Media Keys
'BrowserBack': 'BrowserBack',
'BrowserFavorites': 'BrowserFavorites',
'BrowserForward': 'BrowserForward',
'BrowserHome': 'BrowserHome',
'BrowserRefresh': 'BrowserRefresh',
'BrowserSearch': 'BrowserSearch',
'BrowserStop': 'BrowserStop',
'Eject': 'Eject',
'LaunchApp1': 'LaunchMyComputer',
'LaunchApp2': 'LaunchCalendar',
'LaunchMail': 'LaunchMail',
'MediaPlayPause': 'MediaPlay',
'MediaStop': 'MediaStop',
'MediaTrackNext': 'MediaTrackNext',
'MediaTrackPrevious': 'MediaTrackPrevious',
'Power': 'Power',
'Sleep': 'Sleep',
'AudioVolumeDown': 'AudioVolumeDown',
'AudioVolumeMute': 'AudioVolumeMute',
'AudioVolumeUp': 'AudioVolumeUp',
'WakeUp': 'WakeUp',
};
-567
View File
@@ -1,567 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2020 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
const GH_NOGESTURE = 0;
const GH_ONETAP = 1;
const GH_TWOTAP = 2;
const GH_THREETAP = 4;
const GH_DRAG = 8;
const GH_LONGPRESS = 16;
const GH_TWODRAG = 32;
const GH_PINCH = 64;
const GH_INITSTATE = 127;
const GH_MOVE_THRESHOLD = 50;
const GH_ANGLE_THRESHOLD = 90; // Degrees
// Timeout when waiting for gestures (ms)
const GH_MULTITOUCH_TIMEOUT = 250;
// Maximum time between press and release for a tap (ms)
const GH_TAP_TIMEOUT = 1000;
// Timeout when waiting for longpress (ms)
const GH_LONGPRESS_TIMEOUT = 1000;
// Timeout when waiting to decide between PINCH and TWODRAG (ms)
const GH_TWOTOUCH_TIMEOUT = 50;
export default class GestureHandler {
constructor() {
this._target = null;
this._state = GH_INITSTATE;
this._tracked = [];
this._ignored = [];
this._waitingRelease = false;
this._releaseStart = 0.0;
this._longpressTimeoutId = null;
this._twoTouchTimeoutId = null;
this._boundEventHandler = this._eventHandler.bind(this);
}
attach(target) {
this.detach();
this._target = target;
this._target.addEventListener('touchstart',
this._boundEventHandler);
this._target.addEventListener('touchmove',
this._boundEventHandler);
this._target.addEventListener('touchend',
this._boundEventHandler);
this._target.addEventListener('touchcancel',
this._boundEventHandler);
}
detach() {
if (!this._target) {
return;
}
this._stopLongpressTimeout();
this._stopTwoTouchTimeout();
this._target.removeEventListener('touchstart',
this._boundEventHandler);
this._target.removeEventListener('touchmove',
this._boundEventHandler);
this._target.removeEventListener('touchend',
this._boundEventHandler);
this._target.removeEventListener('touchcancel',
this._boundEventHandler);
this._target = null;
}
_eventHandler(e) {
let fn;
e.stopPropagation();
e.preventDefault();
switch (e.type) {
case 'touchstart':
fn = this._touchStart;
break;
case 'touchmove':
fn = this._touchMove;
break;
case 'touchend':
case 'touchcancel':
fn = this._touchEnd;
break;
}
for (let i = 0; i < e.changedTouches.length; i++) {
let touch = e.changedTouches[i];
fn.call(this, touch.identifier, touch.clientX, touch.clientY);
}
}
_touchStart(id, x, y) {
// Ignore any new touches if there is already an active gesture,
// or we're in a cleanup state
if (this._hasDetectedGesture() || (this._state === GH_NOGESTURE)) {
this._ignored.push(id);
return;
}
// Did it take too long between touches that we should no longer
// consider this a single gesture?
if ((this._tracked.length > 0) &&
((Date.now() - this._tracked[0].started) > GH_MULTITOUCH_TIMEOUT)) {
this._state = GH_NOGESTURE;
this._ignored.push(id);
return;
}
// If we're waiting for fingers to release then we should no longer
// recognize new touches
if (this._waitingRelease) {
this._state = GH_NOGESTURE;
this._ignored.push(id);
return;
}
this._tracked.push({
id: id,
started: Date.now(),
active: true,
firstX: x,
firstY: y,
lastX: x,
lastY: y,
angle: 0
});
switch (this._tracked.length) {
case 1:
this._startLongpressTimeout();
break;
case 2:
this._state &= ~(GH_ONETAP | GH_DRAG | GH_LONGPRESS);
this._stopLongpressTimeout();
break;
case 3:
this._state &= ~(GH_TWOTAP | GH_TWODRAG | GH_PINCH);
break;
default:
this._state = GH_NOGESTURE;
}
}
_touchMove(id, x, y) {
let touch = this._tracked.find(t => t.id === id);
// If this is an update for a touch we're not tracking, ignore it
if (touch === undefined) {
return;
}
// Update the touches last position with the event coordinates
touch.lastX = x;
touch.lastY = y;
let deltaX = x - touch.firstX;
let deltaY = y - touch.firstY;
// Update angle when the touch has moved
if ((touch.firstX !== touch.lastX) ||
(touch.firstY !== touch.lastY)) {
touch.angle = Math.atan2(deltaY, deltaX) * 180 / Math.PI;
}
if (!this._hasDetectedGesture()) {
// Ignore moves smaller than the minimum threshold
if (Math.hypot(deltaX, deltaY) < GH_MOVE_THRESHOLD) {
return;
}
// Can't be a tap or long press as we've seen movement
this._state &= ~(GH_ONETAP | GH_TWOTAP | GH_THREETAP | GH_LONGPRESS);
this._stopLongpressTimeout();
if (this._tracked.length !== 1) {
this._state &= ~(GH_DRAG);
}
if (this._tracked.length !== 2) {
this._state &= ~(GH_TWODRAG | GH_PINCH);
}
// We need to figure out which of our different two touch gestures
// this might be
if (this._tracked.length === 2) {
// The other touch is the one where the id doesn't match
let prevTouch = this._tracked.find(t => t.id !== id);
// How far the previous touch point has moved since start
let prevDeltaMove = Math.hypot(prevTouch.firstX - prevTouch.lastX,
prevTouch.firstY - prevTouch.lastY);
// We know that the current touch moved far enough,
// but unless both touches moved further than their
// threshold we don't want to disqualify any gestures
if (prevDeltaMove > GH_MOVE_THRESHOLD) {
// The angle difference between the direction of the touch points
let deltaAngle = Math.abs(touch.angle - prevTouch.angle);
deltaAngle = Math.abs(((deltaAngle + 180) % 360) - 180);
// PINCH or TWODRAG can be eliminated depending on the angle
if (deltaAngle > GH_ANGLE_THRESHOLD) {
this._state &= ~GH_TWODRAG;
} else {
this._state &= ~GH_PINCH;
}
if (this._isTwoTouchTimeoutRunning()) {
this._stopTwoTouchTimeout();
}
} else if (!this._isTwoTouchTimeoutRunning()) {
// We can't determine the gesture right now, let's
// wait and see if more events are on their way
this._startTwoTouchTimeout();
}
}
if (!this._hasDetectedGesture()) {
return;
}
this._pushEvent('gesturestart');
}
this._pushEvent('gesturemove');
}
_touchEnd(id, x, y) {
// Check if this is an ignored touch
if (this._ignored.indexOf(id) !== -1) {
// Remove this touch from ignored
this._ignored.splice(this._ignored.indexOf(id), 1);
// And reset the state if there are no more touches
if ((this._ignored.length === 0) &&
(this._tracked.length === 0)) {
this._state = GH_INITSTATE;
this._waitingRelease = false;
}
return;
}
// We got a touchend before the timer triggered,
// this cannot result in a gesture anymore.
if (!this._hasDetectedGesture() &&
this._isTwoTouchTimeoutRunning()) {
this._stopTwoTouchTimeout();
this._state = GH_NOGESTURE;
}
// Some gestures don't trigger until a touch is released
if (!this._hasDetectedGesture()) {
// Can't be a gesture that relies on movement
this._state &= ~(GH_DRAG | GH_TWODRAG | GH_PINCH);
// Or something that relies on more time
this._state &= ~GH_LONGPRESS;
this._stopLongpressTimeout();
if (!this._waitingRelease) {
this._releaseStart = Date.now();
this._waitingRelease = true;
// Can't be a tap that requires more touches than we current have
switch (this._tracked.length) {
case 1:
this._state &= ~(GH_TWOTAP | GH_THREETAP);
break;
case 2:
this._state &= ~(GH_ONETAP | GH_THREETAP);
break;
}
}
}
// Waiting for all touches to release? (i.e. some tap)
if (this._waitingRelease) {
// Were all touches released at roughly the same time?
if ((Date.now() - this._releaseStart) > GH_MULTITOUCH_TIMEOUT) {
this._state = GH_NOGESTURE;
}
// Did too long time pass between press and release?
if (this._tracked.some(t => (Date.now() - t.started) > GH_TAP_TIMEOUT)) {
this._state = GH_NOGESTURE;
}
let touch = this._tracked.find(t => t.id === id);
touch.active = false;
// Are we still waiting for more releases?
if (this._hasDetectedGesture()) {
this._pushEvent('gesturestart');
} else {
// Have we reached a dead end?
if (this._state !== GH_NOGESTURE) {
return;
}
}
}
if (this._hasDetectedGesture()) {
this._pushEvent('gestureend');
}
// Ignore any remaining touches until they are ended
for (let i = 0; i < this._tracked.length; i++) {
if (this._tracked[i].active) {
this._ignored.push(this._tracked[i].id);
}
}
this._tracked = [];
this._state = GH_NOGESTURE;
// Remove this touch from ignored if it's in there
if (this._ignored.indexOf(id) !== -1) {
this._ignored.splice(this._ignored.indexOf(id), 1);
}
// We reset the state if ignored is empty
if ((this._ignored.length === 0)) {
this._state = GH_INITSTATE;
this._waitingRelease = false;
}
}
_hasDetectedGesture() {
if (this._state === GH_NOGESTURE) {
return false;
}
// Check to see if the bitmask value is a power of 2
// (i.e. only one bit set). If it is, we have a state.
if (this._state & (this._state - 1)) {
return false;
}
// For taps we also need to have all touches released
// before we've fully detected the gesture
if (this._state & (GH_ONETAP | GH_TWOTAP | GH_THREETAP)) {
if (this._tracked.some(t => t.active)) {
return false;
}
}
return true;
}
_startLongpressTimeout() {
this._stopLongpressTimeout();
this._longpressTimeoutId = setTimeout(() => this._longpressTimeout(),
GH_LONGPRESS_TIMEOUT);
}
_stopLongpressTimeout() {
clearTimeout(this._longpressTimeoutId);
this._longpressTimeoutId = null;
}
_longpressTimeout() {
if (this._hasDetectedGesture()) {
throw new Error("A longpress gesture failed, conflict with a different gesture");
}
this._state = GH_LONGPRESS;
this._pushEvent('gesturestart');
}
_startTwoTouchTimeout() {
this._stopTwoTouchTimeout();
this._twoTouchTimeoutId = setTimeout(() => this._twoTouchTimeout(),
GH_TWOTOUCH_TIMEOUT);
}
_stopTwoTouchTimeout() {
clearTimeout(this._twoTouchTimeoutId);
this._twoTouchTimeoutId = null;
}
_isTwoTouchTimeoutRunning() {
return this._twoTouchTimeoutId !== null;
}
_twoTouchTimeout() {
if (this._tracked.length === 0) {
throw new Error("A pinch or two drag gesture failed, no tracked touches");
}
// How far each touch point has moved since start
let avgM = this._getAverageMovement();
let avgMoveH = Math.abs(avgM.x);
let avgMoveV = Math.abs(avgM.y);
// The difference in the distance between where
// the touch points started and where they are now
let avgD = this._getAverageDistance();
let deltaTouchDistance = Math.abs(Math.hypot(avgD.first.x, avgD.first.y) -
Math.hypot(avgD.last.x, avgD.last.y));
if ((avgMoveV < deltaTouchDistance) &&
(avgMoveH < deltaTouchDistance)) {
this._state = GH_PINCH;
} else {
this._state = GH_TWODRAG;
}
this._pushEvent('gesturestart');
this._pushEvent('gesturemove');
}
_pushEvent(type) {
let detail = { type: this._stateToGesture(this._state) };
// For most gesture events the current (average) position is the
// most useful
let avg = this._getPosition();
let pos = avg.last;
// However we have a slight distance to detect gestures, so for the
// first gesture event we want to use the first positions we saw
if (type === 'gesturestart') {
pos = avg.first;
}
// For these gestures, we always want the event coordinates
// to be where the gesture began, not the current touch location.
switch (this._state) {
case GH_TWODRAG:
case GH_PINCH:
pos = avg.first;
break;
}
detail['clientX'] = pos.x;
detail['clientY'] = pos.y;
// FIXME: other coordinates?
// Some gestures also have a magnitude
if (this._state === GH_PINCH) {
let distance = this._getAverageDistance();
if (type === 'gesturestart') {
detail['magnitudeX'] = distance.first.x;
detail['magnitudeY'] = distance.first.y;
} else {
detail['magnitudeX'] = distance.last.x;
detail['magnitudeY'] = distance.last.y;
}
} else if (this._state === GH_TWODRAG) {
if (type === 'gesturestart') {
detail['magnitudeX'] = 0.0;
detail['magnitudeY'] = 0.0;
} else {
let movement = this._getAverageMovement();
detail['magnitudeX'] = movement.x;
detail['magnitudeY'] = movement.y;
}
}
let gev = new CustomEvent(type, { detail: detail });
this._target.dispatchEvent(gev);
}
_stateToGesture(state) {
switch (state) {
case GH_ONETAP:
return 'onetap';
case GH_TWOTAP:
return 'twotap';
case GH_THREETAP:
return 'threetap';
case GH_DRAG:
return 'drag';
case GH_LONGPRESS:
return 'longpress';
case GH_TWODRAG:
return 'twodrag';
case GH_PINCH:
return 'pinch';
}
throw new Error("Unknown gesture state: " + state);
}
_getPosition() {
if (this._tracked.length === 0) {
throw new Error("Failed to get gesture position, no tracked touches");
}
let size = this._tracked.length;
let fx = 0, fy = 0, lx = 0, ly = 0;
for (let i = 0; i < this._tracked.length; i++) {
fx += this._tracked[i].firstX;
fy += this._tracked[i].firstY;
lx += this._tracked[i].lastX;
ly += this._tracked[i].lastY;
}
return { first: { x: fx / size,
y: fy / size },
last: { x: lx / size,
y: ly / size } };
}
_getAverageMovement() {
if (this._tracked.length === 0) {
throw new Error("Failed to get gesture movement, no tracked touches");
}
let totalH, totalV;
totalH = totalV = 0;
let size = this._tracked.length;
for (let i = 0; i < this._tracked.length; i++) {
totalH += this._tracked[i].lastX - this._tracked[i].firstX;
totalV += this._tracked[i].lastY - this._tracked[i].firstY;
}
return { x: totalH / size,
y: totalV / size };
}
_getAverageDistance() {
if (this._tracked.length === 0) {
throw new Error("Failed to get gesture distance, no tracked touches");
}
// Distance between the first and last tracked touches
let first = this._tracked[0];
let last = this._tracked[this._tracked.length - 1];
let fdx = Math.abs(last.firstX - first.firstX);
let fdy = Math.abs(last.firstY - first.firstY);
let ldx = Math.abs(last.lastX - first.lastX);
let ldy = Math.abs(last.lastY - first.lastY);
return { first: { x: fdx, y: fdy },
last: { x: ldx, y: ldy } };
}
}
-273
View File
@@ -1,273 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/
import * as Log from '../util/logging.js';
import { stopEvent } from '../util/events.js';
import * as KeyboardUtil from "./util.js";
import KeyTable from "./keysym.js";
import * as browser from "../util/browser.js";
//
// Keyboard event handler
//
export default class Keyboard {
constructor(target) {
this._target = target || null;
this._keyDownList = {}; // List of depressed keys
// (even if they are happy)
this._altGrArmed = false; // Windows AltGr detection
// keep these here so we can refer to them later
this._eventHandlers = {
'keyup': this._handleKeyUp.bind(this),
'keydown': this._handleKeyDown.bind(this),
'blur': this._allKeysUp.bind(this),
};
// ===== EVENT HANDLERS =====
this.onkeyevent = () => {}; // Handler for key press/release
}
// ===== PRIVATE METHODS =====
_sendKeyEvent(keysym, code, down) {
if (down) {
this._keyDownList[code] = keysym;
} else {
// Do we really think this key is down?
if (!(code in this._keyDownList)) {
return;
}
delete this._keyDownList[code];
}
Log.Debug("onkeyevent " + (down ? "down" : "up") +
", keysym: " + keysym, ", code: " + code);
this.onkeyevent(keysym, code, down);
}
_getKeyCode(e) {
const code = KeyboardUtil.getKeycode(e);
if (code !== 'Unidentified') {
return code;
}
// Unstable, but we don't have anything else to go on
if (e.keyCode) {
// 229 is used for composition events
if (e.keyCode !== 229) {
return 'Platform' + e.keyCode;
}
}
// A precursor to the final DOM3 standard. Unfortunately it
// is not layout independent, so it is as bad as using keyCode
if (e.keyIdentifier) {
// Non-character key?
if (e.keyIdentifier.substr(0, 2) !== 'U+') {
return e.keyIdentifier;
}
const codepoint = parseInt(e.keyIdentifier.substr(2), 16);
const char = String.fromCharCode(codepoint).toUpperCase();
return 'Platform' + char.charCodeAt();
}
return 'Unidentified';
}
_handleKeyDown(e) {
const code = this._getKeyCode(e);
let keysym = KeyboardUtil.getKeysym(e);
// Windows doesn't have a proper AltGr, but handles it using
// fake Ctrl+Alt. However the remote end might not be Windows,
// so we need to merge those in to a single AltGr event. We
// detect this case by seeing the two key events directly after
// each other with a very short time between them (<50ms).
if (this._altGrArmed) {
this._altGrArmed = false;
clearTimeout(this._altGrTimeout);
if ((code === "AltRight") &&
((e.timeStamp - this._altGrCtrlTime) < 50)) {
// FIXME: We fail to detect this if either Ctrl key is
// first manually pressed as Windows then no
// longer sends the fake Ctrl down event. It
// does however happily send real Ctrl events
// even when AltGr is already down. Some
// browsers detect this for us though and set the
// key to "AltGraph".
keysym = KeyTable.XK_ISO_Level3_Shift;
} else {
this._sendKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
}
}
// We cannot handle keys we cannot track, but we also need
// to deal with virtual keyboards which omit key info
if (code === 'Unidentified') {
if (keysym) {
// If it's a virtual keyboard then it should be
// sufficient to just send press and release right
// after each other
this._sendKeyEvent(keysym, code, true);
this._sendKeyEvent(keysym, code, false);
}
stopEvent(e);
return;
}
// Alt behaves more like AltGraph on macOS, so shuffle the
// keys around a bit to make things more sane for the remote
// server. This method is used by RealVNC and TigerVNC (and
// possibly others).
if (browser.isMac() || browser.isIOS()) {
switch (keysym) {
case KeyTable.XK_Super_L:
keysym = KeyTable.XK_Alt_L;
break;
case KeyTable.XK_Super_R:
keysym = KeyTable.XK_Super_L;
break;
case KeyTable.XK_Alt_L:
keysym = KeyTable.XK_Mode_switch;
break;
case KeyTable.XK_Alt_R:
keysym = KeyTable.XK_ISO_Level3_Shift;
break;
}
}
// Is this key already pressed? If so, then we must use the
// same keysym or we'll confuse the server
if (code in this._keyDownList) {
keysym = this._keyDownList[code];
}
// macOS doesn't send proper key events for modifiers, only
// state change events. That gets extra confusing for CapsLock
// which toggles on each press, but not on release. So pretend
// it was a quick press and release of the button.
if ((browser.isMac() || browser.isIOS()) && (code === 'CapsLock')) {
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);
stopEvent(e);
return;
}
// Windows doesn't send proper key releases for a bunch of
// Japanese IM keys so we have to fake the release right away
const jpBadKeys = [ KeyTable.XK_Zenkaku_Hankaku,
KeyTable.XK_Eisu_toggle,
KeyTable.XK_Katakana,
KeyTable.XK_Hiragana,
KeyTable.XK_Romaji ];
if (browser.isWindows() && jpBadKeys.includes(keysym)) {
this._sendKeyEvent(keysym, code, true);
this._sendKeyEvent(keysym, code, false);
stopEvent(e);
return;
}
stopEvent(e);
// Possible start of AltGr sequence? (see above)
if ((code === "ControlLeft") && browser.isWindows() &&
!("ControlLeft" in this._keyDownList)) {
this._altGrArmed = true;
this._altGrTimeout = setTimeout(this._handleAltGrTimeout.bind(this), 100);
this._altGrCtrlTime = e.timeStamp;
return;
}
this._sendKeyEvent(keysym, code, true);
}
_handleKeyUp(e) {
stopEvent(e);
const code = this._getKeyCode(e);
// We can't get a release in the middle of an AltGr sequence, so
// abort that detection
if (this._altGrArmed) {
this._altGrArmed = false;
clearTimeout(this._altGrTimeout);
this._sendKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
}
// See comment in _handleKeyDown()
if ((browser.isMac() || browser.isIOS()) && (code === 'CapsLock')) {
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);
return;
}
this._sendKeyEvent(this._keyDownList[code], code, false);
// Windows has a rather nasty bug where it won't send key
// release events for a Shift button if the other Shift is still
// pressed
if (browser.isWindows() && ((code === 'ShiftLeft') ||
(code === 'ShiftRight'))) {
if ('ShiftRight' in this._keyDownList) {
this._sendKeyEvent(this._keyDownList['ShiftRight'],
'ShiftRight', false);
}
if ('ShiftLeft' in this._keyDownList) {
this._sendKeyEvent(this._keyDownList['ShiftLeft'],
'ShiftLeft', false);
}
}
}
_handleAltGrTimeout() {
this._altGrArmed = false;
clearTimeout(this._altGrTimeout);
this._sendKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
}
_allKeysUp() {
Log.Debug(">> Keyboard.allKeysUp");
for (let code in this._keyDownList) {
this._sendKeyEvent(this._keyDownList[code], code, false);
}
Log.Debug("<< Keyboard.allKeysUp");
}
// ===== PUBLIC METHODS =====
grab() {
//Log.Debug(">> Keyboard.grab");
this._target.addEventListener('keydown', this._eventHandlers.keydown);
this._target.addEventListener('keyup', this._eventHandlers.keyup);
// Release (key up) if window loses focus
window.addEventListener('blur', this._eventHandlers.blur);
//Log.Debug("<< Keyboard.grab");
}
ungrab() {
//Log.Debug(">> Keyboard.ungrab");
this._target.removeEventListener('keydown', this._eventHandlers.keydown);
this._target.removeEventListener('keyup', this._eventHandlers.keyup);
window.removeEventListener('blur', this._eventHandlers.blur);
// Release (key up) all keys that are in a down state
this._allKeysUp();
//Log.Debug(">> Keyboard.ungrab");
}
}
+3 -237
View File
@@ -1,6 +1,4 @@
/* eslint-disable key-spacing */
export default {
var KeyTable = {
XK_VoidSymbol: 0xffffff, /* Void symbol */
XK_BackSpace: 0xff08, /* Back space, back char */
@@ -14,37 +12,6 @@ export default {
XK_Escape: 0xff1b,
XK_Delete: 0xffff, /* Delete, rubout */
/* International & multi-key character composition */
XK_Multi_key: 0xff20, /* Multi-key character compose */
XK_Codeinput: 0xff37,
XK_SingleCandidate: 0xff3c,
XK_MultipleCandidate: 0xff3d,
XK_PreviousCandidate: 0xff3e,
/* Japanese keyboard support */
XK_Kanji: 0xff21, /* Kanji, Kanji convert */
XK_Muhenkan: 0xff22, /* Cancel Conversion */
XK_Henkan_Mode: 0xff23, /* Start/Stop Conversion */
XK_Henkan: 0xff23, /* Alias for Henkan_Mode */
XK_Romaji: 0xff24, /* to Romaji */
XK_Hiragana: 0xff25, /* to Hiragana */
XK_Katakana: 0xff26, /* to Katakana */
XK_Hiragana_Katakana: 0xff27, /* Hiragana/Katakana toggle */
XK_Zenkaku: 0xff28, /* to Zenkaku */
XK_Hankaku: 0xff29, /* to Hankaku */
XK_Zenkaku_Hankaku: 0xff2a, /* Zenkaku/Hankaku toggle */
XK_Touroku: 0xff2b, /* Add to Dictionary */
XK_Massyo: 0xff2c, /* Delete from Dictionary */
XK_Kana_Lock: 0xff2d, /* Kana Lock */
XK_Kana_Shift: 0xff2e, /* Kana Shift */
XK_Eisu_Shift: 0xff2f, /* Alphanumeric Shift */
XK_Eisu_toggle: 0xff30, /* Alphanumeric toggle */
XK_Kanji_Bangou: 0xff37, /* Codeinput */
XK_Zen_Koho: 0xff3d, /* Multiple/All Candidate(s) */
XK_Mae_Koho: 0xff3e, /* Previous Candidate */
/* Cursor control & motion */
XK_Home: 0xff50,
@@ -204,17 +171,7 @@ export default {
XK_Hyper_L: 0xffed, /* Left hyper */
XK_Hyper_R: 0xffee, /* Right hyper */
/*
* Keyboard (XKB) Extension function and modifier keys
* (from Appendix C of "The X Keyboard Extension: Protocol Specification")
* Byte 3 = 0xfe
*/
XK_ISO_Level3_Shift: 0xfe03, /* AltGr */
XK_ISO_Next_Group: 0xfe08,
XK_ISO_Prev_Group: 0xfe0a,
XK_ISO_First_Group: 0xfe0c,
XK_ISO_Last_Group: 0xfe0e,
/*
* Latin 1
@@ -420,197 +377,6 @@ export default {
XK_yacute: 0x00fd, /* U+00FD LATIN SMALL LETTER Y WITH ACUTE */
XK_thorn: 0x00fe, /* U+00FE LATIN SMALL LETTER THORN */
XK_ydiaeresis: 0x00ff, /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */
/*
* Korean
* Byte 3 = 0x0e
*/
XK_Hangul: 0xff31, /* Hangul start/stop(toggle) */
XK_Hangul_Hanja: 0xff34, /* Start Hangul->Hanja Conversion */
XK_Hangul_Jeonja: 0xff38, /* Jeonja mode */
/*
* XFree86 vendor specific keysyms.
*
* The XFree86 keysym range is 0x10080001 - 0x1008FFFF.
*/
XF86XK_ModeLock: 0x1008FF01,
XF86XK_MonBrightnessUp: 0x1008FF02,
XF86XK_MonBrightnessDown: 0x1008FF03,
XF86XK_KbdLightOnOff: 0x1008FF04,
XF86XK_KbdBrightnessUp: 0x1008FF05,
XF86XK_KbdBrightnessDown: 0x1008FF06,
XF86XK_Standby: 0x1008FF10,
XF86XK_AudioLowerVolume: 0x1008FF11,
XF86XK_AudioMute: 0x1008FF12,
XF86XK_AudioRaiseVolume: 0x1008FF13,
XF86XK_AudioPlay: 0x1008FF14,
XF86XK_AudioStop: 0x1008FF15,
XF86XK_AudioPrev: 0x1008FF16,
XF86XK_AudioNext: 0x1008FF17,
XF86XK_HomePage: 0x1008FF18,
XF86XK_Mail: 0x1008FF19,
XF86XK_Start: 0x1008FF1A,
XF86XK_Search: 0x1008FF1B,
XF86XK_AudioRecord: 0x1008FF1C,
XF86XK_Calculator: 0x1008FF1D,
XF86XK_Memo: 0x1008FF1E,
XF86XK_ToDoList: 0x1008FF1F,
XF86XK_Calendar: 0x1008FF20,
XF86XK_PowerDown: 0x1008FF21,
XF86XK_ContrastAdjust: 0x1008FF22,
XF86XK_RockerUp: 0x1008FF23,
XF86XK_RockerDown: 0x1008FF24,
XF86XK_RockerEnter: 0x1008FF25,
XF86XK_Back: 0x1008FF26,
XF86XK_Forward: 0x1008FF27,
XF86XK_Stop: 0x1008FF28,
XF86XK_Refresh: 0x1008FF29,
XF86XK_PowerOff: 0x1008FF2A,
XF86XK_WakeUp: 0x1008FF2B,
XF86XK_Eject: 0x1008FF2C,
XF86XK_ScreenSaver: 0x1008FF2D,
XF86XK_WWW: 0x1008FF2E,
XF86XK_Sleep: 0x1008FF2F,
XF86XK_Favorites: 0x1008FF30,
XF86XK_AudioPause: 0x1008FF31,
XF86XK_AudioMedia: 0x1008FF32,
XF86XK_MyComputer: 0x1008FF33,
XF86XK_VendorHome: 0x1008FF34,
XF86XK_LightBulb: 0x1008FF35,
XF86XK_Shop: 0x1008FF36,
XF86XK_History: 0x1008FF37,
XF86XK_OpenURL: 0x1008FF38,
XF86XK_AddFavorite: 0x1008FF39,
XF86XK_HotLinks: 0x1008FF3A,
XF86XK_BrightnessAdjust: 0x1008FF3B,
XF86XK_Finance: 0x1008FF3C,
XF86XK_Community: 0x1008FF3D,
XF86XK_AudioRewind: 0x1008FF3E,
XF86XK_BackForward: 0x1008FF3F,
XF86XK_Launch0: 0x1008FF40,
XF86XK_Launch1: 0x1008FF41,
XF86XK_Launch2: 0x1008FF42,
XF86XK_Launch3: 0x1008FF43,
XF86XK_Launch4: 0x1008FF44,
XF86XK_Launch5: 0x1008FF45,
XF86XK_Launch6: 0x1008FF46,
XF86XK_Launch7: 0x1008FF47,
XF86XK_Launch8: 0x1008FF48,
XF86XK_Launch9: 0x1008FF49,
XF86XK_LaunchA: 0x1008FF4A,
XF86XK_LaunchB: 0x1008FF4B,
XF86XK_LaunchC: 0x1008FF4C,
XF86XK_LaunchD: 0x1008FF4D,
XF86XK_LaunchE: 0x1008FF4E,
XF86XK_LaunchF: 0x1008FF4F,
XF86XK_ApplicationLeft: 0x1008FF50,
XF86XK_ApplicationRight: 0x1008FF51,
XF86XK_Book: 0x1008FF52,
XF86XK_CD: 0x1008FF53,
XF86XK_Calculater: 0x1008FF54,
XF86XK_Clear: 0x1008FF55,
XF86XK_Close: 0x1008FF56,
XF86XK_Copy: 0x1008FF57,
XF86XK_Cut: 0x1008FF58,
XF86XK_Display: 0x1008FF59,
XF86XK_DOS: 0x1008FF5A,
XF86XK_Documents: 0x1008FF5B,
XF86XK_Excel: 0x1008FF5C,
XF86XK_Explorer: 0x1008FF5D,
XF86XK_Game: 0x1008FF5E,
XF86XK_Go: 0x1008FF5F,
XF86XK_iTouch: 0x1008FF60,
XF86XK_LogOff: 0x1008FF61,
XF86XK_Market: 0x1008FF62,
XF86XK_Meeting: 0x1008FF63,
XF86XK_MenuKB: 0x1008FF65,
XF86XK_MenuPB: 0x1008FF66,
XF86XK_MySites: 0x1008FF67,
XF86XK_New: 0x1008FF68,
XF86XK_News: 0x1008FF69,
XF86XK_OfficeHome: 0x1008FF6A,
XF86XK_Open: 0x1008FF6B,
XF86XK_Option: 0x1008FF6C,
XF86XK_Paste: 0x1008FF6D,
XF86XK_Phone: 0x1008FF6E,
XF86XK_Q: 0x1008FF70,
XF86XK_Reply: 0x1008FF72,
XF86XK_Reload: 0x1008FF73,
XF86XK_RotateWindows: 0x1008FF74,
XF86XK_RotationPB: 0x1008FF75,
XF86XK_RotationKB: 0x1008FF76,
XF86XK_Save: 0x1008FF77,
XF86XK_ScrollUp: 0x1008FF78,
XF86XK_ScrollDown: 0x1008FF79,
XF86XK_ScrollClick: 0x1008FF7A,
XF86XK_Send: 0x1008FF7B,
XF86XK_Spell: 0x1008FF7C,
XF86XK_SplitScreen: 0x1008FF7D,
XF86XK_Support: 0x1008FF7E,
XF86XK_TaskPane: 0x1008FF7F,
XF86XK_Terminal: 0x1008FF80,
XF86XK_Tools: 0x1008FF81,
XF86XK_Travel: 0x1008FF82,
XF86XK_UserPB: 0x1008FF84,
XF86XK_User1KB: 0x1008FF85,
XF86XK_User2KB: 0x1008FF86,
XF86XK_Video: 0x1008FF87,
XF86XK_WheelButton: 0x1008FF88,
XF86XK_Word: 0x1008FF89,
XF86XK_Xfer: 0x1008FF8A,
XF86XK_ZoomIn: 0x1008FF8B,
XF86XK_ZoomOut: 0x1008FF8C,
XF86XK_Away: 0x1008FF8D,
XF86XK_Messenger: 0x1008FF8E,
XF86XK_WebCam: 0x1008FF8F,
XF86XK_MailForward: 0x1008FF90,
XF86XK_Pictures: 0x1008FF91,
XF86XK_Music: 0x1008FF92,
XF86XK_Battery: 0x1008FF93,
XF86XK_Bluetooth: 0x1008FF94,
XF86XK_WLAN: 0x1008FF95,
XF86XK_UWB: 0x1008FF96,
XF86XK_AudioForward: 0x1008FF97,
XF86XK_AudioRepeat: 0x1008FF98,
XF86XK_AudioRandomPlay: 0x1008FF99,
XF86XK_Subtitle: 0x1008FF9A,
XF86XK_AudioCycleTrack: 0x1008FF9B,
XF86XK_CycleAngle: 0x1008FF9C,
XF86XK_FrameBack: 0x1008FF9D,
XF86XK_FrameForward: 0x1008FF9E,
XF86XK_Time: 0x1008FF9F,
XF86XK_Select: 0x1008FFA0,
XF86XK_View: 0x1008FFA1,
XF86XK_TopMenu: 0x1008FFA2,
XF86XK_Red: 0x1008FFA3,
XF86XK_Green: 0x1008FFA4,
XF86XK_Yellow: 0x1008FFA5,
XF86XK_Blue: 0x1008FFA6,
XF86XK_Suspend: 0x1008FFA7,
XF86XK_Hibernate: 0x1008FFA8,
XF86XK_TouchpadToggle: 0x1008FFA9,
XF86XK_TouchpadOn: 0x1008FFB0,
XF86XK_TouchpadOff: 0x1008FFB1,
XF86XK_AudioMicMute: 0x1008FFB2,
XF86XK_Switch_VT_1: 0x1008FE01,
XF86XK_Switch_VT_2: 0x1008FE02,
XF86XK_Switch_VT_3: 0x1008FE03,
XF86XK_Switch_VT_4: 0x1008FE04,
XF86XK_Switch_VT_5: 0x1008FE05,
XF86XK_Switch_VT_6: 0x1008FE06,
XF86XK_Switch_VT_7: 0x1008FE07,
XF86XK_Switch_VT_8: 0x1008FE08,
XF86XK_Switch_VT_9: 0x1008FE09,
XF86XK_Switch_VT_10: 0x1008FE0A,
XF86XK_Switch_VT_11: 0x1008FE0B,
XF86XK_Switch_VT_12: 0x1008FE0C,
XF86XK_Ungrab: 0x1008FE20,
XF86XK_ClearGrab: 0x1008FE21,
XF86XK_Next_VMode: 0x1008FE22,
XF86XK_Prev_VMode: 0x1008FE23,
XF86XK_LogWindowTree: 0x1008FE24,
XF86XK_LogGrabInfo: 0x1008FE25,
};
/* [module] export default KeyTable; */
+21 -685
View File
File diff suppressed because one or more lines are too long
+646 -158
View File
@@ -1,191 +1,679 @@
import KeyTable from "./keysym.js";
import keysyms from "./keysymdef.js";
import vkeys from "./vkeys.js";
import fixedkeys from "./fixedkeys.js";
import DOMKeyTable from "./domkeytable.js";
import * as browser from "../util/browser.js";
/* [module]
* import KeyTable from "./keysym";
* import keysyms from "./keysymdef";
*/
// Get 'KeyboardEvent.code', handling legacy browsers
export function getKeycode(evt) {
// Are we getting proper key identifiers?
// (unfortunately Firefox and Chrome are crappy here and gives
// us an empty string on some platforms, rather than leaving it
// undefined)
if (evt.code) {
// Mozilla isn't fully in sync with the spec yet
switch (evt.code) {
case 'OSLeft': return 'MetaLeft';
case 'OSRight': return 'MetaRight';
}
var KeyboardUtil = {};
return evt.code;
(function() {
"use strict";
function substituteCodepoint(cp) {
// Any Unicode code points which do not have corresponding keysym entries
// can be swapped out for another code point by adding them to this table
var substitutions = {
// {S,s} with comma below -> {S,s} with cedilla
0x218 : 0x15e,
0x219 : 0x15f,
// {T,t} with comma below -> {T,t} with cedilla
0x21a : 0x162,
0x21b : 0x163
};
var sub = substitutions[cp];
return sub ? sub : cp;
}
// The de-facto standard is to use Windows Virtual-Key codes
// in the 'keyCode' field for non-printable characters
if (evt.keyCode in vkeys) {
let code = vkeys[evt.keyCode];
function isMac() {
return navigator && !!(/mac/i).exec(navigator.platform);
}
function isWindows() {
return navigator && !!(/win/i).exec(navigator.platform);
}
function isLinux() {
return navigator && !!(/linux/i).exec(navigator.platform);
}
// macOS has messed up this code for some reason
if (browser.isMac() && (code === 'ContextMenu')) {
code = 'MetaRight';
}
// The keyCode doesn't distinguish between left and right
// for the standard modifiers
if (evt.location === 2) {
switch (code) {
case 'ShiftLeft': return 'ShiftRight';
case 'ControlLeft': return 'ControlRight';
case 'AltLeft': return 'AltRight';
// Return true if a modifier which is not the specified char modifier (and is not shift) is down
function hasShortcutModifier(charModifier, currentModifiers) {
var mods = {};
for (var key in currentModifiers) {
if (parseInt(key) !== KeyTable.XK_Shift_L) {
mods[key] = currentModifiers[key];
}
}
// Nor a bunch of the numpad keys
if (evt.location === 3) {
switch (code) {
case 'Delete': return 'NumpadDecimal';
case 'Insert': return 'Numpad0';
case 'End': return 'Numpad1';
case 'ArrowDown': return 'Numpad2';
case 'PageDown': return 'Numpad3';
case 'ArrowLeft': return 'Numpad4';
case 'ArrowRight': return 'Numpad6';
case 'Home': return 'Numpad7';
case 'ArrowUp': return 'Numpad8';
case 'PageUp': return 'Numpad9';
case 'Enter': return 'NumpadEnter';
var sum = 0;
for (var k in currentModifiers) {
if (mods[k]) {
++sum;
}
}
if (hasCharModifier(charModifier, mods)) {
return sum > charModifier.length;
}
else {
return sum > 0;
}
}
// Return true if the specified char modifier is currently down
function hasCharModifier(charModifier, currentModifiers) {
if (charModifier.length === 0) { return false; }
for (var i = 0; i < charModifier.length; ++i) {
if (!currentModifiers[charModifier[i]]) {
return false;
}
}
return true;
}
// Helper object tracking modifier key state
// and generates fake key events to compensate if it gets out of sync
function ModifierSync(charModifier) {
if (!charModifier) {
if (isMac()) {
// on Mac, Option (AKA Alt) is used as a char modifier
charModifier = [KeyTable.XK_Alt_L];
}
else if (isWindows()) {
// on Windows, Ctrl+Alt is used as a char modifier
charModifier = [KeyTable.XK_Alt_L, KeyTable.XK_Control_L];
}
else if (isLinux()) {
// on Linux, ISO Level 3 Shift (AltGr) is used as a char modifier
charModifier = [KeyTable.XK_ISO_Level3_Shift];
}
else {
charModifier = [];
}
}
return code;
}
var state = {};
state[KeyTable.XK_Control_L] = false;
state[KeyTable.XK_Alt_L] = false;
state[KeyTable.XK_ISO_Level3_Shift] = false;
state[KeyTable.XK_Shift_L] = false;
state[KeyTable.XK_Meta_L] = false;
return 'Unidentified';
}
function sync(evt, keysym) {
var result = [];
function syncKey(keysym) {
return {keysym: keysyms.lookup(keysym), type: state[keysym] ? 'keydown' : 'keyup'};
}
// Get 'KeyboardEvent.key', handling legacy browsers
export function getKey(evt) {
// Are we getting a proper key value?
if (evt.key !== undefined) {
// Mozilla isn't fully in sync with the spec yet
switch (evt.key) {
case 'OS': return 'Meta';
case 'LaunchMyComputer': return 'LaunchApplication1';
case 'LaunchCalculator': return 'LaunchApplication2';
if (evt.ctrlKey !== undefined &&
evt.ctrlKey !== state[KeyTable.XK_Control_L] && keysym !== KeyTable.XK_Control_L) {
state[KeyTable.XK_Control_L] = evt.ctrlKey;
result.push(syncKey(KeyTable.XK_Control_L));
}
if (evt.altKey !== undefined &&
evt.altKey !== state[KeyTable.XK_Alt_L] && keysym !== KeyTable.XK_Alt_L) {
state[KeyTable.XK_Alt_L] = evt.altKey;
result.push(syncKey(KeyTable.XK_Alt_L));
}
if (evt.altGraphKey !== undefined &&
evt.altGraphKey !== state[KeyTable.XK_ISO_Level3_Shift] && keysym !== KeyTable.XK_ISO_Level3_Shift) {
state[KeyTable.XK_ISO_Level3_Shift] = evt.altGraphKey;
result.push(syncKey(KeyTable.XK_ISO_Level3_Shift));
}
if (evt.shiftKey !== undefined &&
evt.shiftKey !== state[KeyTable.XK_Shift_L] && keysym !== KeyTable.XK_Shift_L) {
state[KeyTable.XK_Shift_L] = evt.shiftKey;
result.push(syncKey(KeyTable.XK_Shift_L));
}
if (evt.metaKey !== undefined &&
evt.metaKey !== state[KeyTable.XK_Meta_L] && keysym !== KeyTable.XK_Meta_L) {
state[KeyTable.XK_Meta_L] = evt.metaKey;
result.push(syncKey(KeyTable.XK_Meta_L));
}
return result;
}
function syncKeyEvent(evt, down) {
var obj = getKeysym(evt);
var keysym = obj ? obj.keysym : null;
// first, apply the event itself, if relevant
if (keysym !== null && state[keysym] !== undefined) {
state[keysym] = down;
}
return sync(evt, keysym);
}
// iOS leaks some OS names
switch (evt.key) {
case 'UIKeyInputUpArrow': return 'ArrowUp';
case 'UIKeyInputDownArrow': return 'ArrowDown';
case 'UIKeyInputLeftArrow': return 'ArrowLeft';
case 'UIKeyInputRightArrow': return 'ArrowRight';
case 'UIKeyInputEscape': return 'Escape';
return {
// sync on the appropriate keyboard event
keydown: function(evt) { return syncKeyEvent(evt, true);},
keyup: function(evt) { return syncKeyEvent(evt, false);},
// Call this with a non-keyboard event (such as mouse events) to use its modifier state to synchronize anyway
syncAny: function(evt) { return sync(evt);},
// is a shortcut modifier down?
hasShortcutModifier: function() { return hasShortcutModifier(charModifier, state); },
// if a char modifier is down, return the keys it consists of, otherwise return null
activeCharModifier: function() { return hasCharModifier(charModifier, state) ? charModifier : null; }
};
}
// Get a key ID from a keyboard event
// May be a string or an integer depending on the available properties
function getKey(evt){
if ('keyCode' in evt && 'key' in evt) {
return evt.key + ':' + evt.keyCode;
}
// Broken behaviour in Chrome
if ((evt.key === '\x00') && (evt.code === 'NumpadDecimal')) {
return 'Delete';
else if ('keyCode' in evt) {
return evt.keyCode;
}
else {
return evt.key;
}
return evt.key;
}
// Try to deduce it based on the physical key
const code = getKeycode(evt);
if (code in fixedkeys) {
return fixedkeys[code];
}
// If that failed, then see if we have a printable character
if (evt.charCode) {
return String.fromCharCode(evt.charCode);
}
// At this point we have nothing left to go on
return 'Unidentified';
}
// Get the most reliable keysym value we can get from a key event
export function getKeysym(evt) {
const key = getKey(evt);
if (key === 'Unidentified') {
// Get the most reliable keysym value we can get from a key event
// if char/charCode is available, prefer those, otherwise fall back to key/keyCode/which
function getKeysym(evt){
var codepoint;
if (evt.char && evt.char.length === 1) {
codepoint = evt.char.charCodeAt();
}
else if (evt.charCode) {
codepoint = evt.charCode;
}
else if (evt.keyCode && evt.type === 'keypress') {
// IE10 stores the char code as keyCode, and has no other useful properties
codepoint = evt.keyCode;
}
if (codepoint) {
return keysyms.fromUnicode(substituteCodepoint(codepoint));
}
// we could check evt.key here.
// Legal values are defined in http://www.w3.org/TR/DOM-Level-3-Events/#key-values-list,
// so we "just" need to map them to keysym, but AFAIK this is only available in IE10, which also provides evt.key
// so we don't *need* it yet
if (evt.keyCode) {
return keysyms.lookup(keysymFromKeyCode(evt.keyCode, evt.shiftKey));
}
if (evt.which) {
return keysyms.lookup(keysymFromKeyCode(evt.which, evt.shiftKey));
}
return null;
}
// First look up special keys
if (key in DOMKeyTable) {
let location = evt.location;
// Safari screws up location for the right cmd key
if ((key === 'Meta') && (location === 0)) {
location = 2;
// Given a keycode, try to predict which keysym it might be.
// If the keycode is unknown, null is returned.
function keysymFromKeyCode(keycode, shiftPressed) {
if (typeof(keycode) !== 'number') {
return null;
}
// won't be accurate for azerty
if (keycode >= 0x30 && keycode <= 0x39) {
return keycode; // digit
}
if (keycode >= 0x41 && keycode <= 0x5a) {
// remap to lowercase unless shift is down
return shiftPressed ? keycode : keycode + 32; // A-Z
}
if (keycode >= 0x60 && keycode <= 0x69) {
return KeyTable.XK_KP_0 + (keycode - 0x60); // numpad 0-9
}
// And for Clear
if ((key === 'Clear') && (location === 3)) {
let code = getKeycode(evt);
if (code === 'NumLock') {
location = 0;
}
switch(keycode) {
case 0x20: return KeyTable.XK_space;
case 0x6a: return KeyTable.XK_KP_Multiply;
case 0x6b: return KeyTable.XK_KP_Add;
case 0x6c: return KeyTable.XK_KP_Separator;
case 0x6d: return KeyTable.XK_KP_Subtract;
case 0x6e: return KeyTable.XK_KP_Decimal;
case 0x6f: return KeyTable.XK_KP_Divide;
case 0xbb: return KeyTable.XK_plus;
case 0xbc: return KeyTable.XK_comma;
case 0xbd: return KeyTable.XK_minus;
case 0xbe: return KeyTable.XK_period;
}
if ((location === undefined) || (location > 3)) {
location = 0;
}
// The original Meta key now gets confused with the Windows key
// https://bugs.chromium.org/p/chromium/issues/detail?id=1020141
// https://bugzilla.mozilla.org/show_bug.cgi?id=1232918
if (key === 'Meta') {
let code = getKeycode(evt);
if (code === 'AltLeft') {
return KeyTable.XK_Meta_L;
} else if (code === 'AltRight') {
return KeyTable.XK_Meta_R;
}
}
// macOS has Clear instead of NumLock, but the remote system is
// probably not macOS, so lying here is probably best...
if (key === 'Clear') {
let code = getKeycode(evt);
if (code === 'NumLock') {
return KeyTable.XK_Num_Lock;
}
}
// Windows sends alternating symbols for some keys when using a
// Japanese layout. We have no way of synchronising with the IM
// running on the remote system, so we send some combined keysym
// instead and hope for the best.
if (browser.isWindows()) {
switch (key) {
case 'Zenkaku':
case 'Hankaku':
return KeyTable.XK_Zenkaku_Hankaku;
case 'Romaji':
case 'KanaMode':
return KeyTable.XK_Romaji;
}
}
return DOMKeyTable[key][location];
return nonCharacterKey({keyCode: keycode});
}
// Now we need to look at the Unicode symbol instead
// if the key is a known non-character key (any key which doesn't generate character data)
// return its keysym value. Otherwise return null
function nonCharacterKey(evt) {
// evt.key not implemented yet
if (!evt.keyCode) { return null; }
var keycode = evt.keyCode;
// Special key? (FIXME: Should have been caught earlier)
if (key.length !== 1) {
return null;
if (keycode >= 0x70 && keycode <= 0x87) {
return KeyTable.XK_F1 + keycode - 0x70; // F1-F24
}
switch (keycode) {
case 8 : return KeyTable.XK_BackSpace;
case 13 : return KeyTable.XK_Return;
case 9 : return KeyTable.XK_Tab;
case 27 : return KeyTable.XK_Escape;
case 46 : return KeyTable.XK_Delete;
case 36 : return KeyTable.XK_Home;
case 35 : return KeyTable.XK_End;
case 33 : return KeyTable.XK_Page_Up;
case 34 : return KeyTable.XK_Page_Down;
case 45 : return KeyTable.XK_Insert;
case 37 : return KeyTable.XK_Left;
case 38 : return KeyTable.XK_Up;
case 39 : return KeyTable.XK_Right;
case 40 : return KeyTable.XK_Down;
case 16 : return KeyTable.XK_Shift_L;
case 17 : return KeyTable.XK_Control_L;
case 18 : return KeyTable.XK_Alt_L; // also: Option-key on Mac
case 224 : return KeyTable.XK_Meta_L;
case 225 : return KeyTable.XK_ISO_Level3_Shift; // AltGr
case 91 : return KeyTable.XK_Super_L; // also: Windows-key
case 92 : return KeyTable.XK_Super_R; // also: Windows-key
case 93 : return KeyTable.XK_Menu; // also: Windows-Menu, Command on Mac
default: return null;
}
}
const codepoint = key.charCodeAt();
if (codepoint) {
return keysyms.lookup(codepoint);
KeyboardUtil.hasShortcutModifier = hasShortcutModifier;
KeyboardUtil.hasCharModifier = hasCharModifier;
KeyboardUtil.ModifierSync = ModifierSync;
KeyboardUtil.getKey = getKey;
KeyboardUtil.getKeysym = getKeysym;
KeyboardUtil.keysymFromKeyCode = keysymFromKeyCode;
KeyboardUtil.nonCharacterKey = nonCharacterKey;
KeyboardUtil.substituteCodepoint = substituteCodepoint;
})();
KeyboardUtil.QEMUKeyEventDecoder = function(modifierState, next) {
"use strict";
function sendAll(evts) {
for (var i = 0; i < evts.length; ++i) {
next(evts[i]);
}
}
return null;
}
var numPadCodes = ["Numpad0", "Numpad1", "Numpad2",
"Numpad3", "Numpad4", "Numpad5", "Numpad6",
"Numpad7", "Numpad8", "Numpad9", "NumpadDecimal"];
var numLockOnKeySyms = {
"Numpad0": 0xffb0, "Numpad1": 0xffb1, "Numpad2": 0xffb2,
"Numpad3": 0xffb3, "Numpad4": 0xffb4, "Numpad5": 0xffb5,
"Numpad6": 0xffb6, "Numpad7": 0xffb7, "Numpad8": 0xffb8,
"Numpad9": 0xffb9, "NumpadDecimal": 0xffac
};
var numLockOnKeyCodes = [96, 97, 98, 99, 100, 101, 102,
103, 104, 105, 108, 110];
function isNumPadMultiKey(evt) {
return (numPadCodes.indexOf(evt.code) !== -1);
}
function getNumPadKeySym(evt) {
if (numLockOnKeyCodes.indexOf(evt.keyCode) !== -1) {
return numLockOnKeySyms[evt.code];
}
return 0;
}
function process(evt, type) {
var result = {type: type};
result.code = evt.code;
result.keysym = 0;
if (isNumPadMultiKey(evt)) {
result.keysym = getNumPadKeySym(evt);
}
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
var isShift = evt.keyCode === 0x10 || evt.key === 'Shift';
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!KeyboardUtil.nonCharacterKey(evt));
next(result);
return suppress;
}
return {
keydown: function(evt) {
sendAll(modifierState.keydown(evt));
return process(evt, 'keydown');
},
keypress: function(evt) {
return true;
},
keyup: function(evt) {
sendAll(modifierState.keyup(evt));
return process(evt, 'keyup');
},
syncModifiers: function(evt) {
sendAll(modifierState.syncAny(evt));
},
releaseAll: function() { next({type: 'releaseall'}); }
};
};
KeyboardUtil.TrackQEMUKeyState = function(next) {
"use strict";
var state = [];
return function (evt) {
var last = state.length !== 0 ? state[state.length-1] : null;
switch (evt.type) {
case 'keydown':
if (!last || last.code !== evt.code) {
last = {code: evt.code};
if (state.length > 0 && state[state.length-1].code == 'ControlLeft') {
if (evt.code !== 'AltRight') {
next({code: 'ControlLeft', type: 'keydown', keysym: 0});
} else {
state.pop();
}
}
state.push(last);
}
if (evt.code !== 'ControlLeft') {
next(evt);
}
break;
case 'keyup':
if (state.length === 0) {
return;
}
var idx = null;
// do we have a matching key tracked as being down?
for (var i = 0; i !== state.length; ++i) {
if (state[i].code === evt.code) {
idx = i;
break;
}
}
// if we couldn't find a match (it happens), assume it was the last key pressed
if (idx === null) {
if (evt.code === 'ControlLeft') {
return;
}
idx = state.length - 1;
}
state.splice(idx, 1);
next(evt);
break;
case 'releaseall':
/* jshint shadow: true */
for (var i = 0; i < state.length; ++i) {
next({code: state[i].code, keysym: 0, type: 'keyup'});
}
/* jshint shadow: false */
state = [];
}
};
};
// Takes a DOM keyboard event and:
// - determines which keysym it represents
// - determines a keyId identifying the key that was pressed (corresponding to the key/keyCode properties on the DOM event)
// - synthesizes events to synchronize modifier key state between which modifiers are actually down, and which we thought were down
// - marks each event with an 'escape' property if a modifier was down which should be "escaped"
// - generates a "stall" event in cases where it might be necessary to wait and see if a keypress event follows a keydown
// This information is collected into an object which is passed to the next() function. (one call per event)
KeyboardUtil.KeyEventDecoder = function(modifierState, next) {
"use strict";
function sendAll(evts) {
for (var i = 0; i < evts.length; ++i) {
next(evts[i]);
}
}
function process(evt, type) {
var result = {type: type};
var keyId = KeyboardUtil.getKey(evt);
if (keyId) {
result.keyId = keyId;
}
var keysym = KeyboardUtil.getKeysym(evt);
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
// Is this a case where we have to decide on the keysym right away, rather than waiting for the keypress?
// "special" keys like enter, tab or backspace don't send keypress events,
// and some browsers don't send keypresses at all if a modifier is down
if (keysym && (type !== 'keydown' || KeyboardUtil.nonCharacterKey(evt) || hasModifier)) {
result.keysym = keysym;
}
var isShift = evt.keyCode === 0x10 || evt.key === 'Shift';
// Should we prevent the browser from handling the event?
// Doing so on a keydown (in most browsers) prevents keypress from being generated
// so only do that if we have to.
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!KeyboardUtil.nonCharacterKey(evt));
// If a char modifier is down on a keydown, we need to insert a stall,
// so VerifyCharModifier knows to wait and see if a keypress is comnig
var stall = type === 'keydown' && modifierState.activeCharModifier() && !KeyboardUtil.nonCharacterKey(evt);
// if a char modifier is pressed, get the keys it consists of (on Windows, AltGr is equivalent to Ctrl+Alt)
var active = modifierState.activeCharModifier();
// If we have a char modifier down, and we're able to determine a keysym reliably
// then (a) we know to treat the modifier as a char modifier,
// and (b) we'll have to "escape" the modifier to undo the modifier when sending the char.
if (active && keysym) {
var isCharModifier = false;
for (var i = 0; i < active.length; ++i) {
if (active[i] === keysym.keysym) {
isCharModifier = true;
}
}
if (type === 'keypress' && !isCharModifier) {
result.escape = modifierState.activeCharModifier();
}
}
if (stall) {
// insert a fake "stall" event
next({type: 'stall'});
}
next(result);
return suppress;
}
return {
keydown: function(evt) {
sendAll(modifierState.keydown(evt));
return process(evt, 'keydown');
},
keypress: function(evt) {
return process(evt, 'keypress');
},
keyup: function(evt) {
sendAll(modifierState.keyup(evt));
return process(evt, 'keyup');
},
syncModifiers: function(evt) {
sendAll(modifierState.syncAny(evt));
},
releaseAll: function() { next({type: 'releaseall'}); }
};
};
// Combines keydown and keypress events where necessary to handle char modifiers.
// On some OS'es, a char modifier is sometimes used as a shortcut modifier.
// For example, on Windows, AltGr is synonymous with Ctrl-Alt. On a Danish keyboard layout, AltGr-2 yields a @, but Ctrl-Alt-D does nothing
// so when used with the '2' key, Ctrl-Alt counts as a char modifier (and should be escaped), but when used with 'D', it does not.
// The only way we can distinguish these cases is to wait and see if a keypress event arrives
// When we receive a "stall" event, wait a few ms before processing the next keydown. If a keypress has also arrived, merge the two
KeyboardUtil.VerifyCharModifier = function(next) {
"use strict";
var queue = [];
var timer = null;
function process() {
if (timer) {
return;
}
var delayProcess = function () {
clearTimeout(timer);
timer = null;
process();
};
while (queue.length !== 0) {
var cur = queue[0];
queue = queue.splice(1);
switch (cur.type) {
case 'stall':
// insert a delay before processing available events.
/* jshint loopfunc: true */
timer = setTimeout(delayProcess, 5);
/* jshint loopfunc: false */
return;
case 'keydown':
// is the next element a keypress? Then we should merge the two
if (queue.length !== 0 && queue[0].type === 'keypress') {
// Firefox sends keypress even when no char is generated.
// so, if keypress keysym is the same as we'd have guessed from keydown,
// the modifier didn't have any effect, and should not be escaped
if (queue[0].escape && (!cur.keysym || cur.keysym.keysym !== queue[0].keysym.keysym)) {
cur.escape = queue[0].escape;
}
cur.keysym = queue[0].keysym;
queue = queue.splice(1);
}
break;
}
// swallow stall events, and pass all others to the next stage
if (cur.type !== 'stall') {
next(cur);
}
}
}
return function(evt) {
queue.push(evt);
process();
};
};
// Keeps track of which keys we (and the server) believe are down
// When a keyup is received, match it against this list, to determine the corresponding keysym(s)
// in some cases, a single key may produce multiple keysyms, so the corresponding keyup event must release all of these chars
// key repeat events should be merged into a single entry.
// Because we can't always identify which entry a keydown or keyup event corresponds to, we sometimes have to guess
KeyboardUtil.TrackKeyState = function(next) {
"use strict";
var state = [];
return function (evt) {
var last = state.length !== 0 ? state[state.length-1] : null;
switch (evt.type) {
case 'keydown':
// insert a new entry if last seen key was different.
if (!last || !evt.keyId || last.keyId !== evt.keyId) {
last = {keyId: evt.keyId, keysyms: {}};
state.push(last);
}
if (evt.keysym) {
// make sure last event contains this keysym (a single "logical" keyevent
// can cause multiple key events to be sent to the VNC server)
last.keysyms[evt.keysym.keysym] = evt.keysym;
last.ignoreKeyPress = true;
next(evt);
}
break;
case 'keypress':
if (!last) {
last = {keyId: evt.keyId, keysyms: {}};
state.push(last);
}
if (!evt.keysym) {
console.log('keypress with no keysym:', evt);
}
// If we didn't expect a keypress, and already sent a keydown to the VNC server
// based on the keydown, make sure to skip this event.
if (evt.keysym && !last.ignoreKeyPress) {
last.keysyms[evt.keysym.keysym] = evt.keysym;
evt.type = 'keydown';
next(evt);
}
break;
case 'keyup':
if (state.length === 0) {
return;
}
var idx = null;
// do we have a matching key tracked as being down?
for (var i = 0; i !== state.length; ++i) {
if (state[i].keyId === evt.keyId) {
idx = i;
break;
}
}
// if we couldn't find a match (it happens), assume it was the last key pressed
if (idx === null) {
idx = state.length - 1;
}
var item = state.splice(idx, 1)[0];
// for each keysym tracked by this key entry, clone the current event and override the keysym
var clone = (function(){
function Clone(){}
return function (obj) { Clone.prototype=obj; return new Clone(); };
}());
for (var key in item.keysyms) {
var out = clone(evt);
out.keysym = item.keysyms[key];
next(out);
}
break;
case 'releaseall':
/* jshint shadow: true */
for (var i = 0; i < state.length; ++i) {
for (var key in state[i].keysyms) {
var keysym = state[i].keysyms[key];
next({keyId: 0, keysym: keysym, type: 'keyup'});
}
}
/* jshint shadow: false */
state = [];
}
};
};
// Handles "escaping" of modifiers: if a char modifier is used to produce a keysym (such as AltGr-2 to generate an @),
// then the modifier must be "undone" before sending the @, and "redone" afterwards.
KeyboardUtil.EscapeModifiers = function(next) {
"use strict";
return function(evt) {
if (evt.type !== 'keydown' || evt.escape === undefined) {
next(evt);
return;
}
// undo modifiers
for (var i = 0; i < evt.escape.length; ++i) {
next({type: 'keyup', keyId: 0, keysym: keysyms.lookup(evt.escape[i])});
}
// send the character event
next(evt);
// redo modifiers
/* jshint shadow: true */
for (var i = 0; i < evt.escape.length; ++i) {
next({type: 'keydown', keyId: 0, keysym: keysyms.lookup(evt.escape[i])});
}
/* jshint shadow: false */
};
};
/* [module] export default KeyboardUtil; */
-116
View File
@@ -1,116 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/
/*
* Mapping between Microsoft® Windows® Virtual-Key codes and
* HTML key codes.
*/
export default {
0x08: 'Backspace',
0x09: 'Tab',
0x0a: 'NumpadClear',
0x0d: 'Enter',
0x10: 'ShiftLeft',
0x11: 'ControlLeft',
0x12: 'AltLeft',
0x13: 'Pause',
0x14: 'CapsLock',
0x15: 'Lang1',
0x19: 'Lang2',
0x1b: 'Escape',
0x1c: 'Convert',
0x1d: 'NonConvert',
0x20: 'Space',
0x21: 'PageUp',
0x22: 'PageDown',
0x23: 'End',
0x24: 'Home',
0x25: 'ArrowLeft',
0x26: 'ArrowUp',
0x27: 'ArrowRight',
0x28: 'ArrowDown',
0x29: 'Select',
0x2c: 'PrintScreen',
0x2d: 'Insert',
0x2e: 'Delete',
0x2f: 'Help',
0x30: 'Digit0',
0x31: 'Digit1',
0x32: 'Digit2',
0x33: 'Digit3',
0x34: 'Digit4',
0x35: 'Digit5',
0x36: 'Digit6',
0x37: 'Digit7',
0x38: 'Digit8',
0x39: 'Digit9',
0x5b: 'MetaLeft',
0x5c: 'MetaRight',
0x5d: 'ContextMenu',
0x5f: 'Sleep',
0x60: 'Numpad0',
0x61: 'Numpad1',
0x62: 'Numpad2',
0x63: 'Numpad3',
0x64: 'Numpad4',
0x65: 'Numpad5',
0x66: 'Numpad6',
0x67: 'Numpad7',
0x68: 'Numpad8',
0x69: 'Numpad9',
0x6a: 'NumpadMultiply',
0x6b: 'NumpadAdd',
0x6c: 'NumpadDecimal',
0x6d: 'NumpadSubtract',
0x6e: 'NumpadDecimal', // Duplicate, because buggy on Windows
0x6f: 'NumpadDivide',
0x70: 'F1',
0x71: 'F2',
0x72: 'F3',
0x73: 'F4',
0x74: 'F5',
0x75: 'F6',
0x76: 'F7',
0x77: 'F8',
0x78: 'F9',
0x79: 'F10',
0x7a: 'F11',
0x7b: 'F12',
0x7c: 'F13',
0x7d: 'F14',
0x7e: 'F15',
0x7f: 'F16',
0x80: 'F17',
0x81: 'F18',
0x82: 'F19',
0x83: 'F20',
0x84: 'F21',
0x85: 'F22',
0x86: 'F23',
0x87: 'F24',
0x90: 'NumLock',
0x91: 'ScrollLock',
0xa6: 'BrowserBack',
0xa7: 'BrowserForward',
0xa8: 'BrowserRefresh',
0xa9: 'BrowserStop',
0xaa: 'BrowserSearch',
0xab: 'BrowserFavorites',
0xac: 'BrowserHome',
0xad: 'AudioVolumeMute',
0xae: 'AudioVolumeDown',
0xaf: 'AudioVolumeUp',
0xb0: 'MediaTrackNext',
0xb1: 'MediaTrackPrevious',
0xb2: 'MediaStop',
0xb3: 'MediaPlayPause',
0xb4: 'LaunchMail',
0xb5: 'MediaSelect',
0xb6: 'LaunchApp1',
0xb7: 'LaunchApp2',
0xe1: 'AltRight', // Only when it is AltGraph
};
+150 -172
View File
@@ -1,173 +1,151 @@
/*
* This file is auto-generated from keymaps.csv
* Database checksum sha256(76d68c10e97d37fe2ea459e210125ae41796253fb217e900bf2983ade13a7920)
* To re-generate, run:
* keymap-gen code-map --lang=js keymaps.csv html atset1
*/
export default {
"Again": 0xe005, /* html:Again (Again) -> linux:129 (KEY_AGAIN) -> atset1:57349 */
"AltLeft": 0x38, /* html:AltLeft (AltLeft) -> linux:56 (KEY_LEFTALT) -> atset1:56 */
"AltRight": 0xe038, /* html:AltRight (AltRight) -> linux:100 (KEY_RIGHTALT) -> atset1:57400 */
"ArrowDown": 0xe050, /* html:ArrowDown (ArrowDown) -> linux:108 (KEY_DOWN) -> atset1:57424 */
"ArrowLeft": 0xe04b, /* html:ArrowLeft (ArrowLeft) -> linux:105 (KEY_LEFT) -> atset1:57419 */
"ArrowRight": 0xe04d, /* html:ArrowRight (ArrowRight) -> linux:106 (KEY_RIGHT) -> atset1:57421 */
"ArrowUp": 0xe048, /* html:ArrowUp (ArrowUp) -> linux:103 (KEY_UP) -> atset1:57416 */
"AudioVolumeDown": 0xe02e, /* html:AudioVolumeDown (AudioVolumeDown) -> linux:114 (KEY_VOLUMEDOWN) -> atset1:57390 */
"AudioVolumeMute": 0xe020, /* html:AudioVolumeMute (AudioVolumeMute) -> linux:113 (KEY_MUTE) -> atset1:57376 */
"AudioVolumeUp": 0xe030, /* html:AudioVolumeUp (AudioVolumeUp) -> linux:115 (KEY_VOLUMEUP) -> atset1:57392 */
"Backquote": 0x29, /* html:Backquote (Backquote) -> linux:41 (KEY_GRAVE) -> atset1:41 */
"Backslash": 0x2b, /* html:Backslash (Backslash) -> linux:43 (KEY_BACKSLASH) -> atset1:43 */
"Backspace": 0xe, /* html:Backspace (Backspace) -> linux:14 (KEY_BACKSPACE) -> atset1:14 */
"BracketLeft": 0x1a, /* html:BracketLeft (BracketLeft) -> linux:26 (KEY_LEFTBRACE) -> atset1:26 */
"BracketRight": 0x1b, /* html:BracketRight (BracketRight) -> linux:27 (KEY_RIGHTBRACE) -> atset1:27 */
"BrowserBack": 0xe06a, /* html:BrowserBack (BrowserBack) -> linux:158 (KEY_BACK) -> atset1:57450 */
"BrowserFavorites": 0xe066, /* html:BrowserFavorites (BrowserFavorites) -> linux:156 (KEY_BOOKMARKS) -> atset1:57446 */
"BrowserForward": 0xe069, /* html:BrowserForward (BrowserForward) -> linux:159 (KEY_FORWARD) -> atset1:57449 */
"BrowserHome": 0xe032, /* html:BrowserHome (BrowserHome) -> linux:172 (KEY_HOMEPAGE) -> atset1:57394 */
"BrowserRefresh": 0xe067, /* html:BrowserRefresh (BrowserRefresh) -> linux:173 (KEY_REFRESH) -> atset1:57447 */
"BrowserSearch": 0xe065, /* html:BrowserSearch (BrowserSearch) -> linux:217 (KEY_SEARCH) -> atset1:57445 */
"BrowserStop": 0xe068, /* html:BrowserStop (BrowserStop) -> linux:128 (KEY_STOP) -> atset1:57448 */
"CapsLock": 0x3a, /* html:CapsLock (CapsLock) -> linux:58 (KEY_CAPSLOCK) -> atset1:58 */
"Comma": 0x33, /* html:Comma (Comma) -> linux:51 (KEY_COMMA) -> atset1:51 */
"ContextMenu": 0xe05d, /* html:ContextMenu (ContextMenu) -> linux:127 (KEY_COMPOSE) -> atset1:57437 */
"ControlLeft": 0x1d, /* html:ControlLeft (ControlLeft) -> linux:29 (KEY_LEFTCTRL) -> atset1:29 */
"ControlRight": 0xe01d, /* html:ControlRight (ControlRight) -> linux:97 (KEY_RIGHTCTRL) -> atset1:57373 */
"Convert": 0x79, /* html:Convert (Convert) -> linux:92 (KEY_HENKAN) -> atset1:121 */
"Copy": 0xe078, /* html:Copy (Copy) -> linux:133 (KEY_COPY) -> atset1:57464 */
"Cut": 0xe03c, /* html:Cut (Cut) -> linux:137 (KEY_CUT) -> atset1:57404 */
"Delete": 0xe053, /* html:Delete (Delete) -> linux:111 (KEY_DELETE) -> atset1:57427 */
"Digit0": 0xb, /* html:Digit0 (Digit0) -> linux:11 (KEY_0) -> atset1:11 */
"Digit1": 0x2, /* html:Digit1 (Digit1) -> linux:2 (KEY_1) -> atset1:2 */
"Digit2": 0x3, /* html:Digit2 (Digit2) -> linux:3 (KEY_2) -> atset1:3 */
"Digit3": 0x4, /* html:Digit3 (Digit3) -> linux:4 (KEY_3) -> atset1:4 */
"Digit4": 0x5, /* html:Digit4 (Digit4) -> linux:5 (KEY_4) -> atset1:5 */
"Digit5": 0x6, /* html:Digit5 (Digit5) -> linux:6 (KEY_5) -> atset1:6 */
"Digit6": 0x7, /* html:Digit6 (Digit6) -> linux:7 (KEY_6) -> atset1:7 */
"Digit7": 0x8, /* html:Digit7 (Digit7) -> linux:8 (KEY_7) -> atset1:8 */
"Digit8": 0x9, /* html:Digit8 (Digit8) -> linux:9 (KEY_8) -> atset1:9 */
"Digit9": 0xa, /* html:Digit9 (Digit9) -> linux:10 (KEY_9) -> atset1:10 */
"Eject": 0xe07d, /* html:Eject (Eject) -> linux:162 (KEY_EJECTCLOSECD) -> atset1:57469 */
"End": 0xe04f, /* html:End (End) -> linux:107 (KEY_END) -> atset1:57423 */
"Enter": 0x1c, /* html:Enter (Enter) -> linux:28 (KEY_ENTER) -> atset1:28 */
"Equal": 0xd, /* html:Equal (Equal) -> linux:13 (KEY_EQUAL) -> atset1:13 */
"Escape": 0x1, /* html:Escape (Escape) -> linux:1 (KEY_ESC) -> atset1:1 */
"F1": 0x3b, /* html:F1 (F1) -> linux:59 (KEY_F1) -> atset1:59 */
"F10": 0x44, /* html:F10 (F10) -> linux:68 (KEY_F10) -> atset1:68 */
"F11": 0x57, /* html:F11 (F11) -> linux:87 (KEY_F11) -> atset1:87 */
"F12": 0x58, /* html:F12 (F12) -> linux:88 (KEY_F12) -> atset1:88 */
"F13": 0x5d, /* html:F13 (F13) -> linux:183 (KEY_F13) -> atset1:93 */
"F14": 0x5e, /* html:F14 (F14) -> linux:184 (KEY_F14) -> atset1:94 */
"F15": 0x5f, /* html:F15 (F15) -> linux:185 (KEY_F15) -> atset1:95 */
"F16": 0x55, /* html:F16 (F16) -> linux:186 (KEY_F16) -> atset1:85 */
"F17": 0xe003, /* html:F17 (F17) -> linux:187 (KEY_F17) -> atset1:57347 */
"F18": 0xe077, /* html:F18 (F18) -> linux:188 (KEY_F18) -> atset1:57463 */
"F19": 0xe004, /* html:F19 (F19) -> linux:189 (KEY_F19) -> atset1:57348 */
"F2": 0x3c, /* html:F2 (F2) -> linux:60 (KEY_F2) -> atset1:60 */
"F20": 0x5a, /* html:F20 (F20) -> linux:190 (KEY_F20) -> atset1:90 */
"F21": 0x74, /* html:F21 (F21) -> linux:191 (KEY_F21) -> atset1:116 */
"F22": 0xe079, /* html:F22 (F22) -> linux:192 (KEY_F22) -> atset1:57465 */
"F23": 0x6d, /* html:F23 (F23) -> linux:193 (KEY_F23) -> atset1:109 */
"F24": 0x6f, /* html:F24 (F24) -> linux:194 (KEY_F24) -> atset1:111 */
"F3": 0x3d, /* html:F3 (F3) -> linux:61 (KEY_F3) -> atset1:61 */
"F4": 0x3e, /* html:F4 (F4) -> linux:62 (KEY_F4) -> atset1:62 */
"F5": 0x3f, /* html:F5 (F5) -> linux:63 (KEY_F5) -> atset1:63 */
"F6": 0x40, /* html:F6 (F6) -> linux:64 (KEY_F6) -> atset1:64 */
"F7": 0x41, /* html:F7 (F7) -> linux:65 (KEY_F7) -> atset1:65 */
"F8": 0x42, /* html:F8 (F8) -> linux:66 (KEY_F8) -> atset1:66 */
"F9": 0x43, /* html:F9 (F9) -> linux:67 (KEY_F9) -> atset1:67 */
"Find": 0xe041, /* html:Find (Find) -> linux:136 (KEY_FIND) -> atset1:57409 */
"Help": 0xe075, /* html:Help (Help) -> linux:138 (KEY_HELP) -> atset1:57461 */
"Hiragana": 0x77, /* html:Hiragana (Lang4) -> linux:91 (KEY_HIRAGANA) -> atset1:119 */
"Home": 0xe047, /* html:Home (Home) -> linux:102 (KEY_HOME) -> atset1:57415 */
"Insert": 0xe052, /* html:Insert (Insert) -> linux:110 (KEY_INSERT) -> atset1:57426 */
"IntlBackslash": 0x56, /* html:IntlBackslash (IntlBackslash) -> linux:86 (KEY_102ND) -> atset1:86 */
"IntlRo": 0x73, /* html:IntlRo (IntlRo) -> linux:89 (KEY_RO) -> atset1:115 */
"IntlYen": 0x7d, /* html:IntlYen (IntlYen) -> linux:124 (KEY_YEN) -> atset1:125 */
"KanaMode": 0x70, /* html:KanaMode (KanaMode) -> linux:93 (KEY_KATAKANAHIRAGANA) -> atset1:112 */
"Katakana": 0x78, /* html:Katakana (Lang3) -> linux:90 (KEY_KATAKANA) -> atset1:120 */
"KeyA": 0x1e, /* html:KeyA (KeyA) -> linux:30 (KEY_A) -> atset1:30 */
"KeyB": 0x30, /* html:KeyB (KeyB) -> linux:48 (KEY_B) -> atset1:48 */
"KeyC": 0x2e, /* html:KeyC (KeyC) -> linux:46 (KEY_C) -> atset1:46 */
"KeyD": 0x20, /* html:KeyD (KeyD) -> linux:32 (KEY_D) -> atset1:32 */
"KeyE": 0x12, /* html:KeyE (KeyE) -> linux:18 (KEY_E) -> atset1:18 */
"KeyF": 0x21, /* html:KeyF (KeyF) -> linux:33 (KEY_F) -> atset1:33 */
"KeyG": 0x22, /* html:KeyG (KeyG) -> linux:34 (KEY_G) -> atset1:34 */
"KeyH": 0x23, /* html:KeyH (KeyH) -> linux:35 (KEY_H) -> atset1:35 */
"KeyI": 0x17, /* html:KeyI (KeyI) -> linux:23 (KEY_I) -> atset1:23 */
"KeyJ": 0x24, /* html:KeyJ (KeyJ) -> linux:36 (KEY_J) -> atset1:36 */
"KeyK": 0x25, /* html:KeyK (KeyK) -> linux:37 (KEY_K) -> atset1:37 */
"KeyL": 0x26, /* html:KeyL (KeyL) -> linux:38 (KEY_L) -> atset1:38 */
"KeyM": 0x32, /* html:KeyM (KeyM) -> linux:50 (KEY_M) -> atset1:50 */
"KeyN": 0x31, /* html:KeyN (KeyN) -> linux:49 (KEY_N) -> atset1:49 */
"KeyO": 0x18, /* html:KeyO (KeyO) -> linux:24 (KEY_O) -> atset1:24 */
"KeyP": 0x19, /* html:KeyP (KeyP) -> linux:25 (KEY_P) -> atset1:25 */
"KeyQ": 0x10, /* html:KeyQ (KeyQ) -> linux:16 (KEY_Q) -> atset1:16 */
"KeyR": 0x13, /* html:KeyR (KeyR) -> linux:19 (KEY_R) -> atset1:19 */
"KeyS": 0x1f, /* html:KeyS (KeyS) -> linux:31 (KEY_S) -> atset1:31 */
"KeyT": 0x14, /* html:KeyT (KeyT) -> linux:20 (KEY_T) -> atset1:20 */
"KeyU": 0x16, /* html:KeyU (KeyU) -> linux:22 (KEY_U) -> atset1:22 */
"KeyV": 0x2f, /* html:KeyV (KeyV) -> linux:47 (KEY_V) -> atset1:47 */
"KeyW": 0x11, /* html:KeyW (KeyW) -> linux:17 (KEY_W) -> atset1:17 */
"KeyX": 0x2d, /* html:KeyX (KeyX) -> linux:45 (KEY_X) -> atset1:45 */
"KeyY": 0x15, /* html:KeyY (KeyY) -> linux:21 (KEY_Y) -> atset1:21 */
"KeyZ": 0x2c, /* html:KeyZ (KeyZ) -> linux:44 (KEY_Z) -> atset1:44 */
"Lang1": 0x72, /* html:Lang1 (Lang1) -> linux:122 (KEY_HANGEUL) -> atset1:114 */
"Lang2": 0x71, /* html:Lang2 (Lang2) -> linux:123 (KEY_HANJA) -> atset1:113 */
"Lang3": 0x78, /* html:Lang3 (Lang3) -> linux:90 (KEY_KATAKANA) -> atset1:120 */
"Lang4": 0x77, /* html:Lang4 (Lang4) -> linux:91 (KEY_HIRAGANA) -> atset1:119 */
"Lang5": 0x76, /* html:Lang5 (Lang5) -> linux:85 (KEY_ZENKAKUHANKAKU) -> atset1:118 */
"LaunchApp1": 0xe06b, /* html:LaunchApp1 (LaunchApp1) -> linux:157 (KEY_COMPUTER) -> atset1:57451 */
"LaunchApp2": 0xe021, /* html:LaunchApp2 (LaunchApp2) -> linux:140 (KEY_CALC) -> atset1:57377 */
"LaunchMail": 0xe06c, /* html:LaunchMail (LaunchMail) -> linux:155 (KEY_MAIL) -> atset1:57452 */
"MediaPlayPause": 0xe022, /* html:MediaPlayPause (MediaPlayPause) -> linux:164 (KEY_PLAYPAUSE) -> atset1:57378 */
"MediaSelect": 0xe06d, /* html:MediaSelect (MediaSelect) -> linux:226 (KEY_MEDIA) -> atset1:57453 */
"MediaStop": 0xe024, /* html:MediaStop (MediaStop) -> linux:166 (KEY_STOPCD) -> atset1:57380 */
"MediaTrackNext": 0xe019, /* html:MediaTrackNext (MediaTrackNext) -> linux:163 (KEY_NEXTSONG) -> atset1:57369 */
"MediaTrackPrevious": 0xe010, /* html:MediaTrackPrevious (MediaTrackPrevious) -> linux:165 (KEY_PREVIOUSSONG) -> atset1:57360 */
"MetaLeft": 0xe05b, /* html:MetaLeft (MetaLeft) -> linux:125 (KEY_LEFTMETA) -> atset1:57435 */
"MetaRight": 0xe05c, /* html:MetaRight (MetaRight) -> linux:126 (KEY_RIGHTMETA) -> atset1:57436 */
"Minus": 0xc, /* html:Minus (Minus) -> linux:12 (KEY_MINUS) -> atset1:12 */
"NonConvert": 0x7b, /* html:NonConvert (NonConvert) -> linux:94 (KEY_MUHENKAN) -> atset1:123 */
"NumLock": 0x45, /* html:NumLock (NumLock) -> linux:69 (KEY_NUMLOCK) -> atset1:69 */
"Numpad0": 0x52, /* html:Numpad0 (Numpad0) -> linux:82 (KEY_KP0) -> atset1:82 */
"Numpad1": 0x4f, /* html:Numpad1 (Numpad1) -> linux:79 (KEY_KP1) -> atset1:79 */
"Numpad2": 0x50, /* html:Numpad2 (Numpad2) -> linux:80 (KEY_KP2) -> atset1:80 */
"Numpad3": 0x51, /* html:Numpad3 (Numpad3) -> linux:81 (KEY_KP3) -> atset1:81 */
"Numpad4": 0x4b, /* html:Numpad4 (Numpad4) -> linux:75 (KEY_KP4) -> atset1:75 */
"Numpad5": 0x4c, /* html:Numpad5 (Numpad5) -> linux:76 (KEY_KP5) -> atset1:76 */
"Numpad6": 0x4d, /* html:Numpad6 (Numpad6) -> linux:77 (KEY_KP6) -> atset1:77 */
"Numpad7": 0x47, /* html:Numpad7 (Numpad7) -> linux:71 (KEY_KP7) -> atset1:71 */
"Numpad8": 0x48, /* html:Numpad8 (Numpad8) -> linux:72 (KEY_KP8) -> atset1:72 */
"Numpad9": 0x49, /* html:Numpad9 (Numpad9) -> linux:73 (KEY_KP9) -> atset1:73 */
"NumpadAdd": 0x4e, /* html:NumpadAdd (NumpadAdd) -> linux:78 (KEY_KPPLUS) -> atset1:78 */
"NumpadComma": 0x7e, /* html:NumpadComma (NumpadComma) -> linux:121 (KEY_KPCOMMA) -> atset1:126 */
"NumpadDecimal": 0x53, /* html:NumpadDecimal (NumpadDecimal) -> linux:83 (KEY_KPDOT) -> atset1:83 */
"NumpadDivide": 0xe035, /* html:NumpadDivide (NumpadDivide) -> linux:98 (KEY_KPSLASH) -> atset1:57397 */
"NumpadEnter": 0xe01c, /* html:NumpadEnter (NumpadEnter) -> linux:96 (KEY_KPENTER) -> atset1:57372 */
"NumpadEqual": 0x59, /* html:NumpadEqual (NumpadEqual) -> linux:117 (KEY_KPEQUAL) -> atset1:89 */
"NumpadMultiply": 0x37, /* html:NumpadMultiply (NumpadMultiply) -> linux:55 (KEY_KPASTERISK) -> atset1:55 */
"NumpadParenLeft": 0xe076, /* html:NumpadParenLeft (NumpadParenLeft) -> linux:179 (KEY_KPLEFTPAREN) -> atset1:57462 */
"NumpadParenRight": 0xe07b, /* html:NumpadParenRight (NumpadParenRight) -> linux:180 (KEY_KPRIGHTPAREN) -> atset1:57467 */
"NumpadSubtract": 0x4a, /* html:NumpadSubtract (NumpadSubtract) -> linux:74 (KEY_KPMINUS) -> atset1:74 */
"Open": 0x64, /* html:Open (Open) -> linux:134 (KEY_OPEN) -> atset1:100 */
"PageDown": 0xe051, /* html:PageDown (PageDown) -> linux:109 (KEY_PAGEDOWN) -> atset1:57425 */
"PageUp": 0xe049, /* html:PageUp (PageUp) -> linux:104 (KEY_PAGEUP) -> atset1:57417 */
"Paste": 0x65, /* html:Paste (Paste) -> linux:135 (KEY_PASTE) -> atset1:101 */
"Pause": 0xe046, /* html:Pause (Pause) -> linux:119 (KEY_PAUSE) -> atset1:57414 */
"Period": 0x34, /* html:Period (Period) -> linux:52 (KEY_DOT) -> atset1:52 */
"Power": 0xe05e, /* html:Power (Power) -> linux:116 (KEY_POWER) -> atset1:57438 */
"PrintScreen": 0x54, /* html:PrintScreen (PrintScreen) -> linux:99 (KEY_SYSRQ) -> atset1:84 */
"Props": 0xe006, /* html:Props (Props) -> linux:130 (KEY_PROPS) -> atset1:57350 */
"Quote": 0x28, /* html:Quote (Quote) -> linux:40 (KEY_APOSTROPHE) -> atset1:40 */
"ScrollLock": 0x46, /* html:ScrollLock (ScrollLock) -> linux:70 (KEY_SCROLLLOCK) -> atset1:70 */
"Semicolon": 0x27, /* html:Semicolon (Semicolon) -> linux:39 (KEY_SEMICOLON) -> atset1:39 */
"ShiftLeft": 0x2a, /* html:ShiftLeft (ShiftLeft) -> linux:42 (KEY_LEFTSHIFT) -> atset1:42 */
"ShiftRight": 0x36, /* html:ShiftRight (ShiftRight) -> linux:54 (KEY_RIGHTSHIFT) -> atset1:54 */
"Slash": 0x35, /* html:Slash (Slash) -> linux:53 (KEY_SLASH) -> atset1:53 */
"Sleep": 0xe05f, /* html:Sleep (Sleep) -> linux:142 (KEY_SLEEP) -> atset1:57439 */
"Space": 0x39, /* html:Space (Space) -> linux:57 (KEY_SPACE) -> atset1:57 */
"Suspend": 0xe025, /* html:Suspend (Suspend) -> linux:205 (KEY_SUSPEND) -> atset1:57381 */
"Tab": 0xf, /* html:Tab (Tab) -> linux:15 (KEY_TAB) -> atset1:15 */
"Undo": 0xe007, /* html:Undo (Undo) -> linux:131 (KEY_UNDO) -> atset1:57351 */
"WakeUp": 0xe063, /* html:WakeUp (WakeUp) -> linux:143 (KEY_WAKEUP) -> atset1:57443 */
var XtScancode = {
"Escape": 0x0001,
"Digit1": 0x0002,
"Digit2": 0x0003,
"Digit3": 0x0004,
"Digit4": 0x0005,
"Digit5": 0x0006,
"Digit6": 0x0007,
"Digit7": 0x0008,
"Digit8": 0x0009,
"Digit9": 0x000A,
"Digit0": 0x000B,
"Minus": 0x000C,
"Equal": 0x000D,
"Backspace": 0x000E,
"Tab": 0x000F,
"KeyQ": 0x0010,
"KeyW": 0x0011,
"KeyE": 0x0012,
"KeyR": 0x0013,
"KeyT": 0x0014,
"KeyY": 0x0015,
"KeyU": 0x0016,
"KeyI": 0x0017,
"KeyO": 0x0018,
"KeyP": 0x0019,
"BracketLeft": 0x001A,
"BracketRight": 0x001B,
"Enter": 0x001C,
"ControlLeft": 0x001D,
"KeyA": 0x001E,
"KeyS": 0x001F,
"KeyD": 0x0020,
"KeyF": 0x0021,
"KeyG": 0x0022,
"KeyH": 0x0023,
"KeyJ": 0x0024,
"KeyK": 0x0025,
"KeyL": 0x0026,
"Semicolon": 0x0027,
"Quote": 0x0028,
"Backquote": 0x0029,
"ShiftLeft": 0x002A,
"Backslash": 0x002B,
"KeyZ": 0x002C,
"KeyX": 0x002D,
"KeyC": 0x002E,
"KeyV": 0x002F,
"KeyB": 0x0030,
"KeyN": 0x0031,
"KeyM": 0x0032,
"Comma": 0x0033,
"Period": 0x0034,
"Slash": 0x0035,
"ShiftRight": 0x0036,
"NumpadMultiply": 0x0037,
"AltLeft": 0x0038,
"Space": 0x0039,
"CapsLock": 0x003A,
"F1": 0x003B,
"F2": 0x003C,
"F3": 0x003D,
"F4": 0x003E,
"F5": 0x003F,
"F6": 0x0040,
"F7": 0x0041,
"F8": 0x0042,
"F9": 0x0043,
"F10": 0x0044,
"Pause": 0xE045,
"ScrollLock": 0x0046,
"Numpad7": 0x0047,
"Numpad8": 0x0048,
"Numpad9": 0x0049,
"NumpadSubtract": 0x004A,
"Numpad4": 0x004B,
"Numpad5": 0x004C,
"Numpad6": 0x004D,
"NumpadAdd": 0x004E,
"Numpad1": 0x004F,
"Numpad2": 0x0050,
"Numpad3": 0x0051,
"Numpad0": 0x0052,
"NumpadDecimal": 0x0053,
"IntlBackslash": 0x0056,
"F11": 0x0057,
"F12": 0x0058,
"IntlYen": 0x007D,
"MediaTrackPrevious": 0xE010,
"MediaTrackNext": 0xE019,
"NumpadEnter": 0xE01C,
"ControlRight": 0xE01D,
"VolumeMute": 0xE020,
"MediaPlayPause": 0xE022,
"MediaStop": 0xE024,
"VolumeDown": 0xE02E,
"VolumeUp": 0xE030,
"BrowserHome": 0xE032,
"NumpadDivide": 0xE035,
"PrintScreen": 0xE037,
"AltRight": 0xE038,
"NumLock": 0x0045,
"Home": 0xE047,
"ArrowUp": 0xE048,
"PageUp": 0xE049,
"ArrowLeft": 0xE04B,
"ArrowRight": 0xE04D,
"End": 0xE04F,
"ArrowDown": 0xE050,
"PageDown": 0xE051,
"Insert": 0xE052,
"Delete": 0xE053,
"MetaLeft": 0xE05B,
"MetaRight": 0xE05C,
"OSLeft": 0xE05B, // OSLeft and OSRight are kept for compatability since
"OSRight": 0xE05C, // Firefox haven't updated to MetaLeft and MetaRight yet
"ContextMenu": 0xE05D,
"BrowserSearch": 0xE065,
"BrowserFavorites": 0xE066,
"BrowserRefresh": 0xE067,
"BrowserStop": 0xE068,
"BrowserForward": 0xE069,
"BrowserBack": 0xE06A,
"NumpadComma": 0x007E,
"NumpadEqual": 0x0059,
"F13": 0x0064,
"F14": 0x0065,
"F15": 0x0066,
"F16": 0x0067,
"F17": 0x0068,
"F18": 0x0069,
"F19": 0x006A,
"F20": 0x006B,
"F21": 0x006C,
"F22": 0x006D,
"F23": 0x006E,
"F24": 0x0076,
"KanaMode": 0x0070,
"Lang2": 0x0071,
"Lang1": 0x0072,
"IntlRo": 0x0073,
"Convert": 0x0079,
"NonConvert": 0x007B,
"LaunchApp2": 0xE021,
"Power": 0xE05E,
"LaunchApp1": 0xE06B,
"LaunchMail": 0xE06C,
"MediaSelect": 0xE06D,
};
/* [module] export default XtScancode */
+2302 -2839
View File
File diff suppressed because it is too large Load Diff
+614
View File
@@ -0,0 +1,614 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
/* jshint white: false, nonstandard: true */
/*global window, console, document, navigator, ActiveXObject, INCLUDE_URI */
var Util = {};
/*
* ------------------------------------------------------
* Namespaced in Util
* ------------------------------------------------------
*/
/*
* Logging/debug routines
*/
Util._log_level = 'warn';
Util.init_logging = function (level) {
"use strict";
if (typeof level === 'undefined') {
level = Util._log_level;
} else {
Util._log_level = level;
}
Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {};
if (typeof window.console !== "undefined") {
/* jshint -W086 */
switch (level) {
case 'debug':
Util.Debug = console.debug.bind(window.console);
case 'info':
Util.Info = console.info.bind(window.console);
case 'warn':
Util.Warn = console.warn.bind(window.console);
case 'error':
Util.Error = console.error.bind(window.console);
case 'none':
break;
default:
throw new Error("invalid logging type '" + level + "'");
}
/* jshint +W086 */
}
};
Util.get_logging = function () {
return Util._log_level;
};
// Initialize logging level
Util.init_logging();
Util.make_property = function (proto, name, mode, type) {
"use strict";
var getter;
if (type === 'arr') {
getter = function (idx) {
if (typeof idx !== 'undefined') {
return this['_' + name][idx];
} else {
return this['_' + name];
}
};
} else {
getter = function () {
return this['_' + name];
};
}
var make_setter = function (process_val) {
if (process_val) {
return function (val, idx) {
if (typeof idx !== 'undefined') {
this['_' + name][idx] = process_val(val);
} else {
this['_' + name] = process_val(val);
}
};
} else {
return function (val, idx) {
if (typeof idx !== 'undefined') {
this['_' + name][idx] = val;
} else {
this['_' + name] = val;
}
};
}
};
var setter;
if (type === 'bool') {
setter = make_setter(function (val) {
if (!val || (val in {'0': 1, 'no': 1, 'false': 1})) {
return false;
} else {
return true;
}
});
} else if (type === 'int') {
setter = make_setter(function (val) { return parseInt(val, 10); });
} else if (type === 'float') {
setter = make_setter(parseFloat);
} else if (type === 'str') {
setter = make_setter(String);
} else if (type === 'func') {
setter = make_setter(function (val) {
if (!val) {
return function () {};
} else {
return val;
}
});
} else if (type === 'arr' || type === 'dom' || type == 'raw') {
setter = make_setter();
} else {
throw new Error('Unknown property type ' + type); // some sanity checking
}
// set the getter
if (typeof proto['get_' + name] === 'undefined') {
proto['get_' + name] = getter;
}
// set the setter if needed
if (typeof proto['set_' + name] === 'undefined') {
if (mode === 'rw') {
proto['set_' + name] = setter;
} else if (mode === 'wo') {
proto['set_' + name] = function (val, idx) {
if (typeof this['_' + name] !== 'undefined') {
throw new Error(name + " can only be set once");
}
setter.call(this, val, idx);
};
}
}
// make a special setter that we can use in set defaults
proto['_raw_set_' + name] = function (val, idx) {
setter.call(this, val, idx);
//delete this['_init_set_' + name]; // remove it after use
};
};
Util.make_properties = function (constructor, arr) {
"use strict";
for (var i = 0; i < arr.length; i++) {
Util.make_property(constructor.prototype, arr[i][0], arr[i][1], arr[i][2]);
}
};
Util.set_defaults = function (obj, conf, defaults) {
var defaults_keys = Object.keys(defaults);
var conf_keys = Object.keys(conf);
var keys_obj = {};
var i;
for (i = 0; i < defaults_keys.length; i++) { keys_obj[defaults_keys[i]] = 1; }
for (i = 0; i < conf_keys.length; i++) { keys_obj[conf_keys[i]] = 1; }
var keys = Object.keys(keys_obj);
for (i = 0; i < keys.length; i++) {
var setter = obj['_raw_set_' + keys[i]];
if (!setter) {
Util.Warn('Invalid property ' + keys[i]);
continue;
}
if (keys[i] in conf) {
setter.call(obj, conf[keys[i]]);
} else {
setter.call(obj, defaults[keys[i]]);
}
}
};
/*
* Decode from UTF-8
*/
Util.decodeUTF8 = function (utf8string) {
"use strict";
return decodeURIComponent(escape(utf8string));
};
/*
* Cross-browser routines
*/
Util.getPointerEvent = function (e) {
return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e;
};
Util.stopEvent = function (e) {
e.stopPropagation();
e.preventDefault();
};
// Touch detection
Util.isTouchDevice = ('ontouchstart' in document.documentElement) ||
// requried for Chrome debugger
(document.ontouchstart !== undefined) ||
// required for MS Surface
(navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0);
window.addEventListener('touchstart', function onFirstTouch() {
Util.isTouchDevice = true;
window.removeEventListener('touchstart', onFirstTouch, false);
}, false);
Util._cursor_uris_supported = null;
Util.browserSupportsCursorURIs = function () {
if (Util._cursor_uris_supported === null) {
try {
var target = document.createElement('canvas');
target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
if (target.style.cursor) {
Util.Info("Data URI scheme cursor supported");
Util._cursor_uris_supported = true;
} else {
Util.Warn("Data URI scheme cursor not supported");
Util._cursor_uris_supported = false;
}
} catch (exc) {
Util.Error("Data URI scheme cursor test exception: " + exc);
Util._cursor_uris_supported = false;
}
}
return Util._cursor_uris_supported;
};
// Set browser engine versions. Based on mootools.
Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)};
(function () {
"use strict";
// 'presto': (function () { return (!window.opera) ? false : true; }()),
var detectPresto = function () {
return !!window.opera;
};
// 'trident': (function () { return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4);
var detectTrident = function () {
if (!window.ActiveXObject) {
return false;
} else {
if (window.XMLHttpRequest) {
return (document.querySelectorAll) ? 6 : 5;
} else {
return 4;
}
}
};
// 'webkit': (function () { try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()),
var detectInitialWebkit = function () {
try {
if (navigator.taintEnabled) {
return false;
} else {
if (Util.Features.xpath) {
return (Util.Features.query) ? 525 : 420;
} else {
return 419;
}
}
} catch (e) {
return false;
}
};
var detectActualWebkit = function (initial_ver) {
var re = /WebKit\/([0-9\.]*) /;
var str_ver = (navigator.userAgent.match(re) || ['', initial_ver])[1];
return parseFloat(str_ver, 10);
};
// 'gecko': (function () { return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19ssName) ? 19 : 18 : 18); }())
var detectGecko = function () {
/* jshint -W041 */
if (!document.getBoxObjectFor && window.mozInnerScreenX == null) {
return false;
} else {
return (document.getElementsByClassName) ? 19 : 18;
}
/* jshint +W041 */
};
Util.Engine = {
// Version detection break in Opera 11.60 (errors on arguments.callee.caller reference)
//'presto': (function() {
// return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
'presto': detectPresto(),
'trident': detectTrident(),
'webkit': detectInitialWebkit(),
'gecko': detectGecko()
};
if (Util.Engine.webkit) {
// Extract actual webkit version if available
Util.Engine.webkit = detectActualWebkit(Util.Engine.webkit);
}
})();
Util.Flash = (function () {
"use strict";
var v, version;
try {
v = navigator.plugins['Shockwave Flash'].description;
} catch (err1) {
try {
v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
} catch (err2) {
v = '0 r0';
}
}
version = v.match(/\d+/g);
return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
}());
Util.Localisation = {
// Currently configured language
language: 'en',
// Configure suitable language based on user preferences
setup: function (supportedLanguages) {
var userLanguages;
Util.Localisation.language = 'en'; // Default: US English
/*
* Navigator.languages only available in Chrome (32+) and FireFox (32+)
* Fall back to navigator.language for other browsers
*/
if (typeof window.navigator.languages == 'object') {
userLanguages = window.navigator.languages;
} else {
userLanguages = [navigator.language || navigator.userLanguage];
}
for (var i = 0;i < userLanguages.length;i++) {
var userLang = userLanguages[i];
userLang = userLang.toLowerCase();
userLang = userLang.replace("_", "-");
userLang = userLang.split("-");
// Built-in default?
if ((userLang[0] === 'en') &&
((userLang[1] === undefined) || (userLang[1] === 'us'))) {
return;
}
// First pass: perfect match
for (var j = 0;j < supportedLanguages.length;j++) {
var supLang = supportedLanguages[j];
supLang = supLang.toLowerCase();
supLang = supLang.replace("_", "-");
supLang = supLang.split("-");
if (userLang[0] !== supLang[0])
continue;
if (userLang[1] !== supLang[1])
continue;
Util.Localisation.language = supportedLanguages[j];
return;
}
// Second pass: fallback
for (var j = 0;j < supportedLanguages.length;j++) {
supLang = supportedLanguages[j];
supLang = supLang.toLowerCase();
supLang = supLang.replace("_", "-");
supLang = supLang.split("-");
if (userLang[0] !== supLang[0])
continue;
if (supLang[1] !== undefined)
continue;
Util.Localisation.language = supportedLanguages[j];
return;
}
}
},
// Retrieve localised text
get: function (id) {
if (typeof Language !== 'undefined' && Language[id]) {
return Language[id];
} else {
return id;
}
},
// Traverses the DOM and translates relevant fields
// See https://html.spec.whatwg.org/multipage/dom.html#attr-translate
translateDOM: function () {
function process(elem, enabled) {
function isAnyOf(searchElement, items) {
return items.indexOf(searchElement) !== -1;
}
function translateAttribute(elem, attr) {
var str = elem.getAttribute(attr);
str = Util.Localisation.get(str);
elem.setAttribute(attr, str);
}
function translateTextNode(node) {
var str = node.data.trim();
str = Util.Localisation.get(str);
node.data = str;
}
if (elem.hasAttribute("translate")) {
if (isAnyOf(elem.getAttribute("translate"), ["", "yes"])) {
enabled = true;
} else if (isAnyOf(elem.getAttribute("translate"), ["no"])) {
enabled = false;
}
}
if (enabled) {
if (elem.hasAttribute("abbr") &&
elem.tagName === "TH") {
translateAttribute(elem, "abbr");
}
if (elem.hasAttribute("alt") &&
isAnyOf(elem.tagName, ["AREA", "IMG", "INPUT"])) {
translateAttribute(elem, "alt");
}
if (elem.hasAttribute("download") &&
isAnyOf(elem.tagName, ["A", "AREA"])) {
translateAttribute(elem, "download");
}
if (elem.hasAttribute("label") &&
isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP",
"OPTION", "TRACK"])) {
translateAttribute(elem, "label");
}
// FIXME: Should update "lang"
if (elem.hasAttribute("placeholder") &&
isAnyOf(elem.tagName in ["INPUT", "TEXTAREA"])) {
translateAttribute(elem, "placeholder");
}
if (elem.hasAttribute("title")) {
translateAttribute(elem, "title");
}
if (elem.hasAttribute("value") &&
elem.tagName === "INPUT" &&
isAnyOf(elem.getAttribute("type"), ["reset", "button"])) {
translateAttribute(elem, "value");
}
}
for (var i = 0;i < elem.childNodes.length;i++) {
node = elem.childNodes[i];
if (node.nodeType === node.ELEMENT_NODE) {
process(node, enabled);
} else if (node.nodeType === node.TEXT_NODE && enabled) {
translateTextNode(node);
}
}
}
process(document.body, true);
},
};
// Emulate Element.setCapture() when not supported
Util._captureRecursion = false;
Util._captureProxy = function (e) {
// Recursion protection as we'll see our own event
if (Util._captureRecursion) return;
// Clone the event as we cannot dispatch an already dispatched event
var newEv = new e.constructor(e.type, e);
Util._captureRecursion = true;
Util._captureElem.dispatchEvent(newEv);
Util._captureRecursion = false;
// Avoid double events
e.stopPropagation();
// Respect the wishes of the redirected event handlers
if (newEv.defaultPrevented) {
e.preventDefault();
}
// Implicitly release the capture on button release
if ((e.type === "mouseup") || (e.type === "touchend")) {
Util.releaseCapture();
}
};
// Follow cursor style of target element
Util._captureElemChanged = function() {
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
captureElem.style.cursor = window.getComputedStyle(Util._captureElem).cursor;
};
Util._captureObserver = new MutationObserver(Util._captureElemChanged);
Util.setCapture = function (elem) {
if (elem.setCapture) {
elem.setCapture();
// IE releases capture on 'click' events which might not trigger
elem.addEventListener('mouseup', Util.releaseCapture);
elem.addEventListener('touchend', Util.releaseCapture);
} else {
// Release any existing capture in case this method is
// called multiple times without coordination
Util.releaseCapture();
// Safari on iOS 9 has a broken constructor for TouchEvent.
// We are fine in this case however, since Safari seems to
// have some sort of implicit setCapture magic anyway.
if (window.TouchEvent !== undefined) {
try {
new TouchEvent("touchstart");
} catch (TypeError) {
return;
}
}
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
if (captureElem === null) {
captureElem = document.createElement("div");
captureElem.id = "noVNC_mouse_capture_elem";
captureElem.style.position = "fixed";
captureElem.style.top = "0px";
captureElem.style.left = "0px";
captureElem.style.width = "100%";
captureElem.style.height = "100%";
captureElem.style.zIndex = 10000;
captureElem.style.display = "none";
document.body.appendChild(captureElem);
// This is to make sure callers don't get confused by having
// our blocking element as the target
captureElem.addEventListener('contextmenu', Util._captureProxy);
captureElem.addEventListener('mousemove', Util._captureProxy);
captureElem.addEventListener('mouseup', Util._captureProxy);
captureElem.addEventListener('touchmove', Util._captureProxy);
captureElem.addEventListener('touchend', Util._captureProxy);
}
Util._captureElem = elem;
// Track cursor and get initial cursor
Util._captureObserver.observe(elem, {attributes:true});
Util._captureElemChanged();
captureElem.style.display = null;
// We listen to events on window in order to keep tracking if it
// happens to leave the viewport
window.addEventListener('mousemove', Util._captureProxy);
window.addEventListener('mouseup', Util._captureProxy);
window.addEventListener('touchmove', Util._captureProxy);
window.addEventListener('touchend', Util._captureProxy);
}
};
Util.releaseCapture = function () {
if (document.releaseCapture) {
document.releaseCapture();
} else {
if (!Util._captureElem) {
return;
}
// There might be events already queued, so we need to wait for
// them to flush. E.g. contextmenu in Microsoft Edge
//
// FIXME: What happens if setCapture is called before this fires?
window.setTimeout(function() { Util._captureElem = null; });
Util._captureObserver.disconnect();
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
captureElem.style.display = "none";
window.removeEventListener('mousemove', Util._captureProxy);
window.removeEventListener('mouseup', Util._captureProxy);
window.removeEventListener('touchmove', Util._captureProxy);
window.removeEventListener('touchend', Util._captureProxy);
}
};
/* [module] export default Util; */
-103
View File
@@ -1,103 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
* Browser feature support detection
*/
import * as Log from './logging.js';
// Touch detection
export let isTouchDevice = ('ontouchstart' in document.documentElement) ||
// requried for Chrome debugger
(document.ontouchstart !== undefined) ||
// required for MS Surface
(navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0);
window.addEventListener('touchstart', function onFirstTouch() {
isTouchDevice = true;
window.removeEventListener('touchstart', onFirstTouch, false);
}, false);
// The goal is to find a certain physical width, the devicePixelRatio
// brings us a bit closer but is not optimal.
export let dragThreshold = 10 * (window.devicePixelRatio || 1);
let _supportsCursorURIs = false;
try {
const target = document.createElement('canvas');
target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
if (target.style.cursor.indexOf("url") === 0) {
Log.Info("Data URI scheme cursor supported");
_supportsCursorURIs = true;
} else {
Log.Warn("Data URI scheme cursor not supported");
}
} catch (exc) {
Log.Error("Data URI scheme cursor test exception: " + exc);
}
export const supportsCursorURIs = _supportsCursorURIs;
let _hasScrollbarGutter = true;
try {
// Create invisible container
const container = document.createElement('div');
container.style.visibility = 'hidden';
container.style.overflow = 'scroll'; // forcing scrollbars
document.body.appendChild(container);
// Create a div and place it in the container
const child = document.createElement('div');
container.appendChild(child);
// Calculate the difference between the container's full width
// and the child's width - the difference is the scrollbars
const scrollbarWidth = (container.offsetWidth - child.offsetWidth);
// Clean up
container.parentNode.removeChild(container);
_hasScrollbarGutter = scrollbarWidth != 0;
} catch (exc) {
Log.Error("Scrollbar test exception: " + exc);
}
export const hasScrollbarGutter = _hasScrollbarGutter;
/*
* The functions for detection of platforms and browsers below are exported
* but the use of these should be minimized as much as possible.
*
* It's better to use feature detection than platform detection.
*/
export function isMac() {
return navigator && !!(/mac/i).exec(navigator.platform);
}
export function isWindows() {
return navigator && !!(/win/i).exec(navigator.platform);
}
export function isIOS() {
return navigator &&
(!!(/ipad/i).exec(navigator.platform) ||
!!(/iphone/i).exec(navigator.platform) ||
!!(/ipod/i).exec(navigator.platform));
}
export function isSafari() {
return navigator && (navigator.userAgent.indexOf('Safari') !== -1 &&
navigator.userAgent.indexOf('Chrome') === -1);
}
export function isFirefox() {
return navigator && !!(/firefox/i).exec(navigator.userAgent);
}
-243
View File
@@ -1,243 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/
import { supportsCursorURIs, isTouchDevice } from './browser.js';
const useFallback = !supportsCursorURIs || isTouchDevice;
export default class Cursor {
constructor() {
this._target = null;
this._canvas = document.createElement('canvas');
if (useFallback) {
this._canvas.style.position = 'fixed';
this._canvas.style.zIndex = '65535';
this._canvas.style.pointerEvents = 'none';
// Can't use "display" because of Firefox bug #1445997
this._canvas.style.visibility = 'hidden';
}
this._position = { x: 0, y: 0 };
this._hotSpot = { x: 0, y: 0 };
this._eventHandlers = {
'mouseover': this._handleMouseOver.bind(this),
'mouseleave': this._handleMouseLeave.bind(this),
'mousemove': this._handleMouseMove.bind(this),
'mouseup': this._handleMouseUp.bind(this),
};
}
attach(target) {
if (this._target) {
this.detach();
}
this._target = target;
if (useFallback) {
document.body.appendChild(this._canvas);
const options = { capture: true, passive: true };
this._target.addEventListener('mouseover', this._eventHandlers.mouseover, options);
this._target.addEventListener('mouseleave', this._eventHandlers.mouseleave, options);
this._target.addEventListener('mousemove', this._eventHandlers.mousemove, options);
this._target.addEventListener('mouseup', this._eventHandlers.mouseup, options);
}
this.clear();
}
detach() {
if (!this._target) {
return;
}
if (useFallback) {
const options = { capture: true, passive: true };
this._target.removeEventListener('mouseover', this._eventHandlers.mouseover, options);
this._target.removeEventListener('mouseleave', this._eventHandlers.mouseleave, options);
this._target.removeEventListener('mousemove', this._eventHandlers.mousemove, options);
this._target.removeEventListener('mouseup', this._eventHandlers.mouseup, options);
document.body.removeChild(this._canvas);
}
this._target = null;
}
change(rgba, hotx, hoty, w, h) {
if ((w === 0) || (h === 0)) {
this.clear();
return;
}
this._position.x = this._position.x + this._hotSpot.x - hotx;
this._position.y = this._position.y + this._hotSpot.y - hoty;
this._hotSpot.x = hotx;
this._hotSpot.y = hoty;
let ctx = this._canvas.getContext('2d');
this._canvas.width = w;
this._canvas.height = h;
let img = new ImageData(new Uint8ClampedArray(rgba), w, h);
ctx.clearRect(0, 0, w, h);
ctx.putImageData(img, 0, 0);
if (useFallback) {
this._updatePosition();
} else {
let url = this._canvas.toDataURL();
this._target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
}
}
clear() {
this._target.style.cursor = 'none';
this._canvas.width = 0;
this._canvas.height = 0;
this._position.x = this._position.x + this._hotSpot.x;
this._position.y = this._position.y + this._hotSpot.y;
this._hotSpot.x = 0;
this._hotSpot.y = 0;
}
// Mouse events might be emulated, this allows
// moving the cursor in such cases
move(clientX, clientY) {
if (!useFallback) {
return;
}
// clientX/clientY are relative the _visual viewport_,
// but our position is relative the _layout viewport_,
// so try to compensate when we can
if (window.visualViewport) {
this._position.x = clientX + window.visualViewport.offsetLeft;
this._position.y = clientY + window.visualViewport.offsetTop;
} else {
this._position.x = clientX;
this._position.y = clientY;
}
this._updatePosition();
let target = document.elementFromPoint(clientX, clientY);
this._updateVisibility(target);
}
_handleMouseOver(event) {
// This event could be because we're entering the target, or
// moving around amongst its sub elements. Let the move handler
// sort things out.
this._handleMouseMove(event);
}
_handleMouseLeave(event) {
// Check if we should show the cursor on the element we are leaving to
this._updateVisibility(event.relatedTarget);
}
_handleMouseMove(event) {
this._updateVisibility(event.target);
this._position.x = event.clientX - this._hotSpot.x;
this._position.y = event.clientY - this._hotSpot.y;
this._updatePosition();
}
_handleMouseUp(event) {
// We might get this event because of a drag operation that
// moved outside of the target. Check what's under the cursor
// now and adjust visibility based on that.
let target = document.elementFromPoint(event.clientX, event.clientY);
this._updateVisibility(target);
// Captures end with a mouseup but we can't know the event order of
// mouseup vs releaseCapture.
//
// In the cases when releaseCapture comes first, the code above is
// enough.
//
// In the cases when the mouseup comes first, we need wait for the
// browser to flush all events and then check again if the cursor
// should be visible.
if (this._captureIsActive()) {
window.setTimeout(() => {
// We might have detached at this point
if (!this._target) {
return;
}
// Refresh the target from elementFromPoint since queued events
// might have altered the DOM
target = document.elementFromPoint(event.clientX,
event.clientY);
this._updateVisibility(target);
}, 0);
}
}
_showCursor() {
if (this._canvas.style.visibility === 'hidden') {
this._canvas.style.visibility = '';
}
}
_hideCursor() {
if (this._canvas.style.visibility !== 'hidden') {
this._canvas.style.visibility = 'hidden';
}
}
// Should we currently display the cursor?
// (i.e. are we over the target, or a child of the target without a
// different cursor set)
_shouldShowCursor(target) {
if (!target) {
return false;
}
// Easy case
if (target === this._target) {
return true;
}
// Other part of the DOM?
if (!this._target.contains(target)) {
return false;
}
// Has the child its own cursor?
// FIXME: How can we tell that a sub element has an
// explicit "cursor: none;"?
if (window.getComputedStyle(target).cursor !== 'none') {
return false;
}
return true;
}
_updateVisibility(target) {
// When the cursor target has capture we want to show the cursor.
// So, if a capture is active - look at the captured element instead.
if (this._captureIsActive()) {
target = document.captureElement;
}
if (this._shouldShowCursor(target)) {
this._showCursor();
} else {
this._hideCursor();
}
}
_updatePosition() {
this._canvas.style.left = this._position.x + "px";
this._canvas.style.top = this._position.y + "px";
}
_captureIsActive() {
return document.captureElement &&
document.documentElement.contains(document.captureElement);
}
}
-32
View File
@@ -1,32 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2020 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
/*
* HTML element utility functions
*/
export function clientToElement(x, y, elem) {
const bounds = elem.getBoundingClientRect();
let pos = { x: 0, y: 0 };
// Clip to target bounds
if (x < bounds.left) {
pos.x = 0;
} else if (x >= bounds.right) {
pos.x = bounds.width - 1;
} else {
pos.x = x - bounds.left;
}
if (y < bounds.top) {
pos.y = 0;
} else if (y >= bounds.bottom) {
pos.y = bounds.height - 1;
} else {
pos.y = y - bounds.top;
}
return pos;
}
-138
View File
@@ -1,138 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
/*
* Cross-browser event and position routines
*/
export function getPointerEvent(e) {
return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e;
}
export function stopEvent(e) {
e.stopPropagation();
e.preventDefault();
}
// Emulate Element.setCapture() when not supported
let _captureRecursion = false;
let _elementForUnflushedEvents = null;
document.captureElement = null;
function _captureProxy(e) {
// Recursion protection as we'll see our own event
if (_captureRecursion) return;
// Clone the event as we cannot dispatch an already dispatched event
const newEv = new e.constructor(e.type, e);
_captureRecursion = true;
if (document.captureElement) {
document.captureElement.dispatchEvent(newEv);
} else {
_elementForUnflushedEvents.dispatchEvent(newEv);
}
_captureRecursion = false;
// Avoid double events
e.stopPropagation();
// Respect the wishes of the redirected event handlers
if (newEv.defaultPrevented) {
e.preventDefault();
}
// Implicitly release the capture on button release
if (e.type === "mouseup") {
releaseCapture();
}
}
// Follow cursor style of target element
function _capturedElemChanged() {
const proxyElem = document.getElementById("noVNC_mouse_capture_elem");
proxyElem.style.cursor = window.getComputedStyle(document.captureElement).cursor;
}
const _captureObserver = new MutationObserver(_capturedElemChanged);
export function setCapture(target) {
if (target.setCapture) {
target.setCapture();
document.captureElement = target;
} else {
// Release any existing capture in case this method is
// called multiple times without coordination
releaseCapture();
let proxyElem = document.getElementById("noVNC_mouse_capture_elem");
if (proxyElem === null) {
proxyElem = document.createElement("div");
proxyElem.id = "noVNC_mouse_capture_elem";
proxyElem.style.position = "fixed";
proxyElem.style.top = "0px";
proxyElem.style.left = "0px";
proxyElem.style.width = "100%";
proxyElem.style.height = "100%";
proxyElem.style.zIndex = 10000;
proxyElem.style.display = "none";
document.body.appendChild(proxyElem);
// This is to make sure callers don't get confused by having
// our blocking element as the target
proxyElem.addEventListener('contextmenu', _captureProxy);
proxyElem.addEventListener('mousemove', _captureProxy);
proxyElem.addEventListener('mouseup', _captureProxy);
}
document.captureElement = target;
// Track cursor and get initial cursor
_captureObserver.observe(target, {attributes: true});
_capturedElemChanged();
proxyElem.style.display = "";
// We listen to events on window in order to keep tracking if it
// happens to leave the viewport
window.addEventListener('mousemove', _captureProxy);
window.addEventListener('mouseup', _captureProxy);
}
}
export function releaseCapture() {
if (document.releaseCapture) {
document.releaseCapture();
document.captureElement = null;
} else {
if (!document.captureElement) {
return;
}
// There might be events already queued. The event proxy needs
// access to the captured element for these queued events.
// E.g. contextmenu (right-click) in Microsoft Edge
//
// Before removing the capturedElem pointer we save it to a
// temporary variable that the unflushed events can use.
_elementForUnflushedEvents = document.captureElement;
document.captureElement = null;
_captureObserver.disconnect();
const proxyElem = document.getElementById("noVNC_mouse_capture_elem");
proxyElem.style.display = "none";
window.removeEventListener('mousemove', _captureProxy);
window.removeEventListener('mouseup', _captureProxy);
}
}
-35
View File
@@ -1,35 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
export default class EventTargetMixin {
constructor() {
this._listeners = new Map();
}
addEventListener(type, callback) {
if (!this._listeners.has(type)) {
this._listeners.set(type, new Set());
}
this._listeners.get(type).add(callback);
}
removeEventListener(type, callback) {
if (this._listeners.has(type)) {
this._listeners.get(type).delete(callback);
}
}
dispatchEvent(event) {
if (!this._listeners.has(event.type)) {
return true;
}
this._listeners.get(event.type)
.forEach(callback => callback.call(this, event));
return !event.defaultPrevented;
}
}
-15
View File
@@ -1,15 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2020 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
export function toUnsigned32bit(toConvert) {
return toConvert >>> 0;
}
export function toSigned32bit(toConvert) {
return toConvert | 0;
}
-56
View File
@@ -1,56 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
/*
* Logging/debug routines
*/
let _logLevel = 'warn';
let Debug = () => {};
let Info = () => {};
let Warn = () => {};
let Error = () => {};
export function initLogging(level) {
if (typeof level === 'undefined') {
level = _logLevel;
} else {
_logLevel = level;
}
Debug = Info = Warn = Error = () => {};
if (typeof window.console !== "undefined") {
/* eslint-disable no-console, no-fallthrough */
switch (level) {
case 'debug':
Debug = console.debug.bind(window.console);
case 'info':
Info = console.info.bind(window.console);
case 'warn':
Warn = console.warn.bind(window.console);
case 'error':
Error = console.error.bind(window.console);
case 'none':
break;
default:
throw new window.Error("invalid logging type '" + level + "'");
}
/* eslint-enable no-console, no-fallthrough */
}
}
export function getLogging() {
return _logLevel;
}
export { Debug, Info, Warn, Error };
// Initialize logging level
initLogging();
-28
View File
@@ -1,28 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
// Decode from UTF-8
export function decodeUTF8(utf8string, allowLatin1=false) {
try {
return decodeURIComponent(escape(utf8string));
} catch (e) {
if (e instanceof URIError) {
if (allowLatin1) {
// If we allow Latin1 we can ignore any decoding fails
// and in these cases return the original string
return utf8string;
}
}
throw e;
}
}
// Encode to UTF-8
export function encodeUTF8(DOMString) {
return unescape(encodeURIComponent(DOMString));
}
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "novnc"
version = "0.1.0"
authors = ["Joel Martin <github@martintribe.org>"]
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
+9
View File
@@ -0,0 +1,9 @@
FROM rustlang/rust:nightly
RUN rustup target add wasm32-unknown-unknown --toolchain nightly
RUN cargo install wasm-bindgen-cli
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash -
RUN apt -y install nodejs
+36
View File
@@ -0,0 +1,36 @@
# noVNC + wasm
This is a WebAssembly proof-of-concept.
It is based on information from the following sources:
* https://hacks.mozilla.org/2018/04/javascript-to-rust-and-back-again-a-wasm-bindgen-tale/
* https://www.hellorust.com/demos/canvas/index.html
* https://github.com/rustwasm/wasm-bindgen/blob/master/examples/julia\_set/
* https://github.com/rustwasm/wasm\_game\_of\_life/
## Prep:
```
docker build -t kanaka/rust-wasm ./core/wasm
# OR
docker pull kanaka/rust-wasm
docker run -it -v `pwd`:/novnc -w /novnc/core/wasm -p 7080:7080 kanaka/rust-wasm bash
npm install
```
## Build:
Run the following inside the container:
```
npm run build-release # or run build-debug (10x slower code)
npm run serve # then visit localhost:7080 outside the container
```
Note that `run server` will automatically detect modification to
`index.js` and reload the page.
+11
View File
@@ -0,0 +1,11 @@
// Currently WebAssembly modules cannot be synchronously imported in the main
// chunk: https://github.com/webpack/webpack/issues/6615
//
// By dynamically importing index.js webpack will split it into a separate chunk
// automatically, where synchronous imports of WebAssembly is allowed.
const index = import("./index");
index.then(() => {
console.log("Loaded...");
});
+11
View File
@@ -0,0 +1,11 @@
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
</head>
<body>
<button id="play-pause"></button>
<pre id="fps">Frames per Second:</pre>
<canvas id="target" width="400" height="400"></canvas>
<script src='./bootstrap.js'></script>
</body>
</html>
+105
View File
@@ -0,0 +1,105 @@
const WIDTH = 2048
const HEIGHT = 1024
import * as novnc from './novnc'
import { memory } from './novnc_bg'
const canvas = document.getElementById('target')
const ctx = canvas.getContext('2d')
canvas.width = WIDTH
canvas.height = HEIGHT
let byteSize = WIDTH * HEIGHT * 4
let pointer = novnc.alloc( byteSize )
let u8array = new Uint8ClampedArray(memory.buffer, pointer, byteSize)
let imgData = new ImageData(u8array, WIDTH, HEIGHT)
let frame = -1
function renderLoop() {
fps.render()
frame += 1
//const tms1 = (new Date()).getTime()
novnc.draw(pointer, WIDTH, HEIGHT, frame)
//const tms2 = (new Date()).getTime()
ctx.putImageData(imgData, 0, 0)
//const tms3 = (new Date()).getTime()
//console.log("elapsed 1:", tms2 - tms1, "elapsed 2:", tms3 - tms2)
animationId = requestAnimationFrame(renderLoop)
}
//////////////////////////////////////////////////////////////////////////////
// From: https://github.com/rustwasm/wasm_game_of_life/blob/3253fa3a1557bdb9525f3b5c134b58efa1041c55/index.js#L27
let animationId = null
const fps = new class {
constructor() {
this.fps = document.getElementById("fps")
this.frames = []
this.lastFrameTimeStamp = performance.now()
}
render() {
const now = performance.now()
const delta = now - this.lastFrameTimeStamp
this.lastFrameTimeStamp = now
const fps = 1 / delta * 1000
this.frames.push(fps)
if (this.frames.length > 100) {
this.frames.shift()
}
let min = Infinity
let max = -Infinity
let sum = 0
for (let i = 0; i < this.frames.length; i++) {
sum += this.frames[i]
min = Math.min(this.frames[i], min)
max = Math.max(this.frames[i], max)
}
let mean = sum / this.frames.length
this.fps.textContent = `
Frames per Second:
latest = ${Math.round(fps)}
avg of last 100 = ${Math.round(mean)}
min of last 100 = ${Math.round(min)}
max of last 100 = ${Math.round(max)}
`.trim()
}
}
const playPauseButton = document.getElementById("play-pause")
const isPaused = () => {
return animationId === null
}
const play = () => {
playPauseButton.textContent = "⏸"
renderLoop()
}
const pause = () => {
playPauseButton.textContent = "▶"
cancelAnimationFrame(animationId)
animationId = null
}
playPauseButton.addEventListener("click", event => {
if (isPaused()) {
play()
} else {
pause()
}
})
playPauseButton.textContent = "▶"
//////////////////////////////////////////////////////////////////////////////
+13
View File
@@ -0,0 +1,13 @@
{
"scripts": {
"serve": "webpack-dev-server --host 0.0.0.0 --port 7080",
"build-debug": "cargo +nightly build --target wasm32-unknown-unknown && wasm-bindgen target/wasm32-unknown-unknown/debug/novnc.wasm --out-dir .",
"build-release": "cargo +nightly build --release --target wasm32-unknown-unknown && wasm-bindgen target/wasm32-unknown-unknown/release/novnc.wasm --out-dir .",
"bundle": "npm run build-release && webpack"
},
"devDependencies": {
"webpack": "^4.16.2",
"webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.0"
}
}
+37
View File
@@ -0,0 +1,37 @@
#![feature(wasm_custom_section, wasm_import_module, use_extern_macros)]
extern crate wasm_bindgen;
use std::mem;
use std::slice;
use std::os::raw::c_void;
use wasm_bindgen::prelude::*;
// In order to work with the memory we expose allocation method
#[wasm_bindgen]
pub fn alloc(size: usize) -> *mut c_void {
let mut buf = Vec::with_capacity(size);
let ptr = buf.as_mut_ptr();
mem::forget(buf);
return ptr as *mut c_void;
}
#[wasm_bindgen]
pub fn draw(mem: *mut u32, width: usize, height: usize, frame: u32) {
// pixels are stored in RGBA
let sl = unsafe { slice::from_raw_parts_mut(mem, width * height) };
for y in 0..height {
for x in 0..width {
let r = if (x%512) < 256 {x%256} else {255-(x%256)};
let g = if (y%512) < 256 {y%256} else {255-(y%256)};
let b = if (frame%512) < 256 {frame%256} else {255-(frame%256)};
let color = 0xff000000 |
(b << 16) |
((g as u32) << 8) |
((r as u32) << 0);
sl[y*width + x] = color;
}
}
}
+10
View File
@@ -0,0 +1,10 @@
const path = require('path');
module.exports = {
entry: './bootstrap.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bootstrap.js',
},
mode: 'development'
};
+304 -301
View File
@@ -1,10 +1,10 @@
/*
* Websock: high-performance buffering wrapper
* Copyright (C) 2019 The noVNC Authors
* Websock: high-performance binary WebSockets
* Copyright (C) 2012 Joel Martin
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* Websock is similar to the standard WebSocket / RTCDataChannel object
* but with extra buffer handling.
* Websock is similar to the standard WebSocket object but with extra
* buffer handling.
*
* Websock has built-in receive queue buffering; the message event
* does not contain actual data but is simply a notification that
@@ -12,342 +12,345 @@
* read binary data off of the receive queue.
*/
import * as Log from './util/logging.js';
/* [module]
* import Util from "./util";
*/
// this has performance issues in some versions Chromium, and
// doesn't gain a tremendous amount of performance increase in Firefox
// at the moment. It may be valuable to turn it on in the future.
const MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
/*jslint browser: true, bitwise: true */
/*global Util*/
// Constants pulled from RTCDataChannelState enum
// https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel/readyState#RTCDataChannelState_enum
const DataChannel = {
CONNECTING: "connecting",
OPEN: "open",
CLOSING: "closing",
CLOSED: "closed"
/* [module] export default */ function Websock() {
"use strict";
this._websocket = null; // WebSocket object
this._rQi = 0; // Receive queue index
this._rQlen = 0; // Next write position in the receive queue
this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
this._rQmax = this._rQbufferSize / 8;
// called in init: this._rQ = new Uint8Array(this._rQbufferSize);
this._rQ = null; // Receive queue
this._sQbufferSize = 1024 * 10; // 10 KiB
// called in init: this._sQ = new Uint8Array(this._sQbufferSize);
this._sQlen = 0;
this._sQ = null; // Send queue
this._eventHandlers = {
'message': function () {},
'open': function () {},
'close': function () {},
'error': function () {}
};
};
const ReadyStates = {
CONNECTING: [WebSocket.CONNECTING, DataChannel.CONNECTING],
OPEN: [WebSocket.OPEN, DataChannel.OPEN],
CLOSING: [WebSocket.CLOSING, DataChannel.CLOSING],
CLOSED: [WebSocket.CLOSED, DataChannel.CLOSED],
};
(function () {
"use strict";
// this has performance issues in some versions Chromium, and
// doesn't gain a tremendous amount of performance increase in Firefox
// at the moment. It may be valuable to turn it on in the future.
var ENABLE_COPYWITHIN = false;
// Properties a raw channel must have, WebSocket and RTCDataChannel are two examples
const rawChannelProps = [
"send",
"close",
"binaryType",
"onerror",
"onmessage",
"onopen",
"protocol",
"readyState",
];
var MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
export default class Websock {
constructor() {
this._websocket = null; // WebSocket or RTCDataChannel object
this._rQi = 0; // Receive queue index
this._rQlen = 0; // Next write position in the receive queue
this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
// called in init: this._rQ = new Uint8Array(this._rQbufferSize);
this._rQ = null; // Receive queue
this._sQbufferSize = 1024 * 10; // 10 KiB
// called in init: this._sQ = new Uint8Array(this._sQbufferSize);
this._sQlen = 0;
this._sQ = null; // Send queue
this._eventHandlers = {
message: () => {},
open: () => {},
close: () => {},
error: () => {}
};
}
// Getters and Setters
get readyState() {
let subState;
if (this._websocket === null) {
return "unused";
var typedArrayToString = (function () {
// This is only for PhantomJS, which doesn't like apply-ing
// with Typed Arrays
try {
var arr = new Uint8Array([1, 2, 3]);
String.fromCharCode.apply(null, arr);
return function (a) { return String.fromCharCode.apply(null, a); };
} catch (ex) {
return function (a) {
return String.fromCharCode.apply(
null, Array.prototype.slice.call(a));
};
}
})();
subState = this._websocket.readyState;
Websock.prototype = {
// Getters and Setters
get_sQ: function () {
return this._sQ;
},
if (ReadyStates.CONNECTING.includes(subState)) {
return "connecting";
} else if (ReadyStates.OPEN.includes(subState)) {
return "open";
} else if (ReadyStates.CLOSING.includes(subState)) {
return "closing";
} else if (ReadyStates.CLOSED.includes(subState)) {
return "closed";
}
get_rQ: function () {
return this._rQ;
},
return "unknown";
}
get_rQi: function () {
return this._rQi;
},
get sQ() {
return this._sQ;
}
set_rQi: function (val) {
this._rQi = val;
},
get rQ() {
return this._rQ;
}
// Receive Queue
rQlen: function () {
return this._rQlen - this._rQi;
},
get rQi() {
return this._rQi;
}
rQpeek8: function () {
return this._rQ[this._rQi];
},
set rQi(val) {
this._rQi = val;
}
rQshift8: function () {
return this._rQ[this._rQi++];
},
// Receive Queue
get rQlen() {
return this._rQlen - this._rQi;
}
rQskip8: function () {
this._rQi++;
},
rQpeek8() {
return this._rQ[this._rQi];
}
rQskipBytes: function (num) {
this._rQi += num;
},
rQskipBytes(bytes) {
this._rQi += bytes;
}
// TODO(directxman12): test performance with these vs a DataView
rQshift16: function () {
return (this._rQ[this._rQi++] << 8) +
this._rQ[this._rQi++];
},
rQshift8() {
return this._rQshift(1);
}
rQshift32: function () {
return (this._rQ[this._rQi++] << 24) +
(this._rQ[this._rQi++] << 16) +
(this._rQ[this._rQi++] << 8) +
this._rQ[this._rQi++];
},
rQshift16() {
return this._rQshift(2);
}
rQshiftStr: function (len) {
if (typeof(len) === 'undefined') { len = this.rQlen(); }
var arr = new Uint8Array(this._rQ.buffer, this._rQi, len);
this._rQi += len;
return typedArrayToString(arr);
},
rQshift32() {
return this._rQshift(4);
}
rQshiftBytes: function (len) {
if (typeof(len) === 'undefined') { len = this.rQlen(); }
this._rQi += len;
return new Uint8Array(this._rQ.buffer, this._rQi - len, len);
},
// TODO(directxman12): test performance with these vs a DataView
_rQshift(bytes) {
let res = 0;
for (let byte = bytes - 1; byte >= 0; byte--) {
res += this._rQ[this._rQi++] << (byte * 8);
}
return res;
}
rQshiftTo: function (target, len) {
if (len === undefined) { len = this.rQlen(); }
// TODO: make this just use set with views when using a ArrayBuffer to store the rQ
target.set(new Uint8Array(this._rQ.buffer, this._rQi, len));
this._rQi += len;
},
rQshiftStr(len) {
if (typeof(len) === 'undefined') { len = this.rQlen; }
let str = "";
// Handle large arrays in steps to avoid long strings on the stack
for (let i = 0; i < len; i += 4096) {
let part = this.rQshiftBytes(Math.min(4096, len - i));
str += String.fromCharCode.apply(null, part);
}
return str;
}
rQwhole: function () {
return new Uint8Array(this._rQ.buffer, 0, this._rQlen);
},
rQshiftBytes(len) {
if (typeof(len) === 'undefined') { len = this.rQlen; }
this._rQi += len;
return new Uint8Array(this._rQ.buffer, this._rQi - len, len);
}
rQslice: function (start, end) {
if (end) {
return new Uint8Array(this._rQ.buffer, this._rQi + start, end - start);
} else {
return new Uint8Array(this._rQ.buffer, this._rQi + start, this._rQlen - this._rQi - start);
}
},
rQshiftTo(target, len) {
if (len === undefined) { len = this.rQlen; }
// TODO: make this just use set with views when using a ArrayBuffer to store the rQ
target.set(new Uint8Array(this._rQ.buffer, this._rQi, len));
this._rQi += len;
}
rQslice(start, end = this.rQlen) {
return new Uint8Array(this._rQ.buffer, this._rQi + start, end - start);
}
// Check to see if we must wait for 'num' bytes (default to FBU.bytes)
// to be available in the receive queue. Return true if we need to
// wait (and possibly print a debug message), otherwise false.
rQwait(msg, num, goback) {
if (this.rQlen < num) {
if (goback) {
if (this._rQi < goback) {
throw new Error("rQwait cannot backup " + goback + " bytes");
// Check to see if we must wait for 'num' bytes (default to FBU.bytes)
// to be available in the receive queue. Return true if we need to
// wait (and possibly print a debug message), otherwise false.
rQwait: function (msg, num, goback) {
var rQlen = this._rQlen - this._rQi; // Skip rQlen() function call
if (rQlen < num) {
if (goback) {
if (this._rQi < goback) {
throw new Error("rQwait cannot backup " + goback + " bytes");
}
this._rQi -= goback;
}
this._rQi -= goback;
return true; // true means need more data
}
return true; // true means need more data
}
return false;
}
return false;
},
// Send Queue
// Send Queue
flush() {
if (this._sQlen > 0 && this.readyState === 'open') {
this._websocket.send(this._encodeMessage());
this._sQlen = 0;
}
}
send(arr) {
this._sQ.set(arr, this._sQlen);
this._sQlen += arr.length;
this.flush();
}
sendString(str) {
this.send(str.split('').map(chr => chr.charCodeAt(0)));
}
// Event Handlers
off(evt) {
this._eventHandlers[evt] = () => {};
}
on(evt, handler) {
this._eventHandlers[evt] = handler;
}
_allocateBuffers() {
this._rQ = new Uint8Array(this._rQbufferSize);
this._sQ = new Uint8Array(this._sQbufferSize);
}
init() {
this._allocateBuffers();
this._rQi = 0;
this._websocket = null;
}
open(uri, protocols) {
this.attach(new WebSocket(uri, protocols));
}
attach(rawChannel) {
this.init();
// Must get object and class methods to be compatible with the tests.
const channelProps = [...Object.keys(rawChannel), ...Object.getOwnPropertyNames(Object.getPrototypeOf(rawChannel))];
for (let i = 0; i < rawChannelProps.length; i++) {
const prop = rawChannelProps[i];
if (channelProps.indexOf(prop) < 0) {
throw new Error('Raw channel missing property: ' + prop);
}
}
this._websocket = rawChannel;
this._websocket.binaryType = "arraybuffer";
this._websocket.onmessage = this._recvMessage.bind(this);
this._websocket.onopen = () => {
Log.Debug('>> WebSock.onopen');
if (this._websocket.protocol) {
Log.Info("Server choose sub-protocol: " + this._websocket.protocol);
flush: function () {
if (this._websocket.bufferedAmount !== 0) {
Util.Debug("bufferedAmount: " + this._websocket.bufferedAmount);
}
this._eventHandlers.open();
Log.Debug("<< WebSock.onopen");
};
this._websocket.onclose = (e) => {
Log.Debug(">> WebSock.onclose");
this._eventHandlers.close(e);
Log.Debug("<< WebSock.onclose");
};
this._websocket.onerror = (e) => {
Log.Debug(">> WebSock.onerror: " + e);
this._eventHandlers.error(e);
Log.Debug("<< WebSock.onerror: " + e);
};
}
close() {
if (this._websocket) {
if (this.readyState === 'connecting' ||
this.readyState === 'open') {
Log.Info("Closing WebSocket connection");
this._websocket.close();
if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) {
this._websocket.send(this._encode_message());
this._sQlen = 0;
}
},
this._websocket.onmessage = () => {};
}
}
send: function (arr) {
this._sQ.set(arr, this._sQlen);
this._sQlen += arr.length;
this.flush();
},
// private methods
_encodeMessage() {
// Put in a binary arraybuffer
// according to the spec, you can send ArrayBufferViews with the send method
return new Uint8Array(this._sQ.buffer, 0, this._sQlen);
}
send_string: function (str) {
this.send(str.split('').map(function (chr) {
return chr.charCodeAt(0);
}));
},
// We want to move all the unread data to the start of the queue,
// e.g. compacting.
// The function also expands the receive que if needed, and for
// performance reasons we combine these two actions to avoid
// unneccessary copying.
_expandCompactRQ(minFit) {
// if we're using less than 1/8th of the buffer even with the incoming bytes, compact in place
// instead of resizing
const requiredBufferSize = (this._rQlen - this._rQi + minFit) * 8;
const resizeNeeded = this._rQbufferSize < requiredBufferSize;
// Event Handlers
off: function (evt) {
this._eventHandlers[evt] = function () {};
},
if (resizeNeeded) {
// Make sure we always *at least* double the buffer size, and have at least space for 8x
// the current amount of data
this._rQbufferSize = Math.max(this._rQbufferSize * 2, requiredBufferSize);
}
on: function (evt, handler) {
this._eventHandlers[evt] = handler;
},
// we don't want to grow unboundedly
if (this._rQbufferSize > MAX_RQ_GROW_SIZE) {
this._rQbufferSize = MAX_RQ_GROW_SIZE;
if (this._rQbufferSize - this.rQlen < minFit) {
throw new Error("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit");
}
}
if (resizeNeeded) {
const oldRQbuffer = this._rQ.buffer;
_allocate_buffers: function () {
this._rQ = new Uint8Array(this._rQbufferSize);
this._rQ.set(new Uint8Array(oldRQbuffer, this._rQi, this._rQlen - this._rQi));
} else {
this._rQ.copyWithin(0, this._rQi, this._rQlen);
}
this._sQ = new Uint8Array(this._sQbufferSize);
},
this._rQlen = this._rQlen - this._rQi;
this._rQi = 0;
}
init: function () {
this._allocate_buffers();
this._rQi = 0;
this._websocket = null;
},
// push arraybuffer values onto the end of the receive que
_DecodeMessage(data) {
const u8 = new Uint8Array(data);
if (u8.length > this._rQbufferSize - this._rQlen) {
this._expandCompactRQ(u8.length);
}
this._rQ.set(u8, this._rQlen);
this._rQlen += u8.length;
}
open: function (uri, protocols) {
var ws_schema = uri.match(/^([a-z]+):\/\//)[1];
this.init();
_recvMessage(e) {
this._DecodeMessage(e.data);
if (this.rQlen > 0) {
this._eventHandlers.message();
if (this._rQlen == this._rQi) {
// All data has now been processed, this means we
// can reset the receive queue.
this._rQlen = 0;
this._rQi = 0;
this._websocket = new WebSocket(uri, protocols);
this._websocket.binaryType = 'arraybuffer';
this._websocket.onmessage = this._recv_message.bind(this);
this._websocket.onopen = (function () {
Util.Debug('>> WebSock.onopen');
if (this._websocket.protocol) {
Util.Info("Server choose sub-protocol: " + this._websocket.protocol);
}
this._eventHandlers.open();
Util.Debug("<< WebSock.onopen");
}).bind(this);
this._websocket.onclose = (function (e) {
Util.Debug(">> WebSock.onclose");
this._eventHandlers.close(e);
Util.Debug("<< WebSock.onclose");
}).bind(this);
this._websocket.onerror = (function (e) {
Util.Debug(">> WebSock.onerror: " + e);
this._eventHandlers.error(e);
Util.Debug("<< WebSock.onerror: " + e);
}).bind(this);
},
close: function () {
if (this._websocket) {
if ((this._websocket.readyState === WebSocket.OPEN) ||
(this._websocket.readyState === WebSocket.CONNECTING)) {
Util.Info("Closing WebSocket connection");
this._websocket.close();
}
this._websocket.onmessage = function (e) { return; };
}
},
// private methods
_encode_message: function () {
// Put in a binary arraybuffer
// according to the spec, you can send ArrayBufferViews with the send method
return new Uint8Array(this._sQ.buffer, 0, this._sQlen);
},
_expand_compact_rQ: function (min_fit) {
var resizeNeeded = min_fit || this._rQlen - this._rQi > this._rQbufferSize / 2;
if (resizeNeeded) {
if (!min_fit) {
// just double the size if we need to do compaction
this._rQbufferSize *= 2;
} else {
// otherwise, make sure we satisy rQlen - rQi + min_fit < rQbufferSize / 8
this._rQbufferSize = (this._rQlen - this._rQi + min_fit) * 8;
}
}
// we don't want to grow unboundedly
if (this._rQbufferSize > MAX_RQ_GROW_SIZE) {
this._rQbufferSize = MAX_RQ_GROW_SIZE;
if (this._rQbufferSize - this._rQlen - this._rQi < min_fit) {
throw new Exception("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit");
}
}
if (resizeNeeded) {
var old_rQbuffer = this._rQ.buffer;
this._rQmax = this._rQbufferSize / 8;
this._rQ = new Uint8Array(this._rQbufferSize);
this._rQ.set(new Uint8Array(old_rQbuffer, this._rQi));
} else {
if (ENABLE_COPYWITHIN) {
this._rQ.copyWithin(0, this._rQi);
} else {
this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi));
}
}
this._rQlen = this._rQlen - this._rQi;
this._rQi = 0;
},
_decode_message: function (data) {
// push arraybuffer values onto the end
var u8 = new Uint8Array(data);
if (u8.length > this._rQbufferSize - this._rQlen) {
this._expand_compact_rQ(u8.length);
}
this._rQ.set(u8, this._rQlen);
this._rQlen += u8.length;
},
_recv_message: function (e) {
try {
this._decode_message(e.data);
if (this.rQlen() > 0) {
this._eventHandlers.message();
// Compact the receive queue
if (this._rQlen == this._rQi) {
this._rQlen = 0;
this._rQi = 0;
} else if (this._rQlen > this._rQmax) {
this._expand_compact_rQ();
}
} else {
Util.Debug("Ignoring empty message");
}
} catch (exc) {
var exception_str = "";
if (exc.name) {
exception_str += "\n name: " + exc.name + "\n";
exception_str += " message: " + exc.message + "\n";
}
if (typeof exc.description !== 'undefined') {
exception_str += " description: " + exc.description + "\n";
}
if (typeof exc.stack !== 'undefined') {
exception_str += exc.stack;
}
if (exception_str.length > 0) {
Util.Error("recv_message, caught exception: " + exception_str);
} else {
Util.Error("recv_message, caught exception: " + exc);
}
if (typeof exc.name !== 'undefined') {
this._eventHandlers.error(exc.name + ": " + exc.message);
} else {
this._eventHandlers.error(exc);
}
}
} else {
Log.Debug("Ignoring empty message");
}
}
}
};
})();
-89
View File
@@ -1,89 +0,0 @@
# 1. Internal Modules
The noVNC client is composed of several internal modules that handle
rendering, input, networking, etc. Each of the modules is designed to
be cross-browser and independent from each other.
Note however that the API of these modules is not guaranteed to be
stable, and this documentation is not maintained as well as the
official external API.
## 1.1 Module List
* __Keyboard__ (core/input/keyboard.js): Keyboard input event handler with
non-US keyboard support. Translates keyDown and keyUp events to X11
keysym values.
* __Display__ (core/display.js): Efficient 2D rendering abstraction
layered on the HTML5 canvas element.
* __Websock__ (core/websock.js): Websock client from websockify
with transparent binary data support.
[Websock API](https://github.com/novnc/websockify-js/wiki/websock.js) wiki page.
## 1.2 Callbacks
For the Mouse, Keyboard and Display objects the callback functions are
assigned to configuration attributes, just as for the RFB object. The
WebSock module has a method named 'on' that takes two parameters: the
callback event name, and the callback function.
## 2. Modules
## 2.1 Keyboard Module
### 2.1.1 Configuration Attributes
None
### 2.1.2 Methods
| name | parameters | description
| ------ | ---------- | ------------
| grab | () | Begin capturing keyboard events
| ungrab | () | Stop capturing keyboard events
### 2.1.3 Callbacks
| name | parameters | description
| ---------- | -------------------- | ------------
| onkeypress | (keysym, code, down) | Handler for key press/release
## 2.2 Display Module
### 2.2.1 Configuration Attributes
| name | type | mode | default | description
| ------------ | ----- | ---- | ------- | ------------
| scale | float | RW | 1.0 | Display area scale factor 0.0 - 1.0
| clipViewport | bool | RW | false | Use viewport clipping
| width | int | RO | | Display area width
| height | int | RO | | Display area height
### 2.2.2 Methods
| name | parameters | description
| ------------------ | ------------------------------------------------------- | ------------
| viewportChangePos | (deltaX, deltaY) | Move the viewport relative to the current location
| viewportChangeSize | (width, height) | Change size of the viewport
| absX | (x) | Return X relative to the remote display
| absY | (y) | Return Y relative to the remote display
| resize | (width, height) | Set width and height
| flip | (from_queue) | Update the visible canvas with the contents of the rendering canvas
| pending | () | Check if there are waiting items in the render queue
| flush | () | Resume processing the render queue unless it's empty
| fillRect | (x, y, width, height, color, from_queue) | Draw a filled in rectangle
| copyImage | (old_x, old_y, new_x, new_y, width, height, from_queue) | Copy a rectangular area
| imageRect | (x, y, width, height, mime, arr) | Draw a rectangle with an image
| blitImage | (x, y, width, height, arr, offset, from_queue) | Blit pixels (of R,G,B,A) to the display
| drawImage | (img, x, y) | Draw image and track damage
| autoscale | (containerWidth, containerHeight) | Scale the display
### 2.2.3 Callbacks
| name | parameters | description
| ------- | ---------- | ------------
| onflush | () | A display flush has been requested and we are now ready to resume FBU processing
-385
View File
@@ -1,385 +0,0 @@
# noVNC API
The interface of the noVNC client consists of a single RFB object that
is instantiated once per connection.
## RFB
The `RFB` object represents a single connection to a VNC server. It
communicates using a WebSocket that must provide a standard RFB
protocol stream.
### Constructor
[`RFB()`](#rfb-1)
- Creates and returns a new `RFB` object.
### Properties
`viewOnly`
- Is a `boolean` indicating if any events (e.g. key presses or mouse
movement) should be prevented from being sent to the server.
Disabled by default.
`focusOnClick`
- Is a `boolean` indicating if keyboard focus should automatically be
moved to the remote session when a `mousedown` or `touchstart`
event is received. Enabled by default.
`clipViewport`
- Is a `boolean` indicating if the remote session should be clipped
to its container. When disabled scrollbars will be shown to handle
the resulting overflow. Disabled by default.
`dragViewport`
- Is a `boolean` indicating if mouse events should control the
relative position of a clipped remote session. Only relevant if
`clipViewport` is enabled. Disabled by default.
`scaleViewport`
- Is a `boolean` indicating if the remote session should be scaled
locally so it fits its container. When disabled it will be centered
if the remote session is smaller than its container, or handled
according to `clipViewport` if it is larger. Disabled by default.
`resizeSession`
- Is a `boolean` indicating if a request to resize the remote session
should be sent whenever the container changes dimensions. Disabled
by default.
`showDotCursor`
- Is a `boolean` indicating whether a dot cursor should be shown
instead of a zero-sized or fully-transparent cursor if the server
sets such invisible cursor. Disabled by default.
`background`
- Is a valid CSS [background](https://developer.mozilla.org/en-US/docs/Web/CSS/background)
style value indicating which background style should be applied
to the element containing the remote session screen. The default value is `rgb(40, 40, 40)`
(solid gray color).
`qualityLevel`
- Is an `int` in range `[0-9]` controlling the desired JPEG quality.
Value `0` implies low quality and `9` implies high quality.
Default value is `6`.
`compressionLevel`
- Is an `int` in range `[0-9]` controlling the desired compression
level. Value `0` means no compression. Level 1 uses a minimum of CPU
resources and achieves weak compression ratios, while level 9 offers
best compression but is slow in terms of CPU consumption on the server
side. Use high levels with very slow network connections.
Default value is `2`.
`capabilities` *Read only*
- Is an `Object` indicating which optional extensions are available
on the server. Some methods may only be called if the corresponding
capability is set. The following capabilities are defined:
| name | type | description
| -------- | --------- | -----------
| `power` | `boolean` | Machine power control is available
### Events
[`connect`](#connect)
- The `connect` event is fired when the `RFB` object has completed
the connection and handshaking with the server.
[`disconnect`](#disconnected)
- The `disconnect` event is fired when the `RFB` object disconnects.
[`credentialsrequired`](#credentialsrequired)
- The `credentialsrequired` event is fired when more credentials must
be given to continue.
[`securityfailure`](#securityfailure)
- The `securityfailure` event is fired when the security negotiation
with the server fails.
[`clipboard`](#clipboard)
- The `clipboard` event is fired when clipboard data is received from
the server.
[`bell`](#bell)
- The `bell` event is fired when a audible bell request is received
from the server.
[`desktopname`](#desktopname)
- The `desktopname` event is fired when the remote desktop name
changes.
[`capabilities`](#capabilities)
- The `capabilities` event is fired when `RFB.capabilities` is
updated.
### Methods
[`RFB.disconnect()`](#rfbdisconnect)
- Disconnect from the server.
[`RFB.sendCredentials()`](#rfbsendcredentials)
- Send credentials to server. Should be called after the
[`credentialsrequired`](#credentialsrequired) event has fired.
[`RFB.sendKey()`](#rfbsendKey)
- Send a key event.
[`RFB.sendCtrlAltDel()`](#rfbsendctrlaltdel)
- Send Ctrl-Alt-Del key sequence.
[`RFB.focus()`](#rfbfocus)
- Move keyboard focus to the remote session.
[`RFB.blur()`](#rfbblur)
- Remove keyboard focus from the remote session.
[`RFB.machineShutdown()`](#rfbmachineshutdown)
- Request a shutdown of the remote machine.
[`RFB.machineReboot()`](#rfbmachinereboot)
- Request a reboot of the remote machine.
[`RFB.machineReset()`](#rfbmachinereset)
- Request a reset of the remote machine.
[`RFB.clipboardPasteFrom()`](#rfbclipboardPasteFrom)
- Send clipboard contents to server.
### Details
#### RFB()
The `RFB()` constructor returns a new `RFB` object and initiates a new
connection to a specified VNC server.
##### Syntax
let rfb = new RFB( target, url [, options] );
###### Parameters
**`target`**
- A block [`HTMLElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement)
that specifies where the `RFB` object should attach itself. The
existing contents of the `HTMLElement` will be untouched, but new
elements will be added during the lifetime of the `RFB` object.
**`urlOrDataChannel`**
- A `DOMString` specifying the VNC server to connect to. This must be
a valid WebSocket URL. This can also be a `WebSocket` or `RTCDataChannel`.
**`options`** *Optional*
- An `Object` specifying extra details about how the connection
should be made.
Possible options:
`shared`
- A `boolean` indicating if the remote server should be shared or
if any other connected clients should be disconnected. Enabled
by default.
`credentials`
- An `Object` specifying the credentials to provide to the server
when authenticating. The following credentials are possible:
| name | type | description
| ------------ | ----------- | -----------
| `"username"` | `DOMString` | The user that authenticates
| `"password"` | `DOMString` | Password for the user
| `"target"` | `DOMString` | Target machine or session
`repeaterID`
- A `DOMString` specifying the ID to provide to any VNC repeater
encountered.
`wsProtocols`
- An `Array` of `DOMString`s specifying the sub-protocols to use
in the WebSocket connection. Empty by default.
#### connect
The `connect` event is fired after all the handshaking with the server
is completed and the connection is fully established. After this event
the `RFB` object is ready to recieve graphics updates and to send input.
#### disconnect
The `disconnect` event is fired when the connection has been
terminated. The `detail` property is an `Object` that contains the
property `clean`. `clean` is a `boolean` indicating if the termination
was clean or not. In the event of an unexpected termination or an error
`clean` will be set to false.
#### credentialsrequired
The `credentialsrequired` event is fired when the server requests more
credentials than were specified to [`RFB()`](#rfb-1). The `detail`
property is an `Object` containing the property `types` which is an
`Array` of `DOMString` listing the credentials that are required.
#### securityfailure
The `securityfailure` event is fired when the handshaking process with
the server fails during the security negotiation step. The `detail`
property is an `Object` containing the following properties:
| Property | Type | Description
| -------- | ----------- | -----------
| `status` | `long` | The failure status code
| `reason` | `DOMString` | The **optional** reason for the failure
The property `status` corresponds to the
[SecurityResult](https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#securityresult)
status code in cases of failure. A status of zero will not be sent in
this event since that indicates a successful security handshaking
process. The optional property `reason` is provided by the server and
thus the language of the string is not known. However most servers will
probably send English strings. The server can choose to not send a
reason and in these cases the `reason` property will be omitted.
#### clipboard
The `clipboard` event is fired when the server has sent clipboard data.
The `detail` property is an `Object` containing the property `text`
which is a `DOMString` with the clipboard data.
#### bell
The `bell` event is fired when the server has requested an audible
bell.
#### desktopname
The `desktopname` event is fired when the name of the remote desktop
changes. The `detail` property is an `Object` with the property `name`
which is a `DOMString` specifying the new name.
#### capabilities
The `capabilities` event is fired whenever an entry is added or removed
from `RFB.capabilities`. The `detail` property is an `Object` with the
property `capabilities` containing the new value of `RFB.capabilities`.
#### RFB.disconnect()
The `RFB.disconnect()` method is used to disconnect from the currently
connected server.
##### Syntax
RFB.disconnect( );
#### RFB.sendCredentials()
The `RFB.sendCredentials()` method is used to provide the missing
credentials after a `credentialsrequired` event has been fired.
##### Syntax
RFB.sendCredentials( credentials );
###### Parameters
**`credentials`**
- An `Object` specifying the credentials to provide to the server
when authenticating. See [`RFB()`](#rfb-1) for details.
#### RFB.sendKey()
The `RFB.sendKey()` method is used to send a key event to the server.
##### Syntax
RFB.sendKey( keysym, code [, down] );
###### Parameters
**`keysym`**
- A `long` specifying the RFB keysym to send. Can be `0` if a valid
**`code`** is specified.
**`code`**
- A `DOMString` specifying the physical key to send. Valid values are
those that can be specified to
[`KeyboardEvent.code`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code).
If the physical key cannot be determined then `null` shall be
specified.
**`down`** *Optional*
- A `boolean` specifying if a press or a release event should be
sent. If omitted then both a press and release event are sent.
#### RFB.sendCtrlAltDel()
The `RFB.sendCtrlAltDel()` method is used to send the key sequence
*left Control*, *left Alt*, *Delete*. This is a convenience wrapper
around [`RFB.sendKey()`](#rfbsendkey).
##### Syntax
RFB.sendCtrlAltDel( );
#### RFB.focus()
The `RFB.focus()` method sets the keyboard focus on the remote session.
Keyboard events will be sent to the remote server after this point.
##### Syntax
RFB.focus( );
#### RFB.blur()
The `RFB.blur()` method remove keyboard focus on the remote session.
Keyboard events will no longer be sent to the remote server after this
point.
##### Syntax
RFB.blur( );
#### RFB.machineShutdown()
The `RFB.machineShutdown()` method is used to request to shut down the
remote machine. The capability `power` must be set for this method to
have any effect.
##### Syntax
RFB.machineShutdown( );
#### RFB.machineReboot()
The `RFB.machineReboot()` method is used to request a clean reboot of
the remote machine. The capability `power` must be set for this method
to have any effect.
##### Syntax
RFB.machineReboot( );
#### RFB.machineReset()
The `RFB.machineReset()` method is used to request a forced reset of
the remote machine. The capability `power` must be set for this method
to have any effect.
##### Syntax
RFB.machineReset( );
#### RFB.clipboardPasteFrom()
The `RFB.clipboardPasteFrom()` method is used to send clipboard data
to the remote server.
##### Syntax
RFB.clipboardPasteFrom( text );
###### Parameters
**`text`**
- A `DOMString` specifying the clipboard data to send.
-105
View File
@@ -1,105 +0,0 @@
# Embedding and Deploying noVNC Application
This document describes how to embed and deploy the noVNC application, which
includes settings and a full user interface. If you are looking for
documentation on how to use the core noVNC library in your own application,
then please see our [library documentation](LIBRARY.md).
## Files
The noVNC application consists of the following files and directories:
* `vnc.html` - The main page for the application and where users should go. It
is possible to rename this file.
* `app/` - Support files for the application. Contains code, images, styles and
translations.
* `core/` - The core noVNC library.
* `vendor/` - Third party support libraries used by the application and the
core library.
The most basic deployment consists of simply serving these files from a web
server and setting up a WebSocket proxy to the VNC server.
## Parameters
The noVNC application can be controlled by including certain settings in the
query string. Currently the following options are available:
* `autoconnect` - Automatically connect as soon as the page has finished
loading.
* `reconnect` - If noVNC should automatically reconnect if the connection is
dropped.
* `reconnect_delay` - How long to wait in milliseconds before attempting to
reconnect.
* `host` - The WebSocket host to connect to.
* `port` - The WebSocket port to connect to.
* `encrypt` - If TLS should be used for the WebSocket connection.
* `path` - The WebSocket path to use.
* `password` - The password sent to the server, if required.
* `repeaterID` - The repeater ID to use if a VNC repeater is detected.
* `shared` - If other VNC clients should be disconnected when noVNC connects.
* `bell` - If the keyboard bell should be enabled or not.
* `view_only` - If the remote session should be in non-interactive mode.
* `view_clip` - If the remote session should be clipped or use scrollbars if
it cannot fit in the browser.
* `resize` - How to resize the remote session if it is not the same size as
the browser window. Can be one of `off`, `scale` and `remote`.
* `quality` - The session JPEG quality level. Can be `0` to `9`.
* `compression` - The session compression level. Can be `0` to `9`.
* `show_dot` - If a dot cursor should be shown when the remote server provides
no local cursor, or provides a fully-transparent (invisible) cursor.
* `logging` - The console log level. Can be one of `error`, `warn`, `info` or
`debug`.
## HTTP Serving Considerations
### Browser Cache Issue
If you serve noVNC files using a web server that provides an ETag header, and
include any options in the query string, a nasty browser cache issue can bite
you on upgrade, resulting in a red error box. The issue is caused by a mismatch
between the new vnc.html (which is reloaded because the user has used it with
new query string after the upgrade) and the old javascript files (that the
browser reuses from its cache). To avoid this issue, the browser must be told
to always revalidate cached files using conditional requests. The correct
semantics are achieved via the (confusingly named) `Cache-Control: no-cache`
header that needs to be provided in the web server responses.
### Example Server Configurations
Apache:
```
# In the main configuration file
# (Debian/Ubuntu users: use "a2enmod headers" instead)
LoadModule headers_module modules/mod_headers.so
# In the <Directory> or <Location> block related to noVNC
Header set Cache-Control "no-cache"
```
Nginx:
```
# In the location block related to noVNC
add_header Cache-Control no-cache;
```
-31
View File
@@ -1,31 +0,0 @@
# Using the noVNC JavaScript library
This document describes how to make use of the noVNC JavaScript library for
integration in your own VNC client application. If you wish to embed the more
complete noVNC application with its included user interface then please see
our [embedding documentation](EMBEDDING.md).
## API
The API of noVNC consists of a single object called `RFB`. The formal
documentation for that object can be found in our [API documentation](API.md).
## Example
noVNC includes a small example application called `vnc_lite.html`. This does
not make use of all the features of noVNC, but is a good start to see how to
do things.
## Conversion of Modules
noVNC is written using ECMAScript 6 modules. This is not supported by older
versions of Node.js. To use noVNC with those older versions of Node.js the
library must first be converted.
Fortunately noVNC includes a script to handle this conversion. Please follow
the following steps:
1. Install Node.js
2. Run `npm install` in the noVNC directory
The result of the conversion is available in the `lib/` directory.
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+621
View File
@@ -0,0 +1,621 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
+165
View File
@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
+1 -1
View File
@@ -1,6 +1,6 @@
(The MIT License)
Copyright (C) 2014-2016 by Vitaly Puzrin
Copyright (C) 2014 by Vitaly Puzrin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+27
View File
@@ -0,0 +1,27 @@
Copyright (c) <year>, <copyright holder>
All rights reserved.
This software is provided 'as-is', without any express
or implied warranty. In no event will the authors be
held liable for any damages arising from the use of
this software.
Permission is granted to anyone to use this software
for any purpose, including commercial applications,
and to alter it and redistribute it freely, subject to
the following restrictions:
1. The origin of this software must not be
misrepresented; you must not claim that you
wrote the original software. If you use this
software in a product, an acknowledgment in
the product documentation would be appreciated
but is not required.
2. Altered source versions must be plainly marked
as such, and must not be misrepresented as
being the original software.
3. This notice may not be removed or altered from
any source distribution.
+1
View File
@@ -0,0 +1 @@
0.6.1
-37
View File
@@ -1,37 +0,0 @@
.TH novnc_proxy 1 "June 25, 2020" "version 1.2.0" "USER COMMANDS"
.SH NAME
novnc_proxy - noVNC proxy server
.SH SYNOPSIS
.B novnc_proxy [--listen PORT] [--vnc VNC_HOST:PORT] [--cert CERT] [--ssl-only]
Starts the WebSockets proxy and a mini-webserver and
provides a cut-and-paste URL to go to.
--listen PORT Port for proxy/webserver to listen on
Default: 6080
--vnc VNC_HOST:PORT VNC server host:port proxy target
Default: localhost:5900
--cert CERT Path to combined cert/key file, or just
the cert file if used with --key
Default: self.pem
--key KEY Path to key file, when not combined with cert
--web WEB Path to web files (e.g. vnc.html)
Default: ./
--ssl-only Disable non-https connections.
--record FILE Record traffic to FILE.session.js
--syslog SERVER Can be local socket such as /dev/log, or a UDP host:port pair.
--heartbeat SEC send a ping to the client every SEC seconds
--timeout SEC after SEC seconds exit when not connected
--idle-timeout SEC server exits after SEC seconds if there are no
active connections
.SH AUTHOR
The noVNC Authors
https://github.com/novnc/noVNC
.SH SEE ALSO
websockify(1), nova-novncproxy(1)
+34
View File
@@ -0,0 +1,34 @@
- Decide a new version number X.Y.Z (follow SemVer)
- Update version in package.json
- Update version in docs/VERSION
- Commit the change with a commit like "Release X.Y.Z"
- Add a new release on GitHub called "vX.Y.Z", and populate it with
release notes of the following form (where A.B.C is the last release):
Major Changes Since A.B.C
=========================
*Insert warnings here about incompatibilities*
*Thanks to all the contributors who filed bugs, added features, and fixed bugs
during this release :tada:*
App-visible Changes
-------------------
- *feature* a feature which improves the app usage (#PRNUM)
- *bugfix* a bug fix which fixes the app usage (#PRNUM)
- *refactor* a refactor which changes the app usage (#PRNUM)
Library-visible Changes
-----------------------
- *feature* a feature which improves the noVNC APIs (#PRNUM)
- *bugfix* a bug fix which fixes the noVNC APIs (#PRNUM)
- *refactor* a refactor which changes the noVNC APIs (#PRNUM)
App-internals Changes
---------------------
- *bugfix* a bug fix with affects the internals of noVNC only (#PRNUM)
- *refactor* a refactor which affects the internals of noVNC only (#PRNUM)

Some files were not shown because too many files have changed in this diff Show More