Compare commits
6 Commits
Author | SHA1 | Date |
---|---|---|
|
876a691d2a | |
|
5372d169cc | |
|
af54d4b95e | |
|
82b8ff1c6a | |
|
9efc362c82 | |
|
62ad00fedf |
|
@ -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.
|
|
|
@ -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
|
|
|
@ -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.
|
|
|
@ -1,97 +0,0 @@
|
||||||
name: Publish
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
pull_request:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
npm:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- run: |
|
|
||||||
GITREV=$(git rev-parse --short HEAD)
|
|
||||||
echo $GITREV
|
|
||||||
sed -i "s/^\(.*\"version\".*\)\"\([^\"]\+\)\"\(.*\)\$/\1\"\2-g$GITREV\"\3/" package.json
|
|
||||||
if: github.event_name != 'release'
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
# Needs to be explicitly specified for auth to work
|
|
||||||
registry-url: 'https://registry.npmjs.org'
|
|
||||||
- run: npm install
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: npm
|
|
||||||
path: lib
|
|
||||||
- run: npm publish --access public
|
|
||||||
env:
|
|
||||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
|
||||||
if: |
|
|
||||||
github.repository == 'novnc/noVNC' &&
|
|
||||||
github.event_name == 'release' &&
|
|
||||||
!github.event.release.prerelease
|
|
||||||
- run: npm publish --access public --tag beta
|
|
||||||
env:
|
|
||||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
|
||||||
if: |
|
|
||||||
github.repository == 'novnc/noVNC' &&
|
|
||||||
github.event_name == 'release' &&
|
|
||||||
github.event.release.prerelease
|
|
||||||
- run: npm publish --access public --tag dev
|
|
||||||
env:
|
|
||||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
|
||||||
if: |
|
|
||||||
github.repository == 'novnc/noVNC' &&
|
|
||||||
github.event_name == 'push' &&
|
|
||||||
github.event.ref == 'refs/heads/master'
|
|
||||||
snap:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- run: |
|
|
||||||
GITREV=$(git rev-parse --short HEAD)
|
|
||||||
echo $GITREV
|
|
||||||
sed -i "s/^\(.*\"version\".*\)\"\([^\"]\+\)\"\(.*\)\$/\1\"\2-g$GITREV\"\3/" package.json
|
|
||||||
if: github.event_name != 'release'
|
|
||||||
- run: |
|
|
||||||
VERSION=$(grep '"version"' package.json | cut -d '"' -f 4)
|
|
||||||
echo $VERSION
|
|
||||||
sed -i "s/^version:.*/version: '$VERSION'/" snap/snapcraft.yaml
|
|
||||||
- uses: snapcore/action-build@v1
|
|
||||||
id: snapcraft
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: snap
|
|
||||||
path: ${{ steps.snapcraft.outputs.snap }}
|
|
||||||
- uses: snapcore/action-publish@v1
|
|
||||||
with:
|
|
||||||
snap: ${{ steps.snapcraft.outputs.snap }}
|
|
||||||
release: stable
|
|
||||||
env:
|
|
||||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_LOGIN }}
|
|
||||||
if: |
|
|
||||||
github.repository == 'novnc/noVNC' &&
|
|
||||||
github.event_name == 'release' &&
|
|
||||||
!github.event.release.prerelease
|
|
||||||
- uses: snapcore/action-publish@v1
|
|
||||||
with:
|
|
||||||
snap: ${{ steps.snapcraft.outputs.snap }}
|
|
||||||
release: beta
|
|
||||||
env:
|
|
||||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_LOGIN }}
|
|
||||||
if: |
|
|
||||||
github.repository == 'novnc/noVNC' &&
|
|
||||||
github.event_name == 'release' &&
|
|
||||||
github.event.release.prerelease
|
|
||||||
- uses: snapcore/action-publish@v1
|
|
||||||
with:
|
|
||||||
snap: ${{ steps.snapcraft.outputs.snap }}
|
|
||||||
release: edge
|
|
||||||
env:
|
|
||||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_LOGIN }}
|
|
||||||
if: |
|
|
||||||
github.repository == 'novnc/noVNC' &&
|
|
||||||
github.event_name == 'push' &&
|
|
||||||
github.event.ref == 'refs/heads/master'
|
|
|
@ -1,19 +0,0 @@
|
||||||
name: Lint
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
eslint:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
- run: npm update
|
|
||||||
- run: npm run lint
|
|
||||||
html:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
- run: npm update
|
|
||||||
- run: git ls-tree --name-only -r HEAD | grep -E "[.](html|css)$" | xargs ./utils/validate
|
|
|
@ -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@v4
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
- run: npm update
|
|
||||||
- run: npm run test
|
|
||||||
env:
|
|
||||||
TEST_BROWSER_NAME: ${{ matrix.browser }}
|
|
|
@ -1,15 +0,0 @@
|
||||||
name: Translate
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
translate:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
- run: npm update
|
|
||||||
- run: sudo apt-get install gettext
|
|
||||||
- run: make -C po update-pot
|
|
||||||
- run: make -C po update-po
|
|
||||||
- run: make -C po update-js
|
|
|
@ -3,10 +3,7 @@
|
||||||
tests/data_*.js
|
tests/data_*.js
|
||||||
utils/rebind.so
|
utils/rebind.so
|
||||||
utils/websockify
|
utils/websockify
|
||||||
/node_modules
|
node_modules
|
||||||
/build
|
build
|
||||||
/lib
|
lib
|
||||||
recordings
|
recordings
|
||||||
*.swp
|
|
||||||
*~
|
|
||||||
noVNC-*.tgz
|
|
||||||
|
|
|
@ -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
|
|
@ -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
AUTHORS
|
@ -1,13 +0,0 @@
|
||||||
maintainers:
|
|
||||||
- Samuel Mannehed for Cendio AB (@samhed)
|
|
||||||
- Pierre Ossman for Cendio AB (@CendioOssman)
|
|
||||||
maintainersEmeritus:
|
|
||||||
- Joel Martin (@kanaka)
|
|
||||||
- Solly Ross (@directxman12)
|
|
||||||
- @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.
|
|
|
@ -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!
|
35
LICENSE.txt
|
@ -1,14 +1,23 @@
|
||||||
noVNC is Copyright (C) 2022 The noVNC authors
|
noVNC is Copyright (C) 2011 Joel Martin <github@martintribe.org>
|
||||||
(./AUTHORS)
|
|
||||||
|
|
||||||
The noVNC core library files are licensed under the MPL 2.0 (Mozilla
|
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
|
Public License 2.0). The noVNC core library is composed of the
|
||||||
Javascript code necessary for full noVNC operation. This includes (but
|
Javascript code necessary for full noVNC operation. This includes (but
|
||||||
is not limited to):
|
is not limited to):
|
||||||
|
|
||||||
core/**/*.js
|
core/base64.js
|
||||||
app/*.js
|
core/des.js
|
||||||
test/playback.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
|
The HTML, CSS, font and images files that included with the noVNC
|
||||||
source distibution (or repository) are not considered part of the
|
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.
|
Some portions of noVNC are copyright to their individual authors.
|
||||||
Please refer to the individual source files and/or to the noVNC commit
|
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 are several files and projects that have been incorporated into
|
||||||
the noVNC core library. Here is a list of those files and the original
|
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
|
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
|
Any other files not mentioned above are typically marked with
|
||||||
a copyright/license header at the top of the file. The default noVNC
|
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:
|
The following license texts are included:
|
||||||
|
|
||||||
docs/LICENSE.MPL-2.0
|
docs/LICENSE.MPL-2.0
|
||||||
|
docs/LICENSE.LGPL-3 and
|
||||||
|
docs/LICENSE.GPL-3
|
||||||
docs/LICENSE.OFL-1.1
|
docs/LICENSE.OFL-1.1
|
||||||
docs/LICENSE.BSD-3-Clause (New BSD)
|
docs/LICENSE.BSD-3-Clause (New BSD)
|
||||||
docs/LICENSE.BSD-2-Clause (Simplified BSD / FreeBSD)
|
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:
|
Or alternatively the license texts may be found here:
|
||||||
|
|
||||||
http://www.mozilla.org/MPL/2.0/
|
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://scripts.sil.org/OFL
|
||||||
http://en.wikipedia.org/wiki/BSD_licenses
|
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
|
||||||
|
|
222
README.md
|
@ -1,53 +1,35 @@
|
||||||
## noVNC: HTML VNC client library and application
|
## noVNC: HTML5 VNC Client
|
||||||
|
|
||||||
[](https://github.com/novnc/noVNC/actions?query=workflow%3ATest)
|
[](https://travis-ci.org/novnc/noVNC)
|
||||||
[](https://github.com/novnc/noVNC/actions?query=workflow%3ALint)
|
|
||||||
|
|
||||||
### Description
|
### Description
|
||||||
|
|
||||||
noVNC is both a HTML VNC client JavaScript library and an application built on
|
noVNC is a HTML5 VNC client that runs well in any modern browser including
|
||||||
top of that library. noVNC runs well in any modern browser including mobile
|
mobile browsers (iOS and Android).
|
||||||
browsers (iOS and Android).
|
|
||||||
|
|
||||||
Many companies, projects and products have integrated noVNC including
|
Many companies, projects and products have integrated noVNC including
|
||||||
|
[Ganeti Web Manager](http://code.osuosl.org/projects/ganeti-webmgr),
|
||||||
[OpenStack](http://www.openstack.org),
|
[OpenStack](http://www.openstack.org),
|
||||||
[OpenNebula](http://opennebula.org/),
|
[OpenNebula](http://opennebula.org/),
|
||||||
[LibVNCServer](http://libvncserver.sourceforge.net), and
|
[LibVNCServer](http://libvncserver.sourceforge.net), and
|
||||||
[ThinLinc](https://cendio.com/thinlinc). See
|
[ThinLinc](https://cendio.com/thinlinc). See
|
||||||
[the Projects and companies wiki page](https://github.com/novnc/noVNC/wiki/Projects-and-companies-using-noVNC)
|
[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.
|
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
|
### News/help/contact
|
||||||
|
|
||||||
The project website is found at [novnc.com](http://novnc.com).
|
Notable commits, announcements and news are posted to
|
||||||
|
<a href="http://www.twitter.com/noVNC">@noVNC</a>.
|
||||||
|
|
||||||
If you are a noVNC developer/integrator/user (or want to be) please join the
|
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
|
Bugs and feature requests can be submitted via
|
||||||
[github issues](https://github.com/novnc/noVNC/issues). If you have questions
|
[github issues](https://github.com/novnc/noVNC/issues).
|
||||||
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.
|
|
||||||
|
|
||||||
If you are looking for a place to start contributing to noVNC, a good place to
|
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
|
start would be the issues that are marked as
|
||||||
["patchwelcome"](https://github.com/novnc/noVNC/issues?labels=patchwelcome).
|
["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-
|
If you want to show appreciation for noVNC you could donate to a great non-
|
||||||
profits such as:
|
profits such as:
|
||||||
|
@ -57,172 +39,112 @@ profits such as:
|
||||||
[Electronic Frontier Foundation](https://www.eff.org/),
|
[Electronic Frontier Foundation](https://www.eff.org/),
|
||||||
[Against Malaria Foundation](http://www.againstmalaria.com/),
|
[Against Malaria Foundation](http://www.againstmalaria.com/),
|
||||||
[Nothing But Nets](http://www.nothingbutnets.net/), etc.
|
[Nothing But Nets](http://www.nothingbutnets.net/), etc.
|
||||||
|
Please tweet <a href="http://www.twitter.com/noVNC">@noVNC</a> if you do.
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Supports all modern browsers including mobile (iOS, Android)
|
* Supports all modern browsers including mobile (iOS, Android)
|
||||||
* Supported authentication methods: none, classical VNC, RealVNC's
|
* Supported VNC encodings: raw, copyrect, rre, hextile, tight, tightPNG
|
||||||
RSA-AES, Tight, VeNCrypt Plain, XVP, Apple's Diffie-Hellman,
|
* WebSocket SSL/TLS encryption (i.e. "wss://") support
|
||||||
UltraVNC's MSLogonII
|
* 24-bit true color and 8 bit colour mapped
|
||||||
* Supported VNC encodings: raw, copyrect, rre, hextile, tight, tightPNG,
|
* Supports desktop resize notification/pseudo-encoding
|
||||||
ZRLE, JPEG, Zlib, H.264
|
* Local or remote cursor
|
||||||
* Supports scaling, clipping and resizing the desktop
|
* Clipboard copy/paste
|
||||||
* Supports back & forward mouse buttons
|
* Clipping or scolling modes for large remote screens
|
||||||
* Local cursor rendering
|
* Easy site integration and theming (3 example themes included)
|
||||||
* Clipboard copy/paste with full Unicode support
|
* Licensed under the [MPL 2.0](http://www.mozilla.org/MPL/2.0/)
|
||||||
* 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
|
|
||||||
|
|
||||||
### Screenshots
|
### 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>
|
<img src="http://novnc.com/img/noVNC-5.png" width=400>
|
||||||
<img src="http://novnc.com/img/noVNC-3-connected.png" width=400>
|
<img src="http://novnc.com/img/noVNC-7.jpg" width=400>
|
||||||
|
|
||||||
See more screenshots
|
See more screenshots
|
||||||
[here](http://novnc.com/screenshots.html).
|
<a href="http://novnc.com/screenshots.html">here</a>.
|
||||||
|
|
||||||
|
|
||||||
### Browser requirements
|
### Browser Requirements
|
||||||
|
|
||||||
noVNC uses many modern web technologies so a formal requirement list is
|
* Chrome 8, Firefox 4, Safari 6, Opera 12, IE 11, Edge 12, etc.
|
||||||
not available. However these are the minimum versions we are currently
|
|
||||||
aware of:
|
|
||||||
|
|
||||||
* Chrome 89, Firefox 89, Safari 15, Opera 75, Edge 89
|
* 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
|
### Server Requirements
|
||||||
|
|
||||||
noVNC follows the standard VNC protocol, but unlike other VNC clients it does
|
Unless you are using a VNC server with support for WebSockets connections (such
|
||||||
require WebSockets support. Many servers include support (e.g.
|
as [x11vnc/libvncserver](http://libvncserver.sourceforge.net/),
|
||||||
[x11vnc/libvncserver](http://libvncserver.sourceforge.net/),
|
[QEMU](http://www.qemu.org/), or
|
||||||
[QEMU](http://www.qemu.org/), and
|
[MobileVNC](http://www.smartlab.at/mobilevnc/)), you need to use a
|
||||||
[MobileVNC](http://www.smartlab.at/mobilevnc/)), but for the others you need to
|
WebSockets to TCP socket proxy. There is a python proxy included
|
||||||
use a WebSockets to TCP socket proxy. noVNC has a sister project
|
('websockify').
|
||||||
[websockify](https://github.com/novnc/websockify) that provides a simple such
|
|
||||||
proxy.
|
|
||||||
|
|
||||||
|
|
||||||
### Quick start
|
### Quick Start
|
||||||
|
|
||||||
* Use the `novnc_proxy` script to automatically download and start websockify, which
|
* Use the launch script to start a mini-webserver and the WebSockets proxy
|
||||||
includes a mini-webserver and the WebSockets proxy. The `--vnc` option is
|
(websockify). The `--vnc` option is used to specify the location of a running
|
||||||
used to specify the location of a running VNC server:
|
VNC server:
|
||||||
|
|
||||||
`./utils/novnc_proxy --vnc localhost:5901`
|
`./utils/launch.sh --vnc localhost:5901`
|
||||||
|
|
||||||
* If you don't need to expose the web server to public internet, you can
|
* Point your browser to the cut-and-paste URL that is output by the launch
|
||||||
bind to localhost:
|
script. Enter a password if the VNC server has one configured. Hit the
|
||||||
|
Connect button and enjoy!
|
||||||
|
|
||||||
`./utils/novnc_proxy --vnc localhost:5901 --listen localhost:6081`
|
|
||||||
|
|
||||||
* Point your browser to the cut-and-paste URL that is output by the `novnc_proxy`
|
### Other Pages
|
||||||
script. Hit the Connect button, enter a password if the VNC server has one
|
|
||||||
configured, and enjoy!
|
|
||||||
|
|
||||||
### Installation from snap package
|
* [Modules/API](https://github.com/novnc/noVNC/wiki/Modules-API) - The library
|
||||||
Running the command below will install the latest release of noVNC from snap:
|
modules and their Javascript API.
|
||||||
|
|
||||||
`sudo snap install novnc`
|
* [Integration](https://github.com/novnc/noVNC/wiki/Integration) - Get noVNC
|
||||||
|
to work in existing projects.
|
||||||
|
|
||||||
#### Running noVNC from snap directly
|
* [Troubleshooting](https://github.com/novnc/noVNC/wiki/Troubleshooting) - How
|
||||||
|
to troubleshoot problems.
|
||||||
|
|
||||||
You can run the snap package installed novnc directly with, for example:
|
* [Encrypted Connections](https://github.com/novnc/websockify/wiki/Encrypted-Connections) -
|
||||||
|
Setup websockify so that you can use encrypted connections from noVNC.
|
||||||
|
|
||||||
`novnc --listen 6081 --vnc localhost:5901 # /snap/bin/novnc if /snap/bin is not in your PATH`
|
* [Advanced Usage](https://github.com/novnc/noVNC/wiki/Advanced-usage) -
|
||||||
|
Generating an SSL certificate, starting a VNC server, advanced websockify
|
||||||
|
usage, etc.
|
||||||
|
|
||||||
If you want to use certificate files, due to standard snap confinement restrictions you need to have them in the /home/\<user\>/snap/novnc/current/ directory. If your username is jsmith an example command would be:
|
* [Testing](https://github.com/novnc/noVNC/wiki/Testing) - Run and write
|
||||||
|
tests.
|
||||||
|
|
||||||
`novnc --listen 8443 --cert ~jsmith/snap/novnc/current/self.crt --key ~jsmith/snap/novnc/current/self.key --vnc ubuntu.example.com:5901`
|
* [Translations](https://github.com/novnc/noVNC/wiki/Translations) - Add and
|
||||||
|
modify localization for JavaScript and HTML.
|
||||||
#### Running noVNC from snap 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 running multiple instances of novnc).
|
|
||||||
Instructions (with example values):
|
|
||||||
|
|
||||||
List current services (out-of-box this will be blank):
|
|
||||||
|
|
||||||
```
|
|
||||||
sudo snap get novnc services
|
|
||||||
Key Value
|
|
||||||
services.n6080 {...}
|
|
||||||
services.n6081 {...}
|
|
||||||
```
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
### Authors/Contributors
|
### 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:
|
* Core team:
|
||||||
|
* [Joel Martin](https://github.com/kanaka)
|
||||||
* [Samuel Mannehed](https://github.com/samhed) (Cendio)
|
* [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)
|
* [Pierre Ossman](https://github.com/CendioOssman) (Cendio)
|
||||||
|
|
||||||
* Previous core contributors:
|
|
||||||
* [Joel Martin](https://github.com/kanaka) (Project founder)
|
|
||||||
* [Solly Ross](https://github.com/DirectXMan12) (Red Hat / OpenStack)
|
|
||||||
|
|
||||||
* Notable contributions:
|
* Notable contributions:
|
||||||
* UI and icons : Pierre Ossman, Chris Gordon
|
* UI and Icons : Pierre Ossman, Chris Gordon
|
||||||
* Original logo : Michael Sersen
|
* Original Logo : Michael Sersen
|
||||||
* tight encoding : Michael Tinglof (Mercuri.ca)
|
* tight encoding : Michael Tinglof (Mercuri.ca)
|
||||||
* RealVNC RSA AES authentication : USTC Vlab Team
|
|
||||||
|
|
||||||
* Included libraries:
|
* Included libraries:
|
||||||
|
* as3crypto : Henri Torgemane (code.google.com/p/as3crypto)
|
||||||
* base64 : Martijn Pieters (Digital Creations 2), Samuel Sieb (sieb.net)
|
* base64 : Martijn Pieters (Digital Creations 2), Samuel Sieb (sieb.net)
|
||||||
* DES : Dave Zimmerman (Widget Workshop), Jef Poskanzer (ACME Labs)
|
* DES : Dave Zimmerman (Widget Workshop), Jef Poskanzer (ACME Labs)
|
||||||
* Pako : Vitaly Puzrin (https://github.com/nodeca/pako)
|
* 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)
|
||||||
[contribution guide](https://github.com/novnc/noVNC/wiki/Contributing) and
|
|
||||||
start hacking!
|
|
||||||
|
|
|
@ -1,79 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Fallback for all uncaught errors
|
|
||||||
function handleError(event, err) {
|
|
||||||
try {
|
|
||||||
const msg = document.getElementById('noVNC_fallback_errormsg');
|
|
||||||
|
|
||||||
// Work around Firefox bug:
|
|
||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1685038
|
|
||||||
if (event.message === "ResizeObserver loop completed with undelivered notifications.") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to disable keyboard interaction, best effort
|
|
||||||
try {
|
|
||||||
// Remove focus from the currently focused element in order to
|
|
||||||
// prevent keyboard interaction from continuing
|
|
||||||
if (document.activeElement) { document.activeElement.blur(); }
|
|
||||||
|
|
||||||
// Don't let any element be focusable when showing the error
|
|
||||||
let keyboardFocusable = 'a[href], button, input, textarea, select, details, [tabindex]';
|
|
||||||
document.querySelectorAll(keyboardFocusable).forEach((elem) => {
|
|
||||||
elem.setAttribute("tabindex", "-1");
|
|
||||||
});
|
|
||||||
} catch (exc) {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't return true since this would prevent the error
|
|
||||||
// from being printed to the browser console.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('error', evt => handleError(evt, evt.error));
|
|
||||||
window.addEventListener('unhandledrejection', evt => handleError(evt.reason, evt.reason));
|
|
|
@ -1,42 +1,43 @@
|
||||||
BROWSER_SIZES := 16 24 32 48 64
|
ICONS := \
|
||||||
#ANDROID_SIZES := 72 96 144 192
|
novnc-16x16.png \
|
||||||
# FIXME: The ICO is limited to 8 icons due to a Chrome bug:
|
novnc-24x24.png \
|
||||||
# https://bugs.chromium.org/p/chromium/issues/detail?id=1381393
|
novnc-32x32.png \
|
||||||
ANDROID_SIZES := 96 144 192
|
novnc-48x48.png \
|
||||||
WEB_ICON_SIZES := $(BROWSER_SIZES) $(ANDROID_SIZES)
|
novnc-64x64.png
|
||||||
|
|
||||||
#IOS_1X_SIZES := 20 29 40 76 # No such devices exist anymore
|
ANDROID_LAUNCHER := \
|
||||||
IOS_2X_SIZES := 40 58 80 120 152 167
|
novnc-48x48.png \
|
||||||
IOS_3X_SIZES := 60 87 120 180
|
novnc-72x72.png \
|
||||||
ALL_IOS_SIZES := $(IOS_1X_SIZES) $(IOS_2X_SIZES) $(IOS_3X_SIZES)
|
novnc-96x96.png \
|
||||||
|
novnc-144x144.png \
|
||||||
|
novnc-192x192.png \
|
||||||
|
novnc-512x512.png
|
||||||
|
|
||||||
ALL_ICONS := \
|
IPHONE_LAUNCHER := \
|
||||||
$(ALL_IOS_SIZES:%=novnc-ios-%.png) \
|
novnc-60x60.png \
|
||||||
novnc.ico
|
novnc-120x120.png
|
||||||
|
|
||||||
|
IPAD_LAUNCHER := \
|
||||||
|
novnc-76x76.png \
|
||||||
|
novnc-152x152.png
|
||||||
|
|
||||||
|
ALL_ICONS := $(ICONS) $(ANDROID_LAUNCHER) $(IPHONE_LAUNCHER) $(IPAD_LAUNCHER)
|
||||||
|
|
||||||
all: $(ALL_ICONS)
|
all: $(ALL_ICONS)
|
||||||
|
|
||||||
# Our testing shows that the ICO file need to be sorted in largest to
|
novnc-16x16.png: novnc-icon-sm.svg
|
||||||
# smallest to get the apporpriate behviour
|
convert -density 90 \
|
||||||
WEB_ICON_SIZES_REVERSE := $(shell echo $(WEB_ICON_SIZES) | tr ' ' '\n' | sort -nr | tr '\n' ' ')
|
-background transparent "$<" "$@"
|
||||||
WEB_BASE_ICONS := $(WEB_ICON_SIZES_REVERSE:%=novnc-%.png)
|
novnc-24x24.png: novnc-icon-sm.svg
|
||||||
.INTERMEDIATE: $(WEB_BASE_ICONS)
|
convert -density 135 \
|
||||||
|
-background transparent "$<" "$@"
|
||||||
|
novnc-32x32.png: novnc-icon-sm.svg
|
||||||
|
convert -density 180 \
|
||||||
|
-background transparent "$<" "$@"
|
||||||
|
|
||||||
novnc.ico: $(WEB_BASE_ICONS)
|
|
||||||
convert $(WEB_BASE_ICONS) "$@"
|
|
||||||
|
|
||||||
# General conversion
|
|
||||||
novnc-%.png: novnc-icon.svg
|
novnc-%.png: novnc-icon.svg
|
||||||
convert -depth 8 -background transparent \
|
convert -density $$[`echo $* | cut -d x -f 1` * 90 / 48] \
|
||||||
-size $*x$* "$(lastword $^)" "$@"
|
-background transparent "$<" "$@"
|
||||||
|
|
||||||
# iOS icons use their own SVG
|
|
||||||
novnc-ios-%.png: novnc-ios-icon.svg
|
|
||||||
convert -depth 8 -background transparent \
|
|
||||||
-size $*x$* "$(lastword $^)" "$@"
|
|
||||||
|
|
||||||
# The smallest sizes are generated using a different SVG
|
|
||||||
novnc-16.png novnc-24.png novnc-32.png: novnc-icon-sm.svg
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.png
|
rm -f *.png
|
||||||
|
|
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 675 B |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 1000 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.6 KiB |
|
@ -1,183 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
width="48"
|
|
||||||
height="48"
|
|
||||||
viewBox="0 0 48 48.000001"
|
|
||||||
id="svg2"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
|
||||||
sodipodi:docname="novnc-ios-icon.svg"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
||||||
<defs
|
|
||||||
id="defs4" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="11.313708"
|
|
||||||
inkscape:cx="27.356195"
|
|
||||||
inkscape:cy="17.810253"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="false"
|
|
||||||
units="px"
|
|
||||||
inkscape:object-nodes="true"
|
|
||||||
inkscape:snap-smooth-nodes="true"
|
|
||||||
inkscape:snap-midpoints="true"
|
|
||||||
inkscape:window-width="2560"
|
|
||||||
inkscape:window-height="1371"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:showpageshadow="2"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#d1d1d1">
|
|
||||||
<inkscape:grid
|
|
||||||
type="xygrid"
|
|
||||||
id="grid4169" />
|
|
||||||
</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" />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(0,-1004.3621)">
|
|
||||||
<rect
|
|
||||||
style="opacity:1;fill:#494949;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
||||||
id="rect4167"
|
|
||||||
width="48"
|
|
||||||
height="48"
|
|
||||||
x="0"
|
|
||||||
y="1004.3621"
|
|
||||||
inkscape:label="background" />
|
|
||||||
<path
|
|
||||||
style="opacity:1;fill:#313131;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
|
||||||
d="m 0,1004.3621 v 48 h 20 c 15.512,0 28,-16.948 28,-38 v -10 z"
|
|
||||||
id="rect4173"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccc"
|
|
||||||
inkscape:label="darker_grey_plate" />
|
|
||||||
<g
|
|
||||||
id="g4300"
|
|
||||||
style="display:inline;fill:#000000;fill-opacity:1;stroke:none"
|
|
||||||
transform="translate(0.5,0.5)"
|
|
||||||
inkscape:label="shadows">
|
|
||||||
<g
|
|
||||||
id="g4302"
|
|
||||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
|
||||||
inkscape:label="no">
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="scsccsssscccs"
|
|
||||||
d="m 11.986926,1016.3621 c 0.554325,0 1.025987,0.2121 1.414987,0.6362 0.398725,0.4138 0.600909,0.9155 0.598087,1.5052 v 6.8586 h -2 v -6.8914 c 0,-0.072 -0.03404,-0.1086 -0.102113,-0.1086 H 7.1021125 C 7.0340375,1018.3621 7,1018.3983 7,1018.4707 v 6.8914 H 5 v -9 z"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
id="path4304"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
inkscape:label="n" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="sscsscsscsscssssssssss"
|
|
||||||
d="m 17.013073,1016.3621 h 4.973854 c 0.554325,0 1.025987,0.2121 1.414986,0.6362 0.398725,0.4138 0.598087,0.9155 0.598087,1.5052 v 4.7172 c 0,0.5897 -0.199362,1.0966 -0.598087,1.5207 -0.388999,0.4138 -0.860661,0.6207 -1.414986,0.6207 h -4.973854 c -0.554325,0 -1.030849,-0.2069 -1.429574,-0.6207 C 15.1945,1024.3173 15,1023.8104 15,1023.2207 v -4.7172 c 0,-0.5897 0.1945,-1.0914 0.583499,-1.5052 0.398725,-0.4241 0.875249,-0.6362 1.429574,-0.6362 z m 4.884815,2 h -4.795776 c -0.06808,0 -0.102112,0.036 -0.102112,0.1086 v 4.7828 c 0,0.072 0.03404,0.1086 0.102112,0.1086 h 4.795776 c 0.06807,0 0.102112,-0.036 0.102112,-0.1086 v -4.7828 c 0,-0.072 -0.03404,-0.1086 -0.102112,-0.1086 z"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
id="path4306"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
inkscape:label="o" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="g4308"
|
|
||||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
|
||||||
inkscape:label="VNC">
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccccccc"
|
|
||||||
d="m 12,1036.9177 4.768114,-8.5556 H 19 l -6,11 h -2 l -6,-11 h 2.2318854 z"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
id="path4310"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
inkscape:label="V" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="ccccccccccc"
|
|
||||||
d="m 29,1036.3621 v -8 h 2 v 11 h -2 l -7,-8 v 8 h -2 v -11 h 2 z"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
id="path4312"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
inkscape:label="N" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cssssccscsscscc"
|
|
||||||
d="m 43,1030.3621 h -8.897887 c -0.06808,0 -0.102113,0.036 -0.102113,0.1069 v 6.7862 c 0,0.071 0.03404,0.1069 0.102113,0.1069 H 43 v 2 h -8.972339 c -0.56405,0 -1.045437,-0.2037 -1.444162,-0.6111 C 32.1945,1038.3334 32,1037.8292 32,1037.2385 v -6.7528 c 0,-0.5907 0.1945,-1.0898 0.583499,-1.4972 0.398725,-0.4176 0.880112,-0.6264 1.444162,-0.6264 H 43 Z"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
id="path4314"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
inkscape:label="C" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="g4291"
|
|
||||||
style="stroke:none"
|
|
||||||
inkscape:label="noVNC">
|
|
||||||
<g
|
|
||||||
id="g4282"
|
|
||||||
style="stroke:none"
|
|
||||||
inkscape:label="no">
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path4143"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#008000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 11.986926,1016.3621 c 0.554325,0 1.025987,0.2121 1.414987,0.6362 0.398725,0.4138 0.600909,0.9155 0.598087,1.5052 l 0,6.8586 -2,0 0,-6.8914 c 0,-0.072 -0.03404,-0.1086 -0.102113,-0.1086 l -4.7957745,0 C 7.0340375,1018.3621 7,1018.3983 7,1018.4707 l 0,6.8914 -2,0 0,-9 z"
|
|
||||||
sodipodi:nodetypes="scsccsssscccs"
|
|
||||||
inkscape:label="n" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path4145"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#008000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 17.013073,1016.3621 4.973854,0 c 0.554325,0 1.025987,0.2121 1.414986,0.6362 0.398725,0.4138 0.598087,0.9155 0.598087,1.5052 l 0,4.7172 c 0,0.5897 -0.199362,1.0966 -0.598087,1.5207 -0.388999,0.4138 -0.860661,0.6207 -1.414986,0.6207 l -4.973854,0 c -0.554325,0 -1.030849,-0.2069 -1.429574,-0.6207 C 15.1945,1024.3173 15,1023.8104 15,1023.2207 l 0,-4.7172 c 0,-0.5897 0.1945,-1.0914 0.583499,-1.5052 0.398725,-0.4241 0.875249,-0.6362 1.429574,-0.6362 z m 4.884815,2 -4.795776,0 c -0.06808,0 -0.102112,0.036 -0.102112,0.1086 l 0,4.7828 c 0,0.072 0.03404,0.1086 0.102112,0.1086 l 4.795776,0 c 0.06807,0 0.102112,-0.036 0.102112,-0.1086 l 0,-4.7828 c 0,-0.072 -0.03404,-0.1086 -0.102112,-0.1086 z"
|
|
||||||
sodipodi:nodetypes="sscsscsscsscssssssssss"
|
|
||||||
inkscape:label="o" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="g4286"
|
|
||||||
style="stroke:none"
|
|
||||||
inkscape:label="VNC">
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path4147"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 12,1036.9177 4.768114,-8.5556 2.231886,0 -6,11 -2,0 -6,-11 2.2318854,0 z"
|
|
||||||
sodipodi:nodetypes="cccccccc"
|
|
||||||
inkscape:label="V" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path4149"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 29,1036.3621 0,-8 2,0 0,11 -2,0 -7,-8 0,8 -2,0 0,-11 2,0 z"
|
|
||||||
sodipodi:nodetypes="ccccccccccc"
|
|
||||||
inkscape:label="N" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path4151"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:Orbitron;-inkscape-font-specification:'Orbitron Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 43,1030.3621 -8.897887,0 c -0.06808,0 -0.102113,0.036 -0.102113,0.1069 l 0,6.7862 c 0,0.071 0.03404,0.1069 0.102113,0.1069 l 8.897887,0 0,2 -8.972339,0 c -0.56405,0 -1.045437,-0.2037 -1.444162,-0.6111 C 32.1945,1038.3334 32,1037.8292 32,1037.2385 l 0,-6.7528 c 0,-0.5907 0.1945,-1.0898 0.583499,-1.4972 0.398725,-0.4176 0.880112,-0.6264 1.444162,-0.6264 l 8.972339,0 z"
|
|
||||||
sodipodi:nodetypes="cssssccscsscscc"
|
|
||||||
inkscape:label="C" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 303 KiB |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 +0,0 @@
|
||||||
DO NOT MODIFY THE FILES IN THIS FOLDER, THEY ARE AUTOMATICALLY GENERATED FROM THE PO-FILES.
|
|
|
@ -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"
|
|
||||||
}
|
|
|
@ -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",
|
||||||
|
};
|
|
@ -1,74 +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.",
|
|
||||||
"Disconnect timeout": "Zeitüberschreitung beim Trennen",
|
|
||||||
"Local Downscaling": "Lokales herunterskalieren",
|
|
||||||
"Local Cursor": "Lokaler Mauszeiger",
|
|
||||||
"Forcing clipping mode since scrollbars aren't supported by IE in fullscreen": "'Clipping-Modus' aktiviert, Scrollbalken in 'IE-Vollbildmodus' werden nicht unterstützt",
|
|
||||||
"True Color": "True Color"
|
|
||||||
}
|
|
|
@ -1,22 +1,29 @@
|
||||||
{
|
/*
|
||||||
"HTTPS is required for full functionality": "Το HTTPS είναι απαιτούμενο για πλήρη λειτουργικότητα",
|
* Translations for el
|
||||||
|
*
|
||||||
|
* This file was autotomatically generated from el.po
|
||||||
|
* DO NOT EDIT!
|
||||||
|
*/
|
||||||
|
|
||||||
|
Language = {
|
||||||
"Connecting...": "Συνδέεται...",
|
"Connecting...": "Συνδέεται...",
|
||||||
"Disconnecting...": "Aποσυνδέεται...",
|
|
||||||
"Reconnecting...": "Επανασυνδέεται...",
|
|
||||||
"Internal error": "Εσωτερικό σφάλμα",
|
|
||||||
"Must set host": "Πρέπει να οριστεί ο διακομιστής",
|
|
||||||
"Connected (encrypted) to ": "Συνδέθηκε (κρυπτογραφημένα) με το ",
|
"Connected (encrypted) to ": "Συνδέθηκε (κρυπτογραφημένα) με το ",
|
||||||
"Connected (unencrypted) to ": "Συνδέθηκε (μη κρυπτογραφημένα) με το ",
|
"Connected (unencrypted) to ": "Συνδέθηκε (μη κρυπτογραφημένα) με το ",
|
||||||
"Something went wrong, connection is closed": "Κάτι πήγε στραβά, η σύνδεση διακόπηκε",
|
"Disconnecting...": "Aποσυνδέεται...",
|
||||||
"Failed to connect to server": "Αποτυχία στη σύνδεση με το διακομιστή",
|
|
||||||
"Disconnected": "Αποσυνδέθηκε",
|
"Disconnected": "Αποσυνδέθηκε",
|
||||||
"New connection has been rejected with reason: ": "Η νέα σύνδεση απορρίφθηκε διότι: ",
|
"Must set host and port": "Πρέπει να οριστεί το όνομα και η πόρτα του διακομιστή",
|
||||||
"New connection has been rejected": "Η νέα σύνδεση απορρίφθηκε ",
|
"Password is required": "Απαιτείται ο κωδικός πρόσβασης",
|
||||||
"Credentials are required": "Απαιτούνται διαπιστευτήρια",
|
"Forcing clipping mode since scrollbars aren't supported by IE in fullscreen": "Εφαρμογή λειτουργίας αποκοπής αφού δεν υποστηρίζονται οι λωρίδες κύλισης σε πλήρη οθόνη στον IE",
|
||||||
"noVNC encountered an error:": "το noVNC αντιμετώπισε ένα σφάλμα:",
|
"Disconnect timeout": "Παρέλευση χρονικού ορίου αποσύνδεσης",
|
||||||
|
"noVNC encountered an error:": "το noVNC αντιμετώπισε ένα σφάλμα",
|
||||||
"Hide/Show the control bar": "Απόκρυψη/Εμφάνιση γραμμής ελέγχου",
|
"Hide/Show the control bar": "Απόκρυψη/Εμφάνιση γραμμής ελέγχου",
|
||||||
"Drag": "Σύρσιμο",
|
|
||||||
"Move/Drag Viewport": "Μετακίνηση/Σύρσιμο Θεατού πεδίου",
|
"Move/Drag Viewport": "Μετακίνηση/Σύρσιμο Θεατού πεδίου",
|
||||||
|
"viewport drag": "σύρσιμο θεατού πεδίου",
|
||||||
|
"Active Mouse Button": "Ενεργό Πλήκτρο Ποντικιού",
|
||||||
|
"No mousebutton": "Χωρίς Πλήκτρο Ποντικιού",
|
||||||
|
"Left mousebutton": "Αριστερό Πλήκτρο Ποντικιού",
|
||||||
|
"Middle mousebutton": "Μεσαίο Πλήκτρο Ποντικιού",
|
||||||
|
"Right mousebutton": "Δεξί Πλήκτρο Ποντικιού",
|
||||||
"Keyboard": "Πληκτρολόγιο",
|
"Keyboard": "Πληκτρολόγιο",
|
||||||
"Show Keyboard": "Εμφάνιση Πληκτρολογίου",
|
"Show Keyboard": "Εμφάνιση Πληκτρολογίου",
|
||||||
"Extra keys": "Επιπλέον πλήκτρα",
|
"Extra keys": "Επιπλέον πλήκτρα",
|
||||||
|
@ -25,8 +32,6 @@
|
||||||
"Toggle Ctrl": "Εναλλαγή Ctrl",
|
"Toggle Ctrl": "Εναλλαγή Ctrl",
|
||||||
"Alt": "Alt",
|
"Alt": "Alt",
|
||||||
"Toggle Alt": "Εναλλαγή Alt",
|
"Toggle Alt": "Εναλλαγή Alt",
|
||||||
"Toggle Windows": "Εναλλαγή Παράθυρων",
|
|
||||||
"Windows": "Παράθυρα",
|
|
||||||
"Send Tab": "Αποστολή Tab",
|
"Send Tab": "Αποστολή Tab",
|
||||||
"Tab": "Tab",
|
"Tab": "Tab",
|
||||||
"Esc": "Esc",
|
"Esc": "Esc",
|
||||||
|
@ -40,61 +45,30 @@
|
||||||
"Reboot": "Επανεκκίνηση",
|
"Reboot": "Επανεκκίνηση",
|
||||||
"Reset": "Επαναφορά",
|
"Reset": "Επαναφορά",
|
||||||
"Clipboard": "Πρόχειρο",
|
"Clipboard": "Πρόχειρο",
|
||||||
"Edit clipboard content in the textarea below.": "Επεξεργαστείτε το περιεχόμενο του πρόχειρου στην περιοχή κειμένου παρακάτω.",
|
"Clear": "Καθάρισμα",
|
||||||
"Full Screen": "Πλήρης Οθόνη",
|
"Fullscreen": "Πλήρης Οθόνη",
|
||||||
"Settings": "Ρυθμίσεις",
|
"Settings": "Ρυθμίσεις",
|
||||||
|
"Encrypt": "Κρυπτογράφηση",
|
||||||
|
"True Color": "Πραγματικά Χρώματα",
|
||||||
|
"Local Cursor": "Τοπικός Δρομέας",
|
||||||
|
"Clip to Window": "Αποκοπή στο όριο του Παράθυρου",
|
||||||
"Shared Mode": "Κοινόχρηστη Λειτουργία",
|
"Shared Mode": "Κοινόχρηστη Λειτουργία",
|
||||||
"View Only": "Μόνο Θέαση",
|
"View Only": "Μόνο Θέαση",
|
||||||
"Clip to Window": "Αποκοπή στο όριο του Παράθυρου",
|
"Path:": "Διαδρομή:",
|
||||||
"Scaling Mode:": "Λειτουργία Κλιμάκωσης:",
|
"Scaling Mode:": "Λειτουργία Κλιμάκωσης:",
|
||||||
"None": "Καμία",
|
"None": "Καμία",
|
||||||
"Local Scaling": "Τοπική Κλιμάκωση",
|
"Local Scaling": "Τοπική Κλιμάκωση",
|
||||||
"Remote Resizing": "Απομακρυσμένη Αλλαγή μεγέθους",
|
|
||||||
"Advanced": "Για προχωρημένους",
|
|
||||||
"Quality:": "Ποιότητα:",
|
|
||||||
"Compression level:": "Επίπεδο συμπίεσης:",
|
|
||||||
"Repeater ID:": "Repeater ID:",
|
|
||||||
"WebSocket": "WebSocket",
|
|
||||||
"Encrypt": "Κρυπτογράφηση",
|
|
||||||
"Host:": "Όνομα διακομιστή:",
|
|
||||||
"Port:": "Πόρτα διακομιστή:",
|
|
||||||
"Path:": "Διαδρομή:",
|
|
||||||
"Automatic Reconnect": "Αυτόματη επανασύνδεση",
|
|
||||||
"Reconnect Delay (ms):": "Καθυστέρηση επανασύνδεσης (ms):",
|
|
||||||
"Show Dot when No Cursor": "Εμφάνιση Τελείας όταν δεν υπάρχει Δρομέας",
|
|
||||||
"Logging:": "Καταγραφή:",
|
|
||||||
"Version:": "Έκδοση:",
|
|
||||||
"Disconnect": "Αποσύνδεση",
|
|
||||||
"Connect": "Σύνδεση",
|
|
||||||
"Server identity": "Ταυτότητα Διακομιστή",
|
|
||||||
"The server has provided the following identifying information:": "Ο διακομιστής παρείχε την ακόλουθη πληροφορία ταυτοποίησης:",
|
|
||||||
"Fingerprint:": "Δακτυλικό αποτύπωμα:",
|
|
||||||
"Please verify that the information is correct and press \"Approve\". Otherwise press \"Reject\".": "Παρακαλώ επαληθεύσετε ότι η πληροφορία είναι σωστή και πιέστε \"Αποδοχή\". Αλλιώς πιέστε \"Απόρριψη\".",
|
|
||||||
"Approve": "Αποδοχή",
|
|
||||||
"Reject": "Απόρριψη",
|
|
||||||
"Credentials": "Διαπιστευτήρια",
|
|
||||||
"Username:": "Κωδικός Χρήστη:",
|
|
||||||
"Password:": "Κωδικός Πρόσβασης:",
|
|
||||||
"Send Credentials": "Αποστολή Διαπιστευτηρίων",
|
|
||||||
"Cancel": "Ακύρωση",
|
|
||||||
"Password is required": "Απαιτείται ο κωδικός πρόσβασης",
|
|
||||||
"viewport drag": "σύρσιμο θεατού πεδίου",
|
|
||||||
"Active Mouse Button": "Ενεργό Πλήκτρο Ποντικιού",
|
|
||||||
"No mousebutton": "Χωρίς Πλήκτρο Ποντικιού",
|
|
||||||
"Left mousebutton": "Αριστερό Πλήκτρο Ποντικιού",
|
|
||||||
"Middle mousebutton": "Μεσαίο Πλήκτρο Ποντικιού",
|
|
||||||
"Right mousebutton": "Δεξί Πλήκτρο Ποντικιού",
|
|
||||||
"Clear": "Καθάρισμα",
|
|
||||||
"Canvas not supported.": "Δεν υποστηρίζεται το στοιχείο Canvas",
|
|
||||||
"Disconnect timeout": "Παρέλευση χρονικού ορίου αποσύνδεσης",
|
|
||||||
"Local Downscaling": "Τοπική Συρρίκνωση",
|
"Local Downscaling": "Τοπική Συρρίκνωση",
|
||||||
"Local Cursor": "Τοπικός Δρομέας",
|
"Remote Resizing": "Απομακρυσμένη Αλλαγή μεγέθους",
|
||||||
"Forcing clipping mode since scrollbars aren't supported by IE in fullscreen": "Εφαρμογή λειτουργίας αποκοπής αφού δεν υποστηρίζονται οι λωρίδες κύλισης σε πλήρη οθόνη στον IE",
|
"Repeater ID:": "Repeater ID:",
|
||||||
"True Color": "Πραγματικά Χρώματα",
|
|
||||||
"Style:": "Στυλ:",
|
"Style:": "Στυλ:",
|
||||||
"default": "προεπιλεγμένο",
|
"default": "προεπιλεγμένο",
|
||||||
|
"Logging:": "Καταγραφή:",
|
||||||
"Apply": "Εφαρμογή",
|
"Apply": "Εφαρμογή",
|
||||||
"Connection": "Σύνδεση",
|
"Host:": "Όνομα διακομιστή:",
|
||||||
|
"Port:": "Πόρτα διακομιστή:",
|
||||||
|
"Password:": "Κωδικός Πρόσβασης:",
|
||||||
"Token:": "Διακριτικό:",
|
"Token:": "Διακριτικό:",
|
||||||
"Send Password": "Αποστολή Κωδικού Πρόσβασης"
|
"Send Password": "Αποστολή Κωδικού Πρόσβασης",
|
||||||
}
|
"Canvas not supported.": "Δεν υποστηρίζεται το στοιχείο Canvas",
|
||||||
|
};
|
|
@ -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": "Se debe configurar el host",
|
|
||||||
"Reconnecting...": "Reconectando...",
|
|
||||||
"Password is required": "La 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",
|
|
||||||
"Encrypt": "Encriptar",
|
|
||||||
"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",
|
|
||||||
"Host:": "Host:",
|
|
||||||
"Port:": "Puerto:",
|
|
||||||
"Path:": "Ruta:",
|
|
||||||
"Automatic reconnect": "Reconexión automática",
|
|
||||||
"Reconnect delay (ms):": "Retraso en la reconexión (ms):",
|
|
||||||
"Logging:": "Registrando:",
|
|
||||||
"Disconnect": "Desconectar",
|
|
||||||
"Connect": "Conectar",
|
|
||||||
"Password:": "Contraseña:",
|
|
||||||
"Cancel": "Cancelar",
|
|
||||||
"Canvas not supported.": "Canvas no soportado."
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
{
|
|
||||||
"Running without HTTPS is not recommended, crashes or other issues are likely.": "Lancer sans HTTPS n'est pas recommandé, crashs ou autres problèmes en vue.",
|
|
||||||
"Connecting...": "En cours de connexion...",
|
|
||||||
"Disconnecting...": "Déconnexion en cours...",
|
|
||||||
"Reconnecting...": "Reconnexion en cours...",
|
|
||||||
"Internal error": "Erreur interne",
|
|
||||||
"Failed to connect to server: ": "Échec de connexion au serveur ",
|
|
||||||
"Connected (encrypted) to ": "Connecté (chiffré) à ",
|
|
||||||
"Connected (unencrypted) to ": "Connecté (non chiffré) à ",
|
|
||||||
"Something went wrong, connection is closed": "Quelque chose s'est mal passé, la connexion a été fermée",
|
|
||||||
"Failed to connect to server": "Échec de connexion au serveur",
|
|
||||||
"Disconnected": "Déconnecté",
|
|
||||||
"New connection has been rejected with reason: ": "Une nouvelle connexion a été rejetée avec motif : ",
|
|
||||||
"New connection has been rejected": "Une nouvelle connexion a été rejetée",
|
|
||||||
"Credentials are required": "Les identifiants sont requis",
|
|
||||||
"noVNC encountered an error:": "noVNC a rencontré une erreur :",
|
|
||||||
"Hide/Show the control bar": "Masquer/Afficher la barre de contrôle",
|
|
||||||
"Drag": "Faire glisser",
|
|
||||||
"Move/Drag viewport": "Déplacer la fenêtre de visualisation",
|
|
||||||
"Keyboard": "Clavier",
|
|
||||||
"Show keyboard": "Afficher le clavier",
|
|
||||||
"Extra keys": "Touches supplémentaires",
|
|
||||||
"Show extra keys": "Afficher les touches supplémentaires",
|
|
||||||
"Ctrl": "Ctrl",
|
|
||||||
"Toggle Ctrl": "Basculer Ctrl",
|
|
||||||
"Alt": "Alt",
|
|
||||||
"Toggle Alt": "Basculer Alt",
|
|
||||||
"Toggle Windows": "Basculer Windows",
|
|
||||||
"Windows": "Fenêtre",
|
|
||||||
"Send Tab": "Envoyer Tab",
|
|
||||||
"Tab": "Tabulation",
|
|
||||||
"Esc": "Esc",
|
|
||||||
"Send Escape": "Envoyer Escape",
|
|
||||||
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
|
|
||||||
"Send Ctrl-Alt-Del": "Envoyer Ctrl-Alt-Del",
|
|
||||||
"Shutdown/Reboot": "Arrêter/Redémarrer",
|
|
||||||
"Shutdown/Reboot...": "Arrêter/Redémarrer...",
|
|
||||||
"Power": "Alimentation",
|
|
||||||
"Shutdown": "Arrêter",
|
|
||||||
"Reboot": "Redémarrer",
|
|
||||||
"Reset": "Réinitialiser",
|
|
||||||
"Clipboard": "Presse-papiers",
|
|
||||||
"Edit clipboard content in the textarea below.": "Editer le contenu du presse-papier dans la zone ci-dessous.",
|
|
||||||
"Full screen": "Plein écran",
|
|
||||||
"Settings": "Paramètres",
|
|
||||||
"Shared mode": "Mode partagé",
|
|
||||||
"View only": "Afficher uniquement",
|
|
||||||
"Clip to window": "Ajuster à la fenêtre",
|
|
||||||
"Scaling mode:": "Mode mise à l'échelle :",
|
|
||||||
"None": "Aucun",
|
|
||||||
"Local scaling": "Mise à l'échelle locale",
|
|
||||||
"Remote resizing": "Redimensionnement à distance",
|
|
||||||
"Advanced": "Avancé",
|
|
||||||
"Quality:": "Qualité :",
|
|
||||||
"Compression level:": "Niveau de compression :",
|
|
||||||
"Repeater ID:": "ID Répéteur :",
|
|
||||||
"WebSocket": "WebSocket",
|
|
||||||
"Encrypt": "Chiffrer",
|
|
||||||
"Host:": "Hôte :",
|
|
||||||
"Port:": "Port :",
|
|
||||||
"Path:": "Chemin :",
|
|
||||||
"Automatic reconnect": "Reconnecter automatiquement",
|
|
||||||
"Reconnect delay (ms):": "Délai de reconnexion (ms) :",
|
|
||||||
"Show dot when no cursor": "Afficher le point lorsqu'il n'y a pas de curseur",
|
|
||||||
"Logging:": "Se connecter :",
|
|
||||||
"Version:": "Version :",
|
|
||||||
"Disconnect": "Déconnecter",
|
|
||||||
"Connect": "Connecter",
|
|
||||||
"Server identity": "Identité du serveur",
|
|
||||||
"The server has provided the following identifying information:": "Le serveur a fourni l'identification suivante :",
|
|
||||||
"Fingerprint:": "Empreinte digitale :",
|
|
||||||
"Please verify that the information is correct and press \"Approve\". Otherwise press \"Reject\".": "SVP, verifiez que l'information est correcte et pressez \"Accepter\". Sinon pressez \"Refuser\".",
|
|
||||||
"Approve": "Accepter",
|
|
||||||
"Reject": "Refuser",
|
|
||||||
"Credentials": "Envoyer les identifiants",
|
|
||||||
"Username:": "Nom d'utilisateur :",
|
|
||||||
"Password:": "Mot de passe :",
|
|
||||||
"Send credentials": "Envoyer les identifiants",
|
|
||||||
"Cancel": "Annuler",
|
|
||||||
"Must set host": "Doit définir l'hôte",
|
|
||||||
"Clear": "Effacer"
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
{
|
|
||||||
"Connecting...": "Connessione in corso...",
|
|
||||||
"Disconnecting...": "Disconnessione...",
|
|
||||||
"Reconnecting...": "Riconnessione...",
|
|
||||||
"Internal error": "Errore interno",
|
|
||||||
"Must set host": "Devi impostare l'host",
|
|
||||||
"Connected (encrypted) to ": "Connesso (crittografato) a ",
|
|
||||||
"Connected (unencrypted) to ": "Connesso (non crittografato) a",
|
|
||||||
"Something went wrong, connection is closed": "Qualcosa è andato storto, la connessione è stata chiusa",
|
|
||||||
"Failed to connect to server": "Impossibile connettersi al server",
|
|
||||||
"Disconnected": "Disconnesso",
|
|
||||||
"New connection has been rejected with reason: ": "La nuova connessione è stata rifiutata con motivo: ",
|
|
||||||
"New connection has been rejected": "La nuova connessione è stata rifiutata",
|
|
||||||
"Credentials are required": "Le credenziali sono obbligatorie",
|
|
||||||
"noVNC encountered an error:": "noVNC ha riscontrato un errore:",
|
|
||||||
"Hide/Show the control bar": "Nascondi/Mostra la barra di controllo",
|
|
||||||
"Keyboard": "Tastiera",
|
|
||||||
"Show keyboard": "Mostra tastiera",
|
|
||||||
"Extra keys": "Tasti Aggiuntivi",
|
|
||||||
"Show Extra Keys": "Mostra Tasti Aggiuntivi",
|
|
||||||
"Ctrl": "Ctrl",
|
|
||||||
"Toggle Ctrl": "Tieni premuto Ctrl",
|
|
||||||
"Alt": "Alt",
|
|
||||||
"Toggle Alt": "Tieni premuto Alt",
|
|
||||||
"Toggle Windows": "Tieni premuto Windows",
|
|
||||||
"Windows": "Windows",
|
|
||||||
"Send Tab": "Invia Tab",
|
|
||||||
"Tab": "Tab",
|
|
||||||
"Esc": "Esc",
|
|
||||||
"Send Escape": "Invia Esc",
|
|
||||||
"Ctrl+Alt+Del": "Ctrl+Alt+Canc",
|
|
||||||
"Send Ctrl-Alt-Del": "Invia Ctrl-Alt-Canc",
|
|
||||||
"Shutdown/Reboot": "Spegnimento/Riavvio",
|
|
||||||
"Shutdown/Reboot...": "Spegnimento/Riavvio...",
|
|
||||||
"Power": "Alimentazione",
|
|
||||||
"Shutdown": "Spegnimento",
|
|
||||||
"Reboot": "Riavvio",
|
|
||||||
"Reset": "Reset",
|
|
||||||
"Clipboard": "Clipboard",
|
|
||||||
"Clear": "Pulisci",
|
|
||||||
"Fullscreen": "Schermo intero",
|
|
||||||
"Settings": "Impostazioni",
|
|
||||||
"Shared mode": "Modalità condivisa",
|
|
||||||
"View Only": "Sola Visualizzazione",
|
|
||||||
"Scaling mode:": "Modalità di ridimensionamento:",
|
|
||||||
"None": "Nessuna",
|
|
||||||
"Local Scaling": "Ridimensionamento Locale",
|
|
||||||
"Remote Resizing": "Ridimensionamento Remoto",
|
|
||||||
"Advanced": "Avanzate",
|
|
||||||
"Quality:": "Qualità:",
|
|
||||||
"Compression level:": "Livello Compressione:",
|
|
||||||
"Repeater ID:": "ID Ripetitore:",
|
|
||||||
"WebSocket": "WebSocket",
|
|
||||||
"Encrypt": "Crittografa",
|
|
||||||
"Host:": "Host:",
|
|
||||||
"Port:": "Porta:",
|
|
||||||
"Path:": "Percorso:",
|
|
||||||
"Automatic Reconnect": "Riconnessione Automatica",
|
|
||||||
"Reconnect Delay (ms):": "Ritardo Riconnessione (ms):",
|
|
||||||
"Show Dot when No Cursor": "Mostra Punto quando Nessun Cursore",
|
|
||||||
"Version:": "Versione:",
|
|
||||||
"Disconnect": "Disconnetti",
|
|
||||||
"Connect": "Connetti",
|
|
||||||
"Username:": "Utente:",
|
|
||||||
"Password:": "Password:",
|
|
||||||
"Send Credentials": "Invia Credenziale",
|
|
||||||
"Cancel": "Annulla"
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
{
|
|
||||||
"Running without HTTPS is not recommended, crashes or other issues are likely.": "HTTPS接続なしで実行することは推奨されません。クラッシュしたりその他の問題が発生したりする可能性があります。",
|
|
||||||
"Connecting...": "接続しています...",
|
|
||||||
"Disconnecting...": "切断しています...",
|
|
||||||
"Reconnecting...": "再接続しています...",
|
|
||||||
"Internal error": "内部エラー",
|
|
||||||
"Must set host": "ホストを設定する必要があります",
|
|
||||||
"Failed to connect to server: ": "サーバーへの接続に失敗しました: ",
|
|
||||||
"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": "クリップボード",
|
|
||||||
"Edit clipboard content in the textarea below.": "以下の入力欄からクリップボードの内容を編集できます。",
|
|
||||||
"Full screen": "全画面表示",
|
|
||||||
"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": "接続",
|
|
||||||
"Server identity": "サーバーの識別情報",
|
|
||||||
"The server has provided the following identifying information:": "サーバーは以下の識別情報を提供しています:",
|
|
||||||
"Fingerprint:": "フィンガープリント:",
|
|
||||||
"Please verify that the information is correct and press \"Approve\". Otherwise press \"Reject\".": "この情報が正しい場合は「承認」を、そうでない場合は「拒否」を押してください。",
|
|
||||||
"Approve": "承認",
|
|
||||||
"Reject": "拒否",
|
|
||||||
"Credentials": "資格情報",
|
|
||||||
"Username:": "ユーザー名:",
|
|
||||||
"Password:": "パスワード:",
|
|
||||||
"Send credentials": "資格情報を送信",
|
|
||||||
"Cancel": "キャンセル"
|
|
||||||
}
|
|
|
@ -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": "취소"
|
|
||||||
}
|
|
|
@ -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",
|
||||||
|
};
|
|
@ -1,95 +0,0 @@
|
||||||
{
|
|
||||||
"Running without HTTPS is not recommended, crashes or other issues are likely.": "Het is niet aan te raden om zonder HTTPS te werken, crashes of andere problemen zijn dan waarschijnlijk.",
|
|
||||||
"Connecting...": "Aan het verbinden…",
|
|
||||||
"Disconnecting...": "Bezig om verbinding te verbreken...",
|
|
||||||
"Reconnecting...": "Opnieuw verbinding maken...",
|
|
||||||
"Internal error": "Interne fout",
|
|
||||||
"Failed to connect to server: ": "Verbinding maken met server is mislukt",
|
|
||||||
"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 met de volgende reden: ",
|
|
||||||
"New connection has been rejected": "Nieuwe verbinding is geweigerd",
|
|
||||||
"Credentials are required": "Inloggegevens zijn nodig",
|
|
||||||
"noVNC encountered an error:": "noVNC heeft een fout bemerkt:",
|
|
||||||
"Hide/Show the control bar": "Verberg/Toon de bedieningsbalk",
|
|
||||||
"Drag": "Sleep",
|
|
||||||
"Move/Drag viewport": "Verplaats/Versleep Kijkvenster",
|
|
||||||
"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": "Vensters omschakelen",
|
|
||||||
"Windows": "Vensters",
|
|
||||||
"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",
|
|
||||||
"Edit clipboard content in the textarea below.": "Edit de inhoud van het klembord in het tekstveld hieronder",
|
|
||||||
"Full screen": "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",
|
|
||||||
"Quality:": "Kwaliteit:",
|
|
||||||
"Compression level:": "Compressieniveau:",
|
|
||||||
"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:",
|
|
||||||
"Version:": "Versie:",
|
|
||||||
"Disconnect": "Verbinding verbreken",
|
|
||||||
"Connect": "Verbinden",
|
|
||||||
"Server identity": "Serveridentiteit",
|
|
||||||
"The server has provided the following identifying information:": "De server geeft de volgende identificerende informatie:",
|
|
||||||
"Fingerprint:": "Vingerafdruk:",
|
|
||||||
"Please verify that the information is correct and press \"Approve\". Otherwise press \"Reject\".": "Verifieer dat de informatie is correct en druk “OK”. Druk anders op “Afwijzen”.",
|
|
||||||
"Approve": "OK",
|
|
||||||
"Reject": "Afwijzen",
|
|
||||||
"Credentials": "Inloggegevens",
|
|
||||||
"Username:": "Gebruikersnaam:",
|
|
||||||
"Password:": "Wachtwoord:",
|
|
||||||
"Send credentials": "Stuur inloggegevens",
|
|
||||||
"Cancel": "Annuleren",
|
|
||||||
"Must set host": "Host moeten worden ingesteld",
|
|
||||||
"Password is required": "Wachtwoord is vereist",
|
|
||||||
"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",
|
|
||||||
"Clear": "Wissen",
|
|
||||||
"Send Password": "Verzend Wachtwoord:",
|
|
||||||
"Disconnect timeout": "Timeout tijdens verbreken van verbinding",
|
|
||||||
"Local Downscaling": "Lokaal Neerschalen",
|
|
||||||
"Local Cursor": "Lokale Cursor",
|
|
||||||
"Canvas not supported.": "Canvas wordt niet ondersteund.",
|
|
||||||
"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"
|
|
||||||
}
|
|
|
@ -1,80 +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.",
|
|
||||||
"Disconnect timeout": "Timeout rozłączenia",
|
|
||||||
"Local Downscaling": "Downscaling lokalny",
|
|
||||||
"Local Cursor": "Lokalny kursor",
|
|
||||||
"Forcing clipping mode since scrollbars aren't supported by IE in fullscreen": "Wymuszam clipping mode ponieważ paski przewijania nie są wspierane przez IE w trybie pełnoekranowym",
|
|
||||||
"True Color": "True Color",
|
|
||||||
"Style:": "Styl:",
|
|
||||||
"default": "domyślny",
|
|
||||||
"Apply": "Zapisz",
|
|
||||||
"Connection": "Połączenie",
|
|
||||||
"Token:": "Token:",
|
|
||||||
"Send Password": "Wyślij Hasło"
|
|
||||||
}
|
|
|
@ -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": "Mostrar 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"
|
|
||||||
}
|
|
|
@ -1,72 +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": "Новое соединение отклонено",
|
|
||||||
"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": "Вкладка",
|
|
||||||
"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": "Выход"
|
|
||||||
}
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Translations for sv
|
||||||
|
*
|
||||||
|
* This file was autotomatically generated from sv.po
|
||||||
|
* DO NOT EDIT!
|
||||||
|
*/
|
||||||
|
|
||||||
|
Language = {
|
||||||
|
"Connecting...": "Ansluter...",
|
||||||
|
"Connected (encrypted) to ": "Ansluten (krypterat) till ",
|
||||||
|
"Connected (unencrypted) to ": "Ansluten (okrypterat) till ",
|
||||||
|
"Disconnecting...": "Kopplar ner...",
|
||||||
|
"Disconnected": "Frånkopplad",
|
||||||
|
"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",
|
||||||
|
"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",
|
||||||
|
"Show Extra Keys": "Visa Extraknappar",
|
||||||
|
"Ctrl": "Ctrl",
|
||||||
|
"Toggle Ctrl": "Växla Ctrl",
|
||||||
|
"Alt": "Alt",
|
||||||
|
"Toggle Alt": "Växla Alt",
|
||||||
|
"Send Tab": "Skicka Tab",
|
||||||
|
"Tab": "Tab",
|
||||||
|
"Esc": "Esc",
|
||||||
|
"Send Escape": "Skicka Escape",
|
||||||
|
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
|
||||||
|
"Send Ctrl-Alt-Del": "Skicka Ctrl-Alt-Del",
|
||||||
|
"Shutdown/Reboot": "Stäng av/Boota om",
|
||||||
|
"Shutdown/Reboot...": "Stäng av/Boota om...",
|
||||||
|
"Power": "Ström",
|
||||||
|
"Shutdown": "Stäng av",
|
||||||
|
"Reboot": "Boota om",
|
||||||
|
"Reset": "Återställ",
|
||||||
|
"Clipboard": "Urklipp",
|
||||||
|
"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",
|
||||||
|
"Path:": "Sökväg:",
|
||||||
|
"Scaling Mode:": "Skalningsläge:",
|
||||||
|
"None": "Ingen",
|
||||||
|
"Local Scaling": "Lokal Skalning",
|
||||||
|
"Local Downscaling": "Lokal Nedskalning",
|
||||||
|
"Remote Resizing": "Ändra Storlek",
|
||||||
|
"Repeater ID:": "Repeater-ID:",
|
||||||
|
"Style:": "Stil:",
|
||||||
|
"default": "standard",
|
||||||
|
"Logging:": "Loggning:",
|
||||||
|
"Apply": "Verkställ",
|
||||||
|
"Connect": "Anslut",
|
||||||
|
"Disconnect": "Koppla från",
|
||||||
|
"Connection": "Uppkoppling",
|
||||||
|
"Host:": "Värd:",
|
||||||
|
"Port:": "Port:",
|
||||||
|
"Password:": "Lösenord:",
|
||||||
|
"Token:": "Token:",
|
||||||
|
"Send Password": "Skicka Lösenord",
|
||||||
|
"Canvas not supported.": "Canvas stöds ej",
|
||||||
|
};
|
|
@ -1,83 +0,0 @@
|
||||||
{
|
|
||||||
"Running without HTTPS is not recommended, crashes or other issues are likely.": "Det är ej rekommenderat att köra utan HTTPS, krascher och andra problem är troliga.",
|
|
||||||
"Connecting...": "Ansluter...",
|
|
||||||
"Disconnecting...": "Kopplar ner...",
|
|
||||||
"Reconnecting...": "Återansluter...",
|
|
||||||
"Internal error": "Internt fel",
|
|
||||||
"Failed to connect to server: ": "Misslyckades att ansluta till servern: ",
|
|
||||||
"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",
|
|
||||||
"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",
|
|
||||||
"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",
|
|
||||||
"Keyboard": "Tangentbord",
|
|
||||||
"Show keyboard": "Visa tangentbord",
|
|
||||||
"Extra keys": "Extraknappar",
|
|
||||||
"Show extra keys": "Visa extraknappar",
|
|
||||||
"Ctrl": "Ctrl",
|
|
||||||
"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",
|
|
||||||
"Send Escape": "Skicka Escape",
|
|
||||||
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
|
|
||||||
"Send Ctrl-Alt-Del": "Skicka Ctrl-Alt-Del",
|
|
||||||
"Shutdown/Reboot": "Stäng av/Boota om",
|
|
||||||
"Shutdown/Reboot...": "Stäng av/Boota om...",
|
|
||||||
"Power": "Ström",
|
|
||||||
"Shutdown": "Stäng av",
|
|
||||||
"Reboot": "Boota om",
|
|
||||||
"Reset": "Återställ",
|
|
||||||
"Clipboard": "Urklipp",
|
|
||||||
"Edit clipboard content in the textarea below.": "Redigera urklippets innehåll i fältet nedan.",
|
|
||||||
"Full screen": "Fullskärm",
|
|
||||||
"Settings": "Inställningar",
|
|
||||||
"Shared mode": "Delat läge",
|
|
||||||
"View only": "Endast visning",
|
|
||||||
"Clip to window": "Begränsa till fönster",
|
|
||||||
"Scaling mode:": "Skalningsläge:",
|
|
||||||
"None": "Ingen",
|
|
||||||
"Local scaling": "Lokal skalning",
|
|
||||||
"Remote resizing": "Ändra storlek",
|
|
||||||
"Advanced": "Avancerat",
|
|
||||||
"Quality:": "Kvalitet:",
|
|
||||||
"Compression level:": "Kompressionsnivå:",
|
|
||||||
"Repeater ID:": "Repeater-ID:",
|
|
||||||
"WebSocket": "WebSocket",
|
|
||||||
"Encrypt": "Kryptera",
|
|
||||||
"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",
|
|
||||||
"Server identity": "Server-identitet",
|
|
||||||
"The server has provided the following identifying information:": "Servern har gett följande identifierande information:",
|
|
||||||
"Fingerprint:": "Fingeravtryck:",
|
|
||||||
"Please verify that the information is correct and press \"Approve\". Otherwise press \"Reject\".": "Kontrollera att informationen är korrekt och tryck sedan \"Godkänn\". Tryck annars \"Neka\".",
|
|
||||||
"Approve": "Godkänn",
|
|
||||||
"Reject": "Neka",
|
|
||||||
"Credentials": "Användaruppgifter",
|
|
||||||
"Username:": "Användarnamn:",
|
|
||||||
"Password:": "Lösenord:",
|
|
||||||
"Send credentials": "Skicka användaruppgifter",
|
|
||||||
"Cancel": "Avbryt",
|
|
||||||
"Must set host": "Du måste specifiera en värd",
|
|
||||||
"HTTPS is required for full functionality": "HTTPS krävs för full funktionalitet",
|
|
||||||
"Clear": "Rensa"
|
|
||||||
}
|
|
|
@ -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."
|
|
||||||
}
|
|
|
@ -1,93 +0,0 @@
|
||||||
{
|
|
||||||
"Running without HTTPS is not recommended, crashes or other issues are likely.": "不建议在没有 HTTPS 的情况下运行,可能会出现崩溃或出现其他问题。",
|
|
||||||
"Connecting...": "连接中...",
|
|
||||||
"Disconnecting...": "正在断开连接...",
|
|
||||||
"Reconnecting...": "重新连接中...",
|
|
||||||
"Internal error": "内部错误",
|
|
||||||
"Must set host": "必须设置主机",
|
|
||||||
"Failed to connect to server: ": "无法连接到服务器:",
|
|
||||||
"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": "窗口",
|
|
||||||
"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": "剪贴板",
|
|
||||||
"Edit clipboard content in the textarea below.": "在下面的文本区域中编辑剪贴板内容。",
|
|
||||||
"Full screen": "全屏",
|
|
||||||
"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):": "重新连接间隔 (ms):",
|
|
||||||
"Show dot when no cursor": "无光标时显示点",
|
|
||||||
"Logging:": "日志级别:",
|
|
||||||
"Version:": "版本:",
|
|
||||||
"Disconnect": "断开连接",
|
|
||||||
"Connect": "连接",
|
|
||||||
"Server identity": "服务器身份",
|
|
||||||
"The server has provided the following identifying information:": "服务器提供了以下识别信息:",
|
|
||||||
"Fingerprint:": "指纹:",
|
|
||||||
"Please verify that the information is correct and press \"Approve\". Otherwise press \"Reject\".": "请核实信息是否正确,并按 “同意”,否则按 “拒绝”。",
|
|
||||||
"Approve": "同意",
|
|
||||||
"Reject": "拒绝",
|
|
||||||
"Credentials": "凭证",
|
|
||||||
"Username:": "用户名:",
|
|
||||||
"Password:": "密码:",
|
|
||||||
"Send credentials": "发送凭证",
|
|
||||||
"Cancel": "取消",
|
|
||||||
"Password is required": "请提供密码",
|
|
||||||
"Disconnect timeout": "超时断开",
|
|
||||||
"viewport drag": "窗口拖动",
|
|
||||||
"Active Mouse Button": "启动鼠标按键",
|
|
||||||
"No mousebutton": "禁用鼠标按键",
|
|
||||||
"Left mousebutton": "鼠标左键",
|
|
||||||
"Middle mousebutton": "鼠标中键",
|
|
||||||
"Right mousebutton": "鼠标右键",
|
|
||||||
"Clear": "清除",
|
|
||||||
"Local Downscaling": "降低本地尺寸",
|
|
||||||
"Local Cursor": "本地光标",
|
|
||||||
"Canvas not supported.": "不支持 Canvas。"
|
|
||||||
}
|
|
|
@ -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": "取消"
|
|
||||||
}
|
|
|
@ -1,206 +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
|
|
||||||
async setup(supportedLanguages, baseURL) {
|
|
||||||
this.language = 'en'; // Default: US English
|
|
||||||
this._dictionary = undefined;
|
|
||||||
|
|
||||||
this._setupLanguage(supportedLanguages);
|
|
||||||
await this._setupDictionary(baseURL);
|
|
||||||
}
|
|
||||||
|
|
||||||
_setupLanguage(supportedLanguages) {
|
|
||||||
/*
|
|
||||||
* 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("-");
|
|
||||||
|
|
||||||
// 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: English fallback
|
|
||||||
if (userLang[0] === 'en') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Third pass pass: other 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async _setupDictionary(baseURL) {
|
|
||||||
if (baseURL) {
|
|
||||||
if (!baseURL.endsWith("/")) {
|
|
||||||
baseURL = baseURL + "/";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
baseURL = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.language === "en") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let response = await fetch(baseURL + this.language + ".json");
|
|
||||||
if (!response.ok) {
|
|
||||||
throw Error("" + response.status + " " + response.statusText);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._dictionary = await response.json();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 translateString(str) {
|
|
||||||
// We assume surrounding whitespace, and whitespace around line
|
|
||||||
// breaks is just for source formatting
|
|
||||||
str = str.split("\n").map(s => s.trim()).join(" ").trim();
|
|
||||||
return self.get(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
function translateAttribute(elem, attr) {
|
|
||||||
const str = translateString(elem.getAttribute(attr));
|
|
||||||
elem.setAttribute(attr, str);
|
|
||||||
}
|
|
||||||
|
|
||||||
function translateTextNode(node) {
|
|
||||||
const str = translateString(node.data);
|
|
||||||
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);
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* noVNC base CSS
|
* 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)
|
* noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
|
||||||
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
|
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
|
||||||
*/
|
*/
|
||||||
|
@ -19,24 +21,10 @@
|
||||||
* 10000: Max (used for polyfills)
|
* 10000: Max (used for polyfills)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
* State variables (set on :root):
|
|
||||||
*
|
|
||||||
* noVNC_loading: Page is still loading
|
|
||||||
* noVNC_connecting: Connecting to server
|
|
||||||
* noVNC_reconnecting: Re-establishing a connection
|
|
||||||
* noVNC_connected: Connected to server (most common state)
|
|
||||||
* noVNC_disconnecting: Disconnecting from server
|
|
||||||
*/
|
|
||||||
|
|
||||||
:root {
|
|
||||||
font-family: sans-serif;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin:0;
|
margin:0;
|
||||||
padding:0;
|
padding:0;
|
||||||
|
font-family: Helvetica;
|
||||||
/*Background image with light grey curve.*/
|
/*Background image with light grey curve.*/
|
||||||
background-color:#494949;
|
background-color:#494949;
|
||||||
background-repeat:no-repeat;
|
background-repeat:no-repeat;
|
||||||
|
@ -53,10 +41,6 @@ html {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.noVNC_disabled {
|
|
||||||
color: var(--novnc-grey);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------------------------------
|
/* ----------------------------------------
|
||||||
* Spinner
|
* Spinner
|
||||||
* ----------------------------------------
|
* ----------------------------------------
|
||||||
|
@ -69,7 +53,6 @@ html {
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
box-shadow: -60px 10px 0 rgba(255, 255, 255, 0);
|
|
||||||
animation: noVNC_spinner 1.0s linear infinite;
|
animation: noVNC_spinner 1.0s linear infinite;
|
||||||
}
|
}
|
||||||
.noVNC_spinner::before {
|
.noVNC_spinner::before {
|
||||||
|
@ -92,6 +75,87 @@ html {
|
||||||
50% { box-shadow: 60px 10px 0 rgba(255, 255, 255, 0); width: 10px; }
|
50% { box-shadow: 60px 10px 0 rgba(255, 255, 255, 0); width: 10px; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------
|
||||||
|
* Input Elements
|
||||||
|
* ----------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
input[type=input], input[type=password], input:not([type]), textarea {
|
||||||
|
/* Disable default rendering */
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
background: none;
|
||||||
|
|
||||||
|
margin: 2px;
|
||||||
|
padding: 2px;
|
||||||
|
border: 1px solid rgb(192, 192, 192);
|
||||||
|
border-radius: 5px;
|
||||||
|
color: black;
|
||||||
|
background: linear-gradient(to top, rgb(255, 255, 255) 80%, rgb(240, 240, 240));
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button], input[type=submit], select {
|
||||||
|
/* Disable default rendering */
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
background: none;
|
||||||
|
|
||||||
|
margin: 2px;
|
||||||
|
padding: 2px;
|
||||||
|
border: 1px solid rgb(192, 192, 192);
|
||||||
|
border-bottom-width: 2px;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: black;
|
||||||
|
background: linear-gradient(to top, rgb(255, 255, 255), rgb(240, 240, 240));
|
||||||
|
|
||||||
|
/* This avoids it jumping around when :active */
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button], input[type=submit] {
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
option {
|
||||||
|
color: black;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=input]:focus, input[type=password]:focus,
|
||||||
|
input:not([type]):focus, input[type=button]:focus,
|
||||||
|
input[type=submit]: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=submit]::-moz-focus-inner {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=input]:disabled, input[type=password]:disabled,
|
||||||
|
input:not([type]):disabled, input[type=button]:disabled,
|
||||||
|
input[type=submit]:disabled,
|
||||||
|
textarea:disabled, select:disabled {
|
||||||
|
color: rgb(128, 128, 128);
|
||||||
|
background: rgb(240, 240, 240);
|
||||||
|
}
|
||||||
|
|
||||||
|
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=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));
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------------------------------------
|
/* ----------------------------------------
|
||||||
* WebKit centering hacks
|
* WebKit centering hacks
|
||||||
* ----------------------------------------
|
* ----------------------------------------
|
||||||
|
@ -118,15 +182,13 @@ html {
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
.noVNC_vcenter {
|
.noVNC_vcenter {
|
||||||
display: flex !important;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0 !important;
|
|
||||||
padding: 0 !important;
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
.noVNC_vcenter > * {
|
.noVNC_vcenter > * {
|
||||||
|
@ -148,39 +210,31 @@ html {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#noVNC_fallback_error {
|
#noVNC_fallback_error {
|
||||||
|
position: fixed;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
visibility: hidden;
|
left: 50%;
|
||||||
/* Put a dark background in front of everything but the error,
|
transform: translate(-50%, -50px);
|
||||||
and don't let mouse events pass through */
|
|
||||||
background: rgba(0, 0, 0, 0.8);
|
|
||||||
pointer-events: all;
|
|
||||||
}
|
|
||||||
#noVNC_fallback_error.noVNC_open {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
#noVNC_fallback_error > div {
|
|
||||||
max-width: calc(100vw - 30px - 30px);
|
|
||||||
max-height: calc(100vh - 30px - 30px);
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
padding: 15px;
|
|
||||||
|
|
||||||
transition: 0.5s ease-in-out;
|
transition: 0.5s ease-in-out;
|
||||||
|
|
||||||
transform: translateY(-50px);
|
visibility: hidden;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|
||||||
|
top: 60px;
|
||||||
|
padding: 15px;
|
||||||
|
width: auto;
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
word-wrap: break-word;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
||||||
border-radius: 12px;
|
border-radius: 10px;
|
||||||
box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5);
|
box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5);
|
||||||
background: rgba(200,55,55,0.8);
|
background: rgba(200,55,55,0.8);
|
||||||
}
|
}
|
||||||
#noVNC_fallback_error.noVNC_open > div {
|
#noVNC_fallback_error.noVNC_open {
|
||||||
transform: translateY(0);
|
transform: translate(-50%, 0);
|
||||||
|
visibility: visible;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,13 +242,6 @@ html {
|
||||||
font-weight: normal;
|
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 {
|
#noVNC_fallback_error .noVNC_location {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
|
@ -206,15 +253,13 @@ html {
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-family: monospace;
|
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
border: 1px solid rgba(0, 0, 0, 0.5);
|
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||||
background: rgba(0, 0, 0, 0.2);
|
background: rgba(0, 0, 0, 0.2);
|
||||||
overflow: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------
|
/* ----------------------------------------
|
||||||
* Control bar
|
* Control Bar
|
||||||
* ----------------------------------------
|
* ----------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -242,12 +287,9 @@ html {
|
||||||
|
|
||||||
transition: 0.5s ease-in-out;
|
transition: 0.5s ease-in-out;
|
||||||
|
|
||||||
background-color: var(--novnc-blue);
|
background-color: rgb(110, 132, 163);
|
||||||
border-radius: 0 12px 12px 0;
|
border-radius: 0 10px 10px 0;
|
||||||
|
|
||||||
user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-webkit-touch-callout: none; /* Disable iOS image long-press popup */
|
|
||||||
}
|
}
|
||||||
#noVNC_control_bar.noVNC_open {
|
#noVNC_control_bar.noVNC_open {
|
||||||
box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5);
|
box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5);
|
||||||
|
@ -268,7 +310,7 @@ html {
|
||||||
}
|
}
|
||||||
.noVNC_right #noVNC_control_bar {
|
.noVNC_right #noVNC_control_bar {
|
||||||
left: 100%;
|
left: 100%;
|
||||||
border-radius: 12px 0 0 12px;
|
border-radius: 10px 0 0 10px;
|
||||||
}
|
}
|
||||||
.noVNC_right #noVNC_control_bar.noVNC_open {
|
.noVNC_right #noVNC_control_bar.noVNC_open {
|
||||||
left: 0;
|
left: 0;
|
||||||
|
@ -286,8 +328,8 @@ html {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 6px;
|
border-radius: 5px;
|
||||||
background-color: var(--novnc-darkblue);
|
background-color: rgb(83, 99, 122);
|
||||||
background-image: url("../images/handle_bg.svg");
|
background-image: url("../images/handle_bg.svg");
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: right;
|
background-position: right;
|
||||||
|
@ -320,87 +362,63 @@ html {
|
||||||
.noVNC_right #noVNC_control_bar.noVNC_open #noVNC_control_bar_handle:after {
|
.noVNC_right #noVNC_control_bar.noVNC_open #noVNC_control_bar_handle:after {
|
||||||
transform: none;
|
transform: none;
|
||||||
}
|
}
|
||||||
/* Larger touch area for the handle, used when a touch screen is available */
|
|
||||||
#noVNC_control_bar_handle div {
|
#noVNC_control_bar_handle div {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -35px;
|
right: -35px;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 100%;
|
height: 50px;
|
||||||
|
}
|
||||||
|
:root:not(.noVNC_touch) #noVNC_control_bar_handle div {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@media (any-pointer: coarse) {
|
|
||||||
#noVNC_control_bar_handle div {
|
|
||||||
display: initial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.noVNC_right #noVNC_control_bar_handle div {
|
.noVNC_right #noVNC_control_bar_handle div {
|
||||||
left: -35px;
|
left: -35px;
|
||||||
right: auto;
|
right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#noVNC_control_bar > .noVNC_scroll {
|
#noVNC_control_bar .noVNC_scroll {
|
||||||
max-height: 100vh; /* Chrome is buggy with 100% */
|
max-height: 100vh; /* Chrome is buggy with 100% */
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 0 10px;
|
padding: 0 10px 0 5px;
|
||||||
|
}
|
||||||
|
.noVNC_right #noVNC_control_bar .noVNC_scroll {
|
||||||
|
padding: 0 5px 0 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#noVNC_control_bar > .noVNC_scroll > * {
|
/* General button style */
|
||||||
|
.noVNC_button {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 10px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Control bar hint */
|
|
||||||
#noVNC_hint_anchor {
|
|
||||||
position: fixed;
|
|
||||||
right: -50px;
|
|
||||||
left: auto;
|
|
||||||
}
|
|
||||||
#noVNC_control_bar_anchor.noVNC_right + #noVNC_hint_anchor {
|
|
||||||
left: -50px;
|
|
||||||
right: auto;
|
|
||||||
}
|
|
||||||
#noVNC_control_bar_hint {
|
|
||||||
position: relative;
|
|
||||||
transform: 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 var(--novnc-darkblue);
|
|
||||||
border-radius: 12px;
|
|
||||||
transition-delay: 0s;
|
|
||||||
}
|
|
||||||
#noVNC_control_bar_hint.noVNC_active {
|
|
||||||
visibility: visible;
|
|
||||||
opacity: 1;
|
|
||||||
transition-delay: 0.2s;
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
#noVNC_control_bar_hint.noVNC_notransition {
|
|
||||||
transition: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Control bar buttons */
|
|
||||||
#noVNC_control_bar .noVNC_button {
|
|
||||||
min-width: unset;
|
|
||||||
padding: 4px 4px;
|
padding: 4px 4px;
|
||||||
|
margin: 10px 0;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
border:1px solid rgba(255, 255, 255, 0.2);
|
border:1px solid rgba(255, 255, 255, 0.2);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background-color: transparent;
|
|
||||||
}
|
}
|
||||||
#noVNC_control_bar .noVNC_button.noVNC_selected {
|
.noVNC_button.noVNC_selected {
|
||||||
border-color: rgba(0, 0, 0, 0.8);
|
border-color: rgba(0, 0, 0, 0.8);
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
background: rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
#noVNC_control_bar .noVNC_button.noVNC_hidden {
|
.noVNC_button:disabled {
|
||||||
display: none !important;
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
.noVNC_button:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.noVNC_button:active {
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 3px;
|
||||||
|
}
|
||||||
|
: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 {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
.noVNC_button.noVNC_hidden {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Panels */
|
/* Panels */
|
||||||
|
@ -409,8 +427,6 @@ html {
|
||||||
|
|
||||||
transition: 0.5s ease-in-out;
|
transition: 0.5s ease-in-out;
|
||||||
|
|
||||||
box-sizing: border-box; /* so max-width don't have to care about padding */
|
|
||||||
max-width: calc(100vw - 75px - 25px); /* minus left and right margins */
|
|
||||||
max-height: 100vh; /* Chrome is buggy with 100% */
|
max-height: 100vh; /* Chrome is buggy with 100% */
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
@ -421,7 +437,7 @@ html {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
|
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 12px;
|
border-radius: 10px;
|
||||||
color: #000;
|
color: #000;
|
||||||
border: 2px solid #E0E0E0;
|
border: 2px solid #E0E0E0;
|
||||||
box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5);
|
box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5);
|
||||||
|
@ -442,71 +458,33 @@ html {
|
||||||
transform: translateX(-75px);
|
transform: translateX(-75px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.noVNC_panel > * {
|
|
||||||
display: block;
|
|
||||||
margin: 10px auto;
|
|
||||||
}
|
|
||||||
.noVNC_panel > *:first-child {
|
|
||||||
margin-top: 0 !important;
|
|
||||||
}
|
|
||||||
.noVNC_panel > *:last-child {
|
|
||||||
margin-bottom: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.noVNC_panel hr {
|
.noVNC_panel hr {
|
||||||
border: none;
|
border: none;
|
||||||
border-top: 1px solid var(--novnc-lightgrey);
|
border-top: 1px solid rgb(192, 192, 192);
|
||||||
width: 100%; /* <hr> inside a flexbox will otherwise be 0px wide */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.noVNC_panel label {
|
.noVNC_panel label {
|
||||||
display: block;
|
display: block;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
margin: 5px;
|
|
||||||
}
|
|
||||||
@media (max-width: 540px) {
|
|
||||||
/* Allow wrapping on small screens */
|
|
||||||
.noVNC_panel label {
|
|
||||||
white-space: unset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.noVNC_panel li {
|
|
||||||
margin: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.noVNC_panel .noVNC_heading {
|
.noVNC_panel .noVNC_heading {
|
||||||
background-color: var(--novnc-blue);
|
background-color: rgb(110, 132, 163);
|
||||||
border-radius: 6px;
|
border-radius: 5px;
|
||||||
padding: 5px 8px;
|
padding: 5px;
|
||||||
/* Compensate for padding in image */
|
/* Compensate for padding in image */
|
||||||
padding-right: 11px;
|
padding-right: 8px;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: bold;
|
margin-bottom: 10px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.noVNC_panel .noVNC_heading img {
|
.noVNC_panel .noVNC_heading img {
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
.noVNC_panel form {
|
.noVNC_submit {
|
||||||
display: flex;
|
float: right;
|
||||||
flex-direction: column;
|
|
||||||
gap: 12px
|
|
||||||
}
|
|
||||||
|
|
||||||
.noVNC_panel .button_row {
|
|
||||||
margin-top: 10px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
.noVNC_panel .button_row *:only-child {
|
|
||||||
margin-left: auto; /* Align single buttons to the right */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Expanders */
|
/* Expanders */
|
||||||
|
@ -526,8 +504,8 @@ html {
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
background: rgba(0, 0, 0, 0.04);
|
background: rgba(0, 0, 0, 0.05);
|
||||||
border-radius: 6px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
.noVNC_expander:not(.noVNC_open) ~ * {
|
.noVNC_expander:not(.noVNC_open) ~ * {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -539,12 +517,6 @@ html {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.noVNC_logo + hr {
|
|
||||||
/* Remove all but top border */
|
|
||||||
border: none;
|
|
||||||
border-top: 1px solid rgba(255, 255, 255, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
:root:not(.noVNC_connected) #noVNC_view_drag_button {
|
:root:not(.noVNC_connected) #noVNC_view_drag_button {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -553,39 +525,32 @@ html {
|
||||||
:root:not(.noVNC_connected) #noVNC_mobile_buttons {
|
:root:not(.noVNC_connected) #noVNC_mobile_buttons {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@media not all and (any-pointer: coarse) {
|
:root:not(.noVNC_touch) #noVNC_mobile_buttons {
|
||||||
/* FIXME: The button for the virtual keyboard is the only button in this
|
|
||||||
group of "mobile buttons". It is bad to assume that no touch
|
|
||||||
devices have physical keyboards available. Hopefully we can get
|
|
||||||
a media query for this:
|
|
||||||
https://github.com/w3c/csswg-drafts/issues/3871 */
|
|
||||||
:root.noVNC_connected #noVNC_mobile_buttons {
|
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Extra manual keys */
|
/* Extra manual keys */
|
||||||
:root:not(.noVNC_connected) #noVNC_toggle_extra_keys_button {
|
:root:not(.noVNC_connected) #noVNC_extra_keys {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#noVNC_modifiers {
|
#noVNC_modifiers {
|
||||||
background-color: var(--novnc-darkgrey);
|
background-color: rgb(92, 92, 92);
|
||||||
border: none;
|
border: none;
|
||||||
padding: 10px;
|
padding: 0 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Shutdown/Reboot */
|
/* XVP Shutdown/Reboot */
|
||||||
:root:not(.noVNC_connected) #noVNC_power_button {
|
:root:not(.noVNC_connected) #noVNC_xvp_button {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#noVNC_power {
|
#noVNC_xvp {
|
||||||
}
|
}
|
||||||
#noVNC_power_buttons {
|
#noVNC_xvp_buttons {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#noVNC_power input[type=button] {
|
#noVNC_xvp input[type=button] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,16 +558,13 @@ html {
|
||||||
:root:not(.noVNC_connected) #noVNC_clipboard_button {
|
:root:not(.noVNC_connected) #noVNC_clipboard_button {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
#noVNC_clipboard {
|
||||||
|
/* Full screen, minus padding and left and right margins */
|
||||||
|
max-width: calc(100vw - 2*15px - 75px - 25px);
|
||||||
|
}
|
||||||
#noVNC_clipboard_text {
|
#noVNC_clipboard_text {
|
||||||
width: 360px;
|
width: 500px;
|
||||||
min-width: 150px;
|
|
||||||
height: 160px;
|
|
||||||
min-height: 70px;
|
|
||||||
|
|
||||||
box-sizing: border-box;
|
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
/* minus approximate height of title, height of subtitle, and margin */
|
|
||||||
max-height: calc(100vh - 10em - 25px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Settings */
|
/* Settings */
|
||||||
|
@ -610,17 +572,9 @@ html {
|
||||||
}
|
}
|
||||||
#noVNC_settings ul {
|
#noVNC_settings ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
#noVNC_settings button,
|
|
||||||
#noVNC_settings select,
|
|
||||||
#noVNC_settings textarea,
|
|
||||||
#noVNC_settings input:not([type=checkbox]):not([type=radio]) {
|
|
||||||
margin-left: 6px;
|
|
||||||
/* Prevent inputs in settings from being too wide */
|
|
||||||
max-width: calc(100% - 6px - var(--input-xpadding) * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#noVNC_setting_port {
|
#noVNC_setting_port {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
}
|
}
|
||||||
|
@ -628,23 +582,13 @@ html {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Version */
|
/* Connection Controls */
|
||||||
|
|
||||||
.noVNC_version_wrapper {
|
|
||||||
font-size: small;
|
|
||||||
}
|
|
||||||
|
|
||||||
.noVNC_version {
|
|
||||||
margin-left: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Connection controls */
|
|
||||||
:root:not(.noVNC_connected) #noVNC_disconnect_button {
|
:root:not(.noVNC_connected) #noVNC_disconnect_button {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------
|
/* ----------------------------------------
|
||||||
* Status dialog
|
* Status Dialog
|
||||||
* ----------------------------------------
|
* ----------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -670,7 +614,7 @@ html {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-content: center;
|
align-content: center;
|
||||||
|
|
||||||
line-height: 1.6;
|
line-height: 25px;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
||||||
|
@ -710,7 +654,7 @@ html {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------
|
/* ----------------------------------------
|
||||||
* Connect dialog
|
* Connect Dialog
|
||||||
* ----------------------------------------
|
* ----------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -734,7 +678,7 @@ html {
|
||||||
font-size: 80px;
|
font-size: 80px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
border-radius: 6px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
@media (max-width: 440px) {
|
@media (max-width: 440px) {
|
||||||
#noVNC_connect_dlg {
|
#noVNC_connect_dlg {
|
||||||
|
@ -744,29 +688,37 @@ html {
|
||||||
font-size: calc(25vw - 30px);
|
font-size: calc(25vw - 30px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#noVNC_connect_dlg div {
|
#noVNC_connect_button {
|
||||||
padding: 18px;
|
cursor: pointer;
|
||||||
|
|
||||||
background-color: var(--novnc-darkgrey);
|
padding: 10px;
|
||||||
|
|
||||||
|
color: white;
|
||||||
|
background-color: rgb(110, 132, 163);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
|
||||||
box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5);
|
box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
#noVNC_connect_button {
|
#noVNC_connect_button div {
|
||||||
width: 100%;
|
margin: 2px;
|
||||||
padding: 6px 30px;
|
padding: 5px 30px;
|
||||||
cursor: pointer;
|
border: 1px solid rgb(83, 99, 122);
|
||||||
border-color: transparent;
|
border-bottom-width: 2px;
|
||||||
border-radius: 12px;
|
border-radius: 5px;
|
||||||
background-color: var(--novnc-blue);
|
background: linear-gradient(to top, rgb(110, 132, 163), rgb(99, 119, 147));
|
||||||
color: white;
|
|
||||||
|
|
||||||
display: flex;
|
/* This avoids it jumping around when :active */
|
||||||
justify-content: center;
|
vertical-align: middle;
|
||||||
place-items: center;
|
}
|
||||||
gap: 4px;
|
#noVNC_connect_button div:active {
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
:root:not(.noVNC_touch) #noVNC_connect_button div:hover {
|
||||||
|
background: linear-gradient(to top, rgb(110, 132, 163), rgb(105, 125, 155));
|
||||||
}
|
}
|
||||||
|
|
||||||
#noVNC_connect_button img {
|
#noVNC_connect_button img {
|
||||||
|
@ -775,53 +727,32 @@ html {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------
|
/* ----------------------------------------
|
||||||
* Server verification dialog
|
* Password Dialog
|
||||||
* ----------------------------------------
|
* ----------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#noVNC_verify_server_dlg {
|
#noVNC_password_dlg {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
transform: translateY(-50px);
|
transform: translateY(-50px);
|
||||||
}
|
}
|
||||||
#noVNC_verify_server_dlg.noVNC_open {
|
#noVNC_password_dlg.noVNC_open {
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
#noVNC_fingerprint_block {
|
#noVNC_password_dlg ul {
|
||||||
margin: 10px;
|
list-style: none;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------
|
/* ----------------------------------------
|
||||||
* Password dialog
|
* Main Area
|
||||||
* ----------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
#noVNC_credentials_dlg {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
transform: translateY(-50px);
|
|
||||||
}
|
|
||||||
#noVNC_credentials_dlg.noVNC_open {
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
#noVNC_username_block.noVNC_hidden,
|
|
||||||
#noVNC_password_block.noVNC_hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ----------------------------------------
|
|
||||||
* Main area
|
|
||||||
* ----------------------------------------
|
* ----------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Transition screen */
|
/* Transition screen */
|
||||||
#noVNC_transition {
|
#noVNC_transition {
|
||||||
transition: 0.5s ease-in-out;
|
display: none;
|
||||||
|
|
||||||
display: flex;
|
|
||||||
opacity: 0;
|
|
||||||
visibility: hidden;
|
|
||||||
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -838,12 +769,10 @@ html {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
:root.noVNC_loading #noVNC_transition,
|
|
||||||
:root.noVNC_connecting #noVNC_transition,
|
:root.noVNC_connecting #noVNC_transition,
|
||||||
:root.noVNC_disconnecting #noVNC_transition,
|
:root.noVNC_disconnecting #noVNC_transition,
|
||||||
:root.noVNC_reconnecting #noVNC_transition {
|
:root.noVNC_reconnecting #noVNC_transition {
|
||||||
opacity: 1;
|
display: flex;
|
||||||
visibility: visible;
|
|
||||||
}
|
}
|
||||||
:root:not(.noVNC_reconnecting) #noVNC_cancel_reconnect_button {
|
:root:not(.noVNC_reconnecting) #noVNC_cancel_reconnect_button {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -859,12 +788,6 @@ html {
|
||||||
background-color: #313131;
|
background-color: #313131;
|
||||||
border-bottom-right-radius: 800px 600px;
|
border-bottom-right-radius: 800px 600px;
|
||||||
/*border-top-left-radius: 800px 600px;*/
|
/*border-top-left-radius: 800px 600px;*/
|
||||||
|
|
||||||
/* If selection isn't disabled, long-pressing stuff in the sidebar
|
|
||||||
can accidentally select the container or the canvas. This can
|
|
||||||
happen when attempting to move the handle. */
|
|
||||||
user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#noVNC_keyboardinput {
|
#noVNC_keyboardinput {
|
||||||
|
@ -879,6 +802,27 @@ html {
|
||||||
ime-mode: disabled;
|
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.*/
|
/*Default noVNC logo.*/
|
||||||
/* From: http://fonts.googleapis.com/css?family=Orbitron:700 */
|
/* From: http://fonts.googleapis.com/css?family=Orbitron:700 */
|
||||||
@font-face {
|
@font-face {
|
||||||
|
@ -890,13 +834,13 @@ html {
|
||||||
}
|
}
|
||||||
|
|
||||||
.noVNC_logo {
|
.noVNC_logo {
|
||||||
color: var(--novnc-yellow);
|
color:yellow;
|
||||||
font-family: 'Orbitron', 'OrbitronTTF', sans-serif;
|
font-family: 'Orbitron', 'OrbitronTTF', sans-serif;
|
||||||
line-height: 0.9;
|
line-height:90%;
|
||||||
text-shadow: 0.1em 0.1em 0 black;
|
text-shadow: 0.1em 0.1em 0 black;
|
||||||
}
|
}
|
||||||
.noVNC_logo span{
|
.noVNC_logo span{
|
||||||
color: var(--novnc-green);
|
color:green;
|
||||||
}
|
}
|
||||||
|
|
||||||
#noVNC_bell {
|
#noVNC_bell {
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* noVNC general CSS constant variables
|
|
||||||
* Copyright (C) 2025 The noVNC authors
|
|
||||||
* noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
|
|
||||||
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* ---------- COLORS ----------- */
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--novnc-grey: rgb(128, 128, 128);
|
|
||||||
--novnc-lightgrey: rgb(192, 192, 192);
|
|
||||||
--novnc-darkgrey: rgb(92, 92, 92);
|
|
||||||
|
|
||||||
/* Transparent to make button colors adapt to the background */
|
|
||||||
--novnc-buttongrey: rgba(192, 192, 192, 0.5);
|
|
||||||
|
|
||||||
--novnc-blue: rgb(110, 132, 163);
|
|
||||||
--novnc-lightblue: rgb(74, 144, 217);
|
|
||||||
--novnc-darkblue: rgb(83, 99, 122);
|
|
||||||
|
|
||||||
--novnc-green: rgb(0, 128, 0);
|
|
||||||
--novnc-yellow: rgb(255, 255, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------ MISC PROPERTIES ------ */
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--input-xpadding: 1em;
|
|
||||||
}
|
|
|
@ -1,628 +0,0 @@
|
||||||
/*
|
|
||||||
* noVNC general input element CSS
|
|
||||||
* Copyright (C) 2025 The noVNC authors
|
|
||||||
* noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
|
|
||||||
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* ------- SHARED BETWEEN INPUT ELEMENTS -------- */
|
|
||||||
|
|
||||||
input,
|
|
||||||
textarea,
|
|
||||||
button,
|
|
||||||
select,
|
|
||||||
input::file-selector-button {
|
|
||||||
padding: 0.5em var(--input-xpadding);
|
|
||||||
border-radius: 6px;
|
|
||||||
appearance: none;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
|
|
||||||
/* Respect standard font settings */
|
|
||||||
font: inherit;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
input:disabled,
|
|
||||||
textarea:disabled,
|
|
||||||
button:disabled,
|
|
||||||
select:disabled,
|
|
||||||
label[disabled] {
|
|
||||||
opacity: 0.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:focus-visible,
|
|
||||||
textarea:focus-visible,
|
|
||||||
button:focus-visible,
|
|
||||||
select:focus-visible,
|
|
||||||
input:focus-visible::file-selector-button {
|
|
||||||
outline: 2px solid var(--novnc-lightblue);
|
|
||||||
outline-offset: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------- TEXT INPUT -------- */
|
|
||||||
|
|
||||||
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 {
|
|
||||||
border: 1px solid var(--novnc-lightgrey);
|
|
||||||
/* Account for borders on text inputs, buttons dont have borders */
|
|
||||||
padding: calc(0.5em - 1px) var(--input-xpadding);
|
|
||||||
}
|
|
||||||
input:not([type]):focus-visible,
|
|
||||||
input[type=date]:focus-visible,
|
|
||||||
input[type=datetime-local]:focus-visible,
|
|
||||||
input[type=email]:focus-visible,
|
|
||||||
input[type=month]:focus-visible,
|
|
||||||
input[type=number]:focus-visible,
|
|
||||||
input[type=password]:focus-visible,
|
|
||||||
input[type=search]:focus-visible,
|
|
||||||
input[type=tel]:focus-visible,
|
|
||||||
input[type=text]:focus-visible,
|
|
||||||
input[type=time]:focus-visible,
|
|
||||||
input[type=url]:focus-visible,
|
|
||||||
input[type=week]:focus-visible,
|
|
||||||
textarea:focus-visible {
|
|
||||||
outline-offset: -1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
margin: unset; /* Remove Firefox's built in margin */
|
|
||||||
/* Prevent layout from shifting when scrollbars show */
|
|
||||||
scrollbar-gutter: stable;
|
|
||||||
/* Make textareas show at minimum one line. This does not work when
|
|
||||||
using box-sizing border-box, in which case, vertical padding and
|
|
||||||
border width needs to be taken into account. */
|
|
||||||
min-height: 1lh;
|
|
||||||
vertical-align: baseline; /* Firefox gives "text-bottom" by default */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------- NUMBER PICKERS ------- */
|
|
||||||
|
|
||||||
/* We can't style the number spinner buttons:
|
|
||||||
https://github.com/w3c/csswg-drafts/issues/8777 */
|
|
||||||
input[type=number]::-webkit-inner-spin-button,
|
|
||||||
input[type=number]::-webkit-outer-spin-button {
|
|
||||||
/* Get rid of increase/decrease buttons in WebKit */
|
|
||||||
appearance: none;
|
|
||||||
}
|
|
||||||
input[type=number] {
|
|
||||||
/* Get rid of increase/decrease buttons in Firefox */
|
|
||||||
appearance: textfield;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------- BUTTON ACTIVATIONS -------- */
|
|
||||||
|
|
||||||
/* A color overlay that depends on the activation level. The level can then be
|
|
||||||
set for different states on an element, for example hover and click on a
|
|
||||||
<button>. */
|
|
||||||
input, button, select, option,
|
|
||||||
input::file-selector-button,
|
|
||||||
.button-activations {
|
|
||||||
--button-activation-level: 0;
|
|
||||||
/* Note that CSS variables aren't functions, beware when inheriting */
|
|
||||||
--button-activation-alpha: calc(0.08 * var(--button-activation-level));
|
|
||||||
/* FIXME: We want the image() function instead of the linear-gradient()
|
|
||||||
function below. But it's not supported in the browsers yet. */
|
|
||||||
--button-activation-overlay:
|
|
||||||
linear-gradient(rgba(0, 0, 0, var(--button-activation-alpha))
|
|
||||||
100%, transparent);
|
|
||||||
--button-activation-overlay-light:
|
|
||||||
linear-gradient(rgba(255, 255, 255, calc(0.23 * var(--button-activation-level)))
|
|
||||||
100%, transparent);
|
|
||||||
}
|
|
||||||
.button-activations {
|
|
||||||
background-image: var(--button-activation-overlay);
|
|
||||||
|
|
||||||
/* Disable Chrome's touch tap highlight to avoid conflicts with overlay */
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
}
|
|
||||||
/* When we want the light overlay on activations instead.
|
|
||||||
This is best used on elements with darker backgrounds. */
|
|
||||||
.button-activations.light-overlay {
|
|
||||||
background-image: var(--button-activation-overlay-light);
|
|
||||||
/* Can't use the normal blend mode since that gives washed out colors. */
|
|
||||||
/* FIXME: For elements with these activation overlays we'd like only
|
|
||||||
the luminosity to change. The proprty "background-blend-mode" set
|
|
||||||
to "luminosity" sounds good, but it doesn't work as intended,
|
|
||||||
see: https://bugzilla.mozilla.org/show_bug.cgi?id=1806417 */
|
|
||||||
background-blend-mode: overlay;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:hover, button:hover, select:hover, option:hover,
|
|
||||||
input::file-selector-button:hover,
|
|
||||||
.button-activations:hover {
|
|
||||||
--button-activation-level: 1;
|
|
||||||
}
|
|
||||||
/* Unfortunately we have to disable the :hover effect on touch devices,
|
|
||||||
otherwise the style lingers after tapping the button. */
|
|
||||||
@media (any-pointer: coarse) {
|
|
||||||
input:hover, button:hover, select:hover, option:hover,
|
|
||||||
input::file-selector-button:hover,
|
|
||||||
.button-activations:hover {
|
|
||||||
--button-activation-level: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
input:active, button:active, select:active, option:active,
|
|
||||||
input::file-selector-button:active,
|
|
||||||
.button-activations:active {
|
|
||||||
--button-activation-level: 2;
|
|
||||||
}
|
|
||||||
input:disabled, button:disabled, select:disabled, select:disabled option,
|
|
||||||
input:disabled::file-selector-button,
|
|
||||||
.button-activations:disabled {
|
|
||||||
--button-activation-level: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------- BUTTONS -------- */
|
|
||||||
|
|
||||||
input[type=button],
|
|
||||||
input[type=color],
|
|
||||||
input[type=image],
|
|
||||||
input[type=reset],
|
|
||||||
input[type=submit],
|
|
||||||
input::file-selector-button,
|
|
||||||
button,
|
|
||||||
select {
|
|
||||||
min-width: 8em;
|
|
||||||
border: none;
|
|
||||||
color: black;
|
|
||||||
font-weight: bold;
|
|
||||||
background-color: var(--novnc-buttongrey);
|
|
||||||
background-image: var(--button-activation-overlay);
|
|
||||||
cursor: pointer;
|
|
||||||
/* Disable Chrome's touch tap highlight */
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
}
|
|
||||||
input[type=button]:disabled,
|
|
||||||
input[type=color]:disabled,
|
|
||||||
input[type=image]:disabled,
|
|
||||||
input[type=reset]:disabled,
|
|
||||||
input[type=submit]:disabled,
|
|
||||||
input:disabled::file-selector-button,
|
|
||||||
button:disabled,
|
|
||||||
select:disabled {
|
|
||||||
/* See Firefox bug:
|
|
||||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1798304 */
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=button],
|
|
||||||
input[type=color],
|
|
||||||
input[type=reset],
|
|
||||||
input[type=submit] {
|
|
||||||
/* Workaround for text-overflow bugs in Firefox and Chromium:
|
|
||||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1800077
|
|
||||||
https://bugs.chromium.org/p/chromium/issues/detail?id=1383144 */
|
|
||||||
overflow: clip;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------- COLOR PICKERS ------- */
|
|
||||||
|
|
||||||
input[type=color] {
|
|
||||||
min-width: unset;
|
|
||||||
box-sizing: content-box;
|
|
||||||
width: 1.4em;
|
|
||||||
height: 1.4em;
|
|
||||||
}
|
|
||||||
input[type=color]::-webkit-color-swatch-wrapper {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
/* -webkit-color-swatch & -moz-color-swatch cant be in a selector list:
|
|
||||||
https://bugs.chromium.org/p/chromium/issues/detail?id=1154623 */
|
|
||||||
input[type=color]::-webkit-color-swatch {
|
|
||||||
border: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
input[type=color]::-moz-color-swatch {
|
|
||||||
border: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- SHARED BETWEEN CHECKBOXES, RADIOBUTTONS AND THE TOGGLE CLASS -- */
|
|
||||||
|
|
||||||
input[type=radio],
|
|
||||||
input[type=checkbox] {
|
|
||||||
display: inline-flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
background-color: var(--novnc-buttongrey);
|
|
||||||
background-image: var(--button-activation-overlay);
|
|
||||||
/* Disable Chrome's touch tap highlight to avoid conflicts with overlay */
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
width: 16px;
|
|
||||||
--checkradio-height: 16px;
|
|
||||||
height: var(--checkradio-height);
|
|
||||||
padding: 0;
|
|
||||||
margin: 0 6px 0 0;
|
|
||||||
/* Don't have transitions for outline in order to be consistent
|
|
||||||
with other elements */
|
|
||||||
transition: all 0.2s, outline-color 0s, outline-offset 0s;
|
|
||||||
|
|
||||||
/* A transparent outline in order to work around a graphical clipping issue
|
|
||||||
in WebKit. See bug: https://bugs.webkit.org/show_bug.cgi?id=256003 */
|
|
||||||
outline: 1px solid transparent;
|
|
||||||
position: relative; /* Since ::before & ::after are absolute positioned */
|
|
||||||
|
|
||||||
/* We want to align with the middle of capital letters, this requires
|
|
||||||
a workaround. The default behavior is to align the bottom of the element
|
|
||||||
on top of the text baseline, this is too far up.
|
|
||||||
We want to push the element down half the difference in height between
|
|
||||||
it and a capital X. In our font, the height of a capital "X" is 0.698em.
|
|
||||||
*/
|
|
||||||
vertical-align: calc(0px - (var(--checkradio-height) - 0.698em) / 2);
|
|
||||||
/* FIXME: Could write 1cap instead of 0.698em, but it's only supported in
|
|
||||||
Firefox as of 2023 */
|
|
||||||
/* FIXME: We probably want to use round() here, see bug 8148 */
|
|
||||||
}
|
|
||||||
input[type=radio]:focus-visible,
|
|
||||||
input[type=checkbox]:focus-visible {
|
|
||||||
outline-color: var(--novnc-lightblue);
|
|
||||||
}
|
|
||||||
input[type=checkbox]::before,
|
|
||||||
input[type=checkbox]:not(.toggle)::after,
|
|
||||||
input[type=radio]::before,
|
|
||||||
input[type=radio]::after {
|
|
||||||
content: "";
|
|
||||||
display: block; /* width & height doesn't work on inline elements */
|
|
||||||
transition: inherit;
|
|
||||||
/* Let's prevent the pseudo-elements from taking up layout space so that
|
|
||||||
the ::before and ::after pseudo-elements can be in the same place. This
|
|
||||||
is also required for vertical-align: baseline to work like we want it to
|
|
||||||
on radio/checkboxes. If the pseudo-elements take up layout space, the
|
|
||||||
baseline of text inside them will be used instead. */
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
input[type=checkbox]:not(.toggle)::after,
|
|
||||||
input[type=radio]::after {
|
|
||||||
width: 10px;
|
|
||||||
height: 2px;
|
|
||||||
background-color: transparent;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------- CHECKBOXES ------- */
|
|
||||||
|
|
||||||
input[type=checkbox]:not(.toggle) {
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
input[type=checkbox]:not(.toggle):checked,
|
|
||||||
input[type=checkbox]:not(.toggle):indeterminate {
|
|
||||||
background-color: var(--novnc-blue);
|
|
||||||
background-image: var(--button-activation-overlay-light);
|
|
||||||
background-blend-mode: overlay;
|
|
||||||
}
|
|
||||||
input[type=checkbox]:not(.toggle)::before {
|
|
||||||
width: 25%;
|
|
||||||
height: 55%;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: transparent;
|
|
||||||
border-width: 0 2px 2px 0;
|
|
||||||
border-radius: 1px;
|
|
||||||
transform: translateY(-1px) rotate(35deg);
|
|
||||||
}
|
|
||||||
input[type=checkbox]:not(.toggle):checked::before {
|
|
||||||
border-color: white;
|
|
||||||
}
|
|
||||||
input[type=checkbox]:not(.toggle):indeterminate::after {
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------- RADIO BUTTONS ------- */
|
|
||||||
|
|
||||||
input[type=radio] {
|
|
||||||
border-radius: 50%;
|
|
||||||
border: 1px solid transparent; /* To ensure a smooth transition */
|
|
||||||
}
|
|
||||||
input[type=radio]:checked {
|
|
||||||
border: 4px solid var(--novnc-blue);
|
|
||||||
background-color: white;
|
|
||||||
/* button-activation-overlay should be removed from the radio
|
|
||||||
element to not interfere with button-activation-overlay-light
|
|
||||||
that is set on the ::before element. */
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
input[type=radio]::before {
|
|
||||||
width: inherit;
|
|
||||||
height: inherit;
|
|
||||||
border-radius: inherit;
|
|
||||||
/* We can achieve the highlight overlay effect on border colors by
|
|
||||||
setting button-activation-overlay-light on an element that stays
|
|
||||||
on top (z-axis) of the element with a border. */
|
|
||||||
background-image: var(--button-activation-overlay-light);
|
|
||||||
mix-blend-mode: overlay;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
input[type=radio]:checked::before {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
input[type=radio]:indeterminate::after {
|
|
||||||
background-color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------- TOGGLE SWITCHES ------- */
|
|
||||||
|
|
||||||
/* These are meant to be used instead of checkboxes in some cases. If all of
|
|
||||||
the following critera are true you should use a toggle switch:
|
|
||||||
|
|
||||||
* The choice is a simple ON/OFF or ENABLE/DISABLE
|
|
||||||
* The choice doesn't give the feeling of "I agree" or "I confirm"
|
|
||||||
* There are not multiple related & grouped options
|
|
||||||
*/
|
|
||||||
|
|
||||||
input[type=checkbox].toggle {
|
|
||||||
display: inline-block;
|
|
||||||
--checkradio-height: 18px; /* Height value used in calc, see above */
|
|
||||||
width: 31px;
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
border-radius: 9px;
|
|
||||||
}
|
|
||||||
input[type=checkbox].toggle:disabled {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
input[type=checkbox].toggle:indeterminate {
|
|
||||||
background-color: var(--novnc-buttongrey);
|
|
||||||
background-image: var(--button-activation-overlay);
|
|
||||||
}
|
|
||||||
input[type=checkbox].toggle:checked {
|
|
||||||
background-color: var(--novnc-blue);
|
|
||||||
background-image: var(--button-activation-overlay-light);
|
|
||||||
background-blend-mode: overlay;
|
|
||||||
}
|
|
||||||
input[type=checkbox].toggle::before {
|
|
||||||
--circle-diameter: 10px;
|
|
||||||
--circle-offset: 4px;
|
|
||||||
width: var(--circle-diameter);
|
|
||||||
height: var(--circle-diameter);
|
|
||||||
top: var(--circle-offset);
|
|
||||||
left: var(--circle-offset);
|
|
||||||
background: white;
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
input[type=checkbox].toggle:checked::before {
|
|
||||||
left: calc(100% - var(--circle-offset) - var(--circle-diameter));
|
|
||||||
}
|
|
||||||
input[type=checkbox].toggle:indeterminate::before {
|
|
||||||
left: calc(50% - var(--circle-diameter) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------- RANGE SLIDERS ------- */
|
|
||||||
|
|
||||||
input[type=range] {
|
|
||||||
border: unset;
|
|
||||||
border-radius: 8px;
|
|
||||||
height: 15px;
|
|
||||||
padding: 0;
|
|
||||||
background: transparent;
|
|
||||||
/* Needed to get properly rounded corners on -moz-range-progress
|
|
||||||
when the thumb is all the way to the right. Without overflow
|
|
||||||
hidden, the pointy edges of the progress track shows to the
|
|
||||||
right of the thumb. */
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
@supports selector(::-webkit-slider-thumb) {
|
|
||||||
input[type=range] {
|
|
||||||
/* Needs a fixed width to match clip-path */
|
|
||||||
width: 125px;
|
|
||||||
/* overflow: hidden is not ideal for hiding the left part of the box
|
|
||||||
shadow of -webkit-slider-thumb since it doesn't match the smaller
|
|
||||||
border-radius of the progress track. The below clip-path has two
|
|
||||||
circular sides to make the ends of the track have correctly rounded
|
|
||||||
corners. The clip path shape looks something like this:
|
|
||||||
|
|
||||||
+-------------------------------+
|
|
||||||
/---| |---\
|
|
||||||
| |
|
|
||||||
\---| |---/
|
|
||||||
+-------------------------------+
|
|
||||||
|
|
||||||
The larger middle part of the clip path is made to have room for the
|
|
||||||
thumb. By using margins on the track, we prevent the thumb from
|
|
||||||
touching the ends of the track.
|
|
||||||
*/
|
|
||||||
clip-path: path(' \
|
|
||||||
M 4.5 3 \
|
|
||||||
L 4.5 0 \
|
|
||||||
L 120.5 0 \
|
|
||||||
L 120.5 3 \
|
|
||||||
A 1 1 0 0 1 120.5 12 \
|
|
||||||
L 120.5 15 \
|
|
||||||
L 4.5 15 \
|
|
||||||
L 4.5 12 \
|
|
||||||
A 1 1 0 0 1 4.5 3 \
|
|
||||||
');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
input[type=range]:hover {
|
|
||||||
cursor: grab;
|
|
||||||
}
|
|
||||||
input[type=range]:active {
|
|
||||||
cursor: grabbing;
|
|
||||||
}
|
|
||||||
input[type=range]:disabled {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
input[type=range]:focus-visible {
|
|
||||||
clip-path: none; /* Otherwise it hides the outline */
|
|
||||||
}
|
|
||||||
/* -webkit-slider.. & -moz-range.. cant be in selector lists:
|
|
||||||
https://bugs.chromium.org/p/chromium/issues/detail?id=1154623 */
|
|
||||||
input[type=range]::-webkit-slider-runnable-track {
|
|
||||||
background-color: var(--novnc-buttongrey);
|
|
||||||
height: 7px;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin: 0 3px;
|
|
||||||
}
|
|
||||||
input[type=range]::-moz-range-track {
|
|
||||||
background-color: var(--novnc-buttongrey);
|
|
||||||
height: 7px;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
input[type=range]::-moz-range-progress {
|
|
||||||
background-color: var(--novnc-blue);
|
|
||||||
height: 9px;
|
|
||||||
/* Needs rounded corners only on the left side. Otherwise the rounding of
|
|
||||||
the progress track starts before the thumb, when the thumb is close to
|
|
||||||
the left edge. */
|
|
||||||
border-radius: 5px 0 0 5px;
|
|
||||||
}
|
|
||||||
input[type=range]::-webkit-slider-thumb {
|
|
||||||
appearance: none;
|
|
||||||
width: 15px;
|
|
||||||
height: 15px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: white;
|
|
||||||
background-image: var(--button-activation-overlay);
|
|
||||||
/* Disable Chrome's touch tap highlight to avoid conflicts with overlay */
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
border: 3px solid var(--novnc-blue);
|
|
||||||
margin-top: -4px; /* (track height / 2) - (thumb height /2) */
|
|
||||||
|
|
||||||
/* Since there is no way to style the left part of the range track in
|
|
||||||
webkit, we add a large shadow (1000px wide) to the left of the thumb and
|
|
||||||
then crop it with a clip-path shaped like this:
|
|
||||||
___
|
|
||||||
+-------------------/ \
|
|
||||||
| progress |Thumb|
|
|
||||||
+-------------------\ ___ /
|
|
||||||
|
|
||||||
The large left part of the shadow is clipped by another clip-path on on
|
|
||||||
the main range input element. */
|
|
||||||
/* FIXME: We can remove the box shadow workaround when this is standardized:
|
|
||||||
https://github.com/w3c/csswg-drafts/issues/4410 */
|
|
||||||
|
|
||||||
box-shadow: calc(-100vw - 8px) 0 0 100vw var(--novnc-blue);
|
|
||||||
clip-path: path(' \
|
|
||||||
M -1000 3 \
|
|
||||||
L 3 3 \
|
|
||||||
L 15 7.5 \
|
|
||||||
A 1 1 0 0 1 0 7.5 \
|
|
||||||
A 1 1 0 0 1 15 7.5 \
|
|
||||||
L 3 12 \
|
|
||||||
L -1000 12 Z \
|
|
||||||
');
|
|
||||||
}
|
|
||||||
input[type=range]::-moz-range-thumb {
|
|
||||||
appearance: none;
|
|
||||||
width: 15px;
|
|
||||||
height: 15px;
|
|
||||||
border-radius: 50%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
background-color: white;
|
|
||||||
background-image: var(--button-activation-overlay);
|
|
||||||
border: 3px solid var(--novnc-blue);
|
|
||||||
margin-top: -7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------- FILE CHOOSERS ------- */
|
|
||||||
|
|
||||||
input[type=file] {
|
|
||||||
background-image: none;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
input::file-selector-button {
|
|
||||||
margin-right: 6px;
|
|
||||||
}
|
|
||||||
input[type=file]:focus-visible {
|
|
||||||
outline: none; /* We outline the button instead of the entire element */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------- SELECT BUTTONS ------- */
|
|
||||||
|
|
||||||
select {
|
|
||||||
--select-arrow: url('data:image/svg+xml;utf8, \
|
|
||||||
<svg width="11" height="6" version="1.1" viewBox="0 0 11 6" \
|
|
||||||
xmlns="http://www.w3.org/2000/svg"> \
|
|
||||||
<path d="m10.5.5-5 5-5-5" fill="none" \
|
|
||||||
stroke="black" stroke-width="1.5" \
|
|
||||||
stroke-linecap="round" stroke-linejoin="round"/> \
|
|
||||||
</svg>');
|
|
||||||
|
|
||||||
/* FIXME: A bug in Firefox, requires a workaround for the background:
|
|
||||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1810958 */
|
|
||||||
/* The dropdown list will show the select element's background above and
|
|
||||||
below the options in Firefox. We want the entire dropdown to be white. */
|
|
||||||
background-color: white;
|
|
||||||
/* However, we don't want the select element to actually show a white
|
|
||||||
background, so let's place a gradient above it with the color we want. */
|
|
||||||
--grey-background: linear-gradient(var(--novnc-buttongrey) 100%,
|
|
||||||
transparent);
|
|
||||||
background-image:
|
|
||||||
var(--select-arrow),
|
|
||||||
var(--button-activation-overlay),
|
|
||||||
var(--grey-background);
|
|
||||||
background-position: calc(100% - var(--input-xpadding)), left top, left top;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
padding-right: calc(2*var(--input-xpadding) + 11px);
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
/* FIXME: :active isn't set when the <select> is opened in Firefox:
|
|
||||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1805406 */
|
|
||||||
select:active {
|
|
||||||
/* Rotated arrow */
|
|
||||||
background-image: url('data:image/svg+xml;utf8, \
|
|
||||||
<svg width="11" height="6" version="1.1" viewBox="0 0 11 6" \
|
|
||||||
xmlns="http://www.w3.org/2000/svg" transform="rotate(180)"> \
|
|
||||||
<path d="m10.5.5-5 5-5-5" fill="none" \
|
|
||||||
stroke="black" stroke-width="1.5" \
|
|
||||||
stroke-linecap="round" stroke-linejoin="round"/> \
|
|
||||||
</svg>'),
|
|
||||||
var(--button-activation-overlay),
|
|
||||||
var(--grey-background);
|
|
||||||
}
|
|
||||||
select:disabled {
|
|
||||||
background-image:
|
|
||||||
var(--select-arrow),
|
|
||||||
var(--grey-background);
|
|
||||||
}
|
|
||||||
/* Note that styling for <option> doesn't work in all browsers
|
|
||||||
since its often drawn directly by the OS. We are generally very
|
|
||||||
limited in what we can change here. */
|
|
||||||
option {
|
|
||||||
/* Prevent Chrome from inheriting background-color from the <select> */
|
|
||||||
background-color: white;
|
|
||||||
color: black;
|
|
||||||
font-weight: normal;
|
|
||||||
background-image: var(--button-activation-overlay);
|
|
||||||
}
|
|
||||||
option:checked {
|
|
||||||
background-color: var(--novnc-lightgrey);
|
|
||||||
}
|
|
||||||
/* Change the look when the <select> isn't used as a dropdown. When "size"
|
|
||||||
or "multiple" are set, these elements behaves more like lists. */
|
|
||||||
select[size]:not([size="1"]), select[multiple] {
|
|
||||||
background-color: white;
|
|
||||||
background-image: unset; /* Don't show the arrow and other gradients */
|
|
||||||
border: 1px solid var(--novnc-lightgrey);
|
|
||||||
padding: 0;
|
|
||||||
font-weight: normal; /* Without this, options get bold font in WebKit. */
|
|
||||||
|
|
||||||
/* As an exception to the "list"-look, multi-selects in Chrome on Android,
|
|
||||||
and Safari on iOS, are unfortunately designed to be shown as a single
|
|
||||||
line. We can mitigate this inconsistency by at least fixing the height
|
|
||||||
here. By setting a min-height that matches other input elements, it
|
|
||||||
doesn't look too much out of place:
|
|
||||||
(1px border * 2) + (6.5px padding * 2) + 24px line-height = 39px */
|
|
||||||
min-height: 39px;
|
|
||||||
}
|
|
||||||
select[size]:not([size="1"]):focus-visible,
|
|
||||||
select[multiple]:focus-visible {
|
|
||||||
/* Text input style focus-visible highlight */
|
|
||||||
outline-offset: -1px;
|
|
||||||
}
|
|
||||||
select[size]:not([size="1"]) option, select[multiple] option {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
padding: 4px var(--input-xpadding);
|
|
||||||
}
|
|
359
app/webutil.js
|
@ -1,82 +1,114 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* 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)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as Log 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
|
// init log level reading the logging HTTP param
|
||||||
export function initLogging(level) {
|
WebUtil.init_logging = function (level) {
|
||||||
"use strict";
|
"use strict";
|
||||||
if (typeof level !== "undefined") {
|
if (typeof level !== "undefined") {
|
||||||
Log.initLogging(level);
|
Util._log_level = level;
|
||||||
} else {
|
} else {
|
||||||
const param = document.location.href.match(/logging=([A-Za-z0-9._-]*)/);
|
var param = document.location.href.match(/logging=([A-Za-z0-9\._\-]*)/);
|
||||||
Log.initLogging(param || undefined);
|
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
|
// 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):
|
WebUtil.getQueryVar = function (name, defVal) {
|
||||||
// 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=secretvalue
|
|
||||||
//
|
|
||||||
// Even mixing public and non public parameters will work:
|
|
||||||
// https://www.example.com?nonsecretparam=example.com#password=secretvalue
|
|
||||||
export function getQueryVar(name, defVal) {
|
|
||||||
"use strict";
|
"use strict";
|
||||||
const re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
|
var re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
|
||||||
match = document.location.href.match(re);
|
match = document.location.href.match(re);
|
||||||
if (typeof defVal === 'undefined') { defVal = null; }
|
if (typeof defVal === 'undefined') { defVal = null; }
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
return decodeURIComponent(match[1]);
|
return decodeURIComponent(match[1]);
|
||||||
}
|
} else {
|
||||||
|
|
||||||
return defVal;
|
return defVal;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Read a hash fragment variable
|
// Read a hash fragment variable
|
||||||
export function getHashVar(name, defVal) {
|
WebUtil.getHashVar = function (name, defVal) {
|
||||||
"use strict";
|
"use strict";
|
||||||
const re = new RegExp('.*[&#]' + name + '=([^&]*)'),
|
var re = new RegExp('.*[&#]' + name + '=([^&]*)'),
|
||||||
match = document.location.hash.match(re);
|
match = document.location.hash.match(re);
|
||||||
if (typeof defVal === 'undefined') { defVal = null; }
|
if (typeof defVal === 'undefined') { defVal = null; }
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
return decodeURIComponent(match[1]);
|
return decodeURIComponent(match[1]);
|
||||||
}
|
} else {
|
||||||
|
|
||||||
return defVal;
|
return defVal;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Read a variable from the fragment or the query string
|
// Read a variable from the fragment or the query string
|
||||||
// Fragment takes precedence
|
// Fragment takes precedence
|
||||||
export function getConfigVar(name, defVal) {
|
WebUtil.getConfigVar = function (name, defVal) {
|
||||||
"use strict";
|
"use strict";
|
||||||
const val = getHashVar(name);
|
var val = WebUtil.getHashVar(name);
|
||||||
|
|
||||||
if (val === null) {
|
if (val === null) {
|
||||||
return getQueryVar(name, defVal);
|
val = WebUtil.getQueryVar(name, defVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
|
* Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// No days means only for this browser session
|
// No days means only for this browser session
|
||||||
export function createCookie(name, value, days) {
|
WebUtil.createCookie = function (name, value, days) {
|
||||||
"use strict";
|
"use strict";
|
||||||
let date, expires;
|
var date, expires;
|
||||||
if (days) {
|
if (days) {
|
||||||
date = new Date();
|
date = new Date();
|
||||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||||
|
@ -85,166 +117,195 @@ export function createCookie(name, value, days) {
|
||||||
expires = "";
|
expires = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
let secure;
|
var secure;
|
||||||
if (document.location.protocol === "https:") {
|
if (document.location.protocol === "https:") {
|
||||||
secure = "; secure";
|
secure = "; secure";
|
||||||
} else {
|
} else {
|
||||||
secure = "";
|
secure = "";
|
||||||
}
|
}
|
||||||
document.cookie = name + "=" + value + expires + "; path=/" + secure;
|
document.cookie = name + "=" + value + expires + "; path=/" + secure;
|
||||||
}
|
};
|
||||||
|
|
||||||
export function readCookie(name, defaultValue) {
|
WebUtil.readCookie = function (name, defaultValue) {
|
||||||
"use strict";
|
"use strict";
|
||||||
const nameEQ = name + "=";
|
var nameEQ = name + "=",
|
||||||
const ca = document.cookie.split(';');
|
ca = document.cookie.split(';');
|
||||||
|
|
||||||
for (let i = 0; i < ca.length; i += 1) {
|
for (var i = 0; i < ca.length; i += 1) {
|
||||||
let c = ca[i];
|
var c = ca[i];
|
||||||
while (c.charAt(0) === ' ') {
|
while (c.charAt(0) === ' ') { c = c.substring(1, c.length); }
|
||||||
c = c.substring(1, c.length);
|
if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length, c.length); }
|
||||||
}
|
}
|
||||||
if (c.indexOf(nameEQ) === 0) {
|
|
||||||
return c.substring(nameEQ.length, c.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (typeof defaultValue !== 'undefined') ? defaultValue : null;
|
return (typeof defaultValue !== 'undefined') ? defaultValue : null;
|
||||||
}
|
};
|
||||||
|
|
||||||
export function eraseCookie(name) {
|
WebUtil.eraseCookie = function (name) {
|
||||||
"use strict";
|
"use strict";
|
||||||
createCookie(name, "", -1);
|
WebUtil.createCookie(name, "", -1);
|
||||||
}
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Setting handling.
|
* Setting handling.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let settings = {};
|
WebUtil.initSettings = function (callback /*, ...callbackArgs */) {
|
||||||
|
"use strict";
|
||||||
export function initSettings() {
|
var callbackArgs = Array.prototype.slice.call(arguments, 1);
|
||||||
if (!window.chrome || !window.chrome.storage) {
|
if (window.chrome && window.chrome.storage) {
|
||||||
settings = {};
|
window.chrome.storage.sync.get(function (cfg) {
|
||||||
return Promise.resolve();
|
WebUtil.settings = cfg;
|
||||||
|
console.log(WebUtil.settings);
|
||||||
|
if (callback) {
|
||||||
|
callback.apply(this, callbackArgs);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
return new Promise(resolve => window.chrome.storage.sync.get(resolve))
|
} else {
|
||||||
.then((cfg) => { settings = cfg; });
|
// No-op
|
||||||
|
if (callback) {
|
||||||
|
callback.apply(this, callbackArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// No days means only for this browser session
|
||||||
export function writeSetting(name, value) {
|
WebUtil.writeSetting = function (name, value) {
|
||||||
"use strict";
|
"use strict";
|
||||||
if (settings[name] === value) return;
|
|
||||||
settings[name] = value;
|
|
||||||
if (window.chrome && window.chrome.storage) {
|
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 {
|
} else {
|
||||||
localStorageSet(name, value);
|
localStorage.setItem(name, value);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export function readSetting(name, defaultValue) {
|
WebUtil.readSetting = function (name, defaultValue) {
|
||||||
"use strict";
|
"use strict";
|
||||||
let value;
|
var value;
|
||||||
if ((name in settings) || (window.chrome && window.chrome.storage)) {
|
if (window.chrome && window.chrome.storage) {
|
||||||
value = settings[name];
|
value = WebUtil.settings[name];
|
||||||
} else {
|
} else {
|
||||||
value = localStorageGet(name);
|
value = localStorage.getItem(name);
|
||||||
settings[name] = value;
|
|
||||||
}
|
}
|
||||||
if (typeof value === "undefined") {
|
if (typeof value === "undefined") {
|
||||||
value = null;
|
value = null;
|
||||||
}
|
}
|
||||||
|
if (value === null && typeof defaultValue !== undefined) {
|
||||||
if (value === null && typeof defaultValue !== "undefined") {
|
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
} else {
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export function eraseSetting(name) {
|
WebUtil.eraseSetting = function (name) {
|
||||||
"use strict";
|
"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) {
|
if (window.chrome && window.chrome.storage) {
|
||||||
window.chrome.storage.sync.remove(name);
|
window.chrome.storage.sync.remove(name);
|
||||||
|
delete WebUtil.settings[name];
|
||||||
} else {
|
} else {
|
||||||
localStorageRemove(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let loggedMsgs = [];
|
|
||||||
function logOnce(msg, level = "warn") {
|
|
||||||
if (!loggedMsgs.includes(msg)) {
|
|
||||||
switch (level) {
|
|
||||||
case "error":
|
|
||||||
Log.Error(msg);
|
|
||||||
break;
|
|
||||||
case "warn":
|
|
||||||
Log.Warn(msg);
|
|
||||||
break;
|
|
||||||
case "debug":
|
|
||||||
Log.Debug(msg);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Log.Info(msg);
|
|
||||||
}
|
|
||||||
loggedMsgs.push(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let cookiesMsg = "Couldn't access noVNC settings, are cookies disabled?";
|
|
||||||
|
|
||||||
function localStorageGet(name) {
|
|
||||||
let r;
|
|
||||||
try {
|
|
||||||
r = localStorage.getItem(name);
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof DOMException) {
|
|
||||||
logOnce(cookiesMsg);
|
|
||||||
logOnce("'localStorage.getItem(" + name + ")' failed: " + e,
|
|
||||||
"debug");
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
function localStorageSet(name, value) {
|
|
||||||
try {
|
|
||||||
localStorage.setItem(name, value);
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof DOMException) {
|
|
||||||
logOnce(cookiesMsg);
|
|
||||||
logOnce("'localStorage.setItem(" + name + "," + value +
|
|
||||||
")' failed: " + e, "debug");
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function localStorageRemove(name) {
|
|
||||||
try {
|
|
||||||
localStorage.removeItem(name);
|
localStorage.removeItem(name);
|
||||||
} catch (e) {
|
}
|
||||||
if (e instanceof DOMException) {
|
};
|
||||||
logOnce(cookiesMsg);
|
|
||||||
logOnce("'localStorage.removeItem(" + name + ")' failed: " + e,
|
WebUtil.injectParamIfMissing = function (path, param, value) {
|
||||||
"debug");
|
// 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 {
|
} else {
|
||||||
throw e;
|
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; */
|
||||||
|
|
|
@ -4,46 +4,50 @@
|
||||||
|
|
||||||
// From: http://hg.mozilla.org/mozilla-central/raw-file/ec10630b1a54/js/src/devtools/jint/sunspider/string-base64.js
|
// 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. */
|
/* Convert data (an array of integers) to a Base64 string. */
|
||||||
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
|
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
|
||||||
base64Pad : '=',
|
base64Pad : '=',
|
||||||
|
|
||||||
encode(data) {
|
encode: function (data) {
|
||||||
"use strict";
|
"use strict";
|
||||||
let result = '';
|
var result = '';
|
||||||
const length = data.length;
|
var toBase64Table = Base64.toBase64Table;
|
||||||
const lengthpad = (length % 3);
|
var length = data.length;
|
||||||
|
var lengthpad = (length % 3);
|
||||||
// Convert every three bytes to 4 ascii characters.
|
// Convert every three bytes to 4 ascii characters.
|
||||||
|
|
||||||
for (let i = 0; i < (length - 2); i += 3) {
|
for (var i = 0; i < (length - 2); i += 3) {
|
||||||
result += this.toBase64Table[data[i] >> 2];
|
result += toBase64Table[data[i] >> 2];
|
||||||
result += this.toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
|
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
|
||||||
result += this.toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
|
result += toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
|
||||||
result += this.toBase64Table[data[i + 2] & 0x3f];
|
result += toBase64Table[data[i + 2] & 0x3f];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
|
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
|
||||||
const j = length - lengthpad;
|
var j = 0;
|
||||||
if (lengthpad === 2) {
|
if (lengthpad === 2) {
|
||||||
result += this.toBase64Table[data[j] >> 2];
|
j = length - lengthpad;
|
||||||
result += this.toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)];
|
result += toBase64Table[data[j] >> 2];
|
||||||
result += this.toBase64Table[(data[j + 1] & 0x0f) << 2];
|
result += toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)];
|
||||||
result += this.toBase64Table[64];
|
result += toBase64Table[(data[j + 1] & 0x0f) << 2];
|
||||||
|
result += toBase64Table[64];
|
||||||
} else if (lengthpad === 1) {
|
} else if (lengthpad === 1) {
|
||||||
result += this.toBase64Table[data[j] >> 2];
|
j = length - lengthpad;
|
||||||
result += this.toBase64Table[(data[j] & 0x03) << 4];
|
result += toBase64Table[data[j] >> 2];
|
||||||
result += this.toBase64Table[64];
|
result += toBase64Table[(data[j] & 0x03) << 4];
|
||||||
result += this.toBase64Table[64];
|
result += toBase64Table[64];
|
||||||
|
result += toBase64Table[64];
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Convert Base64 data to a string */
|
/* Convert Base64 data to a string */
|
||||||
/* eslint-disable comma-spacing */
|
/* jshint -W013 */
|
||||||
toBinaryTable : [
|
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,-1, -1,-1,-1,-1,
|
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||||
|
@ -54,26 +58,31 @@ export default {
|
||||||
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
|
-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
|
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) {
|
decode: function (data, offset) {
|
||||||
let dataLength = data.indexOf('=') - offset;
|
"use strict";
|
||||||
if (dataLength < 0) { dataLength = data.length - offset; }
|
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 */
|
/* Every four characters is 3 resulting numbers */
|
||||||
const resultLength = (dataLength >> 2) * 3 + Math.floor((dataLength % 4) / 1.5);
|
result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5);
|
||||||
const result = new Array(resultLength);
|
result = new Array(result_length);
|
||||||
|
|
||||||
// Convert one by one.
|
// Convert one by one.
|
||||||
|
for (var idx = 0, i = offset; i < data.length; i++) {
|
||||||
let leftbits = 0; // number of bits decoded, but yet to be appended
|
var c = toBinaryTable[data.charCodeAt(i) & 0x7f];
|
||||||
let leftdata = 0; // bits decoded, but yet to be appended
|
var padding = (data.charAt(i) === base64Pad);
|
||||||
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);
|
|
||||||
// Skip illegal characters and whitespace
|
// Skip illegal characters and whitespace
|
||||||
if (c === -1) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +103,7 @@ export default {
|
||||||
|
|
||||||
// If there are any bits left, the base64 string was corrupted
|
// If there are any bits left, the base64 string was corrupted
|
||||||
if (leftbits) {
|
if (leftbits) {
|
||||||
const err = new Error('Corrupted base64 string');
|
err = new Error('Corrupted base64 string');
|
||||||
err.name = 'Base64-Error';
|
err.name = 'Base64-Error';
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
@ -102,3 +111,5 @@ export default {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}; /* End of Base64 namespace */
|
}; /* End of Base64 namespace */
|
||||||
|
|
||||||
|
/* [module] export default Base64; */
|
||||||
|
|
|
@ -1,178 +0,0 @@
|
||||||
export class AESECBCipher {
|
|
||||||
constructor() {
|
|
||||||
this._key = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get algorithm() {
|
|
||||||
return { name: "AES-ECB" };
|
|
||||||
}
|
|
||||||
|
|
||||||
static async importKey(key, _algorithm, extractable, keyUsages) {
|
|
||||||
const cipher = new AESECBCipher;
|
|
||||||
await cipher._importKey(key, extractable, keyUsages);
|
|
||||||
return cipher;
|
|
||||||
}
|
|
||||||
|
|
||||||
async _importKey(key, extractable, keyUsages) {
|
|
||||||
this._key = await window.crypto.subtle.importKey(
|
|
||||||
"raw", key, {name: "AES-CBC"}, extractable, keyUsages);
|
|
||||||
}
|
|
||||||
|
|
||||||
async encrypt(_algorithm, plaintext) {
|
|
||||||
const x = new Uint8Array(plaintext);
|
|
||||||
if (x.length % 16 !== 0 || this._key === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const n = x.length / 16;
|
|
||||||
for (let i = 0; i < n; i++) {
|
|
||||||
const y = new Uint8Array(await window.crypto.subtle.encrypt({
|
|
||||||
name: "AES-CBC",
|
|
||||||
iv: new Uint8Array(16),
|
|
||||||
}, this._key, x.slice(i * 16, i * 16 + 16))).slice(0, 16);
|
|
||||||
x.set(y, i * 16);
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AESEAXCipher {
|
|
||||||
constructor() {
|
|
||||||
this._rawKey = null;
|
|
||||||
this._ctrKey = null;
|
|
||||||
this._cbcKey = null;
|
|
||||||
this._zeroBlock = new Uint8Array(16);
|
|
||||||
this._prefixBlock0 = this._zeroBlock;
|
|
||||||
this._prefixBlock1 = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
|
|
||||||
this._prefixBlock2 = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
get algorithm() {
|
|
||||||
return { name: "AES-EAX" };
|
|
||||||
}
|
|
||||||
|
|
||||||
async _encryptBlock(block) {
|
|
||||||
const encrypted = await window.crypto.subtle.encrypt({
|
|
||||||
name: "AES-CBC",
|
|
||||||
iv: this._zeroBlock,
|
|
||||||
}, this._cbcKey, block);
|
|
||||||
return new Uint8Array(encrypted).slice(0, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _initCMAC() {
|
|
||||||
const k1 = await this._encryptBlock(this._zeroBlock);
|
|
||||||
const k2 = new Uint8Array(16);
|
|
||||||
const v = k1[0] >>> 6;
|
|
||||||
for (let i = 0; i < 15; i++) {
|
|
||||||
k2[i] = (k1[i + 1] >> 6) | (k1[i] << 2);
|
|
||||||
k1[i] = (k1[i + 1] >> 7) | (k1[i] << 1);
|
|
||||||
}
|
|
||||||
const lut = [0x0, 0x87, 0x0e, 0x89];
|
|
||||||
k2[14] ^= v >>> 1;
|
|
||||||
k2[15] = (k1[15] << 2) ^ lut[v];
|
|
||||||
k1[15] = (k1[15] << 1) ^ lut[v >> 1];
|
|
||||||
this._k1 = k1;
|
|
||||||
this._k2 = k2;
|
|
||||||
}
|
|
||||||
|
|
||||||
async _encryptCTR(data, counter) {
|
|
||||||
const encrypted = await window.crypto.subtle.encrypt({
|
|
||||||
name: "AES-CTR",
|
|
||||||
counter: counter,
|
|
||||||
length: 128
|
|
||||||
}, this._ctrKey, data);
|
|
||||||
return new Uint8Array(encrypted);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _decryptCTR(data, counter) {
|
|
||||||
const decrypted = await window.crypto.subtle.decrypt({
|
|
||||||
name: "AES-CTR",
|
|
||||||
counter: counter,
|
|
||||||
length: 128
|
|
||||||
}, this._ctrKey, data);
|
|
||||||
return new Uint8Array(decrypted);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _computeCMAC(data, prefixBlock) {
|
|
||||||
if (prefixBlock.length !== 16) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const n = Math.floor(data.length / 16);
|
|
||||||
const m = Math.ceil(data.length / 16);
|
|
||||||
const r = data.length - n * 16;
|
|
||||||
const cbcData = new Uint8Array((m + 1) * 16);
|
|
||||||
cbcData.set(prefixBlock);
|
|
||||||
cbcData.set(data, 16);
|
|
||||||
if (r === 0) {
|
|
||||||
for (let i = 0; i < 16; i++) {
|
|
||||||
cbcData[n * 16 + i] ^= this._k1[i];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cbcData[(n + 1) * 16 + r] = 0x80;
|
|
||||||
for (let i = 0; i < 16; i++) {
|
|
||||||
cbcData[(n + 1) * 16 + i] ^= this._k2[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let cbcEncrypted = await window.crypto.subtle.encrypt({
|
|
||||||
name: "AES-CBC",
|
|
||||||
iv: this._zeroBlock,
|
|
||||||
}, this._cbcKey, cbcData);
|
|
||||||
|
|
||||||
cbcEncrypted = new Uint8Array(cbcEncrypted);
|
|
||||||
const mac = cbcEncrypted.slice(cbcEncrypted.length - 32, cbcEncrypted.length - 16);
|
|
||||||
return mac;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async importKey(key, _algorithm, _extractable, _keyUsages) {
|
|
||||||
const cipher = new AESEAXCipher;
|
|
||||||
await cipher._importKey(key);
|
|
||||||
return cipher;
|
|
||||||
}
|
|
||||||
|
|
||||||
async _importKey(key) {
|
|
||||||
this._rawKey = key;
|
|
||||||
this._ctrKey = await window.crypto.subtle.importKey(
|
|
||||||
"raw", key, {name: "AES-CTR"}, false, ["encrypt", "decrypt"]);
|
|
||||||
this._cbcKey = await window.crypto.subtle.importKey(
|
|
||||||
"raw", key, {name: "AES-CBC"}, false, ["encrypt"]);
|
|
||||||
await this._initCMAC();
|
|
||||||
}
|
|
||||||
|
|
||||||
async encrypt(algorithm, message) {
|
|
||||||
const ad = algorithm.additionalData;
|
|
||||||
const nonce = algorithm.iv;
|
|
||||||
const nCMAC = await this._computeCMAC(nonce, this._prefixBlock0);
|
|
||||||
const encrypted = await this._encryptCTR(message, nCMAC);
|
|
||||||
const adCMAC = await this._computeCMAC(ad, this._prefixBlock1);
|
|
||||||
const mac = await this._computeCMAC(encrypted, this._prefixBlock2);
|
|
||||||
for (let i = 0; i < 16; i++) {
|
|
||||||
mac[i] ^= nCMAC[i] ^ adCMAC[i];
|
|
||||||
}
|
|
||||||
const res = new Uint8Array(16 + encrypted.length);
|
|
||||||
res.set(encrypted);
|
|
||||||
res.set(mac, encrypted.length);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
async decrypt(algorithm, data) {
|
|
||||||
const encrypted = data.slice(0, data.length - 16);
|
|
||||||
const ad = algorithm.additionalData;
|
|
||||||
const nonce = algorithm.iv;
|
|
||||||
const mac = data.slice(data.length - 16);
|
|
||||||
const nCMAC = await this._computeCMAC(nonce, this._prefixBlock0);
|
|
||||||
const adCMAC = await this._computeCMAC(ad, this._prefixBlock1);
|
|
||||||
const computedMac = await this._computeCMAC(encrypted, this._prefixBlock2);
|
|
||||||
for (let i = 0; i < 16; i++) {
|
|
||||||
computedMac[i] ^= nCMAC[i] ^ adCMAC[i];
|
|
||||||
}
|
|
||||||
if (computedMac.length !== mac.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
for (let i = 0; i < mac.length; i++) {
|
|
||||||
if (computedMac[i] !== mac[i]) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const res = await this._decryptCTR(encrypted, nCMAC);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
export function modPow(b, e, m) {
|
|
||||||
let r = 1n;
|
|
||||||
b = b % m;
|
|
||||||
while (e > 0n) {
|
|
||||||
if ((e & 1n) === 1n) {
|
|
||||||
r = (r * b) % m;
|
|
||||||
}
|
|
||||||
e = e >> 1n;
|
|
||||||
b = (b * b) % m;
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function bigIntToU8Array(bigint, padLength=0) {
|
|
||||||
let hex = bigint.toString(16);
|
|
||||||
if (padLength === 0) {
|
|
||||||
padLength = Math.ceil(hex.length / 2);
|
|
||||||
}
|
|
||||||
hex = hex.padStart(padLength * 2, '0');
|
|
||||||
const length = hex.length / 2;
|
|
||||||
const arr = new Uint8Array(length);
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
arr[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
||||||
}
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function u8ArrayToBigInt(arr) {
|
|
||||||
let hex = '0x';
|
|
||||||
for (let i = 0; i < arr.length; i++) {
|
|
||||||
hex += arr[i].toString(16).padStart(2, '0');
|
|
||||||
}
|
|
||||||
return BigInt(hex);
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
import { AESECBCipher, AESEAXCipher } from "./aes.js";
|
|
||||||
import { DESCBCCipher, DESECBCipher } from "./des.js";
|
|
||||||
import { RSACipher } from "./rsa.js";
|
|
||||||
import { DHCipher } from "./dh.js";
|
|
||||||
import { MD5 } from "./md5.js";
|
|
||||||
|
|
||||||
// A single interface for the cryptographic algorithms not supported by SubtleCrypto.
|
|
||||||
// Both synchronous and asynchronous implmentations are allowed.
|
|
||||||
class LegacyCrypto {
|
|
||||||
constructor() {
|
|
||||||
this._algorithms = {
|
|
||||||
"AES-ECB": AESECBCipher,
|
|
||||||
"AES-EAX": AESEAXCipher,
|
|
||||||
"DES-ECB": DESECBCipher,
|
|
||||||
"DES-CBC": DESCBCCipher,
|
|
||||||
"RSA-PKCS1-v1_5": RSACipher,
|
|
||||||
"DH": DHCipher,
|
|
||||||
"MD5": MD5,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
encrypt(algorithm, key, data) {
|
|
||||||
if (key.algorithm.name !== algorithm.name) {
|
|
||||||
throw new Error("algorithm does not match");
|
|
||||||
}
|
|
||||||
if (typeof key.encrypt !== "function") {
|
|
||||||
throw new Error("key does not support encryption");
|
|
||||||
}
|
|
||||||
return key.encrypt(algorithm, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
decrypt(algorithm, key, data) {
|
|
||||||
if (key.algorithm.name !== algorithm.name) {
|
|
||||||
throw new Error("algorithm does not match");
|
|
||||||
}
|
|
||||||
if (typeof key.decrypt !== "function") {
|
|
||||||
throw new Error("key does not support encryption");
|
|
||||||
}
|
|
||||||
return key.decrypt(algorithm, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
importKey(format, keyData, algorithm, extractable, keyUsages) {
|
|
||||||
if (format !== "raw") {
|
|
||||||
throw new Error("key format is not supported");
|
|
||||||
}
|
|
||||||
const alg = this._algorithms[algorithm.name];
|
|
||||||
if (typeof alg === "undefined" || typeof alg.importKey !== "function") {
|
|
||||||
throw new Error("algorithm is not supported");
|
|
||||||
}
|
|
||||||
return alg.importKey(keyData, algorithm, extractable, keyUsages);
|
|
||||||
}
|
|
||||||
|
|
||||||
generateKey(algorithm, extractable, keyUsages) {
|
|
||||||
const alg = this._algorithms[algorithm.name];
|
|
||||||
if (typeof alg === "undefined" || typeof alg.generateKey !== "function") {
|
|
||||||
throw new Error("algorithm is not supported");
|
|
||||||
}
|
|
||||||
return alg.generateKey(algorithm, extractable, keyUsages);
|
|
||||||
}
|
|
||||||
|
|
||||||
exportKey(format, key) {
|
|
||||||
if (format !== "raw") {
|
|
||||||
throw new Error("key format is not supported");
|
|
||||||
}
|
|
||||||
if (typeof key.exportKey !== "function") {
|
|
||||||
throw new Error("key does not support exportKey");
|
|
||||||
}
|
|
||||||
return key.exportKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
digest(algorithm, data) {
|
|
||||||
const alg = this._algorithms[algorithm];
|
|
||||||
if (typeof alg !== "function") {
|
|
||||||
throw new Error("algorithm is not supported");
|
|
||||||
}
|
|
||||||
return alg(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
deriveBits(algorithm, key, length) {
|
|
||||||
if (key.algorithm.name !== algorithm.name) {
|
|
||||||
throw new Error("algorithm does not match");
|
|
||||||
}
|
|
||||||
if (typeof key.deriveBits !== "function") {
|
|
||||||
throw new Error("key does not support deriveBits");
|
|
||||||
}
|
|
||||||
return key.deriveBits(algorithm, length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default new LegacyCrypto;
|
|
|
@ -1,330 +0,0 @@
|
||||||
/*
|
|
||||||
* Ported from Flashlight VNC ActionScript implementation:
|
|
||||||
* http://www.wizhelp.com/flashlight-vnc/
|
|
||||||
*
|
|
||||||
* Full attribution follows:
|
|
||||||
*
|
|
||||||
* -------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* This DES class has been extracted from package Acme.Crypto for use in VNC.
|
|
||||||
* The unnecessary odd parity code has been removed.
|
|
||||||
*
|
|
||||||
* These changes are:
|
|
||||||
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
|
|
||||||
* DesCipher - the DES encryption method
|
|
||||||
*
|
|
||||||
* The meat of this code is by Dave Zimmerman <dzimm@widget.com>, and is:
|
|
||||||
*
|
|
||||||
* Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software
|
|
||||||
* and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and
|
|
||||||
* without fee is hereby granted, provided that this copyright notice is kept
|
|
||||||
* intact.
|
|
||||||
*
|
|
||||||
* WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY
|
|
||||||
* OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
||||||
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE LIABLE
|
|
||||||
* FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
|
|
||||||
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
|
|
||||||
* CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
|
|
||||||
* PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
|
|
||||||
* NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
|
|
||||||
* SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
|
|
||||||
* SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
|
|
||||||
* PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). WIDGET WORKSHOP
|
|
||||||
* SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
|
|
||||||
* HIGH RISK ACTIVITIES.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* The rest is:
|
|
||||||
*
|
|
||||||
* Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* Visit the ACME Labs Java page for up-to-date versions of this and other
|
|
||||||
* fine Java utilities: http://www.acme.com/java/
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* eslint-disable comma-spacing */
|
|
||||||
|
|
||||||
// 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];
|
|
||||||
|
|
||||||
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];
|
|
||||||
|
|
||||||
/* eslint-enable comma-spacing */
|
|
||||||
|
|
||||||
class DES {
|
|
||||||
constructor(password) {
|
|
||||||
this.keys = [];
|
|
||||||
|
|
||||||
// Set the key.
|
|
||||||
const pc1m = [], pcr = [], kn = [];
|
|
||||||
|
|
||||||
for (let 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < 16; ++i) {
|
|
||||||
const m = i << 1;
|
|
||||||
const 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 (let j = 0; j < 24; ++j) {
|
|
||||||
if (pcr[PC2[j]] !== 0) {
|
|
||||||
kn[m] |= 1 << (23 - j);
|
|
||||||
}
|
|
||||||
if (pcr[PC2[j + 24]] !== 0) {
|
|
||||||
kn[n] |= 1 << (23 - j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
++KnLi;
|
|
||||||
this.keys[KnLi] = (raw0 & 0x0003f000) << 12;
|
|
||||||
this.keys[KnLi] |= (raw0 & 0x0000003f) << 16;
|
|
||||||
this.keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;
|
|
||||||
this.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
|
|
||||||
|
|
||||||
// Squash 8 bytes to 2 ints
|
|
||||||
l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
|
|
||||||
r = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
|
|
||||||
|
|
||||||
x = ((l >>> 4) ^ r) & 0x0f0f0f0f;
|
|
||||||
r ^= x;
|
|
||||||
l ^= (x << 4);
|
|
||||||
x = ((l >>> 16) ^ r) & 0x0000ffff;
|
|
||||||
r ^= x;
|
|
||||||
l ^= (x << 16);
|
|
||||||
x = ((r >>> 2) ^ l) & 0x33333333;
|
|
||||||
l ^= x;
|
|
||||||
r ^= (x << 2);
|
|
||||||
x = ((r >>> 8) ^ l) & 0x00ff00ff;
|
|
||||||
l ^= x;
|
|
||||||
r ^= (x << 8);
|
|
||||||
r = (r << 1) | ((r >>> 31) & 1);
|
|
||||||
x = (l ^ r) & 0xaaaaaaaa;
|
|
||||||
l ^= x;
|
|
||||||
r ^= x;
|
|
||||||
l = (l << 1) | ((l >>> 31) & 1);
|
|
||||||
|
|
||||||
for (let i = 0, keysi = 0; i < 8; ++i) {
|
|
||||||
x = (r << 28) | (r >>> 4);
|
|
||||||
x ^= this.keys[keysi++];
|
|
||||||
let fval = SP7[x & 0x3f];
|
|
||||||
fval |= SP5[(x >>> 8) & 0x3f];
|
|
||||||
fval |= SP3[(x >>> 16) & 0x3f];
|
|
||||||
fval |= SP1[(x >>> 24) & 0x3f];
|
|
||||||
x = r ^ this.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++];
|
|
||||||
fval = SP7[x & 0x3f];
|
|
||||||
fval |= SP5[(x >>> 8) & 0x3f];
|
|
||||||
fval |= SP3[(x >>> 16) & 0x3f];
|
|
||||||
fval |= SP1[(x >>> 24) & 0x3f];
|
|
||||||
x = l ^ this.keys[keysi++];
|
|
||||||
fval |= SP8[x & 0x0000003f];
|
|
||||||
fval |= SP6[(x >>> 8) & 0x3f];
|
|
||||||
fval |= SP4[(x >>> 16) & 0x3f];
|
|
||||||
fval |= SP2[(x >>> 24) & 0x3f];
|
|
||||||
r ^= fval;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = (r << 31) | (r >>> 1);
|
|
||||||
x = (l ^ r) & 0xaaaaaaaa;
|
|
||||||
l ^= x;
|
|
||||||
r ^= x;
|
|
||||||
l = (l << 31) | (l >>> 1);
|
|
||||||
x = ((l >>> 8) ^ r) & 0x00ff00ff;
|
|
||||||
r ^= x;
|
|
||||||
l ^= (x << 8);
|
|
||||||
x = ((l >>> 2) ^ r) & 0x33333333;
|
|
||||||
r ^= x;
|
|
||||||
l ^= (x << 2);
|
|
||||||
x = ((r >>> 16) ^ l) & 0x0000ffff;
|
|
||||||
l ^= x;
|
|
||||||
r ^= (x << 16);
|
|
||||||
x = ((r >>> 4) ^ l) & 0x0f0f0f0f;
|
|
||||||
l ^= x;
|
|
||||||
r ^= (x << 4);
|
|
||||||
|
|
||||||
// Spread ints to bytes
|
|
||||||
x = [r, l];
|
|
||||||
for (i = 0; i < 8; i++) {
|
|
||||||
b[i] = (x[i>>>2] >>> (8 * (3 - (i % 4)))) % 256;
|
|
||||||
if (b[i] < 0) { b[i] += 256; } // unsigned
|
|
||||||
}
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DESECBCipher {
|
|
||||||
constructor() {
|
|
||||||
this._cipher = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get algorithm() {
|
|
||||||
return { name: "DES-ECB" };
|
|
||||||
}
|
|
||||||
|
|
||||||
static importKey(key, _algorithm, _extractable, _keyUsages) {
|
|
||||||
const cipher = new DESECBCipher;
|
|
||||||
cipher._importKey(key);
|
|
||||||
return cipher;
|
|
||||||
}
|
|
||||||
|
|
||||||
_importKey(key, _extractable, _keyUsages) {
|
|
||||||
this._cipher = new DES(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
encrypt(_algorithm, plaintext) {
|
|
||||||
const x = new Uint8Array(plaintext);
|
|
||||||
if (x.length % 8 !== 0 || this._cipher === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const n = x.length / 8;
|
|
||||||
for (let i = 0; i < n; i++) {
|
|
||||||
x.set(this._cipher.enc8(x.slice(i * 8, i * 8 + 8)), i * 8);
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DESCBCCipher {
|
|
||||||
constructor() {
|
|
||||||
this._cipher = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get algorithm() {
|
|
||||||
return { name: "DES-CBC" };
|
|
||||||
}
|
|
||||||
|
|
||||||
static importKey(key, _algorithm, _extractable, _keyUsages) {
|
|
||||||
const cipher = new DESCBCCipher;
|
|
||||||
cipher._importKey(key);
|
|
||||||
return cipher;
|
|
||||||
}
|
|
||||||
|
|
||||||
_importKey(key) {
|
|
||||||
this._cipher = new DES(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
encrypt(algorithm, plaintext) {
|
|
||||||
const x = new Uint8Array(plaintext);
|
|
||||||
let y = new Uint8Array(algorithm.iv);
|
|
||||||
if (x.length % 8 !== 0 || this._cipher === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const n = x.length / 8;
|
|
||||||
for (let i = 0; i < n; i++) {
|
|
||||||
for (let j = 0; j < 8; j++) {
|
|
||||||
y[j] ^= plaintext[i * 8 + j];
|
|
||||||
}
|
|
||||||
y = this._cipher.enc8(y);
|
|
||||||
x.set(y, i * 8);
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
import { modPow, bigIntToU8Array, u8ArrayToBigInt } from "./bigint.js";
|
|
||||||
|
|
||||||
class DHPublicKey {
|
|
||||||
constructor(key) {
|
|
||||||
this._key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
get algorithm() {
|
|
||||||
return { name: "DH" };
|
|
||||||
}
|
|
||||||
|
|
||||||
exportKey() {
|
|
||||||
return this._key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DHCipher {
|
|
||||||
constructor() {
|
|
||||||
this._g = null;
|
|
||||||
this._p = null;
|
|
||||||
this._gBigInt = null;
|
|
||||||
this._pBigInt = null;
|
|
||||||
this._privateKey = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get algorithm() {
|
|
||||||
return { name: "DH" };
|
|
||||||
}
|
|
||||||
|
|
||||||
static generateKey(algorithm, _extractable) {
|
|
||||||
const cipher = new DHCipher;
|
|
||||||
cipher._generateKey(algorithm);
|
|
||||||
return { privateKey: cipher, publicKey: new DHPublicKey(cipher._publicKey) };
|
|
||||||
}
|
|
||||||
|
|
||||||
_generateKey(algorithm) {
|
|
||||||
const g = algorithm.g;
|
|
||||||
const p = algorithm.p;
|
|
||||||
this._keyBytes = p.length;
|
|
||||||
this._gBigInt = u8ArrayToBigInt(g);
|
|
||||||
this._pBigInt = u8ArrayToBigInt(p);
|
|
||||||
this._privateKey = window.crypto.getRandomValues(new Uint8Array(this._keyBytes));
|
|
||||||
this._privateKeyBigInt = u8ArrayToBigInt(this._privateKey);
|
|
||||||
this._publicKey = bigIntToU8Array(modPow(
|
|
||||||
this._gBigInt, this._privateKeyBigInt, this._pBigInt), this._keyBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
deriveBits(algorithm, length) {
|
|
||||||
const bytes = Math.ceil(length / 8);
|
|
||||||
const pkey = new Uint8Array(algorithm.public);
|
|
||||||
const len = bytes > this._keyBytes ? bytes : this._keyBytes;
|
|
||||||
const secret = modPow(u8ArrayToBigInt(pkey), this._privateKeyBigInt, this._pBigInt);
|
|
||||||
return bigIntToU8Array(secret, len).slice(0, len);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
/*
|
|
||||||
* noVNC: HTML5 VNC client
|
|
||||||
* Copyright (C) 2021 The noVNC authors
|
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
|
||||||
*
|
|
||||||
* See README.md for usage and integration instructions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Performs MD5 hashing on an array of bytes, returns an array of bytes
|
|
||||||
*/
|
|
||||||
|
|
||||||
export async function MD5(d) {
|
|
||||||
let s = "";
|
|
||||||
for (let i = 0; i < d.length; i++) {
|
|
||||||
s += String.fromCharCode(d[i]);
|
|
||||||
}
|
|
||||||
return M(V(Y(X(s), 8 * s.length)));
|
|
||||||
}
|
|
||||||
|
|
||||||
function M(d) {
|
|
||||||
let f = new Uint8Array(d.length);
|
|
||||||
for (let i=0;i<d.length;i++) {
|
|
||||||
f[i] = d.charCodeAt(i);
|
|
||||||
}
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
function X(d) {
|
|
||||||
let r = Array(d.length >> 2);
|
|
||||||
for (let m = 0; m < r.length; m++) r[m] = 0;
|
|
||||||
for (let m = 0; m < 8 * d.length; m += 8) r[m >> 5] |= (255 & d.charCodeAt(m / 8)) << m % 32;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
function V(d) {
|
|
||||||
let r = "";
|
|
||||||
for (let m = 0; m < 32 * d.length; m += 8) r += String.fromCharCode(d[m >> 5] >>> m % 32 & 255);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Y(d, g) {
|
|
||||||
d[g >> 5] |= 128 << g % 32, d[14 + (g + 64 >>> 9 << 4)] = g;
|
|
||||||
let m = 1732584193, f = -271733879, r = -1732584194, i = 271733878;
|
|
||||||
for (let n = 0; n < d.length; n += 16) {
|
|
||||||
let h = m,
|
|
||||||
t = f,
|
|
||||||
g = r,
|
|
||||||
e = i;
|
|
||||||
f = ii(f = ii(f = ii(f = ii(f = hh(f = hh(f = hh(f = hh(f = gg(f = gg(f = gg(f = gg(f = ff(f = ff(f = ff(f = ff(f, r = ff(r, i = ff(i, m = ff(m, f, r, i, d[n + 0], 7, -680876936), f, r, d[n + 1], 12, -389564586), m, f, d[n + 2], 17, 606105819), i, m, d[n + 3], 22, -1044525330), r = ff(r, i = ff(i, m = ff(m, f, r, i, d[n + 4], 7, -176418897), f, r, d[n + 5], 12, 1200080426), m, f, d[n + 6], 17, -1473231341), i, m, d[n + 7], 22, -45705983), r = ff(r, i = ff(i, m = ff(m, f, r, i, d[n + 8], 7, 1770035416), f, r, d[n + 9], 12, -1958414417), m, f, d[n + 10], 17, -42063), i, m, d[n + 11], 22, -1990404162), r = ff(r, i = ff(i, m = ff(m, f, r, i, d[n + 12], 7, 1804603682), f, r, d[n + 13], 12, -40341101), m, f, d[n + 14], 17, -1502002290), i, m, d[n + 15], 22, 1236535329), r = gg(r, i = gg(i, m = gg(m, f, r, i, d[n + 1], 5, -165796510), f, r, d[n + 6], 9, -1069501632), m, f, d[n + 11], 14, 643717713), i, m, d[n + 0], 20, -373897302), r = gg(r, i = gg(i, m = gg(m, f, r, i, d[n + 5], 5, -701558691), f, r, d[n + 10], 9, 38016083), m, f, d[n + 15], 14, -660478335), i, m, d[n + 4], 20, -405537848), r = gg(r, i = gg(i, m = gg(m, f, r, i, d[n + 9], 5, 568446438), f, r, d[n + 14], 9, -1019803690), m, f, d[n + 3], 14, -187363961), i, m, d[n + 8], 20, 1163531501), r = gg(r, i = gg(i, m = gg(m, f, r, i, d[n + 13], 5, -1444681467), f, r, d[n + 2], 9, -51403784), m, f, d[n + 7], 14, 1735328473), i, m, d[n + 12], 20, -1926607734), r = hh(r, i = hh(i, m = hh(m, f, r, i, d[n + 5], 4, -378558), f, r, d[n + 8], 11, -2022574463), m, f, d[n + 11], 16, 1839030562), i, m, d[n + 14], 23, -35309556), r = hh(r, i = hh(i, m = hh(m, f, r, i, d[n + 1], 4, -1530992060), f, r, d[n + 4], 11, 1272893353), m, f, d[n + 7], 16, -155497632), i, m, d[n + 10], 23, -1094730640), r = hh(r, i = hh(i, m = hh(m, f, r, i, d[n + 13], 4, 681279174), f, r, d[n + 0], 11, -358537222), m, f, d[n + 3], 16, -722521979), i, m, d[n + 6], 23, 76029189), r = hh(r, i = hh(i, m = hh(m, f, r, i, d[n + 9], 4, -640364487), f, r, d[n + 12], 11, -421815835), m, f, d[n + 15], 16, 530742520), i, m, d[n + 2], 23, -995338651), r = ii(r, i = ii(i, m = ii(m, f, r, i, d[n + 0], 6, -198630844), f, r, d[n + 7], 10, 1126891415), m, f, d[n + 14], 15, -1416354905), i, m, d[n + 5], 21, -57434055), r = ii(r, i = ii(i, m = ii(m, f, r, i, d[n + 12], 6, 1700485571), f, r, d[n + 3], 10, -1894986606), m, f, d[n + 10], 15, -1051523), i, m, d[n + 1], 21, -2054922799), r = ii(r, i = ii(i, m = ii(m, f, r, i, d[n + 8], 6, 1873313359), f, r, d[n + 15], 10, -30611744), m, f, d[n + 6], 15, -1560198380), i, m, d[n + 13], 21, 1309151649), r = ii(r, i = ii(i, m = ii(m, f, r, i, d[n + 4], 6, -145523070), f, r, d[n + 11], 10, -1120210379), m, f, d[n + 2], 15, 718787259), i, m, d[n + 9], 21, -343485551), m = add(m, h), f = add(f, t), r = add(r, g), i = add(i, e);
|
|
||||||
}
|
|
||||||
return Array(m, f, r, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
function cmn(d, g, m, f, r, i) {
|
|
||||||
return add(rol(add(add(g, d), add(f, i)), r), m);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ff(d, g, m, f, r, i, n) {
|
|
||||||
return cmn(g & m | ~g & f, d, g, r, i, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
function gg(d, g, m, f, r, i, n) {
|
|
||||||
return cmn(g & f | m & ~f, d, g, r, i, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
function hh(d, g, m, f, r, i, n) {
|
|
||||||
return cmn(g ^ m ^ f, d, g, r, i, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ii(d, g, m, f, r, i, n) {
|
|
||||||
return cmn(m ^ (g | ~f), d, g, r, i, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
function add(d, g) {
|
|
||||||
let m = (65535 & d) + (65535 & g);
|
|
||||||
return (d >> 16) + (g >> 16) + (m >> 16) << 16 | 65535 & m;
|
|
||||||
}
|
|
||||||
|
|
||||||
function rol(d, g) {
|
|
||||||
return d << g | d >>> 32 - g;
|
|
||||||
}
|
|
|
@ -1,132 +0,0 @@
|
||||||
import Base64 from "../base64.js";
|
|
||||||
import { modPow, bigIntToU8Array, u8ArrayToBigInt } from "./bigint.js";
|
|
||||||
|
|
||||||
export class RSACipher {
|
|
||||||
constructor() {
|
|
||||||
this._keyLength = 0;
|
|
||||||
this._keyBytes = 0;
|
|
||||||
this._n = null;
|
|
||||||
this._e = null;
|
|
||||||
this._d = null;
|
|
||||||
this._nBigInt = null;
|
|
||||||
this._eBigInt = null;
|
|
||||||
this._dBigInt = null;
|
|
||||||
this._extractable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
get algorithm() {
|
|
||||||
return { name: "RSA-PKCS1-v1_5" };
|
|
||||||
}
|
|
||||||
|
|
||||||
_base64urlDecode(data) {
|
|
||||||
data = data.replace(/-/g, "+").replace(/_/g, "/");
|
|
||||||
data = data.padEnd(Math.ceil(data.length / 4) * 4, "=");
|
|
||||||
return Base64.decode(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
_padArray(arr, length) {
|
|
||||||
const res = new Uint8Array(length);
|
|
||||||
res.set(arr, length - arr.length);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async generateKey(algorithm, extractable, _keyUsages) {
|
|
||||||
const cipher = new RSACipher;
|
|
||||||
await cipher._generateKey(algorithm, extractable);
|
|
||||||
return { privateKey: cipher };
|
|
||||||
}
|
|
||||||
|
|
||||||
async _generateKey(algorithm, extractable) {
|
|
||||||
this._keyLength = algorithm.modulusLength;
|
|
||||||
this._keyBytes = Math.ceil(this._keyLength / 8);
|
|
||||||
const key = await window.crypto.subtle.generateKey(
|
|
||||||
{
|
|
||||||
name: "RSA-OAEP",
|
|
||||||
modulusLength: algorithm.modulusLength,
|
|
||||||
publicExponent: algorithm.publicExponent,
|
|
||||||
hash: {name: "SHA-256"},
|
|
||||||
},
|
|
||||||
true, ["encrypt", "decrypt"]);
|
|
||||||
const privateKey = await window.crypto.subtle.exportKey("jwk", key.privateKey);
|
|
||||||
this._n = this._padArray(this._base64urlDecode(privateKey.n), this._keyBytes);
|
|
||||||
this._nBigInt = u8ArrayToBigInt(this._n);
|
|
||||||
this._e = this._padArray(this._base64urlDecode(privateKey.e), this._keyBytes);
|
|
||||||
this._eBigInt = u8ArrayToBigInt(this._e);
|
|
||||||
this._d = this._padArray(this._base64urlDecode(privateKey.d), this._keyBytes);
|
|
||||||
this._dBigInt = u8ArrayToBigInt(this._d);
|
|
||||||
this._extractable = extractable;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async importKey(key, _algorithm, extractable, keyUsages) {
|
|
||||||
if (keyUsages.length !== 1 || keyUsages[0] !== "encrypt") {
|
|
||||||
throw new Error("only support importing RSA public key");
|
|
||||||
}
|
|
||||||
const cipher = new RSACipher;
|
|
||||||
await cipher._importKey(key, extractable);
|
|
||||||
return cipher;
|
|
||||||
}
|
|
||||||
|
|
||||||
async _importKey(key, extractable) {
|
|
||||||
const n = key.n;
|
|
||||||
const e = key.e;
|
|
||||||
if (n.length !== e.length) {
|
|
||||||
throw new Error("the sizes of modulus and public exponent do not match");
|
|
||||||
}
|
|
||||||
this._keyBytes = n.length;
|
|
||||||
this._keyLength = this._keyBytes * 8;
|
|
||||||
this._n = new Uint8Array(this._keyBytes);
|
|
||||||
this._e = new Uint8Array(this._keyBytes);
|
|
||||||
this._n.set(n);
|
|
||||||
this._e.set(e);
|
|
||||||
this._nBigInt = u8ArrayToBigInt(this._n);
|
|
||||||
this._eBigInt = u8ArrayToBigInt(this._e);
|
|
||||||
this._extractable = extractable;
|
|
||||||
}
|
|
||||||
|
|
||||||
async encrypt(_algorithm, message) {
|
|
||||||
if (message.length > this._keyBytes - 11) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const ps = new Uint8Array(this._keyBytes - message.length - 3);
|
|
||||||
window.crypto.getRandomValues(ps);
|
|
||||||
for (let i = 0; i < ps.length; i++) {
|
|
||||||
ps[i] = Math.floor(ps[i] * 254 / 255 + 1);
|
|
||||||
}
|
|
||||||
const em = new Uint8Array(this._keyBytes);
|
|
||||||
em[1] = 0x02;
|
|
||||||
em.set(ps, 2);
|
|
||||||
em.set(message, ps.length + 3);
|
|
||||||
const emBigInt = u8ArrayToBigInt(em);
|
|
||||||
const c = modPow(emBigInt, this._eBigInt, this._nBigInt);
|
|
||||||
return bigIntToU8Array(c, this._keyBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
async decrypt(_algorithm, message) {
|
|
||||||
if (message.length !== this._keyBytes) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const msgBigInt = u8ArrayToBigInt(message);
|
|
||||||
const emBigInt = modPow(msgBigInt, this._dBigInt, this._nBigInt);
|
|
||||||
const em = bigIntToU8Array(emBigInt, this._keyBytes);
|
|
||||||
if (em[0] !== 0x00 || em[1] !== 0x02) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
let i = 2;
|
|
||||||
for (; i < em.length; i++) {
|
|
||||||
if (em[i] === 0x00) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i === em.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return em.slice(i + 1, em.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
async exportKey() {
|
|
||||||
if (!this._extractable) {
|
|
||||||
throw new Error("key is not extractable");
|
|
||||||
}
|
|
||||||
return { n: this._n, e: this._e, d: this._d };
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,321 +0,0 @@
|
||||||
/*
|
|
||||||
* noVNC: HTML5 VNC client
|
|
||||||
* Copyright (C) 2024 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 class H264Parser {
|
|
||||||
constructor(data) {
|
|
||||||
this._data = data;
|
|
||||||
this._index = 0;
|
|
||||||
this.profileIdc = null;
|
|
||||||
this.constraintSet = null;
|
|
||||||
this.levelIdc = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_getStartSequenceLen(index) {
|
|
||||||
let data = this._data;
|
|
||||||
if (data[index + 0] == 0 && data[index + 1] == 0 && data[index + 2] == 0 && data[index + 3] == 1) {
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
if (data[index + 0] == 0 && data[index + 1] == 0 && data[index + 2] == 1) {
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
_indexOfNextNalUnit(index) {
|
|
||||||
let data = this._data;
|
|
||||||
for (let i = index; i < data.length; ++i) {
|
|
||||||
if (this._getStartSequenceLen(i) != 0) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_parseSps(index) {
|
|
||||||
this.profileIdc = this._data[index];
|
|
||||||
this.constraintSet = this._data[index + 1];
|
|
||||||
this.levelIdc = this._data[index + 2];
|
|
||||||
}
|
|
||||||
|
|
||||||
_parseNalUnit(index) {
|
|
||||||
const firstByte = this._data[index];
|
|
||||||
if (firstByte & 0x80) {
|
|
||||||
throw new Error('H264 parsing sanity check failed, forbidden zero bit is set');
|
|
||||||
}
|
|
||||||
const unitType = firstByte & 0x1f;
|
|
||||||
|
|
||||||
switch (unitType) {
|
|
||||||
case 1: // coded slice, non-idr
|
|
||||||
return { slice: true };
|
|
||||||
case 5: // coded slice, idr
|
|
||||||
return { slice: true, key: true };
|
|
||||||
case 6: // sei
|
|
||||||
return {};
|
|
||||||
case 7: // sps
|
|
||||||
this._parseSps(index + 1);
|
|
||||||
return {};
|
|
||||||
case 8: // pps
|
|
||||||
return {};
|
|
||||||
default:
|
|
||||||
Log.Warn("Unhandled unit type: ", unitType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
parse() {
|
|
||||||
const startIndex = this._index;
|
|
||||||
let isKey = false;
|
|
||||||
|
|
||||||
while (this._index < this._data.length) {
|
|
||||||
const startSequenceLen = this._getStartSequenceLen(this._index);
|
|
||||||
if (startSequenceLen == 0) {
|
|
||||||
throw new Error('Invalid start sequence in bit stream');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { slice, key } = this._parseNalUnit(this._index + startSequenceLen);
|
|
||||||
|
|
||||||
let nextIndex = this._indexOfNextNalUnit(this._index + startSequenceLen);
|
|
||||||
if (nextIndex == -1) {
|
|
||||||
this._index = this._data.length;
|
|
||||||
} else {
|
|
||||||
this._index = nextIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key) {
|
|
||||||
isKey = true;
|
|
||||||
}
|
|
||||||
if (slice) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startIndex === this._index) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
frame: this._data.subarray(startIndex, this._index),
|
|
||||||
key: isKey,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class H264Context {
|
|
||||||
constructor(width, height) {
|
|
||||||
this.lastUsed = 0;
|
|
||||||
this._width = width;
|
|
||||||
this._height = height;
|
|
||||||
this._profileIdc = null;
|
|
||||||
this._constraintSet = null;
|
|
||||||
this._levelIdc = null;
|
|
||||||
this._decoder = null;
|
|
||||||
this._pendingFrames = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleFrame(frame) {
|
|
||||||
let pending = this._pendingFrames.shift();
|
|
||||||
if (pending === undefined) {
|
|
||||||
throw new Error("Pending frame queue empty when receiving frame from decoder");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pending.timestamp != frame.timestamp) {
|
|
||||||
throw new Error("Video frame timestamp mismatch. Expected " +
|
|
||||||
frame.timestamp + " but but got " + pending.timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
pending.frame = frame;
|
|
||||||
pending.ready = true;
|
|
||||||
pending.resolve();
|
|
||||||
|
|
||||||
if (!pending.keep) {
|
|
||||||
frame.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleError(e) {
|
|
||||||
throw new Error("Failed to decode frame: " + e.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
_configureDecoder(profileIdc, constraintSet, levelIdc) {
|
|
||||||
if (this._decoder === null || this._decoder.state === 'closed') {
|
|
||||||
this._decoder = new VideoDecoder({
|
|
||||||
output: frame => this._handleFrame(frame),
|
|
||||||
error: e => this._handleError(e),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const codec = 'avc1.' +
|
|
||||||
profileIdc.toString(16).padStart(2, '0') +
|
|
||||||
constraintSet.toString(16).padStart(2, '0') +
|
|
||||||
levelIdc.toString(16).padStart(2, '0');
|
|
||||||
this._decoder.configure({
|
|
||||||
codec: codec,
|
|
||||||
codedWidth: this._width,
|
|
||||||
codedHeight: this._height,
|
|
||||||
optimizeForLatency: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_preparePendingFrame(timestamp) {
|
|
||||||
let pending = {
|
|
||||||
timestamp: timestamp,
|
|
||||||
promise: null,
|
|
||||||
resolve: null,
|
|
||||||
frame: null,
|
|
||||||
ready: false,
|
|
||||||
keep: false,
|
|
||||||
};
|
|
||||||
pending.promise = new Promise((resolve) => {
|
|
||||||
pending.resolve = resolve;
|
|
||||||
});
|
|
||||||
this._pendingFrames.push(pending);
|
|
||||||
|
|
||||||
return pending;
|
|
||||||
}
|
|
||||||
|
|
||||||
decode(payload) {
|
|
||||||
let parser = new H264Parser(payload);
|
|
||||||
let result = null;
|
|
||||||
|
|
||||||
// Ideally, this timestamp should come from the server, but we'll just
|
|
||||||
// approximate it instead.
|
|
||||||
let timestamp = Math.round(window.performance.now() * 1e3);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
let encodedFrame = parser.parse();
|
|
||||||
if (encodedFrame === null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parser.profileIdc !== null) {
|
|
||||||
self._profileIdc = parser.profileIdc;
|
|
||||||
self._constraintSet = parser.constraintSet;
|
|
||||||
self._levelIdc = parser.levelIdc;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._decoder === null || this._decoder.state !== 'configured') {
|
|
||||||
if (!encodedFrame.key) {
|
|
||||||
Log.Warn("Missing key frame. Can't decode until one arrives");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (self._profileIdc === null) {
|
|
||||||
Log.Warn('Cannot config decoder. Have not received SPS and PPS yet.');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
this._configureDecoder(self._profileIdc, self._constraintSet,
|
|
||||||
self._levelIdc);
|
|
||||||
}
|
|
||||||
|
|
||||||
result = this._preparePendingFrame(timestamp);
|
|
||||||
|
|
||||||
const chunk = new EncodedVideoChunk({
|
|
||||||
timestamp: timestamp,
|
|
||||||
type: encodedFrame.key ? 'key' : 'delta',
|
|
||||||
data: encodedFrame.frame,
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
this._decoder.decode(chunk);
|
|
||||||
} catch (e) {
|
|
||||||
Log.Warn("Failed to decode:", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We only keep last frame of each payload
|
|
||||||
if (result !== null) {
|
|
||||||
result.keep = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class H264Decoder {
|
|
||||||
constructor() {
|
|
||||||
this._tick = 0;
|
|
||||||
this._contexts = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
_contextId(x, y, width, height) {
|
|
||||||
return [x, y, width, height].join(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
_findOldestContextId() {
|
|
||||||
let oldestTick = Number.MAX_VALUE;
|
|
||||||
let oldestKey = undefined;
|
|
||||||
for (const [key, value] of Object.entries(this._contexts)) {
|
|
||||||
if (value.lastUsed < oldestTick) {
|
|
||||||
oldestTick = value.lastUsed;
|
|
||||||
oldestKey = key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return oldestKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
_createContext(x, y, width, height) {
|
|
||||||
const maxContexts = 64;
|
|
||||||
if (Object.keys(this._contexts).length >= maxContexts) {
|
|
||||||
let oldestContextId = this._findOldestContextId();
|
|
||||||
delete this._contexts[oldestContextId];
|
|
||||||
}
|
|
||||||
let context = new H264Context(width, height);
|
|
||||||
this._contexts[this._contextId(x, y, width, height)] = context;
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
_getContext(x, y, width, height) {
|
|
||||||
let context = this._contexts[this._contextId(x, y, width, height)];
|
|
||||||
return context !== undefined ? context : this._createContext(x, y, width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
_resetContext(x, y, width, height) {
|
|
||||||
delete this._contexts[this._contextId(x, y, width, height)];
|
|
||||||
}
|
|
||||||
|
|
||||||
_resetAllContexts() {
|
|
||||||
this._contexts = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
decodeRect(x, y, width, height, sock, display, depth) {
|
|
||||||
const resetContextFlag = 1;
|
|
||||||
const resetAllContextsFlag = 2;
|
|
||||||
|
|
||||||
if (sock.rQwait("h264 header", 8)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const length = sock.rQshift32();
|
|
||||||
const flags = sock.rQshift32();
|
|
||||||
|
|
||||||
if (sock.rQwait("h264 payload", length, 8)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & resetAllContextsFlag) {
|
|
||||||
this._resetAllContexts();
|
|
||||||
} else if (flags & resetContextFlag) {
|
|
||||||
this._resetContext(x, y, width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
let context = this._getContext(x, y, width, height);
|
|
||||||
context.lastUsed = this._tick++;
|
|
||||||
|
|
||||||
if (length !== 0) {
|
|
||||||
let payload = sock.rQshiftBytes(length, false);
|
|
||||||
let frame = context.decode(payload);
|
|
||||||
if (frame !== null) {
|
|
||||||
display.videoFrame(x, y, width, height, frame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,181 +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 subencoding = sock.rQpeek8();
|
|
||||||
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 = sock.rQpeekBytes(bytes).at(-1);
|
|
||||||
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
|
|
||||||
sock.rQshift8();
|
|
||||||
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;
|
|
||||||
let data = sock.rQshiftBytes(pixels * 4, false);
|
|
||||||
// Max sure the image is fully opaque
|
|
||||||
for (let i = 0;i < pixels;i++) {
|
|
||||||
data[i * 4 + 3] = 255;
|
|
||||||
}
|
|
||||||
display.blitImage(tx, ty, tw, th, data, 0);
|
|
||||||
} else {
|
|
||||||
if (subencoding & 0x02) { // Background
|
|
||||||
this._background = new Uint8Array(sock.rQshiftBytes(4));
|
|
||||||
}
|
|
||||||
if (subencoding & 0x04) { // Foreground
|
|
||||||
this._foreground = new Uint8Array(sock.rQshiftBytes(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
this._startTile(tx, ty, tw, th, this._background);
|
|
||||||
if (subencoding & 0x08) { // AnySubrects
|
|
||||||
let subrects = sock.rQshift8();
|
|
||||||
|
|
||||||
for (let s = 0; s < subrects; s++) {
|
|
||||||
let color;
|
|
||||||
if (subencoding & 0x10) { // SubrectsColoured
|
|
||||||
color = sock.rQshiftBytes(4);
|
|
||||||
} else {
|
|
||||||
color = this._foreground;
|
|
||||||
}
|
|
||||||
const xy = sock.rQshift8();
|
|
||||||
const sx = (xy >> 4);
|
|
||||||
const sy = (xy & 0x0f);
|
|
||||||
|
|
||||||
const wh = sock.rQshift8();
|
|
||||||
const sw = (wh >> 4) + 1;
|
|
||||||
const sh = (wh & 0x0f) + 1;
|
|
||||||
|
|
||||||
this._subTile(sx, sy, sw, sh, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._finishTile(display);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,146 +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 JPEGDecoder {
|
|
||||||
constructor() {
|
|
||||||
// RealVNC will reuse the quantization tables
|
|
||||||
// and Huffman tables, so we need to cache them.
|
|
||||||
this._cachedQuantTables = [];
|
|
||||||
this._cachedHuffmanTables = [];
|
|
||||||
|
|
||||||
this._segments = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
decodeRect(x, y, width, height, sock, display, depth) {
|
|
||||||
// A rect of JPEG encodings is simply a JPEG file
|
|
||||||
while (true) {
|
|
||||||
let segment = this._readSegment(sock);
|
|
||||||
if (segment === null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this._segments.push(segment);
|
|
||||||
// End of image?
|
|
||||||
if (segment[1] === 0xD9) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let huffmanTables = [];
|
|
||||||
let quantTables = [];
|
|
||||||
for (let segment of this._segments) {
|
|
||||||
let type = segment[1];
|
|
||||||
if (type === 0xC4) {
|
|
||||||
// Huffman tables
|
|
||||||
huffmanTables.push(segment);
|
|
||||||
} else if (type === 0xDB) {
|
|
||||||
// Quantization tables
|
|
||||||
quantTables.push(segment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const sofIndex = this._segments.findIndex(
|
|
||||||
x => x[1] == 0xC0 || x[1] == 0xC2
|
|
||||||
);
|
|
||||||
if (sofIndex == -1) {
|
|
||||||
throw new Error("Illegal JPEG image without SOF");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (quantTables.length === 0) {
|
|
||||||
this._segments.splice(sofIndex+1, 0,
|
|
||||||
...this._cachedQuantTables);
|
|
||||||
}
|
|
||||||
if (huffmanTables.length === 0) {
|
|
||||||
this._segments.splice(sofIndex+1, 0,
|
|
||||||
...this._cachedHuffmanTables);
|
|
||||||
}
|
|
||||||
|
|
||||||
let length = 0;
|
|
||||||
for (let segment of this._segments) {
|
|
||||||
length += segment.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = new Uint8Array(length);
|
|
||||||
length = 0;
|
|
||||||
for (let segment of this._segments) {
|
|
||||||
data.set(segment, length);
|
|
||||||
length += segment.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
display.imageRect(x, y, width, height, "image/jpeg", data);
|
|
||||||
|
|
||||||
if (huffmanTables.length !== 0) {
|
|
||||||
this._cachedHuffmanTables = huffmanTables;
|
|
||||||
}
|
|
||||||
if (quantTables.length !== 0) {
|
|
||||||
this._cachedQuantTables = quantTables;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._segments = [];
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_readSegment(sock) {
|
|
||||||
if (sock.rQwait("JPEG", 2)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let marker = sock.rQshift8();
|
|
||||||
if (marker != 0xFF) {
|
|
||||||
throw new Error("Illegal JPEG marker received (byte: " +
|
|
||||||
marker + ")");
|
|
||||||
}
|
|
||||||
let type = sock.rQshift8();
|
|
||||||
if (type >= 0xD0 && type <= 0xD9 || type == 0x01) {
|
|
||||||
// No length after marker
|
|
||||||
return new Uint8Array([marker, type]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sock.rQwait("JPEG", 2, 2)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let length = sock.rQshift16();
|
|
||||||
if (length < 2) {
|
|
||||||
throw new Error("Illegal JPEG length received (length: " +
|
|
||||||
length + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sock.rQwait("JPEG", length-2, 4)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let extra = 0;
|
|
||||||
if (type === 0xDA) {
|
|
||||||
// start of scan
|
|
||||||
extra += 2;
|
|
||||||
while (true) {
|
|
||||||
if (sock.rQwait("JPEG", length-2+extra, 4)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
let data = sock.rQpeekBytes(length-2+extra, false);
|
|
||||||
if (data.at(-2) === 0xFF && data.at(-1) !== 0x00 &&
|
|
||||||
!(data.at(-1) >= 0xD0 && data.at(-1) <= 0xD7)) {
|
|
||||||
extra -= 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
extra++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let segment = new Uint8Array(2 + length + extra);
|
|
||||||
segment[0] = marker;
|
|
||||||
segment[1] = type;
|
|
||||||
segment[2] = length >> 8;
|
|
||||||
segment[3] = length;
|
|
||||||
segment.set(sock.rQshiftBytes(length-2+extra, false), 4);
|
|
||||||
|
|
||||||
return segment;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +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;
|
|
||||||
|
|
||||||
while (this._lines > 0) {
|
|
||||||
if (sock.rQwait("RAW", bytesPerLine)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const curY = y + (height - this._lines);
|
|
||||||
|
|
||||||
let data = sock.rQshiftBytes(bytesPerLine, false);
|
|
||||||
|
|
||||||
// Convert data if needed
|
|
||||||
if (depth == 8) {
|
|
||||||
const newdata = new Uint8Array(width * 4);
|
|
||||||
for (let i = 0; i < width; i++) {
|
|
||||||
newdata[i * 4 + 0] = ((data[i] >> 0) & 0x3) * 255 / 3;
|
|
||||||
newdata[i * 4 + 1] = ((data[i] >> 2) & 0x3) * 255 / 3;
|
|
||||||
newdata[i * 4 + 2] = ((data[i] >> 4) & 0x3) * 255 / 3;
|
|
||||||
newdata[i * 4 + 3] = 255;
|
|
||||||
}
|
|
||||||
data = newdata;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Max sure the image is fully opaque
|
|
||||||
for (let i = 0; i < width; i++) {
|
|
||||||
data[i * 4 + 3] = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
display.blitImage(x, curY, width, 1, data, 0);
|
|
||||||
this._lines--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,393 +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;
|
|
||||||
}
|
|
||||||
|
|
||||||
let pixel = sock.rQshiftBytes(3);
|
|
||||||
display.fillRect(x, y, width, height, pixel, false);
|
|
||||||
|
|
||||||
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) {
|
|
||||||
// assume the TPIXEL is 3 bytes long
|
|
||||||
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(4 * width * height);
|
|
||||||
|
|
||||||
let rgbxIndex = 0, dataIndex = 0;
|
|
||||||
let left = new Uint8Array(3);
|
|
||||||
for (let x = 0; x < width; x++) {
|
|
||||||
for (let c = 0; c < 3; c++) {
|
|
||||||
const prediction = left[c];
|
|
||||||
const value = data[dataIndex++] + prediction;
|
|
||||||
rgbx[rgbxIndex++] = value;
|
|
||||||
left[c] = value;
|
|
||||||
}
|
|
||||||
rgbx[rgbxIndex++] = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
let upperIndex = 0;
|
|
||||||
let upper = new Uint8Array(3),
|
|
||||||
upperleft = new Uint8Array(3);
|
|
||||||
for (let y = 1; y < height; y++) {
|
|
||||||
left.fill(0);
|
|
||||||
upperleft.fill(0);
|
|
||||||
for (let x = 0; x < width; x++) {
|
|
||||||
for (let c = 0; c < 3; c++) {
|
|
||||||
upper[c] = rgbx[upperIndex++];
|
|
||||||
let prediction = left[c] + upper[c] - upperleft[c];
|
|
||||||
if (prediction < 0) {
|
|
||||||
prediction = 0;
|
|
||||||
} else if (prediction > 255) {
|
|
||||||
prediction = 255;
|
|
||||||
}
|
|
||||||
const value = data[dataIndex++] + prediction;
|
|
||||||
rgbx[rgbxIndex++] = value;
|
|
||||||
upperleft[c] = upper[c];
|
|
||||||
left[c] = value;
|
|
||||||
}
|
|
||||||
rgbx[rgbxIndex++] = 255;
|
|
||||||
upperIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
display.blitImage(x, y, width, height, rgbx, 0, false);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_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, false);
|
|
||||||
this._len = 0;
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
_getScratchBuffer(size) {
|
|
||||||
if (!this._scratchBuffer || (this._scratchBuffer.length < size)) {
|
|
||||||
this._scratchBuffer = new Uint8Array(size);
|
|
||||||
}
|
|
||||||
return this._scratchBuffer;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
/*
|
|
||||||
* noVNC: HTML5 VNC client
|
|
||||||
* Copyright (C) 2024 The noVNC authors
|
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
|
||||||
*
|
|
||||||
* See README.md for usage and integration instructions.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Inflator from "../inflator.js";
|
|
||||||
|
|
||||||
export default class ZlibDecoder {
|
|
||||||
constructor() {
|
|
||||||
this._zlib = new Inflator();
|
|
||||||
this._length = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
decodeRect(x, y, width, height, sock, display, depth) {
|
|
||||||
if ((width === 0) || (height === 0)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._length === 0) {
|
|
||||||
if (sock.rQwait("ZLIB", 4)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._length = sock.rQshift32();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sock.rQwait("ZLIB", this._length)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = new Uint8Array(sock.rQshiftBytes(this._length, false));
|
|
||||||
this._length = 0;
|
|
||||||
|
|
||||||
this._zlib.setInput(data);
|
|
||||||
data = this._zlib.inflate(width * height * 4);
|
|
||||||
this._zlib.setInput(null);
|
|
||||||
|
|
||||||
// Max sure the image is fully opaque
|
|
||||||
for (let i = 0; i < width * height; i++) {
|
|
||||||
data[i * 4 + 3] = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
display.blitImage(x, y, width, height, data, 0);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,185 +0,0 @@
|
||||||
/*
|
|
||||||
* noVNC: HTML5 VNC client
|
|
||||||
* Copyright (C) 2021 The noVNC authors
|
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
|
||||||
*
|
|
||||||
* See README.md for usage and integration instructions.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Inflate from "../inflator.js";
|
|
||||||
|
|
||||||
const ZRLE_TILE_WIDTH = 64;
|
|
||||||
const ZRLE_TILE_HEIGHT = 64;
|
|
||||||
|
|
||||||
export default class ZRLEDecoder {
|
|
||||||
constructor() {
|
|
||||||
this._length = 0;
|
|
||||||
this._inflator = new Inflate();
|
|
||||||
|
|
||||||
this._pixelBuffer = new Uint8Array(ZRLE_TILE_WIDTH * ZRLE_TILE_HEIGHT * 4);
|
|
||||||
this._tileBuffer = new Uint8Array(ZRLE_TILE_WIDTH * ZRLE_TILE_HEIGHT * 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
decodeRect(x, y, width, height, sock, display, depth) {
|
|
||||||
if (this._length === 0) {
|
|
||||||
if (sock.rQwait("ZLib data length", 4)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this._length = sock.rQshift32();
|
|
||||||
}
|
|
||||||
if (sock.rQwait("Zlib data", this._length)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = sock.rQshiftBytes(this._length, false);
|
|
||||||
|
|
||||||
this._inflator.setInput(data);
|
|
||||||
|
|
||||||
for (let ty = y; ty < y + height; ty += ZRLE_TILE_HEIGHT) {
|
|
||||||
let th = Math.min(ZRLE_TILE_HEIGHT, y + height - ty);
|
|
||||||
|
|
||||||
for (let tx = x; tx < x + width; tx += ZRLE_TILE_WIDTH) {
|
|
||||||
let tw = Math.min(ZRLE_TILE_WIDTH, x + width - tx);
|
|
||||||
|
|
||||||
const tileSize = tw * th;
|
|
||||||
const subencoding = this._inflator.inflate(1)[0];
|
|
||||||
if (subencoding === 0) {
|
|
||||||
// raw data
|
|
||||||
const data = this._readPixels(tileSize);
|
|
||||||
display.blitImage(tx, ty, tw, th, data, 0, false);
|
|
||||||
} else if (subencoding === 1) {
|
|
||||||
// solid
|
|
||||||
const background = this._readPixels(1);
|
|
||||||
display.fillRect(tx, ty, tw, th, [background[0], background[1], background[2]]);
|
|
||||||
} else if (subencoding >= 2 && subencoding <= 16) {
|
|
||||||
const data = this._decodePaletteTile(subencoding, tileSize, tw, th);
|
|
||||||
display.blitImage(tx, ty, tw, th, data, 0, false);
|
|
||||||
} else if (subencoding === 128) {
|
|
||||||
const data = this._decodeRLETile(tileSize);
|
|
||||||
display.blitImage(tx, ty, tw, th, data, 0, false);
|
|
||||||
} else if (subencoding >= 130 && subencoding <= 255) {
|
|
||||||
const data = this._decodeRLEPaletteTile(subencoding - 128, tileSize);
|
|
||||||
display.blitImage(tx, ty, tw, th, data, 0, false);
|
|
||||||
} else {
|
|
||||||
throw new Error('Unknown subencoding: ' + subencoding);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._length = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_getBitsPerPixelInPalette(paletteSize) {
|
|
||||||
if (paletteSize <= 2) {
|
|
||||||
return 1;
|
|
||||||
} else if (paletteSize <= 4) {
|
|
||||||
return 2;
|
|
||||||
} else if (paletteSize <= 16) {
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_readPixels(pixels) {
|
|
||||||
let data = this._pixelBuffer;
|
|
||||||
const buffer = this._inflator.inflate(3*pixels);
|
|
||||||
for (let i = 0, j = 0; i < pixels*4; i += 4, j += 3) {
|
|
||||||
data[i] = buffer[j];
|
|
||||||
data[i + 1] = buffer[j + 1];
|
|
||||||
data[i + 2] = buffer[j + 2];
|
|
||||||
data[i + 3] = 255; // Add the Alpha
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
_decodePaletteTile(paletteSize, tileSize, tilew, tileh) {
|
|
||||||
const data = this._tileBuffer;
|
|
||||||
const palette = this._readPixels(paletteSize);
|
|
||||||
const bitsPerPixel = this._getBitsPerPixelInPalette(paletteSize);
|
|
||||||
const mask = (1 << bitsPerPixel) - 1;
|
|
||||||
|
|
||||||
let offset = 0;
|
|
||||||
let encoded = this._inflator.inflate(1)[0];
|
|
||||||
|
|
||||||
for (let y=0; y<tileh; y++) {
|
|
||||||
let shift = 8-bitsPerPixel;
|
|
||||||
for (let x=0; x<tilew; x++) {
|
|
||||||
if (shift<0) {
|
|
||||||
shift=8-bitsPerPixel;
|
|
||||||
encoded = this._inflator.inflate(1)[0];
|
|
||||||
}
|
|
||||||
let indexInPalette = (encoded>>shift) & mask;
|
|
||||||
|
|
||||||
data[offset] = palette[indexInPalette * 4];
|
|
||||||
data[offset + 1] = palette[indexInPalette * 4 + 1];
|
|
||||||
data[offset + 2] = palette[indexInPalette * 4 + 2];
|
|
||||||
data[offset + 3] = palette[indexInPalette * 4 + 3];
|
|
||||||
offset += 4;
|
|
||||||
shift-=bitsPerPixel;
|
|
||||||
}
|
|
||||||
if (shift<8-bitsPerPixel && y<tileh-1) {
|
|
||||||
encoded = this._inflator.inflate(1)[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
_decodeRLETile(tileSize) {
|
|
||||||
const data = this._tileBuffer;
|
|
||||||
let i = 0;
|
|
||||||
while (i < tileSize) {
|
|
||||||
const pixel = this._readPixels(1);
|
|
||||||
const length = this._readRLELength();
|
|
||||||
for (let j = 0; j < length; j++) {
|
|
||||||
data[i * 4] = pixel[0];
|
|
||||||
data[i * 4 + 1] = pixel[1];
|
|
||||||
data[i * 4 + 2] = pixel[2];
|
|
||||||
data[i * 4 + 3] = pixel[3];
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
_decodeRLEPaletteTile(paletteSize, tileSize) {
|
|
||||||
const data = this._tileBuffer;
|
|
||||||
|
|
||||||
// palette
|
|
||||||
const palette = this._readPixels(paletteSize);
|
|
||||||
|
|
||||||
let offset = 0;
|
|
||||||
while (offset < tileSize) {
|
|
||||||
let indexInPalette = this._inflator.inflate(1)[0];
|
|
||||||
let length = 1;
|
|
||||||
if (indexInPalette >= 128) {
|
|
||||||
indexInPalette -= 128;
|
|
||||||
length = this._readRLELength();
|
|
||||||
}
|
|
||||||
if (indexInPalette > paletteSize) {
|
|
||||||
throw new Error('Too big index in palette: ' + indexInPalette + ', palette size: ' + paletteSize);
|
|
||||||
}
|
|
||||||
if (offset + length > tileSize) {
|
|
||||||
throw new Error('Too big rle length in palette mode: ' + length + ', allowed length is: ' + (tileSize - offset));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let j = 0; j < length; j++) {
|
|
||||||
data[offset * 4] = palette[indexInPalette * 4];
|
|
||||||
data[offset * 4 + 1] = palette[indexInPalette * 4 + 1];
|
|
||||||
data[offset * 4 + 2] = palette[indexInPalette * 4 + 2];
|
|
||||||
data[offset * 4 + 3] = palette[indexInPalette * 4 + 3];
|
|
||||||
offset++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
_readRLELength() {
|
|
||||||
let length = 0;
|
|
||||||
let current = 0;
|
|
||||||
do {
|
|
||||||
current = this._inflator.inflate(1)[0];
|
|
||||||
length += current;
|
|
||||||
} while (current === 255);
|
|
||||||
return length + 1;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,84 +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, Z_DEFAULT_COMPRESSION } 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);
|
|
||||||
|
|
||||||
deflateInit(this.strm, Z_DEFAULT_COMPRESSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,276 @@
|
||||||
|
/*
|
||||||
|
* Ported from Flashlight VNC ActionScript implementation:
|
||||||
|
* http://www.wizhelp.com/flashlight-vnc/
|
||||||
|
*
|
||||||
|
* Full attribution follows:
|
||||||
|
*
|
||||||
|
* -------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* This DES class has been extracted from package Acme.Crypto for use in VNC.
|
||||||
|
* The unnecessary odd parity code has been removed.
|
||||||
|
*
|
||||||
|
* These changes are:
|
||||||
|
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
*
|
||||||
|
|
||||||
|
* DesCipher - the DES encryption method
|
||||||
|
*
|
||||||
|
* The meat of this code is by Dave Zimmerman <dzimm@widget.com>, and is:
|
||||||
|
*
|
||||||
|
* Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software
|
||||||
|
* and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and
|
||||||
|
* without fee is hereby granted, provided that this copyright notice is kept
|
||||||
|
* intact.
|
||||||
|
*
|
||||||
|
* WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY
|
||||||
|
* OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE LIABLE
|
||||||
|
* FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
|
||||||
|
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
|
||||||
|
* CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
|
||||||
|
* PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
|
||||||
|
* NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
|
||||||
|
* SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
|
||||||
|
* SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
|
||||||
|
* PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). WIDGET WORKSHOP
|
||||||
|
* SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
|
||||||
|
* HIGH RISK ACTIVITIES.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* The rest is:
|
||||||
|
*
|
||||||
|
* Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* Visit the ACME Labs Java page for up-to-date versions of this and other
|
||||||
|
* fine Java utilities: http://www.acme.com/java/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* jslint white: false */
|
||||||
|
|
||||||
|
/* [module] export default */ function DES(passwd) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// 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 = [];
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// Set the key.
|
||||||
|
function setKeys(keyBlock) {
|
||||||
|
var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [],
|
||||||
|
raw0, raw1, rawi, KnLi;
|
||||||
|
|
||||||
|
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
|
||||||
|
m = l & 0x7;
|
||||||
|
pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 16; ++i) {
|
||||||
|
m = i << 1;
|
||||||
|
n = m + 1;
|
||||||
|
kn[m] = kn[n] = 0;
|
||||||
|
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 (j = 0; j < 24; ++j) {
|
||||||
|
if (pcr[PC2[j]] !== 0) {
|
||||||
|
kn[m] |= 1 << (23 - j);
|
||||||
|
}
|
||||||
|
if (pcr[PC2[j + 24]] !== 0) {
|
||||||
|
kn[n] |= 1 << (23 - j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cookey
|
||||||
|
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;
|
||||||
|
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
|
||||||
|
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++];
|
||||||
|
r = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
|
||||||
|
|
||||||
|
x = ((l >>> 4) ^ r) & 0x0f0f0f0f;
|
||||||
|
r ^= x;
|
||||||
|
l ^= (x << 4);
|
||||||
|
x = ((l >>> 16) ^ r) & 0x0000ffff;
|
||||||
|
r ^= x;
|
||||||
|
l ^= (x << 16);
|
||||||
|
x = ((r >>> 2) ^ l) & 0x33333333;
|
||||||
|
l ^= x;
|
||||||
|
r ^= (x << 2);
|
||||||
|
x = ((r >>> 8) ^ l) & 0x00ff00ff;
|
||||||
|
l ^= x;
|
||||||
|
r ^= (x << 8);
|
||||||
|
r = (r << 1) | ((r >>> 31) & 1);
|
||||||
|
x = (l ^ r) & 0xaaaaaaaa;
|
||||||
|
l ^= x;
|
||||||
|
r ^= x;
|
||||||
|
l = (l << 1) | ((l >>> 31) & 1);
|
||||||
|
|
||||||
|
for (i = 0; i < 8; ++i) {
|
||||||
|
x = (r << 28) | (r >>> 4);
|
||||||
|
x ^= keys[keysi++];
|
||||||
|
fval = SP7[x & 0x3f];
|
||||||
|
fval |= SP5[(x >>> 8) & 0x3f];
|
||||||
|
fval |= SP3[(x >>> 16) & 0x3f];
|
||||||
|
fval |= SP1[(x >>> 24) & 0x3f];
|
||||||
|
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 ^= keys[keysi++];
|
||||||
|
fval = SP7[x & 0x3f];
|
||||||
|
fval |= SP5[(x >>> 8) & 0x3f];
|
||||||
|
fval |= SP3[(x >>> 16) & 0x3f];
|
||||||
|
fval |= SP1[(x >>> 24) & 0x3f];
|
||||||
|
x = l ^ keys[keysi++];
|
||||||
|
fval |= SP8[x & 0x0000003f];
|
||||||
|
fval |= SP6[(x >>> 8) & 0x3f];
|
||||||
|
fval |= SP4[(x >>> 16) & 0x3f];
|
||||||
|
fval |= SP2[(x >>> 24) & 0x3f];
|
||||||
|
r ^= fval;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = (r << 31) | (r >>> 1);
|
||||||
|
x = (l ^ r) & 0xaaaaaaaa;
|
||||||
|
l ^= x;
|
||||||
|
r ^= x;
|
||||||
|
l = (l << 31) | (l >>> 1);
|
||||||
|
x = ((l >>> 8) ^ r) & 0x00ff00ff;
|
||||||
|
r ^= x;
|
||||||
|
l ^= (x << 8);
|
||||||
|
x = ((l >>> 2) ^ r) & 0x33333333;
|
||||||
|
r ^= x;
|
||||||
|
l ^= (x << 2);
|
||||||
|
x = ((r >>> 16) ^ l) & 0x0000ffff;
|
||||||
|
l ^= x;
|
||||||
|
r ^= (x << 16);
|
||||||
|
x = ((r >>> 4) ^ l) & 0x0f0f0f0f;
|
||||||
|
l ^= x;
|
||||||
|
r ^= (x << 4);
|
||||||
|
|
||||||
|
// Spread ints to bytes
|
||||||
|
x = [r, l];
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
b[i] = (x[i>>>2] >>> (8 * (3 - (i % 4)))) % 256;
|
||||||
|
if (b[i] < 0) { b[i] += 256; } // unsigned
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt 16 bytes of text using passwd as key
|
||||||
|
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
|
799
core/display.js
|
@ -1,54 +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,
|
|
||||||
encodingZlib: 6,
|
|
||||||
encodingTight: 7,
|
|
||||||
encodingZRLE: 16,
|
|
||||||
encodingTightPNG: -260,
|
|
||||||
encodingJPEG: 21,
|
|
||||||
encodingH264: 50,
|
|
||||||
|
|
||||||
pseudoEncodingQualityLevel9: -23,
|
|
||||||
pseudoEncodingQualityLevel0: -32,
|
|
||||||
pseudoEncodingDesktopSize: -223,
|
|
||||||
pseudoEncodingLastRect: -224,
|
|
||||||
pseudoEncodingCursor: -239,
|
|
||||||
pseudoEncodingQEMUExtendedKeyEvent: -258,
|
|
||||||
pseudoEncodingQEMULedEvent: -261,
|
|
||||||
pseudoEncodingDesktopName: -307,
|
|
||||||
pseudoEncodingExtendedDesktopSize: -308,
|
|
||||||
pseudoEncodingXvp: -309,
|
|
||||||
pseudoEncodingFence: -312,
|
|
||||||
pseudoEncodingContinuousUpdates: -313,
|
|
||||||
pseudoEncodingExtendedMouseButtons: -316,
|
|
||||||
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.encodingZlib: return "Zlib";
|
|
||||||
case encodings.encodingTight: return "Tight";
|
|
||||||
case encodings.encodingZRLE: return "ZRLE";
|
|
||||||
case encodings.encodingTightPNG: return "TightPNG";
|
|
||||||
case encodings.encodingJPEG: return "JPEG";
|
|
||||||
case encodings.encodingH264: return "H.264";
|
|
||||||
default: return "[unknown encoding " + num + "]";
|
|
||||||
}
|
|
||||||
}
|
|
2468
core/inflator.js
|
@ -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 };
|
|
@ -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)
|
||||||
|
]);
|
||||||
|
})();
|
|
@ -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;
|
|
|
@ -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',
|
|
||||||
};
|
|