502 Commits

Author SHA1 Message Date
Samuel Mannehed 0f4a06ffcd noVNC 1.3.0 beta 2021-09-08 16:06:42 +02:00
Samuel Mannehed f69d55c02f Fix parsing of query string variables
This space that was added here was added to the parsed value of the
query variable. This broke any comparisons with the value, for example
"myvar=true" resulted in a value of "true ".

This was broken by f796b05e42

The commit also adds unit tests for webutil.getConfigVar() that will
detect problems like this in the future.
2021-09-08 15:37:42 +02:00
Samuel Mannehed 7841037618 Merge pull request #1365 from baleeds/feature/detect-parent-resize
feature: Detect parent resize
2021-09-03 17:51:18 +02:00
Samuel Mannehed 1afa18f09e Increase browser version requirements
Now that we use ResizeObserver we know that we require more modern
browsers. The most notable ones here are Firefox and Safari.

With regards to Firefox, while the desktop version has had support
since 69, the Android app requires 79. At the time of writing the
current ESR of Firefox is 78, but the concept of ESR doesn't seem to
exist for Android.

The Safari 13 requirement means we no longer support for example iPhone
5S or the 4th generation of the iPad. These are devices from 2013~2014.
2021-09-03 16:52:20 +02:00
Samuel Mannehed 375f36c575 Modify unit tests to work with ResizeObserver 2021-09-03 16:52:20 +02:00
Benjamin Leeds a9c2ff30b6 Replace window.onresize with ResizeObserver
Fixes an issue where if the screen div resizes for a reason other than
window resize, the canvas wouldn't redraw.
2021-09-01 23:45:55 +02:00
Samuel Mannehed fcb95821b7 Merge pull request #1573 from yatru/security-privacy-url-patch
Security privacy to url parameters
2021-09-01 16:00:19 +02:00
yatru f796b05e42 Add support for URL fragment parameters
Passing parameters as part of the fragment could be considered
benifical from a security or privacy standpoint when compared to query
string parameters. The URL fragment parameters are not sent to the
server.
2021-09-01 14:49:37 +02:00
Samuel Mannehed 0a8ced2cfe Update Swedish translation 2021-08-27 16:12:02 +02:00
Samuel Mannehed 7a76fbb767 Remove duplicate translation string from es.po 2021-08-27 16:04:27 +02:00
Samuel Mannehed cb56f35fab Update the translation template file for v1.3.0 2021-08-27 16:04:27 +02:00
Pierre Ossman 7485e82b72 Update playback test to use new API
Hooking in to the underlying WebSocket after it has been created no
longer works, so clean things up and use the new method of passing an
existing object to the RFB constructor.
2021-07-22 16:56:49 +02:00
Samuel Mannehed d44ddbe186 Merge pull request #1449 from JanZerebecki/manpage
add Man page for launch.sh and rename to novnc_proxy
2021-06-30 16:04:01 +02:00
Jan Zerebecki 89e206c146 add Man page and rename launch.sh to novnc_proxy.
Co-Authored-By: Adam Young <ayoung@redhat.com>
2021-06-25 13:54:04 +02:00
Jose (Ito) Matsuda dd20b17d49 feat: add French localization strings 2021-05-06 22:35:18 -04:00
Samuel Mannehed 8ab18125f9 Merge pull request #1545 from ascillato/patch-2
Update Spanish Translation
2021-04-25 01:20:02 +02:00
Adrian Scillato e283f08d04 Update Spanish Translation
Update Spanish Translation.

Added missing translations. Fixed some typos.
Also, some changes were made to use a more formal and international spanish.
2021-04-23 12:10:53 -03:00
Pierre Ossman dbd519558c Initiate connection from RFB constructor
We need to do this in order to safely attach to existing WebSocket
objects. There may be message events already pending so we must set up
our event handlers before returning.

This means we will now throw errors instead of generating "disconnect"
events on problems as the caller no longer has the opportunity to set up
event handlers.

This might have been the correct approach from the start as it mimics
how e.g. the WebSocket constructor works.
2021-04-18 14:27:57 +02:00
Pierre Ossman de9fc9508c Don't fake open events in Websock
We don't know if the caller is prepared to receive those events right
now as normally they would get them on a fresh new stack later. We also
can't delay delivery since then we might deliver the event after any
pending "message" events.

Better to push the problem one layer up to the caller, which now needs
to be more aware of the state of the WebSocket object it is trying to
use.
2021-04-18 14:26:30 +02:00
Pierre Ossman 9376191fc4 Refuse to use already closed WebSocket objects
We can't do anything useful with them anyway.
2021-04-18 14:26:28 +02:00
Pierre Ossman b7b7e4e26b Provide readyState for Websock objects
It mainly reports the state of the underlying object in consistent
manner.
2021-04-18 14:26:05 +02:00
Pierre Ossman 2244f53774 Move Websock event handlers to own methods
Avoid cluttering up the RFB constructor.
2021-04-18 14:26:02 +02:00
Pierre Ossman 42100e8233 Add unit tests for connect/attach errors 2021-04-18 14:25:59 +02:00
Pierre Ossman ae3c01f782 Add unit tests for passing WebSocket objects 2021-04-18 14:25:03 +02:00
Pierre Ossman f0e4908dec Stop explicitly testing connection states
These are internal and we should be testing the externally observable
behaviour.
2021-04-18 14:24:05 +02:00
Pierre Ossman ae5f3f6909 Revert "Fixed a race condition when attaching to an existing socket"
This reverts commit ef27628c6d. By
bypassing setTimeout() it creates other race conditions so this is not
the proper fix for the issue.
2021-04-16 13:28:47 +02:00
Samuel Mannehed 84f102d6a9 Merge pull request #1537 from TimSBSquare/bugfix/existing-channel-race-condition
Fixed a race condition when attaching to an existing socket
2021-03-30 15:59:37 +02:00
Tim Stableford ef27628c6d Fixed a race condition when attaching to an existing socket
This is an error that presents itself with RTCDataChannel's, I suspect this could not
happen with a pre-existing WebSocket.

If the remote connection creates a data channel then the local (VNC) side gets a channel
created callback. It may also be the case that in that very same tick the socket is also
opened and buffered data received. This meant that (in my tests) about 1/3 of the time
noVNC would fail to respond to the initial message from the server because it was received
and subsequently not handled during that initial tick.

Also made the documentation reflect this new behaviour and document the existing behaviour.
2021-03-30 14:32:04 +01:00
Liddack 89f9ac0016 Add Portuguese (Brazil) translation 2021-03-16 13:30:23 +01:00
lhchavez f9a8c4ccd5 Add VeNCrypt Plain authentication tests
This change adds tests for the VeNCrypt Plain authentication. In doing
that, this also fixes a typo that was introduced in a recent change.
2021-03-13 05:40:38 -08:00
Pierre Ossman 9ca337d3a8 Merge branch 'homogenize-credentials-testing' of https://github.com/lhchavez/noVNC 2021-03-11 16:47:35 +01:00
Pierre Ossman 4c96d4b7bd Merge branch 'feature/support-existing-rtcdatachannel-or-websocket-squashed' of https://github.com/TimSBSquare/noVNC 2021-03-11 16:30:22 +01:00
lhchavez 0c55c64757 Normalize the credentials presence check
Most places that check for the presence / absence of credentials compare
them against `undefined`, except the one for Plain authentication.

This change makes the very last place to use the same pattern (instead
of checking for falsiness) for consistency. Additionally, there are ways
to configure PAM to accept empty passwords, so it's possible for a user
to legitimately send an empty string as password.
2021-03-11 06:42:11 -08:00
Tim Stableford 44d384b99c Added support for RTCDataChannel
This work is originally by Ryan Castner <castner.rr@gmail.com> and
submitted as a PR here https://github.com/novnc/noVNC/pull/1362

Architecturally it is much the same except it doesn't rename a lot
of variables to make this more reviewable. It also avoids unrelated
changes such as replacing .onclose with an event listener, which
caused numerous test failures.

It also adds in ppoffice's fix to initialise the buffers.

Like the original author I don't have enough time available to
refactor this project to the new style event listeners.

Review cleanup for RTCDataChannel support (see below)

* More descriptive error when url or channel not set.
* Moved websocket property check to WebSock.
  This had unintended consequences in the tests that required some
  fixup. Mostly due to some tests not always passing FakeWebsocket.
  FakeWebsocket also needs to set the listeners to null to be compatible
  with what is in thw browser and expected by the property check code.
  The property check code now also takes into account class prototypes
  for test compatibility.
* Removed unreachable code.
* Reverted comment.
* Cleanup raw channel reference in rfb on websock close.
* Use readyState to check whether a socket is open rather than assuming.
* Updated RFB constructor documentation

Removed an unused boolean passed to attach
2021-03-04 18:55:06 +00:00
lhchavez 18593154d3 Allow longer passwords in Plain authentication
Some people have longer passwords than 256 characters (hooray for
password managers!). Server implementations also allow longer passwords:
TigerVNC allows up to 1024 characters.
2021-03-03 17:34:02 -08:00
Pierre Ossman 5a0cceb815 Change phrasing for discussion group link
It's listed with the issue templates, so make sure it follows the same
style in phrasing.
2021-01-15 16:16:38 +01:00
Pierre Ossman d56e042fee Add link to discussion group from issues
So that people can easily find it and not file bug reports for
things that are just questions.
2021-01-15 16:14:40 +01:00
Pierre Ossman babd665c03 Hide link to create blank issues
We want users to use the templates so we don't miss any relevant
information.
2021-01-15 16:08:13 +01:00
NNN1590 199910e63b Update Japanese translation 2021-01-15 14:04:49 +09:00
Pierre Ossman 4a319c414d Merge branch 'more_noie' of https://github.com/CendioOssman/noVNC 2020-12-30 16:13:52 +01:00
Pierre Ossman adfb99e7ec Update conversion documentation for Node.js
We now only support conversion to CommonJS, in order to support Node.js
older than version 15.
2020-12-30 15:57:02 +01:00
Pierre Ossman 32222304f4 Remove documentation about converting the app
This is no longer possible as we now require browser support for
modules.
2020-12-30 15:56:42 +01:00
Pierre Ossman 4a8efa6bc9 Use snap actions instead of the broken container
The container didn't work properly for our base snap anyway.
2020-12-30 15:46:03 +01:00
Pierre Ossman cd9f535eb3 Package files directly in snapcraft.yaml
We don't need to convert things anymore, so reference files directly in
the snap yaml file.
2020-12-30 15:46:03 +01:00
Pierre Ossman 23249c7263 Store result from NPM and snap builds
To ease debugging.
2020-12-30 15:46:03 +01:00
Pierre Ossman 76aa3d1256 Run NPM and snap builds on every push
To make sure these things still build. That means we need to make the
actual deploy parts optional.
2020-12-30 13:40:03 +01:00
Quentin Dreyer 6784bb312f chore: sync with W3C documentation
https://www.w3.org/TR/uievents-key/
2020-12-22 10:50:23 +01:00
Pierre Ossman 67ac9f9c0d Merge branch 'jp' of https://github.com/CendioOssman/noVNC 2020-12-10 10:23:10 +01:00
Pierre Ossman 4ae9d3e75a Remove some unnecessary use of done argument 2020-12-10 10:21:21 +01:00
Pierre Ossman 0cdf2962c0 Fake key releases for some Japanese IM keys
Windows behaves very oddly for some Japanese IM keys in that it won't
send a key release event when the key is released. In some keys it never
sends the event, and in some cases it sends the release as the key is
pressed the subsequent time.
2020-12-10 10:21:21 +01:00
Pierre Ossman 146258291a Send combination keysyms for some Japanese keys
Windows doesn't give us stable symbols for a bunch of Japanese IM keys,
instead alternating between two symbols. This state is not synchronised
with the IM running on the remote server so to have stable behaviour we
have to collapse these multiple symbols in to a single keysym.
2020-12-10 10:00:44 +01:00
Pierre Ossman 3e55d5d71a Fix typo for ZenkakuHankaku key 2020-12-10 09:43:08 +01:00
Pierre Ossman bd1bb2ed75 Use toggle keysym for Eisu key
This matches how the key behaves on a Linux system.
2020-12-10 09:42:19 +01:00
Pierre Ossman dc9da4a042 Merge branch 'noie' of https://github.com/CendioOssman/noVNC 2020-12-07 14:30:05 +01:00
Pierre Ossman 6a4c411976 Remove createEvent() fallbacks
We can now rely on proper constructors for our events.
2020-12-07 10:10:53 +01:00
Pierre Ossman 27496941a0 Remove createImageData() fallback
All our browsers should be new enough now that we can rely on the
ImageData constructor.
2020-12-07 10:10:53 +01:00
Pierre Ossman 5b5b747494 Remove many small, obsolete, old browser hacks
These are for browsers no longer supported anyway.
2020-12-07 10:10:53 +01:00
Pierre Ossman 6cd9bacf8b Use Fetch API for getting JSON data
We no longer need to support Internet Explorer so we can use a more
proper API here.
2020-12-04 16:43:44 +01:00
Pierre Ossman 273acf3e89 Remove unused injectParamIfMissing()
It should have been removed in 58fc267b2b
with the caller.
2020-12-04 16:43:04 +01:00
Pierre Ossman 60c7518f8c Update keycode mappings to latest version
This update fixes Korean layouts.
2020-11-16 13:44:27 +01:00
Pierre Ossman 90456dbeed Merge branches 'logs' and 'timeout' of https://github.com/jcpunk/noVNC 2020-11-10 13:21:15 +01:00
Pat Riehecky 5dbacc5e41 Make timeouts/heartbeats easy to setup 2020-11-09 13:25:29 -06:00
Pat Riehecky 18256baad0 Permit setting syslog on websockify 2020-10-29 13:47:07 -05:00
Pierre Ossman 1f7e1c7572 Remove keypress handling
We no longer support any browser that requires this legacy handling.
2020-10-15 18:53:51 +02:00
Pierre Ossman dccf6facdc Drop support for legacy Edge 2020-10-15 18:53:51 +02:00
Pierre Ossman 890cff921d Remove legacy conversion of modules
We no longer support Internet Explorer so we can now require that
browsers support modules.

Some conversion to commonjs still remains for nodejs.
2020-10-15 18:53:51 +02:00
Pierre Ossman c01eb5e74d Drop support for Internet Explorer 2020-10-15 18:53:51 +02:00
Pierre Ossman 499eee4d06 Include current websockify in noVNC snap
The one from the system is too old to support current versions of noVNC,
so we need to bundle our own.
2020-10-08 16:55:12 +02:00
Pierre Ossman c1281b136d Add workaround for Firefox PNG rounding bug 2020-09-28 12:24:56 +02:00
Pierre Ossman 20e4f1b3f8 Remove Firefox Alt workaround
The bug got fixed way back in Firefox 63, and it is also misbehaving
with modern Firefox as they no longer consider AltGr an Alt-key.
2020-09-28 11:12:16 +02:00
Pierre Ossman b91b1e8edc Handle empty rects in RAW decoder as well
It was overlooked in the previous commit because we couldn't feed
empty data messages through the test framework.
2020-09-07 12:58:52 +02:00
Samuel Mannehed 3037eb16f7 Ignore recording variables in our linter 2020-09-05 11:08:16 +02:00
Pierre Ossman 3762300399 Approximate comparison of JPEG result
The browsers have various rounding errors so we need to compare the
output data only approximately and not exactly.
2020-09-04 16:48:44 +02:00
Pierre Ossman 113fa27ebc Handle empty rects from the server
These are very pointless for the server to send, but not a violation of
the protocol so we need to be able to handle them. We've seen this
happen in real world scenarios a few times.
2020-09-04 16:16:44 +02:00
Pierre Ossman 0630352e19 Merge branch 'rgbx' of https://github.com/CendioOssman/noVNC 2020-09-04 13:40:23 +02:00
Samuel Mannehed 7ce1b071ec Fix call to _recvMessage()
Missed to rename this one in commit ea858bfa27
2020-09-04 10:40:42 +02:00
Samuel Mannehed ecdd075672 Fix names of recording variables
The name of these variables must match how they were set when the
recording was created.

Reverts part of 95632e413d
2020-09-04 10:40:37 +02:00
Pierre Ossman 9142f8f0f7 noVNC 1.2.0 2020-07-14 10:16:52 +02:00
Pierre Ossman b053b3f86d Update generated JS files for translations 2020-07-14 10:16:40 +02:00
Samuel Mannehed 2334a7a7c3 Update Swedish translation 2020-07-14 10:16:02 +02:00
Samuel Mannehed 4fa8051949 Merge pull request #1435 from nnn1590/fix-japanese-translation
Fix Japanese translation
2020-07-08 23:25:28 +02:00
Pierre Ossman 2654bcd354 Explicitly set NPM registry URL
Otherwise the action doesn't configure npm properly to authenticate and
our publish actions will fail with 404 errors.
2020-07-07 12:49:50 +02:00
Pierre Ossman e17de291b9 noVNC 1.2.0 beta 2020-07-07 10:48:24 +02:00
Pierre Ossman 81898d7cea Remove note about clipboard ISO 8859-1 restriction
We now support full Unicode, provided the server also supports the
proper extension.
2020-07-07 10:38:06 +02:00
Pierre Ossman dcdc17bf24 Update translation template file 2020-07-03 16:11:53 +02:00
Pierre Ossman 72ca470750 Merge branch 'deploy' of https://github.com/CendioOssman/noVNC 2020-07-03 16:06:08 +02:00
Pierre Ossman ef5db94a89 Publish pre-releases to beta channels
Extra important for NPM which doesn't allow replacing a release once it
is published.
2020-07-03 16:00:04 +02:00
Samuel Mannehed 90ead240c7 Mention gestures in README 2020-06-28 23:01:23 +02:00
Pierre Ossman 244c02c5ea Deploy new snap on release 2020-06-26 14:48:32 +02:00
Pierre Ossman fe2ad57077 Move snap specific script to snap directory 2020-06-26 14:46:52 +02:00
Pierre Ossman 9b3cea950a Remove redundant VERSION file
We want to avoid having this in multiple places.
2020-06-26 13:25:25 +02:00
Pierre Ossman 794b06b2bd Don't detach handler if it doesn't exist
If the beforeEach() step has been skipped then there won't be anything
here to detach.
2020-06-25 14:45:58 +02:00
Pierre Ossman 484a9551d1 Handle quick Cursor detach after mouse up
This timer might fire after the Cursor object has detached from a DOM
element, causing crashes. This will likely not happen in real scenarios,
but the tests are quick enough to trigger this.
2020-06-25 14:37:21 +02:00
Pierre Ossman 6c6776a7a0 Update build badges for GitHub actions 2020-06-24 13:02:03 +02:00
Pierre Ossman ffb9dfdc0a Merge branch 'actions' of https://github.com/CendioOssman/noVNC 2020-06-24 12:50:31 +02:00
Pierre Ossman 2835616b75 Use GitHub actions instead of Travis/Sauce Labs
The ability to use Sauce Labs for pull requests has now stopped working,
as Travis warned about several years ago. Instead run our tests directly
on GitHub on their various virtual machines.
2020-06-23 13:56:33 +02:00
Pierre Ossman 643442feac Remove default Karma options
No need to mention things where we already use the default value.
2020-06-23 13:54:46 +02:00
Pierre Ossman 57ba67f306 Alway focus on touchstart
The new gesture detection code will always prevent the default behaviour
of touchstart, so this check no longer works properly. We might want to
add something similar to GestureHandler in the future, but let's wait
and see what use cases are requested.
2020-06-16 15:32:38 +02:00
Pierre Ossman 48f15efa69 Compensate for visual viewport when moving cursor 2020-06-16 14:24:00 +02:00
NNN1590 35de121ac5 Fix Japanese translation 2020-06-15 17:46:28 +09:00
Samuel Mannehed bb09e766ba Merge pull request #1414 from CendioOssman/gesture
Add gesture handling
2020-06-12 16:11:30 +02:00
Samuel Mannehed 32ed7c6724 Fake cursor position when using touch
With the new gestures we will simulate the cursor being in a different
location than any of the touch points. This is a bit too complex for the
Cursor class, so let's just explicitly tell it where we want the cursor
rendered.
2020-06-12 14:36:10 +02:00
Pierre Ossman 50cde2faab Move mouse event handling to RFB class
Move the last remaining bits to the RFB class to keep things simple, as
the Mouse class no longer provides any real value.
2020-06-12 14:36:10 +02:00
Pierre Ossman 88589a44f7 Increase wheel step threshold
The previous value made the detection too sensitive and it was very
difficult to scroll precisely. A value of 50 pixels should give similar
behaviour to systems that don't do fine grained scrolling.
2020-06-12 09:18:46 +02:00
Pierre Ossman f84bc57bda Move wheel event handling to RFB class
The Mouse class does very little now so it mostly just obfuscate things.
Move everything directly in to the RFB class instead.
2020-06-12 09:18:46 +02:00
Pierre Ossman 4a87038080 Remove very legacy mouse event handling
This is only needed on such ancient versions of Internet Explorer that
it wouldn't satisfy our other requirements anyway.
2020-06-12 09:18:46 +02:00
Pierre Ossman 77c32d164d Remove delayed wheel timer
This isn't really expected behaviour from a user, i.e. that an extremely
small wheel movement still gives a large scroll event in the remote application.
2020-06-12 09:18:46 +02:00
Samuel Mannehed 07a69954b1 Add lint rule for function declaration indentation 2020-06-12 09:18:46 +02:00
Pierre Ossman 8be924c9d9 Add touch gestures for mouse emulation
Add several single and multitouch gestures to simulate various mouse
actions that would otherwise be impossible to perform.

This replaces the old system where you could select which mouse button
a single touch would generate.
2020-06-12 09:18:46 +02:00
Pierre Ossman 440ec8a0b6 Start fake test clock at real clock time
Some code relies on the clock having a somewhat sane value, so let's not
start at 0.
2020-06-11 16:50:08 +02:00
Pierre Ossman 97b86abc94 Avoid fractional pixel sizes from Display 2020-06-11 16:50:08 +02:00
Pierre Ossman 0a6aec3578 Avoid printing the Websock buffer in tests
It takes forever and just messes up the output.
2020-06-11 16:50:08 +02:00
Pierre Ossman 6a19390baa Switch to RGBx pixel format
This is what the browser wants so it avoids having to spend time
converting everything. Unfortunately it usually means the server instead
needs to convert it for us, but we assume it has more power than we do.
2020-06-08 07:57:17 +02:00
Pierre Ossman f5b5767c98 Standardise on a single blit function
Keep everything simpler by always blitting in the same pixel format.
It's up to the decoders to convert if they need to.
2020-06-08 07:53:41 +02:00
Pierre Ossman 34f52a8f41 Fix bad BasicCompression check in Tight decoder 2020-06-08 07:53:41 +02:00
Pierre Ossman 18a68dfac1 Test correct handling of alpha
The forth byte of a pixel is undefined in most encodings, so make sure
the decoders don't leak that through as an alpha channel.
2020-06-08 07:53:16 +02:00
Pierre Ossman 15cfa13563 Add tests for the Tight decoders 2020-06-08 07:48:20 +02:00
Pierre Ossman 111225fa41 Split decoder tests to separate files 2020-06-08 07:46:42 +02:00
Pierre Ossman 224f95f997 Move tile handling to Hextile decoder
It is only used there so no need for it to be in the general
Display class.
2020-06-06 13:23:05 +02:00
Pierre Ossman f694c32fd5 Merge branch 'camelcase' of https://github.com/samhed/noVNC 2020-06-05 09:41:25 +02:00
Pierre Ossman 0e37a3f83a Merge branch 'limitmouse' of https://github.com/novnc/noVNC 2020-06-03 13:55:33 +02:00
Samuel Mannehed cfb824ed03 Add camelCase rule to eslint 2020-05-31 23:37:29 +02:00
Samuel Mannehed 756af5b44c Standardize on camelCase in App 2020-05-31 23:37:29 +02:00
Samuel Mannehed f2fbaacc82 Standardize on camelCase in Base64 2020-05-31 23:37:29 +02:00
Samuel Mannehed 164bf50fda Standardize on camelCase in Decoders 2020-05-31 23:37:29 +02:00
Samuel Mannehed a7fe079f81 Standardize on camelCase in Logging 2020-05-31 23:37:29 +02:00
Samuel Mannehed ea858bfa27 Standardize on camelCase in Websock 2020-05-31 23:21:35 +02:00
Samuel Mannehed 5d570207f7 Standardize on camelCase in Display 2020-05-31 23:21:35 +02:00
Samuel Mannehed 95632e413d Standardize on camelCase in tests 2020-05-31 23:21:35 +02:00
Samuel Mannehed 8b0034ee84 Standardize on camelCase in utils 2020-05-31 23:21:35 +02:00
Samuel Mannehed 80187d158c Standardize on camelCase in RFB 2020-05-31 23:21:35 +02:00
Samuel Mannehed dff4fefa3c Remove unused properties and variables
The code that used these were removed in the following commits:

* 9ff86fb718 (RFB._mouse_arr)
* bb6965f2e6 (old_requestAnimationFrame)
* 490d471c53 (Display._c_forceCanvas)
2020-05-31 22:49:41 +02:00
Samuel Mannehed 150596be83 Properly limit mouse moves to once every 17 ms
Previous attempt in c958269 had a number of issues, this is a full
rewrite, complete with improved unit tests.

Fixes github issue #1402
2020-05-31 00:53:15 +02:00
Samuel Mannehed 11a22dbf0c Stop send mouse clicks while dragging in view only 2020-05-31 00:53:15 +02:00
Samuel Mannehed 006743857b Standardize on camelCase for functions in RFB 2020-05-31 00:53:15 +02:00
Samuel Mannehed e7dec5270e Standardize on camelCase for variables in RFB 2020-05-31 00:53:15 +02:00
Samuel Mannehed b5ff33a556 Remove unused mouse_arr variable 2020-05-08 22:30:20 +02:00
Pierre Ossman 42e3b03fa8 Consistently close dialogs on connect/disconnect
This was done a bit arbitrarily before which could easily miss things,
end up in the wrong state and not trigger animations correctly.

This reverts commit c12e5b2b54 and fixes
things in a different way.
2020-05-05 12:43:04 +02:00
Samuel Mannehed 776cda5dc4 Merge pull request #1398 from novnc/compressionlevel
Add ability to set Tight compression level
2020-05-01 20:49:49 +02:00
Samuel Mannehed 479d8cefd1 Add ability to set compression level
Fixes github issue #1382.
2020-05-01 20:47:36 +02:00
Samuel Mannehed a672168d4d Add unit tests for mouse move limit 2020-05-01 20:37:48 +02:00
Samuel Mannehed 0f81407c64 Shorten rows to max 80 chars in mouse.js 2020-05-01 20:37:48 +02:00
Samuel Mannehed f477469fb5 Fix wording in comment 2020-05-01 20:37:48 +02:00
Samuel Mannehed c9582690ac Merge pull request #1352 from uklatt/master
Limit mouse move events to one every 17 mS.
2020-05-01 20:36:18 +02:00
Uwe Klatt 44eb1fe59b Limit mouse move events to one every 17 ms 2020-05-01 20:28:33 +02:00
Samuel Mannehed e7fa686f32 Fix indentation for focus check 2020-05-01 15:34:14 +02:00
Samuel Mannehed 8df281cce6 Don't fade the control bar if it has focus
Fixes github issue #1369
2020-05-01 14:38:09 +02:00
Samuel Mannehed c12e5b2b54 Hide the clipboard when not connected
Fixes github issue #1367.
2020-05-01 13:35:34 +02:00
Samuel Mannehed 302895cdf3 Merge pull request #1396 from iblech/patch-1
Document default setting of `focusOnClick`
2020-04-29 18:58:04 +02:00
Ingo Blechschmidt 27a6978e30 Document default setting of focusOnClick 2020-04-29 09:36:32 +02:00
Filip Stedronsky a1015d8db5 rfb: VeNCrypt Plain SecurityType support
This allows using TigerVNC server with PAM authentication (e.g. agains
LDAP or other extensible authentication mechanisms)

Tested with TigerVNC server (Xvnc -SecurityTypes Plain -PlainUsers '*')

Should not break anything else, this method is tried last when all
other fail.

Tested in Firefox 74 and Chromium 80
2020-04-08 08:58:32 +02:00
Alex Tanskanen a040c402ed Fix focus problem after closing the toolbar
Closing the toolbar would make the focus remain on the toolbar and
not in the session. The only way to switch focus was to click in the
session. This commit will automatically switch back focus to the session
after closing the toolbar.
2020-03-12 13:17:51 +01:00
Pierre Ossman c4633ab333 Set a default value for the quality input 2020-02-28 14:56:57 +01:00
Pierre Ossman 5243cbf611 Add UI for quality setting 2020-02-28 14:54:09 +01:00
Pierre Ossman 71429d45d0 Merge branch 'quality-level' of https://github.com/eDrillingSolutions/noVNC 2020-02-28 13:54:36 +01:00
Andrey Trebler efd1f8a4f2 adds qualityLevel property to RFB class for updating JPEG quality level encoding on the fly 2020-02-28 13:14:19 +01:00
Niko Lehto 9253e178fc Hide clipboard side bar button when view only mode
The clipboard side bar button serves no purpose if user uses 'View Only'
mode, this commit hides this button in those instances.
2020-02-24 08:57:28 +01:00
Alex Tanskanen ceb8ef4ec1 Fix crash with too large clipboard data
If too much text is copied in the session, String.fromCharCode.apply()
would crash in Safari on macOS and Chrome on Linux. This commit fixes
this issue by avoiding apply() altogether. Also added test to cover this
issue.
2020-02-21 09:39:31 +01:00
Pierre Ossman e4e6a9b9b4 Style all input types for consistent UI
At least all that the browsers will let us.
2020-02-18 15:24:51 +01:00
Pierre Ossman 384232fb56 Merge branch 'clipboard_unicode' of https://github.com/CendioNiko/noVNC 2020-02-18 09:46:10 +01:00
Niko Lehto f73fdc3ed3 Add extended clipboard Pseudo-Encoding
Add extended clipboard pseudo-encoding to allow the use of unicode
characters in the clipboard.
2020-02-18 09:32:36 +01:00
Niko Lehto 9a31083a8a Export constants in inflate.js for easier usage 2020-02-17 11:29:41 +01:00
Niko Lehto 13be552d60 Fix bug where inflate would read too much data 2020-02-17 11:29:41 +01:00
Niko Lehto 2cee106eee Split api of inflate
Added ability to read data chunk wise.
2020-02-17 11:29:41 +01:00
Niko Lehto 3cf11004b4 Handle errors from zlib/pako 2020-02-17 11:29:41 +01:00
Niko Lehto f6669ff7b2 Move error handling to Inflate class
Every call wants this check so this should be done inside the class.
2020-02-17 11:29:41 +01:00
Niko Lehto fe5aa6408a Add missing copyright header for Inflator.js 2020-02-17 11:29:41 +01:00
Niko Lehto 183cab0eca Remove unused inflate argument
The value true was an invalid flush argument so it was in practice
unused.
2020-02-17 11:29:40 +01:00
Niko Lehto 9575ded8da Add util for unsigned and signed int. conversion
Will be used in later commit in extended clipboard handling.
2020-02-17 11:29:40 +01:00
Niko Lehto f52e979082 Add deflator helper class for deflating data
Wraps pako's deflate for easier usage.
2020-02-17 11:29:29 +01:00
Niko Lehto 3b562e8a0f Make clipBoardPasteFrom() test more specific
Don't rely on clientCutText() to test clipboardPasteFrom().
2020-02-17 09:34:44 +01:00
Samuel Mannehed 4ab5070548 Merge pull request #1361 from alvintownsend/master
Correcting path to package.json for running at a path other than root.
2020-02-12 11:25:04 +01:00
Alvin Townsend 546edcd4a0 Correcting path to package.json for running at a path other than root. 2020-01-31 11:34:53 +01:00
Samuel Mannehed 71bb3fdfa5 Fix color channels for VMware alpha cursors
The red and blue channels were incorrectly swapped.
2020-01-30 11:48:17 +01:00
Pierre Ossman eb05b45b70 Make afterEach() hooks work when skipping tests
Mocha will now run afterEach() hooks when tests are skipped, so we need
to make them more robust against things being partially set up.
2020-01-23 14:27:37 +01:00
Juanjo Diaz 8394462356 Remove generated HTML by Cursor when it detaches 2020-01-23 11:58:16 +02:00
Pierre Ossman 2d53a785d5 Merge branch 'abstraction_for_detection' of https://github.com/samhed/noVNC 2020-01-14 09:45:28 +01:00
Samuel Mannehed 64fdd336a0 Simplify encodeUTF8/decodeUTF8 unittests 2020-01-03 10:41:34 +01:00
Samuel Mannehed 80c72e92d2 Add unit tests for encodeUTF8 and decodeUTF8 2020-01-02 17:29:41 +01:00
Samuel Mannehed cbf090fe70 Remove unused python scripts 2020-01-02 13:56:07 +01:00
Samuel Mannehed 274652d119 Fix chinese translation for "Disconnect"
Thanks for @wavezhang, @litongjava, and @bhzhu203 for helping out.
2020-01-02 13:24:42 +01:00
Samuel Mannehed 208e34bc34 Update chinese translation strings
Thanks to @QQ2017 and @wavezhang for helping out with correcting these.
2020-01-02 13:18:24 +01:00
Samuel Mannehed 11ae8f0ef4 Add comment for browser and platform detection 2020-01-02 11:30:34 +01:00
Samuel Mannehed c32d4f3cd0 Add short description at the top of browser.js 2020-01-02 11:30:34 +01:00
Samuel Mannehed e52a278ed7 Properly detect scrollbar gutter
As a rule, instead of hard-coding a behavior on specific platforms we
should do dynamic detection.

This commit moves away from always hiding scrollbars on Android and iOS
and instead detects the rendered width of scrollbars in the browser.
2020-01-02 11:30:18 +01:00
Samuel Mannehed 8f230f45cc Remove Google Chrome Frame plugin compatability
Chrome Frame has been retired we so we can remove it from this
compatability tag.
2019-12-31 02:15:07 +01:00
Samuel Mannehed 78bbf6bad2 Restore X-UA-Compatible meta tag to vnc_lite
It's still required since without this IE's default setting for "Display intranet web pages in compatibility mode" will cause errors.
2019-12-31 02:07:31 +01:00
Samuel Mannehed 3a64043f28 Restore X-UA-Compatible meta tag to vnc.html
It's still required since without this IE's default setting for "Display intranet web pages in compatibility mode" will cause errors.
2019-12-31 02:06:02 +01:00
Pierre Ossman 49db41ea4b Allow cursor to be updated while connecting
We haven't got a server provided cursor at this point, but we might
have done something local, e.g. enabled the dot cursor.
2019-12-30 09:30:00 +01:00
Pierre Ossman d507d1415e Make sure "undefined" can be a default parameter value
Lower layers can consider null to be a valid value, when we'd rather
they treat the value as not set.
2019-12-25 12:10:21 +01:00
Pierre Ossman c4eb4ddcfe Handle slow loading of images
Internet Explorer seems to flag images as loaded prematurely, which
can result in rendering bugs. We can detect this by looking at the
dimensions though.
2019-12-23 15:52:54 +01:00
Pierre Ossman 4babdf33bd Validate decoded image dimensions
They are expected to be a certain size, so verify this so no server
tries to do something broken.
2019-12-23 15:42:02 +01:00
Pierre Ossman b8d1a8bb57 Avoid using Array.includes()
Internet Explorer doesn't have this method. Use the safer indexOf()
instead.
2019-12-23 15:42:02 +01:00
Samuel Mannehed 2cf82a5c8e Build in the behavior to ignore decodeUTF8 errors
Makes the code clearer and more explicit in intent.
2019-12-23 10:27:40 +01:00
Pierre Ossman 06a8f7d91a Use undefined as the default value for password
An empty password is techincally legal, and now supported by the
RFB core, so we cannot use that as a placeholder for "no password".
2019-12-23 09:35:51 +01:00
Samuel Mannehed dbbb676da9 Peter is no longer part of the noVNC team 2019-12-21 00:26:57 +01:00
Samuel Mannehed 84a8c1b0cc Merge pull request #1327 from vanym/decodeUTF8_try_catch
Fixes exception when desktop name contains non-utf8 character
2019-12-07 20:12:27 +01:00
VanyM ff1b10ca66 Add try catch in every place that uses decodeUTF8 2019-12-06 09:44:11 +03:00
Chris "Koying" Browet 1c9826140a Add support for Unix Tight auth 2019-12-05 15:46:31 +01:00
Chris Koying Browet 5b453ed4a8 Expand password dialog to work for usernames too
Some VNC authentication schemes use usernames, our UI should support
these.
2019-12-05 15:46:24 +01:00
Samuel Mannehed b39caa7469 Merge pull request #1318 from chrisjdev/emptyPassword
Allow connecting with empty string credentials
2019-12-05 12:15:43 +01:00
Chris J 01d4514dee Allow connecting with empty string credentials
Checking for undefined instead of falsy. That way an empty strings are allowed.
2019-12-05 12:13:11 +01:00
Samuel Mannehed 80b078c469 Add encodeUTF8 function to core/util/strings.js 2019-12-04 10:37:11 +01:00
Pierre Ossman 9f557f5280 Make Cursor.detach() safe to call when not attached
Avoids having checks in higher layers.
2019-11-29 10:08:15 +01:00
Samuel Mannehed 94c89284fc Fix chinese translation errors
Thanks to @QQ2017 for providing the translation strings
2019-11-28 12:51:32 +01:00
Samuel Mannehed b17f6c6929 Simplify logic for status hierarchy
Removes unnecessary variable
2019-11-25 17:03:55 +01:00
Samuel Mannehed afa1f8a2ab Ensure warning status timeouts are honored
When showing a new status popup we want to set a timer for how long to
show it. In cases where we show many statuses in a fast succession we
need to remove any running timeouts when showing a new one.

There are exceptions when new statuses won't be shown, and thats if a
more severe status is already showing, i.e and error or a warning.

Warnings can still have timeouts. There was a bug that occured when we
tried to show a normal status while a warning was showing. The bug
caused the warning status timeout to be removed even if the normal
status was never shown. We should only remove running timeouts if we're
actually going to show a new status.
2019-11-25 16:38:03 +01:00
Pierre Ossman 686c8d259a Merge branch 'babel' of https://github.com/CendioOssman/noVNC 2019-11-12 14:12:18 +01:00
Pierre Ossman d01ecc18d5 Don't use arrow functions in legacy loader
The browsers that need the legacy code do not support such fancy
modern things.
2019-11-12 14:05:55 +01:00
Pierre Ossman 66ab0d98d7 Load support scripts first
E.g. SystemJS requires the Promise polyfill, so make sure all our
support files are loaded first.
2019-11-12 14:05:55 +01:00
Pierre Ossman 0dd439a874 Upgrade to latest babel
There has been a lot of renaming and restructuring in babel, so we need
to modify our code to handle the latest version. We also need to adjust
the way we build our babel worker as babel itself no longer runs in older
browsers such as Internet Explorer.
2019-11-12 14:05:55 +01:00
Samuel Mannehed ae127d8a38 Merge pull request #1319 from wavezhang/patch-2
Update zh_CN.po
2019-11-11 16:46:36 +01:00
Pierre Ossman b88a92afe8 Always include Promise polyfill for legacy browsers
It is now used by our general code and not just by the conversion
routines, so we need to make sure it is always included for the
old browsers.
2019-11-11 13:36:30 +01:00
Pierre Ossman 6b20803401 Clean up handling of untransformed files
This control flow is difficult enough as it is to follow. Move the
handling of the untransformed files to a separate block to make it
slightly easier to understand.
2019-11-11 13:33:47 +01:00
Pierre Ossman 8cfa673d94 Remove redundant "no copy" check
We also check this list as a filter to walkDir(), so no need for the
extra check here.
2019-11-11 13:32:19 +01:00
Pierre Ossman c6e37040de Use proper backticks for generating legacy script tags 2019-11-11 10:01:43 +01:00
wavezhang 9653598af7 Update zh_CN.po
fix spell errors, improve translations
2019-11-08 14:08:23 +08:00
Alex Tanskanen a6304f91d0 Fix missing caps lock events on iOS
Caps Lock on iOS only trigged key release or key press events.
When it's clicked it would only send keydown, and next time
it would only send keyup and so on. It should send both a key press
and a key release.

Also added the unit tests for macOS since those were missing.

Co-Authored-By: Alex Tanskanen <aleta@cendio.se>
2019-11-07 16:44:26 +01:00
Samuel Mannehed c15502525e Add README to app/locale warning not to modify 2019-11-07 13:07:45 +01:00
Alex Tanskanen 175b843b66 Add "macOS shuffle" to iOS as well
Since iOS functions like macOS with regards to Alt behaving like AltGr,
we need the same workaround on iOS as well.
2019-11-04 14:22:46 +01:00
Pierre Ossman ccb511a527 Handle missing Shift events on Windows
This is a bug in the OS that leaks through to the browsers. We need
to fake a Shift release here to avoid Shift getting stuck in the remote
session.
2019-11-04 10:17:45 +01:00
Pierre Ossman 3388c92c7f Send NumLock on macOS, even though the key is Clear
There is no obvious choice what works best here, but this is what
TigerVNC has been doing for years without complaints. Let's follow
them until we get reports that this doesn't work well.
2019-11-01 13:13:35 +01:00
Pierre Ossman 1096555414 Fix typo for MailSend key 2019-11-01 10:49:19 +01:00
Pierre Ossman ebee9cddbf Update to latest UI Events key specification 2019-11-01 10:25:23 +01:00
Pierre Ossman 5736ea0bd5 Fix AltGr for a few more keys in IE and Edge
Some keys apparently send 'Unidentified' rather than an unshifted value.
Make sure those are also handled. Examples are \ and | on a Swedish
keyboard.
2019-11-01 09:59:02 +01:00
Samuel Mannehed 94a01b0ae0 Keep the virtual keyboard after using extra keys
If using the extra keys always gives focus to the screen then an
on-screen keyboard would be closed. When using on-screen keyboards we
instead want to give focus to our virtual keyboard input element.
2019-10-31 16:24:06 +01:00
Pierre Ossman 8c51e9a8a2 Revert iOS keyup workaround
It seems Apple has fixed their buggy keyup events, so remove the
workaround and allow keys to be kept pressed again.

This is a revert of 9e99ce126c.
2019-10-31 15:36:40 +01:00
Pierre Ossman 9d956e9198 Handle broken numpad delete key in Chrome 2019-10-31 14:51:36 +01:00
Pierre Ossman dd4341fe67 Explain why Clear maps to KP_Begin 2019-10-31 14:12:58 +01:00
Pierre Ossman 758399050d Try to handle Meta key properly
The standards have unfortunatly caused some confusion between the Windows
key and the original Meta key. Try to handle the common case sanely at least.
2019-10-31 14:12:58 +01:00
Samuel Mannehed 1dd1bf0306 Merge pull request #1312 from samhed/master
Fix so that you can use the keyboard after using the extra keys
2019-10-28 10:45:08 +01:00
Samuel Mannehed a5aa8e1282 Move focus to the screen when using extra keys
A regression from 2afda54 and friends was that you couldn't use the
extra keys and then directly use the keyboard, you would have to click
in the session first.

This commit restores the correct behavior and also adds a visual queue
to the fact that the screen got the focus by fading the controlbar.
2019-10-28 10:37:10 +01:00
Samuel Mannehed c568ad4c74 Add missing scancode for sendTab 2019-10-28 10:02:23 +01:00
Samuel Mannehed 0c4b3e802f Rename document.capturedElem to captureElement
To better fit most naming.
2019-10-23 15:59:43 +02:00
Pierre Ossman 65066326c5 Improve Windows key image
The previous one didn't have sharp lines, or follow pixel boundaries
properly.
2019-10-23 15:51:41 +02:00
Samuel Mannehed ffdd0dfeef Merge pull request #1309 from samhed/disappearing_cursor
Fix disappearing cursor
2019-10-21 14:16:48 +02:00
Samuel Mannehed c3a7524c9e Hide the emulated cursor when target is null
Makes it easier to understand what happens when a real element isn't
passed as a target to updateVisibility(). Also makes the code more
robust to future changes.

Co-authored-by: Alex Tanskanen <aleta@cendio.se>
Co-authored-by: Niko Lehto <nikle@cendio.se>
2019-10-21 13:51:42 +02:00
Samuel Mannehed 7a96fc3785 Fix disappearing cursor after click
In the cursor emulation when deciding if the cursor should be hidden -
Instead of checking what's under the cursor, we check the element that
has capture.

This introduced another bug in the cursor emulation. The cursor did not
always disappear properly when using our cursor emulation together with
our setCapture polyfill. More specifically, we saw a problem when a
capture ended on an element without cursor emulation.

We solved this by introducing another visibility check on a timer in
the cursor emulation. However this led to yet another problem where
this timer conflicted with the timer in the setCapture polyfill.

We removed the timeout in the setCapture polyfill and created a
variable to make sure that all the events remaining in the queue can be
completed.

Co-authored-by: Alex Tanskanen <aleta@cendio.se>
Co-authored-by: Niko Lehto <nikle@cendio.se>
2019-10-21 13:51:42 +02:00
Samuel Mannehed 938690375b Check next elem at mouseleave in cursor emulation
It's not obvious that we want to hide the cursor when we get a leave,
it depends on the element that we're leaving to. This makes the code
more robust.

Co-authored-by: Alex Tanskanen <aleta@cendio.se>
Co-authored-by: Niko Lehto <nikle@cendio.se>
2019-10-21 13:10:13 +02:00
Samuel Mannehed fcd99d04fb Rename variables in setCapture proxy
The names of many variables were too similar. To make the code easier
to follow we renamed:

* _captureElem to _capturedElem
* _captureElemChanged() to _capturedElemChanged()
* captureElem to proxyElem
* elem to target

Co-authored-by: Alex Tanskanen <aleta@cendio.se>
Co-authored-by: Niko Lehto <nikle@cendio.se>
2019-10-21 13:10:09 +02:00
Pierre Ossman 2b4c655405 Fix alt text for drag button
We had left an old placeholder text on this button.
2019-10-14 10:17:44 +02:00
Pierre Ossman f2d42dc357 Never show drag icon if clipping is disabled
Toggling the enabled state is a remnant from an earlier version
of the code where we could determine if the the session is actually
clipped, and not just that the setting is enabled.

Right now we only change things based on the setting, so let's
completely hide the button when clipping is disabled.
2019-10-14 10:15:19 +02:00
Samuel Mannehed 412d93060d Update copyright to 2019 for modified files 2019-09-30 15:35:33 +02:00
Samuel Mannehed e8614e20ef Code comments for how the receieve queue works 2019-09-25 21:04:46 +02:00
Samuel Mannehed c90d53565a Clarify why we ENABLE_COPYWITHIN is false 2019-09-25 21:03:37 +02:00
Samuel Mannehed 3aeaea50af Merge pull request #1298 from CendioNiko/edgeCursorURI
Fix url cursor detection on Edge
2019-09-25 14:46:58 +02:00
Samuel Mannehed 3055307d3d Merge pull request #1299 from CendioNiko/vmwarecursor
Add support for VMware cursor encoding
2019-09-25 14:45:29 +02:00
Samuel Mannehed 8dc47f3c06 Merge pull request #1289 from CendioNiko/master
Desktop name improvements
2019-09-25 14:43:55 +02:00
Samuel Mannehed c51a77c2eb Merge pull request #1281 from jalfd/optimize-receive-buffer
Optimize receive buffer
2019-09-25 13:26:02 +02:00
Samuel Mannehed d39e0d1244 Clarify comments for broken alt in FF on Windows 2019-09-24 16:00:05 +02:00
Samuel Mannehed ebb58c34da Merge pull request #1280 from jalfd/alt-key-recursion
Avoid recursion in Alt check on Firefox
2019-09-24 15:56:13 +02:00
Samuel Mannehed 35b78e95d2 Merge pull request #1279 from CendioOssman/nomodule
Use "nomodule" instead of manual check
2019-09-24 15:40:05 +02:00
Pierre Ossman 0b51419ca4 Use "nomodule" instead of manual check
Very few browsers are left in the wild that supports modules but not
"nomodule", so let's simplify our handling a bit.

Safari 10 supports modules but not 'nomodule', this means that this
particular version of Safari will be broken. Due to this we have to
bump up the required Safari version to 11.
2019-09-24 15:34:59 +02:00
Niko Lehto 296ba51f49 Add support for VMware cursor encoding
Supports both classic cursor type and alpha cursor type. In classic
mode the server can send 'inverted' pixels for the cursor, our code
does not support this but handles these pixels as opaque black.

Co-authored-by: Samuel Mannehed <samuel@cendio.se>
2019-09-24 10:26:30 +02:00
Niko Lehto a1afb2a215 Fix url cursor detection on Edge
_supportCursorURIs was set to true even when Edge didn't support
URIs because the fallback value "default" was used.
2019-09-23 13:50:17 +02:00
Niko Lehto c90245da25 Restore page name after disconnect 2019-09-04 15:05:14 +02:00
Niko Lehto 8d6f686b59 Test unicode desktop names 2019-09-04 14:47:40 +02:00
Niko Lehto ce66b46986 Add support for DesktopName extension
This extension allows session name to be changed during runtime.
2019-09-04 14:23:37 +02:00
Pierre Ossman 9886d5951d Set viewport size for autoscale tests
We were incorrectly relying on the viewport being indirectly set
for us. Make sure we are explicit in what we want for these tests.
2019-08-23 15:48:30 +02:00
Pierre Ossman 30ff15a35a Merge branch 'upgrade' of https://github.com/CendioOssman/noVNC 2019-08-23 15:08:56 +02:00
Pierre Ossman e5255fc246 Remove pointless Display.clear()
It served no meaningful purpose and it had bugs. So let's remove it
rather than try to fix it.
2019-08-23 15:05:58 +02:00
Pierre Ossman 3855a7bee4 Remove unused Display.logo attribute 2019-08-23 15:04:23 +02:00
Pierre Ossman 6aed0b4dd2 Deprecate showDotCursor option for RFB constructor
It is not relevant for the connection stage so it should not have
been a constructor argument to begin with. Ship with a warning for
a release before we remove it.
2019-08-23 14:00:20 +02:00
Pierre Ossman 1f2bb52850 Make sure showDotCursor can be modified before connecting
The cursor object is only attached to our canvas whilst connecting,
so we need to make sure we don't try to update anything when were
not connected or we'll get a crash.
2019-08-23 13:57:30 +02:00
Pierre Ossman 4222d72bfe Regenerate module loader after tool upgrades 2019-08-19 13:32:41 +02:00
Pierre Ossman e24b501c47 Use latest versions of development dependencies
Let's make sure we get the latest features and fixes for all the
tools we are using.
2019-08-19 13:32:41 +02:00
Pierre Ossman c47a3a3e09 Use latest NodeJS version in Travis
We want the latest and not be stuck on some old version. Otherwise
our scripts might not execute correctly.
2019-08-19 13:32:41 +02:00
Pierre Ossman 90d463f969 Make sure translation tools are lint checked
They do not have a .js suffix so eslint isn't finding them
automatically.
2019-08-19 13:32:29 +02:00
Jesper Alf Dam 08567b08ac When compacting the receive buffer don't copy unused buffer space
When compacting the receive buffer, we should only copy the bytes
between _rQi and _rQlen (the index of the first unread byte, and the
next write position).

Previously, we copied everything for _rQi up untill the end of the
buffer.
2019-08-16 19:34:09 +02:00
Jesper Alf Dam 7d755d10dc Don't compact the receive buffer unless we've actually run out of space
Previously, we would compact the buffer (moving unread data to the
start of the buffer) as follows:

- after processing a message, if there are zero unread bytes, just reset
  the indices for first and last unread byte to zero
- else, if at least 1/8th of the buffer is used, copy remaining data to the beginning of the buffer

The second option is never actually necessary, as before inserting new data
into the array, we already check if there's enough free space, and
compact the buffer first if necessary. So we've been doing a lot of
copies that weren't actually needed. Let's not do that any more.
2019-08-16 19:34:09 +02:00
Jesper Alf Dam e9f489a629 Avoid recursion in Alt check on Firefox
The Firefox workaround which checks for missing Alt key events may
synthesise new KeyboardEvents. On these events, checkAlt should not be
recursively triggered. Otherwise, we get "too much recursion" errors
whenever the Alt key is pressed.
2019-08-16 16:20:41 +02:00
Pierre Ossman 776024a008 Fix trivial lint issues in translation tools
Indentation, missing semicolon, etc.
2019-08-15 15:58:09 +02:00
Pierre Ossman 604edf07d2 Upgrade to latest rollup 2019-08-15 15:58:09 +02:00
Pierre Ossman ff7882c44c Remove unused import from module loader 2019-08-15 15:58:09 +02:00
Pierre Ossman c9765e5066 Upgrade to latest sinon and chai 2019-08-15 15:58:09 +02:00
Pierre Ossman b875486db8 Avoid deprecated called.once from sinon-chai
It's been removed in newer versions and will break eventually.
2019-08-15 15:58:09 +02:00
Tim Edwards e1d50c8c10 Added a Snap package for noVNC (#1231)
Creating an Ubuntu Snap package to make noVNC easier to deploy.

Checks for the websockify binary in both the PATH (using which) and in the location where the Snap package places the binary. This is necessary for noVNC to be usable in a Snap. It doesn't affect the original functionality of git cloning websockify if it's not found in PATH or the Snap location.
2019-07-25 22:22:48 +02:00
Samuel Mannehed 897b465b87 Add missing parentheses for arrow func arg
Our lint tests expect this when an arrow functino has a body with
curly braces.
2019-07-23 16:12:22 +02:00
Samuel Mannehed e14aa4d0fe Add documentation for the option showDotCursor
This is not only a property, it's also a parameter to the constructor.
2019-07-23 16:07:17 +02:00
Samuel Mannehed c912230309 Remove the default value of wsProtocols
Using the 'binary' protocol by default is very non-standard.
2019-07-23 16:03:49 +02:00
Samuel Mannehed 21387f9c24 Merge pull request #1262 from shiramax/sub_protocols
Add support in websocket sub-protocols
2019-07-23 15:24:10 +02:00
Shira Maximov 25b3d49d32 Add support in websocket sub-protocols 2019-07-23 13:44:18 +03:00
Samuel Mannehed 8f2bcfbe79 Merge pull request #1265 from juanjoDiaz/add_version_number_to_UI_2
Add version number to UI
2019-07-23 11:51:09 +02:00
Juanjo Diaz 15c7b7a619 Add version number to UI 2019-07-23 10:25:59 +03:00
Samuel Mannehed 21ac6ca0f2 Update link to websock.js API 2019-07-15 14:05:24 +02:00
Samuel Mannehed df4b7515a3 Merge pull request #1259 from lyarwood/launch.sh
launch.sh: Check for a local websockify directory
2019-07-14 23:31:10 +02:00
Lee Yarwood 188c9a591b launch.sh: Check for a local websockify directory
Previously launch.sh would check both for the existence of a local
websockify file and /websockify/run file.

This initial check should really be for a local websockify directory
as in packaged environments a file could very well be the actual
executable leading to launch.sh incorrectly attempting to use a local
version of websockify.
2019-07-09 12:27:26 +01:00
Pierre Ossman 23af6e142a Merge branch 'add-japanese-translation' of https://github.com/nnn1590/noVNC 2019-06-13 13:59:35 +02:00
Pierre Ossman 97924ebd5d Add support for separate key file in launch script 2019-06-13 13:51:56 +02:00
nnn1590 7ded517823 Add Japanese translation 2019-06-12 04:30:56 +09:00
Samuel Mannehed 32e081950c Revert "Fullscreen from iframe (#1236)" (#1247)
This reverts commit 19cdc15aa3.
2019-05-25 02:51:38 +02:00
Pierre Ossman 755d6eae99 Remove server pixel format warnings
These are harmless and really only for debugging. So remove them
as they tend to trick people in to thinking something is wrong.
We already print the entire server pixel format earlier anyway in
case we need the details.
2019-05-24 13:06:26 +02:00
Ján Jockusch 19cdc15aa3 Fullscreen from iframe (#1236)
* First attempt to make the fullscreen button work inside an iframe.

* Cleaner distinction between document element and document.

* Scoping corrections. Auto-detect correct iframe.

* Added comments to the relevant sections.

* IE issue fixed.

* Same source issue solved. fullscreenToggle now checks if it is permitted to inspect other iframes.
2019-05-13 15:06:32 +02:00
Pierre Ossman 2b2b6073dd Don't do cleanup on deploy
Travis wipes out node_modules otherwise, which we need for the prepare
stages of the deployment to npm.
2019-04-15 16:11:01 +02:00
Samuel Mannehed 9fe2fd04d4 noVNC 1.1.0 2019-04-09 16:29:53 +02:00
Samuel Mannehed 3ba5cefef2 Update generated JS files for translations 2019-04-09 16:22:16 +02:00
Samuel Mannehed e94e83c6c8 Update Dutch translations
Co-authored-by: Arend Lapere <arend.lapere@gmail.com>
2019-04-09 16:21:35 +02:00
Samuel Mannehed d6804167ef Update Swedish translations 2019-04-09 16:21:19 +02:00
Samuel Mannehed a136b4b078 Allow autoscale() with zero height or width
Commit 6e7e6f9 stopped the function from running if width or height was
zero, this commit reverts that change. This commit also makes the
resulting canvas 0x0 if autoscale is called with zero. By adding this
special case we can avoid division by zero in the calculations.
2019-04-02 16:51:18 +02:00
Pierre Ossman 2aa3b5bc79 Get rid of self-closing tags
This is a remnant of XHTML and not used in standard HTML. Get rid
of them so that no-one mistakes our files for being XHTML compatible.
2019-04-02 14:22:34 +02:00
Pierre Ossman dcc41bde61 Fix up errors and warnings in vnc_playback.html
Remove the styling, as it isn't really needed, and fix some minor
things that the w3c validator complains about.
2019-03-26 15:21:31 +01:00
Pierre Ossman a98a223e13 Validate HTML and CSS in Travis 2019-03-26 15:19:11 +01:00
Samuel Mannehed f5d76dd5bb Add translations as a feature 2019-03-21 13:57:16 +01:00
Samuel Mannehed effd53838c Merge pull request #1218 from samhed/htmlformat
Some formatting fixes for HTML files
2019-03-14 14:44:10 +01:00
Samuel Mannehed 94e6f8c2fa Remove trailing whitespace 2019-03-14 14:17:04 +01:00
Samuel Mannehed 2500f65d01 Consistently end self closing tags with />
Even though this isn't strictly required by the standard its nice to be
consistent.
2019-03-14 14:16:40 +01:00
Samuel Mannehed 26a9c1c14d Remove invalid HTML attributes from textarea 2019-03-14 14:13:27 +01:00
Samuel Mannehed fe8d784bce img elements must have alt attributes 2019-03-14 14:13:27 +01:00
Samuel Mannehed 5a76000848 Fix invalid input type 'input' 2019-03-14 14:13:27 +01:00
Samuel Mannehed 892c3330cf Input type image is not allowed to have values 2019-03-14 14:13:27 +01:00
Samuel Mannehed 45c644a68d Remove unnecessary type attributes 2019-03-14 14:13:27 +01:00
Samuel Mannehed daff988e17 Remove X-UA-Compatible meta tag
It's only required if we wanted support IE8, IE9 or older. We require at
least IE11 at the moment.
2019-03-14 14:13:27 +01:00
Samuel Mannehed 80c52ba7cb Add default language 2019-03-14 14:13:27 +01:00
Samuel Mannehed 6e7e6f9c9e Add check for bad values for Display.autoscale() 2019-03-08 16:30:43 +01:00
Samuel Mannehed 9a823732a0 Merge pull request #1204 from juanjoDiaz/small_improvements
Small improvements
2019-03-04 09:32:11 +01:00
Juanjo Diaz 1c9b904d1a Remove callbacks from UI in favour of promises 2019-02-27 10:18:59 +02:00
Juanjo Diaz 41ddb35458 Replace unnecessary function supportsCursorURIs by a constant variable 2019-02-27 10:14:50 +02:00
Juanjo Diaz 44f4c5545f Move support check from display to browser 2019-02-27 10:13:50 +02:00
Dmitriy Shweew d917ccdaf7 Add Russian translation (#1211)
By Dmitriy Shweew (shweew)
2019-02-27 01:24:22 +01:00
Juanjo Diaz 0505214cd9 Convert DES into a class 2019-02-26 23:53:43 +02:00
Juanjo Diaz 9d2c9d1a75 Use default argument for base64 2019-02-26 23:52:47 +02:00
Juanjo Diaz 667f3cc20e Remove unnecessary context from eventtarget 2019-02-26 23:51:45 +02:00
Juanjo Diaz fe5974a740 Remove unnecessary constructor parameter from Cursor 2019-02-26 23:51:33 +02:00
Juanjo Diaz 9255e0fb47 Remove intermediate variable from mouse 2019-02-16 23:31:58 +02:00
Pierre Ossman b00a608af7 Remove error handling in clientCutText()
It is not necessary as Websock.flush() is guaranteed to succeed and
give us some space. It also remove the call to _fail(), which was
invalid at this place as clientCutText() is not a method on RFB.
2019-02-15 13:14:36 +01:00
Pierre Ossman 47c66517ae Throw correct Error object
We've already defined the name Error as a logging function, so we
need to be more explicit when we want to refer to the exception
class.
2019-02-15 13:01:28 +01:00
Pierre Ossman 9e03a98182 Merge branch 'slowdata' of https://github.com/CendioOssman/noVNC 2019-02-15 10:40:49 +01:00
Pierre Ossman 70e6795829 Send data one byte at a time in tests
This makes sure we don't have code assuming that everything is
neatly packaged in a single WebSocket message.
2019-02-15 10:26:27 +01:00
Pierre Ossman c02b18f06f Clean up RFB._rfb_auth_schema assignment 2019-02-15 10:25:50 +01:00
Pierre Ossman 3bb15d4aa0 Fix security failure reason handling of slow data
Things would break if the security result and security reason did
not arrive in the same WebSocket message.
2019-02-15 10:24:41 +01:00
Pierre Ossman c13df5ae67 Fix version handshake to handle slow data 2019-02-15 10:23:32 +01:00
Pierre Ossman b8ff5d1bde Use arrow function to avoid bind 2019-02-15 10:22:27 +01:00
Pierre Ossman 17eea9574d Consume data properly in Hextile decoder
We accidentally removed the code updating the data index in 8a189a6,
resulting in the decoder newer consuming any data. So the data would
be parsed as the next rect, causing weird errors.
2019-02-14 16:57:26 +01:00
Samuel Mannehed 36bfcb0714 Merge pull request #1190 from saucelabs/screen_background
Make the screen background customizable
2019-01-21 13:39:31 +01:00
Mykola Mokhnach d7791ebbcd Make the screen background cutomizable 2019-01-18 19:36:23 +01:00
Pierre Ossman 7dc0a67808 Fix naming of Korean translation file 2019-01-16 11:10:04 +01:00
Pierre Ossman 20de5749d2 Update translation template file 2019-01-16 11:07:56 +01:00
Pierre Ossman f50ccd80d1 Fix copyright tag in translations 2019-01-16 11:07:31 +01:00
Baw_Appie 823daa8002 Add Korean translation 2019-01-16 10:43:49 +01:00
Samuel Mannehed 099c419996 Merge pull request #1182 from novnc/scrollbarsfortouch
Enable scrollbars for all touch devices aside from Android and iOS
2019-01-14 22:29:47 +01:00
Pierre Ossman b4819c2558 Merge branch 'npm' of https://github.com/CendioOssman/noVNC 2019-01-11 13:42:31 +01:00
Pierre Ossman 69a9fd6029 Revert 'prepare' to 'prepublish'
Travis uses a so ancient version of npm that it doesn't support
'prepare', so we have to continue using 'prepublish' for now.
2019-01-11 13:39:02 +01:00
Pierre Ossman 1ced5688b5 Change to Ossman's NPM authentication
Solly's doesn't seem to work anymore, so switch to mine.
2019-01-11 13:39:02 +01:00
Pierre Ossman 364849c67b Avoid extra environment for Travis deploy stage
These are not needed, so keep things more clear by skipping them.
2019-01-11 13:39:02 +01:00
Pierre Ossman 6532b4d1b8 List main entry point for our NPM config 2019-01-11 13:39:02 +01:00
Pierre Ossman ea4065f33a Explicitly list what we want in our NPM package
Switch over to explicitly listing what we want to include, rather
than listing what we don't want to include. There is too much risk
of getting random junk from your working copy otherwise. This should
also hopefully complain if something is missing.
2019-01-11 13:39:02 +01:00
Samuel Mannehed ef64917a90 Only disable scrollbars on Android and iOS
Previously scrollbars were disabled on all touch devices. This meant
that they were disabled on Windows when touch was detected. Windows does
in fact have useful scrollbars even in touch mode. Fixes Issue #1172
2019-01-09 14:59:22 +01:00
Samuel Mannehed 47b3eac82b Move UI.isSafari into core/util/browser.js
This is where the rest of these kinds of functions are.
2019-01-09 14:58:03 +01:00
Samuel Mannehed 97e23ebbb2 Reorder browser and platform info functions
Platform info functions grouped together and browser info functions
grouped together.
2019-01-09 14:56:59 +01:00
Samuel Mannehed 77e261dba3 Merge pull request #1179 from ssebs/super-key-feature
Super key feature
2019-01-09 10:41:32 +01:00
Sebastian Safari 3e835a5d37 Add Windows Key Feature 2019-01-08 06:57:12 -08:00
Pierre Ossman 7a1f2e4cf5 Merge branch 'user_getters_and_setters_in_websock' of https://github.com/juanjoDiaz/noVNC 2019-01-08 12:26:17 +01:00
Pierre Ossman e35570227c Use CustomEvent for playback events
Stop abusing Event as that doesn't work everwhere.
2019-01-08 12:25:42 +01:00
Pierre Ossman 568f6567e1 Avoid using String.prototype.startsWith()
IE doesn't support it.
2019-01-08 12:25:01 +01:00
Pierre Ossman 527a1fd0ae Pre-convert recordings for playback
Convert the recordings ahead of time instead of during the playback.
That way we aren't messing up the profiling with time spent converting
data, rather than processing it.
2019-01-08 12:24:39 +01:00
Juanjo Diaz 879e33ab64 Extract rQshift to common function 2018-12-08 17:32:00 +02:00
Juanjo Diaz 8a189a6291 Add getters/setter to websock 2018-12-08 17:31:20 +02:00
Samuel Mannehed 18439b0680 Merge pull request #1165 from juanjoDiaz/throw_errors_instead_of_strings
Throw Error instead of String
2018-11-26 19:09:44 +01:00
Samuel Mannehed 2bab9a0460 Merge pull request #1166 from juanjoDiaz/simplify_EventTargetMixin
Simplify EventTargetMixin
2018-11-26 18:42:10 +01:00
Samuel Mannehed ae1f7a8f5c Merge pull request #1168 from juanjoDiaz/use_new_error
Use `new` when constructing errors
2018-11-26 18:36:10 +01:00
Juanjo Diaz 11ef53544f Simplify EventTargetMixin 2018-11-25 13:53:23 +02:00
Juanjo Diaz d3ed883a8f Use new when constructing errors 2018-11-24 21:44:11 +02:00
Juanjo Diaz 84a5a2d827 Throw Error instead of String 2018-11-24 19:47:57 +02:00
Samuel Mannehed cffb42ee8f Dont reset touch mouse button state for view-only
Fixes bug introduced in 61f93180c8.
2018-11-01 11:22:19 +01:00
Samuel Mannehed 7449170cc8 Fix chinese translation of "Connect"
Thanks to Lyon Hu (@chnhyg) for finding the issue.
2018-11-01 11:07:58 +01:00
Petr 56c82ecd35 Czech translation 2018-10-19 13:01:39 +02:00
Solly Ross 84586c0f17 Change copyright header (#1138)
* Change copyright header

This updates the copyright header to say "The noVNC Authors".  People
who previously had copyright listings are now under the AUTHORS file.
2018-10-09 12:15:35 +02:00
Pierre Ossman d105040581 Add tests for Cursor encoding 2018-09-18 11:36:32 +02:00
Pierre Ossman 679535ec29 Fix cursor encoding handling from earlier merge
Old code snuck in when merging the split of decoders to separate
classes. Restore the proper handling of cursors.
2018-09-18 11:08:02 +02:00
Pierre Ossman 9881899e7b Merge branch 'style' of https://github.com/CendioOssman/noVNC 2018-09-17 13:54:04 +02:00
Pierre Ossman 934c606d91 Merge branch 'decoders' of https://github.com/CendioOssman/noVNC 2018-09-17 13:47:12 +02:00
Samuel Mannehed 772c686776 Merge pull request #1119 from patrakov/master
Show dot when there otherwise would be no visible cursor
2018-09-16 11:20:34 +02:00
Alexander E. Patrakov 4c38179d15 Show dot when there is no visible cursor
Disabled by default.
2018-09-14 20:31:59 +05:00
Alexander E. Patrakov d1314d4b3a Moved the "pixels + mask -> RGBA" logic to rfb.js
As requested by Pierre Ossman - he needs this for supporting other
cursor extensions.
2018-09-07 23:01:46 +08:00
Pierre Ossman 0997b319a3 Enforce switch colon spacing 2018-09-06 17:36:48 +02:00
Pierre Ossman 2c5491e131 Enforce space after function name 2018-09-06 17:34:15 +02:00
Pierre Ossman 3f1cda2e37 Enforce space before code block 2018-09-06 17:29:26 +02:00
Pierre Ossman 0ae5c54ab3 Enforce explicit semi-colons 2018-09-06 17:25:02 +02:00
Pierre Ossman 426a8c927b Enforce curly braces for control statements 2018-09-06 17:22:40 +02:00
Pierre Ossman 4a16dc51a8 Enforce no trailing whitespace 2018-09-06 17:12:45 +02:00
Pierre Ossman 35068204f4 Enforce keyword spacing 2018-09-06 17:08:19 +02:00
Pierre Ossman 942a312779 Enforce object key spacing 2018-09-06 17:07:11 +02:00
Pierre Ossman 22d10c756a Enforce function declaration style 2018-09-06 17:01:43 +02:00
Pierre Ossman e777765320 Enforce function names 2018-09-06 16:53:40 +02:00
Pierre Ossman d80d9d3731 Enforce function call spacing 2018-09-06 16:46:38 +02:00
Pierre Ossman f77f41ee95 Enforce comma style 2018-09-06 16:44:53 +02:00
Pierre Ossman 6786fd8719 Enforce comma spacing 2018-09-06 16:43:31 +02:00
Pierre Ossman 7b536961b2 Enforce indentation 2018-09-06 16:37:38 +02:00
Pierre Ossman 1404984668 Don't lint xtscancodes.js
It's generated by another project so we cannot control the style
of it.
2018-09-06 16:37:11 +02:00
Pierre Ossman a98881151f Enforce brace style 2018-09-06 15:39:26 +02:00
Samuel Mannehed e15950a8ef Merge pull request #1129 from novnc/vnclitecleanup
Cleanup of vnc_lite.html
2018-08-28 10:46:11 +02:00
Samuel Mannehed e20f0ee9b6 Limit line length to 80 in vnc_lite 2018-08-28 10:42:39 +02:00
Samuel Mannehed 1c945f812b Add a screen element for vnc_lite.html
Makes it clearer where the remote screen will be created.
2018-08-28 10:42:39 +02:00
Samuel Mannehed 8613f6f4ae Simplify element names in vnc_lite 2018-08-28 10:42:39 +02:00
Samuel Mannehed 011e4bff34 Create our own button for CtrlAltDel in vnc_lite
In order to have better control of the layout and to make the code
easier to follow. Using input type button or buttons will imply a lot of
built in styling that differs from browser to browser.
2018-08-28 10:42:39 +02:00
Samuel Mannehed 5271e30049 Use a simple prompt for passwords in vnc_lite
It's not password-masked, but it allows for a lot simpler code.
2018-08-28 10:42:39 +02:00
Samuel Mannehed 26d51e490e Order vnc_lite functions 2018-08-28 10:42:39 +02:00
Samuel Mannehed c756665e81 Rename functions in vnc_lite
Give them obvious names to make the code easier to understand.
2018-08-28 10:42:39 +02:00
Samuel Mannehed 25551b6b40 Use let and const instead of var in vnc_lite
The rest of noVNC has been converted already. This allows us to remove
the extra scope that was created for the VNC connection.
2018-08-28 10:42:37 +02:00
Samuel Mannehed 6517c498b9 Remove support for the fragment and WebUtil dep
The only remaining use we had of WebUtil was getConfigVar(). Let's get
rid of that dependency and use our own, query-string-only and richly
commented version of that function. It's easier for people to get an
overview of vnc_lite if it's all in one file.

This commit removes support for the fragment, parameters can only be
passed using the query string from now on.
2018-08-28 10:41:56 +02:00
Samuel Mannehed 51f9f0098d Cleanup non-essential options from vnc_lite
This is supposed to be a simple example, it shouldn't have this many
options. This commit removes the following options:
 * logging - the default level 'warn' is good enough
 * title - a weird thing to set from the query string anyway
 * token - not used by most setups
 * encrypt - looking at the URL is good enough
 * repeaterID - not used by most setups
 * shared - uncommon setting
 * resize - not supported by most servers

Note that the removal of 'encrypt' allows us to remove logic for
establishing a default port. The default port for wss is 443 and for ws
it's 80 anyway.
2018-08-28 10:41:52 +02:00
Samuel Mannehed 8c2866df36 Add code comments to vnc_lite 2018-08-28 10:41:19 +02:00
Pierre Ossman 923cd22083 Move decoders to separate classes
Makes things a lot clearer by letting each encoding handle its own
details and state.
2018-08-22 15:12:34 +02:00
Pierre Ossman 11309f3243 Handle pseudo encodings directly
These have very special behaviour compared to normal data encodings,
so separate out them and handle them separately.
2018-08-22 15:12:10 +02:00
Pierre Ossman e17cae8f32 Remove statistics tracking
The profiles in the browsers are much better these days and give us
much better data than we can provide ourselves.
2018-08-22 15:12:05 +02:00
Pierre Ossman 71960cda85 Give proper int argument to encodingName() 2018-08-22 14:46:37 +02:00
Samuel Mannehed 27dff4a0a2 Simplify connected() function 2018-08-21 12:23:17 +02:00
Samuel Mannehed de79ae92e5 Remove unneccessary code and bling from vnc_lite
Unused code, variables and unnecessary styles. The host/port check
would only have an effect if someone explicitly set them as empty in
the query string. The different colors of the status bar are not
necessary, nor is the styling of the background.
2018-08-21 12:22:57 +02:00
Samuel Mannehed e0d4e5a1c0 Move css rules for vnc_lite to the html
Easier to get an overview if it's all in one file.
2018-08-21 11:34:28 +02:00
Samuel Mannehed e7c1074b65 Get rid of icons for vnc_lite
Icons aren't required and we want to get rid of the 'app/' dependency.
2018-08-20 09:22:05 +02:00
Pierre Ossman ce6287574f Merge branch 'hidpi_scale' of https://github.com/CendioOssman/noVNC 2018-08-16 17:50:09 +02:00
Pierre Ossman 7407c1f4e2 Replace bad sinon stub in mouse tests
It screwed up important calls inside the code being tested. Avoid
the stub by creating a temporary element with the desired properties.
2018-08-16 17:36:54 +02:00
Samuel Mannehed cc2fe2c26e Remove iOS specific code from vnc_lite
vnc_lite.html doesn't have touch support anyway
2018-08-16 15:53:32 +02:00
Samuel Mannehed 0f207c808c Remove machine control buttons from vnc_lite
The vnc_lite example is intended to be minimal and these buttons are
only useful in special cases.
2018-08-16 15:53:32 +02:00
Samuel Mannehed c995c0863e Revert "Handle if desktopName isn't set.."
This reverts commit 22000b93d5. The
'desktopname' and the 'connect' events are dispatched by us in RFB and
are thus serial.
2018-08-16 15:52:21 +02:00
Samuel Mannehed 2c0b146630 Merge pull request #1117 from novnc/bug/fix-test-playback
Fix test playback
2018-08-16 15:38:43 +02:00
Pierre Ossman 16f0861501 Support password auth recordings for playback
When password auth is enabled on the server, the RFB object sends a
'credentialsrequired' event to the UI. This commit adds support for
this event to our recoding playback.
2018-08-16 15:36:43 +02:00
Samuel Mannehed 22000b93d5 Handle if desktopName isn't set when connected
We can't guarantee that the desktopName event has been fired before the
connect event.
2018-08-16 13:33:35 +02:00
Samuel Mannehed a793df3d6d Merge pull request #1118 from novnc/disabledragwhilescale
Turn off view drag when scaling
2018-08-16 10:31:09 +02:00
Samuel Mannehed 4ddcc7537f Merge enableDisableViewClip and updateViewClip
Makes the code easier to follow and makes sure that viewDrag is
properly disabled when scaling. Fixes #1110.
2018-08-16 10:29:01 +02:00
Samuel Mannehed 6d1c036e0c Use macOS 10.13 for Safari tests
Safari 11 had a bug (#1125) which should be fixed in Safari 11.1 which comes with macOS 10.13.
2018-08-15 08:10:38 +02:00
Samuel Mannehed 3b7c47417e Move dragThreshold definition to util/
In order to avoid multiple declarations that has to be updated in the
case of future updates.
2018-08-10 11:24:09 +02:00
Samuel Mannehed b3ac94a978 Remove firebug comments
Firebug is discontinued and it's features are included in browser
development tools now a days.
2018-08-08 14:05:06 +02:00
Samuel Mannehed eebef339be Detail path to icon Makefile 2018-08-08 14:04:45 +02:00
Solly Ross ee3493c060 Add a record flag to launch.sh
Add the `--record` flag to launch.js, for easy recording when testing.
2018-07-30 11:07:33 -04:00
Samuel Mannehed 2bbd15ccaf Remove setViewDrag function
Unnecessary function only used in the toggle function above.
2018-07-30 10:46:41 +02:00
Samuel Mannehed 0b903af296 Remove unnecessary code
The enableDisableViewClip call in the fullscreen code didn't have any
effect and should have been removed when the special case for clipping
in IE and Safari fullscreen was removed in b18ef81.

The setViewDrag call claimed to disable view drag on UI state change.
The UI states are:

 init, connecting, connected, reconnecting, disconnecting, disconnected

The only state where the called function didn't immediately return was
"connected" and that's the only state where enabling view drag is
possible. Thus it could never have been enabled when changing to the
"connected" state.
2018-07-30 10:29:35 +02:00
Solly Ross cccf3b008a Fix perf/playback tool
Somewhere along the way, the refactors broke playback.js.  This fixes
the actual functionality, and makes its JS loading match that in
vnc.html.
2018-07-29 19:14:56 -04:00
Pierre Ossman ab1ace383e Handle fractional screen sizes
With high DPI systems we can end up with a container with a size that
is not an integer number of CSS pixels. Make sure we can handle those
cases by allowing a fractional size for the output canvas. Framebuffer
size and viewport coordinates are still restricted to integer dimensions
though.

Based on initial patch by Alexander E. Patrakov.
2018-07-26 14:15:59 +02:00
Pierre Ossman 862967e089 Merge branch 'master' of https://github.com/patrakov/noVNC 2018-07-25 20:40:51 +02:00
Alexander E. Patrakov 599588fe5f Documented browser cache issue 2018-07-22 23:03:05 +08:00
Pierre Ossman f9b6d7665d Use newer macOS test machine for Travis 2018-07-16 13:46:48 +02:00
Pierre Ossman 7bcdbbc65b Stop transpiling karma tests
This runs our code in the same manner as it would be used if loaded
directly in the browser. Includes the same kind of fallback for older
browsers.
2018-07-16 13:32:35 +02:00
Pierre Ossman 800abf1277 Fix proper triggering of module fallback
We might be in the "interactive" readyState, which means that
DOMContentLoaded has already fired and we'll hang.
2018-07-13 15:57:24 +02:00
Pierre Ossman 9eaea86234 Don't stub out ES module imports
It is not allowed and only happens to work because babel doesn't
strictly follow the specification. It doesn't seem necessary for the
tests to run, so just remove it.
2018-07-13 15:57:24 +02:00
Pierre Ossman d131633471 Better currentScript fallback
The previous heuristic didn't work under all circumstances, so try
something more robust.
2018-07-13 15:57:24 +02:00
Pierre Ossman ae2e1ff7bd Move sinon to karma framework
This frees us from manual imports, and makes things less magical
as those aren't ES modules even if the code suggest that the are.
2018-07-13 15:57:24 +02:00
Juanjo Diaz 885363a373 Use the classic function foo() { ... } for top level functions or functions that depend on the scope 2018-07-12 19:06:57 +02:00
Juanjo Diaz 651c23ece3 Use fat arrow functions const foo = () => { ... }; for callbacks
and any other function that is passed around and it's not a top level function
2018-07-12 19:06:57 +02:00
Juanjo Diaz 0e4808bf6f Use ES6 classes
Always use the shorthand notation if the function is a method of an object or class `{ foo() { ... } }` or `class bar { foo() { ... } }`
unless it's a callback in which case you a fat arrow function should be used `{ cb: () => { ... } }`
2018-07-12 19:06:57 +02:00
Pierre Ossman 67fefcf184 Merge branch 'cursor' of https://github.com/CendioOssman/noVNC 2018-07-11 13:39:37 +02:00
Pierre Ossman baa4f23ee5 Provide fallback cursor method
Some browsers don't support custom cursors, and there are cases
where the browsers refuse to show the cursor. Handle both of these
cases by letting the browser render the cursor via a floating
canvas.

This allows us to support a local cursor at all times.
2018-07-06 16:37:27 +02:00
Pierre Ossman 1073b60155 Sort vkeys table 2018-07-04 15:53:41 +02:00
Pierre Ossman 8acadd9e97 Merge branch 'fix/ie11-numpad5-compatibility' of https://github.com/vlastoun/noVNC 2018-07-04 15:53:30 +02:00
Henry Vindin 9700e3592b Fixes #1075
Rather than trying to pick a utility, we should be able to just use bash to check if a port is available or not.

We can probably assume bash is available due to the shebang declaring it.
2018-07-01 15:37:34 +10:00
Samuel Mannehed f90c2a6d4b Avoid TypedArray.slice() because of IE11 2018-06-15 12:00:43 +02:00
Samuel Mannehed d9814c06bf Use string assignment operator instead of concat()
The assignment operator is a lot faster.
2018-06-15 11:59:28 +02:00
Samuel Mannehed 4318c8cafd Use the correct slicing for rQshiftStr
This didn't result in any error however since slice() handles such
mistakes gracefully.
2018-06-15 11:56:56 +02:00
Samuel Mannehed 178b92d380 Add rQshiftStr unit test for large strings 2018-06-15 11:53:51 +02:00
Samuel Mannehed db9daa98a5 Avoid big strings on the stack
Previous code resulted in RangeErrors by potentially creating big
strings.

Fixes issue #1065
2018-06-14 16:59:52 +02:00
Samuel Mannehed 362bd5e3a2 Call rQshiftBytes to avoid code duplication 2018-06-14 16:51:29 +02:00
Samuel Mannehed e87b645b56 Remove typedArrayToString
We don't use PhantomJS anymore
2018-06-14 16:43:48 +02:00
Pierre Ossman aaa2ecbd95 Merge branch 'issue_templates' of https://github.com/novnc/noVNC 2018-06-07 16:07:54 +02:00
Samuel Mannehed 11715f2092 Properly force clipping on touch
It shouldn't depend on what's saved in webstorage nor save the forced
value to webstorage.

Includes a revert of 0342e4f489
2018-06-07 15:58:31 +02:00
Pierre Ossman 8f47bd296c Work around Siemens touch panel authentication bug
Siemens' touch panels support Tight authentication as well as NOTUNNEL,
but they fail to advertise the latter. Work around this issue by detecting
a Siemens device (through their custom tunnel types) and assume NOTUNNEL
support even if not advertised.
2018-06-07 15:03:34 +02:00
Pierre Ossman e6bad200e4 Add debug logging for Tight authentication
Makes it easier to diagnose user issues when we can see what the
server and noVNC are trying to negotiate.
2018-06-07 14:57:17 +02:00
Pierre Ossman 13364d70dd Merge branch 'travis-lint' of https://github.com/CendioOssman/noVNC 2018-06-07 14:53:02 +02:00
Samuel Mannehed 0342e4f489 Clipping should be enabled on touch
This was always the intention and the main use case of 'clipping'. It
seems like it got lost somewhere along the way.
2018-06-04 21:41:45 +02:00
Pierre Ossman 127b63b79f Stop combining test platforms
Sauce is very unstable, so spread things out so we can more easily
throttle things to more sane levels.
2018-06-01 15:26:52 +02:00
Pierre Ossman 81207ffebd Run eslint in travis
Makes sure we get early feedback for lint violations.
2018-06-01 14:37:00 +02:00
Samuel Mannehed fe70a1d51f Merge pull request #1013 from juanjoDiaz/es6_refactor_2
Add eslint and update noVNC to ES6
2018-05-25 10:22:36 +02:00
Juanjo Diaz 2b5f94fa6a Prefer const/let over var 2018-05-24 00:27:09 +03:00
Juanjo Diaz cdb860ad84 Add transpilation for IE11 and skip linux tests 2018-05-24 00:26:34 +03:00
Juanjo Diaz 8727f598c2 Add eslint and fix reported issues 2018-05-24 00:25:44 +03:00
Pierre Ossman (Work account) 5dad77b9d5 Add issue templates
We often have to ask for the same information for every new issue. Try to avoid this by using github's templates for issues.
2018-05-21 09:37:37 +02:00
Vlastimil Sadilek 5858f472e3 Fix: IE11 Numpad5 compatibility when numlock off
This fix Numpad5 in Internet Explorer 11 if numlock state of host differs with numlock state of
VNC console.
2018-05-16 13:52:56 +02:00
Samuel Mannehed cfe1e44ed7 Merge pull request #1074 from samhed/largeclipboard
Handle sending large clipboards
2018-05-07 13:25:46 +02:00
Samuel Mannehed 2bb8b28d78 Handle sending large clipboards
Pasting clipboard texts that were larger than 10240 bytes didnt work and
caused a crash in noVNC. This commit fixes the crash and adds handling
for sending large clipboard texts. Fixes issue #1065.
2018-05-07 13:02:51 +02:00
Vlastimil Sadilek f3e2fc58ec Fix: undefined err, undefined Exception 2018-05-04 14:23:02 +02:00
Samuel Mannehed 43bbaa8d6e Merge pull request #1066 from colin-zhou/master
Update the internationalization module
2018-04-29 20:18:52 +02:00
Pierre Ossman 9dc580db27 Update browser test list
We want to also test Microsoft Edge, and Internet Explorer on Windows 10
is really a reskinned Edge so we also need to test on Windows 7.
2018-04-27 16:19:40 +02:00
Zhou Chaolin 024aca48e5 Update the noVNC translation part
fix the specification in zh_CN.po
2018-04-24 02:29:51 +08:00
Samuel Mannehed 24231f1ae3 Merge pull request #1048 from ghostplant/master
Add translation in zh_CN
2018-04-09 09:26:52 +02:00
CUI Wei dcee7c5e91 Add translation in zh_CN
Signed-off-by: CUI Wei <ghostplant@qq.com>
2018-04-07 06:16:00 -04:00
Pierre Ossman 3328675b44 Clarify which Chinese translation we have
Chinese has several writing systems so we need to be clear which one
our translation covers. The one we currently have is for Traditional
Chinese so make sure it uses the matching language tag.
2018-04-03 14:51:12 +02:00
Pierre Ossman 7d60e97cc9 Only show error stack if it is not empty
Parsing errors will not have a stack, and we don't want an empty
box in those cases.
2018-03-21 15:33:14 +01:00
Pierre Ossman b475eed5fa Separate out cursor handling
Make cursor handling more generic in preparation for generic handling
of corner cases.
2018-03-15 17:22:21 +01:00
Samuel Mannehed 3f9ca4f5dc Move VERSION to top-level 2018-03-15 14:33:09 +01:00
Samuel Mannehed 25cbf00e13 Remove docs/release.txt
Instructions has been moved to the wiki:

https://github.com/novnc/noVNC/wiki/Development:-Making-a-release
2018-03-15 14:28:08 +01:00
Samuel Mannehed a07d4abe1f Fix docs/VERSION 2018-03-15 09:28:18 +01:00
Pierre Ossman 35dd3c2299 Merge branches 'ffalt' and 'altgr' of https://github.com/CendioOssman/noVNC 2018-03-13 16:03:01 +01:00
Pierre Ossman 3a7c0c67c1 Work around broken Alt keyup in Firefox
Firefox no longer sends keyup events properly for the Alt keys. Try
to sniff out the state of the Alt key by monitoring other events that
include its state.
2018-03-13 16:01:38 +01:00
Pierre Ossman e9118e3bda Get localStorage tests running on more browsers 2018-03-09 12:15:21 +01:00
Pierre Ossman b22c9ef954 Better detection of AltGr on Windows
Try to properly detect the fake CtrlL+AltR sequence Windows sends
when pressing AltGr. This allows us to send more accurate key
events over to the server.
2018-03-09 12:14:23 +01:00
Pierre Ossman d6ae445773 Handle _keyDownList in _sendKeyEvent()
This makes sure it never gets out of sync with what we've actually
sent.
2018-03-09 12:13:21 +01:00
Samuel Mannehed 06309160ee Only disable animation when element is displayed
The transitionend event will not fire when display=none. This can
prevent the initial animation for hiding the controlbar in some cases.
2018-03-08 16:52:53 +01:00
Samuel Mannehed d7a575a2c8 Merge pull request #989 from PeterDaveHelloKitchen/update-travis-ci
Update Travis CI configuration
2018-03-06 16:22:27 +01:00
Pierre Ossman e62b4ccb5e Merge branch 'userequire' of https://github.com/CendioOssman/noVNC 2018-02-28 13:34:28 +01:00
Pierre Ossman 4a65d50d0c Only use converted modules as legacy fallback for app
Several of the major browsers now natively support modules, so we
only need the converted modules to handle older browsers. Make sure
it's only used when necessary.
2018-02-28 13:28:07 +01:00
Pierre Ossman 8aad8f269c Merge branch 'settings' of https://github.com/andrwwbstr/noVNC 2018-02-28 12:57:48 +01:00
Pierre Ossman e1802cac7f Separate Tight PNG in stats output 2018-02-27 10:52:02 +01:00
Pierre Ossman 5bdcf5d31c Enforce Tight PNG restrictions
Tight PNG rects cannot use the basic compression variants, and PNG
cannot be used in a standard Tight rect.

This is a partial revert of 3e8b26a based on better understanding
of the encoding.
2018-02-27 10:50:13 +01:00
Leslie Qi Wang 2c813a33fe add encoding support for TightPNG 2018-02-23 10:38:17 -08:00
Pierre Ossman e91a095ad6 noVNC 1.0.0 2018-02-22 14:10:10 +01:00
Andrew Webster 8ad8f15cf6 Move writeSetting from updateSetting to initSetting
initSetting was the only place that supplied a 'value' to
updateSetting.  So move it to clean up updateSetting.
2018-02-13 10:22:36 -05:00
Andrew Webster e0750f9b2c Use localstorage only to initialize settings map
This only reads from localstorage in order to initialize the settings
map.  After initializaton, reads will return the value from the map.

When writing a value, the settings map and the local storage
are updated, unless the setting is a default value or derived from
the query string.

This has a few advantages:
 1. Saved settings will not be overridden by settings specified in
the query string.  This means a setting could be temporarily changed
using the query string, but once removed from the query string, the
setting would return back to what the user selected.
 2. Default values will not be saved.  If a user has always used
the default value for a setting, then they can move to a new version
with different defaults without clearing localstorage.
 3. Changes made to localstorage in a session running in a different
window will not affect the settings in the current window (until
the page is refreshed).

Regarding eraseSetting:

It is possible that another tab could change the value, leading
to an unexpected value change in the tab that deletes.  However,
this function is currently unused, so this will be evaluted if
and when it used.
2018-02-13 10:22:21 -05:00
Pierre Ossman 37b4d13db8 Add Spanish and Turkish JSON files 2018-02-07 09:33:13 +01:00
Pierre Ossman 7c332ad930 Don't crash on translation errors
A non-translated interface is better than no interface at all.
2018-02-07 09:23:45 +01:00
Peter Dave Hello 1a76fb843a Use node.js 6 instead 6.1 to have the latest v6.x 2018-01-09 11:49:28 +08:00
Pierre Ossman d584c5f624 Don't include icons Makefile when packaging 2018-01-05 16:17:29 +01:00
Pierre Ossman be7b4e88f0 Remove intermediate files when bundling 2018-01-05 16:17:29 +01:00
Pierre Ossman 2163326888 Convert use_require.js to use promises
We had some race conditions between the callbacks that could cause
failures. Order everything properly using promises.
2018-01-05 16:17:26 +01:00
150 changed files with 15973 additions and 68019 deletions
+1
View File
@@ -0,0 +1 @@
**/xtscancodes.js
+50
View File
@@ -0,0 +1,50 @@
{
"env": {
"browser": true,
"es6": true
},
"parserOptions": {
"sourceType": "module"
},
"extends": "eslint:recommended",
"rules": {
// Unsafe or confusing stuff that we forbid
"no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": true }],
"no-constant-condition": ["error", { "checkLoops": false }],
"no-var": "error",
"no-useless-constructor": "error",
"object-shorthand": ["error", "methods", { "avoidQuotes": true }],
"prefer-arrow-callback": "error",
"arrow-body-style": ["error", "as-needed", { "requireReturnForObjectLiteral": false } ],
"arrow-parens": ["error", "as-needed", { "requireForBlockBody": true }],
"arrow-spacing": ["error"],
"no-confusing-arrow": ["error", { "allowParens": true }],
// Enforced coding style
"brace-style": ["error", "1tbs", { "allowSingleLine": true }],
"indent": ["error", 4, { "SwitchCase": 1,
"FunctionDeclaration": { "parameters": "first" },
"CallExpression": { "arguments": "first" },
"ArrayExpression": "first",
"ObjectExpression": "first",
"ignoreComments": true }],
"comma-spacing": ["error"],
"comma-style": ["error"],
"curly": ["error", "multi-line"],
"func-call-spacing": ["error"],
"func-names": ["error"],
"func-style": ["error", "declaration", { "allowArrowFunctions": true }],
"key-spacing": ["error"],
"keyword-spacing": ["error"],
"no-trailing-spaces": ["error"],
"semi": ["error"],
"space-before-blocks": ["error"],
"space-before-function-paren": ["error", { "anonymous": "always",
"named": "never",
"asyncArrow": "always" }],
"switch-colon-spacing": ["error"],
"camelcase": ["error", { allow: ["^XK_", "^XF86XK_"] }],
}
}
+34
View File
@@ -0,0 +1,34 @@
---
name: Bug report
about: Create a report to help us improve
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Client (please complete the following information):**
- OS: [e.g. iOS]
- Browser: [e.g. chrome, safari]
- Browser version: [e.g. 22]
**Server (please complete the following information):**
- noVNC version: [e.g. 1.0.0 or git commit id]
- VNC server: [e.g. QEMU, TigerVNC]
- WebSocket proxy: [e.g. websockify]
**Additional context**
Add any other context about the problem here.
+5
View File
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Question or discussion
url: https://groups.google.com/forum/?fromgroups#!forum/novnc
about: Ask a question or start a discussion
+17
View File
@@ -0,0 +1,17 @@
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+56
View File
@@ -0,0 +1,56 @@
name: Publish
on:
push:
pull_request:
release:
types: [published]
jobs:
npm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
# Needs to be explicitly specified for auth to work
registry-url: 'https://registry.npmjs.org'
- run: npm install
- uses: actions/upload-artifact@v2
with:
name: npm
path: lib
- run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
if: ${{ github.event_name == 'release' && !github.event.release.prerelease }}
- run: npm publish --access public --tag beta
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
if: ${{ github.event_name == 'release' && github.event.release.prerelease }}
snap:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: |
VERSION=$(grep '"version"' package.json | cut -d '"' -f 4)
echo $VERSION
sed -i "s/@VERSION@/$VERSION/g" snap/snapcraft.yaml
- uses: snapcore/action-build@v1
id: snapcraft
- uses: actions/upload-artifact@v2
with:
name: snap
path: ${{ steps.snapcraft.outputs.snap }}
- uses: snapcore/action-publish@v1
with:
store_login: ${{ secrets.SNAPCRAFT_LOGIN }}
snap: ${{ steps.build.outputs.snap }}
release: stable
if: ${{ github.event_name == 'release' && !github.event.release.prerelease }}
- uses: snapcore/action-publish@v1
with:
store_login: ${{ secrets.SNAPCRAFT_LOGIN }}
snap: ${{ steps.build.outputs.snap }}
release: beta
if: ${{ github.event_name == 'release' && github.event.release.prerelease }}
+19
View File
@@ -0,0 +1,19 @@
name: Lint
on: [push, pull_request]
jobs:
eslint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- run: npm install
- run: npm run lint
html:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- run: npm install
- run: git ls-tree --name-only -r HEAD | grep -E "[.](html|css)$" | xargs ./utils/validate
+28
View File
@@ -0,0 +1,28 @@
name: Test
on: [push, pull_request]
jobs:
test:
strategy:
matrix:
os:
- ubuntu-latest
- windows-latest
browser:
- ChromeHeadless
- FirefoxHeadless
include:
- os: macos-latest
browser: Safari
- os: windows-latest
browser: EdgeHeadless
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- run: npm install
- run: npm run test
env:
TEST_BROWSER_NAME: ${{ matrix.browser }}
-37
View File
@@ -1,37 +0,0 @@
# infra JS
/build/
/node_modules/
/tests/
/utils/
/recordings/
/vendor/sinon.js
# noVNC application files
/app
/vendor/browser-es-module-loader
/vendor/promise.js
/vnc.html
/vnc_lite.html
# raw translation files
/po
# config files
/.travis.yml
/karma.conf.js
# various other files
/.gitmodules
.*
*~
*.swp
*.swo
# documentation (except licenses)
/docs/notes
/docs/links
/docs/release.txt
/docs/rfb_notes
/docs/*.pdf
/docs/flash_policy.txt
/CONTRIBUTING.md
-38
View File
@@ -1,38 +0,0 @@
language: node_js
sudo: false
cache:
directories:
- node_modules
node_js:
- '6.1'
env:
matrix:
- 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'
before_script: npm install -g karma-cli
addons:
sauce_connect:
username: "directxman12"
jwt:
secure: "d3ekMYslpn6R4f0ajtRMt9SUFmNGDiItHpqaXC5T4KI0KMEsxgvEOfJot5PiFFJWg1DSpJZH6oaW2UxGZ3duJLZrXIEd/JePY8a6NtT35BNgiDPgcp+eu2Bu3rhrSNg7/HEsD1ma+JeUTnv18Ai5oMFfCCQJx2J6osIxyl/ZVxA="
stages:
- test
- name: deploy
if: tag is PRESENT
jobs:
include:
- stage: deploy
script: skip
before_script: skip
deploy:
provider: npm
email: directxman12+npm@gmail.com
api_key:
secure: cIidkFmvkdmdwWsqBpxyPUCzBqgK8LhPiNxTrIfhwbUunMsJep9MiiBJtv8poVYG2Y4yfiZmqGn4nfetUdc/LDctd73j+/EM4Z/NUDexVAhJ+9/qCogvpJsSQ96VQo7yBceW4E1fBM3WCU0kcGToYIVSSrwvvRDtJfeYJf2Qqw0=
on:
tags: true
repo: novnc/noVNC
+13
View File
@@ -0,0 +1,13 @@
maintainers:
- Joel Martin (@kanaka)
- Solly Ross (@directxman12)
- Samuel Mannehed for Cendio AB (@samhed)
- Pierre Ossman for Cendio AB (@CendioOssman)
maintainersEmeritus:
- @astrand
contributors:
# There are a bunch of people that should be here.
# If you want to be on this list, feel free send a PR
# to add yourself.
- jalf <git@jalf.dk>
- NTT corp.
+2 -7
View File
@@ -1,4 +1,5 @@
noVNC is Copyright (C) 2011 Joel Martin <github@martintribe.org>
noVNC is Copyright (C) 2019 The noVNC Authors
(./AUTHORS)
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
@@ -41,12 +42,6 @@ licenses (all MPL 2.0 compatible):
vendor/pako/ : MIT
vendor/browser-es-module-loader/src/ : MIT
vendor/browser-es-module-loader/dist/ : Various BSD style licenses
vendor/promise.js : MIT
Any other files not mentioned above are typically marked with
a copyright/license header at the top of the file. The default noVNC
license is MPL-2.0.
+72 -6
View File
@@ -1,6 +1,7 @@
## noVNC: HTML VNC Client Library and Application
[![Build Status](https://travis-ci.org/novnc/noVNC.svg?branch=master)](https://travis-ci.org/novnc/noVNC)
[![Test Status](https://github.com/novnc/noVNC/workflows/Test/badge.svg)](https://github.com/novnc/noVNC/actions?query=workflow%3ATest)
[![Lint Status](https://github.com/novnc/noVNC/workflows/Lint/badge.svg)](https://github.com/novnc/noVNC/actions?query=workflow%3ALint)
### Description
@@ -24,6 +25,7 @@ for a more complete list with additional info and links.
- [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)
@@ -67,6 +69,8 @@ Please tweet [@noVNC](http://www.twitter.com/noVNC) if you do.
* Supports scaling, clipping and resizing the desktop
* Local cursor rendering
* Clipboard copy/paste
* Translations
* Touch gestures for emulating common mouse actions
* Licensed mainly under the [MPL 2.0](http://www.mozilla.org/MPL/2.0/), see
[the license document](LICENSE.txt) for details
@@ -87,7 +91,7 @@ noVNC uses many modern web technologies so a formal requirement list is
not available. However these are the minimum versions we are currently
aware of:
* Chrome 49, Firefox 44, Safari 10, Opera 36, IE 11, Edge 12
* Chrome 64, Firefox 79, Safari 13.4, Opera 51, Edge 79
### Server Requirements
@@ -104,16 +108,76 @@ proxy.
### Quick Start
* Use the launch script to automatically download and start websockify, which
* Use the `novnc_proxy` script to automatically download and start websockify, which
includes a mini-webserver and the WebSockets proxy. The `--vnc` option is
used to specify the location of a running VNC server:
`./utils/launch.sh --vnc localhost:5901`
`./utils/novnc_proxy --vnc localhost:5901`
* Point your browser to the cut-and-paste URL that is output by the launch
* Point your browser to the cut-and-paste URL that is output by the `novnc_procy`
script. Hit the Connect button, enter a password if the VNC server has one
configured, and enjoy!
### Installation from Snap Package
Running the command below will install the latest release of noVNC from Snap:
`sudo snap install novnc`
#### Running noVNC
You can run the Snap-package installed novnc directly with, for example:
`novnc --listen 6081 --vnc localhost:5901 # /snap/bin/novnc if /snap/bin is not in your PATH`
#### Running as a Service (Daemon)
The Snap package also has the capability to run a 'novnc' service which can be
configured to listen on multiple ports connecting to multiple VNC servers
(effectively a service runing multiple instances of novnc).
Instructions (with example values):
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
@@ -126,10 +190,12 @@ or deploying the noVNC application in production environments:
### Authors/Contributors
See [AUTHORS](AUTHORS) for a (full-ish) list of authors. If you're not on
that list and you think you should be, feel free to send a PR to fix that.
* Core team:
* [Joel Martin](https://github.com/kanaka)
* [Samuel Mannehed](https://github.com/samhed) (Cendio)
* [Peter Åstrand](https://github.com/astrand) (Cendio)
* [Solly Ross](https://github.com/DirectXMan12) (Red Hat / OpenStack)
* [Pierre Ossman](https://github.com/CendioOssman) (Cendio)
+18 -8
View File
@@ -1,22 +1,32 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
// NB: this should *not* be included as a module until we have
// native support in the browsers, so that our error handler
// can catch script-loading errors.
// No ES6 can be used in this file since it's used for the translation
/* eslint-disable prefer-arrow-callback */
(function(){
(function _scope() {
"use strict";
// Fallback for all uncought errors
function handleError (event, err) {
function handleError(event, err) {
try {
var msg = document.getElementById('noVNC_fallback_errormsg');
const msg = document.getElementById('noVNC_fallback_errormsg');
// Only show the initial error
if (msg.hasChildNodes()) {
return false;
}
var div = document.createElement("div");
let div = document.createElement("div");
div.classList.add('noVNC_message');
div.appendChild(document.createTextNode(event.message));
msg.appendChild(div);
@@ -24,7 +34,7 @@
if (event.filename) {
div = document.createElement("div");
div.className = 'noVNC_location';
var text = event.filename;
let text = event.filename;
if (event.lineno !== undefined) {
text += ":" + event.lineno;
if (event.colno !== undefined) {
@@ -35,7 +45,7 @@
msg.appendChild(div);
}
if (err && (err.stack !== undefined)) {
if (err && err.stack) {
div = document.createElement("div");
div.className = 'noVNC_stack';
div.appendChild(document.createTextNode(err.stack));
@@ -51,6 +61,6 @@
// from being printed to the browser console.
return false;
}
window.addEventListener('error', function (evt) { handleError(evt, evt.error); });
window.addEventListener('unhandledrejection', function (evt) { handleError(evt.reason, evt.reason); });
window.addEventListener('error', function onerror(evt) { handleError(evt, evt.error); });
window.addEventListener('unhandledrejection', function onreject(evt) { handleError(evt.reason, evt.reason); });
})();
-92
View File
@@ -1,92 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 6.8 KiB

-92
View File
@@ -1,92 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 6.8 KiB

-92
View File
@@ -1,92 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 6.8 KiB

-92
View File
@@ -1,92 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 6.8 KiB

+65
View File
@@ -0,0 +1,65 @@
<?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>

After

Width:  |  Height:  |  Size: 2.4 KiB

+1
View File
@@ -0,0 +1 @@
DO NOT MODIFY THE FILES IN THIS FOLDER, THEY ARE AUTOMATICALLY GENERATED FROM THE PO-FILES.
+71
View File
@@ -0,0 +1,71 @@
{
"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"
}
+68
View File
@@ -0,0 +1,68 @@
{
"Connecting...": "Conectando...",
"Connected (encrypted) to ": "Conectado (con encriptación) a",
"Connected (unencrypted) to ": "Conectado (sin encriptación) a",
"Disconnecting...": "Desconectando...",
"Disconnected": "Desconectado",
"Must set host": "Debes configurar el host",
"Reconnecting...": "Reconectando...",
"Password is required": "Contraseña es obligatoria",
"Disconnect timeout": "Tiempo de desconexión agotado",
"noVNC encountered an error:": "noVNC ha encontrado un error:",
"Hide/Show the control bar": "Ocultar/Mostrar la barra de control",
"Move/Drag Viewport": "Mover/Arrastrar la ventana",
"viewport drag": "Arrastrar la ventana",
"Active Mouse Button": "Botón activo del ratón",
"No mousebutton": "Ningún botón del ratón",
"Left mousebutton": "Botón izquierdo del ratón",
"Middle mousebutton": "Botón central del ratón",
"Right mousebutton": "Botón derecho del ratón",
"Keyboard": "Teclado",
"Show Keyboard": "Mostrar teclado",
"Extra keys": "Teclas adicionales",
"Show Extra Keys": "Mostrar Teclas Adicionales",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Pulsar/Soltar Ctrl",
"Alt": "Alt",
"Toggle Alt": "Pulsar/Soltar Alt",
"Send Tab": "Enviar Tabulación",
"Tab": "Tabulación",
"Esc": "Esc",
"Send Escape": "Enviar Escape",
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
"Send Ctrl-Alt-Del": "Enviar Ctrl+Alt+Del",
"Shutdown/Reboot": "Apagar/Reiniciar",
"Shutdown/Reboot...": "Apagar/Reiniciar...",
"Power": "Encender",
"Shutdown": "Apagar",
"Reboot": "Reiniciar",
"Reset": "Restablecer",
"Clipboard": "Portapapeles",
"Clear": "Vaciar",
"Fullscreen": "Pantalla Completa",
"Settings": "Configuraciones",
"Shared Mode": "Modo Compartido",
"View Only": "Solo visualización",
"Clip to Window": "Recortar al tamaño de la ventana",
"Scaling Mode:": "Modo de escalado:",
"None": "Ninguno",
"Local Scaling": "Escalado Local",
"Local Downscaling": "Reducción de escala local",
"Remote Resizing": "Cambio de tamaño remoto",
"Advanced": "Avanzado",
"Local Cursor": "Cursor Local",
"Repeater ID:": "ID del Repetidor",
"WebSocket": "WebSocket",
"Encrypt": "",
"Host:": "Host",
"Port:": "Puesto",
"Path:": "Ruta",
"Automatic Reconnect": "Reconexión automática",
"Reconnect Delay (ms):": "Retraso en la reconexión (ms)",
"Logging:": "Logging",
"Disconnect": "Desconectar",
"Connect": "Conectar",
"Password:": "Contraseña",
"Cancel": "Cancelar",
"Canvas not supported.": "Canvas no está soportado"
}
+72
View File
@@ -0,0 +1,72 @@
{
"Connecting...": "接続しています...",
"Disconnecting...": "切断しています...",
"Reconnecting...": "再接続しています...",
"Internal error": "内部エラー",
"Must set host": "ホストを設定する必要があります",
"Connected (encrypted) to ": "接続しました (暗号化済み): ",
"Connected (unencrypted) to ": "接続しました (暗号化されていません): ",
"Something went wrong, connection is closed": "何らかの問題で、接続が閉じられました",
"Failed to connect to server": "サーバーへの接続に失敗しました",
"Disconnected": "切断しました",
"New connection has been rejected with reason: ": "新規接続は次の理由で拒否されました: ",
"New connection has been rejected": "新規接続は拒否されました",
"Credentials are required": "資格情報が必要です",
"noVNC encountered an error:": "noVNC でエラーが発生しました:",
"Hide/Show the control bar": "コントロールバーを隠す/表示する",
"Drag": "ドラッグ",
"Move/Drag Viewport": "ビューポートを移動/ドラッグ",
"Keyboard": "キーボード",
"Show Keyboard": "キーボードを表示",
"Extra keys": "追加キー",
"Show Extra Keys": "追加キーを表示",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Ctrl キーを切り替え",
"Alt": "Alt",
"Toggle Alt": "Alt キーを切り替え",
"Toggle Windows": "Windows キーを切り替え",
"Windows": "Windows",
"Send Tab": "Tab キーを送信",
"Tab": "Tab",
"Esc": "Esc",
"Send Escape": "Escape キーを送信",
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
"Send Ctrl-Alt-Del": "Ctrl-Alt-Del を送信",
"Shutdown/Reboot": "シャットダウン/再起動",
"Shutdown/Reboot...": "シャットダウン/再起動...",
"Power": "電源",
"Shutdown": "シャットダウン",
"Reboot": "再起動",
"Reset": "リセット",
"Clipboard": "クリップボード",
"Clear": "クリア",
"Fullscreen": "全画面表示",
"Settings": "設定",
"Shared Mode": "共有モード",
"View Only": "表示のみ",
"Clip to Window": "ウィンドウにクリップ",
"Scaling Mode:": "スケーリングモード:",
"None": "なし",
"Local Scaling": "ローカルスケーリング",
"Remote Resizing": "リモートでリサイズ",
"Advanced": "高度",
"Quality:": "品質:",
"Compression level:": "圧縮レベル:",
"Repeater ID:": "リピーター ID:",
"WebSocket": "WebSocket",
"Encrypt": "暗号化",
"Host:": "ホスト:",
"Port:": "ポート:",
"Path:": "パス:",
"Automatic Reconnect": "自動再接続",
"Reconnect Delay (ms):": "再接続する遅延 (ミリ秒):",
"Show Dot when No Cursor": "カーソルがないときにドットを表示",
"Logging:": "ロギング:",
"Version:": "バージョン:",
"Disconnect": "切断",
"Connect": "接続",
"Username:": "ユーザー名:",
"Password:": "パスワード:",
"Send Credentials": "資格情報を送信",
"Cancel": "キャンセル"
}
+70
View File
@@ -0,0 +1,70 @@
{
"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": "취소"
}
+15 -10
View File
@@ -1,13 +1,17 @@
{
"Connecting...": "Verbinden...",
"Disconnecting...": "Verbinding verbreken...",
"Reconnecting...": "Opnieuw verbinding maken...",
"Internal error": "Interne fout",
"Must set host": "Host moeten worden ingesteld",
"Connected (encrypted) to ": "Verbonden (versleuteld) met ",
"Connected (unencrypted) to ": "Verbonden (onversleuteld) met ",
"Disconnecting...": "Verbinding verbreken...",
"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",
"Must set host": "Host moeten worden ingesteld",
"Reconnecting...": "Opnieuw verbinding maken...",
"New connection has been rejected with reason: ": "Nieuwe verbinding is geweigerd omwille van de volgende reden: ",
"New connection has been rejected": "Nieuwe verbinding is geweigerd",
"Password is required": "Wachtwoord is vereist",
"Disconnect timeout": "Timeout tijdens verbreken van verbinding",
"noVNC encountered an error:": "noVNC heeft een fout bemerkt:",
"Hide/Show the control bar": "Verberg/Toon de bedieningsbalk",
"Move/Drag Viewport": "Verplaats/Versleep Kijkvenster",
@@ -22,9 +26,11 @@
"Extra keys": "Extra toetsen",
"Show Extra Keys": "Toon Extra Toetsen",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Ctrl aan/uitzetten",
"Toggle Ctrl": "Ctrl omschakelen",
"Alt": "Alt",
"Toggle Alt": "Alt aan/uitzetten",
"Toggle Alt": "Alt omschakelen",
"Toggle Windows": "Windows omschakelen",
"Windows": "Windows",
"Send Tab": "Tab Sturen",
"Tab": "Tab",
"Esc": "Esc",
@@ -47,10 +53,8 @@
"Scaling Mode:": "Schaalmodus:",
"None": "Geen",
"Local Scaling": "Lokaal Schalen",
"Local Downscaling": "Lokaal Neerschalen",
"Remote Resizing": "Op Afstand Formaat Wijzigen",
"Advanced": "Geavanceerd",
"Local Cursor": "Lokale Cursor",
"Repeater ID:": "Repeater ID:",
"WebSocket": "WebSocket",
"Encrypt": "Versleutelen",
@@ -59,10 +63,11 @@
"Path:": "Pad:",
"Automatic Reconnect": "Automatisch Opnieuw Verbinden",
"Reconnect Delay (ms):": "Vertraging voor Opnieuw Verbinden (ms):",
"Show Dot when No Cursor": "Geef stip weer indien geen cursor",
"Logging:": "Logmeldingen:",
"Disconnect": "Verbinding verbreken",
"Connect": "Verbinden",
"Password:": "Wachtwoord:",
"Cancel": "Annuleren",
"Canvas not supported.": "Canvas wordt niet ondersteund."
"Send Password": "Verzend Wachtwoord:",
"Cancel": "Annuleren"
}
+72
View File
@@ -0,0 +1,72 @@
{
"Connecting...": "Conectando...",
"Disconnecting...": "Desconectando...",
"Reconnecting...": "Reconectando...",
"Internal error": "Erro interno",
"Must set host": "É necessário definir o host",
"Connected (encrypted) to ": "Conectado (com criptografia) a ",
"Connected (unencrypted) to ": "Conectado (sem criptografia) a ",
"Something went wrong, connection is closed": "Algo deu errado. A conexão foi encerrada.",
"Failed to connect to server": "Falha ao conectar-se ao servidor",
"Disconnected": "Desconectado",
"New connection has been rejected with reason: ": "A nova conexão foi rejeitada pelo motivo: ",
"New connection has been rejected": "A nova conexão foi rejeitada",
"Credentials are required": "Credenciais são obrigatórias",
"noVNC encountered an error:": "O noVNC encontrou um erro:",
"Hide/Show the control bar": "Esconder/mostrar a barra de controles",
"Drag": "Arrastar",
"Move/Drag Viewport": "Mover/arrastar a janela",
"Keyboard": "Teclado",
"Show Keyboard": "Mostrar teclado",
"Extra keys": "Teclas adicionais",
"Show Extra Keys": "Mostar teclas adicionais",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Pressionar/soltar Ctrl",
"Alt": "Alt",
"Toggle Alt": "Pressionar/soltar Alt",
"Toggle Windows": "Pressionar/soltar Windows",
"Windows": "Windows",
"Send Tab": "Enviar Tab",
"Tab": "Tab",
"Esc": "Esc",
"Send Escape": "Enviar Esc",
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
"Send Ctrl-Alt-Del": "Enviar Ctrl-Alt-Del",
"Shutdown/Reboot": "Desligar/reiniciar",
"Shutdown/Reboot...": "Desligar/reiniciar...",
"Power": "Ligar",
"Shutdown": "Desligar",
"Reboot": "Reiniciar",
"Reset": "Reiniciar (forçado)",
"Clipboard": "Área de transferência",
"Clear": "Limpar",
"Fullscreen": "Tela cheia",
"Settings": "Configurações",
"Shared Mode": "Modo compartilhado",
"View Only": "Apenas visualizar",
"Clip to Window": "Recortar à janela",
"Scaling Mode:": "Modo de dimensionamento:",
"None": "Nenhum",
"Local Scaling": "Local",
"Remote Resizing": "Remoto",
"Advanced": "Avançado",
"Quality:": "Qualidade:",
"Compression level:": "Nível de compressão:",
"Repeater ID:": "ID do repetidor:",
"WebSocket": "WebSocket",
"Encrypt": "Criptografar",
"Host:": "Host:",
"Port:": "Porta:",
"Path:": "Caminho:",
"Automatic Reconnect": "Reconexão automática",
"Reconnect Delay (ms):": "Atraso da reconexão (ms)",
"Show Dot when No Cursor": "Mostrar ponto quando não há cursor",
"Logging:": "Registros:",
"Version:": "Versão:",
"Disconnect": "Desconectar",
"Connect": "Conectar",
"Username:": "Nome de usuário:",
"Password:": "Senha:",
"Send Credentials": "Enviar credenciais",
"Cancel": "Cancelar"
}
+73
View File
@@ -0,0 +1,73 @@
{
"Connecting...": "Подключение...",
"Disconnecting...": "Отключение...",
"Reconnecting...": "Переподключение...",
"Internal error": "Внутренняя ошибка",
"Must set host": "Задайте имя сервера или IP",
"Connected (encrypted) to ": "Подключено (с шифрованием) к ",
"Connected (unencrypted) to ": "Подключено (без шифрования) к ",
"Something went wrong, connection is closed": "Что-то пошло не так, подключение разорвано",
"Failed to connect to server": "Ошибка подключения к серверу",
"Disconnected": "Отключено",
"New connection has been rejected with reason: ": "Подключиться не удалось: ",
"New connection has been rejected": "Подключиться не удалось",
"Password is required": "Требуется пароль",
"noVNC encountered an error:": "Ошибка noVNC: ",
"Hide/Show the control bar": "Скрыть/Показать контрольную панель",
"Move/Drag Viewport": "Переместить окно",
"viewport drag": "Переместить окно",
"Active Mouse Button": "Активировать кнопки мыши",
"No mousebutton": "Отключить кнопки мыши",
"Left mousebutton": "Левая кнопка мыши",
"Middle mousebutton": "Средняя кнопка мыши",
"Right mousebutton": "Правая кнопка мыши",
"Keyboard": "Клавиатура",
"Show Keyboard": "Показать клавиатуру",
"Extra keys": "Доп. кнопки",
"Show Extra Keys": "Показать дополнительные кнопки",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Передать нажатие Ctrl",
"Alt": "Alt",
"Toggle Alt": "Передать нажатие Alt",
"Toggle Windows": "Переключение вкладок",
"Windows": "Вкладка",
"Send Tab": "Передать нажатие Tab",
"Tab": "Tab",
"Esc": "Esc",
"Send Escape": "Передать нажатие Escape",
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
"Send Ctrl-Alt-Del": "Передать нажатие Ctrl-Alt-Del",
"Shutdown/Reboot": "Выключить/Перезагрузить",
"Shutdown/Reboot...": "Выключить/Перезагрузить...",
"Power": "Питание",
"Shutdown": "Выключить",
"Reboot": "Перезагрузить",
"Reset": "Сброс",
"Clipboard": "Буфер обмена",
"Clear": "Очистить",
"Fullscreen": "Во весь экран",
"Settings": "Настройки",
"Shared Mode": "Общий режим",
"View Only": "Просмотр",
"Clip to Window": "В окно",
"Scaling Mode:": "Масштаб:",
"None": "Нет",
"Local Scaling": "Локльный масштаб",
"Remote Resizing": "Удаленный масштаб",
"Advanced": "Дополнительно",
"Repeater ID:": "Идентификатор ID:",
"WebSocket": "WebSocket",
"Encrypt": "Шифрование",
"Host:": "Сервер:",
"Port:": "Порт:",
"Path:": "Путь:",
"Automatic Reconnect": "Автоматическое переподключение",
"Reconnect Delay (ms):": "Задержка переподключения (мс):",
"Show Dot when No Cursor": "Показать точку вместо курсора",
"Logging:": "Лог:",
"Disconnect": "Отключение",
"Connect": "Подключение",
"Password:": "Пароль:",
"Send Password": "Пароль: ",
"Cancel": "Выход"
}
+19 -15
View File
@@ -1,22 +1,21 @@
{
"Connecting...": "Ansluter...",
"Disconnecting...": "Kopplar ner...",
"Reconnecting...": "Återansluter...",
"Internal error": "Internt fel",
"Must set host": "Du måste specifiera en värd",
"Connected (encrypted) to ": "Ansluten (krypterat) till ",
"Connected (unencrypted) to ": "Ansluten (okrypterat) till ",
"Disconnecting...": "Kopplar ner...",
"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",
"Must set host": "Du måste specifiera en värd",
"Reconnecting...": "Återansluter...",
"Password is required": "Lösenord krävs",
"Disconnect timeout": "Det tog för lång tid att koppla ner",
"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",
"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",
@@ -25,6 +24,8 @@
"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",
@@ -47,10 +48,10 @@
"Scaling Mode:": "Skalningsläge:",
"None": "Ingen",
"Local Scaling": "Lokal Skalning",
"Local Downscaling": "Lokal Nedskalning",
"Remote Resizing": "Ändra Storlek",
"Advanced": "Avancerat",
"Local Cursor": "Lokal Muspekare",
"Quality:": "Kvalitet:",
"Compression level:": "Kompressionsnivå:",
"Repeater ID:": "Repeater-ID:",
"WebSocket": "WebSocket",
"Encrypt": "Kryptera",
@@ -59,10 +60,13 @@
"Path:": "Sökväg:",
"Automatic Reconnect": "Automatisk Återanslutning",
"Reconnect Delay (ms):": "Fördröjning (ms):",
"Show Dot when No Cursor": "Visa prick när ingen muspekare finns",
"Logging:": "Loggning:",
"Version:": "Version:",
"Disconnect": "Koppla från",
"Connect": "Anslut",
"Username:": "Användarnamn:",
"Password:": "Lösenord:",
"Cancel": "Avbryt",
"Canvas not supported.": "Canvas stöds ej"
"Send Credentials": "Skicka Användaruppgifter",
"Cancel": "Avbryt"
}
+69
View File
@@ -0,0 +1,69 @@
{
"Connecting...": "Bağlanıyor...",
"Disconnecting...": "Bağlantı kesiliyor...",
"Reconnecting...": "Yeniden bağlantı kuruluyor...",
"Internal error": "İç hata",
"Must set host": "Sunucuyu kur",
"Connected (encrypted) to ": "Bağlı (şifrelenmiş)",
"Connected (unencrypted) to ": "Bağlandı (şifrelenmemiş)",
"Something went wrong, connection is closed": "Bir şeyler ters gitti, bağlantı kesildi",
"Disconnected": "Bağlantı kesildi",
"New connection has been rejected with reason: ": "Bağlantı aşağıdaki nedenlerden dolayı reddedildi: ",
"New connection has been rejected": "Bağlantı reddedildi",
"Password is required": "Şifre gerekli",
"noVNC encountered an error:": "Bir hata oluştu:",
"Hide/Show the control bar": "Denetim masasını Gizle/Göster",
"Move/Drag Viewport": "Görünümü Taşı/Sürükle",
"viewport drag": "Görüntü penceresini sürükle",
"Active Mouse Button": "Aktif Fare Düğmesi",
"No mousebutton": "Fare düğmesi yok",
"Left mousebutton": "Farenin sol düğmesi",
"Middle mousebutton": "Farenin orta düğmesi",
"Right mousebutton": "Farenin sağ düğmesi",
"Keyboard": "Klavye",
"Show Keyboard": "Klavye Düzenini Göster",
"Extra keys": "Ekstra tuşlar",
"Show Extra Keys": "Ekstra tuşları göster",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Ctrl Değiştir ",
"Alt": "Alt",
"Toggle Alt": "Alt Değiştir",
"Send Tab": "Sekme Gönder",
"Tab": "Sekme",
"Esc": "Esc",
"Send Escape": "Boşluk Gönder",
"Ctrl+Alt+Del": "Ctrl + Alt + Del",
"Send Ctrl-Alt-Del": "Ctrl-Alt-Del Gönder",
"Shutdown/Reboot": "Kapat/Yeniden Başlat",
"Shutdown/Reboot...": "Kapat/Yeniden Başlat...",
"Power": "Güç",
"Shutdown": "Kapat",
"Reboot": "Yeniden Başlat",
"Reset": "Sıfırla",
"Clipboard": "Pano",
"Clear": "Temizle",
"Fullscreen": "Tam Ekran",
"Settings": "Ayarlar",
"Shared Mode": "Paylaşım Modu",
"View Only": "Sadece Görüntüle",
"Clip to Window": "Pencereye Tıkla",
"Scaling Mode:": "Ölçekleme Modu:",
"None": "Bilinmeyen",
"Local Scaling": "Yerel Ölçeklendirme",
"Remote Resizing": "Uzaktan Yeniden Boyutlandırma",
"Advanced": "Gelişmiş",
"Repeater ID:": "Tekralayıcı ID:",
"WebSocket": "WebSocket",
"Encrypt": "Şifrele",
"Host:": "Ana makine:",
"Port:": "Port:",
"Path:": "Yol:",
"Automatic Reconnect": "Otomatik Yeniden Bağlan",
"Reconnect Delay (ms):": "Yeniden Bağlanma Süreci (ms):",
"Logging:": "Giriş yapılıyor:",
"Disconnect": "Bağlantıyı Kes",
"Connect": "Bağlan",
"Password:": "Parola:",
"Cancel": "Vazgeç",
"Canvas not supported.": "Tuval desteklenmiyor."
}
+69
View File
@@ -0,0 +1,69 @@
{
"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": "取消"
}
+47 -45
View File
@@ -1,6 +1,6 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
@@ -10,36 +10,35 @@
* Localization Utilities
*/
export function Localizer() {
// Currently configured language
this.language = 'en';
export class Localizer {
constructor() {
// Currently configured language
this.language = 'en';
// Current dictionary of translations
this.dictionary = undefined;
}
// Current dictionary of translations
this.dictionary = undefined;
}
Localizer.prototype = {
// Configure suitable language based on user preferences
setup: function (supportedLanguages) {
var userLanguages;
setup(supportedLanguages) {
this.language = 'en'; // Default: US English
/*
* Navigator.languages only available in Chrome (32+) and FireFox (32+)
* Fall back to navigator.language for other browsers
*/
let userLanguages;
if (typeof window.navigator.languages == 'object') {
userLanguages = window.navigator.languages;
} else {
userLanguages = [navigator.language || navigator.userLanguage];
}
for (var i = 0;i < userLanguages.length;i++) {
var userLang = userLanguages[i];
userLang = userLang.toLowerCase();
userLang = userLang.replace("_", "-");
userLang = userLang.split("-");
for (let i = 0;i < userLanguages.length;i++) {
const userLang = userLanguages[i]
.toLowerCase()
.replace("_", "-")
.split("-");
// Built-in default?
if ((userLang[0] === 'en') &&
@@ -48,66 +47,69 @@ Localizer.prototype = {
}
// First pass: perfect match
for (var j = 0;j < supportedLanguages.length;j++) {
var supLang = supportedLanguages[j];
supLang = supLang.toLowerCase();
supLang = supLang.replace("_", "-");
supLang = supLang.split("-");
for (let j = 0; j < supportedLanguages.length; j++) {
const supLang = supportedLanguages[j]
.toLowerCase()
.replace("_", "-")
.split("-");
if (userLang[0] !== supLang[0])
if (userLang[0] !== supLang[0]) {
continue;
if (userLang[1] !== supLang[1])
}
if (userLang[1] !== supLang[1]) {
continue;
}
this.language = supportedLanguages[j];
return;
}
// Second pass: fallback
for (var j = 0;j < supportedLanguages.length;j++) {
supLang = supportedLanguages[j];
supLang = supLang.toLowerCase();
supLang = supLang.replace("_", "-");
supLang = supLang.split("-");
for (let j = 0;j < supportedLanguages.length;j++) {
const supLang = supportedLanguages[j]
.toLowerCase()
.replace("_", "-")
.split("-");
if (userLang[0] !== supLang[0])
if (userLang[0] !== supLang[0]) {
continue;
if (supLang[1] !== undefined)
}
if (supLang[1] !== undefined) {
continue;
}
this.language = supportedLanguages[j];
return;
}
}
},
}
// Retrieve localised text
get: function (id) {
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: function () {
var self = this;
translateDOM() {
const self = this;
function process(elem, enabled) {
function isAnyOf(searchElement, items) {
return items.indexOf(searchElement) !== -1;
}
function translateAttribute(elem, attr) {
var str = elem.getAttribute(attr);
str = self.get(str);
const str = self.get(elem.getAttribute(attr));
elem.setAttribute(attr, str);
}
function translateTextNode(node) {
var str = node.data.trim();
str = self.get(str);
const str = self.get(node.data.trim());
node.data = str;
}
@@ -134,7 +136,7 @@ Localizer.prototype = {
}
if (elem.hasAttribute("label") &&
isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP",
"OPTION", "TRACK"])) {
"OPTION", "TRACK"])) {
translateAttribute(elem, "label");
}
// FIXME: Should update "lang"
@@ -152,8 +154,8 @@ Localizer.prototype = {
}
}
for (var i = 0;i < elem.childNodes.length;i++) {
var node = elem.childNodes[i];
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) {
@@ -163,8 +165,8 @@ Localizer.prototype = {
}
process(document.body, true);
},
};
}
}
export var l10n = new Localizer();
export const l10n = new Localizer();
export default l10n.get.bind(l10n);
+87 -19
View File
@@ -1,8 +1,6 @@
/*
* noVNC base CSS
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2016 Samuel Mannehed for Cendio AB
* Copyright (C) 2016 Pierre Ossman for Cendio AB
* Copyright (C) 2019 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).
*/
@@ -85,8 +83,20 @@ html {
* ----------------------------------------
*/
input[type=input], input[type=password], input[type=number],
input:not([type]), textarea {
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 {
/* Disable default rendering */
-webkit-appearance: none;
-moz-appearance: none;
@@ -100,7 +110,11 @@ input:not([type]), textarea {
background: linear-gradient(to top, rgb(255, 255, 255) 80%, rgb(240, 240, 240));
}
input[type=button], input[type=submit], select {
input[type=button],
input[type=color],
input[type=reset],
input[type=submit],
select {
/* Disable default rendering */
-webkit-appearance: none;
-moz-appearance: none;
@@ -118,7 +132,10 @@ input[type=button], input[type=submit], select {
vertical-align: middle;
}
input[type=button], input[type=submit] {
input[type=button],
input[type=color],
input[type=reset],
input[type=submit] {
padding-left: 20px;
padding-right: 20px;
}
@@ -128,35 +145,72 @@ option {
background: white;
}
input[type=input]:focus, input[type=password]:focus,
input:not([type]):focus, input[type=button]:focus,
input:not([type]):focus,
input[type=button]:focus,
input[type=color]:focus,
input[type=date]:focus,
input[type=datetime-local]:focus,
input[type=email]:focus,
input[type=month]:focus,
input[type=number]:focus,
input[type=password]:focus,
input[type=reset]:focus,
input[type=search]:focus,
input[type=submit]:focus,
textarea:focus, select:focus {
input[type=tel]:focus,
input[type=text]:focus,
input[type=time]:focus,
input[type=url]:focus,
input[type=week]:focus,
select:focus,
textarea:focus {
box-shadow: 0px 0px 3px rgba(74, 144, 217, 0.5);
border-color: rgb(74, 144, 217);
outline: none;
}
input[type=button]::-moz-focus-inner,
input[type=color]::-moz-focus-inner,
input[type=reset]::-moz-focus-inner,
input[type=submit]::-moz-focus-inner {
border: none;
}
input[type=input]:disabled, input[type=password]:disabled,
input:not([type]):disabled, input[type=button]:disabled,
input[type=submit]:disabled, input[type=number]:disabled,
textarea:disabled, select:disabled {
input:not([type]):disabled,
input[type=button]:disabled,
input[type=color]:disabled,
input[type=date]:disabled,
input[type=datetime-local]:disabled,
input[type=email]:disabled,
input[type=month]:disabled,
input[type=number]:disabled,
input[type=password]:disabled,
input[type=reset]:disabled,
input[type=search]:disabled,
input[type=submit]:disabled,
input[type=tel]:disabled,
input[type=text]:disabled,
input[type=time]:disabled,
input[type=url]:disabled,
input[type=week]:disabled,
select:disabled,
textarea:disabled {
color: rgb(128, 128, 128);
background: rgb(240, 240, 240);
}
input[type=button]:active, input[type=submit]:active,
input[type=button]:active,
input[type=color]:active,
input[type=reset]:active,
input[type=submit]:active,
select:active {
border-bottom-width: 1px;
margin-top: 3px;
}
:root:not(.noVNC_touch) input[type=button]:hover:not(:disabled),
:root:not(.noVNC_touch) input[type=color]:hover:not(:disabled),
:root:not(.noVNC_touch) input[type=reset]:hover:not(:disabled),
:root:not(.noVNC_touch) input[type=submit]:hover:not(:disabled),
:root:not(.noVNC_touch) select:hover:not(:disabled) {
background: linear-gradient(to top, rgb(255, 255, 255), rgb(250, 250, 250));
@@ -581,7 +635,7 @@ select:active {
}
/* Extra manual keys */
:root:not(.noVNC_connected) #noVNC_extra_keys {
:root:not(.noVNC_connected) #noVNC_toggle_extra_keys_button {
display: none;
}
@@ -633,6 +687,16 @@ select:active {
width: 100px;
}
/* Version */
.noVNC_version_wrapper {
font-size: small;
}
.noVNC_version {
margin-left: 1rem;
}
/* Connection Controls */
:root:not(.noVNC_connected) #noVNC_disconnect_button {
display: none;
@@ -782,19 +846,23 @@ select:active {
* ----------------------------------------
*/
#noVNC_password_dlg {
#noVNC_credentials_dlg {
position: relative;
transform: translateY(-50px);
}
#noVNC_password_dlg.noVNC_open {
#noVNC_credentials_dlg.noVNC_open {
transform: translateY(0);
}
#noVNC_password_dlg ul {
#noVNC_credentials_dlg ul {
list-style: none;
margin: 0px;
padding: 0px;
}
.noVNC_hidden {
display: none;
}
/* ----------------------------------------
* Main Area
-63
View File
@@ -1,63 +0,0 @@
/*
* noVNC auto CSS
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2017 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;
background-color:#313131;
border-bottom-right-radius: 800px 600px;
height:100%;
display: flex;
flex-direction: column;
}
html {
background-color:#494949;
height:100%;
}
#noVNC_status_bar {
width: 100%;
display:flex;
justify-content: space-between;
}
#noVNC_status {
color: #fff;
font: bold 12px Helvetica;
margin: auto;
}
.noVNC_status_normal {
background: linear-gradient(#b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%);
}
.noVNC_status_error {
background: linear-gradient(#c83737 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%);
}
.noVNC_status_warn {
background: linear-gradient(#b4b41e 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%);
}
.noNVC_shown {
display: inline;
}
.noVNC_hidden {
display: none;
}
#noVNC_left_dummy_elem {
flex: 1;
}
#noVNC_buttons {
padding: 1px;
flex: 1;
display: flex;
justify-content: flex-end;
}
+427 -380
View File
File diff suppressed because it is too large Load Diff
+89 -133
View File
@@ -1,70 +1,82 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2013 NTT corp.
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
import { init_logging as main_init_logging } from '../core/util/logging.js';
import { initLogging as mainInitLogging } from '../core/util/logging.js';
// init log level reading the logging HTTP param
export function init_logging (level) {
export function initLogging(level) {
"use strict";
if (typeof level !== "undefined") {
main_init_logging(level);
mainInitLogging(level);
} else {
var param = document.location.href.match(/logging=([A-Za-z0-9\._\-]*)/);
main_init_logging(param || undefined);
const param = document.location.href.match(/logging=([A-Za-z0-9._-]*)/);
mainInitLogging(param || undefined);
}
};
}
// Read a query string variable
export function getQueryVar (name, defVal) {
// A URL with a query parameter can look like this (But will most probably get logged on the http server):
// https://www.example.com?myqueryparam=myvalue
//
// For privacy (Using a hastag #, the parameters will not be sent to the server)
// the url can be requested in the following way:
// https://www.example.com#myqueryparam=myvalue&password=secreatvalue
//
// Even Mixing public and non public parameters will work:
// https://www.example.com?nonsecretparam=example.com#password=secreatvalue
export function getQueryVar(name, defVal) {
"use strict";
var re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
match = document.location.href.match(re);
const re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
match = ''.concat(document.location.href, window.location.hash).match(re);
if (typeof defVal === 'undefined') { defVal = null; }
if (match) {
return decodeURIComponent(match[1]);
} else {
return defVal;
}
};
return defVal;
}
// Read a hash fragment variable
export function getHashVar (name, defVal) {
export function getHashVar(name, defVal) {
"use strict";
var re = new RegExp('.*[&#]' + name + '=([^&]*)'),
const re = new RegExp('.*[&#]' + name + '=([^&]*)'),
match = document.location.hash.match(re);
if (typeof defVal === 'undefined') { defVal = null; }
if (match) {
return decodeURIComponent(match[1]);
} else {
return defVal;
}
};
return defVal;
}
// Read a variable from the fragment or the query string
// Fragment takes precedence
export function getConfigVar (name, defVal) {
export function getConfigVar(name, defVal) {
"use strict";
var val = getHashVar(name);
const val = getHashVar(name);
if (val === null) {
val = getQueryVar(name, defVal);
return getQueryVar(name, defVal);
}
return val;
};
}
/*
* Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
*/
// No days means only for this browser session
export function createCookie (name, value, days) {
export function createCookie(name, value, days) {
"use strict";
var date, expires;
let date, expires;
if (days) {
date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
@@ -73,158 +85,102 @@ export function createCookie (name, value, days) {
expires = "";
}
var secure;
let secure;
if (document.location.protocol === "https:") {
secure = "; secure";
} else {
secure = "";
}
document.cookie = name + "=" + value + expires + "; path=/" + secure;
};
}
export function readCookie (name, defaultValue) {
export function readCookie(name, defaultValue) {
"use strict";
var nameEQ = name + "=",
ca = document.cookie.split(';');
const nameEQ = name + "=";
const ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i += 1) {
var c = ca[i];
while (c.charAt(0) === ' ') { c = c.substring(1, c.length); }
if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length, c.length); }
for (let i = 0; i < ca.length; i += 1) {
let c = ca[i];
while (c.charAt(0) === ' ') {
c = c.substring(1, c.length);
}
if (c.indexOf(nameEQ) === 0) {
return c.substring(nameEQ.length, c.length);
}
}
return (typeof defaultValue !== 'undefined') ? defaultValue : null;
};
export function eraseCookie (name) {
return (typeof defaultValue !== 'undefined') ? defaultValue : null;
}
export function eraseCookie(name) {
"use strict";
createCookie(name, "", -1);
};
}
/*
* Setting handling.
*/
var settings = {};
let settings = {};
export function initSettings (callback /*, ...callbackArgs */) {
"use strict";
var callbackArgs = Array.prototype.slice.call(arguments, 1);
if (window.chrome && window.chrome.storage) {
window.chrome.storage.sync.get(function (cfg) {
settings = cfg;
if (callback) {
callback.apply(this, callbackArgs);
}
});
} else {
// No-op
if (callback) {
callback.apply(this, callbackArgs);
}
export function initSettings() {
if (!window.chrome || !window.chrome.storage) {
settings = {};
return Promise.resolve();
}
};
return new Promise(resolve => window.chrome.storage.sync.get(resolve))
.then((cfg) => { settings = cfg; });
}
// Update the settings cache, but do not write to permanent storage
export function setSetting(name, value) {
settings[name] = value;
}
// No days means only for this browser session
export function writeSetting (name, value) {
export function writeSetting(name, value) {
"use strict";
if (settings[name] === value) return;
settings[name] = value;
if (window.chrome && window.chrome.storage) {
if (settings[name] !== value) {
settings[name] = value;
window.chrome.storage.sync.set(settings);
}
window.chrome.storage.sync.set(settings);
} else {
localStorage.setItem(name, value);
}
};
}
export function readSetting (name, defaultValue) {
export function readSetting(name, defaultValue) {
"use strict";
var value;
if (window.chrome && window.chrome.storage) {
let value;
if ((name in settings) || (window.chrome && window.chrome.storage)) {
value = settings[name];
} else {
value = localStorage.getItem(name);
settings[name] = value;
}
if (typeof value === "undefined") {
value = null;
}
if (value === null && typeof defaultValue !== "undefined") {
return defaultValue;
} else {
return value;
}
};
export function eraseSetting (name) {
return value;
}
export function eraseSetting(name) {
"use strict";
// Deleting here means that next time the setting is read when using local
// storage, it will be pulled from local storage again.
// If the setting in local storage is changed (e.g. in another tab)
// between this delete and the next read, it could lead to an unexpected
// value change.
delete settings[name];
if (window.chrome && window.chrome.storage) {
window.chrome.storage.sync.remove(name);
delete settings[name];
} else {
localStorage.removeItem(name);
}
};
export function injectParamIfMissing (path, param, value) {
// force pretend that we're dealing with a relative path
// (assume that we wanted an extra if we pass one in)
path = "/" + path;
var elem = document.createElement('a');
elem.href = path;
var param_eq = encodeURIComponent(param) + "=";
var query;
if (elem.search) {
query = elem.search.slice(1).split('&');
} else {
query = [];
}
if (!query.some(function (v) { return v.startsWith(param_eq); })) {
query.push(param_eq + encodeURIComponent(value));
elem.search = "?" + query.join("&");
}
// some browsers (e.g. IE11) may occasionally omit the leading slash
// in the elem.pathname string. Handle that case gracefully.
if (elem.pathname.charAt(0) == "/") {
return elem.pathname.slice(1) + elem.search + elem.hash;
} else {
return elem.pathname + elem.search + elem.hash;
}
};
// sadly, we can't use the Fetch API until we decide to drop
// IE11 support or polyfill promises and fetch in IE11.
// resolve will receive an object on success, while reject
// will receive either an event or an error on failure.
export function fetchJSON(path, resolve, reject) {
// NB: IE11 doesn't support JSON as a responseType
var req = new XMLHttpRequest();
req.open('GET', path);
req.onload = function () {
if (req.status === 200) {
try {
var resObj = JSON.parse(req.responseText);
} catch (err) {
reject(err);
return;
}
resolve(resObj);
} else {
reject(new Error("XHR got non-200 status while trying to load '" + path + "': " + req.status));
}
};
req.onerror = function (evt) {
reject(new Error("XHR encountered an error while trying to load '" + path + "': " + evt.message));
};
req.ontimeout = function (evt) {
reject(new Error("XHR timed out while trying to load '" + path + "'"));
};
req.send();
}
+35 -41
View File
@@ -8,45 +8,43 @@ import * as Log from './util/logging.js';
export default {
/* Convert data (an array of integers) to a Base64 string. */
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
base64Pad : '=',
toBase64Table: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
base64Pad: '=',
encode: function (data) {
encode(data) {
"use strict";
var result = '';
var toBase64Table = this.toBase64Table;
var length = data.length;
var lengthpad = (length % 3);
let result = '';
const length = data.length;
const lengthpad = (length % 3);
// Convert every three bytes to 4 ascii characters.
for (var i = 0; i < (length - 2); i += 3) {
result += toBase64Table[data[i] >> 2];
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
result += toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
result += toBase64Table[data[i + 2] & 0x3f];
for (let i = 0; i < (length - 2); i += 3) {
result += this.toBase64Table[data[i] >> 2];
result += this.toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
result += this.toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
result += this.toBase64Table[data[i + 2] & 0x3f];
}
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
var j = 0;
const j = length - lengthpad;
if (lengthpad === 2) {
j = length - lengthpad;
result += toBase64Table[data[j] >> 2];
result += toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)];
result += toBase64Table[(data[j + 1] & 0x0f) << 2];
result += toBase64Table[64];
result += this.toBase64Table[data[j] >> 2];
result += this.toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)];
result += this.toBase64Table[(data[j + 1] & 0x0f) << 2];
result += this.toBase64Table[64];
} else if (lengthpad === 1) {
j = length - lengthpad;
result += toBase64Table[data[j] >> 2];
result += toBase64Table[(data[j] & 0x03) << 4];
result += toBase64Table[64];
result += toBase64Table[64];
result += this.toBase64Table[data[j] >> 2];
result += this.toBase64Table[(data[j] & 0x03) << 4];
result += this.toBase64Table[64];
result += this.toBase64Table[64];
}
return result;
},
/* Convert Base64 data to a string */
toBinaryTable : [
/* eslint-disable comma-spacing */
toBinaryTable: [
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
@@ -56,27 +54,23 @@ export default {
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
],
/* eslint-enable comma-spacing */
decode: function (data, offset) {
"use strict";
offset = typeof(offset) !== 'undefined' ? offset : 0;
var toBinaryTable = this.toBinaryTable;
var base64Pad = this.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; }
decode(data, offset = 0) {
let dataLength = data.indexOf('=') - offset;
if (dataLength < 0) { dataLength = data.length - offset; }
/* Every four characters is 3 resulting numbers */
result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5);
result = new Array(result_length);
const resultLength = (dataLength >> 2) * 3 + Math.floor((dataLength % 4) / 1.5);
const result = new Array(resultLength);
// Convert one by one.
for (var idx = 0, i = offset; i < data.length; i++) {
var c = toBinaryTable[data.charCodeAt(i) & 0x7f];
var padding = (data.charAt(i) === base64Pad);
let leftbits = 0; // number of bits decoded, but yet to be appended
let leftdata = 0; // bits decoded, but yet to be appended
for (let idx = 0, i = offset; i < data.length; i++) {
const c = this.toBinaryTable[data.charCodeAt(i) & 0x7f];
const padding = (data.charAt(i) === this.base64Pad);
// Skip illegal characters and whitespace
if (c === -1) {
Log.Error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
@@ -100,7 +94,7 @@ export default {
// If there are any bits left, the base64 string was corrupted
if (leftbits) {
err = new Error('Corrupted base64 string');
const err = new Error('Corrupted base64 string');
err.name = 'Base64-Error';
throw err;
}
+27
View File
@@ -0,0 +1,27 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
export default class CopyRectDecoder {
decodeRect(x, y, width, height, sock, display, depth) {
if (sock.rQwait("COPYRECT", 4)) {
return false;
}
let deltaX = sock.rQshift16();
let deltaY = sock.rQshift16();
if ((width === 0) || (height === 0)) {
return true;
}
display.copyImage(deltaX, deltaY, x, y, width, height);
return true;
}
}
+191
View File
@@ -0,0 +1,191 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
import * as Log from '../util/logging.js';
export default class HextileDecoder {
constructor() {
this._tiles = 0;
this._lastsubencoding = 0;
this._tileBuffer = new Uint8Array(16 * 16 * 4);
}
decodeRect(x, y, width, height, sock, display, depth) {
if (this._tiles === 0) {
this._tilesX = Math.ceil(width / 16);
this._tilesY = Math.ceil(height / 16);
this._totalTiles = this._tilesX * this._tilesY;
this._tiles = this._totalTiles;
}
while (this._tiles > 0) {
let bytes = 1;
if (sock.rQwait("HEXTILE", bytes)) {
return false;
}
let rQ = sock.rQ;
let rQi = sock.rQi;
let subencoding = rQ[rQi]; // Peek
if (subencoding > 30) { // Raw
throw new Error("Illegal hextile subencoding (subencoding: " +
subencoding + ")");
}
const currTile = this._totalTiles - this._tiles;
const tileX = currTile % this._tilesX;
const tileY = Math.floor(currTile / this._tilesX);
const tx = x + tileX * 16;
const ty = y + tileY * 16;
const tw = Math.min(16, (x + width) - tx);
const th = Math.min(16, (y + height) - ty);
// Figure out how much we are expecting
if (subencoding & 0x01) { // Raw
bytes += tw * th * 4;
} else {
if (subencoding & 0x02) { // Background
bytes += 4;
}
if (subencoding & 0x04) { // Foreground
bytes += 4;
}
if (subencoding & 0x08) { // AnySubrects
bytes++; // Since we aren't shifting it off
if (sock.rQwait("HEXTILE", bytes)) {
return false;
}
let subrects = rQ[rQi + bytes - 1]; // Peek
if (subencoding & 0x10) { // SubrectsColoured
bytes += subrects * (4 + 2);
} else {
bytes += subrects * 2;
}
}
}
if (sock.rQwait("HEXTILE", bytes)) {
return false;
}
// We know the encoding and have a whole tile
rQi++;
if (subencoding === 0) {
if (this._lastsubencoding & 0x01) {
// Weird: ignore blanks are RAW
Log.Debug(" Ignoring blank after RAW");
} else {
display.fillRect(tx, ty, tw, th, this._background);
}
} else if (subencoding & 0x01) { // Raw
let pixels = tw * th;
// Max sure the image is fully opaque
for (let i = 0;i < pixels;i++) {
rQ[rQi + i * 4 + 3] = 255;
}
display.blitImage(tx, ty, tw, th, rQ, rQi);
rQi += bytes - 1;
} else {
if (subencoding & 0x02) { // Background
this._background = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
rQi += 4;
}
if (subencoding & 0x04) { // Foreground
this._foreground = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
rQi += 4;
}
this._startTile(tx, ty, tw, th, this._background);
if (subencoding & 0x08) { // AnySubrects
let subrects = rQ[rQi];
rQi++;
for (let s = 0; s < subrects; s++) {
let color;
if (subencoding & 0x10) { // SubrectsColoured
color = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
rQi += 4;
} else {
color = this._foreground;
}
const xy = rQ[rQi];
rQi++;
const sx = (xy >> 4);
const sy = (xy & 0x0f);
const wh = rQ[rQi];
rQi++;
const sw = (wh >> 4) + 1;
const sh = (wh & 0x0f) + 1;
this._subTile(sx, sy, sw, sh, color);
}
}
this._finishTile(display);
}
sock.rQi = rQi;
this._lastsubencoding = subencoding;
this._tiles--;
}
return true;
}
// start updating a tile
_startTile(x, y, width, height, color) {
this._tileX = x;
this._tileY = y;
this._tileW = width;
this._tileH = height;
const red = color[0];
const green = color[1];
const blue = color[2];
const data = this._tileBuffer;
for (let i = 0; i < width * height * 4; i += 4) {
data[i] = red;
data[i + 1] = green;
data[i + 2] = blue;
data[i + 3] = 255;
}
}
// update sub-rectangle of the current tile
_subTile(x, y, w, h, color) {
const red = color[0];
const green = color[1];
const blue = color[2];
const xend = x + w;
const yend = y + h;
const data = this._tileBuffer;
const width = this._tileW;
for (let j = y; j < yend; j++) {
for (let i = x; i < xend; i++) {
const p = (i + (j * width)) * 4;
data[p] = red;
data[p + 1] = green;
data[p + 2] = blue;
data[p + 3] = 255;
}
}
}
// draw the current tile to the screen
_finishTile(display) {
display.blitImage(this._tileX, this._tileY,
this._tileW, this._tileH,
this._tileBuffer, 0);
}
}
+66
View File
@@ -0,0 +1,66 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
export default class RawDecoder {
constructor() {
this._lines = 0;
}
decodeRect(x, y, width, height, sock, display, depth) {
if ((width === 0) || (height === 0)) {
return true;
}
if (this._lines === 0) {
this._lines = height;
}
const pixelSize = depth == 8 ? 1 : 4;
const bytesPerLine = width * pixelSize;
if (sock.rQwait("RAW", bytesPerLine)) {
return false;
}
const curY = y + (height - this._lines);
const currHeight = Math.min(this._lines,
Math.floor(sock.rQlen / bytesPerLine));
const pixels = width * currHeight;
let data = sock.rQ;
let index = sock.rQi;
// Convert data if needed
if (depth == 8) {
const newdata = new Uint8Array(pixels * 4);
for (let i = 0; i < pixels; i++) {
newdata[i * 4 + 0] = ((data[index + i] >> 0) & 0x3) * 255 / 3;
newdata[i * 4 + 1] = ((data[index + i] >> 2) & 0x3) * 255 / 3;
newdata[i * 4 + 2] = ((data[index + i] >> 4) & 0x3) * 255 / 3;
newdata[i * 4 + 3] = 255;
}
data = newdata;
index = 0;
}
// Max sure the image is fully opaque
for (let i = 0; i < pixels; i++) {
data[i * 4 + 3] = 255;
}
display.blitImage(x, curY, width, currHeight, data, index);
sock.rQskipBytes(currHeight * bytesPerLine);
this._lines -= currHeight;
if (this._lines > 0) {
return false;
}
return true;
}
}
+44
View File
@@ -0,0 +1,44 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
export default class RREDecoder {
constructor() {
this._subrects = 0;
}
decodeRect(x, y, width, height, sock, display, depth) {
if (this._subrects === 0) {
if (sock.rQwait("RRE", 4 + 4)) {
return false;
}
this._subrects = sock.rQshift32();
let color = sock.rQshiftBytes(4); // Background
display.fillRect(x, y, width, height, color);
}
while (this._subrects > 0) {
if (sock.rQwait("RRE", 4 + 8)) {
return false;
}
let color = sock.rQshiftBytes(4);
let sx = sock.rQshift16();
let sy = sock.rQshift16();
let swidth = sock.rQshift16();
let sheight = sock.rQshift16();
display.fillRect(x + sx, y + sy, swidth, sheight, color);
this._subrects--;
}
return true;
}
}
+331
View File
@@ -0,0 +1,331 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
import * as Log from '../util/logging.js';
import Inflator from "../inflator.js";
export default class TightDecoder {
constructor() {
this._ctl = null;
this._filter = null;
this._numColors = 0;
this._palette = new Uint8Array(1024); // 256 * 4 (max palette size * max bytes-per-pixel)
this._len = 0;
this._zlibs = [];
for (let i = 0; i < 4; i++) {
this._zlibs[i] = new Inflator();
}
}
decodeRect(x, y, width, height, sock, display, depth) {
if (this._ctl === null) {
if (sock.rQwait("TIGHT compression-control", 1)) {
return false;
}
this._ctl = sock.rQshift8();
// Reset streams if the server requests it
for (let i = 0; i < 4; i++) {
if ((this._ctl >> i) & 1) {
this._zlibs[i].reset();
Log.Info("Reset zlib stream " + i);
}
}
// Figure out filter
this._ctl = this._ctl >> 4;
}
let ret;
if (this._ctl === 0x08) {
ret = this._fillRect(x, y, width, height,
sock, display, depth);
} else if (this._ctl === 0x09) {
ret = this._jpegRect(x, y, width, height,
sock, display, depth);
} else if (this._ctl === 0x0A) {
ret = this._pngRect(x, y, width, height,
sock, display, depth);
} else if ((this._ctl & 0x08) == 0) {
ret = this._basicRect(this._ctl, x, y, width, height,
sock, display, depth);
} else {
throw new Error("Illegal tight compression received (ctl: " +
this._ctl + ")");
}
if (ret) {
this._ctl = null;
}
return ret;
}
_fillRect(x, y, width, height, sock, display, depth) {
if (sock.rQwait("TIGHT", 3)) {
return false;
}
const rQi = sock.rQi;
const rQ = sock.rQ;
display.fillRect(x, y, width, height,
[rQ[rQi], rQ[rQi + 1], rQ[rQi + 2]], false);
sock.rQskipBytes(3);
return true;
}
_jpegRect(x, y, width, height, sock, display, depth) {
let data = this._readData(sock);
if (data === null) {
return false;
}
display.imageRect(x, y, width, height, "image/jpeg", data);
return true;
}
_pngRect(x, y, width, height, sock, display, depth) {
throw new Error("PNG received in standard Tight rect");
}
_basicRect(ctl, x, y, width, height, sock, display, depth) {
if (this._filter === null) {
if (ctl & 0x4) {
if (sock.rQwait("TIGHT", 1)) {
return false;
}
this._filter = sock.rQshift8();
} else {
// Implicit CopyFilter
this._filter = 0;
}
}
let streamId = ctl & 0x3;
let ret;
switch (this._filter) {
case 0: // CopyFilter
ret = this._copyFilter(streamId, x, y, width, height,
sock, display, depth);
break;
case 1: // PaletteFilter
ret = this._paletteFilter(streamId, x, y, width, height,
sock, display, depth);
break;
case 2: // GradientFilter
ret = this._gradientFilter(streamId, x, y, width, height,
sock, display, depth);
break;
default:
throw new Error("Illegal tight filter received (ctl: " +
this._filter + ")");
}
if (ret) {
this._filter = null;
}
return ret;
}
_copyFilter(streamId, x, y, width, height, sock, display, depth) {
const uncompressedSize = width * height * 3;
let data;
if (uncompressedSize === 0) {
return true;
}
if (uncompressedSize < 12) {
if (sock.rQwait("TIGHT", uncompressedSize)) {
return false;
}
data = sock.rQshiftBytes(uncompressedSize);
} else {
data = this._readData(sock);
if (data === null) {
return false;
}
this._zlibs[streamId].setInput(data);
data = this._zlibs[streamId].inflate(uncompressedSize);
this._zlibs[streamId].setInput(null);
}
let rgbx = new Uint8Array(width * height * 4);
for (let i = 0, j = 0; i < width * height * 4; i += 4, j += 3) {
rgbx[i] = data[j];
rgbx[i + 1] = data[j + 1];
rgbx[i + 2] = data[j + 2];
rgbx[i + 3] = 255; // Alpha
}
display.blitImage(x, y, width, height, rgbx, 0, false);
return true;
}
_paletteFilter(streamId, x, y, width, height, sock, display, depth) {
if (this._numColors === 0) {
if (sock.rQwait("TIGHT palette", 1)) {
return false;
}
const numColors = sock.rQpeek8() + 1;
const paletteSize = numColors * 3;
if (sock.rQwait("TIGHT palette", 1 + paletteSize)) {
return false;
}
this._numColors = numColors;
sock.rQskipBytes(1);
sock.rQshiftTo(this._palette, paletteSize);
}
const bpp = (this._numColors <= 2) ? 1 : 8;
const rowSize = Math.floor((width * bpp + 7) / 8);
const uncompressedSize = rowSize * height;
let data;
if (uncompressedSize === 0) {
return true;
}
if (uncompressedSize < 12) {
if (sock.rQwait("TIGHT", uncompressedSize)) {
return false;
}
data = sock.rQshiftBytes(uncompressedSize);
} else {
data = this._readData(sock);
if (data === null) {
return false;
}
this._zlibs[streamId].setInput(data);
data = this._zlibs[streamId].inflate(uncompressedSize);
this._zlibs[streamId].setInput(null);
}
// Convert indexed (palette based) image data to RGB
if (this._numColors == 2) {
this._monoRect(x, y, width, height, data, this._palette, display);
} else {
this._paletteRect(x, y, width, height, data, this._palette, display);
}
this._numColors = 0;
return true;
}
_monoRect(x, y, width, height, data, palette, display) {
// Convert indexed (palette based) image data to RGB
// TODO: reduce number of calculations inside loop
const dest = this._getScratchBuffer(width * height * 4);
const w = Math.floor((width + 7) / 8);
const w1 = Math.floor(width / 8);
for (let y = 0; y < height; y++) {
let dp, sp, x;
for (x = 0; x < w1; x++) {
for (let b = 7; b >= 0; b--) {
dp = (y * width + x * 8 + 7 - b) * 4;
sp = (data[y * w + x] >> b & 1) * 3;
dest[dp] = palette[sp];
dest[dp + 1] = palette[sp + 1];
dest[dp + 2] = palette[sp + 2];
dest[dp + 3] = 255;
}
}
for (let b = 7; b >= 8 - width % 8; b--) {
dp = (y * width + x * 8 + 7 - b) * 4;
sp = (data[y * w + x] >> b & 1) * 3;
dest[dp] = palette[sp];
dest[dp + 1] = palette[sp + 1];
dest[dp + 2] = palette[sp + 2];
dest[dp + 3] = 255;
}
}
display.blitImage(x, y, width, height, dest, 0, false);
}
_paletteRect(x, y, width, height, data, palette, display) {
// Convert indexed (palette based) image data to RGB
const dest = this._getScratchBuffer(width * height * 4);
const total = width * height * 4;
for (let i = 0, j = 0; i < total; i += 4, j++) {
const sp = data[j] * 3;
dest[i] = palette[sp];
dest[i + 1] = palette[sp + 1];
dest[i + 2] = palette[sp + 2];
dest[i + 3] = 255;
}
display.blitImage(x, y, width, height, dest, 0, false);
}
_gradientFilter(streamId, x, y, width, height, sock, display, depth) {
throw new Error("Gradient filter not implemented");
}
_readData(sock) {
if (this._len === 0) {
if (sock.rQwait("TIGHT", 3)) {
return null;
}
let byte;
byte = sock.rQshift8();
this._len = byte & 0x7f;
if (byte & 0x80) {
byte = sock.rQshift8();
this._len |= (byte & 0x7f) << 7;
if (byte & 0x80) {
byte = sock.rQshift8();
this._len |= byte << 14;
}
}
}
if (sock.rQwait("TIGHT", this._len)) {
return null;
}
let data = sock.rQshiftBytes(this._len);
this._len = 0;
return data;
}
_getScratchBuffer(size) {
if (!this._scratchBuffer || (this._scratchBuffer.length < size)) {
this._scratchBuffer = new Uint8Array(size);
}
return this._scratchBuffer;
}
}
+27
View File
@@ -0,0 +1,27 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
import TightDecoder from './tight.js';
export default class TightPNGDecoder extends TightDecoder {
_pngRect(x, y, width, height, sock, display, depth) {
let data = this._readData(sock);
if (data === null) {
return false;
}
display.imageRect(x, y, width, height, "image/png", data);
return true;
}
_basicRect(ctl, x, y, width, height, sock, display, depth) {
throw new Error("BasicCompression received in TightPNG rect");
}
}
+85
View File
@@ -0,0 +1,85 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2020 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
import { deflateInit, deflate } from "../vendor/pako/lib/zlib/deflate.js";
import { Z_FULL_FLUSH } from "../vendor/pako/lib/zlib/deflate.js";
import ZStream from "../vendor/pako/lib/zlib/zstream.js";
export default class Deflator {
constructor() {
this.strm = new ZStream();
this.chunkSize = 1024 * 10 * 10;
this.outputBuffer = new Uint8Array(this.chunkSize);
this.windowBits = 5;
deflateInit(this.strm, this.windowBits);
}
deflate(inData) {
/* eslint-disable camelcase */
this.strm.input = inData;
this.strm.avail_in = this.strm.input.length;
this.strm.next_in = 0;
this.strm.output = this.outputBuffer;
this.strm.avail_out = this.chunkSize;
this.strm.next_out = 0;
/* eslint-enable camelcase */
let lastRet = deflate(this.strm, Z_FULL_FLUSH);
let outData = new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
if (lastRet < 0) {
throw new Error("zlib deflate failed");
}
if (this.strm.avail_in > 0) {
// Read chunks until done
let chunks = [outData];
let totalLen = outData.length;
do {
/* eslint-disable camelcase */
this.strm.output = new Uint8Array(this.chunkSize);
this.strm.next_out = 0;
this.strm.avail_out = this.chunkSize;
/* eslint-enable camelcase */
lastRet = deflate(this.strm, Z_FULL_FLUSH);
if (lastRet < 0) {
throw new Error("zlib deflate failed");
}
let chunk = new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
totalLen += chunk.length;
chunks.push(chunk);
} while (this.strm.avail_in > 0);
// Combine chunks into a single data
let newData = new Uint8Array(totalLen);
let offset = 0;
for (let i = 0; i < chunks.length; i++) {
newData.set(chunks[i], offset);
offset += chunks[i].length;
}
outData = newData;
}
/* eslint-disable camelcase */
this.strm.input = null;
this.strm.avail_in = 0;
this.strm.next_in = 0;
/* eslint-enable camelcase */
return outData;
}
}
+90 -95
View File
@@ -75,84 +75,83 @@
* fine Java utilities: http://www.acme.com/java/
*/
export default function DES(passwd) {
"use strict";
/* eslint-disable comma-spacing */
// Tables, permutations, S-boxes, etc.
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 = [];
// 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];
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];
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];
// Set the key.
function setKeys(keyBlock) {
var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [],
raw0, raw1, rawi, KnLi;
/* eslint-enable comma-spacing */
for (j = 0, l = 56; j < 56; ++j, l -= 8) {
export default 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
m = l & 0x7;
pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
const m = l & 0x7;
pc1m[j] = ((password[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
}
for (i = 0; i < 16; ++i) {
m = i << 1;
n = m + 1;
for (let i = 0; i < 16; ++i) {
const m = i << 1;
const 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 (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 (j = 0; j < 24; ++j) {
for (let j = 0; j < 24; ++j) {
if (pcr[PC2[j]] !== 0) {
kn[m] |= 1 << (23 - j);
}
@@ -163,26 +162,26 @@ export default function DES(passwd) {
}
// 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;
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;
keys[KnLi] = (raw0 & 0x0003f000) << 12;
keys[KnLi] |= (raw0 & 0x0000003f) << 16;
keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;
keys[KnLi] |= (raw1 & 0x0000003f);
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
function enc8(text) {
var i = 0, b = text.slice(), fval, keysi = 0,
l, r, x; // left, right, accumulator
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++];
@@ -206,26 +205,26 @@ export default function DES(passwd) {
r ^= x;
l = (l << 1) | ((l >>> 31) & 1);
for (i = 0; i < 8; ++i) {
for (let i = 0, keysi = 0; i < 8; ++i) {
x = (r << 28) | (r >>> 4);
x ^= keys[keysi++];
fval = SP7[x & 0x3f];
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 ^ keys[keysi++];
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 ^= keys[keysi++];
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 ^ keys[keysi++];
x = l ^ this.keys[keysi++];
fval |= SP8[x & 0x0000003f];
fval |= SP6[(x >>> 8) & 0x3f];
fval |= SP4[(x >>> 16) & 0x3f];
@@ -261,11 +260,7 @@ export default function DES(passwd) {
}
// Encrypt 16 bytes of text using passwd as key
function encrypt(t) {
return enc8(t.slice(0, 8)).concat(enc8(t.slice(8, 16)));
encrypt(t) {
return this.enc8(t.slice(0, 8)).concat(this.enc8(t.slice(8, 16)));
}
setKeys(passwd); // Setup keys
return {'encrypt': encrypt}; // Public interface
}; // function DES
}
+205 -390
View File
@@ -1,7 +1,6 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2015 Samuel Mannehed for Cendio AB
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
@@ -9,111 +8,93 @@
import * as Log from './util/logging.js';
import Base64 from "./base64.js";
import { toSigned32bit } from './util/int.js';
export default function Display(target) {
this._drawCtx = null;
this._c_forceCanvas = false;
export default class Display {
constructor(target) {
this._drawCtx = null;
this._renderQ = []; // queue drawing actions for in-oder rendering
this._flushing = false;
this._renderQ = []; // queue drawing actions for in-oder rendering
this._flushing = false;
// the full frame buffer (logical canvas) size
this._fb_width = 0;
this._fb_height = 0;
// the full frame buffer (logical canvas) size
this._fbWidth = 0;
this._fbHeight = 0;
this._prevDrawStyle = "";
this._tile = null;
this._tile16x16 = null;
this._tile_x = 0;
this._tile_y = 0;
this._prevDrawStyle = "";
Log.Debug(">> Display.constructor");
Log.Debug(">> Display.constructor");
// The visible canvas
this._target = target;
// The visible canvas
this._target = target;
if (!this._target) {
throw new Error("Target must be set");
if (!this._target) {
throw new Error("Target must be set");
}
if (typeof this._target === 'string') {
throw new Error('target must be a DOM element');
}
if (!this._target.getContext) {
throw new Error("no getContext method");
}
this._targetCtx = this._target.getContext('2d');
// the visible canvas viewport (i.e. what actually gets seen)
this._viewportLoc = { 'x': 0, 'y': 0, 'w': this._target.width, 'h': this._target.height };
// The hidden canvas, where we do the actual rendering
this._backbuffer = document.createElement('canvas');
this._drawCtx = this._backbuffer.getContext('2d');
this._damageBounds = { left: 0, top: 0,
right: this._backbuffer.width,
bottom: this._backbuffer.height };
Log.Debug("User Agent: " + navigator.userAgent);
Log.Debug("<< Display.constructor");
// ===== PROPERTIES =====
this._scale = 1.0;
this._clipViewport = false;
// ===== EVENT HANDLERS =====
this.onflush = () => {}; // A flush request has finished
}
if (typeof this._target === 'string') {
throw new Error('target must be a DOM element');
}
if (!this._target.getContext) {
throw new Error("no getContext method");
}
this._targetCtx = this._target.getContext('2d');
// the visible canvas viewport (i.e. what actually gets seen)
this._viewportLoc = { 'x': 0, 'y': 0, 'w': this._target.width, 'h': this._target.height };
// The hidden canvas, where we do the actual rendering
this._backbuffer = document.createElement('canvas');
this._drawCtx = this._backbuffer.getContext('2d');
this._damageBounds = { left:0, top:0,
right: this._backbuffer.width,
bottom: this._backbuffer.height };
Log.Debug("User Agent: " + navigator.userAgent);
this.clear();
// Check canvas features
if (!('createImageData' in this._drawCtx)) {
throw new Error("Canvas does not support createImageData");
}
this._tile16x16 = this._drawCtx.createImageData(16, 16);
Log.Debug("<< Display.constructor");
};
var SUPPORTS_IMAGEDATA_CONSTRUCTOR = false;
try {
new ImageData(new Uint8ClampedArray(4), 1, 1);
SUPPORTS_IMAGEDATA_CONSTRUCTOR = true;
} catch (ex) {
// ignore failure
}
Display.prototype = {
// ===== PROPERTIES =====
_scale: 1.0,
get scale() { return this._scale; },
get scale() { return this._scale; }
set scale(scale) {
this._rescale(scale);
},
}
_clipViewport: false,
get clipViewport() { return this._clipViewport; },
get clipViewport() { return this._clipViewport; }
set clipViewport(viewport) {
this._clipViewport = viewport;
// May need to readjust the viewport dimensions
var vp = this._viewportLoc;
const vp = this._viewportLoc;
this.viewportChangeSize(vp.w, vp.h);
this.viewportChangePos(0, 0);
},
}
get width() {
return this._fb_width;
},
return this._fbWidth;
}
get height() {
return this._fb_height;
},
logo: null,
// ===== EVENT HANDLERS =====
onflush: function () {}, // A flush request has finished
return this._fbHeight;
}
// ===== PUBLIC METHODS =====
viewportChangePos: function (deltaX, deltaY) {
var vp = this._viewportLoc;
viewportChangePos(deltaX, deltaY) {
const vp = this._viewportLoc;
deltaX = Math.floor(deltaX);
deltaY = Math.floor(deltaY);
@@ -122,23 +103,23 @@ Display.prototype = {
deltaY = -vp.h;
}
var vx2 = vp.x + vp.w - 1;
var vy2 = vp.y + vp.h - 1;
const vx2 = vp.x + vp.w - 1;
const vy2 = vp.y + vp.h - 1;
// Position change
if (deltaX < 0 && vp.x + deltaX < 0) {
deltaX = -vp.x;
}
if (vx2 + deltaX >= this._fb_width) {
deltaX -= vx2 + deltaX - this._fb_width + 1;
if (vx2 + deltaX >= this._fbWidth) {
deltaX -= vx2 + deltaX - this._fbWidth + 1;
}
if (vp.y + deltaY < 0) {
deltaY = -vp.y;
}
if (vy2 + deltaY >= this._fb_height) {
deltaY -= (vy2 + deltaY - this._fb_height + 1);
if (vy2 + deltaY >= this._fbHeight) {
deltaY -= (vy2 + deltaY - this._fbHeight + 1);
}
if (deltaX === 0 && deltaY === 0) {
@@ -152,32 +133,35 @@ Display.prototype = {
this._damage(vp.x, vp.y, vp.w, vp.h);
this.flip();
},
}
viewportChangeSize: function(width, height) {
viewportChangeSize(width, height) {
if (!this._clipViewport ||
typeof(width) === "undefined" ||
typeof(height) === "undefined") {
Log.Debug("Setting viewport to full display region");
width = this._fb_width;
height = this._fb_height;
width = this._fbWidth;
height = this._fbHeight;
}
if (width > this._fb_width) {
width = this._fb_width;
width = Math.floor(width);
height = Math.floor(height);
if (width > this._fbWidth) {
width = this._fbWidth;
}
if (height > this._fb_height) {
height = this._fb_height;
if (height > this._fbHeight) {
height = this._fbHeight;
}
var vp = this._viewportLoc;
const vp = this._viewportLoc;
if (vp.w !== width || vp.h !== height) {
vp.w = width;
vp.h = height;
var canvas = this._target;
const canvas = this._target;
canvas.width = width;
canvas.height = height;
@@ -190,27 +174,33 @@ Display.prototype = {
// Update the visible size of the target canvas
this._rescale(this._scale);
}
},
}
absX: function (x) {
return x / this._scale + this._viewportLoc.x;
},
absX(x) {
if (this._scale === 0) {
return 0;
}
return toSigned32bit(x / this._scale + this._viewportLoc.x);
}
absY: function (y) {
return y / this._scale + this._viewportLoc.y;
},
absY(y) {
if (this._scale === 0) {
return 0;
}
return toSigned32bit(y / this._scale + this._viewportLoc.y);
}
resize: function (width, height) {
resize(width, height) {
this._prevDrawStyle = "";
this._fb_width = width;
this._fb_height = height;
this._fbWidth = width;
this._fbHeight = height;
var canvas = this._backbuffer;
const canvas = this._backbuffer;
if (canvas.width !== width || canvas.height !== height) {
// We have to save the canvas data since changing the size will clear it
var saveImg = null;
let saveImg = null;
if (canvas.width > 0 && canvas.height > 0) {
saveImg = this._drawCtx.getImageData(0, 0, canvas.width, canvas.height);
}
@@ -229,13 +219,13 @@ Display.prototype = {
// Readjust the viewport as it may be incorrectly sized
// and positioned
var vp = this._viewportLoc;
const vp = this._viewportLoc;
this.viewportChangeSize(vp.w, vp.h);
this.viewportChangePos(0, 0);
},
}
// Track what parts of the visible canvas that need updating
_damage: function(x, y, w, h) {
_damage(x, y, w, h) {
if (x < this._damageBounds.left) {
this._damageBounds.left = x;
}
@@ -248,25 +238,23 @@ Display.prototype = {
if ((y + h) > this._damageBounds.bottom) {
this._damageBounds.bottom = y + h;
}
},
}
// Update the visible canvas with the contents of the
// rendering canvas
flip: function(from_queue) {
if (this._renderQ.length !== 0 && !from_queue) {
this._renderQ_push({
flip(fromQueue) {
if (this._renderQ.length !== 0 && !fromQueue) {
this._renderQPush({
'type': 'flip'
});
} else {
var x, y, vx, vy, w, h;
let x = this._damageBounds.left;
let y = this._damageBounds.top;
let w = this._damageBounds.right - x;
let h = this._damageBounds.bottom - y;
x = this._damageBounds.left;
y = this._damageBounds.top;
w = this._damageBounds.right - x;
h = this._damageBounds.bottom - y;
vx = x - this._viewportLoc.x;
vy = y - this._viewportLoc.y;
let vx = x - this._viewportLoc.x;
let vy = y - this._viewportLoc.y;
if (vx < 0) {
w += vx;
@@ -298,34 +286,23 @@ Display.prototype = {
this._damageBounds.left = this._damageBounds.top = 65535;
this._damageBounds.right = this._damageBounds.bottom = 0;
}
},
}
clear: function () {
if (this._logo) {
this.resize(this._logo.width, this._logo.height);
this.imageRect(0, 0, this._logo.type, this._logo.data);
} else {
this.resize(240, 20);
this._drawCtx.clearRect(0, 0, this._fb_width, this._fb_height);
}
this.flip();
},
pending: function() {
pending() {
return this._renderQ.length > 0;
},
}
flush: function() {
flush() {
if (this._renderQ.length === 0) {
this.onflush();
} else {
this._flushing = true;
}
},
}
fillRect: function (x, y, width, height, color, from_queue) {
if (this._renderQ.length !== 0 && !from_queue) {
this._renderQ_push({
fillRect(x, y, width, height, color, fromQueue) {
if (this._renderQ.length !== 0 && !fromQueue) {
this._renderQPush({
'type': 'fill',
'x': x,
'y': y,
@@ -338,16 +315,16 @@ Display.prototype = {
this._drawCtx.fillRect(x, y, width, height);
this._damage(x, y, width, height);
}
},
}
copyImage: function (old_x, old_y, new_x, new_y, w, h, from_queue) {
if (this._renderQ.length !== 0 && !from_queue) {
this._renderQ_push({
copyImage(oldX, oldY, newX, newY, w, h, fromQueue) {
if (this._renderQ.length !== 0 && !fromQueue) {
this._renderQPush({
'type': 'copy',
'old_x': old_x,
'old_y': old_y,
'x': new_x,
'y': new_y,
'oldX': oldX,
'oldY': oldY,
'x': newX,
'y': newY,
'width': w,
'height': h,
});
@@ -365,259 +342,138 @@ Display.prototype = {
this._drawCtx.imageSmoothingEnabled = false;
this._drawCtx.drawImage(this._backbuffer,
old_x, old_y, w, h,
new_x, new_y, w, h);
this._damage(new_x, new_y, w, h);
oldX, oldY, w, h,
newX, newY, w, h);
this._damage(newX, newY, w, h);
}
},
}
imageRect: function(x, y, mime, arr) {
var img = new Image();
imageRect(x, y, width, height, mime, arr) {
/* The internal logic cannot handle empty images, so bail early */
if ((width === 0) || (height === 0)) {
return;
}
const img = new Image();
img.src = "data: " + mime + ";base64," + Base64.encode(arr);
this._renderQ_push({
this._renderQPush({
'type': 'img',
'img': img,
'x': x,
'y': y
'y': y,
'width': width,
'height': height
});
},
}
// start updating a tile
startTile: function (x, y, width, height, color) {
this._tile_x = x;
this._tile_y = y;
if (width === 16 && height === 16) {
this._tile = this._tile16x16;
} else {
this._tile = this._drawCtx.createImageData(width, height);
}
var red = color[2];
var green = color[1];
var blue = color[0];
var data = this._tile.data;
for (var 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: function (x, y, w, h, color) {
var red = color[2];
var green = color[1];
var blue = color[0];
var xend = x + w;
var yend = y + h;
var data = this._tile.data;
var width = this._tile.width;
for (var j = y; j < yend; j++) {
for (var i = x; i < xend; i++) {
var 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: function () {
this._drawCtx.putImageData(this._tile, this._tile_x, this._tile_y);
this._damage(this._tile_x, this._tile_y,
this._tile.width, this._tile.height);
},
blitImage: function (x, y, width, height, arr, offset, from_queue) {
if (this._renderQ.length !== 0 && !from_queue) {
blitImage(x, y, width, height, arr, offset, fromQueue) {
if (this._renderQ.length !== 0 && !fromQueue) {
// NB(directxman12): it's technically more performant here to use preallocated arrays,
// but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
// this probably isn't getting called *nearly* as much
var new_arr = new Uint8Array(width * height * 4);
new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
this._renderQ_push({
const newArr = new Uint8Array(width * height * 4);
newArr.set(new Uint8Array(arr.buffer, 0, newArr.length));
this._renderQPush({
'type': 'blit',
'data': new_arr,
'data': newArr,
'x': x,
'y': y,
'width': width,
'height': height,
});
} else {
this._bgrxImageData(x, y, width, height, arr, offset);
// NB(directxman12): arr must be an Type Array view
let data = new Uint8ClampedArray(arr.buffer,
arr.byteOffset + offset,
width * height * 4);
let img = new ImageData(data, width, height);
this._drawCtx.putImageData(img, x, y);
this._damage(x, y, width, height);
}
},
}
blitRgbImage: function (x, y , width, height, arr, offset, from_queue) {
if (this._renderQ.length !== 0 && !from_queue) {
// NB(directxman12): it's technically more performant here to use preallocated arrays,
// but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
// this probably isn't getting called *nearly* as much
var new_arr = new Uint8Array(width * height * 3);
new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
this._renderQ_push({
'type': 'blitRgb',
'data': new_arr,
'x': x,
'y': y,
'width': width,
'height': height,
});
} else {
this._rgbImageData(x, y, width, height, arr, offset);
}
},
blitRgbxImage: function (x, y, width, height, arr, offset, from_queue) {
if (this._renderQ.length !== 0 && !from_queue) {
// NB(directxman12): it's technically more performant here to use preallocated arrays,
// but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
// this probably isn't getting called *nearly* as much
var new_arr = new Uint8Array(width * height * 4);
new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
this._renderQ_push({
'type': 'blitRgbx',
'data': new_arr,
'x': x,
'y': y,
'width': width,
'height': height,
});
} else {
this._rgbxImageData(x, y, width, height, arr, offset);
}
},
drawImage: function (img, x, y) {
drawImage(img, x, y) {
this._drawCtx.drawImage(img, x, y);
this._damage(x, y, img.width, img.height);
},
}
changeCursor: function (pixels, mask, hotx, hoty, w, h) {
Display.changeCursor(this._target, pixels, mask, hotx, hoty, w, h);
},
autoscale(containerWidth, containerHeight) {
let scaleRatio;
defaultCursor: function () {
this._target.style.cursor = "default";
},
if (containerWidth === 0 || containerHeight === 0) {
scaleRatio = 0;
disableLocalCursor: function () {
this._target.style.cursor = "none";
},
autoscale: function (containerWidth, containerHeight) {
var vp = this._viewportLoc;
var targetAspectRatio = containerWidth / containerHeight;
var fbAspectRatio = vp.w / vp.h;
var scaleRatio;
if (fbAspectRatio >= targetAspectRatio) {
scaleRatio = containerWidth / vp.w;
} else {
scaleRatio = containerHeight / vp.h;
const vp = this._viewportLoc;
const targetAspectRatio = containerWidth / containerHeight;
const fbAspectRatio = vp.w / vp.h;
if (fbAspectRatio >= targetAspectRatio) {
scaleRatio = containerWidth / vp.w;
} else {
scaleRatio = containerHeight / vp.h;
}
}
this._rescale(scaleRatio);
},
}
// ===== PRIVATE METHODS =====
_rescale: function (factor) {
_rescale(factor) {
this._scale = factor;
var vp = this._viewportLoc;
const vp = this._viewportLoc;
// NB(directxman12): If you set the width directly, or set the
// style width to a number, the canvas is cleared.
// However, if you set the style width to a string
// ('NNNpx'), the canvas is scaled without clearing.
var width = Math.round(factor * vp.w) + 'px';
var height = Math.round(factor * vp.h) + 'px';
const width = factor * vp.w + 'px';
const height = factor * vp.h + 'px';
if ((this._target.style.width !== width) ||
(this._target.style.height !== height)) {
this._target.style.width = width;
this._target.style.height = height;
}
},
}
_setFillColor: function (color) {
var newStyle = 'rgb(' + color[2] + ',' + color[1] + ',' + color[0] + ')';
_setFillColor(color) {
const newStyle = 'rgb(' + color[0] + ',' + color[1] + ',' + color[2] + ')';
if (newStyle !== this._prevDrawStyle) {
this._drawCtx.fillStyle = newStyle;
this._prevDrawStyle = newStyle;
}
},
}
_rgbImageData: function (x, y, width, height, arr, offset) {
var img = this._drawCtx.createImageData(width, height);
var data = img.data;
for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 3) {
data[i] = arr[j];
data[i + 1] = arr[j + 1];
data[i + 2] = arr[j + 2];
data[i + 3] = 255; // Alpha
}
this._drawCtx.putImageData(img, x, y);
this._damage(x, y, img.width, img.height);
},
_bgrxImageData: function (x, y, width, height, arr, offset) {
var img = this._drawCtx.createImageData(width, height);
var data = img.data;
for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 4) {
data[i] = arr[j + 2];
data[i + 1] = arr[j + 1];
data[i + 2] = arr[j];
data[i + 3] = 255; // Alpha
}
this._drawCtx.putImageData(img, x, y);
this._damage(x, y, img.width, img.height);
},
_rgbxImageData: function (x, y, width, height, arr, offset) {
// NB(directxman12): arr must be an Type Array view
var img;
if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) {
img = new ImageData(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4), width, height);
} else {
img = this._drawCtx.createImageData(width, height);
img.data.set(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4));
}
this._drawCtx.putImageData(img, x, y);
this._damage(x, y, img.width, img.height);
},
_renderQ_push: function (action) {
_renderQPush(action) {
this._renderQ.push(action);
if (this._renderQ.length === 1) {
// If this can be rendered immediately it will be, otherwise
// the scanner will wait for the relevant event
this._scan_renderQ();
this._scanRenderQ();
}
},
}
_resume_renderQ: function() {
_resumeRenderQ() {
// "this" is the object that is ready, not the
// display object
this.removeEventListener('load', this._noVNC_display._resume_renderQ);
this._noVNC_display._scan_renderQ();
},
this.removeEventListener('load', this._noVNCDisplay._resumeRenderQ);
this._noVNCDisplay._scanRenderQ();
}
_scan_renderQ: function () {
var ready = true;
_scanRenderQ() {
let ready = true;
while (ready && this._renderQ.length > 0) {
var a = this._renderQ[0];
const a = this._renderQ[0];
switch (a.type) {
case 'flip':
this.flip(true);
break;
case 'copy':
this.copyImage(a.old_x, a.old_y, a.x, a.y, a.width, a.height, true);
this.copyImage(a.oldX, a.oldY, a.x, a.y, a.width, a.height, true);
break;
case 'fill':
this.fillRect(a.x, a.y, a.width, a.height, a.color, true);
@@ -625,18 +481,18 @@ Display.prototype = {
case 'blit':
this.blitImage(a.x, a.y, a.width, a.height, a.data, 0, true);
break;
case 'blitRgb':
this.blitRgbImage(a.x, a.y, a.width, a.height, a.data, 0, true);
break;
case 'blitRgbx':
this.blitRgbxImage(a.x, a.y, a.width, a.height, a.data, 0, true);
break;
case 'img':
if (a.img.complete) {
if (a.img.width !== a.width || a.img.height !== a.height) {
Log.Error("Decoded image has incorrect dimensions. Got " +
a.img.width + "x" + a.img.height + ". Expected " +
a.width + "x" + a.height + ".");
return;
}
this.drawImage(a.img, a.x, a.y);
} else {
a.img._noVNC_display = this;
a.img.addEventListener('load', this._resume_renderQ);
a.img._noVNCDisplay = this;
a.img.addEventListener('load', this._resumeRenderQ);
// We need to wait for this image to 'load'
// to keep things in-order
ready = false;
@@ -653,46 +509,5 @@ Display.prototype = {
this._flushing = false;
this.onflush();
}
},
};
// Class Methods
Display.changeCursor = function (target, pixels, mask, hotx, hoty, w, h) {
if ((w === 0) || (h === 0)) {
target.style.cursor = 'none';
return;
}
var cur = []
var y, x;
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
var idx = y * Math.ceil(w / 8) + Math.floor(x / 8);
var alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0;
idx = ((w * y) + x) * 4;
cur.push(pixels[idx + 2]); // red
cur.push(pixels[idx + 1]); // green
cur.push(pixels[idx]); // blue
cur.push(alpha); // alpha
}
}
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width = w;
canvas.height = h;
var img;
if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) {
img = new ImageData(new Uint8ClampedArray(cur), w, h);
} else {
img = ctx.createImageData(w, h);
img.data.set(new Uint8ClampedArray(cur));
}
ctx.clearRect(0, 0, w, h);
ctx.putImageData(img, 0, 0);
var url = canvas.toDataURL();
target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
};
}
+7 -3
View File
@@ -1,17 +1,18 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2017 Pierre Ossman for Cendio AB
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
export var encodings = {
export const encodings = {
encodingRaw: 0,
encodingCopyRect: 1,
encodingRRE: 2,
encodingHextile: 5,
encodingTight: 7,
encodingTightPNG: -260,
pseudoEncodingQualityLevel9: -23,
pseudoEncodingQualityLevel0: -32,
@@ -19,13 +20,15 @@ export var encodings = {
pseudoEncodingLastRect: -224,
pseudoEncodingCursor: -239,
pseudoEncodingQEMUExtendedKeyEvent: -258,
pseudoEncodingTightPNG: -260,
pseudoEncodingDesktopName: -307,
pseudoEncodingExtendedDesktopSize: -308,
pseudoEncodingXvp: -309,
pseudoEncodingFence: -312,
pseudoEncodingContinuousUpdates: -313,
pseudoEncodingCompressLevel9: -247,
pseudoEncodingCompressLevel0: -256,
pseudoEncodingVMwareCursor: 0x574d5664,
pseudoEncodingExtendedClipboard: 0xc0a1e5ce
};
export function encodingName(num) {
@@ -35,6 +38,7 @@ export function encodingName(num) {
case encodings.encodingRRE: return "RRE";
case encodings.encodingHextile: return "Hextile";
case encodings.encodingTight: return "Tight";
case encodings.encodingTightPNG: return "TightPNG";
default: return "[unknown encoding " + num + "]";
}
}
+48 -20
View File
@@ -1,13 +1,40 @@
/*
* 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 { inflateInit, inflate, inflateReset } from "../vendor/pako/lib/zlib/inflate.js";
import ZStream from "../vendor/pako/lib/zlib/zstream.js";
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;
export default class Inflate {
constructor() {
this.strm = new ZStream();
this.chunkSize = 1024 * 10 * 10;
this.strm.output = new Uint8Array(this.chunkSize);
this.windowBits = 5;
inflateInit(this.strm, this.windowBits);
}
setInput(data) {
if (!data) {
//FIXME: flush remaining data.
/* eslint-disable camelcase */
this.strm.input = null;
this.strm.avail_in = 0;
this.strm.next_in = 0;
} else {
this.strm.input = data;
this.strm.avail_in = this.strm.input.length;
this.strm.next_in = 0;
/* eslint-enable camelcase */
}
}
inflate(expected) {
// 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)
@@ -16,23 +43,24 @@ Inflate.prototype = {
this.strm.output = new Uint8Array(this.chunkSize);
}
this.strm.avail_out = this.chunkSize;
/* eslint-disable camelcase */
this.strm.next_out = 0;
this.strm.avail_out = expected;
/* eslint-enable camelcase */
inflate(this.strm, flush);
let ret = inflate(this.strm, 0); // Flush argument not used.
if (ret < 0) {
throw new Error("zlib inflate failed");
}
if (this.strm.next_out != expected) {
throw new Error("Incomplete zlib block");
}
return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
},
}
reset: function () {
reset() {
inflateReset(this.strm);
}
};
export default function Inflate() {
this.strm = new ZStream();
this.chunkSize = 1024 * 10 * 10;
this.strm.output = new Uint8Array(this.chunkSize);
this.windowBits = 5;
inflateInit(this.strm, this.windowBits);
};
}
+46 -45
View File
@@ -1,6 +1,6 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2017 Pierre Ossman for Cendio AB
* Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/
@@ -13,32 +13,29 @@ import KeyTable from "./keysym.js";
* See https://www.w3.org/TR/uievents-key/ for possible values.
*/
var DOMKeyTable = {};
const DOMKeyTable = {};
function addStandard(key, standard)
{
if (standard === undefined) throw "Undefined keysym for key \"" + key + "\"";
if (key in DOMKeyTable) throw "Duplicate entry for key \"" + key + "\"";
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 "Undefined keysym for key \"" + key + "\"";
if (right === undefined) throw "Undefined keysym for key \"" + key + "\"";
if (key in DOMKeyTable) throw "Duplicate entry for key \"" + key + "\"";
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 "Undefined keysym for key \"" + key + "\"";
if (numpad === undefined) throw "Undefined keysym for key \"" + key + "\"";
if (key in DOMKeyTable) throw "Duplicate entry for key \"" + key + "\"";
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];
}
// 2.2. Modifier Keys
// 3.2. Modifier Keys
addLeftRight("Alt", KeyTable.XK_Alt_L, KeyTable.XK_Alt_R);
addStandard("AltGraph", KeyTable.XK_ISO_Level3_Shift);
@@ -46,36 +43,39 @@ addStandard("CapsLock", KeyTable.XK_Caps_Lock);
addLeftRight("Control", KeyTable.XK_Control_L, KeyTable.XK_Control_R);
// - Fn
// - FnLock
addLeftRight("Hyper", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
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);
addLeftRight("Super", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
// - Symbol
// - SymbolLock
// - Hyper
// - Super
// 2.3. Whitespace Keys
// 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);
// 2.4. Navigation Keys
// 3.4. Navigation Keys
addNumpad("ArrowDown", KeyTable.XK_Down, KeyTable.XK_KP_Down);
addNumpad("ArrowUp", KeyTable.XK_Up, KeyTable.XK_KP_Up);
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);
// 2.5. Editing Keys
// 3.5. Editing Keys
addStandard("Backspace", KeyTable.XK_BackSpace);
addStandard("Clear", KeyTable.XK_Clear);
// 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);
@@ -87,7 +87,7 @@ addStandard("Paste", KeyTable.XF86XK_Paste);
addStandard("Redo", KeyTable.XK_Redo);
addStandard("Undo", KeyTable.XK_Undo);
// 2.6. UI Keys
// 3.6. UI Keys
// - Accept
// - Again (could just be XK_Redo)
@@ -105,7 +105,7 @@ addStandard("Select", KeyTable.XK_Select);
addStandard("ZoomIn", KeyTable.XF86XK_ZoomIn);
addStandard("ZoomOut", KeyTable.XF86XK_ZoomOut);
// 2.7. Device Keys
// 3.7. Device Keys
addStandard("BrightnessDown", KeyTable.XF86XK_MonBrightnessDown);
addStandard("BrightnessUp", KeyTable.XF86XK_MonBrightnessUp);
@@ -118,10 +118,10 @@ addStandard("Hibernate", KeyTable.XF86XK_Hibernate);
addStandard("Standby", KeyTable.XF86XK_Standby);
addStandard("WakeUp", KeyTable.XF86XK_WakeUp);
// 2.8. IME and Composition Keys
// 3.8. IME and Composition Keys
addStandard("AllCandidates", KeyTable.XK_MultipleCandidate);
addStandard("Alphanumeric", KeyTable.XK_Eisu_Shift); // could also be _Eisu_Toggle
addStandard("Alphanumeric", KeyTable.XK_Eisu_toggle);
addStandard("CodeInput", KeyTable.XK_Codeinput);
addStandard("Compose", KeyTable.XK_Multi_key);
addStandard("Convert", KeyTable.XK_Henkan);
@@ -139,7 +139,7 @@ addStandard("PreviousCandidate", KeyTable.XK_PreviousCandidate);
addStandard("SingleCandidate", KeyTable.XK_SingleCandidate);
addStandard("HangulMode", KeyTable.XK_Hangul);
addStandard("HanjaMode", KeyTable.XK_Hangul_Hanja);
addStandard("JunjuaMode", KeyTable.XK_Hangul_Jeonja);
addStandard("JunjaMode", KeyTable.XK_Hangul_Jeonja);
addStandard("Eisu", KeyTable.XK_Eisu_toggle);
addStandard("Hankaku", KeyTable.XK_Hankaku);
addStandard("Hiragana", KeyTable.XK_Hiragana);
@@ -149,9 +149,9 @@ addStandard("KanjiMode", KeyTable.XK_Kanji);
addStandard("Katakana", KeyTable.XK_Katakana);
addStandard("Romaji", KeyTable.XK_Romaji);
addStandard("Zenkaku", KeyTable.XK_Zenkaku);
addStandard("ZenkakuHanaku", KeyTable.XK_Zenkaku_Hankaku);
addStandard("ZenkakuHankaku", KeyTable.XK_Zenkaku_Hankaku);
// 2.9. General-Purpose Function Keys
// 3.9. General-Purpose Function Keys
addStandard("F1", KeyTable.XK_F1);
addStandard("F2", KeyTable.XK_F2);
@@ -190,17 +190,19 @@ addStandard("F34", KeyTable.XK_F34);
addStandard("F35", KeyTable.XK_F35);
// - Soft1...
// 2.10. Multimedia Keys
// 3.10. Multimedia Keys
// - ChannelDown
// - ChannelUp
addStandard("Close", KeyTable.XF86XK_Close);
addStandard("MailForward", KeyTable.XF86XK_MailForward);
addStandard("MailReply", KeyTable.XF86XK_Reply);
addStandard("MainSend", KeyTable.XF86XK_Send);
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);
@@ -212,20 +214,18 @@ addStandard("Print", KeyTable.XK_Print);
addStandard("Save", KeyTable.XF86XK_Save);
addStandard("SpellCheck", KeyTable.XF86XK_Spell);
// 2.11. Multimedia Numpad Keys
// 3.11. Multimedia Numpad Keys
// - Key11
// - Key12
// 2.12. Audio Keys
// 3.12. Audio Keys
// - AudioBalanceLeft
// - AudioBalanceRight
// - AudioBassDown
// - AudioBassBoostDown
// - AudioBassBoostToggle
// - AudioBassBoostUp
// - AudioBassUp
// - AudioFaderFront
// - AudioFaderRear
// - AudioSurroundModeNext
@@ -239,19 +239,20 @@ addStandard("AudioVolumeMute", KeyTable.XF86XK_AudioMute);
// - MicrophoneVolumeUp
addStandard("MicrophoneVolumeMute", KeyTable.XF86XK_AudioMicMute);
// 2.13. Speech Keys
// 3.13. Speech Keys
// - SpeechCorrectionList
// - SpeechInputToggle
// 2.14. Application Keys
// 3.14. Application Keys
addStandard("LaunchCalculator", KeyTable.XF86XK_Calculator);
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("LaunchMyComputer", KeyTable.XF86XK_MyComputer);
addStandard("LaunchPhone", KeyTable.XF86XK_Phone);
addStandard("LaunchScreenSaver", KeyTable.XF86XK_ScreenSaver);
addStandard("LaunchSpreadsheet", KeyTable.XF86XK_Excel);
@@ -259,7 +260,7 @@ addStandard("LaunchWebBrowser", KeyTable.XF86XK_WWW);
addStandard("LaunchWebCam", KeyTable.XF86XK_WebCam);
addStandard("LaunchWordProcessor", KeyTable.XF86XK_Word);
// 2.15. Browser Keys
// 3.15. Browser Keys
addStandard("BrowserBack", KeyTable.XF86XK_Back);
addStandard("BrowserFavorites", KeyTable.XF86XK_Favorites);
@@ -269,15 +270,15 @@ addStandard("BrowserRefresh", KeyTable.XF86XK_Refresh);
addStandard("BrowserSearch", KeyTable.XF86XK_Search);
addStandard("BrowserStop", KeyTable.XF86XK_Stop);
// 2.16. Mobile Phone Keys
// 3.16. Mobile Phone Keys
// - A whole bunch...
// 2.17. TV Keys
// 3.17. TV Keys
// - A whole bunch...
// 2.18. Media Controller Keys
// 3.18. Media Controller Keys
// - A whole bunch...
addStandard("Dimmer", KeyTable.XF86XK_BrightnessAdjust);
+3 -1
View File
@@ -1,6 +1,6 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2017 Pierre Ossman for Cendio AB
* Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/
@@ -14,6 +14,8 @@
* See https://www.w3.org/TR/uievents-key/ for possible values.
*/
/* eslint-disable key-spacing */
export default {
// 3.1.1.1. Writing System Keys
+567
View File
@@ -0,0 +1,567 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2020 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
const GH_NOGESTURE = 0;
const GH_ONETAP = 1;
const GH_TWOTAP = 2;
const GH_THREETAP = 4;
const GH_DRAG = 8;
const GH_LONGPRESS = 16;
const GH_TWODRAG = 32;
const GH_PINCH = 64;
const GH_INITSTATE = 127;
const GH_MOVE_THRESHOLD = 50;
const GH_ANGLE_THRESHOLD = 90; // Degrees
// Timeout when waiting for gestures (ms)
const GH_MULTITOUCH_TIMEOUT = 250;
// Maximum time between press and release for a tap (ms)
const GH_TAP_TIMEOUT = 1000;
// Timeout when waiting for longpress (ms)
const GH_LONGPRESS_TIMEOUT = 1000;
// Timeout when waiting to decide between PINCH and TWODRAG (ms)
const GH_TWOTOUCH_TIMEOUT = 50;
export default class GestureHandler {
constructor() {
this._target = null;
this._state = GH_INITSTATE;
this._tracked = [];
this._ignored = [];
this._waitingRelease = false;
this._releaseStart = 0.0;
this._longpressTimeoutId = null;
this._twoTouchTimeoutId = null;
this._boundEventHandler = this._eventHandler.bind(this);
}
attach(target) {
this.detach();
this._target = target;
this._target.addEventListener('touchstart',
this._boundEventHandler);
this._target.addEventListener('touchmove',
this._boundEventHandler);
this._target.addEventListener('touchend',
this._boundEventHandler);
this._target.addEventListener('touchcancel',
this._boundEventHandler);
}
detach() {
if (!this._target) {
return;
}
this._stopLongpressTimeout();
this._stopTwoTouchTimeout();
this._target.removeEventListener('touchstart',
this._boundEventHandler);
this._target.removeEventListener('touchmove',
this._boundEventHandler);
this._target.removeEventListener('touchend',
this._boundEventHandler);
this._target.removeEventListener('touchcancel',
this._boundEventHandler);
this._target = null;
}
_eventHandler(e) {
let fn;
e.stopPropagation();
e.preventDefault();
switch (e.type) {
case 'touchstart':
fn = this._touchStart;
break;
case 'touchmove':
fn = this._touchMove;
break;
case 'touchend':
case 'touchcancel':
fn = this._touchEnd;
break;
}
for (let i = 0; i < e.changedTouches.length; i++) {
let touch = e.changedTouches[i];
fn.call(this, touch.identifier, touch.clientX, touch.clientY);
}
}
_touchStart(id, x, y) {
// Ignore any new touches if there is already an active gesture,
// or we're in a cleanup state
if (this._hasDetectedGesture() || (this._state === GH_NOGESTURE)) {
this._ignored.push(id);
return;
}
// Did it take too long between touches that we should no longer
// consider this a single gesture?
if ((this._tracked.length > 0) &&
((Date.now() - this._tracked[0].started) > GH_MULTITOUCH_TIMEOUT)) {
this._state = GH_NOGESTURE;
this._ignored.push(id);
return;
}
// If we're waiting for fingers to release then we should no longer
// recognize new touches
if (this._waitingRelease) {
this._state = GH_NOGESTURE;
this._ignored.push(id);
return;
}
this._tracked.push({
id: id,
started: Date.now(),
active: true,
firstX: x,
firstY: y,
lastX: x,
lastY: y,
angle: 0
});
switch (this._tracked.length) {
case 1:
this._startLongpressTimeout();
break;
case 2:
this._state &= ~(GH_ONETAP | GH_DRAG | GH_LONGPRESS);
this._stopLongpressTimeout();
break;
case 3:
this._state &= ~(GH_TWOTAP | GH_TWODRAG | GH_PINCH);
break;
default:
this._state = GH_NOGESTURE;
}
}
_touchMove(id, x, y) {
let touch = this._tracked.find(t => t.id === id);
// If this is an update for a touch we're not tracking, ignore it
if (touch === undefined) {
return;
}
// Update the touches last position with the event coordinates
touch.lastX = x;
touch.lastY = y;
let deltaX = x - touch.firstX;
let deltaY = y - touch.firstY;
// Update angle when the touch has moved
if ((touch.firstX !== touch.lastX) ||
(touch.firstY !== touch.lastY)) {
touch.angle = Math.atan2(deltaY, deltaX) * 180 / Math.PI;
}
if (!this._hasDetectedGesture()) {
// Ignore moves smaller than the minimum threshold
if (Math.hypot(deltaX, deltaY) < GH_MOVE_THRESHOLD) {
return;
}
// Can't be a tap or long press as we've seen movement
this._state &= ~(GH_ONETAP | GH_TWOTAP | GH_THREETAP | GH_LONGPRESS);
this._stopLongpressTimeout();
if (this._tracked.length !== 1) {
this._state &= ~(GH_DRAG);
}
if (this._tracked.length !== 2) {
this._state &= ~(GH_TWODRAG | GH_PINCH);
}
// We need to figure out which of our different two touch gestures
// this might be
if (this._tracked.length === 2) {
// The other touch is the one where the id doesn't match
let prevTouch = this._tracked.find(t => t.id !== id);
// How far the previous touch point has moved since start
let prevDeltaMove = Math.hypot(prevTouch.firstX - prevTouch.lastX,
prevTouch.firstY - prevTouch.lastY);
// We know that the current touch moved far enough,
// but unless both touches moved further than their
// threshold we don't want to disqualify any gestures
if (prevDeltaMove > GH_MOVE_THRESHOLD) {
// The angle difference between the direction of the touch points
let deltaAngle = Math.abs(touch.angle - prevTouch.angle);
deltaAngle = Math.abs(((deltaAngle + 180) % 360) - 180);
// PINCH or TWODRAG can be eliminated depending on the angle
if (deltaAngle > GH_ANGLE_THRESHOLD) {
this._state &= ~GH_TWODRAG;
} else {
this._state &= ~GH_PINCH;
}
if (this._isTwoTouchTimeoutRunning()) {
this._stopTwoTouchTimeout();
}
} else if (!this._isTwoTouchTimeoutRunning()) {
// We can't determine the gesture right now, let's
// wait and see if more events are on their way
this._startTwoTouchTimeout();
}
}
if (!this._hasDetectedGesture()) {
return;
}
this._pushEvent('gesturestart');
}
this._pushEvent('gesturemove');
}
_touchEnd(id, x, y) {
// Check if this is an ignored touch
if (this._ignored.indexOf(id) !== -1) {
// Remove this touch from ignored
this._ignored.splice(this._ignored.indexOf(id), 1);
// And reset the state if there are no more touches
if ((this._ignored.length === 0) &&
(this._tracked.length === 0)) {
this._state = GH_INITSTATE;
this._waitingRelease = false;
}
return;
}
// We got a touchend before the timer triggered,
// this cannot result in a gesture anymore.
if (!this._hasDetectedGesture() &&
this._isTwoTouchTimeoutRunning()) {
this._stopTwoTouchTimeout();
this._state = GH_NOGESTURE;
}
// Some gestures don't trigger until a touch is released
if (!this._hasDetectedGesture()) {
// Can't be a gesture that relies on movement
this._state &= ~(GH_DRAG | GH_TWODRAG | GH_PINCH);
// Or something that relies on more time
this._state &= ~GH_LONGPRESS;
this._stopLongpressTimeout();
if (!this._waitingRelease) {
this._releaseStart = Date.now();
this._waitingRelease = true;
// Can't be a tap that requires more touches than we current have
switch (this._tracked.length) {
case 1:
this._state &= ~(GH_TWOTAP | GH_THREETAP);
break;
case 2:
this._state &= ~(GH_ONETAP | GH_THREETAP);
break;
}
}
}
// Waiting for all touches to release? (i.e. some tap)
if (this._waitingRelease) {
// Were all touches released at roughly the same time?
if ((Date.now() - this._releaseStart) > GH_MULTITOUCH_TIMEOUT) {
this._state = GH_NOGESTURE;
}
// Did too long time pass between press and release?
if (this._tracked.some(t => (Date.now() - t.started) > GH_TAP_TIMEOUT)) {
this._state = GH_NOGESTURE;
}
let touch = this._tracked.find(t => t.id === id);
touch.active = false;
// Are we still waiting for more releases?
if (this._hasDetectedGesture()) {
this._pushEvent('gesturestart');
} else {
// Have we reached a dead end?
if (this._state !== GH_NOGESTURE) {
return;
}
}
}
if (this._hasDetectedGesture()) {
this._pushEvent('gestureend');
}
// Ignore any remaining touches until they are ended
for (let i = 0; i < this._tracked.length; i++) {
if (this._tracked[i].active) {
this._ignored.push(this._tracked[i].id);
}
}
this._tracked = [];
this._state = GH_NOGESTURE;
// Remove this touch from ignored if it's in there
if (this._ignored.indexOf(id) !== -1) {
this._ignored.splice(this._ignored.indexOf(id), 1);
}
// We reset the state if ignored is empty
if ((this._ignored.length === 0)) {
this._state = GH_INITSTATE;
this._waitingRelease = false;
}
}
_hasDetectedGesture() {
if (this._state === GH_NOGESTURE) {
return false;
}
// Check to see if the bitmask value is a power of 2
// (i.e. only one bit set). If it is, we have a state.
if (this._state & (this._state - 1)) {
return false;
}
// For taps we also need to have all touches released
// before we've fully detected the gesture
if (this._state & (GH_ONETAP | GH_TWOTAP | GH_THREETAP)) {
if (this._tracked.some(t => t.active)) {
return false;
}
}
return true;
}
_startLongpressTimeout() {
this._stopLongpressTimeout();
this._longpressTimeoutId = setTimeout(() => this._longpressTimeout(),
GH_LONGPRESS_TIMEOUT);
}
_stopLongpressTimeout() {
clearTimeout(this._longpressTimeoutId);
this._longpressTimeoutId = null;
}
_longpressTimeout() {
if (this._hasDetectedGesture()) {
throw new Error("A longpress gesture failed, conflict with a different gesture");
}
this._state = GH_LONGPRESS;
this._pushEvent('gesturestart');
}
_startTwoTouchTimeout() {
this._stopTwoTouchTimeout();
this._twoTouchTimeoutId = setTimeout(() => this._twoTouchTimeout(),
GH_TWOTOUCH_TIMEOUT);
}
_stopTwoTouchTimeout() {
clearTimeout(this._twoTouchTimeoutId);
this._twoTouchTimeoutId = null;
}
_isTwoTouchTimeoutRunning() {
return this._twoTouchTimeoutId !== null;
}
_twoTouchTimeout() {
if (this._tracked.length === 0) {
throw new Error("A pinch or two drag gesture failed, no tracked touches");
}
// How far each touch point has moved since start
let avgM = this._getAverageMovement();
let avgMoveH = Math.abs(avgM.x);
let avgMoveV = Math.abs(avgM.y);
// The difference in the distance between where
// the touch points started and where they are now
let avgD = this._getAverageDistance();
let deltaTouchDistance = Math.abs(Math.hypot(avgD.first.x, avgD.first.y) -
Math.hypot(avgD.last.x, avgD.last.y));
if ((avgMoveV < deltaTouchDistance) &&
(avgMoveH < deltaTouchDistance)) {
this._state = GH_PINCH;
} else {
this._state = GH_TWODRAG;
}
this._pushEvent('gesturestart');
this._pushEvent('gesturemove');
}
_pushEvent(type) {
let detail = { type: this._stateToGesture(this._state) };
// For most gesture events the current (average) position is the
// most useful
let avg = this._getPosition();
let pos = avg.last;
// However we have a slight distance to detect gestures, so for the
// first gesture event we want to use the first positions we saw
if (type === 'gesturestart') {
pos = avg.first;
}
// For these gestures, we always want the event coordinates
// to be where the gesture began, not the current touch location.
switch (this._state) {
case GH_TWODRAG:
case GH_PINCH:
pos = avg.first;
break;
}
detail['clientX'] = pos.x;
detail['clientY'] = pos.y;
// FIXME: other coordinates?
// Some gestures also have a magnitude
if (this._state === GH_PINCH) {
let distance = this._getAverageDistance();
if (type === 'gesturestart') {
detail['magnitudeX'] = distance.first.x;
detail['magnitudeY'] = distance.first.y;
} else {
detail['magnitudeX'] = distance.last.x;
detail['magnitudeY'] = distance.last.y;
}
} else if (this._state === GH_TWODRAG) {
if (type === 'gesturestart') {
detail['magnitudeX'] = 0.0;
detail['magnitudeY'] = 0.0;
} else {
let movement = this._getAverageMovement();
detail['magnitudeX'] = movement.x;
detail['magnitudeY'] = movement.y;
}
}
let gev = new CustomEvent(type, { detail: detail });
this._target.dispatchEvent(gev);
}
_stateToGesture(state) {
switch (state) {
case GH_ONETAP:
return 'onetap';
case GH_TWOTAP:
return 'twotap';
case GH_THREETAP:
return 'threetap';
case GH_DRAG:
return 'drag';
case GH_LONGPRESS:
return 'longpress';
case GH_TWODRAG:
return 'twodrag';
case GH_PINCH:
return 'pinch';
}
throw new Error("Unknown gesture state: " + state);
}
_getPosition() {
if (this._tracked.length === 0) {
throw new Error("Failed to get gesture position, no tracked touches");
}
let size = this._tracked.length;
let fx = 0, fy = 0, lx = 0, ly = 0;
for (let i = 0; i < this._tracked.length; i++) {
fx += this._tracked[i].firstX;
fy += this._tracked[i].firstY;
lx += this._tracked[i].lastX;
ly += this._tracked[i].lastY;
}
return { first: { x: fx / size,
y: fy / size },
last: { x: lx / size,
y: ly / size } };
}
_getAverageMovement() {
if (this._tracked.length === 0) {
throw new Error("Failed to get gesture movement, no tracked touches");
}
let totalH, totalV;
totalH = totalV = 0;
let size = this._tracked.length;
for (let i = 0; i < this._tracked.length; i++) {
totalH += this._tracked[i].lastX - this._tracked[i].firstX;
totalV += this._tracked[i].lastY - this._tracked[i].firstY;
}
return { x: totalH / size,
y: totalV / size };
}
_getAverageDistance() {
if (this._tracked.length === 0) {
throw new Error("Failed to get gesture distance, no tracked touches");
}
// Distance between the first and last tracked touches
let first = this._tracked[0];
let last = this._tracked[this._tracked.length - 1];
let fdx = Math.abs(last.firstX - first.firstX);
let fdy = Math.abs(last.firstY - first.firstY);
let ldx = Math.abs(last.lastX - first.lastX);
let ldy = Math.abs(last.lastY - first.lastY);
return { first: { x: fdx, y: fdy },
last: { x: ldx, y: ldy } };
}
}
+140 -181
View File
@@ -1,7 +1,6 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2013 Samuel Mannehed for Cendio AB
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/
@@ -15,71 +14,52 @@ import * as browser from "../util/browser.js";
// Keyboard event handler
//
export default function Keyboard(target) {
this._target = target || null;
export default class Keyboard {
constructor(target) {
this._target = target || null;
this._keyDownList = {}; // List of depressed keys
// (even if they are happy)
this._pendingKey = null; // Key waiting for keypress
this._keyDownList = {}; // List of depressed keys
// (even if they are happy)
this._altGrArmed = false; // Windows AltGr detection
// keep these here so we can refer to them later
this._eventHandlers = {
'keyup': this._handleKeyUp.bind(this),
'keydown': this._handleKeyDown.bind(this),
'keypress': this._handleKeyPress.bind(this),
'blur': this._allKeysUp.bind(this)
};
};
// keep these here so we can refer to them later
this._eventHandlers = {
'keyup': this._handleKeyUp.bind(this),
'keydown': this._handleKeyDown.bind(this),
'blur': this._allKeysUp.bind(this),
};
Keyboard.prototype = {
// ===== EVENT HANDLERS =====
// ===== EVENT HANDLERS =====
onkeyevent: function () {}, // Handler for key press/release
this.onkeyevent = () => {}; // Handler for key press/release
}
// ===== PRIVATE METHODS =====
_sendKeyEvent: function (keysym, code, down) {
_sendKeyEvent(keysym, code, down) {
if (down) {
this._keyDownList[code] = keysym;
} else {
// Do we really think this key is down?
if (!(code in this._keyDownList)) {
return;
}
delete this._keyDownList[code];
}
Log.Debug("onkeyevent " + (down ? "down" : "up") +
", keysym: " + keysym, ", code: " + code);
// Windows sends CtrlLeft+AltRight when you press
// AltGraph, which tends to confuse the hell out of
// remote systems. Fake a release of these keys until
// there is a way to detect AltGraph properly.
var fakeAltGraph = false;
if (down && browser.isWindows()) {
if ((code !== 'ControlLeft') &&
(code !== 'AltRight') &&
('ControlLeft' in this._keyDownList) &&
('AltRight' in this._keyDownList)) {
fakeAltGraph = true;
this.onkeyevent(this._keyDownList['AltRight'],
'AltRight', false);
this.onkeyevent(this._keyDownList['ControlLeft'],
'ControlLeft', false);
}
}
this.onkeyevent(keysym, code, down);
}
if (fakeAltGraph) {
this.onkeyevent(this._keyDownList['ControlLeft'],
'ControlLeft', true);
this.onkeyevent(this._keyDownList['AltRight'],
'AltRight', true);
}
},
_getKeyCode: function (e) {
var code = KeyboardUtil.getKeycode(e);
_getKeyCode(e) {
const code = KeyboardUtil.getKeycode(e);
if (code !== 'Unidentified') {
return code;
}
// Unstable, but we don't have anything else to go on
// (don't use it for 'keypress' events thought since
// WebKit sets it to the same as charCode)
if (e.keyCode && (e.type !== 'keypress')) {
if (e.keyCode) {
// 229 is used for composition events
if (e.keyCode !== 229) {
return 'Platform' + e.keyCode;
@@ -94,26 +74,46 @@ Keyboard.prototype = {
return e.keyIdentifier;
}
var codepoint = parseInt(e.keyIdentifier.substr(2), 16);
var char = String.fromCharCode(codepoint);
// Some implementations fail to uppercase the symbols
char = char.toUpperCase();
const codepoint = parseInt(e.keyIdentifier.substr(2), 16);
const char = String.fromCharCode(codepoint).toUpperCase();
return 'Platform' + char.charCodeAt();
}
return 'Unidentified';
},
}
_handleKeyDown: function (e) {
var code = this._getKeyCode(e);
var keysym = KeyboardUtil.getKeysym(e);
_handleKeyDown(e) {
const code = this._getKeyCode(e);
let keysym = KeyboardUtil.getKeysym(e);
// Windows doesn't have a proper AltGr, but handles it using
// fake Ctrl+Alt. However the remote end might not be Windows,
// so we need to merge those in to a single AltGr event. We
// detect this case by seeing the two key events directly after
// each other with a very short time between them (<50ms).
if (this._altGrArmed) {
this._altGrArmed = false;
clearTimeout(this._altGrTimeout);
if ((code === "AltRight") &&
((e.timeStamp - this._altGrCtrlTime) < 50)) {
// FIXME: We fail to detect this if either Ctrl key is
// first manually pressed as Windows then no
// longer sends the fake Ctrl down event. It
// does however happily send real Ctrl events
// even when AltGr is already down. Some
// browsers detect this for us though and set the
// key to "AltGraph".
keysym = KeyTable.XK_ISO_Level3_Shift;
} else {
this._sendKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
}
}
// We cannot handle keys we cannot track, but we also need
// to deal with virtual keyboards which omit key info
// (iOS omits tracking info on keyup events, which forces us to
// special treat that platform here)
if ((code === 'Unidentified') || browser.isIOS()) {
if (code === 'Unidentified') {
if (keysym) {
// If it's a virtual keyboard then it should be
// sufficient to just send press and release right
@@ -130,20 +130,20 @@ Keyboard.prototype = {
// keys around a bit to make things more sane for the remote
// server. This method is used by RealVNC and TigerVNC (and
// possibly others).
if (browser.isMac()) {
if (browser.isMac() || browser.isIOS()) {
switch (keysym) {
case KeyTable.XK_Super_L:
keysym = KeyTable.XK_Alt_L;
break;
case KeyTable.XK_Super_R:
keysym = KeyTable.XK_Super_L;
break;
case KeyTable.XK_Alt_L:
keysym = KeyTable.XK_Mode_switch;
break;
case KeyTable.XK_Alt_R:
keysym = KeyTable.XK_ISO_Level3_Shift;
break;
case KeyTable.XK_Super_L:
keysym = KeyTable.XK_Alt_L;
break;
case KeyTable.XK_Super_R:
keysym = KeyTable.XK_Super_L;
break;
case KeyTable.XK_Alt_L:
keysym = KeyTable.XK_Mode_switch;
break;
case KeyTable.XK_Alt_R:
keysym = KeyTable.XK_ISO_Level3_Shift;
break;
}
}
@@ -157,158 +157,117 @@ Keyboard.prototype = {
// state change events. That gets extra confusing for CapsLock
// which toggles on each press, but not on release. So pretend
// it was a quick press and release of the button.
if (browser.isMac() && (code === 'CapsLock')) {
if ((browser.isMac() || browser.isIOS()) && (code === 'CapsLock')) {
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);
stopEvent(e);
return;
}
// If this is a legacy browser then we'll need to wait for
// a keypress event as well
// (IE and Edge has a broken KeyboardEvent.key, so we can't
// just check for the presence of that field)
if (!keysym && (!e.key || browser.isIE() || browser.isEdge())) {
this._pendingKey = code;
// However we might not get a keypress event if the key
// is non-printable, which needs some special fallback
// handling
setTimeout(this._handleKeyPressTimeout.bind(this), 10, e);
// Windows doesn't send proper key releases for a bunch of
// Japanese IM keys so we have to fake the release right away
const jpBadKeys = [ KeyTable.XK_Zenkaku_Hankaku,
KeyTable.XK_Eisu_toggle,
KeyTable.XK_Katakana,
KeyTable.XK_Hiragana,
KeyTable.XK_Romaji ];
if (browser.isWindows() && jpBadKeys.includes(keysym)) {
this._sendKeyEvent(keysym, code, true);
this._sendKeyEvent(keysym, code, false);
stopEvent(e);
return;
}
this._pendingKey = null;
stopEvent(e);
this._keyDownList[code] = keysym;
// Possible start of AltGr sequence? (see above)
if ((code === "ControlLeft") && browser.isWindows() &&
!("ControlLeft" in this._keyDownList)) {
this._altGrArmed = true;
this._altGrTimeout = setTimeout(this._handleAltGrTimeout.bind(this), 100);
this._altGrCtrlTime = e.timeStamp;
return;
}
this._sendKeyEvent(keysym, code, true);
},
}
// Legacy event for browsers without code/key
_handleKeyPress: function (e) {
_handleKeyUp(e) {
stopEvent(e);
// Are we expecting a keypress?
if (this._pendingKey === null) {
return;
const code = this._getKeyCode(e);
// We can't get a release in the middle of an AltGr sequence, so
// abort that detection
if (this._altGrArmed) {
this._altGrArmed = false;
clearTimeout(this._altGrTimeout);
this._sendKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
}
var code = this._getKeyCode(e);
var keysym = KeyboardUtil.getKeysym(e);
// The key we were waiting for?
if ((code !== 'Unidentified') && (code != this._pendingKey)) {
return;
}
code = this._pendingKey;
this._pendingKey = null;
if (!keysym) {
Log.Info('keypress with no keysym:', e);
return;
}
this._keyDownList[code] = keysym;
this._sendKeyEvent(keysym, code, true);
},
_handleKeyPressTimeout: function (e) {
// Did someone manage to sort out the key already?
if (this._pendingKey === null) {
return;
}
var code, keysym;
code = this._pendingKey;
this._pendingKey = null;
// We have no way of knowing the proper keysym with the
// information given, but the following are true for most
// layouts
if ((e.keyCode >= 0x30) && (e.keyCode <= 0x39)) {
// Digit
keysym = e.keyCode;
} else if ((e.keyCode >= 0x41) && (e.keyCode <= 0x5a)) {
// Character (A-Z)
var char = String.fromCharCode(e.keyCode);
// A feeble attempt at the correct case
if (e.shiftKey)
char = char.toUpperCase();
else
char = char.toLowerCase();
keysym = char.charCodeAt();
} else {
// Unknown, give up
keysym = 0;
}
this._keyDownList[code] = keysym;
this._sendKeyEvent(keysym, code, true);
},
_handleKeyUp: function (e) {
stopEvent(e);
var code = this._getKeyCode(e);
// See comment in _handleKeyDown()
if (browser.isMac() && (code === 'CapsLock')) {
if ((browser.isMac() || browser.isIOS()) && (code === 'CapsLock')) {
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);
return;
}
// Do we really think this key is down?
if (!(code in this._keyDownList)) {
return;
}
this._sendKeyEvent(this._keyDownList[code], code, false);
delete this._keyDownList[code];
},
// Windows has a rather nasty bug where it won't send key
// release events for a Shift button if the other Shift is still
// pressed
if (browser.isWindows() && ((code === 'ShiftLeft') ||
(code === 'ShiftRight'))) {
if ('ShiftRight' in this._keyDownList) {
this._sendKeyEvent(this._keyDownList['ShiftRight'],
'ShiftRight', false);
}
if ('ShiftLeft' in this._keyDownList) {
this._sendKeyEvent(this._keyDownList['ShiftLeft'],
'ShiftLeft', false);
}
}
}
_allKeysUp: function () {
_handleAltGrTimeout() {
this._altGrArmed = false;
clearTimeout(this._altGrTimeout);
this._sendKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
}
_allKeysUp() {
Log.Debug(">> Keyboard.allKeysUp");
for (var code in this._keyDownList) {
for (let code in this._keyDownList) {
this._sendKeyEvent(this._keyDownList[code], code, false);
};
this._keyDownList = {};
}
Log.Debug("<< Keyboard.allKeysUp");
},
}
// ===== PUBLIC METHODS =====
grab: function () {
grab() {
//Log.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);
this._target.addEventListener('keydown', this._eventHandlers.keydown);
this._target.addEventListener('keyup', this._eventHandlers.keyup);
// Release (key up) if window loses focus
window.addEventListener('blur', this._eventHandlers.blur);
//Log.Debug("<< Keyboard.grab");
},
}
ungrab: function () {
ungrab() {
//Log.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);
this._target.removeEventListener('keydown', this._eventHandlers.keydown);
this._target.removeEventListener('keyup', this._eventHandlers.keyup);
window.removeEventListener('blur', this._eventHandlers.blur);
// Release (key up) all keys that are in a down state
this._allKeysUp();
//Log.Debug(">> Keyboard.ungrab");
},
};
}
}
+2
View File
@@ -1,3 +1,5 @@
/* eslint-disable key-spacing */
export default {
XK_VoidSymbol: 0xffffff, /* Void symbol */
+3 -3
View File
@@ -7,7 +7,7 @@
/* Functions at the bottom */
var codepoints = {
const codepoints = {
0x0100: 0x03c0, // XK_Amacron
0x0101: 0x03e0, // XK_amacron
0x0102: 0x01c3, // XK_Abreve
@@ -670,14 +670,14 @@ var codepoints = {
};
export default {
lookup : function(u) {
lookup(u) {
// Latin-1 is one-to-one mapping
if ((u >= 0x20) && (u <= 0xff)) {
return u;
}
// Lookup table (fairly random)
var keysym = codepoints[u];
const keysym = codepoints[u];
if (keysym !== undefined) {
return keysym;
}
-280
View File
@@ -1,280 +0,0 @@
/*
* 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)
*/
import * as Log from '../util/logging.js';
import { isTouchDevice } from '../util/browser.js';
import { setCapture, stopEvent, getPointerEvent } from '../util/events.js';
var WHEEL_STEP = 10; // Delta threshold for a mouse wheel step
var WHEEL_STEP_TIMEOUT = 50; // ms
var WHEEL_LINE_HEIGHT = 19;
export default function Mouse(target) {
this._target = target || document;
this._doubleClickTimer = null;
this._lastTouchPos = null;
this._pos = null;
this._wheelStepXTimer = null;
this._wheelStepYTimer = null;
this._accumulatedWheelDeltaX = 0;
this._accumulatedWheelDeltaY = 0;
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 = {
// ===== PROPERTIES =====
touchButton: 1, // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
// ===== EVENT HANDLERS =====
onmousebutton: function () {}, // Handler for mouse button click/release
onmousemove: function () {}, // Handler for mouse movement
// ===== PRIVATE METHODS =====
_resetDoubleClickTimer: function () {
this._doubleClickTimer = null;
},
_handleMouseButton: function (e, down) {
this._updateMousePosition(e);
var pos = this._pos;
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
}
Log.Debug("onmousebutton " + (down ? "down" : "up") +
", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
this.onmousebutton(pos.x, pos.y, down, bmask);
stopEvent(e);
},
_handleMouseDown: function (e) {
// Touch events have implicit capture
if (e.type === "mousedown") {
setCapture(this._target);
}
this._handleMouseButton(e, 1);
},
_handleMouseUp: function (e) {
this._handleMouseButton(e, 0);
},
// Mouse wheel events are sent in steps over VNC. This means that the VNC
// protocol can't handle a wheel event with specific distance or speed.
// Therefor, if we get a lot of small mouse wheel events we combine them.
_generateWheelStepX: function () {
if (this._accumulatedWheelDeltaX < 0) {
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 5);
this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 5);
} else if (this._accumulatedWheelDeltaX > 0) {
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 6);
this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 6);
}
this._accumulatedWheelDeltaX = 0;
},
_generateWheelStepY: function () {
if (this._accumulatedWheelDeltaY < 0) {
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 3);
this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 3);
} else if (this._accumulatedWheelDeltaY > 0) {
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 4);
this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 4);
}
this._accumulatedWheelDeltaY = 0;
},
_resetWheelStepTimers: function () {
window.clearTimeout(this._wheelStepXTimer);
window.clearTimeout(this._wheelStepYTimer);
this._wheelStepXTimer = null;
this._wheelStepYTimer = null;
},
_handleMouseWheel: function (e) {
this._resetWheelStepTimers();
this._updateMousePosition(e);
var dX = e.deltaX;
var dY = e.deltaY;
// Pixel units unless it's non-zero.
// Note that if deltamode is line or page won't matter since we aren't
// sending the mouse wheel delta to the server anyway.
// The difference between pixel and line can be important however since
// we have a threshold that can be smaller than the line height.
if (e.deltaMode !== 0) {
dX *= WHEEL_LINE_HEIGHT;
dY *= WHEEL_LINE_HEIGHT;
}
this._accumulatedWheelDeltaX += dX;
this._accumulatedWheelDeltaY += dY;
// Generate a mouse wheel step event when the accumulated delta
// for one of the axes is large enough.
// Small delta events that do not pass the threshold get sent
// after a timeout.
if (Math.abs(this._accumulatedWheelDeltaX) > WHEEL_STEP) {
this._generateWheelStepX();
} else {
this._wheelStepXTimer =
window.setTimeout(this._generateWheelStepX.bind(this),
WHEEL_STEP_TIMEOUT);
}
if (Math.abs(this._accumulatedWheelDeltaY) > WHEEL_STEP) {
this._generateWheelStepY();
} else {
this._wheelStepYTimer =
window.setTimeout(this._generateWheelStepY.bind(this),
WHEEL_STEP_TIMEOUT);
}
stopEvent(e);
},
_handleMouseMove: function (e) {
this._updateMousePosition(e);
this.onmousemove(this._pos.x, this._pos.y);
stopEvent(e);
},
_handleMouseDisable: function (e) {
/*
* 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) {
stopEvent(e);
}
},
// Update coordinates relative to target
_updateMousePosition: function(e) {
e = 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;
}
this._pos = {x:x, y:y};
},
// ===== PUBLIC METHODS =====
grab: function () {
var c = this._target;
if (isTouchDevice) {
c.addEventListener('touchstart', this._eventHandlers.mousedown);
c.addEventListener('touchend', this._eventHandlers.mouseup);
c.addEventListener('touchmove', this._eventHandlers.mousemove);
}
c.addEventListener('mousedown', this._eventHandlers.mousedown);
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;
this._resetWheelStepTimers();
if (isTouchDevice) {
c.removeEventListener('touchstart', this._eventHandlers.mousedown);
c.removeEventListener('touchend', this._eventHandlers.mouseup);
c.removeEventListener('touchmove', this._eventHandlers.mousemove);
}
c.removeEventListener('mousedown', this._eventHandlers.mousedown);
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);
}
};
+60 -36
View File
@@ -6,7 +6,7 @@ import DOMKeyTable from "./domkeytable.js";
import * as browser from "../util/browser.js";
// Get 'KeyboardEvent.code', handling legacy browsers
export function getKeycode(evt){
export function getKeycode(evt) {
// Are we getting proper key identifiers?
// (unfortunately Firefox and Chrome are crappy here and gives
// us an empty string on some platforms, rather than leaving it
@@ -22,10 +22,9 @@ export function getKeycode(evt){
}
// The de-facto standard is to use Windows Virtual-Key codes
// in the 'keyCode' field for non-printable characters. However
// Webkit sets it to the same as charCode in 'keypress' events.
if ((evt.type !== 'keypress') && (evt.keyCode in vkeys)) {
var code = vkeys[evt.keyCode];
// in the 'keyCode' field for non-printable characters
if (evt.keyCode in vkeys) {
let code = vkeys[evt.keyCode];
// macOS has messed up this code for some reason
if (browser.isMac() && (code === 'ContextMenu')) {
@@ -69,29 +68,11 @@ export function getKeycode(evt){
export function getKey(evt) {
// Are we getting a proper key value?
if (evt.key !== undefined) {
// IE and Edge use some ancient version of the spec
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8860571/
switch (evt.key) {
case 'Spacebar': return ' ';
case 'Esc': return 'Escape';
case 'Scroll': return 'ScrollLock';
case 'Win': return 'Meta';
case 'Apps': return 'ContextMenu';
case 'Up': return 'ArrowUp';
case 'Left': return 'ArrowLeft';
case 'Right': return 'ArrowRight';
case 'Down': return 'ArrowDown';
case 'Del': return 'Delete';
case 'Divide': return '/';
case 'Multiply': return '*';
case 'Subtract': return '-';
case 'Add': return '+';
case 'Decimal': return evt.char;
}
// Mozilla isn't fully in sync with the spec yet
switch (evt.key) {
case 'OS': return 'Meta';
case 'LaunchMyComputer': return 'LaunchApplication1';
case 'LaunchCalculator': return 'LaunchApplication2';
}
// iOS leaks some OS names
@@ -103,15 +84,16 @@ export function getKey(evt) {
case 'UIKeyInputEscape': return 'Escape';
}
// IE and Edge have broken handling of AltGraph so we cannot
// trust them for printable characters
if ((evt.key.length !== 1) || (!browser.isIE() && !browser.isEdge())) {
return evt.key;
// Broken behaviour in Chrome
if ((evt.key === '\x00') && (evt.code === 'NumpadDecimal')) {
return 'Delete';
}
return evt.key;
}
// Try to deduce it based on the physical key
var code = getKeycode(evt);
const code = getKeycode(evt);
if (code in fixedkeys) {
return fixedkeys[code];
}
@@ -126,8 +108,8 @@ export function getKey(evt) {
}
// Get the most reliable keysym value we can get from a key event
export function getKeysym(evt){
var key = getKey(evt);
export function getKeysym(evt) {
const key = getKey(evt);
if (key === 'Unidentified') {
return null;
@@ -135,30 +117,72 @@ export function getKeysym(evt){
// First look up special keys
if (key in DOMKeyTable) {
var location = evt.location;
let location = evt.location;
// Safari screws up location for the right cmd key
if ((key === 'Meta') && (location === 0)) {
location = 2;
}
// And for Clear
if ((key === 'Clear') && (location === 3)) {
let code = getKeycode(evt);
if (code === 'NumLock') {
location = 0;
}
}
if ((location === undefined) || (location > 3)) {
location = 0;
}
// The original Meta key now gets confused with the Windows key
// https://bugs.chromium.org/p/chromium/issues/detail?id=1020141
// https://bugzilla.mozilla.org/show_bug.cgi?id=1232918
if (key === 'Meta') {
let code = getKeycode(evt);
if (code === 'AltLeft') {
return KeyTable.XK_Meta_L;
} else if (code === 'AltRight') {
return KeyTable.XK_Meta_R;
}
}
// macOS has Clear instead of NumLock, but the remote system is
// probably not macOS, so lying here is probably best...
if (key === 'Clear') {
let code = getKeycode(evt);
if (code === 'NumLock') {
return KeyTable.XK_Num_Lock;
}
}
// Windows sends alternating symbols for some keys when using a
// Japanese layout. We have no way of synchronising with the IM
// running on the remote system, so we send some combined keysym
// instead and hope for the best.
if (browser.isWindows()) {
switch (key) {
case 'Zenkaku':
case 'Hankaku':
return KeyTable.XK_Zenkaku_Hankaku;
case 'Romaji':
case 'KanaMode':
return KeyTable.XK_Romaji;
}
}
return DOMKeyTable[key][location];
}
// Now we need to look at the Unicode symbol instead
var codepoint;
// Special key? (FIXME: Should have been caught earlier)
if (key.length !== 1) {
return null;
}
codepoint = key.charCodeAt();
const codepoint = key.charCodeAt();
if (codepoint) {
return keysyms.lookup(codepoint);
}
+1 -1
View File
@@ -1,6 +1,6 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2017 Pierre Ossman for Cendio AB
* Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/
+5 -3
View File
@@ -1,8 +1,8 @@
/*
* This file is auto-generated from keymaps.csv on 2017-05-31 16:20
* Database checksum sha256(92fd165507f2a3b8c5b3fa56e425d45788dbcb98cf067a307527d91ce22cab94)
* This file is auto-generated from keymaps.csv
* Database checksum sha256(76d68c10e97d37fe2ea459e210125ae41796253fb217e900bf2983ade13a7920)
* To re-generate, run:
* keymap-gen --lang=js code-map keymaps.csv html atset1
* keymap-gen code-map --lang=js keymaps.csv html atset1
*/
export default {
"Again": 0xe005, /* html:Again (Again) -> linux:129 (KEY_AGAIN) -> atset1:57349 */
@@ -111,6 +111,8 @@ export default {
"KeyX": 0x2d, /* html:KeyX (KeyX) -> linux:45 (KEY_X) -> atset1:45 */
"KeyY": 0x15, /* html:KeyY (KeyY) -> linux:21 (KEY_Y) -> atset1:21 */
"KeyZ": 0x2c, /* html:KeyZ (KeyZ) -> linux:44 (KEY_Z) -> atset1:44 */
"Lang1": 0x72, /* html:Lang1 (Lang1) -> linux:122 (KEY_HANGEUL) -> atset1:114 */
"Lang2": 0x71, /* html:Lang2 (Lang2) -> linux:123 (KEY_HANJA) -> atset1:113 */
"Lang3": 0x78, /* html:Lang3 (Lang3) -> linux:90 (KEY_KATAKANA) -> atset1:120 */
"Lang4": 0x77, /* html:Lang4 (Lang4) -> linux:91 (KEY_HIRAGANA) -> atset1:119 */
"Lang5": 0x76, /* html:Lang5 (Lang5) -> linux:85 (KEY_ZENKAKUHANKAKU) -> atset1:118 */
+2001 -1553
View File
File diff suppressed because it is too large Load Diff
+63 -29
View File
@@ -1,15 +1,17 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
* Browser feature support detection
*/
import * as Log from './logging.js';
// Touch detection
export var isTouchDevice = ('ontouchstart' in document.documentElement) ||
export let isTouchDevice = ('ontouchstart' in document.documentElement) ||
// requried for Chrome debugger
(document.ontouchstart !== undefined) ||
// required for MS Surface
@@ -20,42 +22,65 @@ window.addEventListener('touchstart', function onFirstTouch() {
window.removeEventListener('touchstart', onFirstTouch, false);
}, false);
var _cursor_uris_supported = null;
export function supportsCursorURIs () {
if (_cursor_uris_supported === null) {
try {
var target = document.createElement('canvas');
target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
// The goal is to find a certain physical width, the devicePixelRatio
// brings us a bit closer but is not optimal.
export let dragThreshold = 10 * (window.devicePixelRatio || 1);
if (target.style.cursor) {
Log.Info("Data URI scheme cursor supported");
_cursor_uris_supported = true;
} else {
Log.Warn("Data URI scheme cursor not supported");
_cursor_uris_supported = false;
}
} catch (exc) {
Log.Error("Data URI scheme cursor test exception: " + exc);
_cursor_uris_supported = false;
}
let _supportsCursorURIs = false;
try {
const target = document.createElement('canvas');
target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
if (target.style.cursor.indexOf("url") === 0) {
Log.Info("Data URI scheme cursor supported");
_supportsCursorURIs = true;
} else {
Log.Warn("Data URI scheme cursor not supported");
}
} catch (exc) {
Log.Error("Data URI scheme cursor test exception: " + exc);
}
return _cursor_uris_supported;
};
export const supportsCursorURIs = _supportsCursorURIs;
let _hasScrollbarGutter = true;
try {
// Create invisible container
const container = document.createElement('div');
container.style.visibility = 'hidden';
container.style.overflow = 'scroll'; // forcing scrollbars
document.body.appendChild(container);
// Create a div and place it in the container
const child = document.createElement('div');
container.appendChild(child);
// Calculate the difference between the container's full width
// and the child's width - the difference is the scrollbars
const scrollbarWidth = (container.offsetWidth - child.offsetWidth);
// Clean up
container.parentNode.removeChild(container);
_hasScrollbarGutter = scrollbarWidth != 0;
} catch (exc) {
Log.Error("Scrollbar test exception: " + exc);
}
export const hasScrollbarGutter = _hasScrollbarGutter;
/*
* The functions for detection of platforms and browsers below are exported
* but the use of these should be minimized as much as possible.
*
* It's better to use feature detection than platform detection.
*/
export function isMac() {
return navigator && !!(/mac/i).exec(navigator.platform);
}
export function isIE() {
return navigator && !!(/trident/i).exec(navigator.userAgent);
}
export function isEdge() {
return navigator && !!(/edge/i).exec(navigator.userAgent);
}
export function isWindows() {
return navigator && !!(/win/i).exec(navigator.platform);
}
@@ -67,3 +92,12 @@ export function isIOS() {
!!(/ipod/i).exec(navigator.platform));
}
export function isSafari() {
return navigator && (navigator.userAgent.indexOf('Safari') !== -1 &&
navigator.userAgent.indexOf('Chrome') === -1);
}
export function isFirefox() {
return navigator && !!(/firefox/i).exec(navigator.userAgent);
}
+243
View File
@@ -0,0 +1,243 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/
import { supportsCursorURIs, isTouchDevice } from './browser.js';
const useFallback = !supportsCursorURIs || isTouchDevice;
export default class Cursor {
constructor() {
this._target = null;
this._canvas = document.createElement('canvas');
if (useFallback) {
this._canvas.style.position = 'fixed';
this._canvas.style.zIndex = '65535';
this._canvas.style.pointerEvents = 'none';
// Can't use "display" because of Firefox bug #1445997
this._canvas.style.visibility = 'hidden';
}
this._position = { x: 0, y: 0 };
this._hotSpot = { x: 0, y: 0 };
this._eventHandlers = {
'mouseover': this._handleMouseOver.bind(this),
'mouseleave': this._handleMouseLeave.bind(this),
'mousemove': this._handleMouseMove.bind(this),
'mouseup': this._handleMouseUp.bind(this),
};
}
attach(target) {
if (this._target) {
this.detach();
}
this._target = target;
if (useFallback) {
document.body.appendChild(this._canvas);
const options = { capture: true, passive: true };
this._target.addEventListener('mouseover', this._eventHandlers.mouseover, options);
this._target.addEventListener('mouseleave', this._eventHandlers.mouseleave, options);
this._target.addEventListener('mousemove', this._eventHandlers.mousemove, options);
this._target.addEventListener('mouseup', this._eventHandlers.mouseup, options);
}
this.clear();
}
detach() {
if (!this._target) {
return;
}
if (useFallback) {
const options = { capture: true, passive: true };
this._target.removeEventListener('mouseover', this._eventHandlers.mouseover, options);
this._target.removeEventListener('mouseleave', this._eventHandlers.mouseleave, options);
this._target.removeEventListener('mousemove', this._eventHandlers.mousemove, options);
this._target.removeEventListener('mouseup', this._eventHandlers.mouseup, options);
document.body.removeChild(this._canvas);
}
this._target = null;
}
change(rgba, hotx, hoty, w, h) {
if ((w === 0) || (h === 0)) {
this.clear();
return;
}
this._position.x = this._position.x + this._hotSpot.x - hotx;
this._position.y = this._position.y + this._hotSpot.y - hoty;
this._hotSpot.x = hotx;
this._hotSpot.y = hoty;
let ctx = this._canvas.getContext('2d');
this._canvas.width = w;
this._canvas.height = h;
let img = new ImageData(new Uint8ClampedArray(rgba), w, h);
ctx.clearRect(0, 0, w, h);
ctx.putImageData(img, 0, 0);
if (useFallback) {
this._updatePosition();
} else {
let url = this._canvas.toDataURL();
this._target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
}
}
clear() {
this._target.style.cursor = 'none';
this._canvas.width = 0;
this._canvas.height = 0;
this._position.x = this._position.x + this._hotSpot.x;
this._position.y = this._position.y + this._hotSpot.y;
this._hotSpot.x = 0;
this._hotSpot.y = 0;
}
// Mouse events might be emulated, this allows
// moving the cursor in such cases
move(clientX, clientY) {
if (!useFallback) {
return;
}
// clientX/clientY are relative the _visual viewport_,
// but our position is relative the _layout viewport_,
// so try to compensate when we can
if (window.visualViewport) {
this._position.x = clientX + window.visualViewport.offsetLeft;
this._position.y = clientY + window.visualViewport.offsetTop;
} else {
this._position.x = clientX;
this._position.y = clientY;
}
this._updatePosition();
let target = document.elementFromPoint(clientX, clientY);
this._updateVisibility(target);
}
_handleMouseOver(event) {
// This event could be because we're entering the target, or
// moving around amongst its sub elements. Let the move handler
// sort things out.
this._handleMouseMove(event);
}
_handleMouseLeave(event) {
// Check if we should show the cursor on the element we are leaving to
this._updateVisibility(event.relatedTarget);
}
_handleMouseMove(event) {
this._updateVisibility(event.target);
this._position.x = event.clientX - this._hotSpot.x;
this._position.y = event.clientY - this._hotSpot.y;
this._updatePosition();
}
_handleMouseUp(event) {
// We might get this event because of a drag operation that
// moved outside of the target. Check what's under the cursor
// now and adjust visibility based on that.
let target = document.elementFromPoint(event.clientX, event.clientY);
this._updateVisibility(target);
// Captures end with a mouseup but we can't know the event order of
// mouseup vs releaseCapture.
//
// In the cases when releaseCapture comes first, the code above is
// enough.
//
// In the cases when the mouseup comes first, we need wait for the
// browser to flush all events and then check again if the cursor
// should be visible.
if (this._captureIsActive()) {
window.setTimeout(() => {
// We might have detached at this point
if (!this._target) {
return;
}
// Refresh the target from elementFromPoint since queued events
// might have altered the DOM
target = document.elementFromPoint(event.clientX,
event.clientY);
this._updateVisibility(target);
}, 0);
}
}
_showCursor() {
if (this._canvas.style.visibility === 'hidden') {
this._canvas.style.visibility = '';
}
}
_hideCursor() {
if (this._canvas.style.visibility !== 'hidden') {
this._canvas.style.visibility = 'hidden';
}
}
// Should we currently display the cursor?
// (i.e. are we over the target, or a child of the target without a
// different cursor set)
_shouldShowCursor(target) {
if (!target) {
return false;
}
// Easy case
if (target === this._target) {
return true;
}
// Other part of the DOM?
if (!this._target.contains(target)) {
return false;
}
// Has the child its own cursor?
// FIXME: How can we tell that a sub element has an
// explicit "cursor: none;"?
if (window.getComputedStyle(target).cursor !== 'none') {
return false;
}
return true;
}
_updateVisibility(target) {
// When the cursor target has capture we want to show the cursor.
// So, if a capture is active - look at the captured element instead.
if (this._captureIsActive()) {
target = document.captureElement;
}
if (this._shouldShowCursor(target)) {
this._showCursor();
} else {
this._hideCursor();
}
}
_updatePosition() {
this._canvas.style.left = this._position.x + "px";
this._canvas.style.top = this._position.y + "px";
}
_captureIsActive() {
return document.captureElement &&
document.documentElement.contains(document.captureElement);
}
}
+32
View File
@@ -0,0 +1,32 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2020 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
/*
* HTML element utility functions
*/
export function clientToElement(x, y, elem) {
const bounds = elem.getBoundingClientRect();
let pos = { x: 0, y: 0 };
// Clip to target bounds
if (x < bounds.left) {
pos.x = 0;
} else if (x >= bounds.right) {
pos.x = bounds.width - 1;
} else {
pos.x = x - bounds.left;
}
if (y < bounds.top) {
pos.y = 0;
} else if (y >= bounds.bottom) {
pos.y = bounds.height - 1;
} else {
pos.y = y - bounds.top;
}
return pos;
}
+58 -58
View File
@@ -1,6 +1,6 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
@@ -10,27 +10,32 @@
* Cross-browser event and position routines
*/
export function getPointerEvent (e) {
export function getPointerEvent(e) {
return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e;
};
}
export function stopEvent (e) {
export function stopEvent(e) {
e.stopPropagation();
e.preventDefault();
};
}
// Emulate Element.setCapture() when not supported
var _captureRecursion = false;
var _captureElem = null;
let _captureRecursion = false;
let _elementForUnflushedEvents = null;
document.captureElement = null;
function _captureProxy(e) {
// Recursion protection as we'll see our own event
if (_captureRecursion) return;
// Clone the event as we cannot dispatch an already dispatched event
var newEv = new e.constructor(e.type, e);
const newEv = new e.constructor(e.type, e);
_captureRecursion = true;
_captureElem.dispatchEvent(newEv);
if (document.captureElement) {
document.captureElement.dispatchEvent(newEv);
} else {
_elementForUnflushedEvents.dispatchEvent(newEv);
}
_captureRecursion = false;
// Avoid double events
@@ -45,94 +50,89 @@ function _captureProxy(e) {
if (e.type === "mouseup") {
releaseCapture();
}
};
}
// Follow cursor style of target element
function _captureElemChanged() {
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
captureElem.style.cursor = window.getComputedStyle(_captureElem).cursor;
};
var _captureObserver = new MutationObserver(_captureElemChanged);
function _capturedElemChanged() {
const proxyElem = document.getElementById("noVNC_mouse_capture_elem");
proxyElem.style.cursor = window.getComputedStyle(document.captureElement).cursor;
}
var _captureIndex = 0;
const _captureObserver = new MutationObserver(_capturedElemChanged);
export function setCapture (elem) {
if (elem.setCapture) {
elem.setCapture();
// IE releases capture on 'click' events which might not trigger
elem.addEventListener('mouseup', releaseCapture);
export function setCapture(target) {
if (target.setCapture) {
target.setCapture();
document.captureElement = target;
} else {
// Release any existing capture in case this method is
// called multiple times without coordination
releaseCapture();
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
let proxyElem = document.getElementById("noVNC_mouse_capture_elem");
if (captureElem === null) {
captureElem = document.createElement("div");
captureElem.id = "noVNC_mouse_capture_elem";
captureElem.style.position = "fixed";
captureElem.style.top = "0px";
captureElem.style.left = "0px";
captureElem.style.width = "100%";
captureElem.style.height = "100%";
captureElem.style.zIndex = 10000;
captureElem.style.display = "none";
document.body.appendChild(captureElem);
if (proxyElem === null) {
proxyElem = document.createElement("div");
proxyElem.id = "noVNC_mouse_capture_elem";
proxyElem.style.position = "fixed";
proxyElem.style.top = "0px";
proxyElem.style.left = "0px";
proxyElem.style.width = "100%";
proxyElem.style.height = "100%";
proxyElem.style.zIndex = 10000;
proxyElem.style.display = "none";
document.body.appendChild(proxyElem);
// This is to make sure callers don't get confused by having
// our blocking element as the target
captureElem.addEventListener('contextmenu', _captureProxy);
proxyElem.addEventListener('contextmenu', _captureProxy);
captureElem.addEventListener('mousemove', _captureProxy);
captureElem.addEventListener('mouseup', _captureProxy);
proxyElem.addEventListener('mousemove', _captureProxy);
proxyElem.addEventListener('mouseup', _captureProxy);
}
_captureElem = elem;
_captureIndex++;
document.captureElement = target;
// Track cursor and get initial cursor
_captureObserver.observe(elem, {attributes:true});
_captureElemChanged();
_captureObserver.observe(target, {attributes: true});
_capturedElemChanged();
captureElem.style.display = "";
proxyElem.style.display = "";
// We listen to events on window in order to keep tracking if it
// happens to leave the viewport
window.addEventListener('mousemove', _captureProxy);
window.addEventListener('mouseup', _captureProxy);
}
};
}
export function releaseCapture () {
export function releaseCapture() {
if (document.releaseCapture) {
document.releaseCapture();
document.captureElement = null;
} else {
if (!_captureElem) {
if (!document.captureElement) {
return;
}
// There might be events already queued, so we need to wait for
// them to flush. E.g. contextmenu in Microsoft Edge
window.setTimeout(function(expected) {
// Only clear it if it's the expected grab (i.e. no one
// else has initiated a new grab)
if (_captureIndex === expected) {
_captureElem = null;
}
}, 0, _captureIndex);
// There might be events already queued. The event proxy needs
// access to the captured element for these queued events.
// E.g. contextmenu (right-click) in Microsoft Edge
//
// Before removing the capturedElem pointer we save it to a
// temporary variable that the unflushed events can use.
_elementForUnflushedEvents = document.captureElement;
document.captureElement = null;
_captureObserver.disconnect();
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
captureElem.style.display = "none";
const proxyElem = document.getElementById("noVNC_mouse_capture_elem");
proxyElem.style.display = "none";
window.removeEventListener('mousemove', _captureProxy);
window.removeEventListener('mouseup', _captureProxy);
}
};
}
+25 -30
View File
@@ -1,40 +1,35 @@
/*
* noVNC: HTML5 VNC client
* Copyright 2017 Pierre Ossman for Cendio AB
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
var EventTargetMixin = {
_listeners: null,
export default class EventTargetMixin {
constructor() {
this._listeners = new Map();
}
addEventListener: function(type, callback) {
if (!this._listeners) {
this._listeners = new Map();
}
if (!this._listeners.has(type)) {
this._listeners.set(type, new Set());
}
this._listeners.get(type).add(callback);
},
addEventListener(type, callback) {
if (!this._listeners.has(type)) {
this._listeners.set(type, new Set());
}
this._listeners.get(type).add(callback);
}
removeEventListener: function(type, callback) {
if (!this._listeners || !this._listeners.has(type)) {
return;
}
this._listeners.get(type).delete(callback);
},
removeEventListener(type, callback) {
if (this._listeners.has(type)) {
this._listeners.get(type).delete(callback);
}
}
dispatchEvent: function(event) {
if (!this._listeners || !this._listeners.has(event.type)) {
return true;
}
this._listeners.get(event.type).forEach(function (callback) {
callback.call(this, event);
}, this);
return !event.defaultPrevented;
},
};
export default EventTargetMixin;
dispatchEvent(event) {
if (!this._listeners.has(event.type)) {
return true;
}
this._listeners.get(event.type)
.forEach(callback => callback.call(this, event));
return !event.defaultPrevented;
}
}
+15
View File
@@ -0,0 +1,15 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2020 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
export function toUnsigned32bit(toConvert) {
return toConvert >>> 0;
}
export function toSigned32bit(toConvert) {
return toConvert | 0;
}
+21 -16
View File
@@ -1,6 +1,6 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
@@ -10,22 +10,24 @@
* Logging/debug routines
*/
var _log_level = 'warn';
let _logLevel = 'warn';
var Debug = function (msg) {};
var Info = function (msg) {};
var Warn = function (msg) {};
var Error = function (msg) {};
let Debug = () => {};
let Info = () => {};
let Warn = () => {};
let Error = () => {};
export function init_logging (level) {
export function initLogging(level) {
if (typeof level === 'undefined') {
level = _log_level;
level = _logLevel;
} else {
_log_level = level;
_logLevel = level;
}
Debug = Info = Warn = Error = function (msg) {};
Debug = Info = Warn = Error = () => {};
if (typeof window.console !== "undefined") {
/* eslint-disable no-console, no-fallthrough */
switch (level) {
case 'debug':
Debug = console.debug.bind(window.console);
@@ -38,14 +40,17 @@ export function init_logging (level) {
case 'none':
break;
default:
throw new Error("invalid logging type '" + level + "'");
throw new window.Error("invalid logging type '" + level + "'");
}
/* eslint-enable no-console, no-fallthrough */
}
};
export function get_logging () {
return _log_level;
};
}
export function getLogging() {
return _logLevel;
}
export { Debug, Info, Warn, Error };
// Initialize logging level
init_logging();
initLogging();
-54
View File
@@ -1,54 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright 2017 Pierre Ossman for noVNC
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/
/* Polyfills to provide new APIs in old browsers */
/* Object.assign() (taken from MDN) */
if (typeof Object.assign != 'function') {
// Must be writable: true, enumerable: false, configurable: true
Object.defineProperty(Object, "assign", {
value: function assign(target, varArgs) { // .length of function is 2
'use strict';
if (target == null) { // TypeError if undefined or null
throw new TypeError('Cannot convert undefined or null to object');
}
var to = Object(target);
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) { // Skip over if undefined or null
for (var nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
},
writable: true,
configurable: true
});
}
/* CustomEvent constructor (taken from MDN) */
(function () {
function CustomEvent ( event, params ) {
params = params || { bubbles: false, cancelable: false, detail: undefined };
var evt = document.createEvent( 'CustomEvent' );
evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
return evt;
}
CustomEvent.prototype = window.Event.prototype;
if (typeof window.CustomEvent !== "function") {
window.CustomEvent = CustomEvent;
}
})();
+21 -8
View File
@@ -1,15 +1,28 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
/*
* Decode from UTF-8
*/
export function decodeUTF8 (utf8string) {
"use strict";
return decodeURIComponent(escape(utf8string));
};
// Decode from UTF-8
export function decodeUTF8(utf8string, allowLatin1=false) {
try {
return decodeURIComponent(escape(utf8string));
} catch (e) {
if (e instanceof URIError) {
if (allowLatin1) {
// If we allow Latin1 we can ignore any decoding fails
// and in these cases return the original string
return utf8string;
}
}
throw e;
}
}
// Encode to UTF-8
export function encodeUTF8(DOMString) {
return unescape(encodeURIComponent(DOMString));
}
+212 -175
View File
@@ -1,10 +1,10 @@
/*
* Websock: high-performance binary WebSockets
* Copyright (C) 2012 Joel Martin
* Websock: high-performance buffering wrapper
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* Websock is similar to the standard WebSocket object but with extra
* buffer handling.
* Websock is similar to the standard WebSocket / RTCDataChannel object
* but with extra buffer handling.
*
* Websock has built-in receive queue buffering; the message event
* does not contain actual data but is simply a notification that
@@ -14,143 +14,169 @@
import * as Log from './util/logging.js';
export default function Websock() {
"use strict";
this._websocket = null; // WebSocket object
this._rQi = 0; // Receive queue index
this._rQlen = 0; // Next write position in the receive queue
this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
this._rQmax = this._rQbufferSize / 8;
// called in init: this._rQ = new Uint8Array(this._rQbufferSize);
this._rQ = null; // Receive queue
this._sQbufferSize = 1024 * 10; // 10 KiB
// called in init: this._sQ = new Uint8Array(this._sQbufferSize);
this._sQlen = 0;
this._sQ = null; // Send queue
this._eventHandlers = {
'message': function () {},
'open': function () {},
'close': function () {},
'error': function () {}
};
};
// this has performance issues in some versions Chromium, and
// doesn't gain a tremendous amount of performance increase in Firefox
// at the moment. It may be valuable to turn it on in the future.
var ENABLE_COPYWITHIN = false;
const MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
var MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
// Constants pulled from RTCDataChannelState enum
// https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel/readyState#RTCDataChannelState_enum
const DataChannel = {
CONNECTING: "connecting",
OPEN: "open",
CLOSING: "closing",
CLOSED: "closed"
};
var typedArrayToString = (function () {
// This is only for PhantomJS, which doesn't like apply-ing
// with Typed Arrays
try {
var arr = new Uint8Array([1, 2, 3]);
String.fromCharCode.apply(null, arr);
return function (a) { return String.fromCharCode.apply(null, a); };
} catch (ex) {
return function (a) {
return String.fromCharCode.apply(
null, Array.prototype.slice.call(a));
const ReadyStates = {
CONNECTING: [WebSocket.CONNECTING, DataChannel.CONNECTING],
OPEN: [WebSocket.OPEN, DataChannel.OPEN],
CLOSING: [WebSocket.CLOSING, DataChannel.CLOSING],
CLOSED: [WebSocket.CLOSED, DataChannel.CLOSED],
};
// Properties a raw channel must have, WebSocket and RTCDataChannel are two examples
const rawChannelProps = [
"send",
"close",
"binaryType",
"onerror",
"onmessage",
"onopen",
"protocol",
"readyState",
];
export default class Websock {
constructor() {
this._websocket = null; // WebSocket or RTCDataChannel object
this._rQi = 0; // Receive queue index
this._rQlen = 0; // Next write position in the receive queue
this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
// called in init: this._rQ = new Uint8Array(this._rQbufferSize);
this._rQ = null; // Receive queue
this._sQbufferSize = 1024 * 10; // 10 KiB
// called in init: this._sQ = new Uint8Array(this._sQbufferSize);
this._sQlen = 0;
this._sQ = null; // Send queue
this._eventHandlers = {
message: () => {},
open: () => {},
close: () => {},
error: () => {}
};
}
})();
Websock.prototype = {
// Getters and Setters
get_sQ: function () {
get readyState() {
let subState;
if (this._websocket === null) {
return "unused";
}
subState = this._websocket.readyState;
if (ReadyStates.CONNECTING.includes(subState)) {
return "connecting";
} else if (ReadyStates.OPEN.includes(subState)) {
return "open";
} else if (ReadyStates.CLOSING.includes(subState)) {
return "closing";
} else if (ReadyStates.CLOSED.includes(subState)) {
return "closed";
}
return "unknown";
}
get sQ() {
return this._sQ;
},
}
get_rQ: function () {
get rQ() {
return this._rQ;
},
}
get_rQi: function () {
get rQi() {
return this._rQi;
},
}
set_rQi: function (val) {
set rQi(val) {
this._rQi = val;
},
}
// Receive Queue
rQlen: function () {
get rQlen() {
return this._rQlen - this._rQi;
},
}
rQpeek8: function () {
rQpeek8() {
return this._rQ[this._rQi];
},
}
rQshift8: function () {
return this._rQ[this._rQi++];
},
rQskipBytes(bytes) {
this._rQi += bytes;
}
rQskip8: function () {
this._rQi++;
},
rQshift8() {
return this._rQshift(1);
}
rQskipBytes: function (num) {
this._rQi += num;
},
rQshift16() {
return this._rQshift(2);
}
rQshift32() {
return this._rQshift(4);
}
// TODO(directxman12): test performance with these vs a DataView
rQshift16: function () {
return (this._rQ[this._rQi++] << 8) +
this._rQ[this._rQi++];
},
_rQshift(bytes) {
let res = 0;
for (let byte = bytes - 1; byte >= 0; byte--) {
res += this._rQ[this._rQi++] << (byte * 8);
}
return res;
}
rQshift32: function () {
return (this._rQ[this._rQi++] << 24) +
(this._rQ[this._rQi++] << 16) +
(this._rQ[this._rQi++] << 8) +
this._rQ[this._rQi++];
},
rQshiftStr(len) {
if (typeof(len) === 'undefined') { len = this.rQlen; }
let str = "";
// Handle large arrays in steps to avoid long strings on the stack
for (let i = 0; i < len; i += 4096) {
let part = this.rQshiftBytes(Math.min(4096, len - i));
str += String.fromCharCode.apply(null, part);
}
return str;
}
rQshiftStr: function (len) {
if (typeof(len) === 'undefined') { len = this.rQlen(); }
var arr = new Uint8Array(this._rQ.buffer, this._rQi, len);
this._rQi += len;
return typedArrayToString(arr);
},
rQshiftBytes: function (len) {
if (typeof(len) === 'undefined') { len = this.rQlen(); }
rQshiftBytes(len) {
if (typeof(len) === 'undefined') { len = this.rQlen; }
this._rQi += len;
return new Uint8Array(this._rQ.buffer, this._rQi - len, len);
},
}
rQshiftTo: function (target, len) {
if (len === undefined) { len = this.rQlen(); }
rQshiftTo(target, len) {
if (len === undefined) { len = this.rQlen; }
// TODO: make this just use set with views when using a ArrayBuffer to store the rQ
target.set(new Uint8Array(this._rQ.buffer, this._rQi, len));
this._rQi += len;
},
}
rQwhole: function () {
return new Uint8Array(this._rQ.buffer, 0, this._rQlen);
},
rQslice: function (start, end) {
if (end) {
return new Uint8Array(this._rQ.buffer, this._rQi + start, end - start);
} else {
return new Uint8Array(this._rQ.buffer, this._rQi + start, this._rQlen - this._rQi - start);
}
},
rQslice(start, end = this.rQlen) {
return new Uint8Array(this._rQ.buffer, this._rQi + start, end - start);
}
// Check to see if we must wait for 'num' bytes (default to FBU.bytes)
// to be available in the receive queue. Return true if we need to
// wait (and possibly print a debug message), otherwise false.
rQwait: function (msg, num, goback) {
var rQlen = this._rQlen - this._rQi; // Skip rQlen() function call
if (rQlen < num) {
rQwait(msg, num, goback) {
if (this.rQlen < num) {
if (goback) {
if (this._rQi < goback) {
throw new Error("rQwait cannot backup " + goback + " bytes");
@@ -160,58 +186,68 @@ Websock.prototype = {
return true; // true means need more data
}
return false;
},
}
// Send Queue
flush: function () {
if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) {
this._websocket.send(this._encode_message());
flush() {
if (this._sQlen > 0 && this.readyState === 'open') {
this._websocket.send(this._encodeMessage());
this._sQlen = 0;
}
},
}
send: function (arr) {
send(arr) {
this._sQ.set(arr, this._sQlen);
this._sQlen += arr.length;
this.flush();
},
}
send_string: function (str) {
this.send(str.split('').map(function (chr) {
return chr.charCodeAt(0);
}));
},
sendString(str) {
this.send(str.split('').map(chr => chr.charCodeAt(0)));
}
// Event Handlers
off: function (evt) {
this._eventHandlers[evt] = function () {};
},
off(evt) {
this._eventHandlers[evt] = () => {};
}
on: function (evt, handler) {
on(evt, handler) {
this._eventHandlers[evt] = handler;
},
}
_allocate_buffers: function () {
_allocateBuffers() {
this._rQ = new Uint8Array(this._rQbufferSize);
this._sQ = new Uint8Array(this._sQbufferSize);
},
}
init: function () {
this._allocate_buffers();
init() {
this._allocateBuffers();
this._rQi = 0;
this._websocket = null;
},
}
open: function (uri, protocols) {
var ws_schema = uri.match(/^([a-z]+):\/\//)[1];
open(uri, protocols) {
this.attach(new WebSocket(uri, protocols));
}
attach(rawChannel) {
this.init();
this._websocket = new WebSocket(uri, protocols);
this._websocket.binaryType = 'arraybuffer';
// Must get object and class methods to be compatible with the tests.
const channelProps = [...Object.keys(rawChannel), ...Object.getOwnPropertyNames(Object.getPrototypeOf(rawChannel))];
for (let i = 0; i < rawChannelProps.length; i++) {
const prop = rawChannelProps[i];
if (channelProps.indexOf(prop) < 0) {
throw new Error('Raw channel missing property: ' + prop);
}
}
this._websocket.onmessage = this._recv_message.bind(this);
this._websocket.onopen = (function () {
this._websocket = rawChannel;
this._websocket.binaryType = "arraybuffer";
this._websocket.onmessage = this._recvMessage.bind(this);
this._websocket.onopen = () => {
Log.Debug('>> WebSock.onopen');
if (this._websocket.protocol) {
Log.Info("Server choose sub-protocol: " + this._websocket.protocol);
@@ -219,98 +255,99 @@ Websock.prototype = {
this._eventHandlers.open();
Log.Debug("<< WebSock.onopen");
}).bind(this);
this._websocket.onclose = (function (e) {
};
this._websocket.onclose = (e) => {
Log.Debug(">> WebSock.onclose");
this._eventHandlers.close(e);
Log.Debug("<< WebSock.onclose");
}).bind(this);
this._websocket.onerror = (function (e) {
};
this._websocket.onerror = (e) => {
Log.Debug(">> WebSock.onerror: " + e);
this._eventHandlers.error(e);
Log.Debug("<< WebSock.onerror: " + e);
}).bind(this);
},
};
}
close: function () {
close() {
if (this._websocket) {
if ((this._websocket.readyState === WebSocket.OPEN) ||
(this._websocket.readyState === WebSocket.CONNECTING)) {
if (this.readyState === 'connecting' ||
this.readyState === 'open') {
Log.Info("Closing WebSocket connection");
this._websocket.close();
}
this._websocket.onmessage = function (e) { return; };
this._websocket.onmessage = () => {};
}
},
}
// private methods
_encode_message: function () {
_encodeMessage() {
// Put in a binary arraybuffer
// according to the spec, you can send ArrayBufferViews with the send method
return new Uint8Array(this._sQ.buffer, 0, this._sQlen);
},
}
// We want to move all the unread data to the start of the queue,
// e.g. compacting.
// The function also expands the receive que if needed, and for
// performance reasons we combine these two actions to avoid
// unneccessary copying.
_expandCompactRQ(minFit) {
// if we're using less than 1/8th of the buffer even with the incoming bytes, compact in place
// instead of resizing
const requiredBufferSize = (this._rQlen - this._rQi + minFit) * 8;
const resizeNeeded = this._rQbufferSize < requiredBufferSize;
_expand_compact_rQ: function (min_fit) {
var resizeNeeded = min_fit || this._rQlen - this._rQi > this._rQbufferSize / 2;
if (resizeNeeded) {
if (!min_fit) {
// just double the size if we need to do compaction
this._rQbufferSize *= 2;
} else {
// otherwise, make sure we satisy rQlen - rQi + min_fit < rQbufferSize / 8
this._rQbufferSize = (this._rQlen - this._rQi + min_fit) * 8;
}
// Make sure we always *at least* double the buffer size, and have at least space for 8x
// the current amount of data
this._rQbufferSize = Math.max(this._rQbufferSize * 2, requiredBufferSize);
}
// we don't want to grow unboundedly
if (this._rQbufferSize > MAX_RQ_GROW_SIZE) {
this._rQbufferSize = MAX_RQ_GROW_SIZE;
if (this._rQbufferSize - this._rQlen - this._rQi < min_fit) {
throw new Exception("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit");
if (this._rQbufferSize - this.rQlen < minFit) {
throw new Error("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit");
}
}
if (resizeNeeded) {
var old_rQbuffer = this._rQ.buffer;
this._rQmax = this._rQbufferSize / 8;
const oldRQbuffer = this._rQ.buffer;
this._rQ = new Uint8Array(this._rQbufferSize);
this._rQ.set(new Uint8Array(old_rQbuffer, this._rQi));
this._rQ.set(new Uint8Array(oldRQbuffer, this._rQi, this._rQlen - this._rQi));
} else {
if (ENABLE_COPYWITHIN) {
this._rQ.copyWithin(0, this._rQi);
} else {
this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi));
}
this._rQ.copyWithin(0, this._rQi, this._rQlen);
}
this._rQlen = this._rQlen - this._rQi;
this._rQi = 0;
},
}
_decode_message: function (data) {
// push arraybuffer values onto the end
var u8 = new Uint8Array(data);
// push arraybuffer values onto the end of the receive que
_DecodeMessage(data) {
const u8 = new Uint8Array(data);
if (u8.length > this._rQbufferSize - this._rQlen) {
this._expand_compact_rQ(u8.length);
this._expandCompactRQ(u8.length);
}
this._rQ.set(u8, this._rQlen);
this._rQlen += u8.length;
},
}
_recv_message: function (e) {
this._decode_message(e.data);
if (this.rQlen() > 0) {
_recvMessage(e) {
this._DecodeMessage(e.data);
if (this.rQlen > 0) {
this._eventHandlers.message();
// Compact the receive queue
if (this._rQlen == this._rQi) {
// All data has now been processed, this means we
// can reset the receive queue.
this._rQlen = 0;
this._rQi = 0;
} else if (this._rQlen > this._rQmax) {
this._expand_compact_rQ();
}
} else {
Log.Debug("Ignoring empty message");
}
}
};
}
+9 -45
View File
@@ -11,9 +11,6 @@ official external API.
## 1.1 Module List
* __Mouse__ (core/input/mouse.js): Mouse input event handler with
limited touch support.
* __Keyboard__ (core/input/keyboard.js): Keyboard input event handler with
non-US keyboard support. Translates keyDown and keyUp events to X11
keysym values.
@@ -23,7 +20,7 @@ layered on the HTML5 canvas element.
* __Websock__ (core/websock.js): Websock client from websockify
with transparent binary data support.
[Websock API](https://github.com/novnc/websockify/wiki/websock.js) wiki page.
[Websock API](https://github.com/novnc/websockify-js/wiki/websock.js) wiki page.
## 1.2 Callbacks
@@ -35,62 +32,38 @@ callback event name, and the callback function.
## 2. Modules
## 2.1 Mouse Module
## 2.1 Keyboard Module
### 2.1.1 Configuration Attributes
| name | type | mode | default | description
| ----------- | ---- | ---- | -------- | ------------
| touchButton | int | RW | 1 | Button mask (1, 2, 4) for which click to send on touch devices. 0 means ignore clicks.
### 2.1.2 Methods
| name | parameters | description
| ------ | ---------- | ------------
| grab | () | Begin capturing mouse events
| ungrab | () | Stop capturing mouse events
### 2.1.2 Callbacks
| name | parameters | description
| ------------- | ------------------- | ------------
| onmousebutton | (x, y, down, bmask) | Handler for mouse button click/release
| onmousemove | (x, y) | Handler for mouse movement
## 2.2 Keyboard Module
### 2.2.1 Configuration Attributes
None
### 2.2.2 Methods
### 2.1.2 Methods
| name | parameters | description
| ------ | ---------- | ------------
| grab | () | Begin capturing keyboard events
| ungrab | () | Stop capturing keyboard events
### 2.2.3 Callbacks
### 2.1.3 Callbacks
| name | parameters | description
| ---------- | -------------------- | ------------
| onkeypress | (keysym, code, down) | Handler for key press/release
## 2.3 Display Module
## 2.2 Display Module
### 2.3.1 Configuration Attributes
### 2.2.1 Configuration Attributes
| name | type | mode | default | description
| ------------ | ----- | ---- | ------- | ------------
| logo | raw | RW | | Logo to display when cleared: {"width": width, "height": height, "type": mime-type, "data": data}
| scale | float | RW | 1.0 | Display area scale factor 0.0 - 1.0
| clipViewport | bool | RW | false | Use viewport clipping
| width | int | RO | | Display area width
| height | int | RO | | Display area height
### 2.3.2 Methods
### 2.2.2 Methods
| name | parameters | description
| ------------------ | ------------------------------------------------------- | ------------
@@ -100,25 +73,16 @@ None
| absY | (y) | Return Y relative to the remote display
| resize | (width, height) | Set width and height
| flip | (from_queue) | Update the visible canvas with the contents of the rendering canvas
| clear | () | Clear the display (show logo if set)
| pending | () | Check if there are waiting items in the render queue
| flush | () | Resume processing the render queue unless it's empty
| fillRect | (x, y, width, height, color, from_queue) | Draw a filled in rectangle
| copyImage | (old_x, old_y, new_x, new_y, width, height, from_queue) | Copy a rectangular area
| imageRect | (x, y, mime, arr) | Draw a rectangle with an image
| startTile | (x, y, width, height, color) | Begin updating a tile
| subTile | (tile, x, y, w, h, color) | Update a sub-rectangle within the given tile
| finishTile | () | Draw the current tile to the display
| imageRect | (x, y, width, height, mime, arr) | Draw a rectangle with an image
| blitImage | (x, y, width, height, arr, offset, from_queue) | Blit pixels (of R,G,B,A) to the display
| blitRgbImage | (x, y, width, height, arr, offset, from_queue) | Blit RGB encoded image to display
| blitRgbxImage | (x, y, width, height, arr, offset, from_queue) | Blit RGBX encoded image to display
| drawImage | (img, x, y) | Draw image and track damage
| changeCursor | (pixels, mask, hotx, hoty, w, h) | Change cursor appearance
| defaultCursor | () | Restore default cursor appearance
| disableLocalCursor | () | Disable local (client-side) cursor
| autoscale | (containerWidth, containerHeight) | Scale the display
### 2.3.3 Callbacks
### 2.2.3 Callbacks
| name | parameters | description
| ------- | ---------- | ------------
+33 -12
View File
@@ -24,13 +24,7 @@ protocol stream.
`focusOnClick`
- Is a `boolean` indicating if keyboard focus should automatically be
moved to the remote session when a `mousedown` or `touchstart`
event is received.
`touchButton`
- Is a `long` controlling the button mask that should be simulated
when a touch event is recieved. Uses the same values as
[`MouseEvent.button`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button).
Is set to `1` by default.
event is received. Enabled by default.
`clipViewport`
- Is a `boolean` indicating if the remote session should be clipped
@@ -53,6 +47,30 @@ protocol stream.
should be sent whenever the container changes dimensions. Disabled
by default.
`showDotCursor`
- Is a `boolean` indicating whether a dot cursor should be shown
instead of a zero-sized or fully-transparent cursor if the server
sets such invisible cursor. Disabled by default.
`background`
- Is a valid CSS [background](https://developer.mozilla.org/en-US/docs/Web/CSS/background)
style value indicating which background style should be applied
to the element containing the remote session screen. The default value is `rgb(40, 40, 40)`
(solid gray color).
`qualityLevel`
- Is an `int` in range `[0-9]` controlling the desired JPEG quality.
Value `0` implies low quality and `9` implies high quality.
Default value is `6`.
`compressionLevel`
- Is an `int` in range `[0-9]` controlling the desired compression
level. Value `0` means no compression. Level 1 uses a minimum of CPU
resources and achieves weak compression ratios, while level 9 offers
best compression but is slow in terms of CPU consumption on the server
side. Use high levels with very slow network connections.
Default value is `2`.
`capabilities` *Read only*
- Is an `Object` indicating which optional extensions are available
on the server. Some methods may only be called if the corresponding
@@ -137,7 +155,7 @@ connection to a specified VNC server.
##### Syntax
var rfb = new RFB( target, url [, options] );
let rfb = new RFB( target, url [, options] );
###### Parameters
@@ -147,9 +165,9 @@ connection to a specified VNC server.
existing contents of the `HTMLElement` will be untouched, but new
elements will be added during the lifetime of the `RFB` object.
**`url`**
**`urlOrDataChannel`**
- A `DOMString` specifying the VNC server to connect to. This must be
a valid WebSocket URL.
a valid WebSocket URL. This can also be a `WebSocket` or `RTCDataChannel`.
**`options`** *Optional*
- An `Object` specifying extra details about how the connection
@@ -176,6 +194,10 @@ connection to a specified VNC server.
- A `DOMString` specifying the ID to provide to any VNC repeater
encountered.
`wsProtocols`
- An `Array` of `DOMString`s specifying the sub-protocols to use
in the WebSocket connection. Empty by default.
#### connect
The `connect` event is fired after all the handshaking with the server
@@ -360,5 +382,4 @@ to the remote server.
###### Parameters
**`text`**
- A `DOMString` specifying the clipboard data to send. Currently only
characters from ISO 8859-1 are supported.
- A `DOMString` specifying the clipboard data to send.
+35 -13
View File
@@ -61,23 +61,45 @@ query string. Currently the following options are available:
* `resize` - How to resize the remote session if it is not the same size as
the browser window. Can be one of `off`, `scale` and `remote`.
* `quality` - The session JPEG quality level. Can be `0` to `9`.
* `compression` - The session compression level. Can be `0` to `9`.
* `show_dot` - If a dot cursor should be shown when the remote server provides
no local cursor, or provides a fully-transparent (invisible) cursor.
* `logging` - The console log level. Can be one of `error`, `warn`, `info` or
`debug`.
## Pre-conversion of Modules
## HTTP Serving Considerations
### Browser Cache Issue
noVNC is written using ECMAScript 6 modules. Many of the major browsers support
these modules natively, but not all. By default the noVNC application includes
a script that can convert these modules to an older format as they are being
loaded. However this process can be slow and severely increases the load time
for the application.
If you serve noVNC files using a web server that provides an ETag header, and
include any options in the query string, a nasty browser cache issue can bite
you on upgrade, resulting in a red error box. The issue is caused by a mismatch
between the new vnc.html (which is reloaded because the user has used it with
new query string after the upgrade) and the old javascript files (that the
browser reuses from its cache). To avoid this issue, the browser must be told
to always revalidate cached files using conditional requests. The correct
semantics are achieved via the (confusingly named) `Cache-Control: no-cache`
header that needs to be provided in the web server responses.
It is possible to perform this conversion ahead of time, avoiding the extra
load times. To do this please follow these steps:
### Example Server Configurations
1. Install Node.js
2. Run `npm install` in the noVNC directory
3. Run `./utils/use_require.js --with-app --as commonjs`
Apache:
This will produce a `build/` directory that includes everything needed to run
the noVNC application.
```
# In the main configuration file
# (Debian/Ubuntu users: use "a2enmod headers" instead)
LoadModule headers_module modules/mod_headers.so
# In the <Directory> or <Location> block related to noVNC
Header set Cache-Control "no-cache"
```
Nginx:
```
# In the location block related to noVNC
add_header Cache-Control no-cache;
```
+3 -7
View File
@@ -18,18 +18,14 @@ do things.
## Conversion of Modules
noVNC is written using ECMAScript 6 modules. Many of the major browsers support
these modules natively, but not all. They are also not supported by Node.js. To
use noVNC in these places the library must first be converted.
noVNC is written using ECMAScript 6 modules. This is not supported by older
versions of Node.js. To use noVNC with those older versions of Node.js the
library must first be converted.
Fortunately noVNC includes a script to handle this conversion. Please follow
the following steps:
1. Install Node.js
2. Run `npm install` in the noVNC directory
3. Run `./utils/use_require.js --as <module format>`
Several module formats are available. Please run
`./utils/use_require.js --help` to see them all.
The result of the conversion is available in the `lib/` directory.
-1
View File
@@ -1 +0,0 @@
1.0.0-testing.2
+37
View File
@@ -0,0 +1,37 @@
.TH novnc_proxy 1 "June 25, 2020" "version 1.2.0" "USER COMMANDS"
.SH NAME
novnc_proxy - noVNC proxy server
.SH SYNOPSIS
.B novnc_proxy [--listen PORT] [--vnc VNC_HOST:PORT] [--cert CERT] [--ssl-only]
Starts the WebSockets proxy and a mini-webserver and
provides a cut-and-paste URL to go to.
--listen PORT Port for proxy/webserver to listen on
Default: 6080
--vnc VNC_HOST:PORT VNC server host:port proxy target
Default: localhost:5900
--cert CERT Path to combined cert/key file, or just
the cert file if used with --key
Default: self.pem
--key KEY Path to key file, when not combined with cert
--web WEB Path to web files (e.g. vnc.html)
Default: ./
--ssl-only Disable non-https connections.
--record FILE Record traffic to FILE.session.js
--syslog SERVER Can be local socket such as /dev/log, or a UDP host:port pair.
--heartbeat SEC send a ping to the client every SEC seconds
--timeout SEC after SEC seconds exit when not connected
--idle-timeout SEC server exits after SEC seconds if there are no
active connections
.SH AUTHOR
The noVNC Authors
https://github.com/novnc/noVNC
.SH SEE ALSO
websockify(1), nova-novncproxy(1)
-34
View File
@@ -1,34 +0,0 @@
- Decide a new version number X.Y.Z (follow SemVer)
- Update version in package.json
- Update version in docs/VERSION
- Commit the change with a commit like "Release X.Y.Z"
- Add a new release on GitHub called "vX.Y.Z", and populate it with
release notes of the following form (where A.B.C is the last release):
Major Changes Since A.B.C
=========================
*Insert warnings here about incompatibilities*
*Thanks to all the contributors who filed bugs, added features, and fixed bugs
during this release :tada:*
App-visible Changes
-------------------
- *feature* a feature which improves the app usage (#PRNUM)
- *bugfix* a bug fix which fixes the app usage (#PRNUM)
- *refactor* a refactor which changes the app usage (#PRNUM)
Library-visible Changes
-----------------------
- *feature* a feature which improves the noVNC APIs (#PRNUM)
- *bugfix* a bug fix which fixes the noVNC APIs (#PRNUM)
- *refactor* a refactor which changes the noVNC APIs (#PRNUM)
App-internals Changes
---------------------
- *bugfix* a bug fix with affects the internals of noVNC only (#PRNUM)
- *refactor* a refactor which affects the internals of noVNC only (#PRNUM)
+31 -100
View File
@@ -1,73 +1,43 @@
// Karma configuration
module.exports = function(config) {
var customLaunchers = {};
var browsers = [];
var useSauce = false;
// The Safari launcher is broken, so construct our own
function SafariBrowser(id, baseBrowserDecorator, args) {
baseBrowserDecorator(this);
// use Sauce when running on Travis
if (process.env.TRAVIS_JOB_NUMBER) {
useSauce = true;
this._start = function(url) {
this._execCommand('/usr/bin/open', ['-W', '-n', '-a', 'Safari', url]);
}
}
SafariBrowser.prototype = {
name: 'Safari'
}
module.exports = (config) => {
let browsers = [];
if (process.env.TEST_BROWSER_NAME) {
browsers = process.env.TEST_BROWSER_NAME.split(',');
}
if (useSauce && process.env.TEST_BROWSER_NAME && process.env.TEST_BROWSER_NAME != 'PhantomJS') {
var names = process.env.TEST_BROWSER_NAME.split(',');
var platforms = process.env.TEST_BROWSER_OS.split(',');
var versions = [];
if (process.env.TEST_BROWSER_VERSION) {
versions = process.env.TEST_BROWSER_VERSION.split(',');
} else {
versions = [null];
}
for (var i = 0; i < names.length; i++) {
for (var j = 0; j < platforms.length; j++) {
for (var k = 0; k < versions.length; k++) {
var launcher_name = 'sl_' + platforms[j].replace(/[^a-zA-Z0-9]/g, '') + '_' + names[i];
if (versions[k]) {
launcher_name += '_' + versions[k];
}
customLaunchers[launcher_name] = {
base: 'SauceLabs',
browserName: names[i],
platform: platforms[j],
};
if (versions[i]) {
customLaunchers[launcher_name].version = versions[k];
}
}
}
}
browsers = Object.keys(customLaunchers);
} else {
useSauce = false;
//browsers = ['PhantomJS'];
browsers = [];
}
var my_conf = {
const my_conf = {
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['requirejs', 'mocha', 'chai'],
frameworks: ['mocha', 'sinon-chai'],
// list of files / patterns to load in the browser (loaded in order)
files: [
{ pattern: 'vendor/sinon.js', included: false },
{ pattern: 'node_modules/sinon-chai/lib/sinon-chai.js', included: false },
{ pattern: 'app/localization.js', included: false },
{ pattern: 'core/**/*.js', included: false },
{ pattern: 'vendor/pako/**/*.js', included: false },
{ pattern: 'tests/test.*.js', included: false },
{ pattern: 'tests/fake.*.js', included: false },
{ pattern: 'tests/assertions.js', included: false },
'tests/karma-test-main.js',
{ pattern: 'app/localization.js', included: false, type: 'module' },
{ pattern: 'app/webutil.js', included: false, type: 'module' },
{ pattern: 'core/**/*.js', included: false, type: 'module' },
{ pattern: 'vendor/pako/**/*.js', included: false, type: 'module' },
{ pattern: 'tests/test.*.js', type: 'module' },
{ pattern: 'tests/fake.*.js', included: false, type: 'module' },
{ pattern: 'tests/assertions.js', type: 'module' },
],
client: {
@@ -82,44 +52,22 @@ module.exports = function(config) {
exclude: [
],
customLaunchers: customLaunchers,
plugins: [
'karma-*',
'@chiragrupani/karma-chromium-edge-launcher',
{ 'launcher:Safari': [ 'type', SafariBrowser ] },
],
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: browsers,
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'app/localization.js': ['babel'],
'core/**/*.js': ['babel'],
'tests/test.*.js': ['babel'],
'tests/fake.*.js': ['babel'],
'tests/assertions.js': ['babel'],
'vendor/pako/**/*.js': ['babel'],
},
babelPreprocessor: {
options: {
plugins: ['transform-es2015-modules-amd', 'syntax-dynamic-import'],
sourceMap: 'inline',
},
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['mocha'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
@@ -131,24 +79,7 @@ module.exports = function(config) {
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: true,
// Increase timeout in case connection is slow/we run more browsers than possible
// (we currently get 3 for free, and we try to run 7, so it can take a while)
captureTimeout: 240000,
// similarly to above
browserNoActivityTimeout: 100000,
};
if (useSauce) {
my_conf.reporters.push('saucelabs');
my_conf.captureTimeout = 0; // use SL timeout
my_conf.sauceLabs = {
testName: 'noVNC Tests (all)',
startConnect: false,
tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
};
}
config.set(my_conf);
};
+45 -28
View File
@@ -1,14 +1,27 @@
{
"name": "@novnc/novnc",
"version": "1.0.0-beta",
"version": "1.3.0-beta",
"description": "An HTML5 VNC client",
"browser": "lib/rfb",
"directories": {
"lib": "lib",
"doc": "docs",
"test": "tests"
},
"files": [
"lib",
"AUTHORS",
"VERSION",
"docs/API.md",
"docs/LIBRARY.md",
"docs/LICENSE*",
"core",
"vendor/pako"
],
"scripts": {
"test": "PATH=$PATH:node_modules/karma/bin karma start karma.conf.js",
"prepare": "node ./utils/use_require.js --as commonjs --clean"
"lint": "eslint app core po/po2js po/xgettext-html tests utils",
"test": "karma start karma.conf.js",
"prepublish": "node ./utils/use_require.js --clean"
},
"repository": {
"type": "git",
@@ -27,35 +40,39 @@
},
"homepage": "https://github.com/novnc/noVNC",
"devDependencies": {
"babel-core": "^6.22.1",
"babel-plugin-add-module-exports": "^0.2.1",
"@babel/core": "*",
"@babel/plugin-syntax-dynamic-import": "*",
"@babel/plugin-transform-modules-commonjs": "*",
"@babel/preset-env": "*",
"@babel/cli": "*",
"babel-plugin-import-redirect": "*",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-plugin-transform-es2015-modules-amd": "^6.22.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.18.0",
"babel-plugin-transform-es2015-modules-systemjs": "^6.22.0",
"babel-plugin-transform-es2015-modules-umd": "^6.22.0",
"babelify": "^7.3.0",
"browserify": "^13.1.0",
"chai": "^3.5.0",
"commander": "^2.9.0",
"es-module-loader": "^2.1.0",
"fs-extra": "^1.0.0",
"browserify": "*",
"babelify": "*",
"core-js": "*",
"chai": "*",
"commander": "*",
"es-module-loader": "*",
"eslint": "*",
"fs-extra": "*",
"jsdom": "*",
"karma": "^1.3.0",
"karma-babel-preprocessor": "^6.0.1",
"karma-chai": "^0.1.0",
"karma-mocha": "^1.3.0",
"karma-mocha-reporter": "^2.2.0",
"karma-requirejs": "^1.1.0",
"karma-sauce-launcher": "^1.0.0",
"mocha": "^3.1.2",
"karma": "*",
"karma-mocha": "*",
"karma-chrome-launcher": "*",
"@chiragrupani/karma-chromium-edge-launcher": "*",
"karma-firefox-launcher": "*",
"karma-ie-launcher": "*",
"karma-mocha-reporter": "*",
"karma-safari-launcher": "*",
"karma-script-launcher": "*",
"karma-sinon-chai": "*",
"mocha": "*",
"node-getopt": "*",
"po2json": "*",
"requirejs": "^2.3.2",
"rollup": "^0.41.4",
"rollup-plugin-node-resolve": "^2.0.0",
"sinon-chai": "^2.8.0"
"requirejs": "*",
"rollup": "*",
"rollup-plugin-node-resolve": "*",
"sinon": "*",
"sinon-chai": "*"
},
"dependencies": {},
"keywords": [
+5
View File
@@ -0,0 +1,5 @@
{
"env": {
"node": true,
},
}
+2 -2
View File
@@ -1,7 +1,7 @@
all:
.PHONY: update-po update-js update-pot
LINGUAS := de el es nl pl sv tr zh
LINGUAS := cs de el es fr ja ko nl pl pt_BR ru sv tr zh_CN zh_TW
VERSION := $(shell grep '"version"' ../package.json | cut -d '"' -f 4)
@@ -18,7 +18,7 @@ update-js: $(JSONFILES)
update-pot:
xgettext --output=noVNC.js.pot \
--copyright-holder="Various Authors" \
--copyright-holder="The noVNC Authors" \
--package-name="noVNC" \
--package-version="$(VERSION)" \
--msgid-bugs-address="novnc@googlegroups.com" \
+294
View File
@@ -0,0 +1,294 @@
# Czech translations for noVNC package.
# Copyright (C) 2018 The noVNC Authors
# This file is distributed under the same license as the noVNC package.
# Petr <petr@kle.cz>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: noVNC 1.0.0-testing.2\n"
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
"POT-Creation-Date: 2018-10-19 12:00+0200\n"
"PO-Revision-Date: 2018-10-19 12:00+0200\n"
"Last-Translator: Petr <petr@kle.cz>\n"
"Language-Team: Czech\n"
"Language: cs\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: ../app/ui.js:389
msgid "Connecting..."
msgstr "Připojení..."
#: ../app/ui.js:396
msgid "Disconnecting..."
msgstr "Odpojení..."
#: ../app/ui.js:402
msgid "Reconnecting..."
msgstr "Obnova připojení..."
#: ../app/ui.js:407
msgid "Internal error"
msgstr "Vnitřní chyba"
#: ../app/ui.js:997
msgid "Must set host"
msgstr "Hostitel musí být nastavení"
#: ../app/ui.js:1079
msgid "Connected (encrypted) to "
msgstr "Připojení (šifrované) k "
#: ../app/ui.js:1081
msgid "Connected (unencrypted) to "
msgstr "Připojení (nešifrované) k "
#: ../app/ui.js:1104
msgid "Something went wrong, connection is closed"
msgstr "Něco se pokazilo, odpojeno"
#: ../app/ui.js:1107
msgid "Failed to connect to server"
msgstr "Chyba připojení k serveru"
#: ../app/ui.js:1117
msgid "Disconnected"
msgstr "Odpojeno"
#: ../app/ui.js:1130
msgid "New connection has been rejected with reason: "
msgstr "Nové připojení bylo odmítnuto s odůvodněním: "
#: ../app/ui.js:1133
msgid "New connection has been rejected"
msgstr "Nové připojení bylo odmítnuto"
#: ../app/ui.js:1153
msgid "Password is required"
msgstr "Je vyžadováno heslo"
#: ../vnc.html:84
msgid "noVNC encountered an error:"
msgstr "noVNC narazilo na chybu:"
#: ../vnc.html:94
msgid "Hide/Show the control bar"
msgstr "Skrýt/zobrazit ovládací panel"
#: ../vnc.html:101
msgid "Move/Drag Viewport"
msgstr "Přesunout/přetáhnout výřez"
#: ../vnc.html:101
msgid "viewport drag"
msgstr "přesun výřezu"
#: ../vnc.html:107 ../vnc.html:110 ../vnc.html:113 ../vnc.html:116
msgid "Active Mouse Button"
msgstr "Aktivní tlačítka myši"
#: ../vnc.html:107
msgid "No mousebutton"
msgstr "Žádné"
#: ../vnc.html:110
msgid "Left mousebutton"
msgstr "Levé tlačítko myši"
#: ../vnc.html:113
msgid "Middle mousebutton"
msgstr "Prostřední tlačítko myši"
#: ../vnc.html:116
msgid "Right mousebutton"
msgstr "Pravé tlačítko myši"
#: ../vnc.html:119
msgid "Keyboard"
msgstr "Klávesnice"
#: ../vnc.html:119
msgid "Show Keyboard"
msgstr "Zobrazit klávesnici"
#: ../vnc.html:126
msgid "Extra keys"
msgstr "Extra klávesy"
#: ../vnc.html:126
msgid "Show Extra Keys"
msgstr "Zobrazit extra klávesy"
#: ../vnc.html:131
msgid "Ctrl"
msgstr "Ctrl"
#: ../vnc.html:131
msgid "Toggle Ctrl"
msgstr "Přepnout Ctrl"
#: ../vnc.html:134
msgid "Alt"
msgstr "Alt"
#: ../vnc.html:134
msgid "Toggle Alt"
msgstr "Přepnout Alt"
#: ../vnc.html:137
msgid "Send Tab"
msgstr "Odeslat tabulátor"
#: ../vnc.html:137
msgid "Tab"
msgstr "Tab"
#: ../vnc.html:140
msgid "Esc"
msgstr "Esc"
#: ../vnc.html:140
msgid "Send Escape"
msgstr "Odeslat Esc"
#: ../vnc.html:143
msgid "Ctrl+Alt+Del"
msgstr "Ctrl+Alt+Del"
#: ../vnc.html:143
msgid "Send Ctrl-Alt-Del"
msgstr "Poslat Ctrl-Alt-Del"
#: ../vnc.html:151
msgid "Shutdown/Reboot"
msgstr "Vypnutí/Restart"
#: ../vnc.html:151
msgid "Shutdown/Reboot..."
msgstr "Vypnutí/Restart..."
#: ../vnc.html:157
msgid "Power"
msgstr "Napájení"
#: ../vnc.html:159
msgid "Shutdown"
msgstr "Vypnout"
#: ../vnc.html:160
msgid "Reboot"
msgstr "Restart"
#: ../vnc.html:161
msgid "Reset"
msgstr "Reset"
#: ../vnc.html:166 ../vnc.html:172
msgid "Clipboard"
msgstr "Schránka"
#: ../vnc.html:176
msgid "Clear"
msgstr "Vymazat"
#: ../vnc.html:182
msgid "Fullscreen"
msgstr "Celá obrazovka"
#: ../vnc.html:187 ../vnc.html:194
msgid "Settings"
msgstr "Nastavení"
#: ../vnc.html:197
msgid "Shared Mode"
msgstr "Sdílený režim"
#: ../vnc.html:200
msgid "View Only"
msgstr "Pouze prohlížení"
#: ../vnc.html:204
msgid "Clip to Window"
msgstr "Přizpůsobit oknu"
#: ../vnc.html:207
msgid "Scaling Mode:"
msgstr "Přizpůsobení velikosti"
#: ../vnc.html:209
msgid "None"
msgstr "Žádné"
#: ../vnc.html:210
msgid "Local Scaling"
msgstr "Místní"
#: ../vnc.html:211
msgid "Remote Resizing"
msgstr "Vzdálené"
#: ../vnc.html:216
msgid "Advanced"
msgstr "Pokročilé"
#: ../vnc.html:219
msgid "Repeater ID:"
msgstr "ID opakovače"
#: ../vnc.html:223
msgid "WebSocket"
msgstr "WebSocket"
#: ../vnc.html:226
msgid "Encrypt"
msgstr "Šifrování:"
#: ../vnc.html:229
msgid "Host:"
msgstr "Hostitel:"
#: ../vnc.html:233
msgid "Port:"
msgstr "Port:"
#: ../vnc.html:237
msgid "Path:"
msgstr "Cesta"
#: ../vnc.html:244
msgid "Automatic Reconnect"
msgstr "Automatická obnova připojení"
#: ../vnc.html:247
msgid "Reconnect Delay (ms):"
msgstr "Zpoždění připojení (ms)"
#: ../vnc.html:252
msgid "Show Dot when No Cursor"
msgstr "Tečka místo chybějícího kurzoru myši"
#: ../vnc.html:257
msgid "Logging:"
msgstr "Logování:"
#: ../vnc.html:269
msgid "Disconnect"
msgstr "Odpojit"
#: ../vnc.html:288
msgid "Connect"
msgstr "Připojit"
#: ../vnc.html:298
msgid "Password:"
msgstr "Heslo"
#: ../vnc.html:302
msgid "Send Password"
msgstr "Odeslat heslo"
#: ../vnc.html:312
msgid "Cancel"
msgstr "Zrušit"
+1 -1
View File
@@ -1,6 +1,6 @@
# German translations for noVNC package
# German translation for noVNC.
# Copyright (C) 2016 Various Authors
# Copyright (C) 2018 The noVNC Authors
# This file is distributed under the same license as the noVNC package.
# Loek Janssen <loekjanssen@gmail.com>, 2016.
#
+1 -1
View File
@@ -1,5 +1,5 @@
# Greek translations for noVNC package.
# Copyright (C) 2016 Various Authors
# Copyright (C) 2018 The noVNC Authors
# This file is distributed under the same license as the noVNC package.
# Giannis Kosmas <kosmasgiannis@gmail.com>, 2016.
#
+18 -17
View File
@@ -1,16 +1,17 @@
# Spanish translations for noVNC package
# Traducciones al español para el paquete noVNC.
# Copyright (C) 2018 Various Authors
# Copyright (C) 2018 The noVNC Authors
# This file is distributed under the same license as the noVNC package.
# Juanjo Diaz <juanjo.diazmo@gmail.com>, 2018.
# Adrian Scillato <ascillato@gmail.com>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: noVNC 1.0.0-testing.2\n"
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
"POT-Creation-Date: 2017-10-06 10:07+0200\n"
"PO-Revision-Date: 2018-01-30 19:14-0800\n"
"Last-Translator: Juanjo Diaz <juanjo.diazmo@gmail.com>\n"
"PO-Revision-Date: 2021-04-23 12:00-0300\n"
"Last-Translator: Adrian Scillato <ascillato@gmail.com>\n"
"Language-Team: Spanish\n"
"Language: es\n"
"MIME-Version: 1.0\n"
@@ -40,7 +41,7 @@ msgstr "Desconectado"
#: ../app/ui.js:1052 ../core/rfb.js:248
msgid "Must set host"
msgstr "Debes configurar el host"
msgstr "Se debe configurar el host"
#: ../app/ui.js:1101
msgid "Reconnecting..."
@@ -48,7 +49,7 @@ msgstr "Reconectando..."
#: ../app/ui.js:1140
msgid "Password is required"
msgstr "Contraseña es obligatoria"
msgstr "La contraseña es obligatoria"
#: ../core/rfb.js:548
msgid "Disconnect timeout"
@@ -186,6 +187,10 @@ msgstr "Pantalla Completa"
msgid "Settings"
msgstr "Configuraciones"
#: ../vnc.html:200
msgid "Encrypt"
msgstr "Encriptar"
#: ../vnc.html:202
msgid "Shared Mode"
msgstr "Modo Compartido"
@@ -228,27 +233,23 @@ msgstr "Cursor Local"
#: ../vnc.html:229
msgid "Repeater ID:"
msgstr "ID del Repetidor"
msgstr "ID del Repetidor:"
#: ../vnc.html:233
msgid "WebSocket"
msgstr "WebSocket"
#: ../vnc.html:236
msgid "Encrypt"
msgstr ""
#: ../vnc.html:239
msgid "Host:"
msgstr "Host"
msgstr "Host:"
#: ../vnc.html:243
msgid "Port:"
msgstr "Puesto"
msgstr "Puerto:"
#: ../vnc.html:247
msgid "Path:"
msgstr "Ruta"
msgstr "Ruta:"
#: ../vnc.html:254
msgid "Automatic Reconnect"
@@ -256,11 +257,11 @@ msgstr "Reconexión automática"
#: ../vnc.html:257
msgid "Reconnect Delay (ms):"
msgstr "Retraso en la reconexión (ms)"
msgstr "Retraso en la reconexión (ms):"
#: ../vnc.html:263
msgid "Logging:"
msgstr "Logging"
msgstr "Registrando:"
#: ../vnc.html:275
msgid "Disconnect"
@@ -272,7 +273,7 @@ msgstr "Conectar"
#: ../vnc.html:304
msgid "Password:"
msgstr "Contraseña"
msgstr "Contraseña:"
#: ../vnc.html:318
msgid "Cancel"
@@ -280,4 +281,4 @@ msgstr "Cancelar"
#: ../vnc.html:334
msgid "Canvas not supported."
msgstr "Canvas no está soportado"
msgstr "Canvas no soportado."
+299
View File
@@ -0,0 +1,299 @@
# French translations for noVNC package
# Traductions françaises du paquet noVNC.
# Copyright (C) 2021 The noVNC Authors
# This file is distributed under the same license as the noVNC package.
# Jose <jose.matsuda@canada.ca>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: noVNC 1.2.0\n"
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
"POT-Creation-Date: 2020-07-03 16:11+0200\n"
"PO-Revision-Date: 2021-05-05 20:19-0400\n"
"Last-Translator: Jose <jose.matsuda@canada.ca>\n"
"Language-Team: French\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: ../app/ui.js:394
msgid "Connecting..."
msgstr "En cours de connexion..."
#: ../app/ui.js:401
msgid "Disconnecting..."
msgstr "Déconnexion en cours..."
#: ../app/ui.js:407
msgid "Reconnecting..."
msgstr "Reconnexion en cours..."
#: ../app/ui.js:412
msgid "Internal error"
msgstr "Erreur interne"
#: ../app/ui.js:1008
msgid "Must set host"
msgstr "Doit définir l'hôte"
#: ../app/ui.js:1090
msgid "Connected (encrypted) to "
msgstr "Connecté (crypté) à "
#: ../app/ui.js:1092
msgid "Connected (unencrypted) to "
msgstr "Connecté (non crypté) à "
#: ../app/ui.js:1115
msgid "Something went wrong, connection is closed"
msgstr "Quelque chose est arrivé, la connexion est fermée"
#: ../app/ui.js:1118
msgid "Failed to connect to server"
msgstr "Échec de connexion au serveur"
#: ../app/ui.js:1128
msgid "Disconnected"
msgstr "Déconnecté"
#: ../app/ui.js:1143
msgid "New connection has been rejected with reason: "
msgstr "Une nouvelle connexion a été rejetée avec raison: "
#: ../app/ui.js:1146
msgid "New connection has been rejected"
msgstr "Une nouvelle connexion a été rejetée"
#: ../app/ui.js:1181
msgid "Credentials are required"
msgstr "Les identifiants sont requis"
#: ../vnc.html:74
msgid "noVNC encountered an error:"
msgstr "noVNC a rencontré une erreur:"
#: ../vnc.html:84
msgid "Hide/Show the control bar"
msgstr "Masquer/Afficher la barre de contrôle"
#: ../vnc.html:91
msgid "Drag"
msgstr "Faire glisser"
#: ../vnc.html:91
msgid "Move/Drag Viewport"
msgstr "Déplacer/faire glisser Viewport"
#: ../vnc.html:97
msgid "Keyboard"
msgstr "Clavier"
#: ../vnc.html:97
msgid "Show Keyboard"
msgstr "Afficher le clavier"
#: ../vnc.html:102
msgid "Extra keys"
msgstr "Touches supplémentaires"
#: ../vnc.html:102
msgid "Show Extra Keys"
msgstr "Afficher les touches supplémentaires"
#: ../vnc.html:107
msgid "Ctrl"
msgstr "Ctrl"
#: ../vnc.html:107
msgid "Toggle Ctrl"
msgstr "Basculer Ctrl"
#: ../vnc.html:110
msgid "Alt"
msgstr "Alt"
#: ../vnc.html:110
msgid "Toggle Alt"
msgstr "Basculer Alt"
#: ../vnc.html:113
msgid "Toggle Windows"
msgstr "Basculer Windows"
#: ../vnc.html:113
msgid "Windows"
msgstr "Windows"
#: ../vnc.html:116
msgid "Send Tab"
msgstr "Envoyer l'onglet"
#: ../vnc.html:116
msgid "Tab"
msgstr "l'onglet"
#: ../vnc.html:119
msgid "Esc"
msgstr "Esc"
#: ../vnc.html:119
msgid "Send Escape"
msgstr "Envoyer Escape"
#: ../vnc.html:122
msgid "Ctrl+Alt+Del"
msgstr "Ctrl+Alt+Del"
#: ../vnc.html:122
msgid "Send Ctrl-Alt-Del"
msgstr "Envoyer Ctrl-Alt-Del"
#: ../vnc.html:129
msgid "Shutdown/Reboot"
msgstr "Arrêter/Redémarrer"
#: ../vnc.html:129
msgid "Shutdown/Reboot..."
msgstr "Arrêter/Redémarrer..."
#: ../vnc.html:135
msgid "Power"
msgstr "Alimentation"
#: ../vnc.html:137
msgid "Shutdown"
msgstr "Arrêter"
#: ../vnc.html:138
msgid "Reboot"
msgstr "Redémarrer"
#: ../vnc.html:139
msgid "Reset"
msgstr "Réinitialiser"
#: ../vnc.html:144 ../vnc.html:150
msgid "Clipboard"
msgstr "Presse-papiers"
#: ../vnc.html:154
msgid "Clear"
msgstr "Effacer"
#: ../vnc.html:160
msgid "Fullscreen"
msgstr "Plein écran"
#: ../vnc.html:165 ../vnc.html:172
msgid "Settings"
msgstr "Paramètres"
#: ../vnc.html:175
msgid "Shared Mode"
msgstr "Mode partagé"
#: ../vnc.html:178
msgid "View Only"
msgstr "Afficher uniquement"
#: ../vnc.html:182
msgid "Clip to Window"
msgstr "Clip à fenêtre"
#: ../vnc.html:185
msgid "Scaling Mode:"
msgstr "Mode mise à l'échelle:"
#: ../vnc.html:187
msgid "None"
msgstr "Aucun"
#: ../vnc.html:188
msgid "Local Scaling"
msgstr "Mise à l'échelle locale"
#: ../vnc.html:189
msgid "Remote Resizing"
msgstr "Redimensionnement à distance"
#: ../vnc.html:194
msgid "Advanced"
msgstr "Avancé"
#: ../vnc.html:197
msgid "Quality:"
msgstr "Qualité:"
#: ../vnc.html:201
msgid "Compression level:"
msgstr "Niveau de compression:"
#: ../vnc.html:206
msgid "Repeater ID:"
msgstr "ID Répéteur:"
#: ../vnc.html:210
msgid "WebSocket"
msgstr "WebSocket"
#: ../vnc.html:213
msgid "Encrypt"
msgstr "Crypter"
#: ../vnc.html:216
msgid "Host:"
msgstr "Hôte:"
#: ../vnc.html:220
msgid "Port:"
msgstr "Port:"
#: ../vnc.html:224
msgid "Path:"
msgstr "Chemin:"
#: ../vnc.html:231
msgid "Automatic Reconnect"
msgstr "Reconnecter automatiquemen"
#: ../vnc.html:234
msgid "Reconnect Delay (ms):"
msgstr "Délai de reconnexion (ms):"
#: ../vnc.html:239
msgid "Show Dot when No Cursor"
msgstr "Afficher le point lorsqu'il n'y a pas de curseur"
#: ../vnc.html:244
msgid "Logging:"
msgstr "Se connecter:"
#: ../vnc.html:253
msgid "Version:"
msgstr "Version:"
#: ../vnc.html:261
msgid "Disconnect"
msgstr "Déconnecter"
#: ../vnc.html:280
msgid "Connect"
msgstr "Connecter"
#: ../vnc.html:290
msgid "Username:"
msgstr "Nom d'utilisateur:"
#: ../vnc.html:294
msgid "Password:"
msgstr "Mot de passe:"
#: ../vnc.html:298
msgid "Send Credentials"
msgstr "Envoyer les identifiants"
#: ../vnc.html:308
msgid "Cancel"
msgstr "Annuler"
+324
View File
@@ -0,0 +1,324 @@
# Japanese translations for noVNC package
# noVNC パッケージに対する日訳
# Copyright (C) 2019 The noVNC Authors
# This file is distributed under the same license as the noVNC package.
# nnn1590 <nnn1590@nnn1590.org>, 2019-2020.
#
msgid ""
msgstr ""
"Project-Id-Version: noVNC 1.1.0\n"
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
"POT-Creation-Date: 2020-07-03 16:11+0200\n"
"PO-Revision-Date: 2021-01-15 12:37+0900\n"
"Last-Translator: nnn1590 <nnn1590@nnn1590.org>\n"
"Language-Team: Japanese\n"
"Language: ja\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 2.3\n"
#: ../app/ui.js:394
msgid "Connecting..."
msgstr "接続しています..."
#: ../app/ui.js:401
msgid "Disconnecting..."
msgstr "切断しています..."
#: ../app/ui.js:407
msgid "Reconnecting..."
msgstr "再接続しています..."
#: ../app/ui.js:412
msgid "Internal error"
msgstr "内部エラー"
#: ../app/ui.js:1008
msgid "Must set host"
msgstr "ホストを設定する必要があります"
#: ../app/ui.js:1090
msgid "Connected (encrypted) to "
msgstr "接続しました (暗号化済み): "
#: ../app/ui.js:1092
msgid "Connected (unencrypted) to "
msgstr "接続しました (暗号化されていません): "
#: ../app/ui.js:1115
msgid "Something went wrong, connection is closed"
msgstr "何らかの問題で、接続が閉じられました"
#: ../app/ui.js:1118
msgid "Failed to connect to server"
msgstr "サーバーへの接続に失敗しました"
#: ../app/ui.js:1128
msgid "Disconnected"
msgstr "切断しました"
#: ../app/ui.js:1143
msgid "New connection has been rejected with reason: "
msgstr "新規接続は次の理由で拒否されました: "
#: ../app/ui.js:1146
msgid "New connection has been rejected"
msgstr "新規接続は拒否されました"
#: ../app/ui.js:1181
msgid "Credentials are required"
msgstr "資格情報が必要です"
#: ../vnc.html:74
msgid "noVNC encountered an error:"
msgstr "noVNC でエラーが発生しました:"
#: ../vnc.html:84
msgid "Hide/Show the control bar"
msgstr "コントロールバーを隠す/表示する"
#: ../vnc.html:91
msgid "Drag"
msgstr "ドラッグ"
#: ../vnc.html:91
msgid "Move/Drag Viewport"
msgstr "ビューポートを移動/ドラッグ"
#: ../vnc.html:97
msgid "Keyboard"
msgstr "キーボード"
#: ../vnc.html:97
msgid "Show Keyboard"
msgstr "キーボードを表示"
#: ../vnc.html:102
msgid "Extra keys"
msgstr "追加キー"
#: ../vnc.html:102
msgid "Show Extra Keys"
msgstr "追加キーを表示"
#: ../vnc.html:107
msgid "Ctrl"
msgstr "Ctrl"
#: ../vnc.html:107
msgid "Toggle Ctrl"
msgstr "Ctrl キーを切り替え"
#: ../vnc.html:110
msgid "Alt"
msgstr "Alt"
#: ../vnc.html:110
msgid "Toggle Alt"
msgstr "Alt キーを切り替え"
#: ../vnc.html:113
msgid "Toggle Windows"
msgstr "Windows キーを切り替え"
#: ../vnc.html:113
msgid "Windows"
msgstr "Windows"
#: ../vnc.html:116
msgid "Send Tab"
msgstr "Tab キーを送信"
#: ../vnc.html:116
msgid "Tab"
msgstr "Tab"
#: ../vnc.html:119
msgid "Esc"
msgstr "Esc"
#: ../vnc.html:119
msgid "Send Escape"
msgstr "Escape キーを送信"
#: ../vnc.html:122
msgid "Ctrl+Alt+Del"
msgstr "Ctrl+Alt+Del"
#: ../vnc.html:122
msgid "Send Ctrl-Alt-Del"
msgstr "Ctrl-Alt-Del を送信"
#: ../vnc.html:129
msgid "Shutdown/Reboot"
msgstr "シャットダウン/再起動"
#: ../vnc.html:129
msgid "Shutdown/Reboot..."
msgstr "シャットダウン/再起動..."
#: ../vnc.html:135
msgid "Power"
msgstr "電源"
#: ../vnc.html:137
msgid "Shutdown"
msgstr "シャットダウン"
#: ../vnc.html:138
msgid "Reboot"
msgstr "再起動"
#: ../vnc.html:139
msgid "Reset"
msgstr "リセット"
#: ../vnc.html:144 ../vnc.html:150
msgid "Clipboard"
msgstr "クリップボード"
#: ../vnc.html:154
msgid "Clear"
msgstr "クリア"
#: ../vnc.html:160
msgid "Fullscreen"
msgstr "全画面表示"
#: ../vnc.html:165 ../vnc.html:172
msgid "Settings"
msgstr "設定"
#: ../vnc.html:175
msgid "Shared Mode"
msgstr "共有モード"
#: ../vnc.html:178
msgid "View Only"
msgstr "表示のみ"
#: ../vnc.html:182
msgid "Clip to Window"
msgstr "ウィンドウにクリップ"
#: ../vnc.html:185
msgid "Scaling Mode:"
msgstr "スケーリングモード:"
#: ../vnc.html:187
msgid "None"
msgstr "なし"
#: ../vnc.html:188
msgid "Local Scaling"
msgstr "ローカルスケーリング"
#: ../vnc.html:189
msgid "Remote Resizing"
msgstr "リモートでリサイズ"
#: ../vnc.html:194
msgid "Advanced"
msgstr "高度"
#: ../vnc.html:197
msgid "Quality:"
msgstr "品質:"
#: ../vnc.html:201
msgid "Compression level:"
msgstr "圧縮レベル:"
#: ../vnc.html:206
msgid "Repeater ID:"
msgstr "リピーター ID:"
#: ../vnc.html:210
msgid "WebSocket"
msgstr "WebSocket"
#: ../vnc.html:213
msgid "Encrypt"
msgstr "暗号化"
#: ../vnc.html:216
msgid "Host:"
msgstr "ホスト:"
#: ../vnc.html:220
msgid "Port:"
msgstr "ポート:"
#: ../vnc.html:224
msgid "Path:"
msgstr "パス:"
#: ../vnc.html:231
msgid "Automatic Reconnect"
msgstr "自動再接続"
#: ../vnc.html:234
msgid "Reconnect Delay (ms):"
msgstr "再接続する遅延 (ミリ秒):"
#: ../vnc.html:239
msgid "Show Dot when No Cursor"
msgstr "カーソルがないときにドットを表示"
#: ../vnc.html:244
msgid "Logging:"
msgstr "ロギング:"
#: ../vnc.html:253
msgid "Version:"
msgstr "バージョン:"
#: ../vnc.html:261
msgid "Disconnect"
msgstr "切断"
#: ../vnc.html:280
msgid "Connect"
msgstr "接続"
#: ../vnc.html:290
msgid "Username:"
msgstr "ユーザー名:"
#: ../vnc.html:294
msgid "Password:"
msgstr "パスワード:"
#: ../vnc.html:298
msgid "Send Credentials"
msgstr "資格情報を送信"
#: ../vnc.html:308
msgid "Cancel"
msgstr "キャンセル"
#~ msgid "Password is required"
#~ msgstr "パスワードが必要です"
#~ msgid "viewport drag"
#~ msgstr "ビューポートをドラッグ"
#~ msgid "Active Mouse Button"
#~ msgstr "アクティブなマウスボタン"
#~ msgid "No mousebutton"
#~ msgstr "マウスボタンなし"
#~ msgid "Left mousebutton"
#~ msgstr "左マウスボタン"
#~ msgid "Middle mousebutton"
#~ msgstr "中マウスボタン"
#~ msgid "Right mousebutton"
#~ msgstr "右マウスボタン"
#~ msgid "Send Password"
#~ msgstr "パスワードを送信"
+290
View File
@@ -0,0 +1,290 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) 2018 The noVNC Authors
# This file is distributed under the same license as the noVNC package.
# Baw Appie <pp121324@gmail.com>, 2018.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: noVNC 1.0.0-testing.2\n"
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
"POT-Creation-Date: 2018-01-31 16:29+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Baw Appie <pp121324@gmail.com>\n"
"Language-Team: Korean\n"
"Language: ko\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: ../app/ui.js:395
msgid "Connecting..."
msgstr "연결중..."
#: ../app/ui.js:402
msgid "Disconnecting..."
msgstr "연결 해제중..."
#: ../app/ui.js:408
msgid "Reconnecting..."
msgstr "재연결중..."
#: ../app/ui.js:413
msgid "Internal error"
msgstr "내부 오류"
#: ../app/ui.js:1002
msgid "Must set host"
msgstr "호스트는 설정되어야 합니다."
#: ../app/ui.js:1083
msgid "Connected (encrypted) to "
msgstr "다음과 (암호화되어) 연결되었습니다:"
#: ../app/ui.js:1085
msgid "Connected (unencrypted) to "
msgstr "다음과 (암호화 없이) 연결되었습니다:"
#: ../app/ui.js:1108
msgid "Something went wrong, connection is closed"
msgstr "무언가 잘못되었습니다, 연결이 닫혔습니다."
#: ../app/ui.js:1111
msgid "Failed to connect to server"
msgstr "서버에 연결하지 못했습니다."
#: ../app/ui.js:1121
msgid "Disconnected"
msgstr "연결이 해제되었습니다."
#: ../app/ui.js:1134
msgid "New connection has been rejected with reason: "
msgstr "새 연결이 다음 이유로 거부되었습니다:"
#: ../app/ui.js:1137
msgid "New connection has been rejected"
msgstr "새 연결이 거부되었습니다."
#: ../app/ui.js:1158
msgid "Password is required"
msgstr "비밀번호가 필요합니다."
#: ../vnc.html:91
msgid "noVNC encountered an error:"
msgstr "noVNC에 오류가 발생했습니다:"
#: ../vnc.html:101
msgid "Hide/Show the control bar"
msgstr "컨트롤 바 숨기기/보이기"
#: ../vnc.html:108
msgid "Move/Drag Viewport"
msgstr "움직이기/드래그 뷰포트"
#: ../vnc.html:108
msgid "viewport drag"
msgstr "뷰포트 드래그"
#: ../vnc.html:114 ../vnc.html:117 ../vnc.html:120 ../vnc.html:123
msgid "Active Mouse Button"
msgstr "마우스 버튼 활성화"
#: ../vnc.html:114
msgid "No mousebutton"
msgstr "마우스 버튼 없음"
#: ../vnc.html:117
msgid "Left mousebutton"
msgstr "왼쪽 마우스 버튼"
#: ../vnc.html:120
msgid "Middle mousebutton"
msgstr "중간 마우스 버튼"
#: ../vnc.html:123
msgid "Right mousebutton"
msgstr "오른쪽 마우스 버튼"
#: ../vnc.html:126
msgid "Keyboard"
msgstr "키보드"
#: ../vnc.html:126
msgid "Show Keyboard"
msgstr "키보드 보이기"
#: ../vnc.html:133
msgid "Extra keys"
msgstr "기타 키들"
#: ../vnc.html:133
msgid "Show Extra Keys"
msgstr "기타 키들 보이기"
#: ../vnc.html:138
msgid "Ctrl"
msgstr "Ctrl"
#: ../vnc.html:138
msgid "Toggle Ctrl"
msgstr "Ctrl 켜기/끄기"
#: ../vnc.html:141
msgid "Alt"
msgstr "Alt"
#: ../vnc.html:141
msgid "Toggle Alt"
msgstr "Alt 켜기/끄기"
#: ../vnc.html:144
msgid "Send Tab"
msgstr "Tab 보내기"
#: ../vnc.html:144
msgid "Tab"
msgstr "Tab"
#: ../vnc.html:147
msgid "Esc"
msgstr "Esc"
#: ../vnc.html:147
msgid "Send Escape"
msgstr "Esc 보내기"
#: ../vnc.html:150
msgid "Ctrl+Alt+Del"
msgstr "Ctrl+Alt+Del"
#: ../vnc.html:150
msgid "Send Ctrl-Alt-Del"
msgstr "Ctrl+Alt+Del 보내기"
#: ../vnc.html:158
msgid "Shutdown/Reboot"
msgstr "셧다운/리붓"
#: ../vnc.html:158
msgid "Shutdown/Reboot..."
msgstr "셧다운/리붓..."
#: ../vnc.html:164
msgid "Power"
msgstr "전원"
#: ../vnc.html:166
msgid "Shutdown"
msgstr "셧다운"
#: ../vnc.html:167
msgid "Reboot"
msgstr "리붓"
#: ../vnc.html:168
msgid "Reset"
msgstr "리셋"
#: ../vnc.html:173 ../vnc.html:179
msgid "Clipboard"
msgstr "클립보드"
#: ../vnc.html:183
msgid "Clear"
msgstr "지우기"
#: ../vnc.html:189
msgid "Fullscreen"
msgstr "전체화면"
#: ../vnc.html:194 ../vnc.html:201
msgid "Settings"
msgstr "설정"
#: ../vnc.html:204
msgid "Shared Mode"
msgstr "공유 모드"
#: ../vnc.html:207
msgid "View Only"
msgstr "보기 전용"
#: ../vnc.html:211
msgid "Clip to Window"
msgstr "창에 클립"
#: ../vnc.html:214
msgid "Scaling Mode:"
msgstr "스케일링 모드:"
#: ../vnc.html:216
msgid "None"
msgstr "없음"
#: ../vnc.html:217
msgid "Local Scaling"
msgstr "로컬 스케일링"
#: ../vnc.html:218
msgid "Remote Resizing"
msgstr "원격 크기 조절"
#: ../vnc.html:223
msgid "Advanced"
msgstr "고급"
#: ../vnc.html:226
msgid "Repeater ID:"
msgstr "중계 ID"
#: ../vnc.html:230
msgid "WebSocket"
msgstr "웹소켓"
#: ../vnc.html:233
msgid "Encrypt"
msgstr "암호화"
#: ../vnc.html:236
msgid "Host:"
msgstr "호스트:"
#: ../vnc.html:240
msgid "Port:"
msgstr "포트:"
#: ../vnc.html:244
msgid "Path:"
msgstr "위치:"
#: ../vnc.html:251
msgid "Automatic Reconnect"
msgstr "자동 재연결"
#: ../vnc.html:254
msgid "Reconnect Delay (ms):"
msgstr "재연결 지연 시간 (ms)"
#: ../vnc.html:260
msgid "Logging:"
msgstr "로깅"
#: ../vnc.html:272
msgid "Disconnect"
msgstr "연결 해제"
#: ../vnc.html:291
msgid "Connect"
msgstr "연결"
#: ../vnc.html:301
msgid "Password:"
msgstr "비밀번호:"
#: ../vnc.html:305
msgid "Send Password"
msgstr "비밀번호 전송"
#: ../vnc.html:315
msgid "Cancel"
msgstr "취소"
+128 -96
View File
@@ -1,16 +1,16 @@
# Dutch translations for noVNC package
# Nederlandse vertalingen voor het pakket noVNC.
# Copyright (C) 2016 Various Authors
# Copyright (C) 2018 The noVNC Authors
# This file is distributed under the same license as the noVNC package.
# Loek Janssen <loekjanssen@gmail.com>, 2016.
#
msgid ""
msgstr ""
"Project-Id-Version: noVNC 0.6.1\n"
"Project-Id-Version: noVNC 1.1.0\n"
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
"POT-Creation-Date: 2017-10-06 10:07+0200\n"
"PO-Revision-Date: 2017-10-11 16:16+0200\n"
"Last-Translator: Yuri van Oers <yvanoers@gmail.com>\n"
"POT-Creation-Date: 2019-04-09 11:06+0100\n"
"PO-Revision-Date: 2019-04-09 17:17+0100\n"
"Last-Translator: Arend Lapere <arend.lapere@gmail.com>\n"
"Language-Team: none\n"
"Language: nl\n"
"MIME-Version: 1.0\n"
@@ -18,269 +18,301 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../app/ui.js:430
#: ../app/ui.js:383
msgid "Connecting..."
msgstr "Verbinden..."
#: ../app/ui.js:438
msgid "Connected (encrypted) to "
msgstr "Verbonden (versleuteld) met "
#: ../app/ui.js:440
msgid "Connected (unencrypted) to "
msgstr "Verbonden (onversleuteld) met "
#: ../app/ui.js:446
#: ../app/ui.js:390
msgid "Disconnecting..."
msgstr "Verbinding verbreken..."
#: ../app/ui.js:450
msgid "Disconnected"
msgstr "Verbinding verbroken"
#: ../app/ui.js:1052 ../core/rfb.js:248
msgid "Must set host"
msgstr "Host moeten worden ingesteld"
#: ../app/ui.js:1101
#: ../app/ui.js:396
msgid "Reconnecting..."
msgstr "Opnieuw verbinding maken..."
#: ../app/ui.js:1140
#: ../app/ui.js:401
msgid "Internal error"
msgstr "Interne fout"
#: ../app/ui.js:991
msgid "Must set host"
msgstr "Host moeten worden ingesteld"
#: ../app/ui.js:1073
msgid "Connected (encrypted) to "
msgstr "Verbonden (versleuteld) met "
#: ../app/ui.js:1075
msgid "Connected (unencrypted) to "
msgstr "Verbonden (onversleuteld) met "
#: ../app/ui.js:1098
msgid "Something went wrong, connection is closed"
msgstr "Er iets fout gelopen, verbinding werd verbroken"
#: ../app/ui.js:1101
msgid "Failed to connect to server"
msgstr "Verbinding maken met server is mislukt"
#: ../app/ui.js:1111
msgid "Disconnected"
msgstr "Verbinding verbroken"
#: ../app/ui.js:1124
msgid "New connection has been rejected with reason: "
msgstr "Nieuwe verbinding is geweigerd omwille van de volgende reden: "
#: ../app/ui.js:1127
msgid "New connection has been rejected"
msgstr "Nieuwe verbinding is geweigerd"
#: ../app/ui.js:1147
msgid "Password is required"
msgstr "Wachtwoord is vereist"
#: ../core/rfb.js:548
msgid "Disconnect timeout"
msgstr "Timeout tijdens verbreken van verbinding"
#: ../vnc.html:89
#: ../vnc.html:80
msgid "noVNC encountered an error:"
msgstr "noVNC heeft een fout bemerkt:"
#: ../vnc.html:99
#: ../vnc.html:90
msgid "Hide/Show the control bar"
msgstr "Verberg/Toon de bedieningsbalk"
#: ../vnc.html:106
#: ../vnc.html:97
msgid "Move/Drag Viewport"
msgstr "Verplaats/Versleep Kijkvenster"
#: ../vnc.html:106
#: ../vnc.html:97
msgid "viewport drag"
msgstr "kijkvenster slepen"
#: ../vnc.html:112 ../vnc.html:115 ../vnc.html:118 ../vnc.html:121
#: ../vnc.html:103 ../vnc.html:106 ../vnc.html:109 ../vnc.html:112
msgid "Active Mouse Button"
msgstr "Actieve Muisknop"
#: ../vnc.html:112
#: ../vnc.html:103
msgid "No mousebutton"
msgstr "Geen muisknop"
#: ../vnc.html:115
#: ../vnc.html:106
msgid "Left mousebutton"
msgstr "Linker muisknop"
#: ../vnc.html:118
#: ../vnc.html:109
msgid "Middle mousebutton"
msgstr "Middelste muisknop"
#: ../vnc.html:121
#: ../vnc.html:112
msgid "Right mousebutton"
msgstr "Rechter muisknop"
#: ../vnc.html:124
#: ../vnc.html:115
msgid "Keyboard"
msgstr "Toetsenbord"
#: ../vnc.html:124
#: ../vnc.html:115
msgid "Show Keyboard"
msgstr "Toon Toetsenbord"
#: ../vnc.html:131
#: ../vnc.html:121
msgid "Extra keys"
msgstr "Extra toetsen"
#: ../vnc.html:131
#: ../vnc.html:121
msgid "Show Extra Keys"
msgstr "Toon Extra Toetsen"
#: ../vnc.html:136
#: ../vnc.html:126
msgid "Ctrl"
msgstr "Ctrl"
#: ../vnc.html:136
#: ../vnc.html:126
msgid "Toggle Ctrl"
msgstr "Ctrl aan/uitzetten"
msgstr "Ctrl omschakelen"
#: ../vnc.html:139
#: ../vnc.html:129
msgid "Alt"
msgstr "Alt"
#: ../vnc.html:139
#: ../vnc.html:129
msgid "Toggle Alt"
msgstr "Alt aan/uitzetten"
msgstr "Alt omschakelen"
#: ../vnc.html:142
#: ../vnc.html:132
msgid "Toggle Windows"
msgstr "Windows omschakelen"
#: ../vnc.html:132
msgid "Windows"
msgstr "Windows"
#: ../vnc.html:135
msgid "Send Tab"
msgstr "Tab Sturen"
#: ../vnc.html:142
#: ../vnc.html:135
msgid "Tab"
msgstr "Tab"
#: ../vnc.html:145
#: ../vnc.html:138
msgid "Esc"
msgstr "Esc"
#: ../vnc.html:145
#: ../vnc.html:138
msgid "Send Escape"
msgstr "Escape Sturen"
#: ../vnc.html:148
#: ../vnc.html:141
msgid "Ctrl+Alt+Del"
msgstr "Ctrl-Alt-Del"
#: ../vnc.html:148
#: ../vnc.html:141
msgid "Send Ctrl-Alt-Del"
msgstr "Ctrl-Alt-Del Sturen"
#: ../vnc.html:156
#: ../vnc.html:149
msgid "Shutdown/Reboot"
msgstr "Uitschakelen/Herstarten"
#: ../vnc.html:156
#: ../vnc.html:149
msgid "Shutdown/Reboot..."
msgstr "Uitschakelen/Herstarten..."
#: ../vnc.html:162
#: ../vnc.html:155
msgid "Power"
msgstr "Systeem"
#: ../vnc.html:164
#: ../vnc.html:157
msgid "Shutdown"
msgstr "Uitschakelen"
#: ../vnc.html:165
#: ../vnc.html:158
msgid "Reboot"
msgstr "Herstarten"
#: ../vnc.html:166
#: ../vnc.html:159
msgid "Reset"
msgstr "Resetten"
#: ../vnc.html:171 ../vnc.html:177
#: ../vnc.html:164 ../vnc.html:170
msgid "Clipboard"
msgstr "Klembord"
#: ../vnc.html:181
#: ../vnc.html:174
msgid "Clear"
msgstr "Wissen"
#: ../vnc.html:187
#: ../vnc.html:180
msgid "Fullscreen"
msgstr "Volledig Scherm"
#: ../vnc.html:192 ../vnc.html:199
#: ../vnc.html:185 ../vnc.html:192
msgid "Settings"
msgstr "Instellingen"
#: ../vnc.html:202
#: ../vnc.html:195
msgid "Shared Mode"
msgstr "Gedeelde Modus"
#: ../vnc.html:205
#: ../vnc.html:198
msgid "View Only"
msgstr "Alleen Kijken"
#: ../vnc.html:209
#: ../vnc.html:202
msgid "Clip to Window"
msgstr "Randen buiten venster afsnijden"
#: ../vnc.html:212
#: ../vnc.html:205
msgid "Scaling Mode:"
msgstr "Schaalmodus:"
#: ../vnc.html:214
#: ../vnc.html:207
msgid "None"
msgstr "Geen"
#: ../vnc.html:215
#: ../vnc.html:208
msgid "Local Scaling"
msgstr "Lokaal Schalen"
#: ../vnc.html:216
msgid "Local Downscaling"
msgstr "Lokaal Neerschalen"
#: ../vnc.html:217
#: ../vnc.html:209
msgid "Remote Resizing"
msgstr "Op Afstand Formaat Wijzigen"
#: ../vnc.html:222
#: ../vnc.html:214
msgid "Advanced"
msgstr "Geavanceerd"
#: ../vnc.html:225
msgid "Local Cursor"
msgstr "Lokale Cursor"
#: ../vnc.html:229
#: ../vnc.html:217
msgid "Repeater ID:"
msgstr "Repeater ID:"
#: ../vnc.html:233
#: ../vnc.html:221
msgid "WebSocket"
msgstr "WebSocket"
#: ../vnc.html:236
#: ../vnc.html:224
msgid "Encrypt"
msgstr "Versleutelen"
#: ../vnc.html:239
#: ../vnc.html:227
msgid "Host:"
msgstr "Host:"
#: ../vnc.html:243
#: ../vnc.html:231
msgid "Port:"
msgstr "Poort:"
#: ../vnc.html:247
#: ../vnc.html:235
msgid "Path:"
msgstr "Pad:"
#: ../vnc.html:254
#: ../vnc.html:242
msgid "Automatic Reconnect"
msgstr "Automatisch Opnieuw Verbinden"
#: ../vnc.html:257
#: ../vnc.html:245
msgid "Reconnect Delay (ms):"
msgstr "Vertraging voor Opnieuw Verbinden (ms):"
#: ../vnc.html:263
#: ../vnc.html:250
msgid "Show Dot when No Cursor"
msgstr "Geef stip weer indien geen cursor"
#: ../vnc.html:255
msgid "Logging:"
msgstr "Logmeldingen:"
#: ../vnc.html:275
#: ../vnc.html:267
msgid "Disconnect"
msgstr "Verbinding verbreken"
#: ../vnc.html:294
#: ../vnc.html:286
msgid "Connect"
msgstr "Verbinden"
#: ../vnc.html:304
#: ../vnc.html:296
msgid "Password:"
msgstr "Wachtwoord:"
#: ../vnc.html:318
#: ../vnc.html:300
msgid "Send Password"
msgstr "Verzend Wachtwoord:"
#: ../vnc.html:310
msgid "Cancel"
msgstr "Annuleren"
#: ../vnc.html:334
msgid "Canvas not supported."
msgstr "Canvas wordt niet ondersteund."
#~ msgid "Disconnect timeout"
#~ msgstr "Timeout tijdens verbreken van verbinding"
#~ msgid "Local Downscaling"
#~ msgstr "Lokaal Neerschalen"
#~ msgid "Local Cursor"
#~ msgstr "Lokale Cursor"
#~ msgid "Canvas not supported."
#~ msgstr "Canvas wordt niet ondersteund."
#~ msgid ""
#~ "Forcing clipping mode since scrollbars aren't supported by IE in "
+99 -91
View File
@@ -1,14 +1,14 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR Various Authors
# Copyright (C) YEAR The noVNC Authors
# This file is distributed under the same license as the noVNC package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: noVNC 1.0.0-testing.2\n"
"Project-Id-Version: noVNC 1.3.0\n"
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
"POT-Creation-Date: 2018-01-31 16:29+0100\n"
"POT-Creation-Date: 2021-08-27 16:03+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,274 +17,282 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: ../app/ui.js:395
#: ../app/ui.js:400
msgid "Connecting..."
msgstr ""
#: ../app/ui.js:402
#: ../app/ui.js:407
msgid "Disconnecting..."
msgstr ""
#: ../app/ui.js:408
#: ../app/ui.js:413
msgid "Reconnecting..."
msgstr ""
#: ../app/ui.js:413
#: ../app/ui.js:418
msgid "Internal error"
msgstr ""
#: ../app/ui.js:1002
#: ../app/ui.js:1009
msgid "Must set host"
msgstr ""
#: ../app/ui.js:1083
#: ../app/ui.js:1091
msgid "Connected (encrypted) to "
msgstr ""
#: ../app/ui.js:1085
#: ../app/ui.js:1093
msgid "Connected (unencrypted) to "
msgstr ""
#: ../app/ui.js:1108
#: ../app/ui.js:1116
msgid "Something went wrong, connection is closed"
msgstr ""
#: ../app/ui.js:1111
#: ../app/ui.js:1119
msgid "Failed to connect to server"
msgstr ""
#: ../app/ui.js:1121
#: ../app/ui.js:1129
msgid "Disconnected"
msgstr ""
#: ../app/ui.js:1134
#: ../app/ui.js:1144
msgid "New connection has been rejected with reason: "
msgstr ""
#: ../app/ui.js:1137
#: ../app/ui.js:1147
msgid "New connection has been rejected"
msgstr ""
#: ../app/ui.js:1158
msgid "Password is required"
#: ../app/ui.js:1182
msgid "Credentials are required"
msgstr ""
#: ../vnc.html:91
#: ../vnc.html:61
msgid "noVNC encountered an error:"
msgstr ""
#: ../vnc.html:101
#: ../vnc.html:71
msgid "Hide/Show the control bar"
msgstr ""
#: ../vnc.html:108
#: ../vnc.html:78
msgid "Drag"
msgstr ""
#: ../vnc.html:78
msgid "Move/Drag Viewport"
msgstr ""
#: ../vnc.html:108
msgid "viewport drag"
msgstr ""
#: ../vnc.html:114 ../vnc.html:117 ../vnc.html:120 ../vnc.html:123
msgid "Active Mouse Button"
msgstr ""
#: ../vnc.html:114
msgid "No mousebutton"
msgstr ""
#: ../vnc.html:117
msgid "Left mousebutton"
msgstr ""
#: ../vnc.html:120
msgid "Middle mousebutton"
msgstr ""
#: ../vnc.html:123
msgid "Right mousebutton"
msgstr ""
#: ../vnc.html:126
#: ../vnc.html:84
msgid "Keyboard"
msgstr ""
#: ../vnc.html:126
#: ../vnc.html:84
msgid "Show Keyboard"
msgstr ""
#: ../vnc.html:133
#: ../vnc.html:89
msgid "Extra keys"
msgstr ""
#: ../vnc.html:133
#: ../vnc.html:89
msgid "Show Extra Keys"
msgstr ""
#: ../vnc.html:138
#: ../vnc.html:94
msgid "Ctrl"
msgstr ""
#: ../vnc.html:138
#: ../vnc.html:94
msgid "Toggle Ctrl"
msgstr ""
#: ../vnc.html:141
#: ../vnc.html:97
msgid "Alt"
msgstr ""
#: ../vnc.html:141
#: ../vnc.html:97
msgid "Toggle Alt"
msgstr ""
#: ../vnc.html:144
#: ../vnc.html:100
msgid "Toggle Windows"
msgstr ""
#: ../vnc.html:100
msgid "Windows"
msgstr ""
#: ../vnc.html:103
msgid "Send Tab"
msgstr ""
#: ../vnc.html:144
#: ../vnc.html:103
msgid "Tab"
msgstr ""
#: ../vnc.html:147
#: ../vnc.html:106
msgid "Esc"
msgstr ""
#: ../vnc.html:147
#: ../vnc.html:106
msgid "Send Escape"
msgstr ""
#: ../vnc.html:150
#: ../vnc.html:109
msgid "Ctrl+Alt+Del"
msgstr ""
#: ../vnc.html:150
#: ../vnc.html:109
msgid "Send Ctrl-Alt-Del"
msgstr ""
#: ../vnc.html:158
#: ../vnc.html:116
msgid "Shutdown/Reboot"
msgstr ""
#: ../vnc.html:158
#: ../vnc.html:116
msgid "Shutdown/Reboot..."
msgstr ""
#: ../vnc.html:164
#: ../vnc.html:122
msgid "Power"
msgstr ""
#: ../vnc.html:166
#: ../vnc.html:124
msgid "Shutdown"
msgstr ""
#: ../vnc.html:167
#: ../vnc.html:125
msgid "Reboot"
msgstr ""
#: ../vnc.html:168
#: ../vnc.html:126
msgid "Reset"
msgstr ""
#: ../vnc.html:173 ../vnc.html:179
#: ../vnc.html:131 ../vnc.html:137
msgid "Clipboard"
msgstr ""
#: ../vnc.html:183
#: ../vnc.html:141
msgid "Clear"
msgstr ""
#: ../vnc.html:189
#: ../vnc.html:147
msgid "Fullscreen"
msgstr ""
#: ../vnc.html:194 ../vnc.html:201
#: ../vnc.html:152 ../vnc.html:159
msgid "Settings"
msgstr ""
#: ../vnc.html:204
#: ../vnc.html:162
msgid "Shared Mode"
msgstr ""
#: ../vnc.html:207
#: ../vnc.html:165
msgid "View Only"
msgstr ""
#: ../vnc.html:211
#: ../vnc.html:169
msgid "Clip to Window"
msgstr ""
#: ../vnc.html:214
#: ../vnc.html:172
msgid "Scaling Mode:"
msgstr ""
#: ../vnc.html:216
#: ../vnc.html:174
msgid "None"
msgstr ""
#: ../vnc.html:217
#: ../vnc.html:175
msgid "Local Scaling"
msgstr ""
#: ../vnc.html:218
#: ../vnc.html:176
msgid "Remote Resizing"
msgstr ""
#: ../vnc.html:223
#: ../vnc.html:181
msgid "Advanced"
msgstr ""
#: ../vnc.html:226
#: ../vnc.html:184
msgid "Quality:"
msgstr ""
#: ../vnc.html:188
msgid "Compression level:"
msgstr ""
#: ../vnc.html:193
msgid "Repeater ID:"
msgstr ""
#: ../vnc.html:230
#: ../vnc.html:197
msgid "WebSocket"
msgstr ""
#: ../vnc.html:233
#: ../vnc.html:200
msgid "Encrypt"
msgstr ""
#: ../vnc.html:236
#: ../vnc.html:203
msgid "Host:"
msgstr ""
#: ../vnc.html:240
#: ../vnc.html:207
msgid "Port:"
msgstr ""
#: ../vnc.html:244
#: ../vnc.html:211
msgid "Path:"
msgstr ""
#: ../vnc.html:251
#: ../vnc.html:218
msgid "Automatic Reconnect"
msgstr ""
#: ../vnc.html:254
#: ../vnc.html:221
msgid "Reconnect Delay (ms):"
msgstr ""
#: ../vnc.html:260
#: ../vnc.html:226
msgid "Show Dot when No Cursor"
msgstr ""
#: ../vnc.html:231
msgid "Logging:"
msgstr ""
#: ../vnc.html:272
#: ../vnc.html:240
msgid "Version:"
msgstr ""
#: ../vnc.html:248
msgid "Disconnect"
msgstr ""
#: ../vnc.html:291
#: ../vnc.html:267
msgid "Connect"
msgstr ""
#: ../vnc.html:301
#: ../vnc.html:277
msgid "Username:"
msgstr ""
#: ../vnc.html:281
msgid "Password:"
msgstr ""
#: ../vnc.html:305
msgid "Send Password"
#: ../vnc.html:285
msgid "Send Credentials"
msgstr ""
#: ../vnc.html:315
#: ../vnc.html:295
msgid "Cancel"
msgstr ""
+1 -1
View File
@@ -1,5 +1,5 @@
# Polish translations for noVNC package.
# Copyright (C) 2017 Various Authors
# Copyright (C) 2018 The noVNC Authors
# This file is distributed under the same license as the noVNC package.
# Mariusz Jamro <mariusz.jamro@gmail.com>, 2017.
#
+12 -12
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env node
/*
* ps2js: gettext .po to noVNC .js converter
* Copyright (C) 2016 Pierre Ossman
* Copyright (C) 2018 The noVNC Authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,27 +17,27 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
var getopt = require('node-getopt');
var fs = require('fs');
var po2json = require("po2json");
const getopt = require('node-getopt');
const fs = require('fs');
const po2json = require("po2json");
opt = getopt.create([
['h' , 'help' , 'display this help'],
const opt = getopt.create([
['h', 'help', 'display this help'],
]).bindHelp().parseSystem();
if (opt.argv.length != 2) {
console.error("Incorrect number of arguments given");
process.exit(1);
console.error("Incorrect number of arguments given");
process.exit(1);
}
var data = po2json.parseFileSync(opt.argv[0]);
const data = po2json.parseFileSync(opt.argv[0]);
var bodyPart = Object.keys(data).filter((msgid) => msgid !== "").map((msgid) => {
const bodyPart = Object.keys(data).filter(msgid => msgid !== "").map((msgid) => {
if (msgid === "") return;
var msgstr = data[msgid][1];
const msgstr = data[msgid][1];
return " " + JSON.stringify(msgid) + ": " + JSON.stringify(msgstr);
}).join(",\n");
var output = "{\n" + bodyPart + "\n}";
const output = "{\n" + bodyPart + "\n}";
fs.writeFileSync(opt.argv[1], output);
+299
View File
@@ -0,0 +1,299 @@
# Portuguese translations for noVNC package.
# Copyright (C) 2021 The noVNC Authors
# This file is distributed under the same license as the noVNC package.
# <liddack@outlook.com>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: noVNC 1.2.0\n"
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
"POT-Creation-Date: 2021-03-15 21:55-0300\n"
"PO-Revision-Date: 2021-03-15 22:09-0300\n"
"Last-Translator: <liddack@outlook.com>\n"
"Language-Team: Brazilian Portuguese\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Poedit 2.4.1\n"
#: ../app/ui.js:400
msgid "Connecting..."
msgstr "Conectando..."
#: ../app/ui.js:407
msgid "Disconnecting..."
msgstr "Desconectando..."
#: ../app/ui.js:413
msgid "Reconnecting..."
msgstr "Reconectando..."
#: ../app/ui.js:418
msgid "Internal error"
msgstr "Erro interno"
#: ../app/ui.js:1009
msgid "Must set host"
msgstr "É necessário definir o host"
#: ../app/ui.js:1091
msgid "Connected (encrypted) to "
msgstr "Conectado (com criptografia) a "
#: ../app/ui.js:1093
msgid "Connected (unencrypted) to "
msgstr "Conectado (sem criptografia) a "
#: ../app/ui.js:1116
msgid "Something went wrong, connection is closed"
msgstr "Algo deu errado. A conexão foi encerrada."
#: ../app/ui.js:1119
msgid "Failed to connect to server"
msgstr "Falha ao conectar-se ao servidor"
#: ../app/ui.js:1129
msgid "Disconnected"
msgstr "Desconectado"
#: ../app/ui.js:1144
msgid "New connection has been rejected with reason: "
msgstr "A nova conexão foi rejeitada pelo motivo: "
#: ../app/ui.js:1147
msgid "New connection has been rejected"
msgstr "A nova conexão foi rejeitada"
#: ../app/ui.js:1182
msgid "Credentials are required"
msgstr "Credenciais são obrigatórias"
#: ../vnc.html:61
msgid "noVNC encountered an error:"
msgstr "O noVNC encontrou um erro:"
#: ../vnc.html:71
msgid "Hide/Show the control bar"
msgstr "Esconder/mostrar a barra de controles"
#: ../vnc.html:78
msgid "Drag"
msgstr "Arrastar"
#: ../vnc.html:78
msgid "Move/Drag Viewport"
msgstr "Mover/arrastar a janela"
#: ../vnc.html:84
msgid "Keyboard"
msgstr "Teclado"
#: ../vnc.html:84
msgid "Show Keyboard"
msgstr "Mostrar teclado"
#: ../vnc.html:89
msgid "Extra keys"
msgstr "Teclas adicionais"
#: ../vnc.html:89
msgid "Show Extra Keys"
msgstr "Mostar teclas adicionais"
#: ../vnc.html:94
msgid "Ctrl"
msgstr "Ctrl"
#: ../vnc.html:94
msgid "Toggle Ctrl"
msgstr "Pressionar/soltar Ctrl"
#: ../vnc.html:97
msgid "Alt"
msgstr "Alt"
#: ../vnc.html:97
msgid "Toggle Alt"
msgstr "Pressionar/soltar Alt"
#: ../vnc.html:100
msgid "Toggle Windows"
msgstr "Pressionar/soltar Windows"
#: ../vnc.html:100
msgid "Windows"
msgstr "Windows"
#: ../vnc.html:103
msgid "Send Tab"
msgstr "Enviar Tab"
#: ../vnc.html:103
msgid "Tab"
msgstr "Tab"
#: ../vnc.html:106
msgid "Esc"
msgstr "Esc"
#: ../vnc.html:106
msgid "Send Escape"
msgstr "Enviar Esc"
#: ../vnc.html:109
msgid "Ctrl+Alt+Del"
msgstr "Ctrl+Alt+Del"
#: ../vnc.html:109
msgid "Send Ctrl-Alt-Del"
msgstr "Enviar Ctrl-Alt-Del"
#: ../vnc.html:116
msgid "Shutdown/Reboot"
msgstr "Desligar/reiniciar"
#: ../vnc.html:116
msgid "Shutdown/Reboot..."
msgstr "Desligar/reiniciar..."
#: ../vnc.html:122
msgid "Power"
msgstr "Ligar"
#: ../vnc.html:124
msgid "Shutdown"
msgstr "Desligar"
#: ../vnc.html:125
msgid "Reboot"
msgstr "Reiniciar"
#: ../vnc.html:126
msgid "Reset"
msgstr "Reiniciar (forçado)"
#: ../vnc.html:131 ../vnc.html:137
msgid "Clipboard"
msgstr "Área de transferência"
#: ../vnc.html:141
msgid "Clear"
msgstr "Limpar"
#: ../vnc.html:147
msgid "Fullscreen"
msgstr "Tela cheia"
#: ../vnc.html:152 ../vnc.html:159
msgid "Settings"
msgstr "Configurações"
#: ../vnc.html:162
msgid "Shared Mode"
msgstr "Modo compartilhado"
#: ../vnc.html:165
msgid "View Only"
msgstr "Apenas visualizar"
#: ../vnc.html:169
msgid "Clip to Window"
msgstr "Recortar à janela"
#: ../vnc.html:172
msgid "Scaling Mode:"
msgstr "Modo de dimensionamento:"
#: ../vnc.html:174
msgid "None"
msgstr "Nenhum"
#: ../vnc.html:175
msgid "Local Scaling"
msgstr "Local"
#: ../vnc.html:176
msgid "Remote Resizing"
msgstr "Remoto"
#: ../vnc.html:181
msgid "Advanced"
msgstr "Avançado"
#: ../vnc.html:184
msgid "Quality:"
msgstr "Qualidade:"
#: ../vnc.html:188
msgid "Compression level:"
msgstr "Nível de compressão:"
#: ../vnc.html:193
msgid "Repeater ID:"
msgstr "ID do repetidor:"
#: ../vnc.html:197
msgid "WebSocket"
msgstr "WebSocket"
#: ../vnc.html:200
msgid "Encrypt"
msgstr "Criptografar"
#: ../vnc.html:203
msgid "Host:"
msgstr "Host:"
#: ../vnc.html:207
msgid "Port:"
msgstr "Porta:"
#: ../vnc.html:211
msgid "Path:"
msgstr "Caminho:"
#: ../vnc.html:218
msgid "Automatic Reconnect"
msgstr "Reconexão automática"
#: ../vnc.html:221
msgid "Reconnect Delay (ms):"
msgstr "Atraso da reconexão (ms)"
#: ../vnc.html:226
msgid "Show Dot when No Cursor"
msgstr "Mostrar ponto quando não há cursor"
#: ../vnc.html:231
msgid "Logging:"
msgstr "Registros:"
#: ../vnc.html:240
msgid "Version:"
msgstr "Versão:"
#: ../vnc.html:248
msgid "Disconnect"
msgstr "Desconectar"
#: ../vnc.html:267
msgid "Connect"
msgstr "Conectar"
#: ../vnc.html:277
msgid "Username:"
msgstr "Nome de usuário:"
#: ../vnc.html:281
msgid "Password:"
msgstr "Senha:"
#: ../vnc.html:285
msgid "Send Credentials"
msgstr "Enviar credenciais"
#: ../vnc.html:295
msgid "Cancel"
msgstr "Cancelar"
+306
View File
@@ -0,0 +1,306 @@
# Russian translations for noVNC package
# Русский перевод для пакета noVNC.
# Copyright (C) 2019 Dmitriy Shweew
# This file is distributed under the same license as the noVNC package.
# Dmitriy Shweew <shweew@it-advisor.ru>, 2019.
#
msgid ""
msgstr ""
"Project-Id-Version: noVNC 1.1.0\n"
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
"POT-Creation-Date: 2019-02-26 14:53+0400\n"
"PO-Revision-Date: 2019-02-17 17:29+0400\n"
"Last-Translator: Dmitriy Shweew <shweew@it-advisor.ru>\n"
"Language-Team: Russian\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: Poedit 2.2.1\n"
"X-Poedit-Flags-xgettext: --add-comments\n"
#: ../app/ui.js:387
msgid "Connecting..."
msgstr "Подключение..."
#: ../app/ui.js:394
msgid "Disconnecting..."
msgstr "Отключение..."
#: ../app/ui.js:400
msgid "Reconnecting..."
msgstr "Переподключение..."
#: ../app/ui.js:405
msgid "Internal error"
msgstr "Внутренняя ошибка"
#: ../app/ui.js:995
msgid "Must set host"
msgstr "Задайте имя сервера или IP"
#: ../app/ui.js:1077
msgid "Connected (encrypted) to "
msgstr "Подключено (с шифрованием) к "
#: ../app/ui.js:1079
msgid "Connected (unencrypted) to "
msgstr "Подключено (без шифрования) к "
#: ../app/ui.js:1102
msgid "Something went wrong, connection is closed"
msgstr "Что-то пошло не так, подключение разорвано"
#: ../app/ui.js:1105
msgid "Failed to connect to server"
msgstr "Ошибка подключения к серверу"
#: ../app/ui.js:1115
msgid "Disconnected"
msgstr "Отключено"
#: ../app/ui.js:1128
msgid "New connection has been rejected with reason: "
msgstr "Подключиться не удалось: "
#: ../app/ui.js:1131
msgid "New connection has been rejected"
msgstr "Подключиться не удалось"
#: ../app/ui.js:1151
msgid "Password is required"
msgstr "Требуется пароль"
#: ../vnc.html:84
msgid "noVNC encountered an error:"
msgstr "Ошибка noVNC: "
#: ../vnc.html:94
msgid "Hide/Show the control bar"
msgstr "Скрыть/Показать контрольную панель"
#: ../vnc.html:101
msgid "Move/Drag Viewport"
msgstr "Переместить окно"
#: ../vnc.html:101
msgid "viewport drag"
msgstr "Переместить окно"
#: ../vnc.html:107 ../vnc.html:110 ../vnc.html:113 ../vnc.html:116
msgid "Active Mouse Button"
msgstr "Активировать кнопки мыши"
#: ../vnc.html:107
msgid "No mousebutton"
msgstr "Отключить кнопки мыши"
#: ../vnc.html:110
msgid "Left mousebutton"
msgstr "Левая кнопка мыши"
#: ../vnc.html:113
msgid "Middle mousebutton"
msgstr "Средняя кнопка мыши"
#: ../vnc.html:116
msgid "Right mousebutton"
msgstr "Правая кнопка мыши"
#: ../vnc.html:119
msgid "Keyboard"
msgstr "Клавиатура"
#: ../vnc.html:119
msgid "Show Keyboard"
msgstr "Показать клавиатуру"
#: ../vnc.html:126
msgid "Extra keys"
msgstr "Доп. кнопки"
#: ../vnc.html:126
msgid "Show Extra Keys"
msgstr "Показать дополнительные кнопки"
#: ../vnc.html:131
msgid "Ctrl"
msgstr "Ctrl"
#: ../vnc.html:131
msgid "Toggle Ctrl"
msgstr "Передать нажатие Ctrl"
#: ../vnc.html:134
msgid "Alt"
msgstr "Alt"
#: ../vnc.html:134
msgid "Toggle Alt"
msgstr "Передать нажатие Alt"
#: ../vnc.html:137
msgid "Toggle Windows"
msgstr "Переключение вкладок"
#: ../vnc.html:137
msgid "Windows"
msgstr "Вкладка"
#: ../vnc.html:140
msgid "Send Tab"
msgstr "Передать нажатие Tab"
#: ../vnc.html:140
msgid "Tab"
msgstr "Tab"
#: ../vnc.html:143
msgid "Esc"
msgstr "Esc"
#: ../vnc.html:143
msgid "Send Escape"
msgstr "Передать нажатие Escape"
#: ../vnc.html:146
msgid "Ctrl+Alt+Del"
msgstr "Ctrl+Alt+Del"
#: ../vnc.html:146
msgid "Send Ctrl-Alt-Del"
msgstr "Передать нажатие Ctrl-Alt-Del"
#: ../vnc.html:154
msgid "Shutdown/Reboot"
msgstr "Выключить/Перезагрузить"
#: ../vnc.html:154
msgid "Shutdown/Reboot..."
msgstr "Выключить/Перезагрузить..."
#: ../vnc.html:160
msgid "Power"
msgstr "Питание"
#: ../vnc.html:162
msgid "Shutdown"
msgstr "Выключить"
#: ../vnc.html:163
msgid "Reboot"
msgstr "Перезагрузить"
#: ../vnc.html:164
msgid "Reset"
msgstr "Сброс"
#: ../vnc.html:169 ../vnc.html:175
msgid "Clipboard"
msgstr "Буфер обмена"
#: ../vnc.html:179
msgid "Clear"
msgstr "Очистить"
#: ../vnc.html:185
msgid "Fullscreen"
msgstr "Во весь экран"
#: ../vnc.html:190 ../vnc.html:197
msgid "Settings"
msgstr "Настройки"
#: ../vnc.html:200
msgid "Shared Mode"
msgstr "Общий режим"
#: ../vnc.html:203
msgid "View Only"
msgstr "Просмотр"
#: ../vnc.html:207
msgid "Clip to Window"
msgstr "В окно"
#: ../vnc.html:210
msgid "Scaling Mode:"
msgstr "Масштаб:"
#: ../vnc.html:212
msgid "None"
msgstr "Нет"
#: ../vnc.html:213
msgid "Local Scaling"
msgstr "Локльный масштаб"
#: ../vnc.html:214
msgid "Remote Resizing"
msgstr "Удаленный масштаб"
#: ../vnc.html:219
msgid "Advanced"
msgstr "Дополнительно"
#: ../vnc.html:222
msgid "Repeater ID:"
msgstr "Идентификатор ID:"
#: ../vnc.html:226
msgid "WebSocket"
msgstr "WebSocket"
#: ../vnc.html:229
msgid "Encrypt"
msgstr "Шифрование"
#: ../vnc.html:232
msgid "Host:"
msgstr "Сервер:"
#: ../vnc.html:236
msgid "Port:"
msgstr "Порт:"
#: ../vnc.html:240
msgid "Path:"
msgstr "Путь:"
#: ../vnc.html:247
msgid "Automatic Reconnect"
msgstr "Автоматическое переподключение"
#: ../vnc.html:250
msgid "Reconnect Delay (ms):"
msgstr "Задержка переподключения (мс):"
#: ../vnc.html:255
msgid "Show Dot when No Cursor"
msgstr "Показать точку вместо курсора"
#: ../vnc.html:260
msgid "Logging:"
msgstr "Лог:"
#: ../vnc.html:272
msgid "Disconnect"
msgstr "Отключение"
#: ../vnc.html:291
msgid "Connect"
msgstr "Подключение"
#: ../vnc.html:301
msgid "Password:"
msgstr "Пароль:"
#: ../vnc.html:305
msgid "Send Password"
msgstr "Пароль: "
#: ../vnc.html:315
msgid "Cancel"
msgstr "Выход"
+131 -115
View File
@@ -1,16 +1,16 @@
# Swedish translations for noVNC package
# Svenska översättningar för paket noVNC.
# Copyright (C) 2016 Various Authors
# Svenska översättningar för paketet noVNC.
# Copyright (C) 2020 The noVNC Authors
# This file is distributed under the same license as the noVNC package.
# Samuel Mannehed <samuel@cendio.se>, 2016.
# Samuel Mannehed <samuel@cendio.se>, 2020.
#
msgid ""
msgstr ""
"Project-Id-Version: noVNC 0.6.1\n"
"Project-Id-Version: noVNC 1.3.0\n"
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
"POT-Creation-Date: 2017-10-06 10:07+0200\n"
"PO-Revision-Date: 2017-10-06 10:18+0200\n"
"Last-Translator: Pierre Ossman <pierre@ossman.eu>\n"
"POT-Creation-Date: 2021-08-27 16:03+0200\n"
"PO-Revision-Date: 2021-08-27 16:18+0200\n"
"Last-Translator: Samuel Mannehed <samuel@cendio.se>\n"
"Language-Team: none\n"
"Language: sv\n"
"MIME-Version: 1.0\n"
@@ -19,266 +19,282 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.0.3\n"
#: ../app/ui.js:430
#: ../app/ui.js:400
msgid "Connecting..."
msgstr "Ansluter..."
#: ../app/ui.js:438
msgid "Connected (encrypted) to "
msgstr "Ansluten (krypterat) till "
#: ../app/ui.js:440
msgid "Connected (unencrypted) to "
msgstr "Ansluten (okrypterat) till "
#: ../app/ui.js:446
#: ../app/ui.js:407
msgid "Disconnecting..."
msgstr "Kopplar ner..."
#: ../app/ui.js:450
msgid "Disconnected"
msgstr "Frånkopplad"
#: ../app/ui.js:1052 ../core/rfb.js:248
msgid "Must set host"
msgstr "Du måste specifiera en värd"
#: ../app/ui.js:1101
#: ../app/ui.js:413
msgid "Reconnecting..."
msgstr "Återansluter..."
#: ../app/ui.js:1140
msgid "Password is required"
msgstr "Lösenord krävs"
#: ../app/ui.js:418
msgid "Internal error"
msgstr "Internt fel"
#: ../core/rfb.js:548
msgid "Disconnect timeout"
msgstr "Det tog för lång tid att koppla ner"
#: ../app/ui.js:1009
msgid "Must set host"
msgstr "Du måste specifiera en värd"
#: ../vnc.html:89
#: ../app/ui.js:1091
msgid "Connected (encrypted) to "
msgstr "Ansluten (krypterat) till "
#: ../app/ui.js:1093
msgid "Connected (unencrypted) to "
msgstr "Ansluten (okrypterat) till "
#: ../app/ui.js:1116
msgid "Something went wrong, connection is closed"
msgstr "Något gick fel, anslutningen avslutades"
#: ../app/ui.js:1119
msgid "Failed to connect to server"
msgstr "Misslyckades att ansluta till servern"
#: ../app/ui.js:1129
msgid "Disconnected"
msgstr "Frånkopplad"
#: ../app/ui.js:1144
msgid "New connection has been rejected with reason: "
msgstr "Ny anslutning har blivit nekad med följande skäl: "
#: ../app/ui.js:1147
msgid "New connection has been rejected"
msgstr "Ny anslutning har blivit nekad"
#: ../app/ui.js:1182
msgid "Credentials are required"
msgstr "Användaruppgifter krävs"
#: ../vnc.html:61
msgid "noVNC encountered an error:"
msgstr "noVNC stötte på ett problem:"
#: ../vnc.html:99
#: ../vnc.html:71
msgid "Hide/Show the control bar"
msgstr "Göm/Visa kontrollbaren"
#: ../vnc.html:106
#: ../vnc.html:78
msgid "Drag"
msgstr "Dra"
#: ../vnc.html:78
msgid "Move/Drag Viewport"
msgstr "Flytta/Dra Vyn"
#: ../vnc.html:106
msgid "viewport drag"
msgstr "dra vy"
#: ../vnc.html:112 ../vnc.html:115 ../vnc.html:118 ../vnc.html:121
msgid "Active Mouse Button"
msgstr "Aktiv musknapp"
#: ../vnc.html:112
msgid "No mousebutton"
msgstr "Ingen musknapp"
#: ../vnc.html:115
msgid "Left mousebutton"
msgstr "Vänster musknapp"
#: ../vnc.html:118
msgid "Middle mousebutton"
msgstr "Mitten-musknapp"
#: ../vnc.html:121
msgid "Right mousebutton"
msgstr "Höger musknapp"
#: ../vnc.html:124
#: ../vnc.html:84
msgid "Keyboard"
msgstr "Tangentbord"
#: ../vnc.html:124
#: ../vnc.html:84
msgid "Show Keyboard"
msgstr "Visa Tangentbord"
#: ../vnc.html:131
#: ../vnc.html:89
msgid "Extra keys"
msgstr "Extraknappar"
#: ../vnc.html:131
#: ../vnc.html:89
msgid "Show Extra Keys"
msgstr "Visa Extraknappar"
#: ../vnc.html:136
#: ../vnc.html:94
msgid "Ctrl"
msgstr "Ctrl"
#: ../vnc.html:136
#: ../vnc.html:94
msgid "Toggle Ctrl"
msgstr "Växla Ctrl"
#: ../vnc.html:139
#: ../vnc.html:97
msgid "Alt"
msgstr "Alt"
#: ../vnc.html:139
#: ../vnc.html:97
msgid "Toggle Alt"
msgstr "Växla Alt"
#: ../vnc.html:142
#: ../vnc.html:100
msgid "Toggle Windows"
msgstr "Växla Windows"
#: ../vnc.html:100
msgid "Windows"
msgstr "Windows"
#: ../vnc.html:103
msgid "Send Tab"
msgstr "Skicka Tab"
#: ../vnc.html:142
#: ../vnc.html:103
msgid "Tab"
msgstr "Tab"
#: ../vnc.html:145
#: ../vnc.html:106
msgid "Esc"
msgstr "Esc"
#: ../vnc.html:145
#: ../vnc.html:106
msgid "Send Escape"
msgstr "Skicka Escape"
#: ../vnc.html:148
#: ../vnc.html:109
msgid "Ctrl+Alt+Del"
msgstr "Ctrl+Alt+Del"
#: ../vnc.html:148
#: ../vnc.html:109
msgid "Send Ctrl-Alt-Del"
msgstr "Skicka Ctrl-Alt-Del"
#: ../vnc.html:156
#: ../vnc.html:116
msgid "Shutdown/Reboot"
msgstr "Stäng av/Boota om"
#: ../vnc.html:156
#: ../vnc.html:116
msgid "Shutdown/Reboot..."
msgstr "Stäng av/Boota om..."
#: ../vnc.html:162
#: ../vnc.html:122
msgid "Power"
msgstr "Ström"
#: ../vnc.html:164
#: ../vnc.html:124
msgid "Shutdown"
msgstr "Stäng av"
#: ../vnc.html:165
#: ../vnc.html:125
msgid "Reboot"
msgstr "Boota om"
#: ../vnc.html:166
#: ../vnc.html:126
msgid "Reset"
msgstr "Återställ"
#: ../vnc.html:171 ../vnc.html:177
#: ../vnc.html:131 ../vnc.html:137
msgid "Clipboard"
msgstr "Urklipp"
#: ../vnc.html:181
#: ../vnc.html:141
msgid "Clear"
msgstr "Rensa"
#: ../vnc.html:187
#: ../vnc.html:147
msgid "Fullscreen"
msgstr "Fullskärm"
#: ../vnc.html:192 ../vnc.html:199
#: ../vnc.html:152 ../vnc.html:159
msgid "Settings"
msgstr "Inställningar"
#: ../vnc.html:202
#: ../vnc.html:162
msgid "Shared Mode"
msgstr "Delat Läge"
#: ../vnc.html:205
#: ../vnc.html:165
msgid "View Only"
msgstr "Endast Visning"
#: ../vnc.html:209
#: ../vnc.html:169
msgid "Clip to Window"
msgstr "Begränsa till Fönster"
#: ../vnc.html:212
#: ../vnc.html:172
msgid "Scaling Mode:"
msgstr "Skalningsläge:"
#: ../vnc.html:214
#: ../vnc.html:174
msgid "None"
msgstr "Ingen"
#: ../vnc.html:215
#: ../vnc.html:175
msgid "Local Scaling"
msgstr "Lokal Skalning"
#: ../vnc.html:216
msgid "Local Downscaling"
msgstr "Lokal Nedskalning"
#: ../vnc.html:217
#: ../vnc.html:176
msgid "Remote Resizing"
msgstr "Ändra Storlek"
#: ../vnc.html:222
#: ../vnc.html:181
msgid "Advanced"
msgstr "Avancerat"
#: ../vnc.html:225
msgid "Local Cursor"
msgstr "Lokal Muspekare"
#: ../vnc.html:184
msgid "Quality:"
msgstr "Kvalitet:"
#: ../vnc.html:229
#: ../vnc.html:188
msgid "Compression level:"
msgstr "Kompressionsnivå:"
#: ../vnc.html:193
msgid "Repeater ID:"
msgstr "Repeater-ID:"
#: ../vnc.html:233
#: ../vnc.html:197
msgid "WebSocket"
msgstr "WebSocket"
#: ../vnc.html:236
#: ../vnc.html:200
msgid "Encrypt"
msgstr "Kryptera"
#: ../vnc.html:239
#: ../vnc.html:203
msgid "Host:"
msgstr "Värd:"
#: ../vnc.html:243
#: ../vnc.html:207
msgid "Port:"
msgstr "Port:"
#: ../vnc.html:247
#: ../vnc.html:211
msgid "Path:"
msgstr "Sökväg:"
#: ../vnc.html:254
#: ../vnc.html:218
msgid "Automatic Reconnect"
msgstr "Automatisk Återanslutning"
#: ../vnc.html:257
#: ../vnc.html:221
msgid "Reconnect Delay (ms):"
msgstr "Fördröjning (ms):"
#: ../vnc.html:263
#: ../vnc.html:226
msgid "Show Dot when No Cursor"
msgstr "Visa prick när ingen muspekare finns"
#: ../vnc.html:231
msgid "Logging:"
msgstr "Loggning:"
#: ../vnc.html:275
#: ../vnc.html:240
msgid "Version:"
msgstr "Version:"
#: ../vnc.html:248
msgid "Disconnect"
msgstr "Koppla från"
#: ../vnc.html:294
#: ../vnc.html:267
msgid "Connect"
msgstr "Anslut"
#: ../vnc.html:304
#: ../vnc.html:277
msgid "Username:"
msgstr "Användarnamn:"
#: ../vnc.html:281
msgid "Password:"
msgstr "Lösenord:"
#: ../vnc.html:318
#: ../vnc.html:285
msgid "Send Credentials"
msgstr "Skicka Användaruppgifter"
#: ../vnc.html:295
msgid "Cancel"
msgstr "Avbryt"
#: ../vnc.html:334
msgid "Canvas not supported."
msgstr "Canvas stöds ej"
+1 -1
View File
@@ -1,6 +1,6 @@
# Turkish translations for noVNC package
# Turkish translation for noVNC.
# Copyright (C) 2016 Various Authors
# Copyright (C) 2018 The noVNC Authors
# This file is distributed under the same license as the noVNC package.
# Ömer ÇAKMAK <farukomercakmak@gmail.com>, 2018.
#
+21 -24
View File
@@ -1,21 +1,20 @@
#!/usr/bin/env node
/*
* xgettext-html: HTML gettext parser
* Copyright (C) 2016 Pierre Ossman
* Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*/
var getopt = require('node-getopt');
const getopt = require('node-getopt');
const jsdom = require("jsdom");
const fs = require("fs");
var jsdom = require("jsdom");
var fs = require("fs");
opt = getopt.create([
['o' , 'output=FILE' , 'write output to specified file'],
['h' , 'help' , 'display this help'],
const opt = getopt.create([
['o', 'output=FILE', 'write output to specified file'],
['h', 'help', 'display this help'],
]).bindHelp().parseSystem();
var strings = {};
const strings = {};
function addString(str, location) {
if (str.length == 0) {
@@ -23,7 +22,7 @@ function addString(str, location) {
}
if (strings[str] === undefined) {
strings[str] = {}
strings[str] = {};
}
strings[str][location] = null;
}
@@ -74,8 +73,8 @@ function process(elem, locator, enabled) {
}
}
for (var i = 0;i < elem.childNodes.length;i++) {
node = elem.childNodes[i];
for (let i = 0; i < elem.childNodes.length; i++) {
let node = elem.childNodes[i];
if (node.nodeType === node.ELEMENT_NODE) {
process(node, locator, enabled);
} else if (node.nodeType === node.TEXT_NODE && enabled) {
@@ -84,26 +83,24 @@ function process(elem, locator, enabled) {
}
}
for (var i = 0;i < opt.argv.length;i++) {
var file;
for (let i = 0; i < opt.argv.length; i++) {
const fn = opt.argv[i];
const file = fs.readFileSync(fn, "utf8");
const dom = new jsdom.JSDOM(file, { includeNodeLocations: true });
const body = dom.window.document.body;
fn = opt.argv[i];
file = fs.readFileSync(fn, "utf8");
dom = new jsdom.JSDOM(file, { includeNodeLocations: true });
body = dom.window.document.body;
locator = function (elem) {
offset = dom.nodeLocation(elem).startOffset;
line = file.slice(0, offset).split("\n").length;
let locator = (elem) => {
const offset = dom.nodeLocation(elem).startOffset;
const line = file.slice(0, offset).split("\n").length;
return fn + ":" + line;
};
process(body, locator, true);
}
var output = "";
let output = "";
for (str in strings) {
for (let str in strings) {
output += "#:";
for (location in strings[str]) {
output += " " + location;
+284
View File
@@ -0,0 +1,284 @@
# Simplified Chinese translations for noVNC package.
# Copyright (C) 2020 The noVNC Authors
# This file is distributed under the same license as the noVNC package.
# Peter Dave Hello <hsu@peterdavehello.org>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: noVNC 1.1.0\n"
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
"POT-Creation-Date: 2018-01-10 00:53+0800\n"
"PO-Revision-Date: 2020-01-02 13:19+0800\n"
"Last-Translator: CUI Wei <ghostplant@qq.com>\n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: ../app/ui.js:395
msgid "Connecting..."
msgstr "连接中..."
#: ../app/ui.js:402
msgid "Disconnecting..."
msgstr "正在断开连接..."
#: ../app/ui.js:408
msgid "Reconnecting..."
msgstr "重新连接中..."
#: ../app/ui.js:413
msgid "Internal error"
msgstr "内部错误"
#: ../app/ui.js:1015
msgid "Must set host"
msgstr "请提供主机名"
#: ../app/ui.js:1097
msgid "Connected (encrypted) to "
msgstr "已连接到(加密)"
#: ../app/ui.js:1099
msgid "Connected (unencrypted) to "
msgstr "已连接到(未加密)"
#: ../app/ui.js:1120
msgid "Something went wrong, connection is closed"
msgstr "发生错误,连接已关闭"
#: ../app/ui.js:1123
msgid "Failed to connect to server"
msgstr "无法连接到服务器"
#: ../app/ui.js:1133
msgid "Disconnected"
msgstr "已断开连接"
#: ../app/ui.js:1146
msgid "New connection has been rejected with reason: "
msgstr "连接被拒绝,原因:"
#: ../app/ui.js:1149
msgid "New connection has been rejected"
msgstr "连接被拒绝"
#: ../app/ui.js:1170
msgid "Password is required"
msgstr "请提供密码"
#: ../vnc.html:89
msgid "noVNC encountered an error:"
msgstr "noVNC 遇到一个错误:"
#: ../vnc.html:99
msgid "Hide/Show the control bar"
msgstr "显示/隐藏控制栏"
#: ../vnc.html:106
msgid "Move/Drag Viewport"
msgstr "拖放显示范围"
#: ../vnc.html:106
msgid "viewport drag"
msgstr "显示范围拖放"
#: ../vnc.html:112 ../vnc.html:115 ../vnc.html:118 ../vnc.html:121
msgid "Active Mouse Button"
msgstr "启动鼠标按鍵"
#: ../vnc.html:112
msgid "No mousebutton"
msgstr "禁用鼠标按鍵"
#: ../vnc.html:115
msgid "Left mousebutton"
msgstr "鼠标左鍵"
#: ../vnc.html:118
msgid "Middle mousebutton"
msgstr "鼠标中鍵"
#: ../vnc.html:121
msgid "Right mousebutton"
msgstr "鼠标右鍵"
#: ../vnc.html:124
msgid "Keyboard"
msgstr "键盘"
#: ../vnc.html:124
msgid "Show Keyboard"
msgstr "显示键盘"
#: ../vnc.html:131
msgid "Extra keys"
msgstr "额外按键"
#: ../vnc.html:131
msgid "Show Extra Keys"
msgstr "显示额外按键"
#: ../vnc.html:136
msgid "Ctrl"
msgstr "Ctrl"
#: ../vnc.html:136
msgid "Toggle Ctrl"
msgstr "切换 Ctrl"
#: ../vnc.html:139
msgid "Alt"
msgstr "Alt"
#: ../vnc.html:139
msgid "Toggle Alt"
msgstr "切换 Alt"
#: ../vnc.html:142
msgid "Send Tab"
msgstr "发送 Tab 键"
#: ../vnc.html:142
msgid "Tab"
msgstr "Tab"
#: ../vnc.html:145
msgid "Esc"
msgstr "Esc"
#: ../vnc.html:145
msgid "Send Escape"
msgstr "发送 Escape 键"
#: ../vnc.html:148
msgid "Ctrl+Alt+Del"
msgstr "Ctrl-Alt-Del"
#: ../vnc.html:148
msgid "Send Ctrl-Alt-Del"
msgstr "发送 Ctrl-Alt-Del 键"
#: ../vnc.html:156
msgid "Shutdown/Reboot"
msgstr "关机/重新启动"
#: ../vnc.html:156
msgid "Shutdown/Reboot..."
msgstr "关机/重新启动..."
#: ../vnc.html:162
msgid "Power"
msgstr "电源"
#: ../vnc.html:164
msgid "Shutdown"
msgstr "关机"
#: ../vnc.html:165
msgid "Reboot"
msgstr "重新启动"
#: ../vnc.html:166
msgid "Reset"
msgstr "重置"
#: ../vnc.html:171 ../vnc.html:177
msgid "Clipboard"
msgstr "剪贴板"
#: ../vnc.html:181
msgid "Clear"
msgstr "清除"
#: ../vnc.html:187
msgid "Fullscreen"
msgstr "全屏"
#: ../vnc.html:192 ../vnc.html:199
msgid "Settings"
msgstr "设置"
#: ../vnc.html:202
msgid "Shared Mode"
msgstr "分享模式"
#: ../vnc.html:205
msgid "View Only"
msgstr "仅查看"
#: ../vnc.html:209
msgid "Clip to Window"
msgstr "限制/裁切窗口大小"
#: ../vnc.html:212
msgid "Scaling Mode:"
msgstr "缩放模式:"
#: ../vnc.html:214
msgid "None"
msgstr "无"
#: ../vnc.html:215
msgid "Local Scaling"
msgstr "本地缩放"
#: ../vnc.html:216
msgid "Remote Resizing"
msgstr "远程调整大小"
#: ../vnc.html:221
msgid "Advanced"
msgstr "高级"
#: ../vnc.html:224
msgid "Repeater ID:"
msgstr "中继站 ID"
#: ../vnc.html:228
msgid "WebSocket"
msgstr "WebSocket"
#: ../vnc.html:231
msgid "Encrypt"
msgstr "加密"
#: ../vnc.html:234
msgid "Host:"
msgstr "主机:"
#: ../vnc.html:238
msgid "Port:"
msgstr "端口:"
#: ../vnc.html:242
msgid "Path:"
msgstr "路径:"
#: ../vnc.html:249
msgid "Automatic Reconnect"
msgstr "自动重新连接"
#: ../vnc.html:252
msgid "Reconnect Delay (ms):"
msgstr "重新连接间隔 (ms)"
#: ../vnc.html:258
msgid "Logging:"
msgstr "日志级别:"
#: ../vnc.html:270
msgid "Disconnect"
msgstr "中断连接"
#: ../vnc.html:289
msgid "Connect"
msgstr "连接"
#: ../vnc.html:299
msgid "Password:"
msgstr "密码:"
#: ../vnc.html:313
msgid "Cancel"
msgstr "取消"
+1 -1
View File
@@ -1,5 +1,5 @@
# Traditional Chinese translations for noVNC package.
# Copyright (C) 2018 Various Authors
# Copyright (C) 2018 The noVNC Authors
# This file is distributed under the same license as the noVNC package.
# Peter Dave Hello <hsu@peterdavehello.org>, 2018.
#
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh -e
snapctl restart novnc.novncsvc
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
# `snapctl get services` returns a JSON array, example:
#{
#"n6801": {
# "listen": 6801,
# "vnc": "localhost:5901"
#},
#"n6802": {
# "listen": 6802,
# "vnc": "localhost:5902"
#}
#}
snapctl get services | jq -c '.[]' | while read service; do # for each service the user sepcified..
# get the important data for the service (listen port, VNC host:port)
listen_port="$(echo $service | jq --raw-output '.listen')"
vnc_host_port="$(echo $service | jq --raw-output '.vnc')" # --raw-output removes any quotation marks from the output
# check whether those values are valid
expr "$listen_port" : '^[0-9]\+$' > /dev/null
listen_port_valid=$?
if [ ! $listen_port_valid ] || [ -z "$vnc_host_port" ]; then
# invalid values mean the service is disabled, do nothing except for printing a message (logged in /var/log/system or systemd journal)
echo "novnc: not starting service ${service} with listen_port ${listen_port} and vnc_host_port ${vnc_host_port}"
else
# start (and fork with '&') the service using the specified listen port and VNC host:port
$SNAP/novnc_proxy --listen $listen_port --vnc $vnc_host_port &
fi
done

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